URL
https://opencores.org/ocsvn/rtf65002/rtf65002/trunk
Subversion Repositories rtf65002
[/] [rtf65002/] [trunk/] [software/] [asm/] [bootrom.asm] - Rev 39
Compare with Previous | Blame | View Log
; ============================================================================
; __
; \\__/ o\ (C) 2013, 2014 Robert Finch, Stratford
; \ __ / All rights reserved.
; \/_// robfinch<remove>@opencores.org
; ||
;
;
; This source file is free software: you can redistribute it and/or modify
; it under the terms of the GNU Lesser General Public License as published
; by the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This source file is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <http://www.gnu.org/licenses/>.
;
; ============================================================================
;
cpu RTF65002
CR EQU 0x0D ;ASCII equates
LF EQU 0x0A
TAB EQU 0x09
CTRLC EQU 0x03
BELL EQU 0x07
CTRLH EQU 0x08
CTRLI EQU 0x09
CTRLJ EQU 0x0A
CTRLK EQU 0x0B
CTRLM EQU 0x0D
CTRLS EQU 0x13
CTRLX EQU 0x18
ESC EQU 0x1b
XON EQU 0x11
XOFF EQU 0x13
; error codes
E_Ok = 0x00
E_Arg = 0x01
E_BadMbx = 0x04
E_QueFull = 0x05
E_NoThread = 0x06
E_NotAlloc = 0x09
E_NoMsg = 0x0b
E_Timeout = 0x10
E_BadAlarm = 0x11
E_NotOwner = 0x12
E_QueStrategy = 0x13
E_BadDevNum = 0x18
E_DCBInUse = 0x19
; Device driver errors
E_BadDevNum = 0x20
E_NoDev = 0x21
E_BadDevOp = 0x22
E_ReadError = 0x23
E_WriteError = 0x24
E_BadBlockNum = 0x25
E_TooManyBlocks = 0x26
; resource errors
E_NoMoreMbx = 0x40
E_NoMoreMsgBlks = 0x41
E_NoMoreAlarmBlks =0x44
E_NoMoreTCBs = 0x45
E_NoMem = 12
; task status
TS_NONE =0
TS_TIMEOUT =1
TS_WAITMSG =2
TS_PREEMPT =4
TS_RUNNING =8
TS_READY =16
TS_SLEEP =32
TS_TIMEOUT_BIT =0
TS_WAITMSG_BIT =1
TS_RUNNING_BIT =3
TS_READY_BIT =4
PRI_HIGHEST =0
PRI_HIGH =1
PRI_NORMAL =2
PRI_LOW =3
PRI_LOWEST =4
MAX_TASKNO = 63
DRAM_BASE = $04000000
DIRENT_NAME =0x00 ; file name
DIRENT_EXT =0x1C ; file name extension
DIRENT_ATTR =0x20 ; attributes
DIRENT_DATETIME =0x28
DIRENT_CLUSTER =0x30 ; starting cluster of file
DIRENT_SIZE =0x34 ; file size (6 bytes)
; One FCB is allocated and filled out for each file that is open.
;
nFCBs = 128
FCB_DE_NAME =0x00
FCB_DE_EXT =0x1C
FCB_DE_ATTR =0x20
FCB_DE_DATETIME =0x28
FCB_DE_CLUSTER =0x30 ; starting cluster of file
FCB_DE_SIZE =0x34 ; 6 byte file size
FCB_DIR_SECTOR =0x40 ; LBA directory sector this is from
FCB_DIR_ENT =0x44 ; offset in sector for dir entry
FCB_LDRV =0x48 ; logical drive this is on
FCB_MODE =0x49 ; 0 read, 1=modify
FCB_NUSERS =0x4A ; number of users of this file
FCB_FMOD =0x4B ; flag: this file was modified
FCB_RESV =0x4C ; padding out to 80 bytes
FCB_SIZE =0x50
FUB_JOB =0x00 ; User's job umber
FUB_iFCB =0x02 ; FCB number for this file
FUB_CrntLFA =0x04 ; six byte current logical file address
FUB_pBuf =0x0C ; pointer to buffer if in stream mode
FUB_sBuf =0x10 ; size of buffer for stream file
FUB_LFABuf =0x14 ; S-First LFA in Clstr Buffer
FUB_LFACluster =0x18 ; LFA of cluster
FUB_Clstr = 0x20 ; The last cluster read
FUB_fModified = 0x24 ; data in buffer was modified
FUB_fStream = 0x25 ; non-zero for stream mode
FUB_PAD =0x26
FUB_SIZE =0x30
; Boot sector info (62 byte structure) */
BSI_JMP = 0x00
BSI_OEMName = 0x03
BSI_bps = 0x0B
BSI_SecPerCluster = 0x0D
BSI_ResSectors = 0x0E
BSI_FATS = 0x10
BSI_RootDirEnts = 0x11
BSI_Sectors = 0x13
BSI_Media = 0x15
BSI_SecPerFAT = 0x16
BSI_SecPerTrack = 0x18
BSI_Heads = 0x1A
BSI_HiddenSecs = 0x1C
BSI_HugeSecs = 0x1E
BSI_DriveNum = 0x24
BSI_Rsvd1 = 0x25
BSI_BootSig = 0x26
BSI_VolID = 0x27
BSI_VolLabel = 0x2B
BSI_FileSysType = 0x36
MEM_CHK =0
MEM_FLAG =1
MEM_PREV =2
MEM_NEXT =3
; message queuing strategy
MQS_UNLIMITED =0 ; unlimited queue size
MQS_NEWEST =1 ; buffer queue size newest messages
MQS_OLDEST =2 ; buffer queue size oldest messages
LEDS EQU 0xFFDC0600
TEXTSCR EQU 0xFFD00000
COLORSCR EQU 0xFFD10000
TEXTREG EQU 0xFFDA0000
TEXT_COLS EQU 0x0
TEXT_ROWS EQU 0x1
TEXT_CURPOS EQU 11
TEXT_CURCTL EQU 8
BMP_CLUT EQU $FFDC5800
KEYBD EQU 0xFFDC0000
KEYBDCLR EQU 0xFFDC0001
PIC EQU 0xFFDC0FF0
PIC_IE EQU 0xFFDC0FF1
PIC_ES EQU 0xFFDC0FF4
PIC_RSTE EQU 0xFFDC0FF5
TASK_SELECT EQU 0xFFDD0008
RQ_SEMA EQU 0xFFDB0000
to_sema EQU 0xFFDB0010
SERIAL_SEMA EQU 0xFFDB0020
keybd_sema EQU 0xFFDB0030
iof_sema EQU 0xFFDB0040
mbx_sema EQU 0xFFDB0050
freembx_sema EQU 0xFFDB0060
mem_sema EQU 0xFFDB0070
freemsg_sema EQU 0xFFDB0080
tcb_sema EQU 0xFFDB0090
readylist_sema EQU 0xFFDB00A0
tolist_sema EQU 0xFFDB00B0
msg_sema EQU 0xFFDB00C0
freetcb_sema EQU 0xFFDB00D0
freejcb_sema EQU 0xFFDB00E0
jcb_sema EQU 0xFFDB00F0
device_semas EQU 0xFFDB1000
device_semas_end EQU 0xFFDB1200
SPIMASTER EQU 0xFFDC0500
SPI_MASTER_VERSION_REG EQU 0x00
SPI_MASTER_CONTROL_REG EQU 0x01
SPI_TRANS_TYPE_REG EQU 0x02
SPI_TRANS_CTRL_REG EQU 0x03
SPI_TRANS_STATUS_REG EQU 0x04
SPI_TRANS_ERROR_REG EQU 0x05
SPI_DIRECT_ACCESS_DATA_REG EQU 0x06
SPI_SD_SECT_7_0_REG EQU 0x07
SPI_SD_SECT_15_8_REG EQU 0x08
SPI_SD_SECT_23_16_REG EQU 0x09
SPI_SD_SECT_31_24_REG EQU 0x0a
SPI_RX_FIFO_DATA_REG EQU 0x10
SPI_RX_FIFO_DATA_COUNT_MSB EQU 0x12
SPI_RX_FIFO_DATA_COUNT_LSB EQU 0x13
SPI_RX_FIFO_CTRL_REG EQU 0x14
SPI_TX_FIFO_DATA_REG EQU 0x20
SPI_TX_FIFO_CTRL_REG EQU 0x24
SPI_RESP_BYTE1 EQU 0x30
SPI_RESP_BYTE2 EQU 0x31
SPI_RESP_BYTE3 EQU 0x32
SPI_RESP_BYTE4 EQU 0x33
SPI_INIT_SD EQU 0x01
SPI_TRANS_START EQU 0x01
SPI_TRANS_BUSY EQU 0x01
SPI_INIT_NO_ERROR EQU 0x00
SPI_READ_NO_ERROR EQU 0x00
SPI_WRITE_NO_ERROR EQU 0x00
RW_READ_SD_BLOCK EQU 0x02
RW_WRITE_SD_BLOCK EQU 0x03
CONFIGREC EQU 0xFFDCFFF0
CR_CLOCK EQU 0xFFDCFFF4
GACCEL EQU 0xFFDAE000
GA_X0 EQU 0xFFDAE002
GA_Y0 EQU 0xFFDAE003
GA_PEN EQU 0xFFDAE000
GA_X1 EQU 0xFFDAE004
GA_Y1 EQU 0xFFDAE005
GA_STATE EQU 0xFFDAE00E
GA_CMD EQU 0xFFDAE00F
AC97 EQU 0xFFDC1000
PSG EQU 0xFFD50000
PSGFREQ0 EQU 0xFFD50000
PSGPW0 EQU 0xFFD50001
PSGCTRL0 EQU 0xFFD50002
PSGADSR0 EQU 0xFFD50003
ETHMAC EQU 0xFFDC2000
ETH_MODER EQU 0x00
ETH_INT_SOURCE EQU 0x01
ETH_INT_MASK EQU 0x02
ETH_IPGT EQU 0x03
ETH_IPGR1 EQU 0x04
ETH_IPGR2 EQU 0x05
ETH_PACKETLEN EQU 0x06
ETH_COLLCONF EQU 0x07
ETH_TX_BD_NUM EQU 0x08
ETH_CTRLMODER EQU 0x09
ETH_MIIMODER EQU 0x0A
ETH_MIICOMMAND EQU 0x0B
ETH_MIIADDRESS EQU 0x0C
ETH_MIITX_DATA EQU 0x0D
ETH_MIIRX_DATA EQU 0x0E
ETH_MIISTATUS EQU 0x0F
ETH_MAC_ADDR0 EQU 0x10
ETH_MAC_ADDR1 EQU 0x11
ETH_HASH0_ADDR EQU 0x12
ETH_HASH1_ADDR EQU 0x13
ETH_TXCTRL EQU 0x14
ETH_WCTRLDATA EQU 4
ETH_MIICOMMAND_RSTAT EQU 2
ETH_MIISTATUS_BUSY EQU 2
ETH_MIIMODER_RST EQU $200
ETH_MODER_RST EQU $800
ETH_MII_BMCR EQU 0 ; basic mode control register
ETH_MII_ADVERTISE EQU 4
ETH_MII_EXPANSION =6
ETH_MII_CTRL1000 =9
ETH_ADVERTISE_ALL EQU $1E0
ETH_ADVERTISE_1000FULL =0x0200 ; Advertise 1000BASE-T full duplex
ETH_ADVERTISE_1000HALF =0x0100 ; Advertise 1000BASE-T half duplex
ETH_ESTATUS_1000_TFULL =0x2000 ; Can do 1000BT Full
ETH_ESTATUS_1000_THALF =0x1000 ; Can do 1000BT Half
ETH_BMCR_ANRESTART = 0x0200 ; Auto negotiation restart
ETH_BMCR_ISOLATE = 0x0400 ; Disconnect DP83840 from MII
ETH_BMCR_PDOWN = 0x0800 ; Powerdown the DP83840
ETH_BMCR_ANENABLE = 0x1000 ; Enable auto negotiation
ETH_PHY =7
MMU EQU 0xFFDC4000
MMU_KVMMU EQU 0xFFDC4800
MMU_FUSE EQU 0xFFDC4811
MMU_AKEY EQU 0xFFDC4812
MMU_OKEY EQU 0xFFDC4813
MMU_MAPEN EQU 0xFFDC4814
DATETIME EQU 0xFFDC0400
DATETIME_TIME EQU 0xFFDC0400
DATETIME_DATE EQU 0xFFDC0401
DATETIME_ALMTIME EQU 0xFFDC0402
DATETIME_ALMDATE EQU 0xFFDC0403
DATETIME_CTRL EQU 0xFFDC0404
DATETIME_SNAPSHOT EQU 0xFFDC0405
SPRITEREGS EQU 0xFFDAD000
SPRRAM EQU 0xFFD80000
THRD_AREA EQU 0x00000000 ; threading area 0x04000000-0x40FFFFF
BITMAPSCR EQU 0x00100000
SECTOR_BUF EQU 0x01FBEC00
BYTE_SECTOR_BUF EQU SECTOR_BUF<<2
PROG_LOAD_AREA EQU 0x0300000<<2
FCBs EQU 0x1F40000 ; room for 128 FCB's
FATOFFS EQU 0x1F50000 ; offset into FAT on card
FATBUF EQU 0x1F60000
DIRBUF EQU 0x1F70000
eth_rx_buffer EQU 0x1F80000
eth_tx_buffer EQU 0x1F84000
; Mailboxes, room for 2048
.bss
.org 0x01F90000
NR_MBX EQU $800
MBX_LINK fill.w NR_MBX,0 ; link to next mailbox in list (free list)
MBX_TQ_HEAD fill.w NR_MBX,0 ; head of task queue
MBX_TQ_TAIL fill.w NR_MBX,0
MBX_MQ_HEAD fill.w NR_MBX,0 ; head of message queue
MBX_MQ_TAIL fill.w NR_MBX,0
MBX_TQ_COUNT fill.w NR_MBX,0 ; count of queued threads
MBX_MQ_SIZE fill.w NR_MBX,0 ; number of messages that may be queued
MBX_MQ_COUNT fill.w NR_MBX,0 ; count of messages that are queued
MBX_MQ_MISSED fill.w NR_MBX,0 ; number of messages dropped from queue
MBX_OWNER fill.w NR_MBX,0 ; job handle of mailbox owner
MBX_MQ_STRATEGY fill.w NR_MBX,0 ; message queueing strategy
MBX_RESV fill.w NR_MBX,0
; Messages, room for 64kW (16,384) messages
.bss
.org 0x01FA0000
NR_MSG EQU 16384
MSG_LINK fill.w NR_MSG,0 ; link to next message in queue or free list
MSG_D1 fill.w NR_MSG,0 ; message data 1
MSG_D2 fill.w NR_MSG,0 ; message data 2
MSG_TYPE fill.w NR_MSG,0 ; message type
MSG_END EQU MSG_TYPE + NR_MSG
MT_SEMA EQU 0xFFFFFFFF
MT_IRQ EQU 0xFFFFFFF0
MT_GETCHAR EQU 0xFFFFFFEF
NR_JCB EQU 32
JCB_Number EQU 0
JCB_Name EQU 1 ; 32 bytes (1 len + 31)
JCB_Map EQU 9 ; memory map number associated with job
JCB_pCode EQU 10
JCB_nCode EQU 11 ; size of code
JCB_pData EQU 12
JCB_nData EQU 13 ; size of data
JCB_pStack EQU 14
JCB_nStack EQU 15
JCB_UserName EQU 16 ; 32 bytes
JCB_Path EQU 24 ; 80 bytes
JCB_ExitRF EQU 44 ; 80 bytes
JCB_CmdLine EQU 84 ; 240 bytes
JCB_SysIn EQU 140 ; 40 chars
JCB_SysOut EQU 150 ; 40 chars
JCB_ExitError EQU 160
JCB_pVidMem EQU 161 ; pointer to video memory
JCB_pVidMemAttr EQU 162
JCB_pVirtVid EQU 163 ; pointer to virtual video buffer
JCB_pVirtVidAttr EQU 164
JCB_VideoMode EQU 165
JCB_VideoRows EQU 166
JCB_VideoCols EQU 167
JCB_CursorRow EQU 168
JCB_CursorCol EQU 169
JCB_CursorOn EQU 170
JCB_CursorFlash EQU 171
JCB_CursorType EQU 172
JCB_NormAttr EQU 173
JCB_CurrAttr EQU 174
JCB_ScrlCnt EQU 175
JCB_fVidPause EQU 176
JCB_Next EQU 177
JCB_iof_next EQU 178 ; I/O focus list
JCB_iof_prev EQU 179
JCB_VMP_bitmap_b0 EQU 180 ; 512 bits - virtual memory page bitmap
JCB_VMP_bitmap_b1 EQU 196 ; 512 bits - virtual memory page bitmap
JCB_KeybdHead EQU 212
JCB_KeybdTail EQU 213
JCB_KeybdEcho EQU 214
JCB_KeybdBad EQU 215
JCB_KeybdAck EQU 216
JCB_KeybdLocks EQU 217
JCB_KeybdBuffer EQU 218 ; buffer is 16 words (chars = words)
JCB_esc EQU 234 ; escape flag for DisplayChar processing
JCB_Size EQU 256
JCB_LogSize EQU 8
.bss
JCBs fill.w NR_JCB * JCB_Size,0
FreeJCB dw 0
.bss
.org 0x01FBA000
; Task control blocks, room for 256 tasks
NR_TCB EQU 256
TCB_NxtRdy fill.w NR_TCB,0 ; EQU 0x01FBE100 ; next task on ready / timeout list
TCB_PrvRdy fill.w NR_TCB,0 ; EQU 0x01FBE200 ; previous task on ready / timeout list
TCB_NxtTCB fill.w NR_TCB,0 ; EQU 0x01FBE300
TCB_Timeout fill.w NR_TCB,0 ; EQU 0x01FBE400
TCB_Priority fill.w NR_TCB,0 ; EQU 0x01FBE500
TCB_MSG_D1 fill.w NR_TCB,0 ; EQU 0x01FBE600
TCB_MSG_D2 fill.w NR_TCB,0 ; EQU 0x01FBE700
TCB_hJCB fill.w NR_TCB,0 ; EQU 0x01FBE800
TCB_Status fill.w NR_TCB,0 ; EQU 0x01FBE900
TCB_CursorRow fill.w NR_TCB,0 ; EQU 0x01FBD100
TCB_CursorCol fill.w NR_TCB,0 ; EQU 0x01FBD200
TCB_hWaitMbx fill.w NR_TCB,0 ; EQU 0x01FBD300 ; handle of mailbox task is waiting at
TCB_mbq_next fill.w NR_TCB,0 ; EQU 0x01FBD400 ; mailbox queue next
TCB_mbq_prev fill.w NR_TCB,0 ; EQU 0x01FBD500 ; mailbox queue previous
TCB_SP8Save fill.w NR_TCB,0 ; EQU 0x01FBD800 ; TCB_SP8Save area
TCB_SPSave fill.w NR_TCB,0 ; EQU 0x01FBD900 ; TCB_SPSave area
TCB_StackTop fill.w NR_TCB,0
TCB_ABS8Save fill.w NR_TCB,0 ; EQU 0x01FBDA00
TCB_mmu_map fill.w NR_TCB,0 ; EQU 0x01FBDB00
TCB_npages fill.w NR_TCB,0 ; EQU 0x01FBDC00
TCB_ASID fill.w NR_TCB,0 ; EQU 0x01FBDD00
TCB_errno fill.w NR_TCB,0 ; EQU 0x01FBDE00
TCB_NxtTo fill.w NR_TCB,0 ; EQU 0x01FBDF00
TCB_PrvTo fill.w NR_TCB,0 ; EQU 0x01FBE000
TCB_MbxList fill.w NR_TCB,0 ; EQU 0x01FBCF00 ; head pointer to list of mailboxes associated with task
TCB_mbx fill.w NR_TCB,0 ; EQU 0x01FBCE00
TCB_HeapStart fill.w NR_TCB,0 ; Starting address of heap in task's memory space
TCB_HeapEnd fill.w NR_TCB,0 ; Ending addres of heap in task's memory space
;include "jcb.inc"
NR_MMU_MAP EQU 32
VPM_bitmap_b0 fill.w NR_MMU_MAP * 16,0
VPM_bitmap_b1 fill.w NR_MMU_MAP * 16,0
nPagesFree dw 0
message "cachInvRout"
.bss
.align 4096
cacheInvRout:
fill.w 4096,0
cacheLineInvRout:
fill.w 4096,0
message "SCREEN_SIZE"
.bss
.org 0x01D00000
SCREEN_SIZE EQU 8192
BIOS_SCREENS fill.w SCREEN_SIZE * NR_JCB ; 0x01D00000 to 0x01EFFFFF
; Bitmap of tasks requesting the I/O focus
;
IOFocusTbl fill.w 8,0
MAX_DEV_OP EQU 31
; Device Control Block
;
DCB_NAME EQU 0
DCB_NAME_LEN EQU 3
DCB_TYPE EQU 4
DCB_nBPB EQU 5
DCB_last_erc EQU 6
DCB_nBlocks EQU 7
DCB_pDevOp EQU 8
DCB_pDevInit EQU 9
DCB_pDevStat EQU 10
DCB_ReentCount EQU 11
DCB_fSingleUser EQU 12
DCB_hJob EQU 13
DCB_Mbx EQU 14
DCB_Sema EQU 15
DCB_OSD3 EQU 16
DCB_OSD4 EQU 17
DCB_OSD5 EQU 18
DCB_OSD6 EQU 19
DCB_SIZE EQU 20
;Standard Devices are:
;# Device Standard name
;0 NULL device NUL (OS built-in)
;1 Keyboard (sequential) KBD (OS built-in)
;2 Video (sequential) VID (OS built-in)
;3 Printer (parallel 1) LPT
;4 Printer (parallel 2) LPT2
;5 RS-232 1 COM1 (OS built-in)
;6 RS-232 2 COM2
;7 RS-232 3 COM3
;8 RS-232 4 COM4
;9
;10 Floppy FD0
;11 Floppy FD1
;12 Hard disk HD0
;13 Hard disk HD1
;14
;15
;16 SDCard CARD1 (OS built-in)
;17
;18
;19
;20
;21
;22
;23
;24
;25
;26
;27
;28 Audio PSG1 (OS built-in)
;29
;30
;31
NR_DCB EQU 32
DCBs fill NR_DCB * DCB_SIZE,0 ; EQU MSG_END
DCBs_END EQU DCBs + DCB_SIZE * NR_DCB
; preallocated stacks for TCBs
.bss
.org 0x01FC0000 ; to 0x01FFFFFF
STACK_SIZE EQU $400 ; 1kW
BIOS_STACKS fill.w STACK_SIZE * NR_TCB ; room for 256 1kW stacks
HeapStart EQU 0x00540000
HeapEnd EQU BIOS_SCREENS-1
; EhBASIC vars:
;
NmiBase EQU 0xDC
IrqBase EQU 0xDF
; BIOS vars at the top of the 8kB scratch memory
;
; TinyBasic AREA = 0x6C0 to 0x77F
PageMap EQU 0x600
PageMapEnd EQU 0x63F
PageMap2 EQU 0x640
PageMap2End EQU 0x67F
mem_pages_free EQU 0x680
bss
org 0x780
QNdx0 dw 0
QNdx1 dw 0
QNdx2 dw 0
QNdx3 dw 0
QNdx4 dw 0
FreeTCB dw 0
TimeoutList dw 0
RunningTCB dw 0
FreeMbxHandle dw 0
nMailbox dw 0
FreeMsg dw 0
nMsgBlk dw 0
missed_ticks dw 0
keybdmsg_d1 dw 0
keybdmsg_d2 dw 0
keybd_mbx dw 0
keybd_char dw 0
keybdIsSetup dw 0
keybdLock dw 0
keybdInIRQ dw 0
iof_switch dw 0
clockmsg_d1 dw 0
clockmsg_d2 dw 0
tcbsema_d1 dw 0
tcbsema_d2 dw 0
mmu_acc_save dw 0
; The IO focus list is a doubly linked list formed into a ring.
;
IOFocusNdx dw 0 ; really a pointer to the JCB owning the IO focus
;
test_mbx dw 0
test_D1 dw 0
test_D2 dw 0
tone_cnt dw 0
IrqSource EQU 0x79F
.align 0x10
JMPTMP dw 0
SP8Save dw 0
SRSave dw 0
R1Save dw 0
R2Save dw 0
R3Save dw 0
R4Save dw 0
R5Save dw 0
R6Save dw 0
R7Save dw 0
R8Save dw 0
R9Save dw 0
R10Save dw 0
R11Save dw 0
R12Save dw 0
R13Save dw 0
R14Save dw 0
R15Save dw 0
SPSave dw 0
.align 0x10
CharColor dw 0
ScreenColor dw 0
CursorRow dw 0
CursorCol dw 0
CursorFlash dw 0
Milliseconds dw 0
IRQFlag dw 0
UserTick dw 0
eth_unique_id dw 0
LineColor dw 0
QIndex dw 0
ROMcs dw 0
mmu_present dw 0
TestTask dw 0
BASIC_SESSION dw 0
gr_cmd dw 0
.align 0x10
startSector dw 0
disk_size dw 0
;
; CAUTION:
; - do not use these macros.
; - there is currently a bug in the assembler that causes it to lose the
; macro text
;
macro mStartTask pri,flags,start_addr,param,job
lda pri
ldx flags
ldy start_addr
ld r4,param
ld r5,job
int #4
db 1
endm
macro mSleep tm
lda tm
int #4
db 5
endm
macro mAllocMbx
int #4
db 6
endm
macro mWaitMsg mbx,tmout
lda mbx
ldx tmout
int #4
db 10
endm
macro mPostMsg mbx,d1,d2
lda mbx
ldx d1
ldy d2
int #4
db 8
endm
macro DisTimer
pha
lda #3
sta PIC+2
pla
endm
macro EnTimer
pha
lda #3
sta PIC+3
pla
endm
macro DisTmrKbd
pha
lda #3
sta PIC+2
lda #15
sta PIC+2
pla
endm
macro EnTmrKbd
pha
lda #3
sta PIC+3
lda #15
sta PIC+3
pla
endm
macro GoReschedule
int #2
endm
;------------------------------------------------------------------------------
; Wait for the TCB array to become available
;------------------------------------------------------------------------------
;
macro mAquireTCB
lda #33
ldx #0
txy
ld r4,#-1
jsr WaitMsg
endm
macro mReleaseTCB
lda #33
ldx #$FFFFFFFE
txy
jsr SendMsg
endm
macro mAquireMBX
lda #34
ldx #0
txy
ld r4,#-1
jsr WaitMsg
endm
macro mReleaseMBX
lda #34
ldx #$FFFFFFFE
txy
jsr SendMsg
endm
cpu rtf65002
code
message "jump table"
; jump table of popular BIOS routines
org $FFFF8000
ROMStart:
dw DisplayChar
dw KeybdCheckForKeyDirect
dw KeybdGetCharDirect
dw KeybdGetChar
dw KeybdCheckForKey
dw RequestIOFocus
dw ReleaseIOFocus
dw ClearScreen
dw HomeCursor
dw ExitTask
dw SetKeyboardEcho
dw Sleep
dw do_load
dw do_save
dw ICacheInvalidateAll
dw ICacheInvalidateLine
org $FFFF8400 ; leave room for 256 vectors
message "cold start point"
KeybdRST
start
sei ; disable interrupts
cld ; disable decimal mode
lda #1
sta LEDS
ldx #BIOS_STACKS+0x03FF ; setup stack pointer top of memory
txs
trs r0,abs8 ; set 8 bit mode absolute address offset
lda #3
trs r1,cc ; enable dcache and icache
jsr ROMChecksum
sta ROMcs
jsr SetupCacheInvalidate
jsr InitDevices
stz mmu_present ; assume no mmu
lda CONFIGREC
bit #4096
beq st_nommu
jsr InitMMU ; setup the maps and enable the mmu
lda #1
sta mmu_present
st_nommu:
jsr MemInit ; Initialize the heap
stz iof_switch
lda #2
sta LEDS
; setup interrupt vectors
ldx #$01FB8001 ; interrupt vector table from $5FB0000 to $5FB01FF
; also sets nmoi policy (native mode on interrupt)
trs r2,vbr
and r2,r2,#-2 ; mask off policy bit
phx
txy ; y = pointer to vector table
lda #511 ; 512 vectors to setup
ldx #brk_rout ; point vector to brk routine
stos
plx
lda #brk_rout
sta (x)
lda #slp_rout
sta 1,x
lda #reschedule ; must be initialized after vectors are initialized to the break vector
sta 2,x
lda #spinlock_irq
sta 3,x
lda #syscall_int
sta 4,x
lda #KeybdRST
sta 448+1,x
lda #p1000Hz
sta 448+2,x
lda #MTKTick
sta 448+3,x
lda #KeybdIRQ
sta 448+15,x
lda #SerialIRQ
sta 448+8,x
lda #InvalidOpIRQ
sta 495,x
lda #bus_err_rout
sta 508,x
sta 509,x
lda #3
sta LEDS
; stay in native mode in case emulation is not supported.
ldx #$1FF ; set 8 bit stack pointer
trs r2,sp8
ldx #0
stz IrqBase ; support for EhBASIC's interrupt mechanism
stz NmiBase
jsr ($FFFFC000>>2) ; Initialize multi-tasking
lda #TickRout ; setup tick routine
sta UserTick
lda #1
sta iof_sema
lda #(DCB_SIZE * NR_DCB)-1
ldx #0
ldy #DCBs
stos
lda #$CE ; CE =blue on blue FB = grey on grey
sta ScreenColor
sta CharColor
sta CursorFlash
jsr ClearScreen
jsr InitBMP
jsr ClearBmpScreen
jsr PICInit
; Enable interrupts
; This will likely cause an interrupt right away because the timer
; pulses run since power-up.
cli
; mStartTask #PRI_LOWEST,#0,#IdleTask,#0,#0
lda #PRI_LOWEST
ldx #0
ldy #IdleTask
ld r4,#0
ld r5,#0
int #4
db 1
lda CONFIGREC ; do we have a serial port ?
bit #32
beq st7
; 19200 * 16
;-------------
; 25MHz / 2^32
lda #$03254E6E ; constant for 19,200 baud at 25MHz
jsr SerialInit
st7:
lda #5
sta LEDS
lda CONFIGREC ; do we have sprites ?
bit #1
beq st8
lda #$3FFF ; turn on sprites
sta SPRITEREGS+120
jsr RandomizeSprram
st8:
; Enable interrupts.
; Keyboard initialization must take place after interrupts are
; enabled.
cli
lda #14
sta LEDS
stz keybdIsSetup
; mStartTask #PRI_NORMAL,#0,#KeybdSetup,#0
lda #PRI_NORMAL
ldx #0
ldy #KeybdSetup
ld r4,#0
ld r5,#0
int #4
db 1
; lea r3,KeybdStatusLEDs
; jsr StartTask
lda #6
sta LEDS
; The following must be after interrupts are enabled.
lda #9
sta LEDS
jsr HomeCursor
lda #msgStart
jsr DisplayStringB
jsr ReportMemFree
lda #msgChecksum
jsr DisplayStringB
lda ROMcs
jsr DisplayWord
jsr CRLF
lda #10
sta LEDS
; The AC97 setup uses the millisecond counter and the
; keyboard.
lda CONFIGREC ; do we have a sound generator ?
bit #4
beq st6
jsr SetupAC97
lda #4
ldx #0
ldy #Beep
; jsr StartTask
st6:
lda #11
sta LEDS
stz BASIC_SESSION
jmp Monitor
st1
jsr KeybdGetCharDirect
bra st1
stp
bra start
msgStart
db "RTF65002 system starting.",$0d,$0a,00
;------------------------------------------------------------------------------
; SetupCacheInvalidate:
;
; Setup the cache invalidate routines. Cache's in the FPGA don't have
; invalidate logic as it cannot be efficiently implemented. So we handle
; cache invalidations using software. By calling a software routine, or
; accessing data in the setup cache invalidate area, the cache will be
; effectively invalidated. This works for caches up to 16kB. (4kW)
;------------------------------------------------------------------------------
message "SetupCacheInvalidate"
SetupCacheInvalidate:
lda #4095
ldx #$EAEAEAEA ; fill memory with NOP's
ldy #cacheInvRout
stos
lda #4095
ldx #$60606060 ; fill memory with RTS's
ldy #cacheLineInvRout
stos
rts
;------------------------------------------------------------------------------
; ICacheInvalidateAll:
;
; Call to invalidate the entire ICache
;------------------------------------------------------------------------------
ICacheInvalidateAll:
jml cacheInvRout<<2
;------------------------------------------------------------------------------
; ICacheInvalidateLine:
;
; Call to invalidate a specific cache line
;
; Parameters:
; r1 = code address in line to invalidate
;------------------------------------------------------------------------------
;
ICacheInvalidateLine:
and #$3FFF
add #cacheLineInvRout<<2
jmp (r1) ; this will touch the cache line then RTS
;------------------------------------------------------------------------------
; DCacheInvalidateAll:
;
; Call to invalidate the entire DCache. Works by performing a data fetch from
; dummy data at each possible cache address. Works for caches up to 16kB in
; size.
;------------------------------------------------------------------------------
DCacheInvalidateAll:
phx
ldx #0
.0001:
ld r0,cacheInvRout,x
inx
cpx #$FFF
bls .0001
plx
rts
;------------------------------------------------------------------------------
; DCacheInvalidateLine:
;
; Call to invalidate a specific cache line in the data cache.
;
; Parameters:
; r1 = data address in line to invalidate
;------------------------------------------------------------------------------
;
DCacheInvalidateLine:
pha
and #$FFF
ld r0,cacheInvRout,r1
pla
rts
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
InitBMP:
ldx #0
ibmp1:
tsr LFSR,r1
sta BMP_CLUT,x
inx
cpx #512
bne ibmp1
rts
;------------------------------------------------------------------------------
; The ROM contents are summed up to ensure the ROM is okay.
;------------------------------------------------------------------------------
ROMChecksum:
lda #0
ldx #ROMStart>>2
idc1:
add (x)
inx
cpx #$100000000>>2
bne idc1
cmp #0 ; The sum of all the words in the
; ROM should be zero.
rts
msgChecksum:
db CR,LF,"ROM checksum: ",0
;----------------------------------------------------------
; Initialize programmable interrupt controller (PIC)
; 0 = nmi (parity error)
; 1 = keyboard reset
; 2 = 1000Hz pulse
; 3 = 100Hz pulse (cursor flash)
; 4 = ethmac
; 8 = uart
; 13 = raster interrupt
; 15 = keyboard char
;----------------------------------------------------------
message "PICInit"
PICInit:
;
lda #$000C ; clock pulses are edge sensitive
sta PIC_ES
lda #$000F ; enable nmi,kbd_rst
; A10F enable serial IRQ
sta PIC_IE
PICret:
rts
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
message "DumpTaskList"
DumpTaskList:
pha
phx
phy
push r4
lda #msgTaskList
jsr DisplayStringB
ldy #0
spl tcb_sema + 1
dtl2:
lda QNdx0,y
ld r4,r1
bmi dtl1
dtl3:
ldx #3
tya
jsr PRTNUM
lda #' '
jsr DisplayChar
ld r1,r4
ldx #3
jsr PRTNUM
lda #' '
jsr DisplayChar
jsr DisplayChar
jsr DisplayChar
ld r1,r4
lda TCB_Status,r1
jsr DisplayByte
lda #' '
jsr DisplayChar
ldx #3
lda TCB_PrvRdy,r4
jsr PRTNUM
lda #' '
jsr DisplayChar
ldx #3
lda TCB_NxtRdy,r4
jsr PRTNUM
lda #' '
jsr DisplayChar
lda TCB_Timeout,r4
jsr DisplayWord
jsr CRLF
ld r4,TCB_NxtRdy,r4
cmp r4,QNdx0,y
bne dtl3
dtl1:
iny
cpy #5
bne dtl2
stz tcb_sema + 1
pop r4
ply
plx
pla
rts
msgTaskList:
db CR,LF,"Pri Task Stat Prv Nxt Timeout",CR,LF,0
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
message "DumpTimeoutList"
DumpTimeoutList:
pha
phx
phy
push r4
lda #msgTimeoutList
jsr DisplayStringB
ldy #11
dtol2:
lda TimeoutList
ld r4,r1
bmi dtol1
spl tcb_sema + 1
dtol3:
dey
beq dtol1
ld r1,r4
ldx #3
jsr PRTNUM
lda #' '
jsr DisplayChar
jsr DisplayChar
jsr DisplayChar
ld r1,r4
ldx #3
lda TCB_PrvTo,r4
jsr PRTNUM
lda #' '
jsr DisplayChar
ldx #3
lda TCB_NxtTo,r4
jsr PRTNUM
lda #' '
jsr DisplayChar
lda TCB_Timeout,r4
jsr DisplayWord
jsr CRLF
ld r4,TCB_NxtTo,r4
bpl dtol3
dtol1:
stz tcb_sema + 1
pop r4
ply
plx
pla
rts
msgTimeoutList:
db CR,LF,"Task Prv Nxt Timeout",CR,LF,0
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
message "DumpIOFocusList"
DumpIOFocusList:
pha
phx
phy
lda #msgIOFocusList
jsr DisplayStringB
spl iof_sema + 1
lda IOFocusNdx
diofl2:
beq diofl1
tay
ldx #3
jsr PRTNUM
lda #' '
jsr DisplayChar
lda JCB_iof_prev,y
ldx #3
jsr PRTNUM
lda #' '
jsr DisplayChar
lda JCB_iof_next,y
ldx #3
jsr PRTNUM
jsr CRLF
lda JCB_iof_next,y
cmp IOFocusNdx
bne diofl2
diofl1:
stz iof_sema + 1
ply
plx
pla
rts
msgIOFocusList:
db CR,LF,"Task Prv Nxt",CR,LF,0
RunningTCBErr:
; lda #$FF
; sta LEDS
lda #msgRunningTCB
jsr DisplayStringB
rtcberr1:
jsr KeybdGetChar
cmp #-1
beq rtcberr1
jmp start
msgRunningTCB:
db CR,LF,"RunningTCB is bad.",CR,LF,0
;------------------------------------------------------------------------------
; Get the handle of the currently running job.
;------------------------------------------------------------------------------
;
GetCurrentJob:
ld r1,RunningTCB
ld r1,TCB_hJCB,r1 ; get the handle
rts
;------------------------------------------------------------------------------
; Get a pointer to the JCB for the currently running task.
;------------------------------------------------------------------------------
;
GetPtrCurrentJCB:
jsr GetCurrentJob
and r1,r1,#NR_JCB-1 ; and convert it to a pointer
; mul r1,r1,#JCB_Size
asl r1,r1,#JCB_LogSize ; 256 words
add r1,r1,#JCBs
rts
;------------------------------------------------------------------------------
; Get the location of the screen and screen attribute memory. The location
; depends on whether or not the task has the output focus.
;------------------------------------------------------------------------------
GetScreenLocation:
jsr GetPtrCurrentJCB
lda JCB_pVidMem,r1
rts
GetColorCodeLocation:
jsr GetPtrCurrentJCB
lda JCB_pVidMemAttr,r1
rts
GetNormAttr:
jsr GetPtrCurrentJCB
lda JCB_NormAttr,r1
rts
GetCurrAttr:
jsr GetPtrCurrentJCB
lda JCB_CurrAttr,r1
rts
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
message "CopyVirtualScreenToScreen"
CopyVirtualScreenToScreen
pha
phx
phy
push r4
ldx IOFocusNdx ; compute virtual screen location
beq cvss3
; copy screen chars
lda #4095 ; number of words to copy-1
ldx JCB_pVirtVid,x
ldy #TEXTSCR
mvn
; now copy the color codes
lda #4095
ldx IOFocusNdx
ldx JCB_pVirtVidAttr,x
ldy #TEXTSCR+$10000
mvn
cvss3:
; reset the cursor position in the text controller
ldy IOFocusNdx
ldx JCB_CursorRow,y
lda TEXTREG+TEXT_COLS
mul r2,r2,r1
add r2,r2,JCB_CursorCol,y
stx TEXTREG+TEXT_CURPOS
pop r4
ply
plx
pla
rts
message "CopyScreenToVirtualScreen"
CopyScreenToVirtualScreen
pha
phx
phy
push r4
lda #4095
ldx #TEXTSCR
ldy IOFocusNdx
beq csvs3
ldy JCB_pVirtVid,y
mvn
lda #4095
ldx #TEXTSCR+$10000
ldy IOFocusNdx
ldy JCB_pVirtVidAttr,y
mvn
csvs3:
pop r4
ply
plx
pla
rts
;------------------------------------------------------------------------------
; Clear the screen and the screen color memory
; We clear the screen to give a visual indication that the system
; is working at all.
;------------------------------------------------------------------------------
;
message "ClearScreen"
ClearScreen:
pha ; holds a space character
phx ; loop counter
phy ; memory addressing
lda TEXTREG+TEXT_COLS ; calc number to clear
ldx TEXTREG+TEXT_ROWS
mul r1,r1,r2 ; r1 = # chars to clear
pha
jsr GetScreenLocation
tay ; y = target address
lda #' ' ; space char
jsr AsciiToScreen
tax ; x is value to store
pla ; a is count
pha
stos ; clear the memory
jsr GetCurrAttr
tax ; x = value to use
jsr GetColorCodeLocation
tay ; y = target address
pla ; a = count
stos
ply
plx
pla
rts
;------------------------------------------------------------------------------
; Scroll text on the screen upwards
;------------------------------------------------------------------------------
;
message "ScrollUp"
ScrollUp:
pha
phx
phy
push r4
push r5
push r6
lda TEXTREG+TEXT_COLS ; acc = # text columns
ldx TEXTREG+TEXT_ROWS
mul r2,r1,r2 ; calc number of chars to scroll
sub r2,r2,r1 ; one less row
pha
jsr GetScreenLocation
tay
jsr GetColorCodeLocation
ld r6,r1
pla
scrup1:
add r5,r3,r1
ld r4,(r5) ; move character
st r4,(y)
add r5,r6,r1
ld r4,(r5) ; and move color code
st r4,(r6)
iny
inc r6
dex
bne scrup1
lda TEXTREG+TEXT_ROWS
dea
jsr BlankLine
pop r6
pop r5
pop r4
ply
plx
pla
rts
;------------------------------------------------------------------------------
; Blank out a line on the display
; line number to blank is in acc
;------------------------------------------------------------------------------
;
BlankLine:
pha
phx
phy
push r4
push r5
ldx TEXTREG+TEXT_COLS ; x = # chars to blank out from video controller
mul r3,r2,r1 ; y = screen index (row# * #cols)
ld r5,r3 ; r5 = screen index
pha
jsr GetScreenLocation
ld r4,r1
pla
add r3,r3,r4 ; y = screen address
lda #' '
jsr AsciiToScreen
blnkln1:
sta (y)
iny
dex
bne blnkln1
; reset the color codes on the display line to the normal attribute
jsr GetColorCodeLocation
tay ; y = destination
add r3,r3,r5 ; add in index
jsr GetNormAttr ; get the value to set
tax
lda TEXTREG+TEXT_COLS ; number of columns to blank out
dea ; acc is one less
stos
pop r5
pop r4
ply
plx
pla
rts
;------------------------------------------------------------------------------
; Convert ASCII character to screen display character.
;------------------------------------------------------------------------------
;
align 8
AsciiToScreen:
and #$FF
or #$100
bit #%00100000 ; if bit 5 isn't set
beq .00001
bit #%01000000 ; or bit 6 isn't set
beq .00001
and #%110011111
.00001:
rts
;------------------------------------------------------------------------------
; Convert screen character to ascii character
;------------------------------------------------------------------------------
;
ScreenToAscii:
and #$FF
cmp #26+1
bcs stasc1
add #$60
stasc1:
rts
;------------------------------------------------------------------------------
; HomeCursor
; Set the cursor location to the top left of the screen.
;------------------------------------------------------------------------------
HomeCursor:
pha
phx
phy
spl jcb_sema + 1
jsr GetPtrCurrentJCB
tax
stz JCB_CursorRow,x
stz JCB_CursorCol,x
stz jcb_sema + 1
cpx IOFocusNdx
bne hc1
stz TEXTREG+TEXT_CURPOS
hc1:
ply
plx
pla
rts
;------------------------------------------------------------------------------
; Update the cursor position in the text controller based on the
; CursorRow,CursorCol.
;------------------------------------------------------------------------------
;
UpdateCursorPos:
pha
jsr GetPtrCurrentJCB
cmp IOFocusNdx ; update cursor position in text controller
bne .ucp1 ; only for the task with the output focus
ld r0,JCB_CursorOn,r4 ; only update if cursor is showing
beq .ucp2
jsr CursorOn
phx
push r4
ld r4,r1
lda JCB_CursorRow,r4
and #$3F ; limit of 63 rows
ldx TEXTREG+TEXT_COLS
mul r2,r2,r1
lda JCB_CursorCol,r4
and #$7F ; limit of 127 cols
add r2,r2,r1
stx TEXTREG+TEXT_CURPOS
pop r4
plx
.ucp1:
pla
rts
.ucp2:
jsr CursorOff
pla
rts
CursorOff:
pha
lda #5
bms TEXTREG+TEXT_CURCTL
lda #6
bmc TEXTREG+TEXT_CURCTL
pla
rts
CursorOn:
pha
lda #5
bmc TEXTREG+TEXT_CURCTL
lda #6
bms TEXTREG+TEXT_CURCTL
pla
rts
;------------------------------------------------------------------------------
; Calculate screen memory location from CursorRow,CursorCol.
; Also refreshes the cursor location.
; Returns:
; r1 = screen location
;------------------------------------------------------------------------------
;
CalcScreenLoc:
phx
push r4
jsr GetPtrCurrentJCB
ld r4,r1
lda JCB_CursorRow,r4
and #$3F ; limit to 63 rows
ldx TEXTREG+TEXT_COLS
mul r2,r2,r1
lda JCB_CursorCol,r4
and #$7F ; limit to 127 cols
add r2,r2,r1
cmp r4,IOFocusNdx ; update cursor position in text controller
bne csl1 ; only for the task with the output focus
stx TEXTREG+TEXT_CURPOS
csl1:
jsr GetScreenLocation
add r1,r1,r2
pop r4
plx
rts
;------------------------------------------------------------------------------
; Display a character on the screen.
; If the task doesn't have the I/O focus then the character is written to
; the virtual screen.
;
; Parameters:
; r1 = char to display
;------------------------------------------------------------------------------
;
message "DisplayChar"
DisplayChar:
push r4
pha
jsr GetPtrCurrentJCB
ld r4,r1
lda JCB_esc,r4 ; are we building an escape sequence ?
bne .processEsc
pla
and #$FF ; mask off any higher order bits (called from eight bit mode).
cmp #ESC
bne .0001
sta JCB_esc,r4 ; begin the esc sequence
pop r4
rts
.0001
cmp #BELL
bne .noBell
jsr Beep
pop r4
rts
.noBell
cmp #'\r' ; carriage return ?
bne .dccr
stz JCB_CursorCol,r4 ; just set cursor column to zero on a CR
jsr UpdateCursorPos
.dcx14:
pop r4
rts
.dccr:
cmp #$91 ; cursor right ?
bne .dcx6
pha
lda JCB_CursorCol,r4
ina
cmp JCB_VideoCols,r4
bhs .dcx7
sta JCB_CursorCol,r4
.dcx7:
jsr UpdateCursorPos
pla
pop r4
rts
.dcx6:
cmp #$90 ; cursor up ?
bne .dcx8
pha
lda JCB_CursorRow,r4
beq .dcx7
dea
sta JCB_CursorRow,r4
bra .dcx7
.dcx8:
cmp #$93 ; cursor left ?
bne .dcx9
pha
lda JCB_CursorCol,r4
beq .dcx7
dea
sta JCB_CursorCol,r4
bra .dcx7
.dcx9:
cmp #$92 ; cursor down ?
bne .dcx10
pha
lda JCB_CursorRow,r4
ina
cmp JCB_VideoRows,r4
bhs .dcx7
sta JCB_CursorRow,r4
bra .dcx7
.dcx10:
cmp #$94 ; cursor home ?
bne .dcx11
pha
lda JCB_CursorCol,r4
beq .dcx12
stz JCB_CursorCol,r4
bra .dcx7
.dcx12:
stz JCB_CursorRow,r4
bra .dcx7
.dcx11:
pha
phx
phy
cmp #$99 ; delete ?
bne .dcx13
.doDel:
jsr CalcScreenLoc
tay ; y = screen location
lda JCB_CursorCol,r4 ; acc = cursor column
bra .dcx5
.dcx13
cmp #CTRLH ; backspace ?
bne .dcx3
lda JCB_CursorCol,r4
beq .dcx4
dea
sta JCB_CursorCol,r4
jsr CalcScreenLoc ; acc = screen location
tay ; y = screen location
lda JCB_CursorCol,r4
.dcx5:
ldx $4,y
stx (y)
iny
ina
cmp JCB_VideoCols,r4
blo .dcx5
lda #' '
jsr AsciiToScreen
dey
sta (y)
bra .dcx4
.dcx3:
cmp #'\n' ; linefeed ?
beq .dclf
tax ; save acc in x
jsr CalcScreenLoc ; acc = screen location
tay ; y = screen location
txa ; restore r1
jsr AsciiToScreen ; convert ascii char to screen char
sta (y)
jsr GetScreenLocation
sub r3,r3,r1 ; make y an index into the screen
jsr GetColorCodeLocation
add r3,r3,r1
jsr GetCurrAttr
sta (y)
jsr IncCursorPos
bra .dcx4
.dclf:
jsr IncCursorRow
.dcx4:
ply
plx
pla
pop r4
rts
; ESC processing
.processEsc:
cmp #(ESC<<24)+('('<<16)+(ESC<<8)+'G'
beq .procAttr
bit #$FF000000 ; is it some other five byte escape sequence ?
bne .unrecogEsc
cmp #(ESC<<16)+('('<<8)+ESC
beq .testG
bit #$FF0000 ; is it some other four byte escape sequence ?
bne .unrecogEsc ; - unrecognized escape sequence
cmp #(ESC<<8)+'`'
beq .cursOnOff
cmp #(ESC<<8)+'('
beq .testEsc
bit #$FF00 ; is it some other three byte sequence ?
bne .unrecogEsc ; - unrecognized escape sequence
cmp #ESC ; check for single char escapes
beq .esc1
pla ; some other garbage in the esc buffer ?
stz JCB_esc,r4
pop r4
rts
.cursOnOff:
pla
stz JCB_CursorOn,r4
and #$FF
cmp #'0'
beq .escRst
inc JCB_CursorOn,r4
.escRst:
stz JCB_esc,r4 ; reset escape sequence capture
pop r4
rts
.procAttr:
pla
and #$FF
cmp #'0'
bne .0005
lda JCB_NormAttr,r4
sta JCB_CurrAttr,r4
bra .escRst
.0005:
cmp #'4'
bne .escRst
phx
lda JCB_NormAttr,r4 ; get the normal attribute
tax
lsr r1,r1,#5 ; swap foreground and background colors
and #$1F
asl r2,r2,#5
or r1,r1,r2
plx
sta JCB_CurrAttr,r4 ; store in current attribute
bra .escRst
.esc1:
pla
and #$FF
cmp #'W' ; esc 'W' - delete char under cursor
bne .0006
stz JCB_esc,r4
pha
phx
phy
bra .doDel
.0006:
cmp #'T' ; esc 'T' - clear to end of line
bne .0009
phx
phy
ldx JCB_CursorCol,r4
jsr CalcScreenLoc ; acc = screen location
tay
lda #' '
jsr AsciiToScreen
.0008:
sta (y)
iny
inx
cpx JCB_VideoCols,r4
blo .0008
ply
plx
bra .escRst
.0009:
cmp #'`'
bne .0010
bra .stuffChar
.0010:
cmp #'('
bne .escRst
bra .stuffChar
.unrecogEsc:
pla
bra .escRst
.testG:
pla
and #$FF
cmp #'G'
bne .escRst
; stuff a character into the escape sequence
.stuffChar:
pha
lda JCB_esc,r4
asl r1,r1,#8
or r1,r1,0,sp
sta JCB_esc,r4
pla
pop r4
rts
.testEsc:
pla
and #$FF
cmp #ESC
bne .escRst
bra .stuffChar
;------------------------------------------------------------------------------
; Increment the cursor position, scroll the screen if needed.
;------------------------------------------------------------------------------
;
message "IncCursorPos"
IncCursorPos:
pha
phx
push r4
jsr GetPtrCurrentJCB
ld r4,r1
lda JCB_CursorCol,r4
ina
sta JCB_CursorCol,r4
ldx JCB_VideoCols,r4
cmp r1,r2
blo icc1
stz JCB_CursorCol,r4 ; column = 0
bra icr1
IncCursorRow:
pha
phx
push r4
jsr GetPtrCurrentJCB
ld r4,r1
icr1:
lda JCB_CursorRow,r4
ina
sta JCB_CursorRow,r4
ldx JCB_VideoRows,r4
cmp r1,r2
blo icc1
dex ; backup the cursor row, we are scrolling up
stx JCB_CursorRow,r4
jsr ScrollUp
icc1:
jsr UpdateCursorPos
icc2:
pop r4
plx
pla
rts
;------------------------------------------------------------------------------
; Display a string on the screen.
; The characters are packed 4 per word
;------------------------------------------------------------------------------
;
message "DisplayStringB"
DisplayStringB:
pha
phx
tax ; r2 = pointer to string
dspj1B:
lb r1,0,x ; move string char into acc
inx ; increment pointer
cmp #0 ; is it end of string ?
beq dsretB
jsr DisplayChar ; display character
bra dspj1B
dsretB:
plx
pla
rts
DisplayStringQ:
pha
phx
tax ; r2 = pointer to string
lda #TEXTSCR
sta QIndex
dspj1Q:
lb r1,0,x ; move string char into acc
inx ; increment pointer
cmp #0 ; is it end of string ?
beq dsretQ
jsr DisplayCharQ ; display character
bra dspj1Q
dsretQ:
plx
pla
rts
DisplayCharQ:
pha
phx
jsr AsciiToScreen
ldx #0
sta (QIndex,x)
lda QIndex
ina
sta QIndex
; inc QIndex
plx
pla
rts
;------------------------------------------------------------------------------
; Display a string on the screen.
; The characters are packed 1 per word
;------------------------------------------------------------------------------
;
message "DisplayStringW"
DisplayStringW:
pha
phx
tax ; r2 = pointer to string
dspj1W:
lda (x) ; move string char into acc
inx ; increment pointer
cmp #0 ; is it end of string ?
beq dsretW
jsr DisplayChar ; display character
bra dspj1W ; go back for next character
dsretW:
plx
pla
rts
DisplayStringCRLFB:
jsr DisplayStringB
CRLF:
pha
lda #'\r'
jsr DisplayChar
lda #'\n'
jsr DisplayChar
pla
rts
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
message "TickRout"
TickRout:
; support EhBASIC's IRQ functionality
; code derived from minimon.asm
lda #3 ; Timer is IRQ #3
sta IrqSource ; stuff a byte indicating the IRQ source for PEEK()
lb r1,IrqBase ; get the IRQ flag byte
lsr r4,r1
or r1,r1,r4
and #$E0
sb r1,IrqBase
inc TEXTSCR+55 ; update IRQ live indicator on screen
; flash the cursor
jsr GetPtrCurrentJCB
tax
cpx IOFocusNdx ; only bother to flash the cursor for the task with the IO focus.
bne tr1a
lda JCB_CursorFlash,x ; test if we want a flashing cursor
beq tr1a
jsr CalcScreenLoc ; compute cursor location in memory
tay
lda $10000,y ; get color code $10000 higher in memory
ld r4,IRQFlag ; get counter
lsr r4,r4
and r4,r4,#$0F ; limit to low order nybble
and #$F0 ; prepare to or in new value, mask off foreground color
or r1,r1,r4 ; set new foreground color for cursor
sta $10000,y ; store the color code back to memory
tr1a
rts
message "null.asm"
include "null.asm"
message "keyboard.asm"
include "keyboard.asm"
message "iofocus.asm"
include "iofocus.asm"
message "serial.asm"
include "serial.asm"
message "797"
;------------------------------------------------------------------------------
; Display the half-word in r1
;------------------------------------------------------------------------------
;
DisplayWord:
pha
lsr r1,r1,#16
jsr DisplayHalf
pla
;------------------------------------------------------------------------------
; Display the half-word in r1
;------------------------------------------------------------------------------
;
DisplayHalf:
pha
lsr r1,r1,#8
jsr DisplayByte
pla
;------------------------------------------------------------------------------
; Display the byte in r1
;------------------------------------------------------------------------------
;
DisplayByte:
pha
lsr r1,r1,#4
jsr DisplayNybble
pla
;------------------------------------------------------------------------------
; Display nybble in r1
;------------------------------------------------------------------------------
;
DisplayNybble:
pha
and #$0F
add #'0'
cmp #'9'+1
bcc dispnyb1
add #7
dispnyb1:
jsr DisplayChar
pla
rts
message "810"
;------------------------------------------------------------------------------
; Display memory pointed to by r2.
; destroys r1,r3
;------------------------------------------------------------------------------
;
DisplayMemW:
pha
lda #'>'
jsr DisplayChar
txa
jsr DisplayWord
lda #' '
jsr DisplayChar
lda (x)
jsr DisplayWord
inx
lda #' '
jsr DisplayChar
lda (x)
jsr DisplayWord
inx
lda #' '
jsr DisplayChar
lda (x)
jsr DisplayWord
inx
lda #' '
jsr DisplayChar
lda (x)
jsr DisplayWord
inx
jsr CRLF
pla
rts
;------------------------------------------------------------------------------
; Display memory pointed to by r2.
; destroys r1,r3
;------------------------------------------------------------------------------
;
DisplayMemBytes:
pha
phy
lda #'>'
jsr DisplayChar
lda #'B'
jsr DisplayChar
lda #' '
jsr DisplayChar
txa
jsr DisplayWord
ldy #0
.001:
lda #' '
jsr DisplayChar
lb r1,0,x
jsr DisplayByte
inx
iny
cpy #8
blo .001
lda #':'
jsr DisplayChar
ldy #0
sub r2,r2,#8
.002
lb r1,0,x
cmp #26 ; convert control characters to '.'
bhs .003
lda #'.'
.003:
jsr DisplayChar
inx
iny
cpy #8
blo .002
jsr CRLF
ply
pla
rts
message "Monitor"
;==============================================================================
; System Monitor Program
; The system monitor is task#0
;==============================================================================
;
Monitor:
ldx #BIOS_STACKS+0x03FF ; setup stack pointer
txs
lda #0 ; turn off keyboard echo
jsr SetKeyboardEcho
jsr RequestIOFocus
.PromptLn:
jsr CRLF
lda #'$'
jsr DisplayChar
; Get characters until a CR is keyed
;
.Prompt3:
jsr RequestIOFocus
; lw r1,#2 ; get keyboard character
; syscall #417
; jsr KeybdCheckForKeyDirect
; cmp #0
jsr KeybdGetChar
cmp #-1
beq .Prompt3
; jsr KeybdGetCharDirect
cmp #CR
beq .Prompt1
jsr DisplayChar
bra .Prompt3
; Process the screen line that the CR was keyed on
;
.Prompt1:
lda #80
sta LEDS
ldx RunningTCB
ldx TCB_hJCB,x
cpx #NR_JCB
bhs .Prompt3
mul r2,r2,#JCB_Size
add r2,r2,#JCBs
lda #81
sta LEDS
stz JCB_CursorCol,x ; go back to the start of the line
jsr CalcScreenLoc ; r1 = screen memory location
tay
lda #82
sta LEDS
jsr MonGetch
cmp #'$'
bne .Prompt2 ; skip over '$' prompt character
lda #83
sta LEDS
jsr MonGetch
; Dispatch based on command character
;
.Prompt2:
cmp #'>'
beq EditMem
cmp #'M'
bne .testDIR
jsr MonGetch
cmp #'B'
beq DumpMemBytes
dey
bra DumpMem
.testDIR:
cmp #'D'
bne .Prompt8
cmp #'I'
beq DoDir
bra Monitor
.Prompt8:
cmp #'F'
bne .Prompt7
jsr MonGetch
cmp #'L'
bne .Prompt8a
jsr DumpIOFocusList
jmp Monitor
.Prompt8a:
cmp #'I'
beq DoFig
cmp #'M'
beq DoFmt
dey
bra FillMem
.Prompt7:
cmp #'B' ; $B - start tiny basic
bne .Prompt4
mStartTask #PRI_LOW,#0,#CSTART,#0,#4
bra Monitor
.Prompt4:
cmp #'b'
bne .Prompt5
lda BASIC_SESSION
cmp #0
bne .bsess1
inc BASIC_SESSION
; lda #3 ; priority level 3
; ldy #$F000 ; start address $F000
; ldx #$00000000 ; flags:
; jmp (y)
; jsr ($FFFFC004>>2) ; StartTask
; mStartTask #PRI_LOW,#0,#$F000,#0,#0
lda #PRI_LOW
ldx #0
ldy #$F000
ld r4,#0
ld r5,#3
int #4
db 1
bra Monitor
.bsess1:
inc BASIC_SESSION
ldx #$3000
ldy #$4303000
asl r1,r1,#14 ; * 16kW
add r3,r3,r1
phy
lda #4095 ; 4096 words to copy
mvn ; copy BASIC ROM
ply
asl r3,r3,#2 ; convert to code address
add r3,r3,#$3000 ; xxxx_F000
lda #3
ldx #$00000000 ; zero flags at startup
jsr ($FFFFC004>>2) ; StartTask
bra Monitor
emm
cpu W65C02
jml $0C000
cpu rtf65002
.Prompt5:
cmp #'J' ; $J - execute code
beq ExecuteCode
cmp #'L' ; $L - load dector
beq LoadBlock
cmp #'W'
beq WriteBlock
.Prompt9:
cmp #'?' ; $? - display help
bne .Prompt10
lda #HelpMsg
jsr DisplayStringB
jmp Monitor
.Prompt10:
cmp #'C' ; $C - clear screen
beq TestCLS
cmp #'r'
bne .Prompt12
lda #4 ; priority level 4
ldx #0 ; zero all flags at startup
ldy #RandomLines ; task address
jsr (y)
; jsr StartTask
; jsr ($FFFFC004>>2) ; StartTask
jmp Monitor
; jmp RandomLinesCall
.Prompt12:
.Prompt13:
cmp #'P'
bne .Prompt14
mStartTask #PRI_NORMAL,#0,#Piano,#0,#2
jmp Monitor
.Prompt14:
cmp #'T'
bne .Prompt15
jsr MonGetch
cmp #'O'
bne .Prompt14a
jsr DumpTimeoutList
jmp Monitor
.Prompt14a:
cmp #'I'
bne .Prompt14b
jsr DisplayDatetime
jmp Monitor
.Prompt14b:
cmp #'E'
bne .Prompt14c
jsr ReadTemp
jmp Monitor
.Prompt14c:
dey
jsr DumpTaskList
jmp Monitor
.Prompt15:
cmp #'S'
bne .Prompt16
jsr MonGetch
cmp #'P'
bne .Prompt18
jsr ignBlanks
jsr GetHexNumber
sta SPSave
jmp Monitor
.Prompt18:
cmp #'U'
bne .Prompt18a
; jsl $F500
mStartTask #PRI_HIGH,#0,#$F500,#0,#6
; lda #PRI_HIGH
; ldx #0
; ldy #$F500
; ld r4,#0
; ld r5,#6
; int #4
; db 1
jmp Monitor
.Prompt18a:
dey
jsr SDInit
cmp #0
bne Monitor
jsr SDReadPart
cmp #0
bne Monitor
jsr SDReadBoot
cmp #0
bne Monitor
jsr loadBootFile
jmp Monitor
.Prompt16:
cmp #'e'
bne .Prompt17
; lda #1
; ldx #0
; ldy #eth_main
; jsr StartTask
mStartTask #PRI_HIGH,#0,#eth_main,#0,#0
; jsr eth_main
jmp Monitor
.Prompt17:
cmp #'R'
bne .Prompt19
jsr MonGetch
cmp #'S'
beq LoadBlock
dey
bra SetRegValue
jmp Monitor
.Prompt19:
cmp #'K'
bne .Prompt20
.Prompt19a:
jsr MonGetch
cmp #' '
bne .Prompt19a
jsr ignBlanks
jsr GetDecNumber
jsr KillTask
jmp Monitor
.Prompt20:
cmp #'8'
bne .Prompt21
jsr Test816
jmp Monitor
.Prompt21:
cmp #'m'
bne Monitor
; lda #3
; ldx #0
; ldy #test_mbx_prg
; jsr StartTask
lda #PRI_LOW
ldx #0
ldy #test_mbx_prg
ld r4,#0
ld r5,#1 ; Job 1!
int #4
db 1
bra Monitor
message "Prompt16"
RandomLinesCall:
; jsr RandomLines
jmp Monitor
MonGetch:
lda (y)
iny
jsr ScreenToAscii
rts
DoDir:
jsr do_dir
jmp Monitor
DoFmt:
jsr do_fmt
jmp Monitor
DoFig:
lda #3 ; priority level 3
ldy #$A000 ; start address $A000
ldx #$20000000 ; flags: emmulation mode set
jsr StartTask
bra Monitor
TestCLS:
jsr MonGetch
cmp #'L'
bne Monitor
jsr MonGetch
cmp #'S'
bne Monitor
jsr ClearScreen
jsr HomeCursor
; jsr CalcScreenLoc
jmp Monitor
message "HelpMsg"
HelpMsg:
db "? = Display help",CR,LF
db "CLS = clear screen",CR,LF
db "S = Boot from SD Card",CR,LF
db "SU = supermon816",CR,LF
db "L = Load Block",CR,LF
db "W = Write Block",CR,LF
db "DIR = Disk directory",CR,LF
db "M = Dump memory words, MB = Dump memory bytes",CR,LF
db "> = Edit memory words",CR,LF
db "F = Fill memory",CR,LF
db "FL = Dump I/O Focus List",CR,LF
; db "FIG = start FIG Forth",CR,LF
db "KILL n = kill task #n",CR,LF
db "B = start tiny basic",CR,LF
db "b = start EhBasic 6502",CR,LF
db "J = Jump to code",CR,LF
db "R = Dump registers, Rn = Set register value",CR,LF
db "r = random lines - test bitmap",CR,LF
db "e = ethernet test",CR,LF
db "T = Dump task list",CR,LF
db "TO = Dump timeout list",CR,LF
db "TI = display date/time",CR,LF
db "TEMP = display temperature",CR,LF
db "P = Piano",CR,LF
db "8 = 816 test",CR,LF,0
;------------------------------------------------------------------------------
; Ignore blanks in the input
; r3 = text pointer
; r1 destroyed
;------------------------------------------------------------------------------
;
ignBlanks:
ignBlanks1:
jsr MonGetch
cmp #' '
beq ignBlanks1
dey
rts
;------------------------------------------------------------------------------
; Edit memory byte(s).
;------------------------------------------------------------------------------
;
EditMem:
jsr ignBlanks
jsr GetHexNumber
ld r5,r1
ld r4,#3
edtmem1:
jsr ignBlanks
jsr GetHexNumber
sta (r5)
add r5,r5,#1
dec r4
bne edtmem1
jmp Monitor
;------------------------------------------------------------------------------
; Execute code at the specified address.
;------------------------------------------------------------------------------
;
message "ExecuteCode"
ExecuteCode:
jsr ignBlanks
jsr GetHexNumber
st r1,JMPTMP
lda #xcret ; push return address so we can do an indirect jump
pha
ld r1,R1Save
ld r2,R2Save
ld r3,R3Save
ld r4,R4Save
ld r5,R5Save
ld r6,R6Save
ld r7,R7Save
ld r8,R8Save
ld r9,R9Save
ld r10,R10Save
ld r11,R11Save
ld r12,R12Save
ld r13,R13Save
ld r14,R14Save
ld r15,R15Save
jmp (JMPTMP)
xcret:
php
st r1,R1Save
st r2,R2Save
st r3,R3Save
st r4,R4Save
st r5,R5Save
st r6,R6Save
st r7,R7Save
st r8,R8Save
st r9,R9Save
st r10,R10Save
st r11,R11Save
st r12,R12Save
st r13,R13Save
st r14,R14Save
st r15,R15Save
tsr sp,r1
st r1,SPSave
tsr sp8,r1
st r1,SP8Save
pla
sta SRSave
jmp Monitor
LoadBlock:
jsr ignBlanks
jsr GetDecNumber
pha
jsr ignBlanks
jsr GetHexNumber
tax
phx
; ld r2,#0x3800
lda #16 ; SD Card device #
ldx #1 ; Init
jsr DeviceOp
; jsr SDInit
plx
pla
lda #16 ; SD Card device #
ldx #11 ; opcode: Read blocks
pop r5 ; r5 = pointer to data storage area
ply ; y = block number to read
ld r4,#1 ; 1 block to read
jsr DeviceOp
; jsr SDReadSector
jmp Monitor
WriteBlock:
jsr ignBlanks
jsr GetDecNumber
pha
jsr ignBlanks
jsr GetHexNumber
tax
phx
jsr SDInit
plx
pla
jsr SDWriteSector
jmp Monitor
;------------------------------------------------------------------------------
; Command 'R'
; Dump the register set.
;------------------------------------------------------------------------------
message "DumpReg"
DumpReg:
ldy #0
DumpReg1:
jsr CRLF
lda #'$'
jsr DisplayChar
lda #'R'
jsr DisplayChar
ldx #1
tya
ina
jsr PRTNUM
lda #' '
jsr DisplayChar
lda R1Save,y
jsr DisplayWord
iny
cpy #15
bne DumpReg1
jsr CRLF
lda #':'
jsr DisplayChar
lda #'S'
jsr DisplayChar
lda #'P'
jsr DisplayChar
lda #' '
jsr DisplayChar
lda TCB_SPSave
jsr DisplayWord
jsr CRLF
jmp Monitor
;------------------------------------------------------------------------------
; Command 'Rn'
;------------------------------------------------------------------------------
SetRegValue:
jsr GetDecNumber
cmp #0
beq DumpReg
cmp #15
bpl Monitor
pha
jsr ignBlanks
jsr GetHexNumber
ply
sta R1Save,y
jmp Monitor
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
GetTwoParams:
jsr ignBlanks
jsr GetHexNumber ; get start address of dump
tax
jsr ignBlanks
jsr GetHexNumber ; get end address of dump
rts
;------------------------------------------------------------------------------
; Get a range, the end must be greater or equal to the start.
;------------------------------------------------------------------------------
GetRange:
jsr GetTwoParams
cmp r2,r1
bhi DisplayErr
rts
;------------------------------------------------------------------------------
; Command 'M'
; Do a memory dump of the requested location.
;------------------------------------------------------------------------------
;
DumpMem:
jsr GetRange
jsr CRLF
DumpmemW:
jsr CheckKeys
jsr DisplayMemW
cmp r2,r1
bls DumpmemW
jmp Monitor
DumpMemBytes:
jsr GetRange
jsr CRLF
.001:
jsr CheckKeys
jsr DisplayMemBytes
cmp r2,r1
bls .001
jmp Monitor
;------------------------------------------------------------------------------
; CheckKeys:
; Checks for a CTRLC or a scroll lock during long running dumps.
;------------------------------------------------------------------------------
CheckKeys:
jsr CTRLCCheck
jmp CheckScrollLock
;------------------------------------------------------------------------------
; CTRLCCheck
; Checks to see if CTRL-C is pressed. If so then the current routine is
; aborted and control is returned to the monitor.
;------------------------------------------------------------------------------
CTRLCCheck:
pha
jsr KeybdGetChar
cmp #CTRLC
beq .0001
pla
rts
.0001:
pla
pla
jmp Monitor
;------------------------------------------------------------------------------
; CheckScrollLock:
; Check for a scroll lock by the user. If scroll lock is active then tasks
; are rescheduled while the scroll lock state is tested in a loop.
;------------------------------------------------------------------------------
CheckScrollLock:
pha
.0002:
jsr GetPtrCurrentJCB
lda JCB_KeybdLocks,r1
bit #$4000 ; is scroll lock active ?
beq .0001
int #2 ; reschedule tasks
bra .0002
.0001:
pla
rts
;------------------------------------------------------------------------------
; Command 'F' or "FB"
; Fill memory with specified value.
;------------------------------------------------------------------------------
FillMem:
jsr GetRange
txy ; y = start address
sub r1,r1,r2 ; acc = count
pha
jsr ignBlanks
jsr GetHexNumber ; get the fill byte
tax
pla
stos
jmp Monitor
FillMemBytes:
jsr GetRange
txy
sub r2,r1,r2 ; x = count
inx
jsr ignBlanks
jsr GetHexNumber
.0001:
sb r1,0,y
iny
dex
bne .0001
jmp Monitor
;------------------------------------------------------------------------------
; Get a hexidecimal number. Maximum of eight digits.
; R3 = text pointer (updated)
; R1 = hex number
;------------------------------------------------------------------------------
;
GetHexNumber:
phx
push r4
ldx #0
ld r4,#8
gthxn2:
jsr MonGetch
jsr AsciiToHexNybble
cmp #-1
beq gthxn1
asl r2,r2,#4
and #$0f
or r2,r2,r1
dec r4
bne gthxn2
gthxn1:
txa
pop r4
plx
rts
GetDecNumber:
phx
push r4
push r5
ldx #0
ld r4,#10
ld r5,#10
gtdcn2:
jsr MonGetch
jsr AsciiToDecNybble
cmp #-1
beq gtdcn1
mul r2,r2,r5
add r2,r2,r1
dec r4
bne gtdcn2
gtdcn1:
txa
pop r5
pop r4
plx
rts
;------------------------------------------------------------------------------
; Convert ASCII character in the range '0' to '9', 'a' to 'f' or 'A' to 'F'
; to a hex nybble.
;------------------------------------------------------------------------------
;
AsciiToHexNybble:
cmp #'0'
bcc gthx3
cmp #'9'+1
bcs gthx5
sub #'0'
rts
gthx5:
cmp #'A'
bcc gthx3
cmp #'F'+1
bcs gthx6
sub #'A'
add #10
rts
gthx6:
cmp #'a'
bcc gthx3
cmp #'z'+1
bcs gthx3
sub #'a'
add #10
rts
gthx3:
lda #-1 ; not a hex number
rts
AsciiToDecNybble:
cmp #'0'
bcc gtdc3
cmp #'9'+1
bcs gtdc3
sub #'0'
rts
gtdc3:
lda #-1
rts
DisplayErr:
lda #msgErr
jsr DisplayStringB
jmp Monitor
msgErr:
db "**Err",CR,LF,0
;==============================================================================
;==============================================================================
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
ClearBmpScreen:
pha
phx
phy
lda #(680*384) ; a = # bytes to clear
ldx #0x29292929 ; acc = color for four pixels
ldy #BITMAPSCR;<<2 ; y = screen address
cbmp1:
; tsr LFSR,r2
; sb r2,0,y
; iny
; dea
; bne cbmp1
stos
ply
plx
pla
rts
;==============================================================================
;==============================================================================
;--------------------------------------------------------------------------
; Setup the AC97/LM4550 audio controller. Check keyboard for a CTRL-C
; interrupt which may be necessary if the audio controller isn't
; responding.
;--------------------------------------------------------------------------
;
SetupAC97:
pha
phx
phy
push r4
ld r4,Milliseconds
sac974:
stz AC97+0x26 ; trigger a read of register 26 (status reg)
sac971: ; wait for status to register 0xF (all ready)
ld r3,Milliseconds
sub r3,r3,r4
cmp r3,#1000
bhi sac97Abort
jsr KeybdGetChar ; see if we needed to CTRL-C
cmp #CTRLC
beq sac973
lda AC97+0x68 ; wait for dirty bit to clear
bne sac971
lda AC97+0x26 ; check status at reg h26, wait for
and #0x0F ; analogue to be ready
cmp #$0F
bne sac974
sac973:
stz AC97+2 ; master volume, 0db attenuation, mute off
stz AC97+4 ; headphone volume, 0db attenuation, mute off
stz AC97+0x18 ; PCM gain (mixer) mute off, no attenuation
stz AC97+0x0A ; mute PC beep
lda #0x8000 ; bypass 3D sound
sta AC97+0x20
ld r4,Milliseconds
sac972:
ld r3,Milliseconds
sub r3,r3,r4
cmp r3,#1000
bhi sac97Abort
jsr KeybdGetChar
cmp #CTRLC
beq sac975
lda AC97+0x68 ; wait for dirty bits to clear
bne sac972 ; wait a while for the settings to take effect
sac975:
pop r4
ply
plx
pla
rts
sac97Abort:
lda #msgAC97bad
jsr DisplayStringCRLFB
pop r4
ply
plx
pla
rts
msgAC97bad:
db "The AC97 controller is not responding.",CR,LF,0
;--------------------------------------------------------------------------
; Sound a 800 Hz beep
;--------------------------------------------------------------------------
;
Beep:
lda #2 ; check for a PSG
bmt CONFIGREC
beq .ret
lda #15 ; master volume to max
sta PSG+64
lda #13422 ; 800Hz
sta PSGFREQ0
; decay (16.384 ms)2
; attack (8.192 ms)1
; release (1.024 s)A
; sustain level C
lda #0xCA12
sta PSGADSR0
lda #0x1104 ; gate, output enable, triangle waveform
sta PSGCTRL0
; lda #1000 ; delay about 1s
mSleep #1000
lda #0x0104 ; gate off, output enable, triangle waveform
sta PSGCTRL0
; lda #1000 ; delay about 1s
mSleep #1000
lda #83
sta LEDS
lda #0x0000 ; gate off, output enable off, no waveform
sta PSGCTRL0
.ret
rts
include "Piano.asm"
include "SDCard.asm"
; Load the root directory from disk
; r2 = where to place root directory in memory
;
loadBootFile:
lb r1,BYTE_SECTOR_BUF+BSI_SecPerFAT+1 ; sectors per FAT
asl r1,r1,#8
orb r1,r1,BYTE_SECTOR_BUF+BSI_SecPerFAT
bne loadBootFile7
lb r1,BYTE_SECTOR_BUF+$27 ; sectors per FAT, FAT32
asl r1,r1,#8
orb r1,r1,BYTE_SECTOR_BUF+$26
asl r1,r1,#8
orb r1,r1,BYTE_SECTOR_BUF+$25
asl r1,r1,#8
orb r1,r1,BYTE_SECTOR_BUF+$24
loadBootFile7:
lb r4,BYTE_SECTOR_BUF+$10 ; number of FATs
mul r3,r1,r4 ; offset
lb r1,BYTE_SECTOR_BUF+$F ; r1 = # reserved sectors before FAT
asl r1,r1,#8
orb r1,r1,BYTE_SECTOR_BUF+$E
add r3,r3,r1 ; r3 = root directory sector number
ld r6,startSector
add r5,r3,r6 ; r5 = root directory sector number
lb r1,BYTE_SECTOR_BUF+$D ; sectors per cluster
add r3,r1,r5 ; r3 = first cluster after first cluster of directory
bra loadBootFile6
loadBootFile6:
; For now we cheat and just go directly to sector 512.
bra loadBootFileTmp
loadBootFileTmp:
; We load the number of sectors per cluster, then load a single cluster of the file.
; This is 16kib
ld r5,r3 ; r5 = start sector of data area
ld r2,#PROG_LOAD_AREA ; where to place file in memory
lb r3,BYTE_SECTOR_BUF+$D ; sectors per cluster
loadBootFile1:
ld r1,r5 ; r1=sector to read
jsr SDReadSector
inc r5 ; r5 = next sector
add r2,r2,#512
dec r3
bne loadBootFile1
lda PROG_LOAD_AREA>>2 ; make sure it's bootable
cmp #$544F4F42
bne loadBootFile2
lda #msgJumpingToBoot
jsr DisplayStringB
lda (PROG_LOAD_AREA>>2)+$1
jsr (r1)
jmp Monitor
loadBootFile2:
lda #msgNotBootable
jsr DisplayStringB
ldx #PROG_LOAD_AREA>>2
jsr DisplayMemW
jsr DisplayMemW
jsr DisplayMemW
jsr DisplayMemW
jmp Monitor
msgJumpingToBoot:
db "Jumping to boot",0
msgNotBootable:
db "SD card not bootable.",0
spi_init_ok_msg:
db "SD card initialized okay.",0
spi_init_error_msg:
db ": error occurred initializing the SD card.",0
spi_boot_error_msg:
db "SD card boot error",CR,LF,0
spi_read_error_msg:
db "SD card read error",CR,LF,0
spi_write_error_msg:
db "SD card write error",0
do_fmt:
jsr SDInit
cmp #0
bne fmt_abrt
; clear out the directory buffer
lda #65535
ldx #0
ldy #DIRBUF
stos
jsr store_dir
fmt_abrt:
rts
do_dir:
jsr CRLF
jsr SDInit
cmp #0
bne dirabrt
jsr load_dir
ld r4,#0 ; r4 = entry counter
ddir3:
asl r3,r4,#6 ; y = start of entry, 64 bytes per entry
ldx #32 ; 32 chars in filename
ddir4:
lb r1,DIRBUF<<2,y
beq ddir2 ; move to next dir entry if null is found
cmp #$20 ; don't display control chars
bmi ddir1
jsr DisplayChar
bra ddir5
ddir1:
lda #' '
jsr DisplayChar
ddir5:
iny
dex
bne ddir4
lda #' '
jsr DisplayChar
asl r3,r4,#4 ; y = start of entry, 16 words per entry
lda DIRBUF+$D,y
ldx #5
jsr PRTNUM
jsr CRLF
ddir2:
jsr KeybdGetChar
cmp #CTRLC
beq ddir6
inc r4
cmp r4,#512 ; max 512 dir entries
bne ddir3
ddir6:
dirabrt:
rts
load_dir:
pha
phx
phy
lda #4000
ldx #DIRBUF<<2
ldy #64
jsr SDReadMultiple
ply
plx
pla
rts
store_dir:
pha
phx
phy
lda #4000
ldx #DIRBUF<<2
ldy #64
jsr SDWriteMultiple
ply
plx
pla
rts
; r1 = pointer to file name
; r2 = pointer to buffer to save
; r3 = length of buffer
;
do_save:
pha
jsr SDInit
cmp #0
bne dsavErr
pla
jsr load_dir
ld r4,#0
dsav4:
asl r5,r4,#6
ld r7,#0
ld r10,r1
dsav2:
lb r6,DIRBUF<<2,r5
lb r8,0,r10
cmp r6,r8
bne dsav1
inc r5
inc r7
inc r10
cmp r7,#32
bne dsav2
; here the filename matched
dsav8:
asl r7,r4,#7 ; compute file address 64k * entry #
add r7,r7,#5000 ; start at sector 5,000
ld r1,r7 ; r1 = sector number
lsr r3,r3,#9 ; r3/512
iny ; +1
jsr SDWriteMultiple
dsav3:
rts
; Here the filename didn't match
dsav1:
inc r4
cmp r4,#512
bne dsav4
; Here none of the filenames in the directory matched
; Find an empty entry.
ld r4,#0
dsav6:
asl r5,r4,#6
lb r6,DIRBUF<<2,r5
beq dsav5
inc r4
cmp r4,#512
bne dsav6
; Here there were no empty entries
lda #msgDiskFull
jsr DisplayStringB
rts
dsav5:
ld r7,#32
ld r10,r1
dsav7:
lb r6,0,r10 ; copy the filename into the directory entry
sb r6,DIRBUF<<2,r5
inc r5
inc r10
dec r7
bne dsav7
; copy the file size into the directory entry
asl r5,r4,#4 ; 16 words per dir entry
sty DIRBUF+$D,r5
jsr store_dir
bra dsav8
dsavErr:
pla
rts
msgDiskFull
db CR,LF,"The disk is full, unable to save file.",CR,LF,0
do_load:
pha
jsr SDInit
cmp #0
bne dsavErr
pla
jsr load_dir
ld r4,#0
dlod4:
asl r5,r4,#6
ld r7,#0
ld r10,r1
dlod2:
lb r6,DIRBUF<<2,r5
lb r8,0,r10
cmp r6,r8
bne dlod1
inc r5
inc r7
inc r10
cmp r7,#32
bne dlod2
; here the filename matched
dlod8:
asl r5,r4,#4 ; 16 words
ld r3,DIRBUF+$d,r5 ; get file size into y register
asl r7,r4,#7 ; compute file address 64k * entry #
add r7,r7,#5000 ; start at sector 5,000
ld r1,r7 ; r1 = sector number
lsr r3,r3,#9 ; r3/512
iny ; +1
jsr SDReadMultiple
dlod3:
rts
; Here the filename didn't match
dlod1:
inc r4
cmp r4,#512
bne dlod4
; Here none of the filenames in the directory matched
;
lda #msgFileNotFound
jsr DisplayStringB
rts
msgFileNotFound:
db CR,LF,"File not found.",CR,LF
;include "ethernet.asm"
;--------------------------------------------------------------------------
; Initialize sprite image caches with random data.
;--------------------------------------------------------------------------
message "RandomizeSprram"
RandomizeSprram:
ldx #SPRRAM
ld r4,#14336 ; number of chars to initialize
rsr1:
tsr LFSR,r1
sta (x)
inx
dec r4
bne rsr1
rts
;include "float.asm"
include "RandomLines.asm"
;--------------------------------------------------------------------------
; RTF65002 code to display the date and time from the date/time device.
;--------------------------------------------------------------------------
DisplayDatetime
pha
phx
lda #' '
jsr DisplayChar
stz DATETIME_SNAPSHOT ; take a snapshot of the running date/time
lda DATETIME_DATE
tax
lsr r1,r1,#16
jsr DisplayHalf ; display the year
lda #'/'
jsr DisplayChar
txa
lsr r1,r1,#8
and #$FF
jsr DisplayByte ; display the month
lda #'/'
jsr DisplayChar
txa
and #$FF
jsr DisplayByte ; display the day
lda #' '
jsr DisplayChar
lda #' '
jsr DisplayChar
lda DATETIME_TIME
tax
lsr r1,r1,#24
jsr DisplayByte ; display hours
lda #':'
jsr DisplayChar
txa
lsr r1,r1,#16
jsr DisplayByte ; display minutes
lda #':'
jsr DisplayChar
txa
lsr r1,r1,#8
jsr DisplayByte ; display seconds
lda #'.'
jsr DisplayChar
txa
jsr DisplayByte ; display 100ths seconds
jsr CRLF
plx
pla
rts
include "ReadTemp.asm"
include "memory.asm"
;------------------------------------------------------------------------------
; Bus Error Routine
; This routine display a message then restarts the BIOS.
;------------------------------------------------------------------------------
;
message "bus_err_rout"
bus_err_rout:
cld
ldx #87
stx LEDS
pla ; get rid of the stacked flags
ply ; get the error PC
ldx #$05FFFFF8 ; setup stack pointer top of memory
txs
ldx #88
stx LEDS
jsr CRLF
stz RunningTCB
lda #JCBs
sta IOFocusNdx
lda #msgBusErr
jsr DisplayStringB
tya
jsr DisplayWord ; display the originating PC address
lda #msgDataAddr
jsr DisplayStringB
tsr #9,r1
jsr DisplayWord
ldx #89
stx LEDS
ldx #128
ber2:
lda #' '
jsr DisplayChar
tsr hist,r1
jsr DisplayWord
dex
bne ber2
jsr CRLF
ber3:
nop
jmp ber3
;cli ; enable interrupts so we can get a char
ber1:
jsr KeybdGetCharDirect ; Don't use the keyboard buffer
cmp #-1
beq ber1
lda RunningTCB
jsr KillTask
jmp SelectTaskToRun
msgBusErr:
db "Bus error at: ",0
msgDataAddr:
db " data address: ",0
;------------------------------------------------------------------------------
; 1000 Hz interrupt
; This IRQ must be fast.
; Increments the millisecond counter
;------------------------------------------------------------------------------
message "p1000Hz"
p1000Hz:
pha
lda #2 ; reset edge sense circuit
sta PIC_RSTE
inc Milliseconds ; increment milliseconds count
pla
rti
;------------------------------------------------------------------------------
; Sleep interrupt
; This interrupt just selects another task to run. The current task is
; stuck in an infinite loop.
;------------------------------------------------------------------------------
message "slp_rout"
slp_rout:
cld ; clear extended precision mode
pusha
lda RunningTCB
cmp #MAX_TASKNO
bhi slp1
jsr RemoveTaskFromReadyList
tax
tsa ; save off the stack pointer
sta TCB_SPSave,x
tsr sp8,r1 ; and the eight bit mode stack pointer
sta TCB_SP8Save,x
tsr abs8,r1
sta TCB_ABS8Save,x
lda #TS_SLEEP ; set the task status to SLEEP
sta TCB_Status,x
slp1:
jmp SelectTaskToRun
;------------------------------------------------------------------------------
; Check for and emulate unsupoorted instructions.
;------------------------------------------------------------------------------
InvalidOpIRQ:
pha
phx
phy
tsx
lda 4,x ; get the address of the invalid op off the stack
lb r3,0,r1 ; get the opcode byte
cpy #$44 ; is it MVP ?
beq EmuMVP
cpy #$54 ; is it MVN ?
beq EmuMVN
; We don't know what the op is. Treat it like a NOP
; Increment the address and return.
pha
lda #msgUnimp
jsr DisplayStringB
pla
jsr DisplayWord
jsr CRLF
ina
sta 4,x ; save incremented return address back to stack
jsr DumpHistoryTable
ply
plx
pla
rti
DumpHistoryTable:
pha
phx
ldx #64
ioi1:
tsr hist,r1
jsr DisplayWord
lda #' '
jsr DisplayChar
dex
bne ioi1
plx
pla
rts
EmuMVP:
push r4
push r5
tsr sp,r4
lda 4,r4
ldx 3,r4
ldy 2,r4
EmuMVP1:
ld r5,(x)
st r5,(y)
dex
dey
dea
cmp #$FFFFFFFF
bne EmuMVP1
sta 4,r4
stx 3,r4
sty 2,r4
inc 6,r4 ; increment the return address by one.
pop r5
pop r4
ply
plx
pla
rti
EmuMVN:
push r4
push r5
tsr sp,r4
lda 4,r4
ldx 3,r4
ldy 2,r4
EmuMVN1:
ld r5,(x)
st r5,(y)
inx
iny
dea
cmp #$FFFFFFFF
bne EmuMVN1
sta 4,r4
stx 3,r4
sty 2,r4
inc 6,r4 ; increment the return address by one.
pop r5
pop r4
ply
plx
pla
rti
msgUnimp:
db "Unimplemented at: ",0
brk_rout:
lda #16
sta LEDS
jsr kernel_panic
db "Break routine",0
jsr DumpHistoryTable
stp
rti
nmirout:
pha
lda #msgPerr
jsr DisplayStringB
lda 3,sp
jsr DisplayWord
jsr CRLF
pla
rti
msgPerr:
db "Parity error at: ",0
;==============================================================================
; Finitron Multi-Tasking Kernel (FMTK)
; __
; \\__/ o\ (C) 2013, 2014 Robert Finch, Stratford
; \ __ / All rights reserved.
; \/_// robfinch<remove>@opencores.org
; ||
;==============================================================================
message "FMTK"
org $FFFFC000
syscall_vectors:
dw MTKInitialize
dw StartTask
dw ExitTask
dw KillTask
dw SetTaskPriority
dw Sleep
dw AllocMbx
dw FreeMbx
dw PostMsg
dw SendMsg
dw WaitMsg
dw CheckMsg
org $FFFFC200
message "MTKInitialize"
MTKInitialize:
; Initialize semaphores
lda #1
sta freetcb_sema
sta freembx_sema
sta freemsg_sema
sta tcb_sema
sta readylist_sema
sta tolist_sema
sta mbx_sema
sta msg_sema
sta jcb_sema
tsr vbr,r2
and r2,#-2
lda #reschedule
sta 2,x
lda #syscall_int
sta 4,x
lda #MTKTick
sta 448+3,x
stz UserTick
lda #-1
sta TimeoutList ; no entries in timeout list
sta QNdx0
sta QNdx1
sta QNdx2
sta QNdx3
sta QNdx4
stz missed_ticks
; Initialize IO Focus List
;
lda #7
ldx #0
ldy #IOFocusTbl
stos
; Set owning job to zero (the monitor)
lda #255
ldx #0
ldy #TCB_hJCB
stos
; zero out JCB's
; This will NULL out the I/O focus list pointers
lda #NR_JCB * JCB_Size
ldx #0
lea r3,JCBs
stos
; Setup default values in the JCB's
ldy #0
ldx #JCBs
ijcb1:
sty JCB_Number,x
sty JCB_Map,x
stz JCB_esc,x
lda #31
sta JCB_VideoRows,x
lda #56
sta JCB_VideoCols,x
lda #1 ; turn on keyboard echo
sta JCB_KeybdEcho,x
sta JCB_CursorOn,x
sta JCB_CursorFlash,x
stz JCB_CursorRow,x
stz JCB_CursorCol,x
stz JCB_CursorType,x
lda #%1011_01111 ; grey on grey
sta JCB_NormAttr,x
sta JCB_CurrAttr,x
ld r4,r3
mul r4,r4,#8192 ; 8192 words per screen
add r4,r4,#BIOS_SCREENS
st r4,JCB_pVirtVid,x
st r4,JCB_pVidMem,x
add r4,r4,#$1000
st r4,JCB_pVirtVidAttr,x
st r4,JCB_pVidMemAttr,x
cpy #0
bne ijcb2
lda #%0110_01110 ; CE =blue on blue FB = grey on grey
sta JCB_NormAttr,x
sta JCB_CurrAttr,x
ld r4,#TEXTSCR
st r4,JCB_pVidMem,x
add r4,r4,#$10000
st r4,JCB_pVidMemAttr,x
ijcb2:
lda #8
sta JCB_LogSize,x
iny
add r2,r2,#JCB_Size
cpy #32
blo ijcb1
; Initialize free message list
lda #NR_MSG
sta nMsgBlk
stz FreeMsg
ldx #0
lda #1
st4:
sta MSG_LINK,x
ina
inx
cpx #NR_MSG
bne st4
lda #-1
sta MBX_LINK+NR_MSG-1
; Initialize free mailbox list
; Note the first NR_TCB mailboxes are statically allocated to the tasks.
; They are effectively pre-allocated.
lda #NR_MBX-NR_TCB
sta nMailbox
ldx #NR_TCB
stx FreeMbxHandle
lda #NR_TCB+1
st3:
sta MBX_LINK,x
ina
inx
cpx #NR_MBX
bne st3
lda #-1
sta MBX_LINK+NR_MBX-1
; Initialize the FreeJCB list
lda #JCBs+JCB_Size ; the next available JCB
sta FreeJCB
tax
add r1,r1,#JCB_Size
ldy #NR_JCB-1
st5:
sta JCB_Next,x
add r1,r1,#JCB_Size
add r2,r2,#JCB_Size
dey
bne st5
stz JCB_Next,x
; Initialize the FreeTCB list
lda #1 ; the next available TCB
sta FreeTCB
ldx #1
lda #2
st2:
sta TCB_NxtTCB,x
ina
inx
cpx #256
bne st2
lda #-1
sta TCB_NxtTCB+255
lda #4
sta LEDS
; Manually setup the BIOS task
stz RunningTCB ; BIOS is task #0
stz TCB_NxtRdy ; manually build the ready list
stz TCB_PrvRdy
lda #-1
sta TCB_NxtTo
sta TCB_PrvTo
stz QNdx2 ; insert at priority 2
; manually build the IO focus list
lda #JCBs
sta IOFocusNdx ; Job #0 (Monitor) has the focus
stz JCB_iof_next,r1
stz JCB_iof_prev,r1
lda #1
sta IOFocusTbl ; set the job #0 request bit
lda #PRI_NORMAL
sta TCB_Priority
stz TCB_Timeout
lda #TS_RUNNING|TS_READY
sta TCB_Status
stz TCB_CursorRow
stz TCB_CursorCol
stz TCB_ABS8Save
ldx #BIOS_STACKS+0x03FF ; setup stack pointer top of memory
stx TCB_SPSave
ldx #$1FF
stx TCB_SP8Save
rts
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
message "startIdleTask"
StartIdleTask:
lda #4
ldx #0
ldy #IdleTask
jsr StartTask
rts
;------------------------------------------------------------------------------
; IdleTask
;
; IdleTask is a low priority task that is always running. It runs when there
; is nothing else to run.
; This task check for tasks that are stuck in infinite loops and kills them.
;------------------------------------------------------------------------------
IdleTask:
stz TestTask
it2:
inc TEXTSCR+111 ; increment IDLE active flag
ldx TestTask
and r2,r2,#$FF
beq it1
lda TCB_Status,x
cmp #TS_SLEEP
bne it1
txa
int #4 ; KillTask function
db 3
; jsr KillTask
it1:
inc TestTask
cli ; enable interrupts
wai ; wait for one to happen
bra it2
;------------------------------------------------------------------------------
; Parameters:
; r1 = job name
; r2 = start address
;------------------------------------------------------------------------------
;
StartJob:
pha
; Get a free JCB
spl freejcb_sema + 1
ld r6,FreeJCB
beq sjob1
ld r7,JCB_Next,r6
st r7,FreeJCB
stz freejcb_sema + 1
lea r7,JCB_Name,r6 ; r7 = address of name field
asl r7,r7,#2 ; convert word to byte address
ld r9,r7 ; save off buffer address
ld r8,#0 ; r8 = count of characters (0 to 31)
sjob3:
lb r5,0,r1 ; get a character
beq sjob2 ; end of string ?
sb r5,1,r7
ina
inc r7
inc r8
cmp r8,#31 ; max number of chars ?
blo sjob3
sjob2:
sb r8,0,r9 ; save name length
sjob1:
stz freejcb_sema + 1
pla
rts
;------------------------------------------------------------------------------
; StartTask
;
; Startup a task. The task is automatically allocated a 1kW stack from the BIOS
; stacks area. The scheduler is invoked after the task is added to the ready
; list.
;
; Parameters:
; r1 = task priority
; r2 = start flags
; r3 = start address
; r4 = start parameter
; r5 = job handle
;------------------------------------------------------------------------------
message "StartTask"
StartTask:
pusha
ld r6,r1 ; r6 = task priority
ld r8,r2 ; r8 = flag register value on startup
; get a free TCB
;
spl freetcb_sema+1
lda FreeTCB ; get free tcb list pointer
bmi stask1
tax
lda TCB_NxtTCB,x
sta FreeTCB ; update the FreeTCB list pointer
stz freetcb_sema+1
lda #81
sta LEDS
txa ; acc = TCB index (task number)
sta TCB_mbx,x
; setup the stack for the task
; Zap the stack memory.
ld r7,r2
asl r2,r2,#10 ; 1kW stack per task
add r2,r2,#BIOS_STACKS ;+0x3ff ; add in stack base
pha
phx
phy
txy ; y = target address
ldx #ExitTask ; x = fill value
lda #$3FF ; acc = # words to fill -1
stos
ply
plx
pla
add r2,r2,#$3FF ; Move pointer to top of stack
stx TCB_StackTop,r7
sub r2,r2,#128
tsr sp,r9 ; save off current stack pointer
spl tcb_sema + 1
txs
st r6,TCB_Priority,r7
stz TCB_Status,r7
stz TCB_Timeout,r7
st r5,TCB_hJCB,r7 ; save job handle
; setup virtual video for the task
; stz TCB_CursorRow,r7
; stz TCB_CursorCol,r7
stz TCB_mmu_map,r7 ; use mmu map
; jsr AllocateMemPage
pha
lda #82
sta LEDS
lda #-1
sta TCB_MbxList,r7
lda BASIC_SESSION
cmp #1
bls stask3
asl r1,r1,#14
add r1,r1,#$430_0000
sta TCB_ABS8Save,r7
add r1,r1,#$1FF
sta TCB_SP8Save,r7
bra stask4
stask3:
lda #$1FF
sta TCB_SP8Save,r7
stz TCB_ABS8Save,r7
stask4:
lda #83
sta LEDS
pla
; tay
; setup the initial stack image for the task
; Cause a return to the ExitTask routine when the task does a
; final rts.
; fake an IRQ call by stacking the return address and processor
; flags on the stack
ldx #ExitTask ; save the address of the task exit routine
phx
phy ; save start address on stack
push r8 ; save processor status reg on stack
; now fake pushing the register set onto the stack. Registers start up
; in an undefined state.
; sub sp,#15 ; 15 registers
push r4
push r4
push r4
push r4
push r4
push r4
push r4
push r4
push r4
push r4
push r4
push r4
push r4
push r4
push r4
tsx
stx TCB_SPSave,r7
; now restore the current stack pointer
trs r9,sp
; Insert the task into the ready list
ld r4,#84
st r4,LEDS
jsr AddTaskToReadyList
lda #1
sta tcb_sema
int #2 ; invoke the scheduler
; GoReschedule ; invoke the scheduler
stask2:
popa
rts
stask1:
stz freetcb_sema+1
jsr kernel_panic
db "No more task control blocks available.",0
bra stask2
;------------------------------------------------------------------------------
; ExitTask
;
; This routine is called when the task exits with an rts instruction. OR
; it may be invoked with a JMP ExitTask. In either case the task must be
; running so it can't be on the timeout list. The scheduler is invoked
; after the task is removed from the ready list.
;------------------------------------------------------------------------------
message "ExitTask"
ExitTask:
; release any aquired resources
; - mailboxes
; - messages
hoff
spl tcb_sema + 1
lda RunningTCB
cmp #MAX_TASKNO
bhi xtsk1
jsr RemoveTaskFromReadyList
jsr RemoveFromTimeoutList
stz TCB_Status,r1 ; set task status to TS_NONE
jsr ReleaseIOFocus
; lda TCB_ABS8Save,x
; jsr FreeMemPage
; Free up all the mailboxes associated with the task.
xtsk7:
pha
lda TCB_MbxList,r1
bmi xtsk6
jsr FreeMbx
pla
bra xtsk7
xtsk6:
pla
ldx #86
stx LEDS
spl freetcb_sema+1
ldx FreeTCB ; add the task control block to the free list
stx TCB_NxtTCB,r1
sta FreeTCB
stz freetcb_sema+1
xtsk1:
jmp SelectTaskToRun
;------------------------------------------------------------------------------
; r1 = task number
; r2 = new priority
;------------------------------------------------------------------------------
;
SetTaskPriority:
cmp #MAX_TASKNO ; make sure task number is reasonable
bhi stp1
phy
spl tcb_sema + 1
ldy TCB_Status,r1 ; if the task is on the ready list
bit r3,#TS_READY|TS_RUNNING ; then remove it and re-add it.
beq stp2 ; Otherwise just go set the priority field
jsr RemoveTaskFromReadyList
stx TCB_Priority,r1
jsr AddTaskToReadyList
bra stp3
stp2:
stx TCB_Priority,r1
stp3:
ldy #1
sty tcb_sema
int #2
ply
stp1:
rts
;------------------------------------------------------------------------------
; AddTaskToReadyList
;
; The ready list is a group of five ready lists, one for each priority
; level. Each ready list is organized as a doubly linked list to allow fast
; insertions and removals. The list is organized as a ring (or bubble) with
; the last entry pointing back to the first. This allows a fast task switch
; to the next task. Which task is at the head of the list is maintained
; in the variable QNdx for the priority level.
;
; Registers Affected: none
; Parameters:
; r1 = task number
; Returns:
; none
;------------------------------------------------------------------------------
;
message "AddTaskToReadyList"
AddTaskToReadyList:
phx
phy
ldx #TS_READY
stx TCB_Status,r1
ldx #-1
stx TCB_NxtRdy,r1
stx TCB_PrvRdy,r1
ldy TCB_Priority,r1
cpy #5
blo arl1
ldy #PRI_LOWEST
arl1:
ldx QNdx0,y
bmi arl5
ldy TCB_PrvRdy,x
sta TCB_NxtRdy,y
sty TCB_PrvRdy,r1
sta TCB_PrvRdy,x
stx TCB_NxtRdy,r1
ply
plx
rts
; Here the ready list was empty, so add at head
arl5:
sta QNdx0,y
sta TCB_NxtRdy,r1
sta TCB_PrvRdy,r1
ply
plx
rts
;------------------------------------------------------------------------------
; RemoveTaskFromReadyList
;
; This subroutine removes a task from the ready list.
;
; Registers Affected: none
; Parameters:
; r1 = task number
; Returns:
; r1 = task number
;------------------------------------------------------------------------------
message "RemoveTaskFromReadyList"
RemoveTaskFromReadyList:
phx
phy
push r4
push r5
ldy TCB_Status,r1 ; is the task on the ready list ?
bit r3,#TS_READY|TS_RUNNING
beq rfr2
and r3,r3,#~(TS_READY|TS_RUNNING)
sty TCB_Status,r1 ; task status no longer running or ready
ld r4,TCB_NxtRdy,r1 ; Get previous and next fields.
ld r5,TCB_PrvRdy,r1
st r4,TCB_NxtRdy,r5
st r5,TCB_PrvRdy,r4
ldy TCB_Priority,r1
cmp r1,QNdx0,y ; Are we removing the QNdx task ?
bne rfr2
st r4,QNdx0,y
; Now we test for the case where the task being removed was the only one
; on the ready list of that priority level. We can tell because the
; NxtRdy would point to the task itself.
cmp r4,r1
bne rfr2
ldx #-1 ; Make QNdx negative
stx QNdx0,y
stx TCB_NxtRdy,r1
stx TCB_PrvRdy,r1
rfr2:
pop r5
pop r4
ply
plx
rts
;------------------------------------------------------------------------------
; AddToTimeoutList
; AddToTimeoutList adds a task to the timeout list. The task is placed in the
; list depending on it's timeout value.
;
; Registers Affected: none
; Parameters:
; r1 = task
; r2 = timeout value
;------------------------------------------------------------------------------
message "AddToTimeoutList"
AddToTimeoutList:
phx
push r4
push r5
ld r5,#-1
st r5,TCB_NxtTo,r1 ; these fields should already be -1
st r5,TCB_PrvTo,r1
ld r4,TimeoutList ; are there any tasks on the timeout list ?
bmi attl_add_at_head ; If not, update head of list
attl_check_next:
sub r2,r2,TCB_Timeout,r4 ; is this timeout > next
bmi attl_insert_before
ld r5,r4
ld r4,TCB_NxtTo,r4
bpl attl_check_next
; Here we scanned until the end of the timeout list and didn't find a
; timeout of a greater value. So we add the task to the end of the list.
attl_add_at_end:
st r4,TCB_NxtTo,r1 ; r4 is = -1
st r1,TCB_NxtTo,r5
st r5,TCB_PrvTo,r1
stx TCB_Timeout,r1
bra attl_exit
attl_insert_before:
cmp r5,#0
bmi attl_insert_before_head
st r4,TCB_NxtTo,r1 ; next on list goes after this task
st r5,TCB_PrvTo,r1 ; set previous link
st r1,TCB_NxtTo,r5
st r1,TCB_PrvTo,r4
bra attl_adjust_timeout
; Here there is no previous entry in the timeout list
; Add at start
attl_insert_before_head:
sta TCB_PrvTo,r4
st r5,TCB_PrvTo,r1 ; r5 is = -1
st r4,TCB_NxtTo,r1
sta TimeoutList ; update the head pointer
attl_adjust_timeout:
add r2,r2,TCB_Timeout,r4 ; get back timeout
stx TCB_Timeout,r1
ld r5,TCB_Timeout,r4 ; adjust the timeout of the next task
sub r5,r5,r2
st r5,TCB_Timeout,r4
bra attl_exit
; Here there were no tasks on the timeout list, so we add at the
; head of the list.
attl_add_at_head:
sta TimeoutList ; set the head of the timeout list
stx TCB_Timeout,r1
ldx #-1 ; flag no more entries in timeout list
stx TCB_NxtTo,r1 ; no next entries
stx TCB_PrvTo,r1 ; and no prev entries
attl_exit:
ldx TCB_Status,r1 ; set the task's status as timing out
or r2,r2,#TS_TIMEOUT
stx TCB_Status,r1
pop r5
pop r4
plx
rts
;------------------------------------------------------------------------------
; RemoveFromTimeoutList
;
; This routine is called when a task is killed. The task may need to be
; removed from the middle of the timeout list.
;
; On entry: the timeout list semaphore must be already set.
; Registers Affected: none
; Parameters:
; r1 = task number
;------------------------------------------------------------------------------
message "RemoveFromTimeoutList"
RemoveFromTimeoutList:
cmp #MAX_TASKNO
bhi rftl_not_on_list2
phx
push r4
push r5
ld r4,TCB_Status,r1 ; Is the task even on the timeout list ?
bit r4,#TS_TIMEOUT
beq rftl_not_on_list
cmp TimeoutList ; Are we removing the head of the list ?
beq rftl_remove_from_head
ld r4,TCB_PrvTo,r1 ; adjust the links of the next and previous
bmi rftl_empty_list ; no previous link - list corrupt?
ld r5,TCB_NxtTo,r1 ; tasks on the list to point around the task
st r5,TCB_NxtTo,r4
bmi rftl_empty_list
st r4,TCB_PrvTo,r5
ldx TCB_Timeout,r1 ; update the timeout of the next on list
add r2,r2,TCB_Timeout,r5 ; with any remaining timeout in the task
stx TCB_Timeout,r5 ; removed from the list
bra rftl_empty_list
; Update the head of the list.
rftl_remove_from_head:
ld r5,TCB_NxtTo,r1
st r5,TimeoutList ; store next field into list head
bmi rftl_empty_list
ld r4,TCB_Timeout,r1 ; add any remaining timeout to the timeout
add r4,r4,TCB_Timeout,r5 ; of the next task on the list.
st r4,TCB_Timeout,r5
ld r4,#-1 ; there is no previous item to the head
sta TCB_PrvTo,r5
; Here there is no previous or next items in the list, so the list
; will be empty once this task is removed from it.
rftl_empty_list:
tax
lda #0 ; clear timeout status (bit #0)
bmc TCB_Status,x
dea ; acc=-1; make sure the next and prev fields indicate
sta TCB_NxtTo,x ; the task is not on a list.
sta TCB_PrvTo,x
txa
rftl_not_on_list:
pop r5
pop r4
plx
rftl_not_on_list2:
rts
;------------------------------------------------------------------------------
; PopTimeoutList
;
; This subroutine is called from within the timer ISR when the task's
; timeout expires. It's always the head of the list that's being removed in
; the timer ISR so the removal from the timeout list is optimized. We know
; the timeout expired, so the amount of time to add to the next task is zero.
; This routine is written as a macro since it's only called from one place.
; This routine is inlined. Implementing it as a macro increases performance.
;
; Registers Affected: acc, x, y, flags
; Parameters:
; x: head of timeout list
; Returns:
; r1 = task id of task popped from timeout list
;------------------------------------------------------------------------------
;
message "PopTimeoutList"
macro PopTimeoutList
ldy #-1
lda TCB_NxtTo,x
sta TimeoutList ; store next field into list head
bmi ptl1
sty TCB_PrvTo,r1 ; previous link = -1
ptl1:
lda #0 ; clear timeout status
bmc TCB_Status,x
sty TCB_NxtTo,x ; make sure the next and prev fields indicate
sty TCB_PrvTo,x ; the task is not on a list.
txa
endm
;------------------------------------------------------------------------------
; Sleep
;
; Put the currently running task to sleep for a specified time.
;
; Registers Affected: none
; Parameters:
; r1 = time duration in centi-seconds (1/100 second).
; Returns: none
;------------------------------------------------------------------------------
;
Sleep:
pha
phx
tax
spl tcb_sema + 1
lda RunningTCB
jsr RemoveTaskFromReadyList
jsr AddToTimeoutList ; The scheduler will be returning to this
lda #1
sta tcb_sema
int #2 ; task eventually, once the timeout expires,
plx
pla
rts
;------------------------------------------------------------------------------
; Short delay routine.
; This routine works by reading the tick register. When a subsequent read
; of the tick register exceeds the value of the original read by at least
; the value passed as a parameter, then this routine returns.
; The tick register increments at the clock rate (eg 25 MHz).
;------------------------------------------------------------------------------
;
short_delay:
phx
phy
tsr tick,r2
usec1:
tsr tick,r3
sub r3,r3,r2
cmp r1,r3
blo usec1
ply
plx
rts
;------------------------------------------------------------------------------
; KillTask
;
; "Kills" a task, removing it from all system lists. If the task has the
; IO focus, the IO focus is switched. Task #0 is immortal and cannot be
; killed.
;
; Registers Affected: none
; Parameters:
; r1 = task number
;------------------------------------------------------------------------------
;
KillTask:
phx
cmp #1 ; BIOS task and IDLE task are immortal
bls kt1
cmp #MAX_TASKNO
bhi kt1
tax
lda TCB_hJCB,r1
jsr ForceReleaseIOFocus
txa
spl tcb_sema + 1
jsr RemoveTaskFromReadyList
jsr RemoveFromTimeoutList
stz TCB_Status,r1 ; set task status to TS_NONE
; Free up all the mailboxes associated with the task.
kt7:
pha
tax
lda TCB_MbxList,r1
bmi kt6
jsr FreeMbx2
pla
bra kt7
kt6:
lda #1
sta tcb_sema
pla
spl freetcb_sema + 1
ldx FreeTCB ; add the task control block to the free list
stx TCB_NxtTCB,r1
sta FreeTCB
stz freetcb_sema + 1
cmp RunningTCB ; keep running the current task as long as
bne kt1 ; the task didn't kill itself.
int #2 ; invoke scheduler to reschedule tasks
kt1:
plx
rts
;------------------------------------------------------------------------------
; Allocate a mailbox
; Parameters:
; r1 = pointer to place to store handle
; Returns:
; r1 = E_Ok means mailbox allocated properly
; r1 = E_Arg means a NULL pointer was passed in r1
; r1 = E_NoMoreMbx means no more mailboxes were available
; zf is set if everything is ok, otherwise zf is clear
;------------------------------------------------------------------------------
;
message "AllocMbx"
AllocMbx:
cmp #0
beq ambx_bad_ptr
phx
phy
push r4
ld r4,r1 ; r4 = pointer to returned handle
spl freembx_sema + 1
lda FreeMbxHandle ; Get mailbox off of free mailbox list
sta (r4) ; store off the mailbox number
bmi ambx_no_mbxs
ldx MBX_LINK,r1 ; and update the head of the list
stx FreeMbxHandle
dec nMailbox ; decrement number of available mailboxes
stz freembx_sema + 1
spl tcb_sema + 1
ldy RunningTCB ; Add the mailbox to the list of mailboxes
ldx TCB_MbxList,y ; managed by the task.
stx MBX_LINK,r1
sta TCB_MbxList,y
tax
ldy RunningTCB ; set the mailbox owner
; bmi RunningTCBErr
lda TCB_hJCB,y
stz tcb_sema + 1
spl mbx_sema + 1
sta MBX_OWNER,x
lda #-1 ; initialize the head and tail of the queues
sta MBX_TQ_HEAD,x
sta MBX_TQ_TAIL,x
sta MBX_MQ_HEAD,x
sta MBX_MQ_TAIL,x
stz MBX_TQ_COUNT,x ; initialize counts to zero
stz MBX_MQ_COUNT,x
stz MBX_MQ_MISSED,x
lda #8 ; set the max queue size
sta MBX_MQ_SIZE,x ; and
lda #MQS_NEWEST ; queueing strategy
sta MBX_MQ_STRATEGY,x
stz mbx_sema + 1
pop r4
ply
plx
lda #E_Ok
rts
ambx_bad_ptr:
lda #E_Arg
rts
ambx_no_mbxs:
stz freembx_sema + 1
pop r4
ply
plx
lda #E_NoMoreMbx
rts
;------------------------------------------------------------------------------
; Free up a mailbox.
; This function frees a mailbox from the currently running task. It may be
; called by ExitTask().
;
; Parameters:
; r1 = mailbox handle
;------------------------------------------------------------------------------
;
FreeMbx:
phx
ldx RunningTCB
jsr FreeMbx2
plx
rts
;------------------------------------------------------------------------------
; Free up a mailbox.
; This function dequeues any messages from the mailbox and adds the messages
; back to the free message pool. The function also dequeues any threads from
; the mailbox.
; Called from KillTask() and FreeMbx().
;
; Parameters:
; r1 = mailbox handle
; r2 = task handle
; Returns:
; r1 = E_Ok if everything ok
; r1 = E_Arg if a bad handle is passed
;------------------------------------------------------------------------------
;
FreeMbx2:
cmp #NR_MBX ; check mailbox handle parameter
bhs fmbx1
cpx #MAX_TASKNO
bhi fmbx1
phx
phy
spl mbx_sema + 1
; Dequeue messages from mailbox and add them back to the free message list.
fmbx5:
pha
jsr DequeueMsgFromMbx
bmi fmbx3
spl freemsg_sema + 1
phx
ldx FreeMsg
stx MSG_LINK,r1
sta FreeMsg
stz freemsg_sema + 1
plx
pla
bra fmbx5
fmbx3:
pla
; Dequeue threads from mailbox.
fmbx6:
pha
jsr DequeueThreadFromMbx2
bmi fmbx7
pla
bra fmbx6
fmbx7:
pla
; Remove mailbox from TCB list
ldy TCB_MbxList,x
phx
ldx #-1
fmbx10:
cmp r1,r3
beq fmbx9
tyx
ldy MBX_LINK,y
bpl fmbx10
; ?The mailbox was not in the list managed by the task.
plx
bra fmbx2
fmbx9:
cmp r2,r0
bmi fmbx11
ldy MBX_LINK,y
sty MBX_LINK,x
plx
bra fmbx12
fmbx11:
; No prior mailbox in list, update head
ldy MBX_LINK,r1
plx
sty TCB_MbxList,x
fmbx12:
; Add mailbox back to mailbox pool
spl freembx_sema + 1
ldx FreeMbxHandle
stx MBX_LINK,r1
sta FreeMbxHandle
stz freembx_sema + 1
fmbx2:
stz mbx_sema + 1
ply
plx
lda #E_Ok
rts
fmbx1:
lda #E_Arg
rts
;------------------------------------------------------------------------------
; Queue a message at a mailbox.
; On entry the mailbox semaphore is already activated.
;
; Parameters:
; r1 = message
; r2 = mailbox
;------------------------------------------------------------------------------
message "QueueMsgAtMbx"
QueueMsgAtMbx:
cmp #0
beq qmam_bad_msg
pha
phx
phy
push r4
ld r4,MBX_MQ_STRATEGY,x
cmp r4,#MQS_UNLIMITED
beq qmam_unlimited
cmp r4,#MQS_NEWEST
beq qmam_newest
cmp r4,#MQS_OLDEST
beq qmam_oldest
jsr kernel_panic
db "Illegal message queue strategy",0
bra qmam8
; Here we assumed "unlimited" message storage. Just add the new message at
; the tail of the queue.
qmam_unlimited:
ldy MBX_MQ_TAIL,x
bmi qmam_add_at_head
sta MSG_LINK,y
bra qmam2
qmam_add_at_head:
sta MBX_MQ_HEAD,x
qmam2:
sta MBX_MQ_TAIL,x
qmam6:
inc MBX_MQ_COUNT,x ; increase the queued message count
ldx #-1
stx MSG_LINK,r1
pop r4
ply
plx
pla
qmam_bad_msg:
rts
; Here we are queueing a limited number of messages. As new messages are
; added at the tail of the queue, messages drop off the head of the queue.
qmam_newest:
ldy MBX_MQ_TAIL,x
bmi qmam3
sta MSG_LINK,y
bra qmam4
qmam3:
sta MBX_MQ_HEAD,x
qmam4:
sta MBX_MQ_TAIL,x
ldy MBX_MQ_COUNT,x
iny
cmp r3,MBX_MQ_SIZE,x
bls qmam6
ldy #-1
sty MSG_LINK,r1
; Remove the oldest message which is the one at the head of the mailbox queue.
; Add the message back to the pool of free messages.
lda MBX_MQ_HEAD,x
ldy MSG_LINK,r1 ; move next in queue
sty MBX_MQ_HEAD,x ; to head of list
qmam8:
inc MBX_MQ_MISSED,x
qmam1:
spl freemsg_sema + 1
ldy FreeMsg ; put old message back into free message list
sty MSG_LINK,r1
sta FreeMsg
inc nMsgBlk
stz freemsg_sema + 1
;GoReschedule
pop r4
ply
plx
pla
rts
; Here we are buffering the oldest messages. So if there are too many messages
; in the queue already, then the queue doesn't change and the new message is
; lost.
qmam_oldest:
ldy MBX_MQ_COUNT,x ; Check if the queue is full
cmp r3,MBX_MQ_SIZE,x
bhs qmam8 ; If the queue is full, then lose the current message
bra qmam_unlimited ; Otherwise add message to queue
;------------------------------------------------------------------------------
; Dequeue a message from a mailbox.
;
; Returns
; r1 = message number
; nf set if there is no message, otherwise clear
;------------------------------------------------------------------------------
message "DequeueMsgFromMbx"
DequeueMsgFromMbx:
phx
phy
tax ; x = mailbox index
lda MBX_MQ_COUNT,x ; are there any messages available ?
beq dmfm3
dea
sta MBX_MQ_COUNT,x ; update the message count
lda MBX_MQ_HEAD,x ; Get the head of the list, this should not be -1
bmi dmfm3 ; since the message count > 0
ldy MSG_LINK,r1 ; get the link to the next message
sty MBX_MQ_HEAD,x ; update the head of the list
bpl dmfm2 ; if there was no more messages then update the
sty MBX_MQ_TAIL,x ; tail of the list as well.
dmfm2:
sta MSG_LINK,r1 ; point the link to the messahe itself to indicate it's dequeued
dmfm1:
ply
plx
cmp #0
rts
dmfm3:
ply
plx
lda #-1
rts
;------------------------------------------------------------------------------
; Parameters:
; r1 = mailbox handle
; Returns:
; r1 = E_arg means pointer is invalid
; r1 = E_NoThread means no thread was queued at the mailbox
; r2 = thead handle
;------------------------------------------------------------------------------
message "DequeueThreadFromNbx"
DequeueThreadFromMbx:
push r4
ld r4,MBX_TQ_HEAD,r1
bpl dtfm2
pop r4
ldx #-1
lda #E_NoThread
rts
dtfm2:
push r5
dec MBX_TQ_COUNT,r1
ld r2,r4
ld r4,TCB_mbq_next,r4
st r4,MBX_TQ_HEAD,r1
bmi dtfm3
ld r5,#-1
st r5,TCB_mbq_prev,r4
bra dtfm4
dtfm3:
ld r5,#-1
st r5,MBX_TQ_TAIL,r1
dtfm4:
; stz MBX_SEMA+1
ld r5,r2
lda TCB_Status,r5
bit #TS_TIMEOUT
beq dtfm5
ld r1,r5
jsr RemoveFromTimeoutList
dtfm5:
ld r4,#-1
st r4,TCB_mbq_next,r5
st r4,TCB_mbq_prev,r5
stz TCB_hWaitMbx,r5
stz TCB_Status,r5 ; set task status = TS_NONE
pop r5
pop r4
lda #E_Ok
rts
;------------------------------------------------------------------------------
; This function is called from FreeMbx(). It dequeues threads from the
; mailbox without removing the thread from the timeout list. The thread will
; then timeout waiting for a message that can never be delivered.
;
; Parameters:
; r1 = mailbox handle
; Returns:
; r1 = E_arg means pointer is invalid
; r1 = E_NoThread means no thread was queued at the mailbox
; r2 = thead handle
;------------------------------------------------------------------------------
message "DequeueThreadFromNbx2"
DequeueThreadFromMbx2:
push r4
ld r4,MBX_TQ_HEAD,r1
bpl dtfm2a
pop r4
ldx #-1
lda #E_NoThread
rts
dtfm2a:
push r5
dec MBX_TQ_COUNT,r1
ld r2,r4
ld r4,TCB_mbq_next,r4
st r4,MBX_TQ_HEAD,r1
bmi dtfm3a
ld r5,#-1
st r5,TCB_mbq_prev,r4
bra dtfm4a
dtfm3a:
ld r5,#-1
st r5,MBX_TQ_TAIL,r1
dtfm4a:
ld r4,#-1
st r4,TCB_mbq_next,x
st r4,TCB_mbq_prev,x
stz TCB_hWaitMbx,x
sei
lda #TS_WAITMSG_BIT
bmc TCB_Status,x
cli
pop r5
pop r4
lda #E_Ok
rts
;------------------------------------------------------------------------------
; PostMsg and SendMsg are the same operation except that PostMsg doesn't
; invoke rescheduling while SendMsg does. So they both call the same
; SendMsgPrim primitive routine. This two wrapper functions for convenience.
;------------------------------------------------------------------------------
;
PostMsg:
push r4
ld r4,#0 ; Don't invoke scheduler
jsr SendMsgPrim
pop r4
rts
SendMsg:
push r4
ld r4,#1 ; Do invoke scheduler
jsr SendMsgPrim
pop r4
rts
;------------------------------------------------------------------------------
; SendMsgPrim
; Send a message to a mailbox
;
; Parameters
; r1 = handle to mailbox
; r2 = message D1
; r3 = message D2
; r4 = scheduler flag 1=invoke,0=don't invoke
;
; Returns
; r1=E_Ok everything is ok
; r1=E_BadMbx for a bad mailbox number
; r1=E_NotAlloc for a mailbox that isn't allocated
; r1=E_NoMsg if there are no more message blocks available
; zf is set if everything is okay, otherwise zf is clear
;------------------------------------------------------------------------------
message "SendMsgPrim"
SendMsgPrim:
cmp #NR_MBX ; check the mailbox number to make sure
bhs smsg1 ; that it's sensible
push r5
push r6
push r7
spl mbx_sema + 1
ld r7,MBX_OWNER,r1
bmi smsg2 ; error: no owner
pha
phx
jsr DequeueThreadFromMbx ; r1=mbx
ld r6,r2 ; r6 = thread
plx
pla
cmp r6,#0
bpl smsg3
; Here there was no thread waiting at the mailbox, so a message needs to
; be allocated
smp2:
spl freemsg_sema + 1
ld r7,FreeMsg
bmi smsg4 ; no more messages available
ld r5,MSG_LINK,r7
st r5,FreeMsg
dec nMsgBlk ; decrement the number of available messages
stz freemsg_sema + 1
stx MSG_D1,r7
sty MSG_D2,r7
pha
phx
tax ; x = mailbox
ld r1,r7 ; acc = message
jsr QueueMsgAtMbx
plx
pla
cmp r6,#0 ; check if there is a thread waiting for a message
bmi smsg5
smsg3:
stx TCB_MSG_D1,r6
sty TCB_MSG_D2,r6
smsg7:
spl tcb_sema + 1
ld r5,TCB_Status,r6
bit r5,#TS_TIMEOUT
beq smsg8
ld r1,r6
jsr RemoveFromTimeoutList
smsg8:
lda #TS_WAITMSG_BIT
bmc TCB_Status,r6
lda #1
sta tcb_sema
ld r1,r6
spl tcb_sema + 1
jsr AddTaskToReadyList
stz tcb_sema + 1
cmp r4,#0
beq smsg5
stz mbx_sema + 1
int #2
;GoReschedule
bra smsg9
smsg5:
stz mbx_sema + 1
smsg9:
pop r7
pop r6
pop r5
lda #E_Ok
rts
smsg1:
lda #E_BadMbx
rts
smsg2:
stz mbx_sema + 1
pop r7
pop r6
pop r5
lda #E_NotAlloc
rts
smsg4:
stz freemsg_sema + 1
stz mbx_sema + 1
pop r7
pop r6
pop r5
lda #E_NoMsg
rts
;------------------------------------------------------------------------------
; WaitMsg
; Wait at a mailbox for a message to arrive. This subroutine will block the
; task until a message is available or the task times out on the timeout
; list.
;
; Parameters
; r1=mailbox
; r2=timeout
; Returns:
; r1=E_Ok if everything is ok
; r1=E_BadMbx for a bad mailbox number
; r1=E_NotAlloc for a mailbox that isn't allocated
; r2=message D1
; r3=message D2
;------------------------------------------------------------------------------
message "WaitMsg"
WaitMsg:
cmp #NR_MBX ; check the mailbox number to make sure
bhs wmsg1 ; that it's sensible
push r4
push r5
push r6
push r7
ld r6,r1
wmsg11:
spl mbx_sema + 1
ld r5,MBX_OWNER,r1
cmp r5,#MAX_TASKNO
bhi wmsg2 ; error: no owner
jsr DequeueMsgFromMbx
; cmp #0
bpl wmsg3
; Here there was no message available, remove the task from
; the ready list, and optionally add it to the timeout list.
; Queue the task at the mailbox.
wmsg12:
spl tcb_sema + 1
lda RunningTCB ; remove the task from the ready list
jsr RemoveTaskFromReadyList
stz tcb_sema + 1
wmsg13:
spl tcb_sema + 1
ld r7,TCB_Status,r1
or r7,r7,#TS_WAITMSG ; set task status to waiting
st r7,TCB_Status,r1
st r6,TCB_hWaitMbx,r1 ; set which mailbox is waited for
ld r7,#-1
st r7,TCB_mbq_next,r1 ; adding at tail, so there is no next
ld r7,MBX_TQ_HEAD,r6 ; is there a task que setup at the mailbox ?
bmi wmsg6
ld r7,MBX_TQ_TAIL,r6
st r7,TCB_mbq_prev,r1
sta TCB_mbq_next,r7
sta MBX_TQ_TAIL,r6
inc MBX_TQ_COUNT,r6 ; increment number of tasks queued
wmsg7:
stz tcb_sema + 1
stz mbx_sema + 1
cmp r2,#0 ; check for a timeout
beq wmsg10
wmsg14:
spl tcb_sema + 1
jsr AddToTimeoutList
stz tcb_sema + 1
int #2 ; GoReschedule ; invoke the scheduler
wmsg10:
; At this point either a message was sent to the task, or the task
; timed out. If a message is still not available then the task must
; have timed out. Return a timeout error.
; Note that SendMsg will directly set the message D1, D2 data
; without queing a message at the mailbox (if there is a task
; waiting already). So we cannot just try dequeing a message again.
ldx TCB_MSG_D1,r1
ldy TCB_MSG_D2,r1
ld r4,TCB_Status,r1
bit r4,#TS_WAITMSG ; Is the task still waiting for a message ?
beq wmsg8 ; If not, go return OK status
pop r7 ; Otherwise return timeout error
pop r6
pop r5
pop r4
lda #E_Timeout
rts
; Here there were no prior tasks queued at the mailbox
wmsg6:
ld r7,#-1
st r7,TCB_mbq_prev,r1 ; no previous tasks
st r7,TCB_mbq_next,r1
sta MBX_TQ_HEAD,r6 ; set both head and tail indexes
sta MBX_TQ_TAIL,r6
ld r7,#1
st r7,MBX_TQ_COUNT,r6 ; one task queued
bra wmsg7 ; check for a timeout value
wmsg3:
stz mbx_sema + 1
ldx MSG_D1,r1
ldy MSG_D2,r1
; Add the newly dequeued message to the free messsage list
wmsg5:
spl freemsg_sema + 1
ld r7,FreeMsg
st r7,MSG_LINK,r1
sta FreeMsg
inc nMsgBlk
stz freemsg_sema + 1
wmsg8:
pop r7
pop r6
pop r5
pop r4
lda #E_Ok
rts
wmsg1:
lda #E_BadMbx
rts
wmsg2:
stz mbx_sema + 1
pop r7
pop r6
pop r5
pop r4
lda #E_NotAlloc
rts
;------------------------------------------------------------------------------
; Check for a message at a mailbox. Does not block. This function is a
; convenience wrapper for CheckMsg().
;
; Parameters
; r1=mailbox handle
; Returns:
; r1=E_Ok if everything is ok
; r1=E_NoMsg if no message is available
; r1=E_BadMbx for a bad mailbox number
; r1=E_NotAlloc for a mailbox that isn't allocated
; r2=message D1
; r3=message D2
;------------------------------------------------------------------------------
;
PeekMsg:
ld r2,#0 ; don't remove from queue
jsr CheckMsg
rts
;------------------------------------------------------------------------------
; CheckMsg
; Check for a message at a mailbox. Does not block.
;
; Parameters
; r1=mailbox handle
; r2=remove from queue if present
; Returns:
; r1=E_Ok if everything is ok
; r1=E_NoMsg if no message is available
; r1=E_BadMbx for a bad mailbox number
; r1=E_NotAlloc for a mailbox that isn't allocated
; r2=message D1
; r3=message D2
;------------------------------------------------------------------------------
CheckMsg:
cmp #NR_MBX ; check the mailbox number to make sure
bhs cmsg1 ; that it's sensible
push r4
push r5
spl mbx_sema + 1
ld r5,MBX_OWNER,r1
bmi cmsg2 ; error: no owner
cpx #0 ; are we to dequeue the message ?
php
beq cmsg3
jsr DequeueMsgFromMbx
bra cmsg4
cmsg3:
lda MBX_MQ_HEAD,r1 ; peek the message at the head of the messages queue
cmsg4:
cmp #0
bmi cmsg5
ldx MSG_D1,r1
ldy MSG_D2,r1
plp ; get back dequeue flag
beq cmsg8
cmsg10:
spl freemsg_sema + 1
ld r5,FreeMsg
st r5,MSG_LINK,r1
sta FreeMsg
inc nMsgBlk
stz freemsg_sema + 1
cmsg8:
stz mbx_sema + 1
pop r5
pop r4
lda #E_Ok
rts
cmsg1:
lda #E_BadMbx
rts
cmsg2:
stz mbx_sema + 1
pop r5
pop r4
lda #E_NotAlloc
rts
cmsg5:
stz mbx_sema + 1
pop r5
pop r4
lda #E_NoMsg
rts
;------------------------------------------------------------------------------
; Spinlock interrupt
; Go reschedule tasks if a spinlock is taking too long.
;------------------------------------------------------------------------------
;
spinlock_irq:
cli
ld r0,tcb_sema + 1
beq spi1
cld
pusha
bra resched1
spi1:
rti
;------------------------------------------------------------------------------
; System Call Interrupt
;
; The system call is executed using the caller's system stack.
;
; Stack Frame
; 4,sp: return address
; 3,sp: status register
; 2,sp: r6 save
; 1,sp: r7 save
; 0,sp: r8 save
;------------------------------------------------------------------------------
;
syscall_int:
cli
cld
push r6 ; save off some working registers
push r7
push r8
ld r6,4,sp ; get return address into r6
lb r7,0,r6 ; get static call number parameter into r7
inc r6 ; update return address
st r6,4,sp
; tsr sp,r8 ; save off stack pointer
; ld r6,RunningTCB ; load the stack pointer with the system call
; ld r6,TCB_StackTop,r6 ; stack area
; trs r6,sp
ld r6,(syscall_vectors>>2),r7 ; load the vector into r6
jsr (r6) ; do the system function
; trs r8,sp ; restore the stack pointer
pop r8
pop r7
pop r6
rti
;------------------------------------------------------------------------------
; Reschedule tasks to run without affecting the timeout list timing.
;------------------------------------------------------------------------------
;
reschedule:
cli ; enable interrupts
cld ; clear extended precision mode
pusha ; save off regs on the stack
spl tcb_sema + 1
resched1:
ldx RunningTCB
tsa
sta TCB_SPSave,x ; save stack pointer in TCB
tsr sp8,r1 ; and the eight bit mode stack pointer
sta TCB_SP8Save,x
tsr abs8,r1
sta TCB_ABS8Save,x ; 8 bit emulation base register
lda #TS_RUNNING_BIT ; clear RUNNING status (bit #3)
bmc TCB_Status,x
; lda TCB_StackTop,x ; switch to the system call stack
; tas
jmp SelectTaskToRun
strStartQue:
db 0,0,0,1,0,0,0,2,0,1,0,3,0,0,0,4
; db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;------------------------------------------------------------------------------
; 100 Hz interrupt
; - takes care of "flashing" the cursor
; - decrements timeouts for tasks on timeout list
; - switching tasks
;------------------------------------------------------------------------------
;
MTKTick:
pha
lda #3 ; reset the edge sense circuit
sta PIC_RSTE
pla
inc IRQFlag
; Try and aquire the ready list and tcb. If unsuccessful it means there is
; a system function in the process of updating the list. All we can do is
; return to the system function and let it complete whatever it was doing.
; As if we don't return to the system function we will be deadlocked.
; The tick will be deferred; however if the system function was busy updating
; the ready list, in all likelyhood it's about to call the reschedule
; interrupt.
ld r0,tcb_sema+1
bne p100Hz11
inc missed_ticks
rti
p100Hz11:
cli
cld ; clear extended precision mode
pusha ; save off regs on the stack
lda #96
sta LEDS
lda UserTick
beq p100Hz4
jsr (r1)
cli
p100Hz4:
ldx RunningTCB
tsa ; save off the stack pointer
sta TCB_SPSave,x
tsr sp8,r1 ; and the eight bit mode stack pointer
sta TCB_SP8Save,x
tsr abs8,r1
sta TCB_ABS8Save,x ; 8 bit emulation base register
lda #TS_RUNNING_BIT
bmc TCB_Status,x
lda #97
sta LEDS
; Check the timeout list to see if there are items ready to be removed from
; the list. Also decrement the timeout of the item at the head of the list.
p100Hz15:
ldx TimeoutList
bmi p100Hz12 ; are there any entries in the timeout list ?
lda TCB_Timeout,x
bgt p100Hz14 ; has this entry timed out ?
PopTimeoutList
jsr AddTaskToReadyList
bra p100Hz15 ; go back and see if there's another task to be removed
; there could be a string of tasks to make ready.
p100Hz14:
dea ; decrement the entry's timeout
sub r1,r1,missed_ticks ; account for any missed ticks
stz missed_ticks
sta TCB_Timeout,x
p100Hz12:
; Falls through into selecting a task to run
tck3:
lda #98
sta LEDS
;------------------------------------------------------------------------------
; Search the ready queues for a ready task.
; The search is occasionally started at a lower priority queue in order
; to prevent starvation of lower priority tasks. This is managed by
; using a tick count as an index to a string containing the start que.
;------------------------------------------------------------------------------
;
SelectTaskToRun:
ld r6,#5 ; number of queues to search
ldy IRQFlag ; use the IRQFlag as a buffer index
; lsr r3,r3,#1 ; the LSB is always the same
and r3,r3,#$0F ; counts from 0 to 15
lb r3,strStartQue,y ; get the queue to start search at
sttr2:
lda QNdx0,y
bmi sttr1
lda TCB_NxtRdy,r1 ; Advance the queue index
sta QNdx0,y
; This is the only place the RunningTCB is set (except for initialization).
sta RunningTCB
tax
lda #TS_RUNNING_BIT
bms TCB_Status,x ; flag the task as the running task
lda #99
sta LEDS
lda TCB_ABS8Save,x ; 8 bit emulation base register
trs r1,abs8
lda TCB_SP8Save,x ; get back eight bit stack pointer
trs r1,sp8
ldx TCB_SPSave,x ; get back stack pointer
txs
lda #1
sta tcb_sema
ld r0,iof_switch
beq sttr6
ld r0,iof_sema + 1 ; just ignore the request to switch
beq sttr6 ; I/O focus if the semaphore can't be aquired
stz iof_switch
jsr SwitchIOFocus
stz iof_sema + 1
sttr6:
popa ; restore registers
rti
; Set index to check the next ready list for a task to run
sttr1:
iny
cpy #5
bne sttr5
ldy #0
sttr5:
dec r6
bne sttr2
; Here there were no tasks ready
; This should not be able to happen, so hang the machine (in a lower
; power mode).
sttr3:
ldx #94
stx LEDS
jsr kernel_panic
db "No tasks in ready queue.",0
; Might as well power down the clock and wait for a reset or
; NMI. In the case of an NMI the kernel is reinitialized without
; doing the boot reset.
stp
jmp MTKInitialize
;------------------------------------------------------------------------------
; kernal_panic:
; All this does right now is display the panic message on the screen.
; Parameters:
; inline: string
;------------------------------------------------------------------------------
;
kernel_panic:
pla ; pop the return address off the stack
push r4 ; save off r4
ld r4,r1
kpan2:
lb r1,0,r4 ; get a byte from the code space
add r4,#1 ; increment pointer
and #$FF ; we want only eight bits
beq kpan1 ; is it end of string ?
jsr DisplayChar
bra kpan2
kpan1: ; must update the return address !
jsr CRLF
ld r1,r4 ; get return address into acc
pop r4 ; restore r4
jmp (r1)
include "DeviceDriver.asm"
;------------------------------------------------------------------
;------------------------------------------------------------------
include "Test816.asm"
include "pi_calc816.asm"
;------------------------------------------------------------------
; Kind of a chicken and egg problem here. If there is something
; wrong with the processor, then this code likely won't execute.
;
; put message to screen
; tests pla,sta,ldy,inc,bne,ora,jmp,jmp(abs)
putmsg
pla ; pop the return address off the stack
wdm ; switch to 32 bits
xce
cpu RTF65002
push r4 ; save off r4
or r4,r1,#$FFFF0000 ; set program bank bits; code is at $FFFFxxxx
pm2
add r4,#1 ; increment pointer
lb r1,0,r4 ; get a byte from the code space
and #$FF ; we want only eight bits
beq pm1 ; is it end of string ?
jsr DisplayChar
jmp pm2
pm1 ; must update the return address !
ld r1,r4 ; get return address into acc
pop r4 ; restore r4
clc ; switch back to '816 mode
xce
cpu W65C816S
rep #$30 ; mem,ndx = 16 bits
pha
rts
cpu RTF65002
;------------------------------------------------------------------
; This test program just loop around waiting to recieve a message.
; The message is a pointer to a string to display.
;------------------------------------------------------------------
;
test_mbx_prg:
jsr RequestIOFocus
lda #test_mbx ; where to put mailbox handle
int #4
db 6 ; AllocMbx
ldx #5
jsr PRTNUM
; mStartTask #PRI_LOWEST,#0,#test_mbx_prg2,#0,#0
lda #PRI_LOWEST
ldx #0
ldy #test_mbx_prg2
ld r4,#0
ld r5,#1
int #4
db 1 ; StartTask
tmp2:
lda test_mbx
ldx #100
int #4
db 10 ; WaitMsg
cmp #E_Ok
bne tmp1
txa
jsr DisplayStringB
bra tmp2
tmp1:
ldx #4
jsr PRTNUM
bra tmp2
test_mbx_prg2:
tmp2a:
lda test_mbx
ldx #msg_hello
ldy #0
int #4
db 8 ; PostMsg
bra tmp2a
msg_hello:
db "Hello from RTF",13,10,0
message "DOS.asm"
include "DOS.asm"
cpu RTF65002
message "1298"
include "TinyBasic65002.asm"
message "1640"
org $0FFFFFFF4 ; NMI vector
dw nmirout
org $0FFFFFFF8 ; reset vector, native mode
dw start
end