Line 1... |
Line 1... |
|
|
; ============================================================================
|
; ============================================================================
|
; __
|
; __
|
; \\__/ o\ (C) 2013 Robert Finch, Stratford
|
; \\__/ o\ (C) 2013, 2014 Robert Finch, Stratford
|
; \ __ / All rights reserved.
|
; \ __ / All rights reserved.
|
; \/_// robfinch@opencores.org
|
; \/_// robfinch@opencores.org
|
; ||
|
; ||
|
;
|
;
|
;
|
;
|
Line 20... |
Line 20... |
; You should have received a copy of the GNU General Public License
|
; You should have received a copy of the GNU General Public License
|
; along with this program. If not, see .
|
; along with this program. If not, see .
|
;
|
;
|
; ============================================================================
|
; ============================================================================
|
;
|
;
|
|
cpu RTF65002
|
|
|
CR EQU 0x0D ;ASCII equates
|
CR EQU 0x0D ;ASCII equates
|
LF EQU 0x0A
|
LF EQU 0x0A
|
TAB EQU 0x09
|
TAB EQU 0x09
|
CTRLC EQU 0x03
|
CTRLC EQU 0x03
|
|
BELL EQU 0x07
|
CTRLH EQU 0x08
|
CTRLH EQU 0x08
|
CTRLI EQU 0x09
|
CTRLI EQU 0x09
|
CTRLJ EQU 0x0A
|
CTRLJ EQU 0x0A
|
CTRLK EQU 0x0B
|
CTRLK EQU 0x0B
|
CTRLM EQU 0x0D
|
CTRLM EQU 0x0D
|
CTRLS EQU 0x13
|
CTRLS EQU 0x13
|
CTRLX EQU 0x18
|
CTRLX EQU 0x18
|
|
ESC EQU 0x1b
|
XON EQU 0x11
|
XON EQU 0x11
|
XOFF EQU 0x13
|
XOFF EQU 0x13
|
|
|
; error codes
|
; error codes
|
E_Ok = 0x00
|
E_Ok = 0x00
|
Line 45... |
Line 49... |
E_NotAlloc = 0x09
|
E_NotAlloc = 0x09
|
E_NoMsg = 0x0b
|
E_NoMsg = 0x0b
|
E_Timeout = 0x10
|
E_Timeout = 0x10
|
E_BadAlarm = 0x11
|
E_BadAlarm = 0x11
|
E_NotOwner = 0x12
|
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
|
; resource errors
|
E_NoMoreMbx = 0x40
|
E_NoMoreMbx = 0x40
|
E_NoMoreMsgBlks = 0x41
|
E_NoMoreMsgBlks = 0x41
|
E_NoMoreAlarmBlks =0x44
|
E_NoMoreAlarmBlks =0x44
|
E_NoMoreTCBs = 0x45
|
E_NoMoreTCBs = 0x45
|
|
E_NoMem = 12
|
|
|
; task status
|
; task status
|
TS_NONE =0
|
TS_NONE =0
|
TS_TIMEOUT =1
|
TS_TIMEOUT =1
|
TS_WAITMSG =2
|
TS_WAITMSG =2
|
TS_PREEMPT =4
|
TS_PREEMPT =4
|
TS_RUNNING =8
|
TS_RUNNING =8
|
TS_READY =16
|
TS_READY =16
|
TS_SLEEP =32
|
TS_SLEEP =32
|
|
|
MAX_TASKNO = 255
|
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_NAME =0x00 ; file name
|
DIRENT_EXT =0x1C ; file name extension
|
DIRENT_EXT =0x1C ; file name extension
|
DIRENT_ATTR =0x20 ; attributes
|
DIRENT_ATTR =0x20 ; attributes
|
DIRENT_DATETIME =0x28
|
DIRENT_DATETIME =0x28
|
Line 142... |
Line 171... |
COLORSCR EQU 0xFFD10000
|
COLORSCR EQU 0xFFD10000
|
TEXTREG EQU 0xFFDA0000
|
TEXTREG EQU 0xFFDA0000
|
TEXT_COLS EQU 0x0
|
TEXT_COLS EQU 0x0
|
TEXT_ROWS EQU 0x1
|
TEXT_ROWS EQU 0x1
|
TEXT_CURPOS EQU 11
|
TEXT_CURPOS EQU 11
|
|
TEXT_CURCTL EQU 8
|
|
BMP_CLUT EQU $FFDC5800
|
KEYBD EQU 0xFFDC0000
|
KEYBD EQU 0xFFDC0000
|
KEYBDCLR EQU 0xFFDC0001
|
KEYBDCLR EQU 0xFFDC0001
|
PIC EQU 0xFFDC0FF0
|
PIC EQU 0xFFDC0FF0
|
PIC_IE EQU 0xFFDC0FF1
|
PIC_IE EQU 0xFFDC0FF1
|
PIC_ES EQU 0xFFDC0FF4
|
PIC_ES EQU 0xFFDC0FF4
|
PIC_RSTE EQU 0xFFDC0FF5
|
PIC_RSTE EQU 0xFFDC0FF5
|
TASK_SELECT EQU 0xFFDD0008
|
TASK_SELECT EQU 0xFFDD0008
|
|
|
RQ_SEMA EQU 0xFFDB0000
|
RQ_SEMA EQU 0xFFDB0000
|
TO_SEMA EQU 0xFFDB0010
|
to_sema EQU 0xFFDB0010
|
SERIAL_SEMA EQU 0xFFDB0020
|
SERIAL_SEMA EQU 0xFFDB0020
|
KEYBD_SEMA EQU 0xFFDB0030
|
keybd_sema EQU 0xFFDB0030
|
IOF_LIST_SEMA EQU 0xFFDB0040
|
iof_sema EQU 0xFFDB0040
|
MBX_SEMA EQU 0xFFDB0050
|
mbx_sema EQU 0xFFDB0050
|
MEM_SEMA EQU 0xFFDB0060
|
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
|
SPIMASTER EQU 0xFFDC0500
|
SPI_MASTER_VERSION_REG EQU 0x00
|
SPI_MASTER_VERSION_REG EQU 0x00
|
SPI_MASTER_CONTROL_REG EQU 0x01
|
SPI_MASTER_CONTROL_REG EQU 0x01
|
SPI_TRANS_TYPE_REG EQU 0x02
|
SPI_TRANS_TYPE_REG EQU 0x02
|
Line 188... |
Line 231... |
SPI_READ_NO_ERROR EQU 0x00
|
SPI_READ_NO_ERROR EQU 0x00
|
SPI_WRITE_NO_ERROR EQU 0x00
|
SPI_WRITE_NO_ERROR EQU 0x00
|
RW_READ_SD_BLOCK EQU 0x02
|
RW_READ_SD_BLOCK EQU 0x02
|
RW_WRITE_SD_BLOCK EQU 0x03
|
RW_WRITE_SD_BLOCK EQU 0x03
|
|
|
UART EQU 0xFFDC0A00
|
|
UART_LS EQU 0xFFDC0A01
|
|
UART_MS EQU 0xFFDC0A02
|
|
UART_IS EQU 0xFFDC0A03
|
|
UART_IE EQU 0xFFDC0A04
|
|
UART_MC EQU 0xFFDC0A06
|
|
UART_CM1 EQU 0xFFDC0A09
|
|
UART_CM2 EQU 0xFFDC0A0A
|
|
UART_CM3 EQU 0xFFDC0A0B
|
|
txempty EQU 0x40
|
|
rxfull EQU 0x01
|
|
|
|
CONFIGREC EQU 0xFFDCFFF0
|
CONFIGREC EQU 0xFFDCFFF0
|
CR_CLOCK EQU 0xFFDCFFF4
|
CR_CLOCK EQU 0xFFDCFFF4
|
GACCEL EQU 0xFFDAE000
|
GACCEL EQU 0xFFDAE000
|
GA_X0 EQU 0xFFDAE002
|
GA_X0 EQU 0xFFDAE002
|
GA_Y0 EQU 0xFFDAE003
|
GA_Y0 EQU 0xFFDAE003
|
Line 280... |
Line 311... |
DATETIME_SNAPSHOT EQU 0xFFDC0405
|
DATETIME_SNAPSHOT EQU 0xFFDC0405
|
|
|
SPRITEREGS EQU 0xFFDAD000
|
SPRITEREGS EQU 0xFFDAD000
|
SPRRAM EQU 0xFFD80000
|
SPRRAM EQU 0xFFD80000
|
|
|
THRD_AREA EQU 0x04000000 ; threading area 0x04000000-0x40FFFFF
|
THRD_AREA EQU 0x00000000 ; threading area 0x04000000-0x40FFFFF
|
BITMAPSCR EQU 0x04100000
|
BITMAPSCR EQU 0x00100000
|
SECTOR_BUF EQU 0x05FBEC00
|
SECTOR_BUF EQU 0x01FBEC00
|
BIOS_STACKS EQU 0x05FC0000 ; room for 256 1kW stacks
|
|
BIOS_SCREENS EQU 0x05C00000 ; 0x05C00000 to 0x05DFFFFF
|
|
|
|
BYTE_SECTOR_BUF EQU SECTOR_BUF<<2
|
BYTE_SECTOR_BUF EQU SECTOR_BUF<<2
|
PROG_LOAD_AREA EQU 0x4180000<<2
|
PROG_LOAD_AREA EQU 0x0300000<<2
|
|
|
FCBs EQU 0x5F40000 ; room for 128 FCB's
|
FCBs EQU 0x1F40000 ; room for 128 FCB's
|
|
|
FATOFFS EQU 0x5F50000 ; offset into FAT on card
|
FATOFFS EQU 0x1F50000 ; offset into FAT on card
|
FATBUF EQU 0x5F60000
|
FATBUF EQU 0x1F60000
|
DIRBUF EQU 0x5F70000
|
DIRBUF EQU 0x1F70000
|
eth_rx_buffer EQU 0x5F80000
|
eth_rx_buffer EQU 0x1F80000
|
eth_tx_buffer EQU 0x5F84000
|
eth_tx_buffer EQU 0x1F84000
|
|
|
; Mailboxes, room for 2048
|
; Mailboxes, room for 2048
|
MBX_LINK EQU 0x05F90000
|
.bss
|
MBX_TQ_HEAD EQU 0x05F90800
|
.org 0x01F90000
|
MBX_TQ_TAIL EQU 0x05F91000
|
NR_MBX EQU $800
|
MBX_MQ_HEAD EQU 0x05F91800
|
MBX_LINK fill.w NR_MBX,0 ; link to next mailbox in list (free list)
|
MBX_MQ_TAIL EQU 0x05F92000
|
MBX_TQ_HEAD fill.w NR_MBX,0 ; head of task queue
|
MBX_TQ_COUNT EQU 0x05F92800
|
MBX_TQ_TAIL fill.w NR_MBX,0
|
MBX_MQ_SIZE EQU 0x05F93000
|
MBX_MQ_HEAD fill.w NR_MBX,0 ; head of message queue
|
MBX_MQ_COUNT EQU 0x05F93800
|
MBX_MQ_TAIL fill.w NR_MBX,0
|
MBX_MQ_MISSED EQU 0x05F94000
|
MBX_TQ_COUNT fill.w NR_MBX,0 ; count of queued threads
|
MBX_OWNER EQU 0x05F94800
|
MBX_MQ_SIZE fill.w NR_MBX,0 ; number of messages that may be queued
|
MBX_MQ_STRATEGY EQU 0x05F95000
|
MBX_MQ_COUNT fill.w NR_MBX,0 ; count of messages that are queued
|
MBX_RESV EQU 0x05F95800
|
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
|
; Messages, room for 8kW (8,192) messages
|
MBX_MQ_STRATEGY fill.w NR_MBX,0 ; message queueing strategy
|
MSG_LINK EQU 0x05FA0000
|
MBX_RESV fill.w NR_MBX,0
|
MSG_D1 EQU 0x05FA2000
|
|
MSG_D2 EQU 0x05FA4000
|
; Messages, room for 64kW (16,384) messages
|
MSG_TYPE EQU 0x05FA6000
|
.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
|
|
|
; Task control blocks, room for 256 tasks
|
.bss
|
TCB_NxtRdy EQU 0x05FBE100 ; next task on ready / timeout list
|
.org 0x01FBA000
|
TCB_PrvRdy EQU 0x05FBE200 ; previous task on ready / timeout list
|
|
TCB_NxtTCB EQU 0x05FBE300
|
|
TCB_Timeout EQU 0x05FBE400
|
|
TCB_Priority EQU 0x05FBE500
|
|
TCB_MSGPTR_D1 EQU 0x05FBE600
|
|
TCB_MSGPTR_D2 EQU 0x05FBE700
|
|
TCB_hJCB EQU 0x05FBE800
|
|
TCB_Status EQU 0x05FBE900
|
|
TCB_CursorRow EQU 0x05FBD100
|
|
TCB_CursorCol EQU 0x05FBD200
|
|
TCB_hWaitMbx EQU 0x05FBD300 ; handle of mailbox task is waiting at
|
|
TCB_mbq_next EQU 0x05FBD400 ; mailbox queue next
|
|
TCB_mbq_prev EQU 0x05FBD500 ; mailbox queue previous
|
|
TCB_iof_next EQU 0x05FBD600
|
|
TCB_iof_prev EQU 0x05FBD700
|
|
TCB_SP8Save EQU 0x05FBD800 ; TCB_SP8Save area
|
|
TCB_SPSave EQU 0x05FBD900 ; TCB_SPSave area
|
|
TCB_ABS8Save EQU 0x05FBDA00
|
|
TCB_mmu_map EQU 0x05FBDB00
|
|
|
|
KeybdHead EQU 0x05FBEA00
|
|
KeybdTail EQU 0x05FBEB00
|
|
KeybdEcho EQU 0x05FBEC00
|
|
KeybdBad EQU 0x05FBED00
|
|
KeybdAck EQU 0x05FBEE00
|
|
KeybdLocks EQU 0x05FBEF00
|
|
KeybdBuffer EQU 0x05FBF000 ; buffer is 16 chars
|
|
|
|
HeapStart EQU 0x04200000
|
; Task control blocks, room for 256 tasks
|
HeapEnd EQU 0x043FFFFF
|
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
|
; Bitmap of tasks requesting the I/O focus
|
;
|
;
|
IOFocusTbl EQU 0x05FBD000
|
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:
|
; EhBASIC vars:
|
;
|
;
|
NmiBase EQU 0xDC
|
NmiBase EQU 0xDC
|
IrqBase EQU 0xDF
|
IrqBase EQU 0xDF
|
|
|
; BIOS vars at the top of the 8kB scratch memory
|
; BIOS vars at the top of the 8kB scratch memory
|
;
|
;
|
; TinyBasic AREA = 0xF00 to 0xF7F
|
; TinyBasic AREA = 0x6C0 to 0x77F
|
|
|
QNdx0 EQU 0xF80
|
PageMap EQU 0x600
|
QNdx1 EQU QNdx0+1
|
PageMapEnd EQU 0x63F
|
QNdx2 EQU QNdx1+1
|
PageMap2 EQU 0x640
|
QNdx3 EQU QNdx2+1
|
PageMap2End EQU 0x67F
|
QNdx4 EQU QNdx3+1
|
mem_pages_free EQU 0x680
|
FreeTCB EQU QNdx4+1
|
|
TimeoutList EQU FreeTCB+1
|
bss
|
RunningTCB EQU TimeoutList+1
|
org 0x780
|
FreeMbx EQU RunningTCB + 1
|
|
nMailbox EQU FreeMbx + 1
|
QNdx0 dw 0
|
FreeMsg EQU nMailbox + 1
|
QNdx1 dw 0
|
nMsgBlk EQU FreeMsg + 1
|
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.
|
; The IO focus list is a doubly linked list formed into a ring.
|
;
|
;
|
IOFocusNdx EQU nMsgBlk + 1
|
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
|
|
|
IrqSource EQU 0xF98
|
macro mReleaseTCB
|
|
lda #33
|
|
ldx #$FFFFFFFE
|
|
txy
|
|
jsr SendMsg
|
|
endm
|
|
|
JMPTMP EQU 0xFA0
|
macro mAquireMBX
|
SP8Save EQU 0xFAE
|
lda #34
|
SRSave EQU 0xFAF
|
ldx #0
|
R1Save EQU 0xFB0
|
txy
|
R2Save EQU 0xFB1
|
ld r4,#-1
|
R3Save EQU 0xFB2
|
jsr WaitMsg
|
R4Save EQU 0xFB3
|
endm
|
R5Save EQU 0xFB4
|
|
R6Save EQU 0xFB5
|
|
R7Save EQU 0xFB6
|
|
R8Save EQU 0xFB7
|
|
R9Save EQU 0xFB8
|
|
R10Save EQU 0xFB9
|
|
R11Save EQU 0xFBA
|
|
R12Save EQU 0xFBB
|
|
R13Save EQU 0xFBC
|
|
R14Save EQU 0xFBD
|
|
R15Save EQU 0xFBE
|
|
SPSave EQU 0xFBF
|
|
|
|
CharColor EQU 0xFC0
|
|
ScreenColor EQU 0xFC1
|
|
CursorRow EQU 0xFC2
|
|
CursorCol EQU 0xFC3
|
|
CursorFlash EQU 0xFC4
|
|
Milliseconds EQU 0xFC5
|
|
IRQFlag EQU 0xFC6
|
|
RdyQueTick EQU 0xFC7
|
|
eth_unique_id EQU 0xFC8
|
|
LineColor EQU 0xFC9
|
|
QIndex EQU 0xFCA
|
|
ROMcs EQU 0xFCB
|
|
mmu_present EQU 0xFCC
|
|
TestTask EQU 0xFCD
|
|
KeybdIsSetup EQU 0xFCE
|
|
|
|
Uart_rxfifo EQU 0x05FBC000
|
|
Uart_rxhead EQU 0xFD0
|
|
Uart_rxtail EQU 0xFD1
|
|
Uart_ms EQU 0xFD2
|
|
Uart_rxrts EQU 0xFD3
|
|
Uart_rxdtr EQU 0xFD4
|
|
Uart_rxxon EQU 0xFD5
|
|
Uart_rxflow EQU 0xFD6
|
|
Uart_fon EQU 0xFD7
|
|
Uart_foff EQU 0xFD8
|
|
Uart_txrts EQU 0xFD9
|
|
Uart_txdtr EQU 0xFDA
|
|
Uart_txxon EQU 0xFDB
|
|
Uart_txxonoff EQU 0xFDC
|
|
|
|
startSector EQU 0xFF0
|
macro mReleaseMBX
|
|
lda #34
|
|
ldx #$FFFFFFFE
|
|
txy
|
|
jsr SendMsg
|
|
endm
|
|
|
|
|
cpu rtf65002
|
cpu rtf65002
|
code
|
code
|
|
|
Line 457... |
Line 777... |
dw ExitTask
|
dw ExitTask
|
dw SetKeyboardEcho
|
dw SetKeyboardEcho
|
dw Sleep
|
dw Sleep
|
dw do_load
|
dw do_load
|
dw do_save
|
dw do_save
|
|
dw ICacheInvalidateAll
|
|
dw ICacheInvalidateLine
|
|
|
org $FFFF8400 ; leave room for 256 vectors
|
org $FFFF8400 ; leave room for 256 vectors
|
message "cold start point"
|
message "cold start point"
|
KeybdRST
|
KeybdRST
|
start
|
start
|
Line 473... |
Line 795... |
trs r0,abs8 ; set 8 bit mode absolute address offset
|
trs r0,abs8 ; set 8 bit mode absolute address offset
|
lda #3
|
lda #3
|
trs r1,cc ; enable dcache and icache
|
trs r1,cc ; enable dcache and icache
|
jsr ROMChecksum
|
jsr ROMChecksum
|
sta ROMcs
|
sta ROMcs
|
|
jsr SetupCacheInvalidate
|
|
jsr InitDevices
|
stz mmu_present ; assume no mmu
|
stz mmu_present ; assume no mmu
|
lda CONFIGREC
|
lda CONFIGREC
|
bit #4096
|
bit #4096
|
beq st_nommu
|
beq st_nommu
|
jsr InitMMU ; setup the maps and enable the mmu
|
jsr InitMMU ; setup the maps and enable the mmu
|
lda #1
|
lda #1
|
sta mmu_present
|
sta mmu_present
|
st_nommu:
|
st_nommu:
|
jsr MemInit ; Initialize the heap
|
jsr MemInit ; Initialize the heap
|
|
stz iof_switch
|
|
|
lda #2
|
lda #2
|
sta LEDS
|
sta LEDS
|
|
|
; setup interrupt vectors
|
; setup interrupt vectors
|
ldx #$05FB0001 ; interrupt vector table from $5FB0000 to $5FB01FF
|
ldx #$01FB8001 ; interrupt vector table from $5FB0000 to $5FB01FF
|
; also sets nmoi policy (native mode on interrupt)
|
; also sets nmoi policy (native mode on interrupt)
|
trs r2,vbr
|
trs r2,vbr
|
dex
|
and r2,r2,#-2 ; mask off policy bit
|
phx
|
phx
|
txy ; y = pointer to vector table
|
txy ; y = pointer to vector table
|
lda #511 ; 512 vectors to setup
|
lda #511 ; 512 vectors to setup
|
ldx #brk_rout ; point vector to brk routine
|
ldx #brk_rout ; point vector to brk routine
|
stos
|
stos
|
Line 502... |
Line 827... |
plx
|
plx
|
lda #brk_rout
|
lda #brk_rout
|
sta (x)
|
sta (x)
|
lda #slp_rout
|
lda #slp_rout
|
sta 1,x
|
sta 1,x
|
lda #reschedule
|
lda #reschedule ; must be initialized after vectors are initialized to the break vector
|
sta 2,x
|
sta 2,x
|
|
lda #spinlock_irq
|
|
sta 3,x
|
|
lda #syscall_int
|
|
sta 4,x
|
lda #KeybdRST
|
lda #KeybdRST
|
sta 448+1,x
|
sta 448+1,x
|
lda #p1000Hz
|
lda #p1000Hz
|
sta 448+2,x
|
sta 448+2,x
|
lda #p100Hz
|
lda #MTKTick
|
sta 448+3,x
|
sta 448+3,x
|
lda #KeybdIRQ
|
lda #KeybdIRQ
|
sta 448+15,x
|
sta 448+15,x
|
lda #SerialIRQ
|
lda #SerialIRQ
|
sta 448+8,x
|
sta 448+8,x
|
Line 531... |
Line 860... |
|
|
ldx #0
|
ldx #0
|
stz IrqBase ; support for EhBASIC's interrupt mechanism
|
stz IrqBase ; support for EhBASIC's interrupt mechanism
|
stz NmiBase
|
stz NmiBase
|
|
|
lda #-1
|
jsr ($FFFFC000>>2) ; Initialize multi-tasking
|
sta TimeoutList ; no entries in timeout list
|
lda #TickRout ; setup tick routine
|
sta QNdx0
|
sta UserTick
|
sta QNdx1
|
|
sta QNdx2
|
|
sta QNdx3
|
|
sta QNdx4
|
|
|
|
|
|
; Initialize IO Focus List
|
|
;
|
|
lda #7
|
|
ldx #0
|
|
ldy #IOFocusTbl
|
|
stos
|
|
|
|
lda #255
|
|
ldx #-1
|
|
ldy #TCB_iof_next
|
|
stos
|
|
lda #255
|
|
ldx #-1
|
|
ldy #TCB_iof_prev
|
|
stos
|
|
|
|
; Initialize free message list
|
|
lda #8192
|
|
sta nMsgBlk
|
|
stz FreeMsg
|
|
ldx #0
|
|
lda #1
|
lda #1
|
st4:
|
sta iof_sema
|
sta MSG_LINK,x
|
|
ina
|
|
inx
|
|
cpx #8192
|
|
bne st4
|
|
lda #-1
|
|
sta MBX_LINK+8191
|
|
|
|
; Initialize free mailbox list
|
lda #(DCB_SIZE * NR_DCB)-1
|
lda #2048
|
|
sta nMailbox
|
|
|
|
stz FreeMbx
|
|
ldx #0
|
ldx #0
|
lda #1
|
ldy #DCBs
|
st3:
|
stos
|
sta MBX_LINK,x
|
|
ina
|
|
inx
|
|
cpx #2048
|
|
bne st3
|
|
lda #-1
|
|
sta MBX_LINK+2047
|
|
|
|
; 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
|
|
stz QNdx0 ; insert at priority 0
|
|
stz TCB_iof_next ; manually build the IO focus list
|
|
stz TCB_iof_prev
|
|
stz IOFocusNdx ; task #0 has the focus
|
|
lda #1
|
|
sta IOFocusTbl ; set the task#0 request bit
|
|
lda #0
|
|
sta TCB_Priority
|
|
stz TCB_Timeout
|
|
lda #TS_RUNNING|TS_READY
|
|
sta TCB_Status
|
|
stz TCB_CursorRow
|
|
stz TCB_CursorCol
|
|
|
|
lda #1
|
|
sta MBX_SEMA
|
|
sta IOF_LIST_SEMA
|
|
sta RQ_SEMA ; set ready queue semaphore
|
|
sta TO_SEMA ; set timeout list semaphore
|
|
|
|
lda #$CE ; CE =blue on blue FB = grey on grey
|
lda #$CE ; CE =blue on blue FB = grey on grey
|
sta ScreenColor
|
sta ScreenColor
|
sta CharColor
|
sta CharColor
|
sta CursorFlash
|
sta CursorFlash
|
jsr ClearScreen
|
jsr ClearScreen
|
|
jsr InitBMP
|
jsr ClearBmpScreen
|
jsr ClearBmpScreen
|
jsr PICInit
|
jsr PICInit
|
; Enable interrupts
|
; Enable interrupts
|
; This will likely cause an interrupt right away because the timer
|
; This will likely cause an interrupt right away because the timer
|
; pulses run since power-up.
|
; pulses run since power-up.
|
cli
|
cli
|
lda #4
|
; mStartTask #PRI_LOWEST,#0,#IdleTask,#0,#0
|
|
lda #PRI_LOWEST
|
ldx #0
|
ldx #0
|
ldy #IdleTask
|
ldy #IdleTask
|
jsr StartTask
|
ld r4,#0
|
|
ld r5,#0
|
|
int #4
|
|
db 1
|
lda CONFIGREC ; do we have a serial port ?
|
lda CONFIGREC ; do we have a serial port ?
|
bit #32
|
bit #32
|
beq st7
|
beq st7
|
; 19200 * 16
|
; 19200 * 16
|
;-------------
|
;-------------
|
Line 666... |
Line 916... |
; Keyboard initialization must take place after interrupts are
|
; Keyboard initialization must take place after interrupts are
|
; enabled.
|
; enabled.
|
cli
|
cli
|
lda #14
|
lda #14
|
sta LEDS
|
sta LEDS
|
jsr KeybdInit
|
stz keybdIsSetup
|
lda #1
|
; mStartTask #PRI_NORMAL,#0,#KeybdSetup,#0
|
sta KeybdEcho
|
lda #PRI_NORMAL
|
|
ldx #0
|
|
ldy #KeybdSetup
|
|
ld r4,#0
|
|
ld r5,#0
|
|
int #4
|
|
db 1
|
|
; lea r3,KeybdStatusLEDs
|
|
; jsr StartTask
|
lda #6
|
lda #6
|
sta LEDS
|
sta LEDS
|
|
|
; The following must be after interrupts are enabled.
|
; The following must be after interrupts are enabled.
|
lda #9
|
lda #9
|
Line 700... |
Line 958... |
ldy #Beep
|
ldy #Beep
|
; jsr StartTask
|
; jsr StartTask
|
st6:
|
st6:
|
lda #11
|
lda #11
|
sta LEDS
|
sta LEDS
|
|
stz BASIC_SESSION
|
jmp Monitor
|
jmp Monitor
|
st1
|
st1
|
jsr KeybdGetCharDirect
|
jsr KeybdGetCharDirect
|
bra st1
|
bra st1
|
stp
|
stp
|
Line 711... |
Line 970... |
|
|
msgStart
|
msgStart
|
db "RTF65002 system starting.",$0d,$0a,00
|
db "RTF65002 system starting.",$0d,$0a,00
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; InitMMU
|
; SetupCacheInvalidate:
|
;
|
;
|
; Initialize the 64 maps of the MMU.
|
; Setup the cache invalidate routines. Cache's in the FPGA don't have
|
; Initially all the maps are set the same:
|
; invalidate logic as it cannot be efficiently implemented. So we handle
|
; Virtual Page Physical Page
|
; cache invalidations using software. By calling a software routine, or
|
; 000-255 000-255
|
; accessing data in the setup cache invalidate area, the cache will be
|
; 256-511 1792-2047
|
; effectively invalidated. This works for caches up to 16kB. (4kW)
|
; Note that there are only 512 virtual pages per map, and 2048 real
|
|
; physical pages of memory. This limits maps to 32MB.
|
|
; This range includes the BIOS assigned stacks for the tasks and tasks
|
|
; virtual video buffers. It also includes the bitmap screen buffer.
|
|
; Note that physical pages 256 to 1791 are not mapped, but do exist.
|
|
; If changing the maps the last 128 pages (8MB) of the map should always point
|
|
; to the BIOS area. Don't change map entries 384-511 or the system may
|
|
; crash.
|
|
; If the rts at the end of this routine works, then memory was mapped
|
|
; successfully.
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
InitMMU:
|
|
lda #1
|
message "SetupCacheInvalidate"
|
sta MMU_KVMMU+1
|
SetupCacheInvalidate:
|
dea
|
lda #4095
|
sta MMU_KVMMU
|
ldx #$EAEAEAEA ; fill memory with NOP's
|
immu1:
|
ldy #cacheInvRout
|
sta MMU_AKEY ; set access key for map
|
stos
|
ldy #0 ;
|
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
|
ldx #0
|
immu2:
|
.0001:
|
; set the first 256 pages to physical page 0-255
|
ld r0,cacheInvRout,x
|
; set the last 256 pages to physical page 1792-2047
|
|
ld r4,r3
|
|
bit r3,#$0100
|
|
beq immu3
|
|
add r4,r4,#1536
|
|
immu3:
|
|
st r4,MMU,x
|
|
iny
|
|
inx
|
inx
|
cpx #512
|
cpx #$FFF
|
bne immu2
|
bls .0001
|
ina
|
plx
|
cmp #64 ; 64 MMU maps
|
rts
|
bne immu1
|
|
stz MMU_OKEY ; set operating key to map #0
|
|
lda #2
|
|
sta MMU_FUSE ; set fuse to 2 clocks before mapping starts
|
|
nop
|
|
nop
|
|
|
|
EnableMMUMapping:
|
;------------------------------------------------------------------------------
|
|
; DCacheInvalidateLine:
|
|
;
|
|
; Call to invalidate a specific cache line in the data cache.
|
|
;
|
|
; Parameters:
|
|
; r1 = data address in line to invalidate
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
DCacheInvalidateLine:
|
pha
|
pha
|
lda #1
|
and #$FFF
|
sta MMU_MAPEN
|
ld r0,cacheInvRout,r1
|
pla
|
pla
|
rts
|
rts
|
DisableMMUMapping:
|
|
stz MMU_MAPEN
|
;------------------------------------------------------------------------------
|
|
;------------------------------------------------------------------------------
|
|
InitBMP:
|
|
ldx #0
|
|
ibmp1:
|
|
tsr LFSR,r1
|
|
sta BMP_CLUT,x
|
|
inx
|
|
cpx #512
|
|
bne ibmp1
|
rts
|
rts
|
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; The ROM contents are summed up to ensure the ROM is okay.
|
; The ROM contents are summed up to ensure the ROM is okay.
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
ROMChecksum:
|
ROMChecksum:
|
lda #0
|
lda #0
|
Line 821... |
Line 1113... |
phy
|
phy
|
push r4
|
push r4
|
lda #msgTaskList
|
lda #msgTaskList
|
jsr DisplayStringB
|
jsr DisplayStringB
|
ldy #0
|
ldy #0
|
php
|
spl tcb_sema + 1
|
sei
|
|
dtl2:
|
dtl2:
|
lda QNdx0,y
|
lda QNdx0,y
|
ld r4,r1
|
ld r4,r1
|
bmi dtl1
|
bmi dtl1
|
dtl3:
|
dtl3:
|
Line 865... |
Line 1156... |
bne dtl3
|
bne dtl3
|
dtl1:
|
dtl1:
|
iny
|
iny
|
cpy #5
|
cpy #5
|
bne dtl2
|
bne dtl2
|
plp
|
stz tcb_sema + 1
|
pop r4
|
pop r4
|
ply
|
ply
|
plx
|
plx
|
pla
|
pla
|
rts
|
rts
|
Line 885... |
Line 1176... |
phx
|
phx
|
phy
|
phy
|
push r4
|
push r4
|
lda #msgTimeoutList
|
lda #msgTimeoutList
|
jsr DisplayStringB
|
jsr DisplayStringB
|
php
|
ldy #11
|
sei
|
|
dtol2:
|
dtol2:
|
lda TimeoutList
|
lda TimeoutList
|
ld r4,r1
|
ld r4,r1
|
bmi dtol1
|
bmi dtol1
|
|
spl tcb_sema + 1
|
dtol3:
|
dtol3:
|
|
dey
|
|
beq dtol1
|
ld r1,r4
|
ld r1,r4
|
ldx #3
|
ldx #3
|
jsr PRTNUM
|
jsr PRTNUM
|
lda #' '
|
lda #' '
|
jsr DisplayChar
|
jsr DisplayChar
|
jsr DisplayChar
|
jsr DisplayChar
|
jsr DisplayChar
|
jsr DisplayChar
|
ld r1,r4
|
ld r1,r4
|
ldx #3
|
ldx #3
|
lda TCB_PrvRdy,r4
|
lda TCB_PrvTo,r4
|
jsr PRTNUM
|
jsr PRTNUM
|
lda #' '
|
lda #' '
|
jsr DisplayChar
|
jsr DisplayChar
|
ldx #3
|
ldx #3
|
lda TCB_NxtRdy,r4
|
lda TCB_NxtTo,r4
|
jsr PRTNUM
|
jsr PRTNUM
|
lda #' '
|
lda #' '
|
jsr DisplayChar
|
jsr DisplayChar
|
lda TCB_Timeout,r4
|
lda TCB_Timeout,r4
|
jsr DisplayWord
|
jsr DisplayWord
|
jsr CRLF
|
jsr CRLF
|
ld r4,TCB_NxtRdy,r4
|
ld r4,TCB_NxtTo,r4
|
bpl dtol3
|
bpl dtol3
|
dtol1:
|
dtol1:
|
plp
|
stz tcb_sema + 1
|
pop r4
|
pop r4
|
ply
|
ply
|
plx
|
plx
|
pla
|
pla
|
rts
|
rts
|
Line 933... |
Line 1226... |
message "DumpIOFocusList"
|
message "DumpIOFocusList"
|
DumpIOFocusList:
|
DumpIOFocusList:
|
pha
|
pha
|
phx
|
phx
|
phy
|
phy
|
php
|
|
sei
|
|
lda #msgIOFocusList
|
lda #msgIOFocusList
|
jsr DisplayStringB
|
jsr DisplayStringB
|
|
spl iof_sema + 1
|
lda IOFocusNdx
|
lda IOFocusNdx
|
diofl2:
|
diofl2:
|
bmi diofl1
|
beq diofl1
|
tay
|
tay
|
ldx #3
|
ldx #3
|
jsr PRTNUM
|
jsr PRTNUM
|
lda #' '
|
lda #' '
|
jsr DisplayChar
|
jsr DisplayChar
|
lda TCB_iof_prev,y
|
lda JCB_iof_prev,y
|
ldx #3
|
ldx #3
|
jsr PRTNUM
|
jsr PRTNUM
|
lda #' '
|
lda #' '
|
jsr DisplayChar
|
jsr DisplayChar
|
lda TCB_iof_next,y
|
lda JCB_iof_next,y
|
ldx #3
|
ldx #3
|
jsr PRTNUM
|
jsr PRTNUM
|
jsr CRLF
|
jsr CRLF
|
lda TCB_iof_next,y
|
lda JCB_iof_next,y
|
cmp IOFocusNdx
|
cmp IOFocusNdx
|
bne diofl2
|
bne diofl2
|
|
|
diofl1:
|
diofl1:
|
plp
|
stz iof_sema + 1
|
ply
|
ply
|
plx
|
plx
|
pla
|
pla
|
rts
|
rts
|
|
|
Line 983... |
Line 1275... |
|
|
msgRunningTCB:
|
msgRunningTCB:
|
db CR,LF,"RunningTCB is bad.",CR,LF,0
|
db CR,LF,"RunningTCB is bad.",CR,LF,0
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; IdleTask
|
; Get the handle of the currently running job.
|
;
|
|
; 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
|
GetCurrentJob:
|
it2:
|
ld r1,RunningTCB
|
inc TEXTSCR+111 ; increment IDLE active flag
|
ld r1,TCB_hJCB,r1 ; get the handle
|
ldx TestTask
|
rts
|
and r2,r2,#$FF
|
|
beq it1
|
|
lda TCB_Status,x
|
|
cmp #TS_SLEEP
|
|
bne it1
|
|
txa
|
|
jsr KillTask
|
|
it1:
|
|
inc TestTask
|
|
cli ; enable interrupts
|
|
wai ; wait for one to happen
|
|
bra it2
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; StartTask
|
; Get a pointer to the JCB for the currently running task.
|
;
|
;------------------------------------------------------------------------------
|
; 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:
|
GetPtrCurrentJCB:
|
; r1 = task priority
|
jsr GetCurrentJob
|
; r2 = start flags
|
and r1,r1,#NR_JCB-1 ; and convert it to a pointer
|
; r3 = start address
|
; mul r1,r1,#JCB_Size
|
|
asl r1,r1,#JCB_LogSize ; 256 words
|
|
add r1,r1,#JCBs
|
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
message "StartTask"
|
; Get the location of the screen and screen attribute memory. The location
|
StartTask:
|
; 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
|
pha
|
phx
|
phx
|
phy
|
phy
|
push r4
|
push r4
|
push r5
|
ldx IOFocusNdx ; compute virtual screen location
|
push r6
|
beq cvss3
|
push r7
|
; copy screen chars
|
push r8
|
lda #4095 ; number of words to copy-1
|
ld r6,r1 ; r6 = task priority
|
ldx JCB_pVirtVid,x
|
ld r8,r2 ; r8 = flag register value on startup
|
ldy #TEXTSCR
|
|
mvn
|
; get a free TCB
|
; now copy the color codes
|
;
|
lda #4095
|
php
|
ldx IOFocusNdx
|
sei
|
ldx JCB_pVirtVidAttr,x
|
lda FreeTCB ; get free tcb list pointer
|
ldy #TEXTSCR+$10000
|
bmi stask1
|
mvn
|
tax
|
cvss3:
|
lda TCB_NxtTCB,x
|
; reset the cursor position in the text controller
|
sta FreeTCB ; update the FreeTCB list pointer
|
ldy IOFocusNdx
|
plp
|
ldx JCB_CursorRow,y
|
txa ; acc = TCB index (task number)
|
lda TEXTREG+TEXT_COLS
|
|
mul r2,r2,r1
|
; setup the stack for the task
|
add r2,r2,JCB_CursorCol,y
|
; Zap the stack memory.
|
stx TEXTREG+TEXT_CURPOS
|
ld r7,r2
|
pop r4
|
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
|
ply
|
plx
|
plx
|
pla
|
pla
|
|
rts
|
add r2,r2,#$3FF ; Move pointer to top of stack
|
message "CopyScreenToVirtualScreen"
|
php
|
CopyScreenToVirtualScreen
|
tsr sp,r4 ; save off current stack pointer
|
pha
|
sei
|
|
txs
|
|
ldx #$1FF
|
|
stx TCB_SP8Save,r7
|
|
st r6,TCB_Priority,r7
|
|
stz TCB_Status,r7
|
|
stz TCB_Timeout,r7
|
|
; setup virtual video for the task
|
|
stz TCB_CursorRow,r7
|
|
stz TCB_CursorCol,r7
|
|
stz TCB_mmu_map,r7 ; use mmu map #0
|
|
stz TCB_ABS8Save,r7
|
|
|
|
; 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
|
phx
|
phy ; save start address on stack
|
phy
|
push r8 ; save processor status reg on stack
|
push r4
|
|
lda #4095
|
; now fake pushing the register set onto the stack. Registers start up
|
ldx #TEXTSCR
|
; in an undefined state.
|
ldy IOFocusNdx
|
sub sp,#15 ; 15 registers
|
beq csvs3
|
tsx
|
ldy JCB_pVirtVid,y
|
stx TCB_SPSave,r7
|
mvn
|
|
lda #4095
|
; now restore the current stack pointer
|
ldx #TEXTSCR+$10000
|
trs r4,sp
|
ldy IOFocusNdx
|
|
ldy JCB_pVirtVidAttr,y
|
; Insert the task into the ready list
|
mvn
|
jsr AddTaskToReadyList
|
csvs3:
|
plp
|
|
int #2 ; invoke the scheduler
|
|
stask2:
|
|
pop r8
|
|
pop r7
|
|
pop r6
|
|
pop r5
|
|
pop r4
|
pop r4
|
ply
|
ply
|
plx
|
plx
|
pla
|
pla
|
rts
|
rts
|
stask1:
|
|
plp
|
|
lda #msgNoTCBs
|
|
jsr DisplayStringB
|
|
bra stask2
|
|
|
|
msgNoTCBs:
|
|
db "No more task control blocks available.",CR,LF,0
|
|
|
|
;------------------------------------------------------------------------------
|
|
; 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:
|
|
sei
|
|
; release any aquired resources
|
|
; - mailboxes
|
|
; - messages
|
|
hoff
|
|
lda RunningTCB
|
|
cmp #MAX_TASKNO
|
|
bhi xtsk1
|
|
jsr RemoveTaskFromReadyList
|
|
jsr RemoveFromTimeoutList
|
|
stz TCB_Status,r1 ; set task status to TS_NONE
|
|
jsr ReleaseIOFocus
|
|
ldx #86
|
|
stx LEDS
|
|
ldx FreeTCB ; add the task control block to the free list
|
|
stx TCB_NxtTCB,r1
|
|
sta FreeTCB
|
|
xtsk1:
|
|
jmp SelectTaskToRun
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; r1 = task number
|
; Clear the screen and the screen color memory
|
; r2 = new priority
|
; We clear the screen to give a visual indication that the system
|
|
; is working at all.
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
SetTaskPriority:
|
;
|
cmp #MAX_TASKNO ; make sure task number is reasonable
|
message "ClearScreen"
|
bhi stp1
|
ClearScreen:
|
cpx #5 ; make sure priority is okay
|
pha ; holds a space character
|
bhs stp1
|
phx ; loop counter
|
phy
|
phy ; memory addressing
|
php
|
lda TEXTREG+TEXT_COLS ; calc number to clear
|
sei
|
ldx TEXTREG+TEXT_ROWS
|
ldy TCB_Status,r1 ; if the task is on the ready list
|
mul r1,r1,r2 ; r1 = # chars to clear
|
bit r3,#TS_READY|TS_RUNNING ; then remove it and re-add it.
|
pha
|
beq stp2 ; Otherwise just go set the priority field
|
jsr GetScreenLocation
|
jsr RemoveTaskFromReadyList
|
tay ; y = target address
|
stx TCB_Priority,r1
|
lda #' ' ; space char
|
jsr AddTaskToReadyList
|
jsr AsciiToScreen
|
plp
|
tax ; x is value to store
|
ply
|
pla ; a is count
|
stp1:
|
pha
|
rts
|
stos ; clear the memory
|
stp2:
|
jsr GetCurrAttr
|
stx TCB_Priority,r1
|
tax ; x = value to use
|
plp
|
jsr GetColorCodeLocation
|
|
tay ; y = target address
|
|
pla ; a = count
|
|
stos
|
ply
|
ply
|
|
plx
|
|
pla
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; AddTaskToReadyList
|
; Scroll text on the screen upwards
|
;
|
|
; 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"
|
message "ScrollUp"
|
AddTaskToReadyList:
|
ScrollUp:
|
php
|
pha
|
phx
|
phx
|
phy
|
phy
|
sei
|
push r4
|
ldx #TS_READY
|
push r5
|
stx TCB_Status,r1
|
push r6
|
ldy TCB_Priority,r1
|
lda TEXTREG+TEXT_COLS ; acc = # text columns
|
ldx QNdx0,y
|
ldx TEXTREG+TEXT_ROWS
|
bmi arl5
|
mul r2,r1,r2 ; calc number of chars to scroll
|
ldy TCB_PrvRdy,x
|
sub r2,r2,r1 ; one less row
|
sta TCB_NxtRdy,y
|
pha
|
sty TCB_PrvRdy,r1
|
jsr GetScreenLocation
|
sta TCB_PrvRdy,x
|
tay
|
stx TCB_NxtRdy,r1
|
jsr GetColorCodeLocation
|
arl3:
|
ld r6,r1
|
ply
|
pla
|
plx
|
scrup1:
|
plp
|
add r5,r3,r1
|
rts
|
ld r4,(r5) ; move character
|
|
st r4,(y)
|
; Here the ready list was empty, so add at head
|
add r5,r6,r1
|
arl5:
|
ld r4,(r5) ; and move color code
|
sta QNdx0,y
|
st r4,(r6)
|
sta TCB_NxtRdy,r1
|
iny
|
sta TCB_PrvRdy,r1
|
inc r6
|
|
dex
|
|
bne scrup1
|
|
lda TEXTREG+TEXT_ROWS
|
|
dea
|
|
jsr BlankLine
|
|
pop r6
|
|
pop r5
|
|
pop r4
|
ply
|
ply
|
plx
|
plx
|
plp
|
pla
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; RemoveTaskFromReadyList
|
; Blank out a line on the display
|
;
|
; line number to blank is in acc
|
; This subroutine removes a task from the ready list.
|
|
;
|
|
; Registers Affected: none
|
|
; Parameters:
|
|
; r1 = task number
|
|
; Returns:
|
|
; r1 = task number
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
|
;
|
message "RemoveTaskFromReadyList"
|
BlankLine:
|
RemoveTaskFromReadyList:
|
pha
|
php ; save off interrupt mask state
|
|
phx
|
phx
|
phy
|
phy
|
push r4
|
push r4
|
push r5
|
push r5
|
|
ldx TEXTREG+TEXT_COLS ; x = # chars to blank out from video controller
|
sei
|
mul r3,r2,r1 ; y = screen index (row# * #cols)
|
ldy TCB_Status,r1 ; is the task on the ready list ?
|
ld r5,r3 ; r5 = screen index
|
bit r3,#TS_READY|TS_RUNNING
|
pha
|
beq rfr2
|
jsr GetScreenLocation
|
stz TCB_Status,r1 ; task status = TS_NONE
|
ld r4,r1
|
ld r4,TCB_NxtRdy,r1 ; Get previous and next fields.
|
pla
|
ld r5,TCB_PrvRdy,r1
|
add r3,r3,r4 ; y = screen address
|
st r4,TCB_NxtRdy,r5
|
lda #' '
|
st r5,TCB_PrvRdy,r4
|
jsr AsciiToScreen
|
ldy TCB_Priority,r1
|
blnkln1:
|
cmp r1,QNdx0,y ; Are we removing the QNdx task ?
|
sta (y)
|
bne rfr2
|
iny
|
st r4,QNdx0,y
|
dex
|
; Now we test for the case where the task being removed was the only one
|
bne blnkln1
|
; on the ready list of that priority level. We can tell because the
|
; reset the color codes on the display line to the normal attribute
|
; NxtRdy would point to the task itself.
|
jsr GetColorCodeLocation
|
cmp r4,r1
|
tay ; y = destination
|
bne rfr2
|
add r3,r3,r5 ; add in index
|
ldx #-1 ; Make QNdx negative
|
jsr GetNormAttr ; get the value to set
|
stx QNdx0,y
|
tax
|
stx TCB_NxtRdy,r1
|
lda TEXTREG+TEXT_COLS ; number of columns to blank out
|
stx TCB_PrvRdy,r1
|
dea ; acc is one less
|
rfr2:
|
stos
|
pop r5
|
pop r5
|
pop r4
|
pop r4
|
ply
|
ply
|
plx
|
plx
|
plp
|
pla
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; AddToTimeoutList
|
; Convert ASCII character to screen display character.
|
; 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:
|
align 8
|
php
|
AsciiToScreen:
|
phx
|
and #$FF
|
push r4
|
or #$100
|
push r5
|
bit #%00100000 ; if bit 5 isn't set
|
sei
|
beq .00001
|
|
bit #%01000000 ; or bit 6 isn't set
|
ld r5,#-1
|
beq .00001
|
ld r4,TimeoutList ; are there any tasks on the timeout list ?
|
and #%110011111
|
cmp r4,#MAX_TASKNO
|
.00001:
|
bhi attl1
|
|
attl_check_next:
|
|
sub r2,r2,TCB_Timeout,r4 ; is this timeout > next
|
|
bmi attl_insert_before
|
|
ld r5,r4
|
|
ld r4,TCB_NxtRdy,r4
|
|
cmp r4,#MAX_TASKNO
|
|
bls 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_NxtRdy,r1 ; r4 was = -1
|
|
st r1,TCB_NxtRdy,r5
|
|
st r5,TCB_PrvRdy,r1
|
|
st r2,TCB_Timeout,r1
|
|
bra attl_exit
|
|
|
|
attl_insert_before:
|
|
cmp r5,#MAX_TASKNO
|
|
bhi attl2
|
|
st r4,TCB_NxtRdy,r1 ; next on list goes after this task
|
|
st r5,TCB_PrvRdy,r1 ; set previous link
|
|
st r1,TCB_NxtRdy,r5
|
|
st r1,TCB_PrvRdy,r4
|
|
bra attl3
|
|
|
|
; Here there is no previous entry in the timeout list
|
|
; Add at start
|
|
attl2:
|
|
sta TCB_PrvRdy,r4
|
|
st r5,TCB_PrvRdy,r1 ; r5 = -1
|
|
st r4,TCB_NxtRdy,r1
|
|
sta TimeoutList ; update the head pointer
|
|
attl3:
|
|
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.
|
|
attl1:
|
|
sta TimeoutList ; set the head of the timeout list
|
|
stx TCB_Timeout,r1
|
|
ldx #-1 ; flag no more entries in timeout list
|
|
stx TCB_NxtRdy,r1 ; no next entries
|
|
stx TCB_PrvRdy,r1 ; and no prev entries
|
|
attl_exit:
|
|
ldx #TS_TIMEOUT ; set the task's status as timing out
|
|
stx TCB_Status,r1
|
|
pop r5
|
|
pop r4
|
|
plx
|
|
plp
|
|
rts
|
rts
|
msgTimeout1:
|
|
db CR,LF,"Adding to timeout list:",CR,LF,0
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; RemoveFromTimeoutList
|
; Convert screen character to ascii character
|
;
|
|
; This subroutine is called from within the timer ISR when the task's
|
|
; timeout expires. It may also be called when a task is killed.
|
|
;
|
|
; Registers Affected: none
|
|
; Parameters:
|
|
; r1 = task number
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
message "RemoveFromTimeoutList"
|
;
|
RemoveFromTimeoutList:
|
ScreenToAscii:
|
php
|
and #$FF
|
phx
|
cmp #26+1
|
push r4
|
bcs stasc1
|
push r5
|
add #$60
|
sei
|
stasc1:
|
|
|
ld r4,TCB_Status,r1 ; Is the task even on the timeout list ?
|
|
bit #TS_TIMEOUT
|
|
beq rftl5
|
|
cmp TimeoutList ; Are we removing the head of the list ?
|
|
beq rftl2
|
|
ld r4,TCB_PrvRdy,r1 ; adjust the links of the next and previous
|
|
bmi rftl3 ; no previous link - list corrupt?
|
|
ld r5,TCB_NxtRdy,r1 ; tasks on the list to point around the task
|
|
st r5,TCB_NxtRdy,r4
|
|
bmi rftl3
|
|
st r4,TCB_PrvRdy,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 rftl3
|
|
|
|
; Update the head of the list.
|
|
rftl2:
|
|
ld r5,TCB_NxtRdy,r1
|
|
st r5,TimeoutList ; store next field into list head
|
|
bmi rftl3
|
|
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_PrvRdy,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.
|
|
rftl3:
|
|
stz TCB_Status,r1 ; set the task status to TS_NONE
|
|
ldx #-1 ; make sure the next and prev fields indicate
|
|
stx TCB_NxtRdy,r1 ; the task is not on a list.
|
|
stx TCB_PrvRdy,r1
|
|
rftl5:
|
|
pop r5
|
|
pop r4
|
|
plx
|
|
plp
|
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Sleep
|
; HomeCursor
|
;
|
; Set the cursor location to the top left of the screen.
|
; Put the currently running task to sleep for a specified time.
|
|
;
|
|
; Registers Affected: none
|
|
; Parameters:
|
|
; r1 = time duration in centi-seconds (1/100 second).
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
Sleep:
|
HomeCursor:
|
php
|
|
pha
|
pha
|
phx
|
phx
|
|
phy
|
|
spl jcb_sema + 1
|
|
jsr GetPtrCurrentJCB
|
tax
|
tax
|
sei
|
stz JCB_CursorRow,x
|
lda RunningTCB
|
stz JCB_CursorCol,x
|
jsr RemoveTaskFromReadyList
|
stz jcb_sema + 1
|
jsr AddToTimeoutList ; The scheduler will be returning to this
|
cpx IOFocusNdx
|
int #2 ; task eventually, once the timeout expires,
|
bne hc1
|
SleepRet:
|
stz TEXTREG+TEXT_CURPOS
|
|
hc1:
|
|
ply
|
plx
|
plx
|
pla
|
pla
|
plp
|
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; KillTask
|
; Update the cursor position in the text controller based on the
|
;
|
; CursorRow,CursorCol.
|
; "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:
|
UpdateCursorPos:
|
php
|
pha
|
phx
|
jsr GetPtrCurrentJCB
|
cmp #1 ; BIOS task and IDLE task are immortal
|
cmp IOFocusNdx ; update cursor position in text controller
|
bls kt1
|
bne .ucp1 ; only for the task with the output focus
|
cmp #MAX_TASKNO
|
ld r0,JCB_CursorOn,r4 ; only update if cursor is showing
|
bhi kt1
|
beq .ucp2
|
sei
|
jsr CursorOn
|
jsr ForceReleaseIOFocus
|
|
jsr RemoveTaskFromReadyList
|
|
jsr RemoveFromTimeoutList
|
|
stz TCB_Status,r1 ; set task status to TS_NONE
|
|
ldx FreeTCB ; add the task control block to the free list
|
|
stx TCB_NxtTCB,r1
|
|
sta FreeTCB
|
|
int #2 ; invoke scheduler to reschedule tasks
|
|
kt1:
|
|
plx
|
|
plp
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Allocate a mailbox
|
|
; r1 = pointer to place to store handle
|
|
;------------------------------------------------------------------------------
|
|
message "AllocMbx"
|
|
AllocMbx:
|
|
cmp #0
|
|
beq ambx1
|
|
phx
|
phx
|
phy
|
|
push r4
|
push r4
|
ld r4,r1
|
ld r4,r1
|
php
|
lda JCB_CursorRow,r4
|
sei
|
and #$3F ; limit of 63 rows
|
lda FreeMbx ; Get mailbox off of free mailbox list
|
ldx TEXTREG+TEXT_COLS
|
sta (r4) ; store off the mailbox number
|
mul r2,r2,r1
|
bmi ambx2
|
lda JCB_CursorCol,r4
|
ldx MBX_LINK,r1 ; and update the head of the list
|
and #$7F ; limit of 127 cols
|
stx FreeMbx
|
add r2,r2,r1
|
dec nMailbox ; decrement number of available mailboxes
|
stx TEXTREG+TEXT_CURPOS
|
tax
|
|
ldy RunningTCB ; set the mailbox owner
|
|
bmi RunningTCBErr
|
|
lda TCB_hJCB,y
|
|
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
|
|
ambx3:
|
|
plp
|
|
pop r4
|
pop r4
|
ply
|
|
plx
|
plx
|
lda #E_Ok
|
.ucp1:
|
|
pla
|
rts
|
rts
|
ambx1:
|
.ucp2:
|
lda #E_Arg
|
jsr CursorOff
|
|
pla
|
rts
|
rts
|
ambx2:
|
|
plp
|
CursorOff:
|
pop r4
|
pha
|
ply
|
lda #5
|
plx
|
bms TEXTREG+TEXT_CURCTL
|
lda #E_NoMoreMbx
|
lda #6
|
|
bmc TEXTREG+TEXT_CURCTL
|
|
pla
|
|
rts
|
|
|
|
CursorOn:
|
|
pha
|
|
lda #5
|
|
bmc TEXTREG+TEXT_CURCTL
|
|
lda #6
|
|
bms TEXTREG+TEXT_CURCTL
|
|
pla
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; r1 = message
|
; Calculate screen memory location from CursorRow,CursorCol.
|
; r2 = mailbox
|
; Also refreshes the cursor location.
|
|
; Returns:
|
|
; r1 = screen location
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
message "QueueMsgAtMbx"
|
;
|
QueueMsgAtMbx:
|
CalcScreenLoc:
|
pha
|
|
phx
|
phx
|
phy
|
push r4
|
php
|
jsr GetPtrCurrentJCB
|
sei
|
ld r4,r1
|
ldy MBX_MQ_TAIL,x
|
lda JCB_CursorRow,r4
|
bmi qmam1
|
and #$3F ; limit to 63 rows
|
sta MBX_LINK,y
|
ldx TEXTREG+TEXT_COLS
|
bra qmam2
|
mul r2,r2,r1
|
qmam1:
|
lda JCB_CursorCol,r4
|
sta MBX_MQ_HEAD,x
|
and #$7F ; limit to 127 cols
|
qmam2:
|
add r2,r2,r1
|
sta MBX_MQ_TAIL,x
|
cmp r4,IOFocusNdx ; update cursor position in text controller
|
inc MBX_MQ_COUNT,x ; increase the queued message count
|
bne csl1 ; only for the task with the output focus
|
ldx #-1
|
stx TEXTREG+TEXT_CURPOS
|
stx MSG_LINK,r1
|
csl1:
|
plp
|
jsr GetScreenLocation
|
ply
|
add r1,r1,r2
|
|
pop r4
|
plx
|
plx
|
pla
|
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Returns
|
; Display a character on the screen.
|
; r1 = message number
|
; 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 "DequeueMsgFromMbx"
|
;
|
DequeueMsgFromMbx:
|
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
|
phx
|
phy
|
phy
|
php
|
cmp #$99 ; delete ?
|
sei
|
bne .dcx13
|
tax ; x = mailbox index
|
.doDel:
|
lda MBX_MQ_COUNT,x ; are there any messages available ?
|
jsr CalcScreenLoc
|
beq dmfm1
|
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
|
dea
|
sta MBX_MQ_COUNT,x ; update the message count
|
sta JCB_CursorCol,r4
|
lda MBX_MQ_HEAD,x ; Get the head of the list, this should not be -1
|
jsr CalcScreenLoc ; acc = screen location
|
bmi dmfm1 ; since the message count > 0
|
tay ; y = screen location
|
ldy MSG_LINK,r1 ; get the link to the next message
|
lda JCB_CursorCol,r4
|
sty MBX_MQ_HEAD,x ; update the head of the list
|
.dcx5:
|
bpl dmfm2 ; if there was no more messages then update the
|
ldx $4,y
|
sty MBX_MQ_TAIL,x ; tail of the list as well.
|
stx (y)
|
dmfm2:
|
iny
|
sta MSG_LINK,r1 ; point the link to the messahe itself to indicate it's dequeued
|
ina
|
dmfm1:
|
cmp JCB_VideoCols,r4
|
plp
|
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
|
ply
|
plx
|
plx
|
|
pla
|
|
pop r4
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
; ESC processing
|
;------------------------------------------------------------------------------
|
.processEsc:
|
DequeueThreadFromMbx:
|
cmp #(ESC<<24)+('('<<16)+(ESC<<8)+'G'
|
cpx #0
|
beq .procAttr
|
beq dtfm1
|
bit #$FF000000 ; is it some other five byte escape sequence ?
|
php
|
bne .unrecogEsc
|
sei
|
cmp #(ESC<<16)+('('<<8)+ESC
|
push r4
|
beq .testG
|
ld r4,MBX_TQ_HEAD,r1
|
bit #$FF0000 ; is it some other four byte escape sequence ?
|
bpl dtfm2
|
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
|
pop r4
|
stz (x)
|
|
plp
|
|
lda #E_NoThread
|
|
rts
|
rts
|
dtfm2:
|
|
push r5
|
.cursOnOff:
|
dec MBX_TQ_COUNT,r1
|
pla
|
st r4,(x)
|
stz JCB_CursorOn,r4
|
ld r4,TCB_mbq_next,r4
|
and #$FF
|
st r4,MBX_TQ_HEAD,r1
|
cmp #'0'
|
bmi dtfm3
|
beq .escRst
|
ld r5,#-1
|
inc JCB_CursorOn,r4
|
st r5,TCB_mbq_prev,r4
|
.escRst:
|
bra dtfm4
|
stz JCB_esc,r4 ; reset escape sequence capture
|
dtfm3:
|
|
ld r5,#-1
|
|
st r5,MBX_TQ_TAIL,r1
|
|
dtfm4:
|
|
stz MBX_SEMA+1
|
|
ld r5,(x)
|
|
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
|
pop r4
|
plp
|
|
lda #E_Ok
|
|
rts
|
|
dtfm1:
|
|
lda #E_Arg
|
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
.procAttr:
|
; r1 = handle to mailbox
|
pla
|
; r2 = message D1
|
and #$FF
|
; r3 = message D2
|
cmp #'0'
|
;------------------------------------------------------------------------------
|
bne .0005
|
message "SendMsg"
|
lda JCB_NormAttr,r4
|
SendMsg:
|
sta JCB_CurrAttr,r4
|
cmp #2047 ; check the mailbox number to make sure
|
bra .escRst
|
bhi smsg1 ; that it's sensible
|
.0005:
|
push r4
|
cmp #'4'
|
push r5
|
bne .escRst
|
push r6
|
|
php
|
|
sei
|
|
ld r4,MBX_OWNER,r1
|
|
bmi smsg2 ; error: no owner
|
|
pha
|
|
phx
|
phx
|
jsr DequeueThreadFromMbx ; r1=mbx, r2=thread (returned)
|
lda JCB_NormAttr,r4 ; get the normal attribute
|
ld r6,r2 ; r6 = thread
|
tax
|
|
lsr r1,r1,#5 ; swap foreground and background colors
|
|
and #$1F
|
|
asl r2,r2,#5
|
|
or r1,r1,r2
|
plx
|
plx
|
|
sta JCB_CurrAttr,r4 ; store in current attribute
|
|
bra .escRst
|
|
|
|
.esc1:
|
pla
|
pla
|
cmp r6,#0
|
and #$FF
|
bpl smsg3
|
cmp #'W' ; esc 'W' - delete char under cursor
|
; Here there was no thread waiting at the mailbox, so a message needs to
|
bne .0006
|
; be allocated
|
stz JCB_esc,r4
|
ld r4,FreeMsg
|
|
bmi smsg4 ; no more messages available
|
|
ld r5,MSG_LINK,r4
|
|
st r5,FreeMsg
|
|
dec nMsgBlk ; decrement the number of available messages
|
|
stx MSG_D1,r4
|
|
sty MSG_D2,r4
|
|
pha
|
pha
|
phx
|
phx
|
tax ; x = mailbox
|
phy
|
ld r1,r4 ; acc = message
|
bra .doDel
|
jsr QueueMsgAtMbx
|
.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
|
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
|
pla
|
cmp r6,#0 ; check if there is a thread waiting for a message
|
|
bmi smsg5
|
|
smsg3:
|
|
ld r5,TCB_MSGPTR_D1,r6
|
|
beq smsg6
|
|
stx (r5)
|
|
smsg6:
|
|
ld r5,TCB_MSGPTR_D2,r6
|
|
beq smsg7
|
|
sty (r5)
|
|
smsg7:
|
|
ld r5,TCB_Status,r6
|
|
bit r5,#TS_TIMEOUT
|
|
beq smsg8
|
|
ld r1,r6
|
|
jsr RemoveFromTimeoutList
|
|
smsg8:
|
|
ld r1,r6
|
|
jsr AddTaskToReadyList
|
|
int #2 ; invoke the scheduler
|
|
smsg5:
|
|
plp
|
|
pop r6
|
|
pop r5
|
|
pop r4
|
|
lda #E_Ok
|
|
rts
|
|
smsg1:
|
|
lda #E_BadMbx
|
|
rts
|
|
smsg2:
|
|
plp
|
|
pop r6
|
|
pop r5
|
|
pop r4
|
|
lda #E_NotAlloc
|
|
rts
|
|
smsg4:
|
|
plp
|
|
pop r6
|
|
pop r5
|
|
pop r4
|
pop r4
|
lda #E_NoMsg
|
|
rts
|
rts
|
|
|
|
.testEsc:
|
|
pla
|
|
and #$FF
|
|
cmp #ESC
|
|
bne .escRst
|
|
bra .stuffChar
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; WaitMsg
|
; Increment the cursor position, scroll the screen if needed.
|
; 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=pointer to D1
|
|
; r3=pointer to D2
|
|
; r4=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
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
WaitMsg:
|
;
|
cmp #2047 ; check the mailbox number to make sure
|
message "IncCursorPos"
|
bhi wmsg1 ; that it's sensible
|
IncCursorPos:
|
|
pha
|
phx
|
phx
|
phy
|
|
push r4
|
push r4
|
push r5
|
jsr GetPtrCurrentJCB
|
push r6
|
ld r4,r1
|
push r7
|
lda JCB_CursorCol,r4
|
ld r6,r1
|
ina
|
php
|
sta JCB_CursorCol,r4
|
sei
|
ldx JCB_VideoCols,r4
|
ld r5,MBX_OWNER,r1
|
cmp r1,r2
|
cmp r5,#MAX_TASKNO
|
blo icc1
|
bhi wmsg2 ; error: no owner
|
stz JCB_CursorCol,r4 ; column = 0
|
jsr DequeueMsgFromMbx
|
bra icr1
|
cmp #0
|
IncCursorRow:
|
bpl wmsg3
|
pha
|
|
|
; 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.
|
|
lda RunningTCB ; remove the task from the ready list
|
|
jsr RemoveTaskFromReadyList
|
|
ld 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
|
|
stx TCB_MSGPTR_D1,r1 ; save off the message pointers
|
|
sty TCB_MSGPTR_D2,r1
|
|
ld r7,MBX_TQ_HEAD,r1 ; 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:
|
|
cmp r4,#0 ; check for a timeout
|
|
beq wmsg10
|
|
ld r2,r4
|
|
jsr AddToTimeoutList
|
|
wmsg10:
|
|
int #2 ; invoke the scheduler
|
|
|
|
; 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
|
|
|
|
; Store message D1 to pointer
|
|
wmsg3:
|
|
cpx #0
|
|
beq wmsg4
|
|
ld r7,MSG_D1,r1
|
|
st r7,(x)
|
|
; Store message D2 to pointer
|
|
wmsg4:
|
|
cpy #0
|
|
beq wmsg5
|
|
ld r7,MSG_D2,r1
|
|
st r7,(y)
|
|
; Add the newly dequeued message to the free messsage list
|
|
wmsg5:
|
|
ld r7,FreeMsg
|
|
st r7,MSG_LINK,r1
|
|
sta FreeMsg
|
|
inc nMsgBlk
|
|
wmsg8:
|
|
plp
|
|
pop r7
|
|
pop r6
|
|
pop r5
|
|
pop r4
|
|
ply
|
|
plx
|
|
lda #E_Ok
|
|
rts
|
|
wmsg1:
|
|
lda #E_BadMbx
|
|
rts
|
|
wmsg2:
|
|
plp
|
|
pop r7
|
|
pop r6
|
|
pop r5
|
|
pop r4
|
|
ply
|
|
plx
|
|
lda #E_NotAlloc
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; CheckMsg
|
|
; Check for a message at a mailbox. Does not block.
|
|
;
|
|
; Parameters
|
|
; r1=mailbox
|
|
; r2=pointer to D1
|
|
; r3=pointer to D2
|
|
; r4=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
|
|
;------------------------------------------------------------------------------
|
|
CheckMsg:
|
|
cmp #2047 ; check the mailbox number to make sure
|
|
bhi cmsg1 ; that it's sensible
|
|
phx
|
phx
|
phy
|
|
push r4
|
push r4
|
push r5
|
jsr GetPtrCurrentJCB
|
php
|
ld r4,r1
|
sei
|
icr1:
|
ld r5,MBX_OWNER,r1
|
lda JCB_CursorRow,r4
|
bmi cmsg2 ; error: no owner
|
ina
|
cmp r4,#0 ; are we to dequeue the message ?
|
sta JCB_CursorRow,r4
|
beq cmsg3
|
ldx JCB_VideoRows,r4
|
jsr DequeueMsgFromMbx
|
cmp r1,r2
|
bra cmsg4
|
blo icc1
|
cmsg3:
|
dex ; backup the cursor row, we are scrolling up
|
lda MBX_MQ_HEAD,r1 ; peek the message at the head of the messages queue
|
stx JCB_CursorRow,r4
|
cmsg4:
|
jsr ScrollUp
|
cmp #0
|
icc1:
|
bmi cmsg5
|
jsr UpdateCursorPos
|
cpx #0
|
icc2:
|
beq cmsg6
|
|
ld r5,MSG_D1,r1
|
|
st r5,(x)
|
|
cmsg6:
|
|
cpy #0
|
|
beq cmsg7
|
|
ld r5,MSG_D2,r1
|
|
st r5,(y)
|
|
cmsg7:
|
|
cmp r4,#0
|
|
beq cmsg8
|
|
ld r5,FreeMsg
|
|
st r5,MSG_LINK,r1
|
|
sta FreeMsg
|
|
inc nMsgBlk
|
|
cmsg8:
|
|
plp
|
|
pop r5
|
|
pop r4
|
|
ply
|
|
plx
|
|
lda #E_Ok
|
|
rts
|
|
cmsg1:
|
|
lda #E_BadMbx
|
|
rts
|
|
cmsg2:
|
|
plp
|
|
pop r5
|
|
pop r4
|
|
ply
|
|
plx
|
|
lda #E_NotAlloc
|
|
rts
|
|
cmsg5:
|
|
plp
|
|
pop r5
|
|
pop r4
|
pop r4
|
ply
|
|
plx
|
plx
|
lda #E_NoMsg
|
pla
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
|
; Display a string on the screen.
|
|
; The characters are packed 4 per word
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
comment ~
|
;
|
SetIOFocusBit:
|
message "DisplayStringB"
|
and r2,r2,#$FF
|
DisplayStringB:
|
and r1,r2,#$1F ; get bit index 0 to 31
|
|
ldy #1
|
|
asl r3,r3,r1 ; shift bit to proper place
|
|
lsr r2,r2,#5 ; get word index /32 bits per word
|
|
lda IOFocusTbl,x
|
|
or r1,r1,r3
|
|
sta IOFocusTbl,x
|
|
rts
|
|
~
|
|
;------------------------------------------------------------------------------
|
|
; The I/O focus list is an array indicating which tasks are requesting the
|
|
; I/O focus. The I/O focus is user controlled by pressing ALT-TAB on the
|
|
; keyboard.
|
|
;------------------------------------------------------------------------------
|
|
message "RequestIOFocus"
|
|
RequestIOFocus:
|
|
pha
|
pha
|
phx
|
phx
|
phy
|
tax ; r2 = pointer to string
|
php
|
dspj1B:
|
sei
|
lb r1,0,x ; move string char into acc
|
ldx RunningTCB
|
inx ; increment pointer
|
cpx #MAX_TASKNO
|
cmp #0 ; is it end of string ?
|
bhi riof1
|
beq dsretB
|
ldy IOFocusNdx ; Is the focus list empty ?
|
jsr DisplayChar ; display character
|
bmi riof2
|
bra dspj1B
|
riof4:
|
dsretB:
|
lda TCB_iof_next,x ; is the task already in the IO focus list ?
|
|
bpl riof3
|
|
lda IOFocusNdx ; Expand the list
|
|
ldy TCB_iof_prev,r1
|
|
stx TCB_iof_prev,r1
|
|
sta TCB_iof_next,x
|
|
sty TCB_iof_prev,x
|
|
stx TCB_iof_next,y
|
|
riof3:
|
|
txa
|
|
bms IOFocusTbl
|
|
; jsr SetIOFocusBit
|
|
riof1:
|
|
plp
|
|
ply
|
|
plx
|
plx
|
pla
|
pla
|
rts
|
rts
|
|
|
; Here, the IO focus list was empty. So expand it.
|
DisplayStringQ:
|
; Update pointers to loop back to self.
|
|
riof2:
|
|
stx IOFocusNdx
|
|
stx TCB_iof_next,x
|
|
stx TCB_iof_prev,x
|
|
bra riof3
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Releasing the I/O focus causes the focus to switch if the running task
|
|
; had the I/O focus.
|
|
; ForceReleaseIOFocus forces the release of the IO focus for a task
|
|
; different than the one currently running.
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
message "ForceReleaseIOFocus"
|
|
ForceReleaseIOFocus:
|
|
php
|
|
pha
|
pha
|
phx
|
phx
|
phy
|
tax ; r2 = pointer to string
|
sei
|
lda #TEXTSCR
|
tax
|
sta QIndex
|
jmp rliof4
|
dspj1Q:
|
message "ReleaseIOFocus"
|
lb r1,0,x ; move string char into acc
|
ReleaseIOFocus:
|
inx ; increment pointer
|
php
|
cmp #0 ; is it end of string ?
|
|
beq dsretQ
|
|
jsr DisplayCharQ ; display character
|
|
bra dspj1Q
|
|
dsretQ:
|
|
plx
|
|
pla
|
|
rts
|
|
|
|
DisplayCharQ:
|
pha
|
pha
|
phx
|
phx
|
phy
|
jsr AsciiToScreen
|
sei
|
ldx #0
|
ldx RunningTCB
|
sta (QIndex,x)
|
rliof4:
|
lda QIndex
|
cpx #MAX_TASKNO
|
ina
|
bhi rliof3
|
sta QIndex
|
; phx
|
; inc QIndex
|
ldy #1
|
|
txa
|
|
bmt IOFocusTbl
|
|
beq rliof3
|
|
bmc IOFocusTbl
|
|
comment ~
|
|
and r1,r2,#$1F ; get bit index 0 to 31
|
|
asl r3,r3,r1 ; shift bit to proper place
|
|
eor r3,r3,#-1 ; invert bit mask
|
|
lsr r2,r2,#5 ; get word index /32 bits per word
|
|
lda IOFocusTbl,x
|
|
and r1,r1,r3
|
|
sta IOFocusTbl,x
|
|
~
|
|
; plx
|
|
cpx IOFocusNdx ; Does the running task have the I/O focus ?
|
|
bne rliof1
|
|
jsr SwitchIOFocus ; If so, then switch the focus.
|
|
rliof1:
|
|
lda TCB_iof_next,x ; get next and previous fields.
|
|
bmi rliof2 ; Is the task on the list ?
|
|
ldy TCB_iof_prev,x
|
|
sta TCB_iof_next,y ; prev->next = current->next
|
|
sty TCB_iof_prev,r1 ; next->prev = current->prev
|
|
cmp r1,r3 ; Check if the IO focus list is collapsing.
|
|
bne rliof2 ; If the list just points back to the task
|
|
cmp r1,r2 ; being removed, then it's the last task
|
|
bne rliof2 ; removed from the list, so the list is being
|
|
lda #-1 ; emptied.
|
|
sta IOFocusNdx
|
|
rliof2:
|
|
lda #-1 ; Update the next and prev fields to indicate
|
|
sta TCB_iof_next,x ; the task is no longer on the list.
|
|
sta TCB_iof_prev,x
|
|
rliof3:
|
|
ply
|
|
plx
|
plx
|
pla
|
pla
|
plp
|
|
rts
|
rts
|
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Get the location of the screen and screen attribute memory. The location
|
; Display a string on the screen.
|
; depends on whether or not the task has the output focus.
|
; The characters are packed 1 per word
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
GetScreenLocation:
|
;
|
lda RunningTCB
|
message "DisplayStringW"
|
cmp IOFocusNdx
|
DisplayStringW:
|
beq gsl1
|
pha
|
and r1,r1,#$FF
|
phx
|
asl r1,r1,#13 ; 8192 words per screen
|
tax ; r2 = pointer to string
|
add r1,r1,#BIOS_SCREENS
|
dspj1W:
|
rts
|
lda (x) ; move string char into acc
|
gsl1:
|
inx ; increment pointer
|
lda #TEXTSCR
|
cmp #0 ; is it end of string ?
|
|
beq dsretW
|
|
jsr DisplayChar ; display character
|
|
bra dspj1W ; go back for next character
|
|
dsretW:
|
|
plx
|
|
pla
|
rts
|
rts
|
|
|
GetColorCodeLocation:
|
DisplayStringCRLFB:
|
lda RunningTCB
|
jsr DisplayStringB
|
cmp IOFocusNdx
|
CRLF:
|
beq gccl1
|
pha
|
and r1,r1,#$FF
|
lda #'\r'
|
asl r1,r1,#13 ; 8192 words per screen
|
jsr DisplayChar
|
add r1,r1,#BIOS_SCREENS+4096
|
lda #'\n'
|
rts
|
jsr DisplayChar
|
gccl1:
|
pla
|
lda #TEXTSCR+$10000
|
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
message "CopyVirtualScreenToScreen"
|
message "TickRout"
|
CopyVirtualScreenToScreen
|
TickRout:
|
pha
|
; support EhBASIC's IRQ functionality
|
phx
|
; code derived from minimon.asm
|
phy
|
lda #3 ; Timer is IRQ #3
|
push r4
|
sta IrqSource ; stuff a byte indicating the IRQ source for PEEK()
|
lda #4095 ; number of words to copy-1
|
lb r1,IrqBase ; get the IRQ flag byte
|
ldx IOFocusNdx ; compute virtual screen location
|
lsr r4,r1
|
bmi cvss3
|
or r1,r1,r4
|
asl r2,r2,#13 ; 8192 words per screen
|
and #$E0
|
add r2,r2,#BIOS_SCREENS ; add in screens array base address
|
sb r1,IrqBase
|
ldy #TEXTSCR
|
|
mvn
|
inc TEXTSCR+55 ; update IRQ live indicator on screen
|
;cvss1:
|
|
; ld r4,(x)
|
; flash the cursor
|
; st r4,(y)
|
jsr GetPtrCurrentJCB
|
; inx
|
tax
|
; iny
|
cpx IOFocusNdx ; only bother to flash the cursor for the task with the IO focus.
|
; dea
|
bne tr1a
|
; bne cvss1
|
lda JCB_CursorFlash,x ; test if we want a flashing cursor
|
; now copy the color codes
|
beq tr1a
|
lda #4095
|
jsr CalcScreenLoc ; compute cursor location in memory
|
ldx IOFocusNdx
|
tay
|
asl r2,r2,#13
|
lda $10000,y ; get color code $10000 higher in memory
|
add r2,r2,#BIOS_SCREENS+4096 ; virtual char color array
|
ld r4,IRQFlag ; get counter
|
ldy #TEXTSCR+$10000
|
lsr r4,r4
|
mvn
|
and r4,r4,#$0F ; limit to low order nybble
|
;cvss2:
|
and #$F0 ; prepare to or in new value, mask off foreground color
|
; ld r4,(x)
|
or r1,r1,r4 ; set new foreground color for cursor
|
; st r4,(y)
|
sta $10000,y ; store the color code back to memory
|
; inx
|
tr1a
|
; iny
|
|
; dea
|
|
; bne cvss2
|
|
cvss3:
|
|
; reset the cursor position in the text controller
|
|
ldy IOFocusNdx
|
|
ldx TCB_CursorRow,y
|
|
lda TEXTREG+TEXT_COLS
|
|
mul r2,r2,r1
|
|
add r2,r2,TCB_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
|
|
bmi csvs3
|
|
asl r3,r3,#13
|
|
add r3,r3,#BIOS_SCREENS
|
|
mvn
|
|
;csvs1:
|
|
; ld r4,(x)
|
|
; st r4,(y)
|
|
; inx
|
|
; iny
|
|
; dea
|
|
; bne csvs1
|
|
lda #4095
|
|
ldx #TEXTSCR+$10000
|
|
ldy IOFocusNdx
|
|
asl r3,r3,#13
|
|
add r3,r3,#BIOS_SCREENS+4096
|
|
mvn
|
|
;csvs2:
|
|
; ld r4,(x)
|
|
; st r4,(y)
|
|
; inx
|
|
; iny
|
|
; dea
|
|
; bne csvs2
|
|
csvs3:
|
|
pop r4
|
|
ply
|
|
plx
|
|
pla
|
|
rts
|
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"
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Clear the screen and the screen color memory
|
; Display the half-word in r1
|
; We clear the screen to give a visual indication that the system
|
|
; is working at all.
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
message "ClearScreen"
|
DisplayWord:
|
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
|
pha
|
stos ; clear the memory
|
lsr r1,r1,#16
|
ld r2,ScreenColor ; x = value to use
|
jsr DisplayHalf
|
jsr GetColorCodeLocation
|
|
tay ; y = targte address
|
|
pla ; a = count
|
|
stos
|
|
ply
|
|
plx
|
|
pla
|
pla
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Scroll text on the screen upwards
|
; Display the half-word in r1
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
message "ScrollUp"
|
DisplayHalf:
|
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
|
pha
|
jsr GetScreenLocation
|
lsr r1,r1,#8
|
tay
|
jsr DisplayByte
|
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
|
pla
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Blank out a line on the display
|
; Display the byte in r1
|
; line number to blank is in acc
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
BlankLine:
|
DisplayByte:
|
pha
|
|
phx
|
|
phy
|
|
push r4
|
|
ldx TEXTREG+TEXT_COLS ; x = # chars to blank out from video controller
|
|
mul r3,r2,r1 ; y = screen index (row# * #cols)
|
|
pha
|
pha
|
jsr GetScreenLocation
|
lsr r1,r1,#4
|
ld r4,r1
|
jsr DisplayNybble
|
pla
|
|
add r3,r3,r4 ; y = screen address
|
|
lda #' '
|
|
jsr AsciiToScreen
|
|
blnkln1:
|
|
sta (y)
|
|
iny
|
|
dex
|
|
bne blnkln1
|
|
pop r4
|
|
ply
|
|
plx
|
|
pla
|
pla
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Convert ASCII character to screen display character.
|
; Display nybble in r1
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
AsciiToScreen:
|
DisplayNybble:
|
and #$FF
|
pha
|
cmp #'A'
|
and #$0F
|
bcc atoscr1 ; blt
|
add #'0'
|
cmp #'Z'
|
cmp #'9'+1
|
bcc atoscr1
|
bcc dispnyb1
|
beq atoscr1
|
add #7
|
cmp #'z'+1
|
dispnyb1:
|
bcs atoscr1
|
jsr DisplayChar
|
cmp #'a'
|
pla
|
bcc atoscr1
|
|
sub #$60
|
|
atoscr1:
|
|
or #$100
|
|
rts
|
rts
|
|
|
|
message "810"
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Convert screen character to ascii character
|
; Display memory pointed to by r2.
|
|
; destroys r1,r3
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
ScreenToAscii:
|
DisplayMemW:
|
and #$FF
|
pha
|
cmp #26+1
|
lda #'>'
|
bcs stasc1
|
jsr DisplayChar
|
add #$60
|
txa
|
stasc1:
|
jsr DisplayWord
|
rts
|
lda #' '
|
|
jsr DisplayChar
|
;------------------------------------------------------------------------------
|
lda (x)
|
; HomeCursor
|
jsr DisplayWord
|
; Set the cursor location to the top left of the screen.
|
inx
|
;------------------------------------------------------------------------------
|
lda #' '
|
HomeCursor:
|
jsr DisplayChar
|
phx
|
lda (x)
|
ldx RunningTCB
|
jsr DisplayWord
|
and r2,r2,#$FF
|
inx
|
stz TCB_CursorRow,x
|
lda #' '
|
stz TCB_CursorCol,x
|
jsr DisplayChar
|
cpx IOFocusNdx
|
lda (x)
|
bne hc1
|
jsr DisplayWord
|
stz TEXTREG+TEXT_CURPOS
|
inx
|
hc1:
|
lda #' '
|
plx
|
jsr DisplayChar
|
|
lda (x)
|
|
jsr DisplayWord
|
|
inx
|
|
jsr CRLF
|
|
pla
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Update the cursor position in the text controller based on the
|
; Display memory pointed to by r2.
|
; CursorRow,CursorCol.
|
; destroys r1,r3
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
UpdateCursorPos:
|
DisplayMemBytes:
|
pha
|
pha
|
phx
|
|
push r4
|
|
ld r4,RunningTCB
|
|
and r4,r4,#$FF
|
|
cmp r4,IOFocusNdx ; update cursor position in text controller
|
|
bne ucp1 ; only for the task with the output focus
|
|
lda TCB_CursorRow,r4
|
|
and #$3F ; limit of 63 rows
|
|
ldx TEXTREG+TEXT_COLS
|
|
mul r2,r2,r1
|
|
lda TCB_CursorCol,r4
|
|
and #$7F ; limit of 127 cols
|
|
add r2,r2,r1
|
|
stx TEXTREG+TEXT_CURPOS
|
|
ucp1:
|
|
pop r4
|
|
plx
|
|
pla
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Calculate screen memory location from CursorRow,CursorCol.
|
|
; Also refreshes the cursor location.
|
|
; Returns:
|
|
; r1 = screen location
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
CalcScreenLoc:
|
|
phx
|
|
push r4
|
|
ld r4,RunningTCB
|
|
and r4,r4,#$FF
|
|
lda TCB_CursorRow,r4
|
|
and #$3F ; limit to 63 rows
|
|
ldx TEXTREG+TEXT_COLS
|
|
mul r2,r2,r1
|
|
ld r1,TCB_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,r2,r1
|
|
pop r4
|
|
plx
|
|
rts
|
|
csl2:
|
|
lda #TEXTSCR
|
|
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.
|
|
; r1 = char to display
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
message "DisplayChar"
|
|
DisplayChar:
|
|
push r4
|
|
ld r4,RunningTCB
|
|
and r4,r4,#$FF
|
|
and #$FF ; mask off any higher order bits (called from eight bit mode).
|
|
cmp #'\r' ; carriage return ?
|
|
bne dccr
|
|
stz TCB_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 TCB_CursorCol,r4
|
|
cmp #55
|
|
bcs dcx7
|
|
ina
|
|
sta TCB_CursorCol,r4
|
|
dcx7:
|
|
jsr UpdateCursorPos
|
|
pla
|
|
pop r4
|
|
rts
|
|
dcx6:
|
|
cmp #$90 ; cursor up ?
|
|
bne dcx8
|
|
pha
|
|
lda TCB_CursorRow,r4
|
|
beq dcx7
|
|
dea
|
|
sta TCB_CursorRow,r4
|
|
bra dcx7
|
|
dcx8:
|
|
cmp #$93 ; cursor left ?
|
|
bne dcx9
|
|
pha
|
|
lda TCB_CursorCol,r4
|
|
beq dcx7
|
|
dea
|
|
sta TCB_CursorCol,r4
|
|
bra dcx7
|
|
dcx9:
|
|
cmp #$92 ; cursor down ?
|
|
bne dcx10
|
|
pha
|
|
lda TCB_CursorRow,r4
|
|
cmp #46
|
|
beq dcx7
|
|
ina
|
|
sta TCB_CursorRow,r4
|
|
bra dcx7
|
|
dcx10:
|
|
cmp #$94 ; cursor home ?
|
|
bne dcx11
|
|
pha
|
|
lda TCB_CursorCol,r4
|
|
beq dcx12
|
|
stz TCB_CursorCol,r4
|
|
bra dcx7
|
|
dcx12:
|
|
stz TCB_CursorRow,r4
|
|
bra dcx7
|
|
dcx11:
|
|
pha
|
|
phx
|
|
phy
|
phy
|
cmp #$99 ; delete ?
|
lda #'>'
|
bne dcx13
|
jsr DisplayChar
|
jsr CalcScreenLoc
|
lda #'B'
|
tay ; y = screen location
|
jsr DisplayChar
|
lda TCB_CursorCol,r4 ; acc = cursor column
|
|
bra dcx5
|
|
dcx13
|
|
cmp #CTRLH ; backspace ?
|
|
bne dcx3
|
|
lda TCB_CursorCol,r4
|
|
beq dcx4
|
|
dea
|
|
sta TCB_CursorCol,r4
|
|
jsr CalcScreenLoc ; acc = screen location
|
|
tay ; y = screen location
|
|
lda TCB_CursorCol,r4
|
|
dcx5:
|
|
ldx $4,y
|
|
stx (y)
|
|
iny
|
|
ina
|
|
cmp TEXTREG+TEXT_COLS
|
|
bcc dcx5
|
|
lda #' '
|
lda #' '
|
jsr AsciiToScreen
|
jsr DisplayChar
|
dey
|
txa
|
sta (y)
|
jsr DisplayWord
|
bra dcx4
|
ldy #0
|
dcx3:
|
.001:
|
cmp #'\n' ; linefeed ?
|
lda #' '
|
beq dclf
|
jsr DisplayChar
|
tax ; save acc in x
|
lb r1,0,x
|
jsr CalcScreenLoc ; acc = screen location
|
jsr DisplayByte
|
tay ; y = screen location
|
inx
|
txa ; restore r1
|
iny
|
jsr AsciiToScreen ; convert ascii char to screen char
|
cpy #8
|
sta (y)
|
blo .001
|
jsr GetScreenLocation
|
lda #':'
|
sub r3,r3,r1 ; make y an index into the screen
|
jsr DisplayChar
|
jsr GetColorCodeLocation
|
ldy #0
|
add r3,r3,r1
|
sub r2,r2,#8
|
lda CharColor
|
.002
|
sta (y)
|
lb r1,0,x
|
jsr IncCursorPos
|
cmp #26 ; convert control characters to '.'
|
bra dcx4
|
bhs .003
|
dclf:
|
lda #'.'
|
jsr IncCursorRow
|
.003:
|
dcx4:
|
jsr DisplayChar
|
|
inx
|
|
iny
|
|
cpy #8
|
|
blo .002
|
|
jsr CRLF
|
ply
|
ply
|
plx
|
|
pla
|
pla
|
pop r4
|
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
message "Monitor"
|
; Increment the cursor position, scroll the screen if needed.
|
;==============================================================================
|
;------------------------------------------------------------------------------
|
; System Monitor Program
|
|
; The system monitor is task#0
|
|
;==============================================================================
|
;
|
;
|
IncCursorPos:
|
Monitor:
|
pha
|
ldx #BIOS_STACKS+0x03FF ; setup stack pointer
|
phx
|
txs
|
push r4
|
lda #0 ; turn off keyboard echo
|
ld r4,RunningTCB
|
jsr SetKeyboardEcho
|
and r4,r4,#$FF
|
jsr RequestIOFocus
|
lda TCB_CursorCol,r4
|
.PromptLn:
|
ina
|
jsr CRLF
|
sta TCB_CursorCol,r4
|
lda #'$'
|
ldx TEXTREG+TEXT_COLS
|
jsr DisplayChar
|
cmp r1,r2
|
|
bcc icc1
|
|
stz TCB_CursorCol,r4 ; column = 0
|
|
bra icr1
|
|
IncCursorRow:
|
|
pha
|
|
phx
|
|
push r4
|
|
ld r4,RunningTCB
|
|
and r4,r4,#$FF
|
|
icr1:
|
|
lda TCB_CursorRow,r4
|
|
ina
|
|
sta TCB_CursorRow,r4
|
|
ldx TEXTREG+TEXT_ROWS
|
|
cmp r1,r2
|
|
bcc icc1
|
|
beq icc1
|
|
dex ; backup the cursor row, we are scrolling up
|
|
stx TCB_CursorRow,r4
|
|
jsr ScrollUp
|
|
icc1:
|
|
jsr UpdateCursorPos
|
|
icc2:
|
|
pop r4
|
|
plx
|
|
pla
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
; Get characters until a CR is keyed
|
; Display a string on the screen.
|
|
; The characters are packed 4 per word
|
|
;------------------------------------------------------------------------------
|
|
;
|
;
|
DisplayStringB:
|
.Prompt3:
|
pha
|
jsr RequestIOFocus
|
phx
|
; lw r1,#2 ; get keyboard character
|
tax ; r2 = pointer to string
|
; syscall #417
|
dspj1B:
|
; jsr KeybdCheckForKeyDirect
|
lb r1,0,x ; move string char into acc
|
; cmp #0
|
inx ; increment pointer
|
jsr KeybdGetChar
|
cmp #0 ; is it end of string ?
|
cmp #-1
|
beq dsretB
|
beq .Prompt3
|
jsr DisplayChar ; display character
|
; jsr KeybdGetCharDirect
|
bra dspj1B
|
cmp #CR
|
dsretB:
|
beq .Prompt1
|
plx
|
jsr DisplayChar
|
pla
|
bra .Prompt3
|
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
|
|
|
|
|
; 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
|
; Display a string on the screen.
|
|
; The characters are packed 1 per word
|
|
;------------------------------------------------------------------------------
|
|
;
|
;
|
DisplayStringW:
|
.Prompt2:
|
pha
|
cmp #'>'
|
phx
|
beq EditMem
|
tax ; r2 = pointer to string
|
cmp #'M'
|
dspj1W:
|
bne .testDIR
|
lda (x) ; move string char into acc
|
jsr MonGetch
|
inx ; increment pointer
|
cmp #'B'
|
cmp #0 ; is it end of string ?
|
beq DumpMemBytes
|
beq dsretW
|
dey
|
jsr DisplayChar ; display character
|
bra DumpMem
|
bra dspj1W ; go back for next character
|
.testDIR:
|
dsretW:
|
cmp #'D'
|
plx
|
bne .Prompt8
|
pla
|
cmp #'I'
|
rts
|
beq DoDir
|
|
bra Monitor
|
DisplayStringCRLFB:
|
.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
|
jsr DisplayStringB
|
CRLF:
|
jmp Monitor
|
pha
|
.Prompt10:
|
lda #'\r'
|
cmp #'C' ; $C - clear screen
|
jsr DisplayChar
|
beq TestCLS
|
lda #'\n'
|
cmp #'r'
|
jsr DisplayChar
|
bne .Prompt12
|
pla
|
lda #4 ; priority level 4
|
rts
|
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:
|
; Initialize keyboard
|
cmp #'T'
|
;
|
bne .Prompt15
|
; Issues a 'reset keyboard' command to the keyboard, then selects scan code
|
jsr MonGetch
|
; set #2 (the most common one). Also sets up the keyboard buffer and
|
cmp #'O'
|
; initializes the keyboard semaphore.
|
bne .Prompt14a
|
;------------------------------------------------------------------------------
|
jsr DumpTimeoutList
|
;
|
jmp Monitor
|
message "KeybdInit"
|
.Prompt14a:
|
KeybdInit:
|
cmp #'I'
|
lda #1 ; setup semaphore
|
bne .Prompt14b
|
sta KEYBD_SEMA
|
jsr DisplayDatetime
|
lda #32
|
jmp Monitor
|
sta LEDS
|
.Prompt14b:
|
ldx #0
|
cmp #'E'
|
|
bne .Prompt14c
|
|
jsr ReadTemp
|
|
jmp Monitor
|
|
.Prompt14c:
|
|
dey
|
|
jsr DumpTaskList
|
|
jmp Monitor
|
|
|
lda #MAX_TASKNO
|
.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
|
ldx #0
|
ldy #KeybdHead
|
ldy #test_mbx_prg
|
stos
|
ld r4,#0
|
lda #MAX_TASKNO
|
ld r5,#1 ; Job 1!
|
ldy #KeybdTail
|
int #4
|
stos
|
db 1
|
lda #MAX_TASKNO
|
bra Monitor
|
ldy #KeybdBad
|
|
stos
|
|
lda #MAX_TASKNO
|
|
ldx #1 ; turn on keyboard echo
|
|
ldy #KeybdEcho
|
|
stos
|
|
|
|
lda PIC_IE
|
message "Prompt16"
|
or r1,r1,#$8000 ; enable kbd_irq
|
RandomLinesCall:
|
sta PIC_IE
|
; jsr RandomLines
|
|
jmp Monitor
|
|
|
lda #33
|
MonGetch:
|
sta LEDS
|
lda (y)
|
lda #$ff ; issue keyboard reset
|
iny
|
jsr SendByteToKeybd
|
jsr ScreenToAscii
|
lda #38
|
|
sta LEDS
|
|
lda #1000000 ; delay a bit
|
|
kbdi5:
|
|
dea
|
|
sta LEDS
|
|
bne kbdi5
|
|
lda #34
|
|
sta LEDS
|
|
lda #0xf0 ; send scan code select
|
|
jsr SendByteToKeybd
|
|
lda #35
|
|
sta LEDS
|
|
ldx #0xFA
|
|
jsr WaitForKeybdAck
|
|
cmp #$FA
|
|
bne kbdi2
|
|
lda #36
|
|
sta LEDS
|
|
lda #2 ; select scan code set#2
|
|
jsr SendByteToKeybd
|
|
lda #39
|
|
sta LEDS
|
|
kbdi2:
|
|
rts
|
rts
|
|
|
msgBadKeybd:
|
DoDir:
|
db "Keyboard not responding.",0
|
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
|
|
|
SendByteToKeybd:
|
TestCLS:
|
phx
|
jsr MonGetch
|
ldx RunningTCB
|
cmp #'L'
|
sta KEYBD
|
bne Monitor
|
lda #40
|
jsr MonGetch
|
sta LEDS
|
cmp #'S'
|
tsr TICK,r3
|
bne Monitor
|
kbdi4: ; wait for transmit complete
|
jsr ClearScreen
|
tsr TICK,r4
|
jsr HomeCursor
|
sub r4,r4,r3
|
; jsr CalcScreenLoc
|
cmp r4,#1000000
|
jmp Monitor
|
bcs kbdbad
|
message "HelpMsg"
|
lda #41
|
HelpMsg:
|
sta LEDS
|
db "? = Display help",CR,LF
|
lda KEYBD+3
|
db "CLS = clear screen",CR,LF
|
bit #64
|
db "S = Boot from SD Card",CR,LF
|
beq kbdi4
|
db "SU = supermon816",CR,LF
|
bra sbtk1
|
db "L = Load Block",CR,LF
|
kbdbad:
|
db "W = Write Block",CR,LF
|
lda #42
|
db "DIR = Disk directory",CR,LF
|
sta LEDS
|
db "M = Dump memory words, MB = Dump memory bytes",CR,LF
|
lda KeybdBad,x
|
db "> = Edit memory words",CR,LF
|
bne sbtk1
|
db "F = Fill memory",CR,LF
|
lda #1
|
db "FL = Dump I/O Focus List",CR,LF
|
sta KeybdBad,x
|
; db "FIG = start FIG Forth",CR,LF
|
lda #43
|
db "KILL n = kill task #n",CR,LF
|
sta LEDS
|
db "B = start tiny basic",CR,LF
|
lda #msgBadKeybd
|
db "b = start EhBasic 6502",CR,LF
|
jsr DisplayStringCRLFB
|
db "J = Jump to code",CR,LF
|
sbtk1:
|
db "R = Dump registers, Rn = Set register value",CR,LF
|
lda #44
|
db "r = random lines - test bitmap",CR,LF
|
sta LEDS
|
db "e = ethernet test",CR,LF
|
plx
|
db "T = Dump task list",CR,LF
|
rts
|
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
|
|
|
; Wait for keyboard to respond with an ACK (FA)
|
;------------------------------------------------------------------------------
|
|
; Ignore blanks in the input
|
|
; r3 = text pointer
|
|
; r1 destroyed
|
|
;------------------------------------------------------------------------------
|
;
|
;
|
WaitForKeybdAck:
|
ignBlanks:
|
lda #64
|
ignBlanks1:
|
sta LEDS
|
jsr MonGetch
|
tsr TICK,r3
|
cmp #' '
|
wkbdack1:
|
beq ignBlanks1
|
tsr TICK,r4
|
dey
|
sub r4,r4,r3
|
|
cmp r4,#1000000
|
|
bcs wkbdbad
|
|
lda #65
|
|
sta LEDS
|
|
lda KEYBD
|
|
bit #$8000
|
|
beq wkbdack1
|
|
; lda KEYBD+8
|
|
and #$ff
|
|
wkbdbad:
|
|
rts
|
rts
|
|
|
; Wait for keyboard to respond with an ACK (FA)
|
;------------------------------------------------------------------------------
|
; This routine picks up the ack status left by the
|
; Edit memory byte(s).
|
; keyboard IRQ routine.
|
;------------------------------------------------------------------------------
|
; r2 = 0xFA (could also be 0xEE for echo command)
|
|
;
|
;
|
WaitForKeybdAck2:
|
EditMem:
|
phx
|
jsr ignBlanks
|
ldx RunningTCB
|
jsr GetHexNumber
|
WaitForKeybdAck2a:
|
ld r5,r1
|
lda KeybdAck,x
|
ld r4,#3
|
cmp r1,r2
|
edtmem1:
|
bne WaitForKeybdAck2a
|
jsr ignBlanks
|
stz KeybdAck,x
|
jsr GetHexNumber
|
plx
|
sta (r5)
|
rts
|
add r5,r5,#1
|
|
dec r4
|
|
bne edtmem1
|
|
jmp Monitor
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; KeybdIRQ
|
; Execute code at the specified address.
|
;
|
|
; Normal keyboard interrupt, the lowest priority interrupt in the system.
|
|
; Grab the character from the keyboard device and store it in a buffer.
|
|
; The buffer of the task with the input focus is updated.
|
|
; This IRQ has to check for the ALT-tab character and take care of
|
|
; switching the IO focus if detected. It can't be done in the KeybdGetChar
|
|
; because the app with the IO focus may not call that routine. We know for
|
|
; sure the interrupt routine will be called when a key is pressed.
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
message "KeybdIRQ"
|
message "ExecuteCode"
|
KeybdIRQ:
|
ExecuteCode:
|
cld
|
jsr ignBlanks
|
|
jsr GetHexNumber
|
|
st r1,JMPTMP
|
|
lda #xcret ; push return address so we can do an indirect jump
|
pha
|
pha
|
phx
|
ld r1,R1Save
|
phy
|
ld r2,R2Save
|
push r4
|
ld r3,R3Save
|
|
ld r4,R4Save
|
; support EhBASIC's IRQ functionality
|
ld r5,R5Save
|
; code derived from minimon.asm
|
ld r6,R6Save
|
lda #15 ; Keyboard is IRQ #15
|
ld r7,R7Save
|
sta IrqSource
|
ld r8,R8Save
|
lb r1,IrqBase ; get the IRQ flag byte
|
ld r9,R9Save
|
lsr r2,r1
|
ld r10,R10Save
|
or r1,r1,r2
|
ld r11,R11Save
|
and #$E0
|
ld r12,R12Save
|
sb r1,IrqBase ; save the new IRQ flag byte
|
ld r13,R13Save
|
|
ld r14,R14Save
|
ld r4,IOFocusNdx ; get the task with the input focus
|
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
|
|
|
ldx KEYBD ; get keyboard character
|
LoadBlock:
|
ld r0,KEYBD+1 ; clear keyboard strobe (turns off the IRQ)
|
jsr ignBlanks
|
txy ; check for a keyboard ACK code
|
jsr GetDecNumber
|
|
pha
|
bit r3,#$800 ; test bit #11
|
jsr ignBlanks
|
bne KeybdIRQc ; ignore keyup messages for now
|
jsr GetHexNumber
|
bit r3,#$200 ; check for ALT-tab
|
tax
|
beq KeybdIrq3
|
phx
|
and r3,r3,#$FF
|
; ld r2,#0x3800
|
cmp r3,#TAB ; if we find an ALT-tab
|
lda #16 ; SD Card device #
|
bne KeybdIrq3
|
ldx #1 ; Init
|
jsr SwitchIOFocus
|
jsr DeviceOp
|
bra KeybdIRQc ; don't store off the ALT-tab character
|
; jsr SDInit
|
KeybdIrq3:
|
|
and r3,r3,#$ff
|
|
cmp r3,#$FA
|
|
bne KeybdIrq1
|
|
sty KeybdAck,r4
|
|
bra KeybdIRQc
|
|
KeybdIrq1:
|
|
bit r2,#$800 ; test bit #11
|
|
bne KeybdIRQc ; ignore keyup messages for now
|
|
KeybdIrq2:
|
|
lda KeybdHead,r4
|
|
ina ; increment head pointer
|
|
and #$f ; limit
|
|
ldy KeybdTail,r4 ; check for room in the keyboard buffer
|
|
cmp r1,r3
|
|
beq KeybdIRQc ; if no room, the newest char will be lost
|
|
sta KeybdHead,r4
|
|
dea
|
|
and #$f
|
|
stx KeybdLocks,r4
|
|
asl r4,r4,#4 ; * 16
|
|
add r1,r1,r4
|
|
stx KeybdBuffer,r1 ; store character in buffer
|
|
KeybdIRQc:
|
|
pop r4
|
|
ply
|
|
plx
|
plx
|
pla
|
pla
|
rti
|
lda #16 ; SD Card device #
|
|
ldx #11 ; opcode: Read blocks
|
KeybdRstIRQ:
|
pop r5 ; r5 = pointer to data storage area
|
jmp start
|
ply ; y = block number to read
|
|
ld r4,#1 ; 1 block to read
|
|
jsr DeviceOp
|
|
; jsr SDReadSector
|
|
jmp Monitor
|
|
|
;------------------------------------------------------------------------------
|
WriteBlock:
|
; r1 0=echo off, non-zero = echo on
|
jsr ignBlanks
|
;------------------------------------------------------------------------------
|
jsr GetDecNumber
|
SetKeyboardEcho:
|
pha
|
|
jsr ignBlanks
|
|
jsr GetHexNumber
|
|
tax
|
phx
|
phx
|
ldx RunningTCB
|
jsr SDInit
|
sta KeybdEcho,x
|
|
plx
|
plx
|
rts
|
pla
|
|
jsr SDWriteSector
|
|
jmp Monitor
|
|
|
comment ~
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Get a bit from the I/O focus table.
|
; Command 'R'
|
|
; Dump the register set.
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
GetIOFocusBit:
|
message "DumpReg"
|
phx
|
DumpReg:
|
phy
|
ldy #0
|
tax
|
DumpReg1:
|
and r1,r1,#$1F ; get bit index into word
|
jsr CRLF
|
lsr r2,r2,#5 ; get word index into table
|
lda #'$'
|
ldy IOFocusTbl,x
|
jsr DisplayChar
|
lsr r3,r3,r1 ; extract bit
|
lda #'R'
|
and r1,r3,#1
|
jsr DisplayChar
|
ply
|
ldx #1
|
plx
|
tya
|
rts
|
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
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; ForceIOFocus
|
; Command 'Rn'
|
;
|
|
; Force the IO focus to a specific task.
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
SetRegValue:
|
ForceIOFocus:
|
jsr GetDecNumber
|
php
|
cmp #0
|
|
beq DumpReg
|
|
cmp #15
|
|
bpl Monitor
|
pha
|
pha
|
phy
|
jsr ignBlanks
|
ldy IOFocusNdx
|
jsr GetHexNumber
|
cmp r1,r3
|
|
beq fif1
|
|
jsr CopyScreenToVirtualScreen
|
|
sta IOFocusNdx
|
|
jsr CopyVirtualScreenToScreen
|
|
fif1:
|
|
ply
|
ply
|
pla
|
sta R1Save,y
|
plp
|
jmp Monitor
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; SwitchIOFocus
|
|
;
|
|
; Switches the IO focus to the next task requesting the I/O focus. This
|
|
; routine may be called when a task releases the I/O focus as well as when
|
|
; the user presses ALT-TAB on the keyboard.
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
GetTwoParams:
|
SwitchIOFocus:
|
jsr ignBlanks
|
pha
|
jsr GetHexNumber ; get start address of dump
|
phy
|
tax
|
|
jsr ignBlanks
|
; First check if it's even possible to switch the focus to another
|
jsr GetHexNumber ; get end address of dump
|
; task. The I/O focus list could be empty or there may be only a
|
|
; single task in the list. In either case it's not possible to
|
|
; switch.
|
|
ldy IOFocusNdx ; Get the task at the head of the list.
|
|
bmi siof3 ; Is the list empty ?
|
|
lda TCB_iof_next,y ; Get the next task on the list.
|
|
cmp r1,r3 ; Will the list head change ?
|
|
beq siof3 ; If not then no switch will occur
|
|
|
|
; Copy the current task's screen to it's virtual screen buffer.
|
|
jsr CopyScreenToVirtualScreen
|
|
|
|
sta IOFocusNdx ; Make task the new head of list.
|
|
|
|
; Copy the virtual screen of the task recieving the I/O focus to the
|
|
; text screen.
|
|
jsr CopyVirtualScreenToScreen
|
|
siof3:
|
|
ply
|
|
pla
|
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Get character from keyboard buffer
|
; Get a range, the end must be greater or equal to the start.
|
; return character in acc or -1 if no
|
|
; characters available.
|
|
; Also check for ALT-TAB and switch the I/O focus.
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
message "KeybdGetChar"
|
GetRange:
|
KeybdGetChar:
|
jsr GetTwoParams
|
php
|
cmp r2,r1
|
phx
|
bhi DisplayErr
|
push r4
|
|
sei
|
|
ld r4,RunningTCB
|
|
cmp r4,#MAX_TASKNO
|
|
bhi nochar
|
|
ldx KeybdTail,r4 ; if keybdTail==keybdHead then there are no
|
|
lda KeybdHead,r4 ; characters in the keyboard buffer
|
|
cmp r1,r2
|
|
beq nochar
|
|
asl r4,r4,#4 ; * 16
|
|
phx
|
|
add r2,r2,r4
|
|
lda KeybdBuffer,x
|
|
plx
|
|
and r1,r1,#$ff ; mask off control bits
|
|
inx ; increment index
|
|
and r2,r2,#$0f
|
|
lsr r4,r4,#4 ; / 16
|
|
stx KeybdTail,r4
|
|
ldx KeybdEcho,r4
|
|
beq kgc3
|
|
cmp #CR
|
|
bne kgc8
|
|
jsr CRLF ; convert CR keystroke into CRLF
|
|
bra kgc3
|
|
kgc8:
|
|
jsr DisplayChar
|
|
bra kgc3
|
|
nochar:
|
|
lda #-1
|
|
kgc3:
|
|
pop r4
|
|
plx
|
|
plp
|
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Check if there is a keyboard character available in the keyboard buffer.
|
; Command 'M'
|
; Returns
|
; Do a memory dump of the requested location.
|
; r1 = 1, Z=0 if there is a key available, otherwise
|
|
; r1 = 0, Z=1 if there is not a key available
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
message "KeybdCheckForKey"
|
DumpMem:
|
KeybdCheckForKey:
|
jsr GetRange
|
phx
|
jsr CRLF
|
push r4
|
DumpmemW:
|
php
|
jsr CheckKeys
|
sei
|
jsr DisplayMemW
|
ld r4,RunningTCB
|
cmp r2,r1
|
lda KeybdTail,r4
|
bls DumpmemW
|
ldx KeybdHead,r4
|
jmp Monitor
|
sub r1,r1,r2
|
|
bne kcfk1
|
DumpMemBytes:
|
plp
|
jsr GetRange
|
pop r4
|
jsr CRLF
|
plx
|
.001:
|
lda #0
|
jsr CheckKeys
|
rts
|
jsr DisplayMemBytes
|
kcfk1
|
cmp r2,r1
|
plp
|
bls .001
|
pop r4
|
jmp Monitor
|
plx
|
|
lda #1
|
|
rts
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Check if there is a keyboard character available. If so return true (1)
|
; CheckKeys:
|
; otherwise return false (0) in r1.
|
; Checks for a CTRLC or a scroll lock during long running dumps.
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
CheckKeys:
|
message "KeybdCheckForKeyDirect"
|
jsr CTRLCCheck
|
KeybdCheckForKeyDirect:
|
jmp CheckScrollLock
|
lda KEYBD
|
|
and #$8000
|
;------------------------------------------------------------------------------
|
beq kcfkd1
|
; CTRLCCheck
|
lda #1
|
; Checks to see if CTRL-C is pressed. If so then the current routine is
|
kcfkd1
|
; aborted and control is returned to the monitor.
|
|
;------------------------------------------------------------------------------
|
|
|
|
CTRLCCheck:
|
|
pha
|
|
jsr KeybdGetChar
|
|
cmp #CTRLC
|
|
beq .0001
|
|
pla
|
rts
|
rts
|
|
.0001:
|
|
pla
|
|
pla
|
|
jmp Monitor
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Get character directly from keyboard. This routine blocks until a key is
|
; CheckScrollLock:
|
; available.
|
; 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.
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
|
KeybdGetCharDirect:
|
CheckScrollLock:
|
phx
|
pha
|
kgc1:
|
.0002:
|
lda KEYBD
|
jsr GetPtrCurrentJCB
|
bit #$8000
|
lda JCB_KeybdLocks,r1
|
beq kgc1
|
bit #$4000 ; is scroll lock active ?
|
ld r0,KEYBD+1 ; clear keyboard strobe
|
beq .0001
|
bit #$800 ; is it a keydown event ?
|
int #2 ; reschedule tasks
|
bne kgc1
|
bra .0002
|
; bit #$200 ; check for ALT-tab
|
.0001:
|
; bne kgc2
|
pla
|
; and r2,r1,#$7f
|
|
; cmp r2,#TAB ; if we find an ALT-tab
|
|
; bne kgc2
|
|
; jsr SwitchIOFocus
|
|
; bra kgc1
|
|
;kgc2:
|
|
and #$ff ; remove strobe bit
|
|
ldx KeybdEcho ; is keyboard echo on ?
|
|
beq gk1
|
|
cmp #CR
|
|
bne gk2 ; convert CR keystroke into CRLF
|
|
jsr CRLF
|
|
bra gk1
|
|
gk2:
|
|
jsr DisplayChar
|
|
gk1:
|
|
plx
|
|
rts
|
rts
|
|
|
|
|
;==============================================================================
|
|
; Serial port
|
|
;==============================================================================
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Initialize the serial port
|
; Command 'F' or "FB"
|
; r1 = low 28 bits = baud rate
|
; Fill memory with specified value.
|
; r2 = other settings
|
|
; The desired baud rate must fit in 28 bits or less.
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
|
SerialInit:
|
|
; asl r1,r1,#4 ; * 16
|
|
; shlui r1,r1,#32 ; * 2^32
|
|
; inhu r2,CR_CLOCK ; get clock frequency from config record
|
|
; divu r1,r1,r2 ; / clock frequency
|
|
|
|
lsr r1,r1,#8 ; drop the lowest 8 bits
|
FillMem:
|
sta UART_CM1 ; set LSB
|
jsr GetRange
|
lsr r1,r1,#8
|
txy ; y = start address
|
sta UART_CM2 ; set middle bits
|
sub r1,r1,r2 ; acc = count
|
lsr r1,r1,#8
|
pha
|
sta UART_CM3 ; set MSB
|
jsr ignBlanks
|
stz Uart_rxhead ; reset buffer indexes
|
jsr GetHexNumber ; get the fill byte
|
stz Uart_rxtail
|
tax
|
lda #0x1f0
|
pla
|
sta Uart_foff ; set threshold for XOFF
|
stos
|
lda #0x010
|
jmp Monitor
|
sta Uart_fon ; set threshold for XON
|
|
lda #1
|
|
sta UART_IE ; enable receive interrupt only
|
|
stz Uart_rxrts ; no RTS/CTS signals available
|
|
stz Uart_txrts ; no RTS/CTS signals available
|
|
stz Uart_txdtr ; no DTR signals available
|
|
stz Uart_rxdtr ; no DTR signals available
|
|
lda #1
|
|
sta Uart_txxon ; for now
|
|
lda #1
|
|
sta SERIAL_SEMA
|
|
rts
|
|
|
|
;---------------------------------------------------------------------------------
|
FillMemBytes:
|
; Get character directly from serial port. Blocks until a character is available.
|
jsr GetRange
|
;---------------------------------------------------------------------------------
|
txy
|
;
|
sub r2,r1,r2 ; x = count
|
SerialGetCharDirect:
|
inx
|
sgc1:
|
jsr ignBlanks
|
lda UART_LS ; uart status
|
jsr GetHexNumber
|
and #rxfull ; is there a char available ?
|
.0001:
|
beq sgc1
|
sb r1,0,y
|
lda UART
|
iny
|
rts
|
dex
|
|
bne .0001
|
|
jmp Monitor
|
|
|
;------------------------------------------------
|
|
; Check for a character at the serial port
|
|
; returns r1 = 1 if char available, 0 otherwise
|
|
;------------------------------------------------
|
|
;
|
|
SerialCheckForCharDirect:
|
|
lda UART_LS ; uart status
|
|
and #rxfull ; is there a char available ?
|
|
rts
|
|
|
|
;-----------------------------------------
|
;------------------------------------------------------------------------------
|
; Put character to serial port
|
; Get a hexidecimal number. Maximum of eight digits.
|
; r1 = char to put
|
; R3 = text pointer (updated)
|
;-----------------------------------------
|
; R1 = hex number
|
|
;------------------------------------------------------------------------------
|
;
|
;
|
SerialPutChar:
|
GetHexNumber:
|
phx
|
phx
|
phy
|
|
push r4
|
push r4
|
push r5
|
ldx #0
|
|
ld r4,#8
|
ldx UART_MC
|
gthxn2:
|
or r2,r2,#3 ; assert DTR / RTS
|
jsr MonGetch
|
stx UART_MC
|
jsr AsciiToHexNybble
|
ldx Uart_txrts
|
cmp #-1
|
beq spcb1
|
beq gthxn1
|
ld r4,Milliseconds
|
asl r2,r2,#4
|
ldy #1000 ; delay count (1 s)
|
and #$0f
|
spcb3:
|
or r2,r2,r1
|
ldx UART_MS
|
dec r4
|
and r2,r2,#$10 ; is CTS asserted ?
|
bne gthxn2
|
bne spcb1
|
gthxn1:
|
ld r5,Milliseconds
|
txa
|
cmp r4,r5
|
|
beq spcb3
|
|
ld r4,r5
|
|
dey
|
|
bne spcb3
|
|
bra spcabort
|
|
spcb1:
|
|
ldx Uart_txdtr
|
|
beq spcb2
|
|
ld r4,Milliseconds
|
|
ldy #1000 ; delay count
|
|
spcb4:
|
|
ldx UART_MS
|
|
and r2,r2,#$20 ; is DSR asserted ?
|
|
bne spcb2
|
|
ld r5,Milliseconds
|
|
cmp r4,r5
|
|
beq spcb4
|
|
ld r4,r5
|
|
dey
|
|
bne spcb4
|
|
bra spcabort
|
|
spcb2:
|
|
ldx Uart_txxon
|
|
beq spcb5
|
|
spcb6:
|
|
ldx Uart_txxonoff
|
|
beq spcb5
|
|
ld r4,UART_MS
|
|
and r4,r4,#0x80 ; DCD ?
|
|
bne spcb6
|
|
spcb5:
|
|
ld r4,Milliseconds
|
|
ldy #1000 ; wait up to 1s
|
|
spcb8:
|
|
ldx UART_LS
|
|
and r2,r2,#0x20 ; tx not full ?
|
|
bne spcb7
|
|
ld r5,Milliseconds
|
|
cmp r4,r5
|
|
beq spcb8
|
|
ld r4,r5
|
|
dey
|
|
bne spcb8
|
|
bra spcabort
|
|
spcb7:
|
|
sta UART
|
|
spcabort:
|
|
pop r5
|
|
pop r4
|
pop r4
|
ply
|
|
plx
|
plx
|
rts
|
rts
|
|
|
;-------------------------------------------------
|
GetDecNumber:
|
; Compute number of characters in recieve buffer.
|
|
; r4 = number of chars
|
|
;-------------------------------------------------
|
|
CharsInRxBuf:
|
|
ld r4,Uart_rxhead
|
|
ldx Uart_rxtail
|
|
sub r4,r4,r2
|
|
bpl cirxb1
|
|
ld r4,#0x200
|
|
add r4,r4,r2
|
|
ldx Uart_rxhead
|
|
sub r4,r4,r2
|
|
cirxb1:
|
|
rts
|
|
|
|
;----------------------------------------------
|
|
; Get character from rx fifo
|
|
; If the fifo is empty enough then send an XON
|
|
;----------------------------------------------
|
|
;
|
|
SerialGetChar:
|
|
phx
|
phx
|
phy
|
|
push r4
|
push r4
|
|
push r5
|
ldy Uart_rxhead
|
ldx #0
|
ldx Uart_rxtail
|
ld r4,#10
|
cmp r2,r3
|
ld r5,#10
|
beq sgcfifo1 ; is there a char available ?
|
gtdcn2:
|
lda Uart_rxfifo,x ; get the char from the fifo into r1
|
jsr MonGetch
|
inx ; increment the fifo pointer
|
jsr AsciiToDecNybble
|
and r2,r2,#$1ff
|
cmp #-1
|
stx Uart_rxtail
|
beq gtdcn1
|
ldx Uart_rxflow ; using flow control ?
|
mul r2,r2,r5
|
beq sgcfifo2
|
add r2,r2,r1
|
ldy Uart_fon ; enough space in Rx buffer ?
|
dec r4
|
jsr CharsInRxBuf
|
bne gtdcn2
|
cmp r4,r3
|
gtdcn1:
|
bpl sgcfifo2
|
txa
|
stz Uart_rxflow ; flow off
|
pop r5
|
ld r4,Uart_rxrts
|
|
beq sgcfifo3
|
|
ld r4,UART_MC ; set rts bit in MC
|
|
or r4,r4,#2
|
|
st r4,UART_MC
|
|
sgcfifo3:
|
|
ld r4,Uart_rxdtr
|
|
beq sgcfifo4
|
|
ld r4,UART_MC ; set DTR
|
|
or r4,r4,#1
|
|
st r4,UART_MC
|
|
sgcfifo4:
|
|
ld r4,Uart_rxxon
|
|
beq sgcfifo5
|
|
ld r4,#XON
|
|
st r4,UART
|
|
sgcfifo5:
|
|
sgcfifo2: ; return with char in r1
|
|
pop r4
|
|
ply
|
|
plx
|
|
rts
|
|
sgcfifo1:
|
|
lda #-1 ; no char available
|
|
pop r4
|
pop r4
|
ply
|
|
plx
|
plx
|
rts
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
;-----------------------------------------
|
; Convert ASCII character in the range '0' to '9', 'a' to 'f' or 'A' to 'F'
|
; Serial port IRQ
|
; to a hex nybble.
|
;-----------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
SerialIRQ:
|
AsciiToHexNybble:
|
pha
|
cmp #'0'
|
phx
|
bcc gthx3
|
phy
|
cmp #'9'+1
|
push r4
|
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
|
|
|
lda UART_IS ; get interrupt status
|
AsciiToDecNybble:
|
bpl sirq1 ; no interrupt
|
cmp #'0'
|
and #0x7f ; switch on interrupt type
|
bcc gtdc3
|
cmp #4
|
cmp #'9'+1
|
beq srxirq
|
bcs gtdc3
|
cmp #$0C
|
sub #'0'
|
beq stxirq
|
rts
|
cmp #$10
|
gtdc3:
|
beq smsirq
|
lda #-1
|
; unknown IRQ type
|
rts
|
sirq1:
|
|
pop r4
|
|
ply
|
|
plx
|
|
pla
|
|
rti
|
|
|
|
|
DisplayErr:
|
|
lda #msgErr
|
|
jsr DisplayStringB
|
|
jmp Monitor
|
|
|
; Get the modem status and record it
|
msgErr:
|
smsirq:
|
db "**Err",CR,LF,0
|
lda UART_MS
|
|
sta Uart_ms
|
|
bra sirq1
|
|
|
|
stxirq:
|
|
bra sirq1
|
|
|
|
; Get a character from the uart and store it in the rx fifo
|
|
srxirq:
|
|
srxirq1:
|
|
lda UART ; get the char (clears interrupt)
|
|
ldx Uart_txxon
|
|
beq srxirq3
|
|
cmp #XOFF
|
|
bne srxirq2
|
|
lda #1
|
|
sta Uart_txxonoff
|
|
bra srxirq5
|
|
srxirq2:
|
|
cmp #XON
|
|
bne srxirq3
|
|
stz Uart_txxonoff
|
|
bra srxirq5
|
|
srxirq3:
|
|
stz Uart_txxonoff
|
|
ldx Uart_rxhead
|
|
sta Uart_rxfifo,x ; store in buffer
|
|
inx
|
|
and r2,r2,#$1ff
|
|
stx Uart_rxhead
|
|
srxirq5:
|
|
lda UART_LS ; check for another ready character
|
|
and #rxfull
|
|
bne srxirq1
|
|
lda Uart_rxflow ; are we using flow controls?
|
|
bne srxirq8
|
|
jsr CharsInRxBuf
|
|
lda Uart_foff
|
|
cmp r4,r1
|
|
bmi srxirq8
|
|
lda #1
|
|
sta Uart_rxflow
|
|
lda Uart_rxrts
|
|
beq srxirq6
|
|
lda UART_MC
|
|
and #$FD ; turn off RTS
|
|
sta UART_MC
|
|
srxirq6:
|
|
lda Uart_rxdtr
|
|
beq srxirq7
|
|
lda UART_MC
|
|
and #$FE ; turn off DTR
|
|
sta UART_MC
|
|
srxirq7:
|
|
lda Uart_rxxon
|
|
beq srxirq8
|
|
lda #XOFF
|
|
sta UART
|
|
srxirq8:
|
|
bra sirq1
|
|
|
|
|
;==============================================================================
|
|
;==============================================================================
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Display nybble in r1
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
ClearBmpScreen:
|
DisplayNybble:
|
|
pha
|
pha
|
and #$0F
|
phx
|
add #'0'
|
phy
|
cmp #'9'+1
|
lda #(680*384) ; a = # bytes to clear
|
bcc dispnyb1
|
ldx #0x29292929 ; acc = color for four pixels
|
add #7
|
ldy #BITMAPSCR;<<2 ; y = screen address
|
dispnyb1:
|
cbmp1:
|
jsr DisplayChar
|
; tsr LFSR,r2
|
|
; sb r2,0,y
|
|
; iny
|
|
; dea
|
|
; bne cbmp1
|
|
stos
|
|
ply
|
|
plx
|
pla
|
pla
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;==============================================================================
|
; Display the byte in r1
|
;==============================================================================
|
;------------------------------------------------------------------------------
|
;--------------------------------------------------------------------------
|
;
|
; Setup the AC97/LM4550 audio controller. Check keyboard for a CTRL-C
|
DisplayByte:
|
; interrupt which may be necessary if the audio controller isn't
|
pha
|
; responding.
|
lsr r1,r1,#4
|
;--------------------------------------------------------------------------
|
jsr DisplayNybble
|
|
pla
|
|
jmp DisplayNybble ; tail rts
|
|
message "785"
|
|
;------------------------------------------------------------------------------
|
|
; Display the half-word in r1
|
|
;------------------------------------------------------------------------------
|
|
;
|
;
|
DisplayHalf:
|
SetupAC97:
|
pha
|
pha
|
lsr r1,r1,#8
|
phx
|
jsr DisplayByte
|
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
|
pla
|
jsr DisplayByte
|
|
rts
|
rts
|
|
sac97Abort:
|
message "797"
|
lda #msgAC97bad
|
;------------------------------------------------------------------------------
|
jsr DisplayStringCRLFB
|
; Display the half-word in r1
|
pop r4
|
;------------------------------------------------------------------------------
|
ply
|
;
|
plx
|
DisplayWord:
|
|
pha
|
|
lsr r1,r1,#16
|
|
jsr DisplayHalf
|
|
pla
|
pla
|
jsr DisplayHalf
|
|
rts
|
rts
|
message "810"
|
|
;------------------------------------------------------------------------------
|
msgAC97bad:
|
; Display memory pointed to by r2.
|
db "The AC97 controller is not responding.",CR,LF,0
|
; destroys r1,r3
|
|
;------------------------------------------------------------------------------
|
;--------------------------------------------------------------------------
|
|
; Sound a 800 Hz beep
|
|
;--------------------------------------------------------------------------
|
;
|
;
|
DisplayMemW:
|
Beep:
|
pha
|
lda #2 ; check for a PSG
|
lda #':'
|
bmt CONFIGREC
|
jsr DisplayChar
|
beq .ret
|
txa
|
lda #15 ; master volume to max
|
jsr DisplayWord
|
sta PSG+64
|
lda #' '
|
lda #13422 ; 800Hz
|
jsr DisplayChar
|
sta PSGFREQ0
|
lda (x)
|
; decay (16.384 ms)2
|
jsr DisplayWord
|
; attack (8.192 ms)1
|
inx
|
; release (1.024 s)A
|
lda #' '
|
; sustain level C
|
jsr DisplayChar
|
lda #0xCA12
|
lda (x)
|
sta PSGADSR0
|
jsr DisplayWord
|
lda #0x1104 ; gate, output enable, triangle waveform
|
inx
|
sta PSGCTRL0
|
lda #' '
|
; lda #1000 ; delay about 1s
|
jsr DisplayChar
|
mSleep #1000
|
lda (x)
|
lda #0x0104 ; gate off, output enable, triangle waveform
|
jsr DisplayWord
|
sta PSGCTRL0
|
inx
|
; lda #1000 ; delay about 1s
|
lda #' '
|
mSleep #1000
|
jsr DisplayChar
|
lda #83
|
lda (x)
|
sta LEDS
|
jsr DisplayWord
|
lda #0x0000 ; gate off, output enable off, no waveform
|
inx
|
sta PSGCTRL0
|
jsr CRLF
|
.ret
|
pla
|
|
rts
|
rts
|
|
|
message "Monitor"
|
include "Piano.asm"
|
;==============================================================================
|
include "SDCard.asm"
|
; System Monitor Program
|
|
; The system monitor is task#0
|
; Load the root directory from disk
|
;==============================================================================
|
; r2 = where to place root directory in memory
|
;
|
;
|
Monitor:
|
loadBootFile:
|
ldx #BIOS_STACKS+0x03FF ; setup stack pointer
|
lb r1,BYTE_SECTOR_BUF+BSI_SecPerFAT+1 ; sectors per FAT
|
txs
|
asl r1,r1,#8
|
lda #0 ; turn off keyboard echo
|
orb r1,r1,BYTE_SECTOR_BUF+BSI_SecPerFAT
|
jsr SetKeyboardEcho
|
bne loadBootFile7
|
jsr RequestIOFocus
|
lb r1,BYTE_SECTOR_BUF+$27 ; sectors per FAT, FAT32
|
PromptLn:
|
asl r1,r1,#8
|
jsr CRLF
|
orb r1,r1,BYTE_SECTOR_BUF+$26
|
lda #'$'
|
asl r1,r1,#8
|
jsr DisplayChar
|
orb r1,r1,BYTE_SECTOR_BUF+$25
|
|
asl r1,r1,#8
|
; Get characters until a CR is keyed
|
orb r1,r1,BYTE_SECTOR_BUF+$24
|
;
|
loadBootFile7:
|
Prompt3:
|
lb r4,BYTE_SECTOR_BUF+$10 ; number of FATs
|
jsr RequestIOFocus
|
mul r3,r1,r4 ; offset
|
; lw r1,#2 ; get keyboard character
|
lb r1,BYTE_SECTOR_BUF+$F ; r1 = # reserved sectors before FAT
|
; syscall #417
|
asl r1,r1,#8
|
; jsr KeybdCheckForKeyDirect
|
orb r1,r1,BYTE_SECTOR_BUF+$E
|
; cmp #0
|
add r3,r3,r1 ; r3 = root directory sector number
|
jsr KeybdGetChar
|
ld r6,startSector
|
cmp #-1
|
add r5,r3,r6 ; r5 = root directory sector number
|
beq Prompt3
|
lb r1,BYTE_SECTOR_BUF+$D ; sectors per cluster
|
; jsr KeybdGetCharDirect
|
add r3,r1,r5 ; r3 = first cluster after first cluster of directory
|
cmp #CR
|
bra loadBootFile6
|
beq Prompt1
|
|
jsr DisplayChar
|
|
bra Prompt3
|
|
|
|
; Process the screen line that the CR was keyed on
|
loadBootFile6:
|
;
|
; For now we cheat and just go directly to sector 512.
|
Prompt1:
|
bra loadBootFileTmp
|
lda #80
|
|
sta LEDS
|
|
ldx RunningTCB
|
|
cpx #MAX_TASKNO
|
|
bhi Prompt3
|
|
lda #81
|
|
sta LEDS
|
|
stz TCB_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
|
loadBootFileTmp:
|
;
|
; We load the number of sectors per cluster, then load a single cluster of the file.
|
Prompt2:
|
; This is 16kib
|
cmp #':'
|
ld r5,r3 ; r5 = start sector of data area
|
beq EditMem
|
ld r2,#PROG_LOAD_AREA ; where to place file in memory
|
cmp #'D'
|
lb r3,BYTE_SECTOR_BUF+$D ; sectors per cluster
|
bne Prompt8
|
loadBootFile1:
|
jsr MonGetch
|
ld r1,r5 ; r1=sector to read
|
cmp #'R'
|
jsr SDReadSector
|
beq DumpReg
|
inc r5 ; r5 = next sector
|
cmp #'I'
|
add r2,r2,#512
|
beq DoDir
|
dec r3
|
dey
|
bne loadBootFile1
|
bra DumpMem
|
lda PROG_LOAD_AREA>>2 ; make sure it's bootable
|
Prompt8:
|
cmp #$544F4F42
|
cmp #'F'
|
bne loadBootFile2
|
bne Prompt7
|
lda #msgJumpingToBoot
|
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
|
|
lda #3
|
|
ldy #CSTART
|
|
ldx #0
|
|
jsr StartTask
|
|
; jsr CSTART
|
|
bra Monitor
|
|
Prompt4:
|
|
cmp #'b'
|
|
bne Prompt5
|
|
lda #3 ; priority level 3
|
|
ldy #$C000 ; start address $C000
|
|
ldx #$00000000 ; flags:
|
|
jsr StartTask
|
|
bra Monitor
|
|
emm
|
|
cpu W65C02
|
|
jml $0C000
|
|
cpu rtf65002
|
|
Prompt5:
|
|
cmp #'J' ; $J - execute code
|
|
beq ExecuteCode
|
|
cmp #'L' ; $L - load dector
|
|
beq LoadSector
|
|
cmp #'W'
|
|
beq WriteSector
|
|
Prompt9:
|
|
cmp #'?' ; $? - display help
|
|
bne Prompt10
|
|
lda #HelpMsg
|
|
jsr DisplayStringB
|
jsr DisplayStringB
|
|
lda (PROG_LOAD_AREA>>2)+$1
|
|
jsr (r1)
|
jmp Monitor
|
jmp Monitor
|
Prompt10:
|
loadBootFile2:
|
cmp #'C' ; $C - clear screen
|
lda #msgNotBootable
|
beq TestCLS
|
jsr DisplayStringB
|
cmp #'r'
|
ldx #PROG_LOAD_AREA>>2
|
bne Prompt12
|
jsr DisplayMemW
|
lda #4 ; priority level 4
|
jsr DisplayMemW
|
ldx #0 ; zero all flags at startup
|
jsr DisplayMemW
|
ldy #RandomLines ; task address
|
jsr DisplayMemW
|
; jsr (y)
|
|
jsr StartTask
|
|
jmp Monitor
|
|
; jmp RandomLinesCall
|
|
Prompt12:
|
|
Prompt13:
|
|
cmp #'P'
|
|
bne Prompt14
|
|
lda #2
|
|
ldx #0
|
|
ldy #Piano
|
|
jsr StartTask
|
|
jmp Monitor
|
jmp Monitor
|
|
|
Prompt14:
|
msgJumpingToBoot:
|
cmp #'T'
|
db "Jumping to boot",0
|
bne Prompt15
|
msgNotBootable:
|
jsr MonGetch
|
db "SD card not bootable.",0
|
cmp #'O'
|
spi_init_ok_msg:
|
bne Prompt14a
|
db "SD card initialized okay.",0
|
jsr DumpTimeoutList
|
spi_init_error_msg:
|
jmp Monitor
|
db ": error occurred initializing the SD card.",0
|
Prompt14a:
|
spi_boot_error_msg:
|
cmp #'I'
|
db "SD card boot error",CR,LF,0
|
bne Prompt14b
|
spi_read_error_msg:
|
jsr DisplayDatetime
|
db "SD card read error",CR,LF,0
|
jmp Monitor
|
spi_write_error_msg:
|
Prompt14b:
|
db "SD card write error",0
|
cmp #'E'
|
|
bne Prompt14c
|
|
jsr ReadTemp
|
|
jmp Monitor
|
|
Prompt14c:
|
|
dey
|
|
jsr DumpTaskList
|
|
jmp Monitor
|
|
|
|
Prompt15:
|
do_fmt:
|
cmp #'S'
|
jsr SDInit
|
bne Prompt16
|
|
jsr MonGetch
|
|
cmp #'P'
|
|
bne Prompt18
|
|
jsr ignBlanks
|
|
jsr GetHexNumber
|
|
sta SPSave
|
|
jmp Monitor
|
|
Prompt18:
|
|
dey
|
|
jsr spi_init
|
|
cmp #0
|
|
bne Monitor
|
|
jsr spi_read_part
|
|
cmp #0
|
|
bne Monitor
|
|
jsr spi_read_boot
|
|
cmp #0
|
cmp #0
|
bne Monitor
|
bne fmt_abrt
|
jsr loadBootFile
|
; clear out the directory buffer
|
jmp Monitor
|
lda #65535
|
Prompt16:
|
|
cmp #'e'
|
|
bne Prompt17
|
|
lda #1
|
|
ldx #0
|
ldx #0
|
ldy #eth_main
|
ldy #DIRBUF
|
jsr StartTask
|
stos
|
; jsr eth_main
|
jsr store_dir
|
jmp Monitor
|
fmt_abrt:
|
Prompt17:
|
rts
|
cmp #'R'
|
|
bne Prompt19
|
|
jsr MonGetch
|
|
cmp #'S'
|
|
beq LoadSector
|
|
dey
|
|
bra SetRegValue
|
|
jmp Monitor
|
|
Prompt19:
|
|
cmp #'K'
|
|
bne Monitor
|
|
Prompt19a:
|
|
jsr MonGetch
|
|
cmp #' '
|
|
bne Prompt19a
|
|
jsr ignBlanks
|
|
jsr GetDecNumber
|
|
jsr KillTask
|
|
jmp Monitor
|
|
|
|
message "Prompt16"
|
|
RandomLinesCall:
|
|
; jsr RandomLines
|
|
jmp Monitor
|
|
|
|
MonGetch:
|
do_dir:
|
lda (y)
|
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
|
iny
|
jsr ScreenToAscii
|
dex
|
rts
|
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:
|
|
|
DoDir:
|
dirabrt:
|
jsr do_dir
|
rts
|
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:
|
load_dir:
|
jsr MonGetch
|
pha
|
cmp #'L'
|
phx
|
bne Monitor
|
phy
|
jsr MonGetch
|
lda #4000
|
cmp #'S'
|
ldx #DIRBUF<<2
|
bne Monitor
|
ldy #64
|
jsr ClearScreen
|
jsr SDReadMultiple
|
ldx RunningTCB
|
ply
|
stz TCB_CursorCol,x
|
plx
|
stz TCB_CursorRow,x
|
pla
|
jsr CalcScreenLoc
|
rts
|
jmp Monitor
|
store_dir:
|
message "HelpMsg"
|
pha
|
HelpMsg:
|
phx
|
db "? = Display help",CR,LF
|
phy
|
db "CLS = clear screen",CR,LF
|
lda #4000
|
db "S = Boot from SD Card",CR,LF
|
ldx #DIRBUF<<2
|
db ": = Edit memory bytes",CR,LF
|
ldy #64
|
db "L = Load sector",CR,LF
|
jsr SDWriteMultiple
|
db "W = Write sector",CR,LF
|
ply
|
db "DR = Dump registers",CR,LF
|
plx
|
db "D = Dump memory",CR,LF
|
pla
|
db "F = Fill memory",CR,LF
|
rts
|
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[n] = 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,0
|
|
|
|
;------------------------------------------------------------------------------
|
; r1 = pointer to file name
|
; Ignore blanks in the input
|
; r2 = pointer to buffer to save
|
; r3 = text pointer
|
; r3 = length of buffer
|
; r1 destroyed
|
|
;------------------------------------------------------------------------------
|
|
;
|
;
|
ignBlanks:
|
do_save:
|
ignBlanks1:
|
pha
|
jsr MonGetch
|
jsr SDInit
|
cmp #' '
|
cmp #0
|
beq ignBlanks1
|
bne dsavErr
|
dey
|
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
|
rts
|
|
|
;------------------------------------------------------------------------------
|
msgDiskFull
|
; Edit memory byte(s).
|
db CR,LF,"The disk is full, unable to save file.",CR,LF,0
|
;------------------------------------------------------------------------------
|
|
;
|
|
EditMem:
|
|
jsr ignBlanks
|
|
jsr GetHexNumber
|
|
or r5,r1,r0
|
|
ld r4,#3
|
|
edtmem1:
|
|
jsr ignBlanks
|
|
jsr GetHexNumber
|
|
sta (r5)
|
|
add r5,r5,#1
|
|
dec r4
|
|
bne edtmem1
|
|
jmp Monitor
|
|
|
|
;------------------------------------------------------------------------------
|
do_load:
|
; 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
|
pha
|
ld r1,R1Save
|
jsr SDInit
|
ld r2,R2Save
|
cmp #0
|
ld r3,R3Save
|
bne dsavErr
|
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
|
pla
|
sta SRSave
|
jsr load_dir
|
jmp Monitor
|
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
|
|
|
LoadSector:
|
msgFileNotFound:
|
jsr ignBlanks
|
db CR,LF,"File not found.",CR,LF
|
jsr GetDecNumber
|
|
pha
|
|
jsr ignBlanks
|
|
jsr GetHexNumber
|
|
tax
|
|
phx
|
|
; ld r2,#0x3800
|
|
jsr spi_init
|
|
plx
|
|
pla
|
|
jsr spi_read_sector
|
|
jmp Monitor
|
|
|
|
WriteSector:
|
;include "ethernet.asm"
|
jsr ignBlanks
|
|
jsr GetDecNumber
|
;--------------------------------------------------------------------------
|
|
; 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
|
pha
|
jsr ignBlanks
|
|
jsr GetHexNumber
|
|
tax
|
|
phx
|
phx
|
jsr spi_init
|
|
plx
|
|
pla
|
|
jsr spi_write_sector
|
|
jmp Monitor
|
|
|
|
;------------------------------------------------------------------------------
|
|
; 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 #' '
|
lda #' '
|
jsr DisplayChar
|
jsr DisplayChar
|
lda R1Save,y
|
stz DATETIME_SNAPSHOT ; take a snapshot of the running date/time
|
jsr DisplayWord
|
lda DATETIME_DATE
|
iny
|
tax
|
cpy #15
|
lsr r1,r1,#16
|
bne DumpReg1
|
jsr DisplayHalf ; display the year
|
jsr CRLF
|
lda #'/'
|
lda #':'
|
|
jsr DisplayChar
|
jsr DisplayChar
|
lda #'S'
|
txa
|
|
lsr r1,r1,#8
|
|
and #$FF
|
|
jsr DisplayByte ; display the month
|
|
lda #'/'
|
jsr DisplayChar
|
jsr DisplayChar
|
lda #'P'
|
txa
|
|
and #$FF
|
|
jsr DisplayByte ; display the day
|
|
lda #' '
|
jsr DisplayChar
|
jsr DisplayChar
|
lda #' '
|
lda #' '
|
jsr DisplayChar
|
jsr DisplayChar
|
lda TCB_SPSave
|
lda DATETIME_TIME
|
jsr DisplayWord
|
|
jsr CRLF
|
|
jmp Monitor
|
|
;------------------------------------------------------------------------------
|
|
;------------------------------------------------------------------------------
|
|
SetRegValue:
|
|
jsr GetDecNumber
|
|
cmp #15
|
|
bpl Monitor
|
|
pha
|
|
jsr ignBlanks
|
|
jsr GetHexNumber
|
|
ply
|
|
sta R1Save,y
|
|
jmp Monitor
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Do a memory dump of the requested location.
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
DumpMem:
|
|
jsr ignBlanks
|
|
jsr GetHexNumber ; get start address of dump
|
|
tax
|
tax
|
jsr ignBlanks
|
lsr r1,r1,#24
|
jsr GetHexNumber ; get number of words to dump
|
jsr DisplayByte ; display hours
|
lsr ; 1/4 as many dump rows
|
lda #':'
|
lsr
|
jsr DisplayChar
|
bne Dumpmem2
|
txa
|
lda #1 ; dump at least one row
|
lsr r1,r1,#16
|
Dumpmem2:
|
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
|
jsr CRLF
|
bra DumpmemW
|
plx
|
DumpmemW:
|
pla
|
jsr DisplayMemW
|
rts
|
dea
|
|
bne DumpmemW
|
|
jmp Monitor
|
|
|
|
|
include "ReadTemp.asm"
|
|
|
bra Monitor
|
include "memory.asm"
|
message "FillMem"
|
|
FillMem:
|
|
jsr ignBlanks
|
|
jsr GetHexNumber ; get start address of dump
|
|
tax
|
|
jsr ignBlanks
|
|
jsr GetHexNumber ; get number of bytes to fill
|
|
ld r5,r1
|
|
jsr ignBlanks
|
|
jsr GetHexNumber ; get the fill byte
|
|
FillmemW:
|
|
sta (x)
|
|
inx
|
|
dec r5
|
|
bne FillmemW
|
|
jmp Monitor
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Get a hexidecimal number. Maximum of eight digits.
|
; Bus Error Routine
|
; R3 = text pointer (updated)
|
; This routine display a message then restarts the BIOS.
|
; R1 = hex number
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
GetHexNumber:
|
message "bus_err_rout"
|
phx
|
bus_err_rout:
|
push r4
|
cld
|
ldx #0
|
ldx #87
|
ld r4,#8
|
stx LEDS
|
gthxn2:
|
pla ; get rid of the stacked flags
|
jsr MonGetch
|
ply ; get the error PC
|
jsr AsciiToHexNybble
|
ldx #$05FFFFF8 ; setup stack pointer top of memory
|
cmp #-1
|
txs
|
beq gthxn1
|
ldx #88
|
asl r2,r2,#4
|
stx LEDS
|
and #$0f
|
jsr CRLF
|
or r2,r2,r1
|
stz RunningTCB
|
dec r4
|
lda #JCBs
|
bne gthxn2
|
sta IOFocusNdx
|
gthxn1:
|
lda #msgBusErr
|
txa
|
jsr DisplayStringB
|
pop r4
|
tya
|
plx
|
jsr DisplayWord ; display the originating PC address
|
rts
|
lda #msgDataAddr
|
|
jsr DisplayStringB
|
GetDecNumber:
|
tsr #9,r1
|
phx
|
jsr DisplayWord
|
push r4
|
ldx #89
|
push r5
|
stx LEDS
|
ldx #0
|
ldx #128
|
ld r4,#10
|
ber2:
|
ld r5,#10
|
lda #' '
|
gtdcn2:
|
jsr DisplayChar
|
jsr MonGetch
|
tsr hist,r1
|
jsr AsciiToDecNybble
|
jsr DisplayWord
|
cmp #-1
|
dex
|
beq gtdcn1
|
bne ber2
|
mul r2,r2,r5
|
jsr CRLF
|
add r2,r2,r1
|
ber3:
|
dec r4
|
nop
|
bne gtdcn2
|
jmp ber3
|
gtdcn1:
|
;cli ; enable interrupts so we can get a char
|
txa
|
ber1:
|
pop r5
|
jsr KeybdGetCharDirect ; Don't use the keyboard buffer
|
pop r4
|
cmp #-1
|
plx
|
beq ber1
|
rts
|
lda RunningTCB
|
|
jsr KillTask
|
|
jmp SelectTaskToRun
|
|
|
|
msgBusErr:
|
|
db "Bus error at: ",0
|
|
msgDataAddr:
|
|
db " data address: ",0
|
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Convert ASCII character in the range '0' to '9', 'a' to 'f' or 'A' to 'F'
|
; 1000 Hz interrupt
|
; to a hex nybble.
|
; This IRQ must be fast.
|
|
; Increments the millisecond counter
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
message "p1000Hz"
|
AsciiToHexNybble:
|
p1000Hz:
|
cmp #'0'
|
pha
|
bcc gthx3
|
lda #2 ; reset edge sense circuit
|
cmp #'9'+1
|
sta PIC_RSTE
|
bcs gthx5
|
inc Milliseconds ; increment milliseconds count
|
sub #'0'
|
pla
|
rts
|
rti
|
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
|
|
|
|
|
;------------------------------------------------------------------------------
|
|
; 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.
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
ClearBmpScreen:
|
InvalidOpIRQ:
|
pha
|
pha
|
phx
|
phx
|
phy
|
phy
|
lda #(1364*768)>>2 ; a = # words to clear
|
tsx
|
ldx #0x29292929 ; acc = color for four pixels
|
lda 4,x ; get the address of the invalid op off the stack
|
ldy #BITMAPSCR ; y = screen address
|
lb r3,0,r1 ; get the opcode byte
|
stos
|
cpy #$44 ; is it MVP ?
|
;cbsj4
|
beq EmuMVP
|
; sta (y) ; store pixel data
|
cpy #$54 ; is it MVN ?
|
; iny ; advance screen address
|
beq EmuMVN
|
; dex ; decrement pixel count and loop back
|
; We don't know what the op is. Treat it like a NOP
|
; bne cbsj4
|
; 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
|
ply
|
plx
|
plx
|
pla
|
pla
|
rts
|
rti
|
|
|
;==============================================================================
|
DumpHistoryTable:
|
;==============================================================================
|
|
;--------------------------------------------------------------------------
|
|
; 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
|
pha
|
phx
|
phx
|
phy
|
ldx #64
|
|
ioi1:
|
|
tsr hist,r1
|
|
jsr DisplayWord
|
|
lda #' '
|
|
jsr DisplayChar
|
|
dex
|
|
bne ioi1
|
|
plx
|
|
pla
|
|
rts
|
|
|
|
EmuMVP:
|
push r4
|
push r4
|
ld r4,Milliseconds
|
push r5
|
sac974:
|
tsr sp,r4
|
stz AC97+0x26 ; trigger a read of register 26 (status reg)
|
lda 4,r4
|
sac971: ; wait for status to register 0xF (all ready)
|
ldx 3,r4
|
ld r3,Milliseconds
|
ldy 2,r4
|
sub r3,r3,r4
|
EmuMVP1:
|
cmp r3,#1000
|
ld r5,(x)
|
bhi sac97Abort
|
st r5,(y)
|
jsr KeybdGetChar ; see if we needed to CTRL-C
|
dex
|
cmp #CTRLC
|
dey
|
beq sac973
|
dea
|
lda AC97+0x68 ; wait for dirty bit to clear
|
cmp #$FFFFFFFF
|
bne sac971
|
bne EmuMVP1
|
lda AC97+0x26 ; check status at reg h26, wait for
|
sta 4,r4
|
and #0x0F ; analogue to be ready
|
stx 3,r4
|
cmp #$0F
|
sty 2,r4
|
bne sac974
|
inc 6,r4 ; increment the return address by one.
|
sac973:
|
pop r5
|
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
|
pop r4
|
ply
|
ply
|
plx
|
plx
|
pla
|
pla
|
rts
|
rti
|
sac97Abort:
|
|
lda #msgAC97bad
|
EmuMVN:
|
jsr DisplayStringCRLFB
|
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
|
pop r4
|
ply
|
ply
|
plx
|
plx
|
pla
|
pla
|
rts
|
rti
|
|
|
msgAC97bad:
|
|
db "The AC97 controller is not responding.",CR,LF,0
|
|
|
|
;--------------------------------------------------------------------------
|
|
; Sound a 800 Hz beep
|
|
;--------------------------------------------------------------------------
|
|
;
|
|
Beep:
|
|
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 #100 ; delay about 1s
|
|
jsr Sleep
|
|
lda #0x0104 ; gate off, output enable, triangle waveform
|
|
sta PSGCTRL0
|
|
lda #100 ; delay about 1s
|
|
jsr Sleep
|
|
lda #83
|
|
sta LEDS
|
|
lda #0x0000 ; gate off, output enable off, no waveform
|
|
sta PSGCTRL0
|
|
rts
|
|
|
|
;--------------------------------------------------------------------------
|
msgUnimp:
|
;--------------------------------------------------------------------------
|
db "Unimplemented at: ",0
|
;
|
|
Piano:
|
|
jsr RequestIOFocus
|
|
lda #15 ; master volume to max
|
|
sta PSG+64
|
|
playnt:
|
|
jsr KeybdGetChar
|
|
cmp #CTRLC
|
|
beq PianoX
|
|
cmp #'a'
|
|
beq playnt1a
|
|
cmp #'b'
|
|
beq playnt1b
|
|
cmp #'c'
|
|
beq playnt1c
|
|
cmp #'d'
|
|
beq playnt1d
|
|
cmp #'e'
|
|
beq playnt1e
|
|
cmp #'f'
|
|
beq playnt1f
|
|
cmp #'g'
|
|
beq playnt1g
|
|
bra playnt
|
|
PianoX:
|
|
jsr ReleaseIOFocus
|
|
rts
|
|
|
|
playnt1a:
|
brk_rout:
|
lda #7217
|
lda #16
|
jsr Tone
|
sta LEDS
|
bra playnt
|
jsr kernel_panic
|
playnt1b:
|
db "Break routine",0
|
lda #8101
|
jsr DumpHistoryTable
|
jsr Tone
|
stp
|
bra playnt
|
rti
|
playnt1c:
|
|
lda #4291
|
|
jsr Tone
|
|
bra playnt
|
|
playnt1d:
|
|
lda #4817
|
|
jsr Tone
|
|
bra playnt
|
|
playnt1e:
|
|
lda #5407
|
|
jsr Tone
|
|
bra playnt
|
|
playnt1f:
|
|
lda #5728
|
|
jsr Tone
|
|
bra playnt
|
|
playnt1g:
|
|
lda #6430
|
|
jsr Tone
|
|
bra playnt
|
|
|
|
Tone:
|
nmirout:
|
pha
|
pha
|
sta PSGFREQ0
|
lda #msgPerr
|
; decay (16.384 ms)2
|
jsr DisplayStringB
|
; attack (8.192 ms)1
|
lda 3,sp
|
; release (1.024 s)A
|
jsr DisplayWord
|
; sustain level C
|
jsr CRLF
|
lda #0xCA12
|
|
sta PSGADSR0
|
|
lda #0x1104 ; gate, output enable, triangle waveform
|
|
sta PSGCTRL0
|
|
lda #1 ; delay about 10ms
|
|
jsr Sleep
|
|
lda #0x0104 ; gate off, output enable, triangle waveform
|
|
sta PSGCTRL0
|
|
lda #1 ; delay about 10ms
|
|
jsr Sleep
|
|
lda #0x0000 ; gate off, output enable off, no waveform
|
|
sta PSGCTRL0
|
|
pla
|
pla
|
rts
|
rti
|
|
|
|
msgPerr:
|
|
db "Parity error at: ",0
|
|
|
;==============================================================================
|
;==============================================================================
|
|
; Finitron Multi-Tasking Kernel (FMTK)
|
|
; __
|
|
; \\__/ o\ (C) 2013, 2014 Robert Finch, Stratford
|
|
; \ __ / All rights reserved.
|
|
; \/_// robfinch@opencores.org
|
|
; ||
|
;==============================================================================
|
;==============================================================================
|
;
|
message "FMTK"
|
; Initialize the SD card
|
org $FFFFC000
|
; Returns
|
syscall_vectors:
|
; acc = 0 if successful, 1 otherwise
|
dw MTKInitialize
|
; Z=1 if successful, otherwise Z=0
|
dw StartTask
|
;
|
dw ExitTask
|
message "spi_init"
|
dw KillTask
|
spi_init
|
dw SetTaskPriority
|
lda #SPI_INIT_SD
|
dw Sleep
|
sta SPIMASTER+SPI_TRANS_TYPE_REG
|
dw AllocMbx
|
lda #SPI_TRANS_START
|
dw FreeMbx
|
sta SPIMASTER+SPI_TRANS_CTRL_REG
|
dw PostMsg
|
nop
|
dw SendMsg
|
spi_init1
|
dw WaitMsg
|
lda SPIMASTER+SPI_TRANS_STATUS_REG
|
dw CheckMsg
|
nop
|
|
nop
|
org $FFFFC200
|
cmp #SPI_TRANS_BUSY
|
message "MTKInitialize"
|
beq spi_init1
|
MTKInitialize:
|
lda SPIMASTER+SPI_TRANS_ERROR_REG
|
; Initialize semaphores
|
and #3
|
|
cmp #SPI_INIT_NO_ERROR
|
|
bne spi_error
|
|
; lda #spi_init_ok_msg
|
|
; jsr DisplayStringB
|
|
lda #0
|
|
rts
|
|
spi_error
|
|
jsr DisplayByte
|
|
lda #spi_init_error_msg
|
|
jsr DisplayStringB
|
|
lda SPIMASTER+SPI_RESP_BYTE1
|
|
jsr DisplayByte
|
|
lda SPIMASTER+SPI_RESP_BYTE2
|
|
jsr DisplayByte
|
|
lda SPIMASTER+SPI_RESP_BYTE3
|
|
jsr DisplayByte
|
|
lda SPIMASTER+SPI_RESP_BYTE4
|
|
jsr DisplayByte
|
|
lda #1
|
lda #1
|
rts
|
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
|
|
|
spi_delay:
|
tsr vbr,r2
|
nop
|
and r2,#-2
|
nop
|
lda #reschedule
|
rts
|
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
|
|
|
; SPI read sector
|
; Initialize IO Focus List
|
;
|
|
; r1= sector number to read
|
|
; r2= address to place read data
|
|
; Returns:
|
|
; r1 = 0 if successful
|
|
;
|
;
|
spi_read_sector:
|
lda #7
|
phx
|
ldx #0
|
phy
|
ldy #IOFocusTbl
|
push r4
|
stos
|
|
|
sta SPIMASTER+SPI_SD_SECT_7_0_REG
|
; Set owning job to zero (the monitor)
|
lsr r1,r1,#8
|
lda #255
|
sta SPIMASTER+SPI_SD_SECT_15_8_REG
|
ldx #0
|
lsr r1,r1,#8
|
ldy #TCB_hJCB
|
sta SPIMASTER+SPI_SD_SECT_23_16_REG
|
stos
|
lsr r1,r1,#8
|
|
sta SPIMASTER+SPI_SD_SECT_31_24_REG
|
|
|
|
ld r4,#20 ; retry count
|
; 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
|
|
|
spi_read_retry:
|
; Setup default values in the JCB's
|
; Force the reciever fifo to be empty, in case a prior error leaves it
|
ldy #0
|
; in an unknown state.
|
ldx #JCBs
|
lda #1
|
ijcb1:
|
sta SPIMASTER+SPI_RX_FIFO_CTRL_REG
|
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
|
|
|
lda #RW_READ_SD_BLOCK
|
|
sta SPIMASTER+SPI_TRANS_TYPE_REG
|
|
lda #SPI_TRANS_START
|
|
sta SPIMASTER+SPI_TRANS_CTRL_REG
|
|
nop
|
|
spi_read_sect1:
|
|
lda SPIMASTER+SPI_TRANS_STATUS_REG
|
|
jsr spi_delay ; just a delay between consecutive status reg reads
|
|
cmp #SPI_TRANS_BUSY
|
|
beq spi_read_sect1
|
|
lda SPIMASTER+SPI_TRANS_ERROR_REG
|
|
lsr
|
|
lsr
|
|
and #3
|
|
cmp #SPI_READ_NO_ERROR
|
|
bne spi_read_error
|
|
ldy #512 ; read 512 bytes from fifo
|
|
spi_read_sect2:
|
|
lda SPIMASTER+SPI_RX_FIFO_DATA_REG
|
|
sb r1,0,x
|
|
inx
|
|
dey
|
|
bne spi_read_sect2
|
|
lda #0
|
|
bra spi_read_ret
|
|
spi_read_error:
|
|
dec r4
|
|
bne spi_read_retry
|
|
jsr DisplayByte
|
|
lda #spi_read_error_msg
|
|
jsr DisplayStringB
|
|
lda #1
|
|
spi_read_ret:
|
|
pop r4
|
|
ply
|
|
plx
|
|
rts
|
|
|
|
; SPI write sector
|
; Initialize free message list
|
;
|
lda #NR_MSG
|
; r1= sector number to write
|
sta nMsgBlk
|
; r2= address to get data from
|
stz FreeMsg
|
; Returns:
|
ldx #0
|
; r1 = 0 if successful
|
|
;
|
|
spi_write_sector:
|
|
phx
|
|
phy
|
|
pha
|
|
; Force the transmitter fifo to be empty, in case a prior error leaves it
|
|
; in an unknown state.
|
|
lda #1
|
lda #1
|
sta SPIMASTER+SPI_TX_FIFO_CTRL_REG
|
st4:
|
nop ; give I/O time to respond
|
sta MSG_LINK,x
|
nop
|
ina
|
|
inx
|
|
cpx #NR_MSG
|
|
bne st4
|
|
lda #-1
|
|
sta MBX_LINK+NR_MSG-1
|
|
|
; now fill up the transmitter fifo
|
; Initialize free mailbox list
|
ldy #512
|
; Note the first NR_TCB mailboxes are statically allocated to the tasks.
|
spi_write_sect1:
|
; They are effectively pre-allocated.
|
lb r1,0,x
|
lda #NR_MBX-NR_TCB
|
sta SPIMASTER+SPI_TX_FIFO_DATA_REG
|
sta nMailbox
|
nop ; give the I/O time to respond
|
|
nop
|
ldx #NR_TCB
|
|
stx FreeMbxHandle
|
|
lda #NR_TCB+1
|
|
st3:
|
|
sta MBX_LINK,x
|
|
ina
|
inx
|
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
|
dey
|
bne spi_write_sect1
|
bne st5
|
|
stz JCB_Next,x
|
|
|
; set the sector number in the spi master address registers
|
; Initialize the FreeTCB list
|
pla
|
lda #1 ; the next available TCB
|
sta SPIMASTER+SPI_SD_SECT_7_0_REG
|
sta FreeTCB
|
lsr r1,r1,#8
|
ldx #1
|
sta SPIMASTER+SPI_SD_SECT_15_8_REG
|
lda #2
|
lsr r1,r1,#8
|
st2:
|
sta SPIMASTER+SPI_SD_SECT_23_16_REG
|
sta TCB_NxtTCB,x
|
lsr r1,r1,#8
|
ina
|
sta SPIMASTER+SPI_SD_SECT_31_24_REG
|
inx
|
|
cpx #256
|
|
bne st2
|
|
lda #-1
|
|
sta TCB_NxtTCB+255
|
|
lda #4
|
|
sta LEDS
|
|
|
; issue the write command
|
; Manually setup the BIOS task
|
lda #RW_WRITE_SD_BLOCK
|
stz RunningTCB ; BIOS is task #0
|
sta SPIMASTER+SPI_TRANS_TYPE_REG
|
stz TCB_NxtRdy ; manually build the ready list
|
lda #SPI_TRANS_START
|
stz TCB_PrvRdy
|
sta SPIMASTER+SPI_TRANS_CTRL_REG
|
lda #-1
|
nop
|
sta TCB_NxtTo
|
spi_write_sect2:
|
sta TCB_PrvTo
|
lda SPIMASTER+SPI_TRANS_STATUS_REG
|
stz QNdx2 ; insert at priority 2
|
nop ; just a delay between consecutive status reg reads
|
; manually build the IO focus list
|
nop
|
lda #JCBs
|
cmp #SPI_TRANS_BUSY
|
sta IOFocusNdx ; Job #0 (Monitor) has the focus
|
beq spi_write_sect2
|
stz JCB_iof_next,r1
|
lda SPIMASTER+SPI_TRANS_ERROR_REG
|
stz JCB_iof_prev,r1
|
lsr r1,r1,#4
|
|
and #3
|
|
cmp #SPI_WRITE_NO_ERROR
|
|
bne spi_write_error
|
|
lda #0
|
|
bra spi_write_ret
|
|
spi_write_error:
|
|
jsr DisplayByte
|
|
lda #spi_write_error_msg
|
|
jsr DisplayStringB
|
|
lda #1
|
lda #1
|
|
sta IOFocusTbl ; set the job #0 request bit
|
|
|
spi_write_ret:
|
lda #PRI_NORMAL
|
ply
|
sta TCB_Priority
|
plx
|
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
|
rts
|
|
|
; SPI read multiple sector
|
;------------------------------------------------------------------------------
|
;
|
;------------------------------------------------------------------------------
|
; r1= sector number to read
|
message "startIdleTask"
|
; r2= address to write data
|
StartIdleTask:
|
; r3= number of sectors to read
|
lda #4
|
|
ldx #0
|
|
ldy #IdleTask
|
|
jsr StartTask
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; IdleTask
|
;
|
;
|
; Returns:
|
; IdleTask is a low priority task that is always running. It runs when there
|
; r1 = 0 if successful
|
; 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
|
|
;------------------------------------------------------------------------------
|
;
|
;
|
spi_read_multiple:
|
StartJob:
|
push r4
|
|
ld r4,#0
|
|
spi_rm1:
|
|
pha
|
pha
|
jsr spi_read_sector
|
|
add r4,r4,r1
|
; Get a free JCB
|
add r2,r2,#512
|
spl freejcb_sema + 1
|
pla
|
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
|
ina
|
dey
|
inc r7
|
bne spi_rm1
|
inc r8
|
ld r1,r4
|
cmp r8,#31 ; max number of chars ?
|
pop r4
|
blo sjob3
|
|
sjob2:
|
|
sb r8,0,r9 ; save name length
|
|
|
|
sjob1:
|
|
stz freejcb_sema + 1
|
|
pla
|
rts
|
rts
|
|
|
; SPI write multiple sector
|
;------------------------------------------------------------------------------
|
;
|
; StartTask
|
; r1= sector number to write
|
|
; r2= address to get data from
|
|
; r3= number of sectors to write
|
|
;
|
;
|
; Returns:
|
; Startup a task. The task is automatically allocated a 1kW stack from the BIOS
|
; r1 = 0 if successful
|
; stacks area. The scheduler is invoked after the task is added to the ready
|
|
; list.
|
;
|
;
|
spi_write_multiple:
|
; Parameters:
|
push r4
|
; r1 = task priority
|
ld r4,#0
|
; r2 = start flags
|
spi_wm1:
|
; r3 = start address
|
pha
|
; r4 = start parameter
|
jsr spi_write_sector
|
; r5 = job handle
|
add r4,r4,r1 ; accumulate an error count
|
;------------------------------------------------------------------------------
|
add r2,r2,#512 ; 512 bytes per sector
|
message "StartTask"
|
pla
|
StartTask:
|
ina
|
pusha
|
dey
|
ld r6,r1 ; r6 = task priority
|
bne spi_wm1
|
ld r8,r2 ; r8 = flag register value on startup
|
ld r1,r4
|
|
pop r4
|
|
rts
|
|
|
|
; read the partition table to find out where the boot sector is.
|
; get a free TCB
|
; Returns
|
|
; r1 = 0 everything okay, 1=read error
|
|
; also Z=1=everything okay, Z=0=read error
|
|
;
|
;
|
spi_read_part:
|
spl freetcb_sema+1
|
phx
|
lda FreeTCB ; get free tcb list pointer
|
stz startSector ; default starting sector
|
bmi stask1
|
lda #0 ; r1 = sector number (#0)
|
tax
|
ldx #BYTE_SECTOR_BUF ; r2 = target address (word to byte address)
|
lda TCB_NxtTCB,x
|
jsr spi_read_sector
|
sta FreeTCB ; update the FreeTCB list pointer
|
cmp #0
|
stz freetcb_sema+1
|
bne spi_rp1
|
lda #81
|
lb r1,BYTE_SECTOR_BUF+$1C9
|
sta LEDS
|
asl r1,r1,#8
|
txa ; acc = TCB index (task number)
|
orb r1,r1,BYTE_SECTOR_BUF+$1C8
|
sta TCB_mbx,x
|
asl r1,r1,#8
|
|
orb r1,r1,BYTE_SECTOR_BUF+$1C7
|
|
asl r1,r1,#8
|
|
orb r1,r1,BYTE_SECTOR_BUF+$1C6
|
|
sta startSector ; r1 = 0, for okay status
|
|
plx
|
|
lda #0
|
|
rts
|
|
spi_rp1:
|
|
plx
|
|
lda #1
|
|
rts
|
|
|
|
; Read the boot sector from the disk.
|
; setup the stack for the task
|
; Make sure it's the boot sector by looking for the signature bytes 'EB' and '55AA'.
|
; Zap the stack memory.
|
; Returns:
|
ld r7,r2
|
; r1 = 0 means this card is bootable
|
asl r2,r2,#10 ; 1kW stack per task
|
; r1 = 1 means a read error occurred
|
add r2,r2,#BIOS_STACKS ;+0x3ff ; add in stack base
|
; r1 = 2 means the card is not bootable
|
pha
|
;
|
|
spi_read_boot:
|
|
phx
|
phx
|
phy
|
phy
|
push r5
|
txy ; y = target address
|
lda startSector ; r1 = sector number
|
ldx #ExitTask ; x = fill value
|
ldx #BYTE_SECTOR_BUF ; r2 = target address
|
lda #$3FF ; acc = # words to fill -1
|
jsr spi_read_sector
|
stos
|
cmp #0
|
|
bne spi_read_boot_err
|
|
lb r1,BYTE_SECTOR_BUF
|
|
cmp #$EB
|
|
bne spi_eb_err
|
|
spi_read_boot2:
|
|
lda #msgFoundEB
|
|
jsr DisplayStringB
|
|
lb r1,BYTE_SECTOR_BUF+$1FE ; check for 0x55AA signature
|
|
cmp #$55
|
|
bne spi_eb_err
|
|
lb r1,BYTE_SECTOR_BUF+$1FF ; check for 0x55AA signature
|
|
cmp #$AA
|
|
bne spi_eb_err
|
|
pop r5
|
|
ply
|
|
plx
|
|
lda #0 ; r1 = 0, for okay status
|
|
rts
|
|
spi_read_boot_err:
|
|
pop r5
|
|
ply
|
|
plx
|
|
lda #1
|
|
rts
|
|
spi_eb_err:
|
|
lda #msgNotFoundEB
|
|
jsr DisplayStringB
|
|
pop r5
|
|
ply
|
ply
|
plx
|
plx
|
lda #2
|
pla
|
rts
|
|
|
|
msgFoundEB:
|
|
db "Found EB code.",CR,LF,0
|
|
msgNotFoundEB:
|
|
db "EB/55AA Code missing.",CR,LF,0
|
|
|
|
; 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:
|
add r2,r2,#$3FF ; Move pointer to top of stack
|
; For now we cheat and just go directly to sector 512.
|
stx TCB_StackTop,r7
|
bra loadBootFileTmp
|
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
|
|
|
loadBootFileTmp:
|
; setup the initial stack image for the task
|
; We load the number of sectors per cluster, then load a single cluster of the file.
|
; Cause a return to the ExitTask routine when the task does a
|
; This is 16kib
|
; final rts.
|
ld r5,r3 ; r5 = start sector of data area
|
; fake an IRQ call by stacking the return address and processor
|
ld r2,#PROG_LOAD_AREA ; where to place file in memory
|
; flags on the stack
|
lb r3,BYTE_SECTOR_BUF+$D ; sectors per cluster
|
ldx #ExitTask ; save the address of the task exit routine
|
loadBootFile1:
|
phx
|
ld r1,r5 ; r1=sector to read
|
phy ; save start address on stack
|
jsr spi_read_sector
|
push r8 ; save processor status reg on stack
|
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:
|
; now fake pushing the register set onto the stack. Registers start up
|
db "Jumping to boot",0
|
; in an undefined state.
|
msgNotBootable:
|
; sub sp,#15 ; 15 registers
|
db "SD card not bootable.",0
|
push r4
|
spi_init_ok_msg:
|
push r4
|
db "SD card initialized okay.",0
|
push r4
|
spi_init_error_msg:
|
push r4
|
db ": error occurred initializing the SD card.",0
|
push r4
|
spi_boot_error_msg:
|
push r4
|
db "SD card boot error",CR,LF,0
|
push r4
|
spi_read_error_msg:
|
push r4
|
db "SD card read error",CR,LF,0
|
push r4
|
spi_write_error_msg:
|
push r4
|
db "SD card write error",0
|
push r4
|
|
push r4
|
|
push r4
|
|
push r4
|
|
push r4
|
|
tsx
|
|
stx TCB_SPSave,r7
|
|
; now restore the current stack pointer
|
|
trs r9,sp
|
|
|
do_fmt:
|
; Insert the task into the ready list
|
jsr spi_init
|
ld r4,#84
|
cmp #0
|
st r4,LEDS
|
bne fmt_abrt
|
jsr AddTaskToReadyList
|
ldx #DIRBUF
|
lda #1
|
ldy #65536
|
sta tcb_sema
|
; clear out the directory buffer
|
int #2 ; invoke the scheduler
|
dfmt1:
|
; GoReschedule ; invoke the scheduler
|
stz (x)
|
stask2:
|
inx
|
popa
|
dey
|
|
bne dfmt1
|
|
jsr store_dir
|
|
fmt_abrt:
|
|
rts
|
rts
|
|
stask1:
|
|
stz freetcb_sema+1
|
|
jsr kernel_panic
|
|
db "No more task control blocks available.",0
|
|
bra stask2
|
|
|
do_dir:
|
;------------------------------------------------------------------------------
|
jsr CRLF
|
; ExitTask
|
jsr spi_init
|
;
|
cmp #0
|
; This routine is called when the task exits with an rts instruction. OR
|
bne dirabrt
|
; it may be invoked with a JMP ExitTask. In either case the task must be
|
jsr load_dir
|
; running so it can't be on the timeout list. The scheduler is invoked
|
ld r4,#0 ; r4 = entry counter
|
; after the task is removed from the ready list.
|
ddir3:
|
;------------------------------------------------------------------------------
|
asl r3,r4,#6 ; y = start of entry, 64 bytes per entry
|
message "ExitTask"
|
ldx #32 ; 32 chars in filename
|
ExitTask:
|
ddir4:
|
; release any aquired resources
|
lb r1,DIRBUF<<2,y
|
; - mailboxes
|
beq ddir2 ; move to next dir entry if null is found
|
; - messages
|
cmp #$20 ; don't display control chars
|
hoff
|
bmi ddir1
|
spl tcb_sema + 1
|
jsr DisplayChar
|
lda RunningTCB
|
bra ddir5
|
cmp #MAX_TASKNO
|
ddir1:
|
bhi xtsk1
|
lda #' '
|
jsr RemoveTaskFromReadyList
|
jsr DisplayChar
|
jsr RemoveFromTimeoutList
|
ddir5:
|
stz TCB_Status,r1 ; set task status to TS_NONE
|
iny
|
jsr ReleaseIOFocus
|
dex
|
; lda TCB_ABS8Save,x
|
bne ddir4
|
; jsr FreeMemPage
|
lda #' '
|
; Free up all the mailboxes associated with the task.
|
jsr DisplayChar
|
xtsk7:
|
asl r3,r4,#4 ; y = start of entry, 16 words per entry
|
pha
|
lda DIRBUF+$D,y
|
lda TCB_MbxList,r1
|
ldx #5
|
bmi xtsk6
|
jsr PRTNUM
|
jsr FreeMbx
|
jsr CRLF
|
pla
|
ddir2:
|
bra xtsk7
|
jsr KeybdGetChar
|
xtsk6:
|
cmp #CTRLC
|
pla
|
beq ddir6
|
ldx #86
|
inc r4
|
stx LEDS
|
cmp r4,#512 ; max 512 dir entries
|
spl freetcb_sema+1
|
bne ddir3
|
ldx FreeTCB ; add the task control block to the free list
|
ddir6:
|
stx TCB_NxtTCB,r1
|
|
sta FreeTCB
|
|
stz freetcb_sema+1
|
|
xtsk1:
|
|
jmp SelectTaskToRun
|
|
|
dirabrt:
|
;------------------------------------------------------------------------------
|
|
; 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
|
rts
|
|
|
load_dir:
|
;------------------------------------------------------------------------------
|
pha
|
; 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
|
phx
|
phy
|
phy
|
lda #4000
|
ldx #TS_READY
|
ldx #DIRBUF<<2
|
stx TCB_Status,r1
|
ldy #64
|
ldx #-1
|
jsr spi_read_multiple
|
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
|
ply
|
plx
|
plx
|
pla
|
|
rts
|
rts
|
store_dir:
|
|
pha
|
; Here the ready list was empty, so add at head
|
phx
|
arl5:
|
phy
|
sta QNdx0,y
|
lda #4000
|
sta TCB_NxtRdy,r1
|
ldx #DIRBUF<<2
|
sta TCB_PrvRdy,r1
|
ldy #64
|
|
jsr spi_write_multiple
|
|
ply
|
ply
|
plx
|
plx
|
pla
|
|
rts
|
rts
|
|
|
; r1 = pointer to file name
|
;------------------------------------------------------------------------------
|
; r2 = pointer to buffer to save
|
; RemoveTaskFromReadyList
|
; r3 = length of buffer
|
|
;
|
;
|
do_save:
|
; This subroutine removes a task from the ready list.
|
pha
|
;
|
jsr spi_init
|
; Registers Affected: none
|
cmp #0
|
; Parameters:
|
bne dsavErr
|
; r1 = task number
|
pla
|
; Returns:
|
jsr load_dir
|
; r1 = task number
|
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 spi_write_multiple
|
|
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
|
message "RemoveTaskFromReadyList"
|
db CR,LF,"The disk is full, unable to save file.",CR,LF,0
|
RemoveTaskFromReadyList:
|
|
phx
|
|
phy
|
|
push r4
|
|
push r5
|
|
|
do_load:
|
ldy TCB_Status,r1 ; is the task on the ready list ?
|
pha
|
bit r3,#TS_READY|TS_RUNNING
|
jsr spi_init
|
beq rfr2
|
cmp #0
|
and r3,r3,#~(TS_READY|TS_RUNNING)
|
bne dsavErr
|
sty TCB_Status,r1 ; task status no longer running or ready
|
pla
|
ld r4,TCB_NxtRdy,r1 ; Get previous and next fields.
|
jsr load_dir
|
ld r5,TCB_PrvRdy,r1
|
ld r4,#0
|
st r4,TCB_NxtRdy,r5
|
dlod4:
|
st r5,TCB_PrvRdy,r4
|
asl r5,r4,#6
|
ldy TCB_Priority,r1
|
ld r7,#0
|
cmp r1,QNdx0,y ; Are we removing the QNdx task ?
|
ld r10,r1
|
bne rfr2
|
dlod2:
|
st r4,QNdx0,y
|
lb r6,DIRBUF<<2,r5
|
; Now we test for the case where the task being removed was the only one
|
lb r8,0,r10
|
; on the ready list of that priority level. We can tell because the
|
cmp r6,r8
|
; NxtRdy would point to the task itself.
|
bne dlod1
|
cmp r4,r1
|
inc r5
|
bne rfr2
|
inc r7
|
ldx #-1 ; Make QNdx negative
|
inc r10
|
stx QNdx0,y
|
cmp r7,#32
|
stx TCB_NxtRdy,r1
|
bne dlod2
|
stx TCB_PrvRdy,r1
|
; here the filename matched
|
rfr2:
|
dlod8:
|
pop r5
|
asl r5,r4,#4 ; 16 words
|
pop r4
|
ld r3,DIRBUF+$d,r5 ; get file size into y register
|
ply
|
asl r7,r4,#7 ; compute file address 64k * entry #
|
plx
|
add r7,r7,#5000 ; start at sector 5,000
|
|
ld r1,r7 ; r1 = sector number
|
|
lsr r3,r3,#9 ; r3/512
|
|
iny ; +1
|
|
jsr spi_read_multiple
|
|
dlod3:
|
|
rts
|
rts
|
; Here the filename didn't match
|
|
dlod1:
|
;------------------------------------------------------------------------------
|
inc r4
|
; AddToTimeoutList
|
cmp r4,#512
|
; AddToTimeoutList adds a task to the timeout list. The task is placed in the
|
bne dlod4
|
; list depending on it's timeout value.
|
; Here none of the filenames in the directory matched
|
|
;
|
;
|
lda #msgFileNotFound
|
; Registers Affected: none
|
jsr DisplayStringB
|
; 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
|
rts
|
|
|
msgFileNotFound:
|
;------------------------------------------------------------------------------
|
db CR,LF,"File not found.",CR,LF
|
; RemoveFromTimeoutList
|
|
|
;==============================================================================
|
|
; Ethernet
|
|
;==============================================================================
|
|
my_MAC1 EQU 0x00
|
|
my_MAC2 EQU 0xFF
|
|
my_MAC3 EQU 0xEE
|
|
my_MAC4 EQU 0xF0
|
|
my_MAC5 EQU 0xDA
|
|
my_MAC6 EQU 0x42
|
|
|
|
; r1 = PHY
|
|
; r2 = regnum
|
|
; r3 = data
|
|
;
|
;
|
eth_mii_write:
|
; This routine is called when a task is killed. The task may need to be
|
pha
|
; 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
|
phx
|
push r4
|
push r4
|
ld r4,#ETHMAC
|
push r5
|
asl r2,r2,#8
|
|
or r1,r1,r2
|
|
sta ETH_MIIADDRESS,r4
|
|
sty ETH_MIITX_DATA,r4
|
|
lda #ETH_WCTRLDATA
|
|
sta ETH_MIICOMMAND,r4
|
|
stz ETH_MIICOMMAND,r4
|
|
emiw1:
|
|
lda ETH_MIISTATUS,r4
|
|
bit #ETH_MIISTATUS_BUSY
|
|
bne emiw1
|
|
pop r4
|
|
plx
|
|
pla
|
|
rts
|
|
|
|
; r1 = PHY
|
ld r4,TCB_Status,r1 ; Is the task even on the timeout list ?
|
; r2 = reg
|
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
|
|
|
eth_mii_read:
|
; Update the head of the list.
|
phx
|
rftl_remove_from_head:
|
phy
|
ld r5,TCB_NxtTo,r1
|
ldy #ETHMAC
|
st r5,TimeoutList ; store next field into list head
|
asl r2,r2,#8
|
bmi rftl_empty_list
|
or r1,r1,r2
|
ld r4,TCB_Timeout,r1 ; add any remaining timeout to the timeout
|
sta ETH_MIIADDRESS,y
|
add r4,r4,TCB_Timeout,r5 ; of the next task on the list.
|
lda #ETH_MIICOMMAND_RSTAT
|
st r4,TCB_Timeout,r5
|
sta ETH_MIICOMMAND,y
|
ld r4,#-1 ; there is no previous item to the head
|
stz ETH_MIICOMMAND,y
|
sta TCB_PrvTo,r5
|
emir1:
|
|
lda ETH_MIISTATUS,y
|
; Here there is no previous or next items in the list, so the list
|
bit #ETH_MIISTATUS_BUSY
|
; will be empty once this task is removed from it.
|
bne emir1
|
rftl_empty_list:
|
lda ETH_MIIRX_DATA,y
|
tax
|
ply
|
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
|
plx
|
|
rftl_not_on_list2:
|
rts
|
rts
|
|
|
ethmac_setup:
|
;------------------------------------------------------------------------------
|
ld r4,#ETHMAC
|
; PopTimeoutList
|
lda #ETH_MIIMODER_RST
|
;
|
sta ETH_MIIMODER,r4
|
; This subroutine is called from within the timer ISR when the task's
|
lda ETH_MIIMODER,r4
|
; timeout expires. It's always the head of the list that's being removed in
|
and #~ETH_MIIMODER_RST
|
; the timer ISR so the removal from the timeout list is optimized. We know
|
sta ETH_MIIMODER,r4
|
; the timeout expired, so the amount of time to add to the next task is zero.
|
lda #$10 ; /16=1.25MHz
|
; This routine is written as a macro since it's only called from one place.
|
sta ETH_MIIMODER,r4 ; Clock divider for MII Management interface
|
; This routine is inlined. Implementing it as a macro increases performance.
|
lda #ETH_MODER_RST
|
;
|
sta ETH_MODER,r4
|
; Registers Affected: acc, x, y, flags
|
lda ETH_MODER,r4
|
; Parameters:
|
and #~ETH_MODER_RST
|
; x: head of timeout list
|
sta ETH_MODER,r4
|
; Returns:
|
|
; r1 = task id of task popped from timeout list
|
stz ETH_MIITX_DATA,r4
|
;------------------------------------------------------------------------------
|
stz ETH_MIIADDRESS,r4
|
;
|
stz ETH_MIICOMMAND,r4
|
message "PopTimeoutList"
|
|
macro PopTimeoutList
|
lda #0xEEF0DA42
|
ldy #-1
|
sta ETH_MAC_ADDR0,r4 ; MAC0
|
lda TCB_NxtTo,x
|
lda #0x00FF
|
sta TimeoutList ; store next field into list head
|
sta ETH_MAC_ADDR1,r4 ; MAC1
|
bmi ptl1
|
|
sty TCB_PrvTo,r1 ; previous link = -1
|
lda #-1
|
ptl1:
|
sta ETH_INT_SOURCE,r4
|
lda #0 ; clear timeout status
|
|
bmc TCB_Status,x
|
; Advertise support for 10/100 FD/HD
|
sty TCB_NxtTo,x ; make sure the next and prev fields indicate
|
lda #ETH_PHY
|
sty TCB_PrvTo,x ; the task is not on a list.
|
ldx #ETH_MII_ADVERTISE
|
txa
|
jsr eth_mii_read
|
endm
|
or r3,r1,#ETH_ADVERTISE_ALL
|
|
lda #ETH_PHY
|
|
ldx #ETH_MII_ADVERTISE
|
|
jsr eth_mii_write
|
|
|
|
; Do NOT advertise support for 1000BT
|
|
lda #ETH_PHY
|
|
ldx #ETH_MII_CTRL1000
|
|
jsr eth_mii_read
|
|
and r3,r1,#~(ETH_ADVERTISE_1000FULL|ETH_ADVERTISE_1000HALF)
|
|
lda #ETH_PHY
|
|
ldx #ETH_MII_CTRL1000
|
|
jsr eth_mii_write
|
|
|
|
; Disable 1000BT
|
|
lda #ETH_PHY
|
|
ldx #ETH_MII_EXPANSION
|
|
jsr eth_mii_read
|
|
and r3,r1,#~(ETH_ESTATUS_1000_THALF|ETH_ESTATUS_1000_TFULL)
|
|
ldx #ETH_MII_EXPANSION
|
|
jsr eth_mii_write
|
|
|
|
; Restart autonegotiation
|
|
lda #0
|
|
ldx #ETH_MII_BMCR
|
|
jsr eth_mii_read
|
|
and r3,r1,#~(ETH_BMCR_ANRESTART|ETH_BMCR_ANENABLE)
|
|
lda #7
|
|
jsr eth_mii_write
|
|
|
|
; Enable BOTH the transmiter and receiver
|
|
lda #$A003
|
|
sta ETH_MODER,r4
|
|
rts
|
|
|
|
; Initialize the ethmac controller.
|
;------------------------------------------------------------------------------
|
; Supply a MAC address, set MD clock
|
; Sleep
|
;
|
;
|
message "eth_init"
|
; Put the currently running task to sleep for a specified time.
|
eth_init:
|
;
|
|
; Registers Affected: none
|
|
; Parameters:
|
|
; r1 = time duration in centi-seconds (1/100 second).
|
|
; Returns: none
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
Sleep:
|
pha
|
pha
|
phy
|
phx
|
ldy #ETHMAC
|
tax
|
lda #$A003
|
spl tcb_sema + 1
|
sta ETH_MODER,y
|
lda RunningTCB
|
; lda #0x64 ; 100
|
jsr RemoveTaskFromReadyList
|
; sta ETH_MIIMODER,y
|
jsr AddToTimeoutList ; The scheduler will be returning to this
|
; lda #7 ; PHY address
|
lda #1
|
; sta ETH_MIIADDRESS,y
|
sta tcb_sema
|
lda #0xEEF0DA42
|
int #2 ; task eventually, once the timeout expires,
|
sta ETH_MAC_ADDR0,y ; MAC0
|
plx
|
lda #0x00FF
|
|
sta ETH_MAC_ADDR1,y ; MAC1
|
|
ply
|
|
pla
|
pla
|
rts
|
rts
|
|
|
; Request a packet and display on screen
|
;------------------------------------------------------------------------------
|
; r1 = address where to put packet
|
; 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).
|
|
;------------------------------------------------------------------------------
|
;
|
;
|
message "eth_request_packet"
|
short_delay:
|
eth_request_packet:
|
|
phx
|
phx
|
phy
|
phy
|
push r4
|
tsr tick,r2
|
push r5
|
usec1:
|
ldy #ETHMAC
|
tsr tick,r3
|
ldx #4 ; clear rx interrupt
|
sub r3,r3,r2
|
stx ETH_INT_SOURCE,y
|
cmp r1,r3
|
sta 0x181,y ; storage address
|
blo usec1
|
ldx #0xe000 ; enable interrupt
|
|
stx 0x180,y
|
|
eth1:
|
|
nop
|
|
ldx ETH_INT_SOURCE,y
|
|
bit r2,#4 ; get bit #2
|
|
beq eth1
|
|
ldx 0x180,y ; get from descriptor
|
|
lsr r2,r2,#16
|
|
ldy #0
|
|
pha
|
|
jsr GetScreenLocation
|
|
add r4,r1,3780 ; second last line of screen
|
|
pla
|
|
eth20:
|
|
add r5,r1,r3
|
|
lb r2,0,r5 ; get byte
|
|
add r5,r4,r3
|
|
stx (r5) ; store to screen
|
|
iny
|
|
cpy #83
|
|
bne eth20
|
|
pop r5
|
|
pop r4
|
|
ply
|
ply
|
plx
|
plx
|
rts
|
rts
|
|
|
; r1 = packet address
|
;------------------------------------------------------------------------------
|
|
; 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
|
|
;------------------------------------------------------------------------------
|
;
|
;
|
message "eth_interpret_packet"
|
KillTask:
|
eth_interpret_packet:
|
|
phx
|
phx
|
phy
|
cmp #1 ; BIOS task and IDLE task are immortal
|
lb r2,12,r1
|
bls kt1
|
lb r3,13,r1
|
cmp #MAX_TASKNO
|
cpx #8 ; 0x806 ?
|
bhi kt1
|
bne eth2
|
tax
|
cpy #6
|
lda TCB_hJCB,r1
|
bne eth2
|
jsr ForceReleaseIOFocus
|
lda #2 ; return r1 = 2 for ARP
|
txa
|
eth5:
|
spl tcb_sema + 1
|
ply
|
jsr RemoveTaskFromReadyList
|
plx
|
jsr RemoveFromTimeoutList
|
rts
|
stz TCB_Status,r1 ; set task status to TS_NONE
|
eth2:
|
|
cpx #8
|
; Free up all the mailboxes associated with the task.
|
bne eth3 ; 0x800 ?
|
kt7:
|
cpy #0
|
pha
|
bne eth3
|
tax
|
lb r2,23,r1
|
lda TCB_MbxList,r1
|
cpx #1
|
bmi kt6
|
bne eth4
|
jsr FreeMbx2
|
|
pla
|
|
bra kt7
|
|
kt6:
|
lda #1
|
lda #1
|
bra eth5 ; return 1 ICMP
|
sta tcb_sema
|
eth4:
|
pla
|
cpx #$11
|
|
bne eth6
|
|
lda #3 ; return 3 for UDP
|
|
bra eth5
|
|
eth6:
|
|
cpx #6
|
|
bne eth7
|
|
lda #4 ; return 4 for TCP
|
|
bra eth5
|
|
eth7:
|
|
eth3:
|
|
eor r1,r1,r1 ; return zero for unknown
|
|
ply
|
|
plx
|
|
rts
|
|
|
|
; r1 = address of packet to send
|
spl freetcb_sema + 1
|
; r2 = packet length
|
ldx FreeTCB ; add the task control block to the free list
|
;
|
stx TCB_NxtTCB,r1
|
message "eth_send_packet"
|
sta FreeTCB
|
eth_send_packet:
|
stz freetcb_sema + 1
|
phx
|
cmp RunningTCB ; keep running the current task as long as
|
phy
|
bne kt1 ; the task didn't kill itself.
|
push r4
|
int #2 ; invoke scheduler to reschedule tasks
|
ldy #ETHMAC
|
kt1:
|
; wait for tx buffer to be clear
|
|
eth8:
|
|
ld r4,0x100,y
|
|
bit r4,#$8000
|
|
bne eth8
|
|
ld r4,#1 ; clear tx interrupt
|
|
st r4,ETH_INT_SOURCE,y
|
|
; set address
|
|
sta 0x101,y
|
|
; set the packet length field and enable interrupts
|
|
asl r2,r2,#16
|
|
or r2,r2,#0xF000
|
|
stx 0x100,y
|
|
pop r4
|
|
ply
|
|
plx
|
plx
|
rts
|
rts
|
|
|
; Only for IP type packets (not ARP)
|
;------------------------------------------------------------------------------
|
; r1 = rx buffer address
|
; Allocate a mailbox
|
; r2 = swap flag
|
; Parameters:
|
|
; r1 = pointer to place to store handle
|
; Returns:
|
; Returns:
|
; r1 = data start index
|
; 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 "eth_build_packet"
|
message "AllocMbx"
|
eth_build_packet:
|
AllocMbx:
|
|
cmp #0
|
|
beq ambx_bad_ptr
|
|
phx
|
phy
|
phy
|
push r4
|
push r4
|
push r5
|
ld r4,r1 ; r4 = pointer to returned handle
|
push r6
|
spl freembx_sema + 1
|
push r7
|
lda FreeMbxHandle ; Get mailbox off of free mailbox list
|
push r8
|
sta (r4) ; store off the mailbox number
|
push r9
|
bmi ambx_no_mbxs
|
push r10
|
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
|
|
|
lb r3,6,r1
|
spl mbx_sema + 1
|
lb r4,7,r1
|
sta MBX_OWNER,x
|
lb r5,8,r1
|
lda #-1 ; initialize the head and tail of the queues
|
lb r6,9,r1
|
sta MBX_TQ_HEAD,x
|
lb r7,10,r1
|
sta MBX_TQ_TAIL,x
|
lb r8,11,r1
|
sta MBX_MQ_HEAD,x
|
; write to destination header
|
sta MBX_MQ_TAIL,x
|
sb r3,0,r1
|
stz MBX_TQ_COUNT,x ; initialize counts to zero
|
sb r4,1,r1
|
stz MBX_MQ_COUNT,x
|
sb r5,2,r1
|
stz MBX_MQ_MISSED,x
|
sb r6,3,r1
|
lda #8 ; set the max queue size
|
sb r7,4,r1
|
sta MBX_MQ_SIZE,x ; and
|
sb r8,5,r1
|
lda #MQS_NEWEST ; queueing strategy
|
; write to source header
|
sta MBX_MQ_STRATEGY,x
|
ld r3,#my_MAC1
|
stz mbx_sema + 1
|
sb r3,6,r1
|
|
ld r3,#my_MAC2
|
|
sb r3,7,r1
|
|
ld r3,#my_MAC3
|
|
sb r3,8,r1
|
|
ld r3,#my_MAC4
|
|
sb r3,9,r1
|
|
ld r3,#my_MAC5
|
|
sb r3,10,r1
|
|
ld r3,#my_MAC6
|
|
sb r3,11,r1
|
|
cmp r2,#1
|
|
bne eth16 ; if (swap)
|
|
lb r3,26,r1
|
|
lb r4,27,r1
|
|
lb r5,28,r1
|
|
lb r6,29,r1
|
|
; read destination
|
|
lb r7,30,r1
|
|
lb r8,31,r1
|
|
lb r9,32,r1
|
|
lb r10,33,r1
|
|
; write to sender
|
|
sb r7,26,r1
|
|
sb r8,27,r1
|
|
sb r9,28,r1
|
|
sb r10,29,r1
|
|
; write destination
|
|
sb r3,30,r1
|
|
sb r4,31,r1
|
|
sb r5,32,r1
|
|
sb r6,33,r1
|
|
eth16:
|
|
ldy eth_unique_id
|
|
iny
|
|
sty eth_unique_id
|
|
sb r3,19,r1
|
|
lsr r3,r3,#8
|
|
sb r3,18,r1
|
|
lb r3,14,r1
|
|
and r3,r3,#0xF
|
|
asl r3,r3,#2 ; *4
|
|
add r1,r3,#14 ; return datastart in r1
|
|
pop r10
|
|
pop r9
|
|
pop r8
|
|
pop r7
|
|
pop r6
|
|
pop r5
|
|
pop r4
|
pop r4
|
ply
|
ply
|
|
plx
|
|
lda #E_Ok
|
rts
|
rts
|
|
ambx_bad_ptr:
|
; Compute IPv4 checksum of header
|
lda #E_Arg
|
; r1 = packet address
|
rts
|
; r2 = data start
|
ambx_no_mbxs:
|
;
|
stz freembx_sema + 1
|
message "eth_checksum"
|
|
eth_checksum:
|
|
phy
|
|
push r4
|
|
push r5
|
|
push r6
|
|
; set checksum to zero
|
|
stz 24,r1
|
|
stz 25,r1
|
|
eor r3,r3,r3 ; r3 = sum = zero
|
|
ld r4,#14
|
|
eth15:
|
|
ld r5,r2
|
|
dec r5 ; r5 = datastart - 1
|
|
cmp r4,r5
|
|
bpl eth14
|
|
add r6,r1,r4
|
|
lb r5,0,r6 ; shi = [rx_addr+i]
|
|
lb r6,1,r6 ; slo = [rx_addr+i+1]
|
|
asl r5,r5,#8
|
|
or r5,r5,r6 ; shilo
|
|
add r3,r3,r5 ; sum = sum + shilo
|
|
add r4,r4,#2 ; i = i + 2
|
|
bra eth15
|
|
eth14:
|
|
ld r5,r3 ; r5 = sum
|
|
and r3,r3,#0xffff
|
|
lsr r5,r5,#16
|
|
add r3,r3,r5
|
|
eor r3,r3,#-1
|
|
sb r3,25,r1 ; low byte
|
|
lsr r3,r3,#8
|
|
sb r3,24,r1 ; high byte
|
|
pop r6
|
|
pop r5
|
|
pop r4
|
pop r4
|
ply
|
ply
|
|
plx
|
|
lda #E_NoMoreMbx
|
rts
|
rts
|
|
|
; r1 = packet address
|
;------------------------------------------------------------------------------
|
; returns r1 = 1 if this IP
|
; Free up a mailbox.
|
|
; This function frees a mailbox from the currently running task. It may be
|
|
; called by ExitTask().
|
|
;
|
|
; Parameters:
|
|
; r1 = mailbox handle
|
|
;------------------------------------------------------------------------------
|
;
|
;
|
message "eth_verifyIP"
|
FreeMbx:
|
eth_verifyIP:
|
|
phx
|
phx
|
phy
|
ldx RunningTCB
|
push r4
|
jsr FreeMbx2
|
push r5
|
|
lb r2,30,r1
|
|
lb r3,31,r1
|
|
lb r4,32,r1
|
|
lb r5,33,r1
|
|
; Check for general broadcast
|
|
cmp r2,#$FF
|
|
bne eth11
|
|
cmp r3,#$FF
|
|
bne eth11
|
|
cmp r4,#$FF
|
|
bne eth11
|
|
cmp r5,#$FF
|
|
bne eth11
|
|
eth12:
|
|
lda #1
|
|
eth13:
|
|
pop r5
|
|
pop r4
|
|
ply
|
|
plx
|
plx
|
rts
|
rts
|
eth11:
|
|
ld r1,r2
|
|
asl r1,r1,#8
|
|
or r1,r1,r3
|
|
asl r1,r1,#8
|
|
or r1,r1,r4
|
|
asl r1,r1,#8
|
|
or r1,r1,r5
|
|
cmp #$C0A8012A ; 192.168.1.42
|
|
beq eth12
|
|
eor r1,r1,r1
|
|
bra eth13
|
|
|
|
msgEthTest
|
|
db CR,LF,"Ethernet test - press CTRL-C to exit.",CR,LF,0
|
|
|
|
message "eth_main"
|
|
eth_main:
|
|
jsr RequestIOFocus
|
|
jsr ClearScreen
|
|
jsr HomeCursor
|
|
lda #msgEthTest
|
|
jsr DisplayStringB
|
|
; jsr eth_init
|
|
jsr ethmac_setup
|
|
eth_loop:
|
|
jsr KeybdGetChar
|
|
cmp #-1
|
|
beq eth17
|
|
cmp #CTRLC
|
|
bne eth17
|
|
lda #$A000 ; tunr off transmit/recieve
|
|
sta ETH_MODER+ETHMAC
|
|
jsr ReleaseIOFocus
|
|
rts
|
|
eth17
|
|
lda #eth_rx_buffer<<2 ; memory address zero
|
|
jsr eth_request_packet
|
|
jsr eth_interpret_packet ; r1 = packet type
|
|
|
|
cmp #1
|
|
bne eth10
|
|
ld r2,r1 ; save off r1, r2 = packet type
|
|
lda #eth_rx_buffer<<2 ; memory address zero
|
|
jsr eth_verifyIP
|
|
tay
|
|
txa ; r1 = packet type again
|
|
cpy #1
|
|
bne eth10
|
|
|
|
lda #eth_rx_buffer<<2 ; memory address zero
|
|
ldx #1
|
|
jsr eth_build_packet
|
|
tay ; y = icmpstart
|
|
lda #eth_rx_buffer<<2 ; memory address zero
|
|
add r4,r1,r3
|
|
sb r0,0,r4 ; [rx_addr+icmpstart] = 0
|
|
lb r2,17,r1
|
|
add r2,r2,#14 ; r2 = len
|
|
ld r6,r2 ; r6 = len
|
|
add r15,r1,r3
|
|
lb r4,2,r15 ; shi
|
|
lb r5,3,r15 ; slo
|
|
asl r4,r4,#8
|
|
or r4,r4,r5 ; sum = {shi,slo};
|
|
eor r4,r4,#-1 ; sum = ~sum
|
|
sub r4,r4,#0x800 ; sum = sum - 0x800
|
|
eor r4,r4,#-1 ; sum = ~sum
|
|
add r15,r1,r3
|
|
sb r4,3,r15
|
|
lsr r4,r4,#8
|
|
sb r4,2,r15
|
|
tyx
|
|
jsr eth_checksum
|
|
lda #eth_rx_buffer<<2 ; memory address zero
|
|
ld r2,r6
|
|
jsr eth_send_packet
|
|
jmp eth_loop
|
|
eth10:
|
|
; r2 = rx_addr
|
|
cmp #2
|
|
bne eth_loop ; Do we have ARP ?
|
|
; xor r2,r2,r2 ; memory address zero
|
|
ldx #eth_rx_buffer<<2
|
|
; get the opcode
|
|
lb r13,21,x
|
|
cmp r13,#1
|
|
bne eth_loop ; ARP request
|
|
; get destination IP address
|
|
lb r9,38,x
|
|
lb r10,39,x
|
|
lb r11,40,x
|
|
lb r12,41,x
|
|
; set r15 = destination IP
|
|
ld r15,r9
|
|
asl r15,r15,#8
|
|
or r15,r15,r10
|
|
asl r15,r15,#8
|
|
or r15,r15,r11
|
|
asl r15,r15,#8
|
|
or r15,r15,r12
|
|
; Is it our IP ?
|
|
cmp r15,#$C0A8012A ; //192.168.1.42
|
|
bne eth_loop
|
|
; get source IP address
|
|
lb r5,28,x
|
|
lb r6,29,x
|
|
lb r7,30,x
|
|
lb r8,31,x
|
|
; set r14 = source IP
|
|
ld r14,r5
|
|
asl r14,r14,#8
|
|
or r14,r14,r6
|
|
asl r14,r14,#8
|
|
or r14,r14,r7
|
|
asl r14,r14,#8
|
|
or r14,r14,r8
|
|
; Get the source MAC address
|
|
push r6
|
|
push r7
|
|
push r8
|
|
push r9
|
|
push r10
|
|
push r11
|
|
lb r6,22,x
|
|
lb r7,23,x
|
|
lb r8,24,x
|
|
lb r9,25,x
|
|
lb r10,26,x
|
|
lb r11,27,x
|
|
; write to destination header
|
|
sb r6,0,x
|
|
sb r7,1,x
|
|
sb r8,2,x
|
|
sb r9,3,x
|
|
sb r10,4,x
|
|
sb r11,5,x
|
|
; and write to ARP destination
|
|
sb r6,32,x
|
|
sb r7,33,x
|
|
sb r8,34,x
|
|
sb r9,35,x
|
|
sb r10,36,x
|
|
sb r11,37,x
|
|
pop r11
|
|
pop r10
|
|
pop r9
|
|
pop r8
|
|
pop r7
|
|
pop r6
|
|
; write to source header
|
|
; stbc #0x00,6[r2]
|
|
; stbc #0xFF,7[r2]
|
|
; stbc #0xEE,8[r2]
|
|
; stbc #0xF0,9[r2]
|
|
; stbc #0xDA,10[r2]
|
|
; stbc #0x42,11[r2]
|
|
sb r0,6,x
|
|
lda #0xFF
|
|
sb r1,7,x
|
|
lda #0xEE
|
|
sb r1,8,x
|
|
lda #0xF0
|
|
sb r1,9,x
|
|
lda #0xDA
|
|
sb r1,10,x
|
|
lda #0x42
|
|
sb r1,11,x
|
|
; write to ARP source
|
|
; stbc #0x00,22[r2]
|
|
; stbc #0xFF,23[r2]
|
|
; stbc #0xEE,24[r2]
|
|
; stbc #0xF0,25[r2]
|
|
; stbc #0xDA,26[r2]
|
|
; stbc #0x42,27[r2]
|
|
sb r0,22,x
|
|
lda #0xFF
|
|
sb r1,23,x
|
|
lda #0xEE
|
|
sb r1,24,x
|
|
lda #0xF0
|
|
sb r1,25,x
|
|
lda #0xDA
|
|
sb r1,26,x
|
|
lda #0x42
|
|
sb r1,27,x
|
|
; swap sender / destination IP
|
|
; write sender
|
|
sb r9,28,x
|
|
sb r10,29,x
|
|
sb r11,30,x
|
|
sb r12,31,x
|
|
; write destination
|
|
sb r5,38,x
|
|
sb r6,39,x
|
|
sb r7,40,x
|
|
sb r8,41,x
|
|
; change request to reply
|
|
; stbc #2,21[r2]
|
|
lda #2
|
|
sb r1,21,x
|
|
txa ; r1 = packet address
|
|
ldx #0x2A ; r2 = packet length
|
|
jsr eth_send_packet
|
|
jmp eth_loop
|
|
|
|
;--------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Initialize sprite image caches with random data.
|
; Free up a mailbox.
|
;--------------------------------------------------------------------------
|
; This function dequeues any messages from the mailbox and adds the messages
|
message "RandomizeSprram"
|
; back to the free message pool. The function also dequeues any threads from
|
RandomizeSprram:
|
; the mailbox.
|
ldx #SPRRAM
|
; Called from KillTask() and FreeMbx().
|
ld r4,#14336 ; number of chars to initialize
|
;
|
rsr1:
|
; Parameters:
|
tsr LFSR,r1
|
; r1 = mailbox handle
|
sta (x)
|
; r2 = task handle
|
inx
|
; Returns:
|
dec r4
|
; r1 = E_Ok if everything ok
|
bne rsr1
|
; r1 = E_Arg if a bad handle is passed
|
rts
|
;------------------------------------------------------------------------------
|
|
;
|
|
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.
|
; Draw random lines on the bitmap screen.
|
fmbx5:
|
;--------------------------------------------------------------------------
|
|
;
|
|
message "RandomLines"
|
|
RandomLines:
|
|
pha
|
pha
|
|
jsr DequeueMsgFromMbx
|
|
bmi fmbx3
|
|
spl freemsg_sema + 1
|
phx
|
phx
|
phy
|
ldx FreeMsg
|
push r4
|
stx MSG_LINK,r1
|
push r5
|
sta FreeMsg
|
jsr RequestIOFocus
|
stz freemsg_sema + 1
|
jsr ClearScreen
|
|
jsr HomeCursor
|
|
lda #msgRandomLines
|
|
jsr DisplayStringB
|
|
rl5:
|
|
tsr LFSR,r1
|
|
tsr LFSR,r2
|
|
tsr LFSR,r3
|
|
mod r1,r1,#1364
|
|
mod r2,r2,#768
|
|
jsr DrawPixel
|
|
tsr LFSR,r1
|
|
sta LineColor ; select a random color
|
|
rl1: ; random X0
|
|
tsr LFSR,r1
|
|
mod r1,r1,#1364
|
|
rl2: ; random X1
|
|
tsr LFSR,r3
|
|
mod r3,r3,#1364
|
|
rl3: ; random Y0
|
|
tsr LFSR,r2
|
|
mod r2,r2,#768
|
|
rl4: ; random Y1
|
|
tsr LFSR,r4
|
|
mod r4,r4,#768
|
|
rl8:
|
|
ld r5,GA_STATE ; make sure state is IDLE
|
|
bne rl8
|
|
jsr DrawLine
|
|
jsr KeybdGetChar
|
|
cmp #CTRLC
|
|
beq rl7
|
|
bra rl5
|
|
rl7:
|
|
jsr ReleaseIOFocus
|
|
pop r5
|
|
pop r4
|
|
ply
|
|
plx
|
plx
|
pla
|
pla
|
rts
|
bra fmbx5
|
|
fmbx3:
|
msgRandomLines:
|
pla
|
db CR,LF,"Random lines running - press CTRL-C to exit.",CR,LF,0
|
|
|
|
;--------------------------------------------------------------------------
|
; Dequeue threads from mailbox.
|
; Draw a pixel on the bitmap screen.
|
fmbx6:
|
; r1 = x coordinate
|
|
; r2 = y coordinate
|
|
; r3 = color
|
|
;--------------------------------------------------------------------------
|
|
message "DrawPixel"
|
|
DrawPixel:
|
|
pha
|
pha
|
sta GA_X0
|
jsr DequeueThreadFromMbx2
|
stx GA_Y0
|
bmi fmbx7
|
sty GA_PEN
|
|
lda #1
|
|
sta GA_CMD
|
|
pla
|
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
|
rts
|
|
|
comment ~
|
;------------------------------------------------------------------------------
|
|
; 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
|
pha
|
phx
|
phx
|
|
phy
|
push r4
|
push r4
|
mod r2,r2,#768
|
ld r4,MBX_MQ_STRATEGY,x
|
mod r1,r1,#1364
|
cmp r4,#MQS_UNLIMITED
|
mul r2,r2,#1364 ; y * 1364
|
beq qmam_unlimited
|
add r1,r1,r2 ; + x
|
cmp r4,#MQS_NEWEST
|
sb r3,BITMAPSCR<<2,r1
|
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
|
pop r4
|
|
ply
|
plx
|
plx
|
pla
|
pla
|
|
qmam_bad_msg:
|
rts
|
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:
|
; Draw a line on the bitmap screen.
|
ldy MBX_MQ_TAIL,x
|
;--------------------------------------------------------------------------
|
bmi qmam3
|
;50 REM DRAWLINE
|
sta MSG_LINK,y
|
;100 dx = ABS(xb-xa)
|
bra qmam4
|
;110 dy = ABS(yb-ya)
|
qmam3:
|
;120 sx = SGN(xb-xa)
|
sta MBX_MQ_HEAD,x
|
;130 sy = SGN(yb-ya)
|
qmam4:
|
;140 er = dx-dy
|
sta MBX_MQ_TAIL,x
|
;150 PLOT xa,ya
|
ldy MBX_MQ_COUNT,x
|
;160 if xa<>xb goto 200
|
iny
|
;170 if ya=yb goto 300
|
cmp r3,MBX_MQ_SIZE,x
|
;200 ee = er * 2
|
bls qmam6
|
;210 if ee <= -dy goto 240
|
ldy #-1
|
;220 er = er - dy
|
sty MSG_LINK,r1
|
;230 xa = xa + sx
|
; Remove the oldest message which is the one at the head of the mailbox queue.
|
;240 if ee >= dx goto 270
|
; Add the message back to the pool of free messages.
|
;250 er = er + dx
|
lda MBX_MQ_HEAD,x
|
;260 ya = ya + sy
|
ldy MSG_LINK,r1 ; move next in queue
|
;270 GOTO 150
|
sty MBX_MQ_HEAD,x ; to head of list
|
;300 RETURN
|
qmam8:
|
|
inc MBX_MQ_MISSED,x
|
message "DrawLine"
|
qmam1:
|
DrawLine:
|
spl freemsg_sema + 1
|
pha
|
ldy FreeMsg ; put old message back into free message list
|
sta GA_X0
|
sty MSG_LINK,r1
|
stx GA_Y0
|
sta FreeMsg
|
sty GA_X1
|
inc nMsgBlk
|
st r4,GA_Y1
|
stz freemsg_sema + 1
|
lda LineColor
|
;GoReschedule
|
sta GA_PEN
|
pop r4
|
lda #2
|
ply
|
sta GA_CMD
|
plx
|
pla
|
pla
|
rts
|
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
|
|
|
comment ~
|
;------------------------------------------------------------------------------
|
pha
|
; Dequeue a message from a mailbox.
|
|
;
|
|
; Returns
|
|
; r1 = message number
|
|
; nf set if there is no message, otherwise clear
|
|
;------------------------------------------------------------------------------
|
|
message "DequeueMsgFromMbx"
|
|
DequeueMsgFromMbx:
|
phx
|
phx
|
phy
|
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
|
push r4
|
|
ld r4,MBX_TQ_HEAD,r1
|
|
bpl dtfm2
|
|
pop r4
|
|
ldx #-1
|
|
lda #E_NoThread
|
|
rts
|
|
dtfm2:
|
push r5
|
push r5
|
push r6
|
dec MBX_TQ_COUNT,r1
|
push r7
|
ld r2,r4
|
push r8
|
ld r4,TCB_mbq_next,r4
|
push r9
|
st r4,MBX_TQ_HEAD,r1
|
push r10
|
bmi dtfm3
|
push r11
|
ld r5,#-1
|
|
st r5,TCB_mbq_prev,r4
|
sub r5,r3,r1 ; dx = abs(x2-x1)
|
bra dtfm4
|
bpl dln1
|
dtfm3:
|
sub r5,r0,r5
|
ld r5,#-1
|
dln1:
|
st r5,MBX_TQ_TAIL,r1
|
sub r6,r4,r2 ; dy = abs(y2-y1)
|
dtfm4:
|
bpl dln2
|
; stz MBX_SEMA+1
|
sub r6,r0,r6
|
ld r5,r2
|
dln2:
|
lda TCB_Status,r5
|
|
bit #TS_TIMEOUT
|
sub r7,r3,r1 ; sx = sgn(x2-x1)
|
beq dtfm5
|
beq dln5
|
ld r1,r5
|
bpl dln4
|
jsr RemoveFromTimeoutList
|
ld r7,#-1
|
dtfm5:
|
bra dln5
|
ld r4,#-1
|
dln4:
|
st r4,TCB_mbq_next,r5
|
ld r7,#1
|
st r4,TCB_mbq_prev,r5
|
dln5:
|
stz TCB_hWaitMbx,r5
|
|
stz TCB_Status,r5 ; set task status = TS_NONE
|
|
pop r5
|
|
pop r4
|
|
lda #E_Ok
|
|
rts
|
|
|
sub r8,r4,r2 ; sy = sgn(y2-y1)
|
;------------------------------------------------------------------------------
|
beq dln8
|
; This function is called from FreeMbx(). It dequeues threads from the
|
bpl dln7
|
; mailbox without removing the thread from the timeout list. The thread will
|
ld r8,#-1
|
; then timeout waiting for a message that can never be delivered.
|
bra dln8
|
;
|
dln7:
|
; Parameters:
|
ld r8,#1
|
; r1 = mailbox handle
|
|
; Returns:
|
dln8:
|
; r1 = E_arg means pointer is invalid
|
sub r9,r5,r6 ; er = dx-dy
|
; r1 = E_NoThread means no thread was queued at the mailbox
|
dln150:
|
; r2 = thead handle
|
phy
|
;------------------------------------------------------------------------------
|
ldy LineColor
|
message "DequeueThreadFromNbx2"
|
jsr DrawPixel
|
DequeueThreadFromMbx2:
|
ply
|
push r4
|
cmp r1,r3 ; if (xa <> xb)
|
ld r4,MBX_TQ_HEAD,r1
|
bne dln200 ; goto 200
|
bpl dtfm2a
|
cmp r2,r4 ; if (ya==yb)
|
pop r4
|
beq dln300 ; goto 300
|
ldx #-1
|
dln200:
|
lda #E_NoThread
|
asl r10,r9 ; ee = er * 2
|
rts
|
sub r11,r0,r6 ; r11 = -dy
|
dtfm2a:
|
cmp r10,r11 ; if (ee <= -dy)
|
push r5
|
bmi dln240 ; goto 240
|
dec MBX_TQ_COUNT,r1
|
beq dln240
|
ld r2,r4
|
sub r9,r9,r6 ; er = er - dy
|
ld r4,TCB_mbq_next,r4
|
add r1,r1,r7 ; xa = xa + sx
|
st r4,MBX_TQ_HEAD,r1
|
dln240:
|
bmi dtfm3a
|
cmp r10,r5 ; if (ee >= dx)
|
ld r5,#-1
|
bpl dln150 ; goto 150
|
st r5,TCB_mbq_prev,r4
|
add r9,r9,r5 ; er = er + dx
|
bra dtfm4a
|
add r2,r2,r8 ; ya = ya + sy
|
dtfm3a:
|
bra dln150 ; goto 150
|
ld r5,#-1
|
|
st r5,MBX_TQ_TAIL,r1
|
dln300:
|
dtfm4a:
|
pop r11
|
ld r4,#-1
|
pop r10
|
st r4,TCB_mbq_next,x
|
pop r9
|
st r4,TCB_mbq_prev,x
|
pop r8
|
stz TCB_hWaitMbx,x
|
pop r7
|
sei
|
pop r6
|
lda #TS_WAITMSG_BIT
|
|
bmc TCB_Status,x
|
|
cli
|
pop r5
|
pop r5
|
pop r4
|
pop r4
|
ply
|
lda #E_Ok
|
plx
|
|
pla
|
|
rts
|
rts
|
~
|
|
|
|
;include "float.asm"
|
;------------------------------------------------------------------------------
|
|
; 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:
|
; RTF65002 code to display the date and time from the date/time device.
|
push r4
|
;--------------------------------------------------------------------------
|
ld r4,#1 ; Do invoke scheduler
|
DisplayDatetime
|
jsr SendMsgPrim
|
pha
|
pop r4
|
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
|
rts
|
|
|
;--------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; ReadTemp
|
; SendMsgPrim
|
; Read and display the temperature from a DS1626 temperature sensor
|
; Send a message to a mailbox
|
; device. RTF65002 source code.
|
;
|
;--------------------------------------------------------------------------
|
; Parameters
|
DS1626_CMD =$FFDC0300
|
; r1 = handle to mailbox
|
DS1626_DAT =$FFDC0301
|
; r2 = message D1
|
; Commands
|
; r3 = message D2
|
START_CNV = $51;
|
; r4 = scheduler flag 1=invoke,0=don't invoke
|
STOP_CNV = $22;
|
;
|
READ_TEMP = $AA;
|
; Returns
|
READ_CONFIG = $AC;
|
; r1=E_Ok everything is ok
|
READ_TH = $A1;
|
; r1=E_BadMbx for a bad mailbox number
|
READ_TL = $A2;
|
; r1=E_NotAlloc for a mailbox that isn't allocated
|
WRITE_TH = $01;
|
; r1=E_NoMsg if there are no more message blocks available
|
WRITE_TL = $02;
|
; zf is set if everything is okay, otherwise zf is clear
|
WRITE_CONFIG = $0C;
|
;------------------------------------------------------------------------------
|
POR = $54;
|
message "SendMsgPrim"
|
|
SendMsgPrim:
|
ReadTemp:
|
cmp #NR_MBX ; check the mailbox number to make sure
|
lda CONFIGREC ; Do we even have a temperature sensor ?
|
bhs smsg1 ; that it's sensible
|
bit #$10
|
push r5
|
beq rdtmp3 ; If not, output '0.000'
|
push r6
|
rdtmp1:
|
push r7
|
; On power up the DS1626 interface circuit sends a power on reset (POR)
|
|
; command to the DS1626. Waiting here makes sure this command has been
|
spl mbx_sema + 1
|
; completed.
|
ld r7,MBX_OWNER,r1
|
jsr rdt_busy_wait
|
bmi smsg2 ; error: no owner
|
lda #$0F ; 12 bits resolution, cpu mode, one-shot mode
|
|
sta DS1626_DAT
|
|
lda #WRITE_CONFIG ; write the desired config to the device
|
|
sta DS1626_CMD
|
|
jsr rdt_busy_wait
|
|
lda #10
|
|
jsr tSleep
|
|
lda #0
|
|
sta DS1626_DAT
|
|
lda #START_CNV ; issue a start conversion command
|
|
sta DS1626_CMD
|
|
jsr rdt_busy_wait
|
|
lda #10
|
|
jsr tSleep
|
|
; Now poll the config register to determine when the conversion has completed.
|
|
rdtmp2:
|
|
lda #READ_CONFIG ; issue the READ_CONFIG command
|
|
sta DS1626_CMD
|
|
jsr rdt_busy_wait
|
|
pha
|
|
lda #10 ; Wait a bit before checking again. The conversion
|
|
jsr tSleep ; can take up to 1s to complete.
|
|
pla
|
|
bit #$80 ; test done bit
|
|
beq rdtmp2 ; loop back if not done conversion
|
|
lda #0
|
|
sta DS1626_DAT ; issue a stop conversion command
|
|
lda #STOP_CNV
|
|
sta DS1626_CMD
|
|
jsr rdt_busy_wait
|
|
lda #10
|
|
jsr tSleep
|
|
lda #READ_TEMP ; issue the READ_TEMP command
|
|
sta DS1626_CMD
|
|
jsr rdt_busy_wait
|
|
pha
|
pha
|
lda #10
|
phx
|
jsr tSleep
|
jsr DequeueThreadFromMbx ; r1=mbx
|
|
ld r6,r2 ; r6 = thread
|
|
plx
|
pla
|
pla
|
rdtmp4:
|
cmp r6,#0
|
jsr CRLF
|
bpl smsg3
|
and #$FFF
|
; Here there was no thread waiting at the mailbox, so a message needs to
|
bit #$800 ; check for negative temperature
|
; be allocated
|
beq rdtmp7
|
smp2:
|
sub r1,r0,r1 ; negate the number
|
spl freemsg_sema + 1
|
and #$FFF
|
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
|
pha
|
lda #'-' ; output a minus sign
|
phx
|
jsr DisplayChar
|
tax ; x = mailbox
|
|
ld r1,r7 ; acc = message
|
|
jsr QueueMsgAtMbx
|
|
plx
|
pla
|
pla
|
rdtmp7:
|
cmp r6,#0 ; check if there is a thread waiting for a message
|
pha ; save off value
|
bmi smsg5
|
lsr r1,r1,#4 ; get rid of fractional portion
|
smsg3:
|
and #$7F ; strip off sign bit
|
stx TCB_MSG_D1,r6
|
ldx #3 ; output the whole number part
|
sty TCB_MSG_D2,r6
|
jsr PRTNUM
|
smsg7:
|
lda #'.' ; followed by a decimal point
|
spl tcb_sema + 1
|
jsr DisplayChar
|
ld r5,TCB_Status,r6
|
pla ; get back temp value
|
bit r5,#TS_TIMEOUT
|
and #$0F
|
beq smsg8
|
mul r1,r1,#625 ; 1/16th's per degree
|
ld r1,r6
|
pha ; save off fraction bits
|
jsr RemoveFromTimeoutList
|
div r1,r1,#100 ; calculate the first digit
|
smsg8:
|
add #'0'
|
lda #TS_WAITMSG_BIT
|
jsr DisplayChar ; output digit
|
bmc TCB_Status,r6
|
pla ; get back fractions bits
|
lda #1
|
pha ; and save again
|
sta tcb_sema
|
div r1,r1,#10 ; shift over to second digit
|
ld r1,r6
|
mod r1,r1,#10 ; ignore high order bits
|
spl tcb_sema + 1
|
add #'0'
|
jsr AddTaskToReadyList
|
jsr DisplayChar ; display the digit
|
stz tcb_sema + 1
|
pla ; get back fraction
|
cmp r4,#0
|
mod r1,r1,#10 ; compute low order digit
|
beq smsg5
|
add #'0'
|
stz mbx_sema + 1
|
jsr DisplayChar ; display low order digit
|
int #2
|
jsr CRLF
|
;GoReschedule
|
rts
|
bra smsg9
|
rdtmp3:
|
smsg5:
|
lda #0
|
stz mbx_sema + 1
|
bra rdtmp4
|
smsg9:
|
|
pop r7
|
; Returns:
|
pop r6
|
; acc = value from data register
|
pop r5
|
;
|
lda #E_Ok
|
rdt_busy_wait:
|
|
jsr KeybdGetChar
|
|
cmp #CTRLC
|
|
beq Monitor
|
|
lda DS1626_DAT
|
|
bit #$8000
|
|
bne rdt_busy_wait
|
|
rts
|
rts
|
|
smsg1:
|
tSleep:
|
lda #E_BadMbx
|
ldx Milliseconds
|
|
txa
|
|
tSleep1:
|
|
ldx Milliseconds
|
|
sub r2,r2,r1
|
|
cpx #100
|
|
blo tSleep1
|
|
rts
|
rts
|
|
smsg2:
|
;==============================================================================
|
stz mbx_sema + 1
|
; Memory Management routines follow.
|
pop r7
|
;==============================================================================
|
pop r6
|
MemInit:
|
pop r5
|
lda #1 ; initialize memory semaphore
|
lda #E_NotAlloc
|
sta MEM_SEMA
|
|
lda #$4D454D20
|
|
sta HeapStart+MEM_CHK
|
|
sta HeapStart+MEM_FLAG
|
|
sta HeapEnd-2
|
|
sta HeapEnd-3
|
|
lda #0
|
|
sta HeapStart+MEM_PREV ; prev of first MEMHDR
|
|
sta HeapEnd ; next of last MEMHDR
|
|
lda #HeapEnd
|
|
ina
|
|
sub #$4
|
|
sta HeapStart+MEM_NEXT ; next of first MEMHDR
|
|
lda #HeapStart
|
|
sta HeapEnd-1 ; prev of last MEMHDR
|
|
rts
|
rts
|
|
smsg4:
|
ReportMemFree:
|
stz freemsg_sema + 1
|
jsr CRLF
|
stz mbx_sema + 1
|
lda #HeapEnd
|
pop r7
|
ina
|
pop r6
|
sub #HeapStart
|
pop r5
|
ldx #5
|
lda #E_NoMsg
|
jsr PRTNUM
|
|
lda #msgMemFree
|
|
jsr DisplayStringB
|
|
rts
|
rts
|
|
|
msgMemFree:
|
|
db " words free",CR,LF,0
|
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Allocate memory from the heap.
|
; 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
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
MemAlloc:
|
message "WaitMsg"
|
phx
|
WaitMsg:
|
phy
|
cmp #NR_MBX ; check the mailbox number to make sure
|
|
bhs wmsg1 ; that it's sensible
|
push r4
|
push r4
|
memaSpin:
|
push r5
|
ldx MEM_SEMA+1
|
push r6
|
beq memaSpin
|
push r7
|
ldx #HeapStart
|
ld r6,r1
|
mema4:
|
wmsg11:
|
ldy MEM_FLAG,x ; Check the flag word to see if this block is available
|
spl mbx_sema + 1
|
cpy #$4D454D20
|
ld r5,MBX_OWNER,r1
|
bne mema1 ; block not available, go to next block
|
cmp r5,#MAX_TASKNO
|
ld r4,MEM_NEXT,x ; compute the size of this block
|
bhi wmsg2 ; error: no owner
|
sub r4,r4,r2
|
jsr DequeueMsgFromMbx
|
sub r4,r4,#4 ; minus size of block header
|
; cmp #0
|
cmp r1,r4 ; is the block large enough ?
|
bpl wmsg3
|
bmi mema2 ; if yes, go allocate
|
|
mema1:
|
; Here there was no message available, remove the task from
|
ldx MEM_NEXT,x ; go to the next block
|
; the ready list, and optionally add it to the timeout list.
|
beq mema3 ; if no more blocks, out of memory error
|
; Queue the task at the mailbox.
|
bra mema4
|
wmsg12:
|
mema2:
|
spl tcb_sema + 1
|
ldy #$6D656D20
|
lda RunningTCB ; remove the task from the ready list
|
sty MEM_FLAG,x
|
jsr RemoveTaskFromReadyList
|
sub r4,r4,r1
|
stz tcb_sema + 1
|
cmp r4,#4 ; is the block large enough to split
|
wmsg13:
|
bpl memaSplit
|
spl tcb_sema + 1
|
stz MEM_SEMA+1
|
ld r7,TCB_Status,r1
|
txa
|
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
|
pop r4
|
ply
|
lda #E_Timeout
|
plx
|
|
rts
|
rts
|
mema3: ; insufficient memory
|
|
stz MEM_SEMA+1
|
; 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
|
pop r4
|
ply
|
lda #E_Ok
|
plx
|
|
lda #0
|
|
rts
|
rts
|
memaSplit:
|
wmsg1:
|
add r4,r1,r2
|
lda #E_BadMbx
|
add r4,r4,#4
|
rts
|
ldy #$4D454D20
|
wmsg2:
|
sty (r4)
|
stz mbx_sema + 1
|
sty MEM_FLAG,r4
|
pop r7
|
stx MEM_PREV,r4
|
pop r6
|
ldy MEM_NEXT,x
|
pop r5
|
sty MEM_NEXT,r4
|
|
st r4,MEM_PREV,y
|
|
ld r1,r4
|
|
stz MEM_SEMA+1
|
|
pop r4
|
pop r4
|
ply
|
lda #E_NotAlloc
|
plx
|
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Free previously allocated memory. Recombine with next and previous blocks
|
; Check for a message at a mailbox. Does not block. This function is a
|
; if they are free as well.
|
; 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
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
MemFree:
|
;
|
cmp #0 ; null pointer ?
|
PeekMsg:
|
beq memf2
|
ld r2,#0 ; don't remove from queue
|
phx
|
jsr CheckMsg
|
phy
|
|
memfSpin:
|
|
ldx MEM_SEMA+1
|
|
beq memfSpin
|
|
ldx MEM_FLAG,r1
|
|
cpx #$6D656D20 ; is the block allocated ?
|
|
bne memf1
|
|
ldx #$4D454D20
|
|
stx MEM_FLAG,r1 ; mark block as free
|
|
ldx MEM_PREV,r1 ; is the previous block free ?
|
|
beq memf3 ; no previous block
|
|
ldy MEM_FLAG,x
|
|
cpy #$4D454D20
|
|
bne memf3 ; the previous block is not free
|
|
ldy MEM_NEXT,r1
|
|
sty MEM_NEXT,x
|
|
beq memf1 ; no next block
|
|
stx MEM_PREV,y
|
|
memf3:
|
|
ldy MEM_NEXT,r1
|
|
ldx MEM_FLAG,y
|
|
cpx #$4D454D20
|
|
bne memf1 ; next block not free
|
|
ldx MEM_PREV,r1
|
|
stx MEM_PREV,y
|
|
beq memf1 ; no previous block
|
|
sty MEM_NEXT,x
|
|
memf1:
|
|
stz MEM_SEMA+1
|
|
ply
|
|
plx
|
|
memf2:
|
|
rts
|
rts
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Bus Error Routine
|
; CheckMsg
|
; This routine display a message then restarts the BIOS.
|
; Check for a message at a mailbox. Does not block.
|
;------------------------------------------------------------------------------
|
|
;
|
;
|
message "bus_err_rout"
|
; Parameters
|
bus_err_rout:
|
; r1=mailbox handle
|
cld
|
; r2=remove from queue if present
|
ldx #87
|
; Returns:
|
stx LEDS
|
; r1=E_Ok if everything is ok
|
pla ; get rid of the stacked flags
|
; r1=E_NoMsg if no message is available
|
ply ; get the error PC
|
; r1=E_BadMbx for a bad mailbox number
|
ldx #$05FFFFF8 ; setup stack pointer top of memory
|
; r1=E_NotAlloc for a mailbox that isn't allocated
|
txs
|
; r2=message D1
|
ldx #88
|
; r3=message D2
|
stx LEDS
|
;------------------------------------------------------------------------------
|
jsr CRLF
|
CheckMsg:
|
stz RunningTCB
|
cmp #NR_MBX ; check the mailbox number to make sure
|
stz IOFocusNdx
|
bhs cmsg1 ; that it's sensible
|
lda #msgBusErr
|
push r4
|
jsr DisplayStringB
|
push r5
|
tya
|
|
jsr DisplayWord ; display the originating PC address
|
spl mbx_sema + 1
|
lda #msgDataAddr
|
ld r5,MBX_OWNER,r1
|
jsr DisplayStringB
|
bmi cmsg2 ; error: no owner
|
tsr #9,r1
|
cpx #0 ; are we to dequeue the message ?
|
jsr DisplayWord
|
php
|
ldx #89
|
beq cmsg3
|
stx LEDS
|
jsr DequeueMsgFromMbx
|
ldx #128
|
bra cmsg4
|
ber2:
|
cmsg3:
|
lda #' '
|
lda MBX_MQ_HEAD,r1 ; peek the message at the head of the messages queue
|
jsr DisplayChar
|
cmsg4:
|
tsr hist,r1
|
cmp #0
|
jsr DisplayWord
|
bmi cmsg5
|
dex
|
ldx MSG_D1,r1
|
bne ber2
|
ldy MSG_D2,r1
|
jsr CRLF
|
plp ; get back dequeue flag
|
ber3:
|
beq cmsg8
|
nop
|
cmsg10:
|
jmp ber3
|
spl freemsg_sema + 1
|
;cli ; enable interrupts so we can get a char
|
ld r5,FreeMsg
|
ber1:
|
st r5,MSG_LINK,r1
|
jsr KeybdGetCharDirect ; Don't use the keyboard buffer
|
sta FreeMsg
|
cmp #-1
|
inc nMsgBlk
|
beq ber1
|
stz freemsg_sema + 1
|
lda RunningTCB
|
cmsg8:
|
jsr KillTask
|
stz mbx_sema + 1
|
jmp SelectTaskToRun
|
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
|
|
|
msgBusErr:
|
;------------------------------------------------------------------------------
|
db "Bus error at: ",0
|
; Spinlock interrupt
|
msgDataAddr:
|
; Go reschedule tasks if a spinlock is taking too long.
|
db " data address: ",0
|
;------------------------------------------------------------------------------
|
|
;
|
|
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 tasks to run without affecting the timeout list timing.
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
reschedule:
|
reschedule:
|
|
cli ; enable interrupts
|
cld ; clear extended precision mode
|
cld ; clear extended precision mode
|
|
|
pusha ; save off regs on the stack
|
pusha ; save off regs on the stack
|
|
spl tcb_sema + 1
|
|
resched1:
|
ldx RunningTCB
|
ldx RunningTCB
|
tsa ; save off the stack pointer
|
tsa
|
sta TCB_SPSave,x
|
sta TCB_SPSave,x ; save stack pointer in TCB
|
tsr sp8,r1 ; and the eight bit mode stack pointer
|
tsr sp8,r1 ; and the eight bit mode stack pointer
|
sta TCB_SP8Save,x
|
sta TCB_SP8Save,x
|
tsr abs8,r1
|
tsr abs8,r1
|
sta TCB_ABS8Save,x ; 8 bit emulation base register
|
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
|
jmp SelectTaskToRun
|
|
|
|
|
strStartQue:
|
strStartQue:
|
db 1,0,0,0,2,0,0,0,3,0,1,0,4,0,0,0
|
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
|
; db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; 100 Hz interrupt
|
; 100 Hz interrupt
|
; - takes care of "flashing" the cursor
|
; - takes care of "flashing" the cursor
|
; - decrements timeouts for tasks on timeout list
|
; - decrements timeouts for tasks on timeout list
|
; - switching tasks
|
; - switching tasks
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
p100Hz:
|
MTKTick:
|
; Handle every other interrupt because 100Hz interrupts may be too fast.
|
|
pha
|
pha
|
lda #3 ; reset the edge sense circuit
|
lda #3 ; reset the edge sense circuit
|
sta PIC_RSTE
|
sta PIC_RSTE
|
lda IRQFlag
|
|
ina
|
|
sta IRQFlag
|
|
ror
|
|
pla
|
pla
|
bcc p100Hz11
|
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
|
rti
|
|
|
p100Hz11:
|
p100Hz11:
|
|
cli
|
cld ; clear extended precision mode
|
cld ; clear extended precision mode
|
|
|
pusha ; save off regs on the stack
|
pusha ; save off regs on the stack
|
|
lda #96
|
|
sta LEDS
|
|
lda UserTick
|
|
beq p100Hz4
|
|
jsr (r1)
|
|
cli
|
|
p100Hz4:
|
|
|
ldx RunningTCB
|
ldx RunningTCB
|
tsa ; save off the stack pointer
|
tsa ; save off the stack pointer
|
sta TCB_SPSave,x
|
sta TCB_SPSave,x
|
tsr sp8,r1 ; and the eight bit mode stack pointer
|
tsr sp8,r1 ; and the eight bit mode stack pointer
|
sta TCB_SP8Save,x
|
sta TCB_SP8Save,x
|
tsr abs8,r1
|
tsr abs8,r1
|
sta TCB_ABS8Save,x ; 8 bit emulation base register
|
sta TCB_ABS8Save,x ; 8 bit emulation base register
|
lda #TS_READY
|
lda #TS_RUNNING_BIT
|
sta TCB_Status,x
|
bmc TCB_Status,x
|
p100Hz4:
|
lda #97
|
|
sta LEDS
|
; 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
|
|
cpx IOFocusNdx ; only bother to flash the cursor for the task with the IO focus.
|
|
bne p100Hz1a
|
|
lda CursorFlash ; test if we want a flashing cursor
|
|
beq p100Hz1a
|
|
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
|
|
p100Hz1a
|
|
|
|
; Check the timeout list to see if there are items ready to be removed from
|
; 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.
|
; the list. Also decrement the timeout of the item at the head of the list.
|
|
|
p100Hz15:
|
p100Hz15:
|
ldx TimeoutList
|
ldx TimeoutList
|
bmi p100Hz12 ; are there any entries in the timeout list ?
|
bmi p100Hz12 ; are there any entries in the timeout list ?
|
lda TCB_Timeout,x
|
lda TCB_Timeout,x
|
bne p100Hz14 ; has this entry timed out ?
|
bgt p100Hz14 ; has this entry timed out ?
|
txa
|
PopTimeoutList
|
jsr RemoveFromTimeoutList
|
|
jsr AddTaskToReadyList
|
jsr AddTaskToReadyList
|
bra p100Hz15 ; go back and see if there's another task to be removed
|
bra p100Hz15 ; go back and see if there's another task to be removed
|
; there could be a string of tasks to make ready.
|
; there could be a string of tasks to make ready.
|
p100Hz14:
|
p100Hz14:
|
dea ; decrement the entry's timeout
|
dea ; decrement the entry's timeout
|
|
sub r1,r1,missed_ticks ; account for any missed ticks
|
|
stz missed_ticks
|
sta TCB_Timeout,x
|
sta TCB_Timeout,x
|
|
|
p100Hz12:
|
p100Hz12:
|
; Falls through into selecting a task to run
|
; Falls through into selecting a task to run
|
|
tck3:
|
|
lda #98
|
|
sta LEDS
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; Search the ready queues for a ready task.
|
; Search the ready queues for a ready task.
|
; The search is occasionally started at a lower priority queue in order
|
; The search is occasionally started at a lower priority queue in order
|
; to prevent starvation of lower priority tasks. This is managed by
|
; 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.
|
; using a tick count as an index to a string containing the start que.
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
SelectTaskToRun:
|
SelectTaskToRun:
|
ld r6,#5 ; number of queues to search
|
ld r6,#5 ; number of queues to search
|
ldy IRQFlag ; use the IRQFlag as a buffer index
|
ldy IRQFlag ; use the IRQFlag as a buffer index
|
lsr r3,r3,#1 ; the LSB is always the same
|
; lsr r3,r3,#1 ; the LSB is always the same
|
and r3,r3,#$0F ; counts from 0 to 15
|
and r3,r3,#$0F ; counts from 0 to 15
|
lb r3,strStartQue,y ; get the queue to start search at
|
lb r3,strStartQue,y ; get the queue to start search at
|
sttr2:
|
sttr2:
|
lda QNdx0,y
|
lda QNdx0,y
|
bmi sttr1
|
bmi sttr1
|
lda TCB_NxtRdy,r1 ; Advance the queue index
|
lda TCB_NxtRdy,r1 ; Advance the queue index
|
sta QNdx0,y
|
sta QNdx0,y
|
; This is the only place the RunningTCB is set (except for initialization).
|
; This is the only place the RunningTCB is set (except for initialization).
|
sta RunningTCB
|
sta RunningTCB
|
ldx #TS_RUNNING ; flag the task as the running task
|
|
stx TCB_Status,r1
|
|
; The mmu map better have the task control block area mapped
|
|
; properly.
|
|
tax
|
tax
|
lda CONFIGREC
|
lda #TS_RUNNING_BIT
|
bit #4096
|
bms TCB_Status,x ; flag the task as the running task
|
beq sttr4
|
lda #99
|
lda TCB_mmu_map,x
|
sta LEDS
|
sta MMU_OKEY ; select the mmu map for the task
|
|
lda #2
|
|
sta MMU_FUSE ; set fuse to 2 clocks before mapping starts
|
|
sttr4:
|
|
lda TCB_ABS8Save,x ; 8 bit emulation base register
|
lda TCB_ABS8Save,x ; 8 bit emulation base register
|
trs r0,abs8
|
trs r1,abs8
|
lda TCB_SP8Save,x ; get back eight bit stack pointer
|
lda TCB_SP8Save,x ; get back eight bit stack pointer
|
trs r1,sp8
|
trs r1,sp8
|
ldx TCB_SPSave,x ; get back stack pointer
|
ldx TCB_SPSave,x ; get back stack pointer
|
txs
|
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
|
popa ; restore registers
|
rti
|
rti
|
|
|
; Set index to check the next ready list for a task to run
|
; Set index to check the next ready list for a task to run
|
sttr1:
|
sttr1:
|
iny
|
iny
|
cpy #5
|
cpy #5
|
bne sttr5
|
bne sttr5
|
ldy #0
|
ldy #0
|
sttr5
|
sttr5:
|
dec r6
|
dec r6
|
bne sttr2
|
bne sttr2
|
|
|
; Here there were no tasks ready
|
; Here there were no tasks ready
|
; This should not be able to happen, so hang the machine.
|
; This should not be able to happen, so hang the machine (in a lower
|
|
; power mode).
|
sttr3:
|
sttr3:
|
ldx #94
|
ldx #94
|
stx LEDS
|
stx LEDS
|
bra sttr3
|
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
|
|
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
; 1000 Hz interrupt
|
; kernal_panic:
|
; This IRQ must be fast.
|
; All this does right now is display the panic message on the screen.
|
; Increments the millisecond counter
|
; Parameters:
|
|
; inline: string
|
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
;
|
;
|
p1000Hz:
|
kernel_panic:
|
pha
|
pla ; pop the return address off the stack
|
lda #2 ; reset edge sense circuit
|
push r4 ; save off r4
|
sta PIC_RSTE
|
ld r4,r1
|
inc Milliseconds ; increment milliseconds count
|
kpan2:
|
pla
|
lb r1,0,r4 ; get a byte from the code space
|
rti
|
add r4,#1 ; increment pointer
|
|
and #$FF ; we want only eight bits
|
;------------------------------------------------------------------------------
|
beq kpan1 ; is it end of string ?
|
; Sleep interrupt
|
|
; This interrupt just selects another task to run. The current task is
|
|
; stuck in an infinite loop.
|
|
;------------------------------------------------------------------------------
|
|
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
|
|
ldx #64
|
|
ioi1:
|
|
tsr hist,r1
|
|
jsr DisplayWord
|
|
lda #' '
|
|
jsr DisplayChar
|
jsr DisplayChar
|
dex
|
bra kpan2
|
bne ioi1
|
kpan1: ; must update the return address !
|
jsr CRLF
|
jsr CRLF
|
ply
|
ld r1,r4 ; get return address into acc
|
plx
|
pop r4 ; restore r4
|
pla
|
jmp (r1)
|
rti
|
|
|
include "DeviceDriver.asm"
|
EmuMVP:
|
|
push r4
|
;------------------------------------------------------------------
|
push r5
|
;------------------------------------------------------------------
|
tsr sp,r4
|
include "Test816.asm"
|
lda 4,r4
|
include "pi_calc816.asm"
|
ldx 3,r4
|
|
ldy 2,r4
|
;------------------------------------------------------------------
|
EmuMVP1:
|
; Kind of a chicken and egg problem here. If there is something
|
ld r5,(x)
|
; wrong with the processor, then this code likely won't execute.
|
st r5,(y)
|
;
|
dex
|
|
dey
|
; put message to screen
|
dea
|
; tests pla,sta,ldy,inc,bne,ora,jmp,jmp(abs)
|
cmp #$FFFFFFFF
|
|
bne EmuMVP1
|
putmsg
|
sta 4,r4
|
pla ; pop the return address off the stack
|
stx 3,r4
|
wdm ; switch to 32 bits
|
sty 2,r4
|
xce
|
inc 6,r4 ; increment the return address by one.
|
cpu RTF65002
|
pop r5
|
push r4 ; save off r4
|
pop r4
|
or r4,r1,#$FFFF0000 ; set program bank bits; code is at $FFFFxxxx
|
ply
|
pm2
|
plx
|
add r4,#1 ; increment pointer
|
pla
|
lb r1,0,r4 ; get a byte from the code space
|
rti
|
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
|
|
|
EmuMVN:
|
cpu RTF65002
|
push r4
|
;------------------------------------------------------------------
|
push r5
|
; This test program just loop around waiting to recieve a message.
|
tsr sp,r4
|
; The message is a pointer to a string to display.
|
lda 4,r4
|
;------------------------------------------------------------------
|
ldx 3,r4
|
;
|
ldy 2,r4
|
test_mbx_prg:
|
EmuMVN1:
|
jsr RequestIOFocus
|
ld r5,(x)
|
lda #test_mbx ; where to put mailbox handle
|
st r5,(y)
|
int #4
|
inx
|
db 6 ; AllocMbx
|
iny
|
ldx #5
|
dea
|
jsr PRTNUM
|
cmp #$FFFFFFFF
|
; mStartTask #PRI_LOWEST,#0,#test_mbx_prg2,#0,#0
|
bne EmuMVN1
|
lda #PRI_LOWEST
|
sta 4,r4
|
ldx #0
|
stx 3,r4
|
ldy #test_mbx_prg2
|
sty 2,r4
|
ld r4,#0
|
inc 6,r4 ; increment the return address by one.
|
ld r5,#1
|
pop r5
|
int #4
|
pop r4
|
db 1 ; StartTask
|
ply
|
tmp2:
|
plx
|
lda test_mbx
|
pla
|
ldx #100
|
rti
|
int #4
|
|
db 10 ; WaitMsg
|
|
cmp #E_Ok
|
|
bne tmp1
|
|
txa
|
|
jsr DisplayStringB
|
|
bra tmp2
|
|
tmp1:
|
|
ldx #4
|
|
jsr PRTNUM
|
|
bra tmp2
|
|
|
msgUnimp:
|
test_mbx_prg2:
|
db "Unimplemented at: ",0
|
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
|
|
|
brk_rout:
|
message "DOS.asm"
|
rti
|
include "DOS.asm"
|
nmirout:
|
|
pha
|
|
phx
|
|
lda #msgPerr
|
|
jsr DisplayStringB
|
|
tsx
|
|
lda 4,x
|
|
jsr DisplayWord
|
|
jsr CRLF
|
|
plx
|
|
pla
|
|
rti
|
|
|
|
msgPerr:
|
cpu RTF65002
|
db "Parity error at: ",0
|
|
|
|
message "1298"
|
message "1298"
|
include "TinyBasic65002.asm"
|
include "TinyBasic65002.asm"
|
message "1640"
|
message "1640"
|
org $0FFFFFFF4 ; NMI vector
|
org $0FFFFFFF4 ; NMI vector
|