URL
https://opencores.org/ocsvn/rtf65002/rtf65002/trunk
Subversion Repositories rtf65002
Compare Revisions
- This comparison shows the changes necessary to convert path
/rtf65002
- from Rev 38 to Rev 39
- ↔ Reverse comparison
Rev 38 → Rev 39
/trunk/software/asm/asm.exe
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/software/asm/bootrom.asm
1,7 → 1,7
|
; ============================================================================ |
; __ |
; \\__/ o\ (C) 2013 Robert Finch, Stratford |
; \\__/ o\ (C) 2013, 2014 Robert Finch, Stratford |
; \ __ / All rights reserved. |
; \/_// robfinch<remove>@opencores.org |
; || |
22,10 → 22,13
; |
; ============================================================================ |
; |
cpu RTF65002 |
|
CR EQU 0x0D ;ASCII equates |
LF EQU 0x0A |
TAB EQU 0x09 |
CTRLC EQU 0x03 |
BELL EQU 0x07 |
CTRLH EQU 0x08 |
CTRLI EQU 0x09 |
CTRLJ EQU 0x0A |
33,6 → 36,7
CTRLM EQU 0x0D |
CTRLS EQU 0x13 |
CTRLX EQU 0x18 |
ESC EQU 0x1b |
XON EQU 0x11 |
XOFF EQU 0x13 |
|
47,11 → 51,24
E_Timeout = 0x10 |
E_BadAlarm = 0x11 |
E_NotOwner = 0x12 |
E_QueStrategy = 0x13 |
E_BadDevNum = 0x18 |
E_DCBInUse = 0x19 |
; Device driver errors |
E_BadDevNum = 0x20 |
E_NoDev = 0x21 |
E_BadDevOp = 0x22 |
E_ReadError = 0x23 |
E_WriteError = 0x24 |
E_BadBlockNum = 0x25 |
E_TooManyBlocks = 0x26 |
|
; resource errors |
E_NoMoreMbx = 0x40 |
E_NoMoreMsgBlks = 0x41 |
E_NoMoreAlarmBlks =0x44 |
E_NoMoreTCBs = 0x45 |
E_NoMem = 12 |
|
; task status |
TS_NONE =0 |
62,8 → 79,20
TS_READY =16 |
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_EXT =0x1C ; file name extension |
DIRENT_ATTR =0x20 ; attributes |
144,6 → 173,8
TEXT_COLS EQU 0x0 |
TEXT_ROWS EQU 0x1 |
TEXT_CURPOS EQU 11 |
TEXT_CURCTL EQU 8 |
BMP_CLUT EQU $FFDC5800 |
KEYBD EQU 0xFFDC0000 |
KEYBDCLR EQU 0xFFDC0001 |
PIC EQU 0xFFDC0FF0 |
151,13 → 182,25
PIC_ES EQU 0xFFDC0FF4 |
PIC_RSTE EQU 0xFFDC0FF5 |
TASK_SELECT EQU 0xFFDD0008 |
|
RQ_SEMA EQU 0xFFDB0000 |
TO_SEMA EQU 0xFFDB0010 |
to_sema EQU 0xFFDB0010 |
SERIAL_SEMA EQU 0xFFDB0020 |
KEYBD_SEMA EQU 0xFFDB0030 |
IOF_LIST_SEMA EQU 0xFFDB0040 |
MBX_SEMA EQU 0xFFDB0050 |
MEM_SEMA EQU 0xFFDB0060 |
keybd_sema EQU 0xFFDB0030 |
iof_sema EQU 0xFFDB0040 |
mbx_sema EQU 0xFFDB0050 |
freembx_sema EQU 0xFFDB0060 |
mem_sema EQU 0xFFDB0070 |
freemsg_sema EQU 0xFFDB0080 |
tcb_sema EQU 0xFFDB0090 |
readylist_sema EQU 0xFFDB00A0 |
tolist_sema EQU 0xFFDB00B0 |
msg_sema EQU 0xFFDB00C0 |
freetcb_sema EQU 0xFFDB00D0 |
freejcb_sema EQU 0xFFDB00E0 |
jcb_sema EQU 0xFFDB00F0 |
device_semas EQU 0xFFDB1000 |
device_semas_end EQU 0xFFDB1200 |
|
SPIMASTER EQU 0xFFDC0500 |
SPI_MASTER_VERSION_REG EQU 0x00 |
190,18 → 233,6
RW_READ_SD_BLOCK EQU 0x02 |
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 |
CR_CLOCK EQU 0xFFDCFFF4 |
GACCEL EQU 0xFFDAE000 |
282,80 → 313,239
SPRITEREGS EQU 0xFFDAD000 |
SPRRAM EQU 0xFFD80000 |
|
THRD_AREA EQU 0x04000000 ; threading area 0x04000000-0x40FFFFF |
BITMAPSCR EQU 0x04100000 |
SECTOR_BUF EQU 0x05FBEC00 |
BIOS_STACKS EQU 0x05FC0000 ; room for 256 1kW stacks |
BIOS_SCREENS EQU 0x05C00000 ; 0x05C00000 to 0x05DFFFFF |
THRD_AREA EQU 0x00000000 ; threading area 0x04000000-0x40FFFFF |
BITMAPSCR EQU 0x00100000 |
SECTOR_BUF EQU 0x01FBEC00 |
|
BYTE_SECTOR_BUF EQU SECTOR_BUF<<2 |
PROG_LOAD_AREA EQU 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 |
FATBUF EQU 0x5F60000 |
DIRBUF EQU 0x5F70000 |
eth_rx_buffer EQU 0x5F80000 |
eth_tx_buffer EQU 0x5F84000 |
FATOFFS EQU 0x1F50000 ; offset into FAT on card |
FATBUF EQU 0x1F60000 |
DIRBUF EQU 0x1F70000 |
eth_rx_buffer EQU 0x1F80000 |
eth_tx_buffer EQU 0x1F84000 |
|
; Mailboxes, room for 2048 |
MBX_LINK EQU 0x05F90000 |
MBX_TQ_HEAD EQU 0x05F90800 |
MBX_TQ_TAIL EQU 0x05F91000 |
MBX_MQ_HEAD EQU 0x05F91800 |
MBX_MQ_TAIL EQU 0x05F92000 |
MBX_TQ_COUNT EQU 0x05F92800 |
MBX_MQ_SIZE EQU 0x05F93000 |
MBX_MQ_COUNT EQU 0x05F93800 |
MBX_MQ_MISSED EQU 0x05F94000 |
MBX_OWNER EQU 0x05F94800 |
MBX_MQ_STRATEGY EQU 0x05F95000 |
MBX_RESV EQU 0x05F95800 |
.bss |
.org 0x01F90000 |
NR_MBX EQU $800 |
MBX_LINK fill.w NR_MBX,0 ; link to next mailbox in list (free list) |
MBX_TQ_HEAD fill.w NR_MBX,0 ; head of task queue |
MBX_TQ_TAIL fill.w NR_MBX,0 |
MBX_MQ_HEAD fill.w NR_MBX,0 ; head of message queue |
MBX_MQ_TAIL fill.w NR_MBX,0 |
MBX_TQ_COUNT fill.w NR_MBX,0 ; count of queued threads |
MBX_MQ_SIZE fill.w NR_MBX,0 ; number of messages that may be queued |
MBX_MQ_COUNT fill.w NR_MBX,0 ; count of messages that are queued |
MBX_MQ_MISSED fill.w NR_MBX,0 ; number of messages dropped from queue |
MBX_OWNER fill.w NR_MBX,0 ; job handle of mailbox owner |
MBX_MQ_STRATEGY fill.w NR_MBX,0 ; message queueing strategy |
MBX_RESV fill.w NR_MBX,0 |
|
; Messages, room for 8kW (8,192) messages |
MSG_LINK EQU 0x05FA0000 |
MSG_D1 EQU 0x05FA2000 |
MSG_D2 EQU 0x05FA4000 |
MSG_TYPE EQU 0x05FA6000 |
; Messages, room for 64kW (16,384) messages |
.bss |
.org 0x01FA0000 |
NR_MSG EQU 16384 |
MSG_LINK fill.w NR_MSG,0 ; link to next message in queue or free list |
MSG_D1 fill.w NR_MSG,0 ; message data 1 |
MSG_D2 fill.w NR_MSG,0 ; message data 2 |
MSG_TYPE fill.w NR_MSG,0 ; message type |
MSG_END EQU MSG_TYPE + NR_MSG |
|
MT_SEMA EQU 0xFFFFFFFF |
MT_IRQ EQU 0xFFFFFFF0 |
MT_GETCHAR EQU 0xFFFFFFEF |
|
NR_JCB EQU 32 |
JCB_Number EQU 0 |
JCB_Name EQU 1 ; 32 bytes (1 len + 31) |
JCB_Map EQU 9 ; memory map number associated with job |
JCB_pCode EQU 10 |
JCB_nCode EQU 11 ; size of code |
JCB_pData EQU 12 |
JCB_nData EQU 13 ; size of data |
JCB_pStack EQU 14 |
JCB_nStack EQU 15 |
JCB_UserName EQU 16 ; 32 bytes |
JCB_Path EQU 24 ; 80 bytes |
JCB_ExitRF EQU 44 ; 80 bytes |
JCB_CmdLine EQU 84 ; 240 bytes |
JCB_SysIn EQU 140 ; 40 chars |
JCB_SysOut EQU 150 ; 40 chars |
JCB_ExitError EQU 160 |
JCB_pVidMem EQU 161 ; pointer to video memory |
JCB_pVidMemAttr EQU 162 |
JCB_pVirtVid EQU 163 ; pointer to virtual video buffer |
JCB_pVirtVidAttr EQU 164 |
JCB_VideoMode EQU 165 |
JCB_VideoRows EQU 166 |
JCB_VideoCols EQU 167 |
JCB_CursorRow EQU 168 |
JCB_CursorCol EQU 169 |
JCB_CursorOn EQU 170 |
JCB_CursorFlash EQU 171 |
JCB_CursorType EQU 172 |
JCB_NormAttr EQU 173 |
JCB_CurrAttr EQU 174 |
JCB_ScrlCnt EQU 175 |
JCB_fVidPause EQU 176 |
JCB_Next EQU 177 |
JCB_iof_next EQU 178 ; I/O focus list |
JCB_iof_prev EQU 179 |
JCB_VMP_bitmap_b0 EQU 180 ; 512 bits - virtual memory page bitmap |
JCB_VMP_bitmap_b1 EQU 196 ; 512 bits - virtual memory page bitmap |
JCB_KeybdHead EQU 212 |
JCB_KeybdTail EQU 213 |
JCB_KeybdEcho EQU 214 |
JCB_KeybdBad EQU 215 |
JCB_KeybdAck EQU 216 |
JCB_KeybdLocks EQU 217 |
JCB_KeybdBuffer EQU 218 ; buffer is 16 words (chars = words) |
JCB_esc EQU 234 ; escape flag for DisplayChar processing |
JCB_Size EQU 256 |
JCB_LogSize EQU 8 |
|
.bss |
JCBs fill.w NR_JCB * JCB_Size,0 |
FreeJCB dw 0 |
|
.bss |
.org 0x01FBA000 |
|
; Task control blocks, room for 256 tasks |
TCB_NxtRdy EQU 0x05FBE100 ; next task on ready / timeout list |
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 |
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 |
|
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 |
;include "jcb.inc" |
|
HeapStart EQU 0x04200000 |
HeapEnd EQU 0x043FFFFF |
NR_MMU_MAP EQU 32 |
VPM_bitmap_b0 fill.w NR_MMU_MAP * 16,0 |
VPM_bitmap_b1 fill.w NR_MMU_MAP * 16,0 |
nPagesFree dw 0 |
|
message "cachInvRout" |
.bss |
.align 4096 |
cacheInvRout: |
fill.w 4096,0 |
cacheLineInvRout: |
fill.w 4096,0 |
|
message "SCREEN_SIZE" |
.bss |
.org 0x01D00000 |
SCREEN_SIZE EQU 8192 |
BIOS_SCREENS fill.w SCREEN_SIZE * NR_JCB ; 0x01D00000 to 0x01EFFFFF |
|
; Bitmap of tasks requesting the I/O focus |
; |
IOFocusTbl 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: |
; |
NmiBase EQU 0xDC |
363,81 → 553,211
|
; BIOS vars at the top of the 8kB scratch memory |
; |
; TinyBasic AREA = 0xF00 to 0xF7F |
; TinyBasic AREA = 0x6C0 to 0x77F |
|
QNdx0 EQU 0xF80 |
QNdx1 EQU QNdx0+1 |
QNdx2 EQU QNdx1+1 |
QNdx3 EQU QNdx2+1 |
QNdx4 EQU QNdx3+1 |
FreeTCB EQU QNdx4+1 |
TimeoutList EQU FreeTCB+1 |
RunningTCB EQU TimeoutList+1 |
FreeMbx EQU RunningTCB + 1 |
nMailbox EQU FreeMbx + 1 |
FreeMsg EQU nMailbox + 1 |
nMsgBlk EQU FreeMsg + 1 |
PageMap EQU 0x600 |
PageMapEnd EQU 0x63F |
PageMap2 EQU 0x640 |
PageMap2End EQU 0x67F |
mem_pages_free EQU 0x680 |
|
bss |
org 0x780 |
|
QNdx0 dw 0 |
QNdx1 dw 0 |
QNdx2 dw 0 |
QNdx3 dw 0 |
QNdx4 dw 0 |
FreeTCB dw 0 |
TimeoutList dw 0 |
RunningTCB dw 0 |
FreeMbxHandle dw 0 |
nMailbox dw 0 |
FreeMsg dw 0 |
nMsgBlk dw 0 |
missed_ticks dw 0 |
keybdmsg_d1 dw 0 |
keybdmsg_d2 dw 0 |
keybd_mbx dw 0 |
keybd_char dw 0 |
keybdIsSetup dw 0 |
keybdLock dw 0 |
keybdInIRQ dw 0 |
iof_switch dw 0 |
clockmsg_d1 dw 0 |
clockmsg_d2 dw 0 |
tcbsema_d1 dw 0 |
tcbsema_d2 dw 0 |
mmu_acc_save dw 0 |
|
; The IO focus list is a doubly linked list formed into a ring. |
; |
IOFocusNdx 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 0xF98 |
IrqSource EQU 0x79F |
|
JMPTMP EQU 0xFA0 |
SP8Save EQU 0xFAE |
SRSave EQU 0xFAF |
R1Save EQU 0xFB0 |
R2Save EQU 0xFB1 |
R3Save EQU 0xFB2 |
R4Save EQU 0xFB3 |
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 |
.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 |
|
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 |
.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 |
|
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 |
; |
; 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 |
|
startSector EQU 0xFF0 |
macro mSleep tm |
lda tm |
int #4 |
db 5 |
endm |
|
macro mAllocMbx |
int #4 |
db 6 |
endm |
|
macro mWaitMsg mbx,tmout |
lda mbx |
ldx tmout |
int #4 |
db 10 |
endm |
|
macro mPostMsg mbx,d1,d2 |
lda mbx |
ldx d1 |
ldy d2 |
int #4 |
db 8 |
endm |
|
macro DisTimer |
pha |
lda #3 |
sta PIC+2 |
pla |
endm |
|
macro EnTimer |
pha |
lda #3 |
sta PIC+3 |
pla |
endm |
|
macro DisTmrKbd |
pha |
lda #3 |
sta PIC+2 |
lda #15 |
sta PIC+2 |
pla |
endm |
|
macro EnTmrKbd |
pha |
lda #3 |
sta PIC+3 |
lda #15 |
sta PIC+3 |
pla |
endm |
|
macro GoReschedule |
int #2 |
endm |
|
;------------------------------------------------------------------------------ |
; Wait for the TCB array to become available |
;------------------------------------------------------------------------------ |
; |
macro mAquireTCB |
lda #33 |
ldx #0 |
txy |
ld r4,#-1 |
jsr WaitMsg |
endm |
|
macro mReleaseTCB |
lda #33 |
ldx #$FFFFFFFE |
txy |
jsr SendMsg |
endm |
|
macro mAquireMBX |
lda #34 |
ldx #0 |
txy |
ld r4,#-1 |
jsr WaitMsg |
endm |
|
macro mReleaseMBX |
lda #34 |
ldx #$FFFFFFFE |
txy |
jsr SendMsg |
endm |
|
|
cpu rtf65002 |
code |
|
459,6 → 779,8
dw Sleep |
dw do_load |
dw do_save |
dw ICacheInvalidateAll |
dw ICacheInvalidateLine |
|
org $FFFF8400 ; leave room for 256 vectors |
message "cold start point" |
475,6 → 797,8
trs r1,cc ; enable dcache and icache |
jsr ROMChecksum |
sta ROMcs |
jsr SetupCacheInvalidate |
jsr InitDevices |
stz mmu_present ; assume no mmu |
lda CONFIGREC |
bit #4096 |
484,15 → 808,16
sta mmu_present |
st_nommu: |
jsr MemInit ; Initialize the heap |
stz iof_switch |
|
lda #2 |
sta LEDS |
|
; 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) |
trs r2,vbr |
dex |
and r2,r2,#-2 ; mask off policy bit |
phx |
txy ; y = pointer to vector table |
lda #511 ; 512 vectors to setup |
504,13 → 829,17
sta (x) |
lda #slp_rout |
sta 1,x |
lda #reschedule |
lda #reschedule ; must be initialized after vectors are initialized to the break vector |
sta 2,x |
lda #spinlock_irq |
sta 3,x |
lda #syscall_int |
sta 4,x |
lda #KeybdRST |
sta 448+1,x |
lda #p1000Hz |
sta 448+2,x |
lda #p100Hz |
lda #MTKTick |
sta 448+3,x |
lda #KeybdIRQ |
sta 448+15,x |
533,107 → 862,24
stz IrqBase ; support for EhBASIC's interrupt mechanism |
stz NmiBase |
|
lda #-1 |
sta TimeoutList ; no entries in timeout list |
sta QNdx0 |
sta QNdx1 |
sta QNdx2 |
sta QNdx3 |
sta QNdx4 |
jsr ($FFFFC000>>2) ; Initialize multi-tasking |
lda #TickRout ; setup tick routine |
sta UserTick |
|
lda #1 |
sta iof_sema |
|
; Initialize IO Focus List |
; |
lda #7 |
lda #(DCB_SIZE * NR_DCB)-1 |
ldx #0 |
ldy #IOFocusTbl |
ldy #DCBs |
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 |
st4: |
sta MSG_LINK,x |
ina |
inx |
cpx #8192 |
bne st4 |
lda #-1 |
sta MBX_LINK+8191 |
|
; Initialize free mailbox list |
lda #2048 |
sta nMailbox |
|
stz FreeMbx |
ldx #0 |
lda #1 |
st3: |
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 |
sta ScreenColor |
sta CharColor |
sta CursorFlash |
jsr ClearScreen |
jsr InitBMP |
jsr ClearBmpScreen |
jsr PICInit |
; Enable interrupts |
640,10 → 886,14
; This will likely cause an interrupt right away because the timer |
; pulses run since power-up. |
cli |
lda #4 |
; mStartTask #PRI_LOWEST,#0,#IdleTask,#0,#0 |
lda #PRI_LOWEST |
ldx #0 |
ldy #IdleTask |
jsr StartTask |
ld r4,#0 |
ld r5,#0 |
int #4 |
db 1 |
lda CONFIGREC ; do we have a serial port ? |
bit #32 |
beq st7 |
668,9 → 918,17
cli |
lda #14 |
sta LEDS |
jsr KeybdInit |
lda #1 |
sta KeybdEcho |
stz keybdIsSetup |
; mStartTask #PRI_NORMAL,#0,#KeybdSetup,#0 |
lda #PRI_NORMAL |
ldx #0 |
ldy #KeybdSetup |
ld r4,#0 |
ld r5,#0 |
int #4 |
db 1 |
; lea r3,KeybdStatusLEDs |
; jsr StartTask |
lda #6 |
sta LEDS |
|
702,6 → 960,7
st6: |
lda #11 |
sta LEDS |
stz BASIC_SESSION |
jmp Monitor |
st1 |
jsr KeybdGetCharDirect |
713,65 → 972,98
db "RTF65002 system starting.",$0d,$0a,00 |
|
;------------------------------------------------------------------------------ |
; InitMMU |
; SetupCacheInvalidate: |
; |
; Initialize the 64 maps of the MMU. |
; Initially all the maps are set the same: |
; Virtual Page Physical Page |
; 000-255 000-255 |
; 256-511 1792-2047 |
; 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. |
; Setup the cache invalidate routines. Cache's in the FPGA don't have |
; invalidate logic as it cannot be efficiently implemented. So we handle |
; cache invalidations using software. By calling a software routine, or |
; accessing data in the setup cache invalidate area, the cache will be |
; effectively invalidated. This works for caches up to 16kB. (4kW) |
;------------------------------------------------------------------------------ |
InitMMU: |
lda #1 |
sta MMU_KVMMU+1 |
dea |
sta MMU_KVMMU |
immu1: |
sta MMU_AKEY ; set access key for map |
ldy #0 ; |
|
message "SetupCacheInvalidate" |
SetupCacheInvalidate: |
lda #4095 |
ldx #$EAEAEAEA ; fill memory with NOP's |
ldy #cacheInvRout |
stos |
lda #4095 |
ldx #$60606060 ; fill memory with RTS's |
ldy #cacheLineInvRout |
stos |
rts |
|
;------------------------------------------------------------------------------ |
; ICacheInvalidateAll: |
; |
; Call to invalidate the entire ICache |
;------------------------------------------------------------------------------ |
|
ICacheInvalidateAll: |
jml cacheInvRout<<2 |
|
;------------------------------------------------------------------------------ |
; ICacheInvalidateLine: |
; |
; Call to invalidate a specific cache line |
; |
; Parameters: |
; r1 = code address in line to invalidate |
;------------------------------------------------------------------------------ |
; |
ICacheInvalidateLine: |
and #$3FFF |
add #cacheLineInvRout<<2 |
jmp (r1) ; this will touch the cache line then RTS |
|
;------------------------------------------------------------------------------ |
; DCacheInvalidateAll: |
; |
; Call to invalidate the entire DCache. Works by performing a data fetch from |
; dummy data at each possible cache address. Works for caches up to 16kB in |
; size. |
;------------------------------------------------------------------------------ |
|
DCacheInvalidateAll: |
phx |
ldx #0 |
immu2: |
; set the first 256 pages to physical page 0-255 |
; 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 |
.0001: |
ld r0,cacheInvRout,x |
inx |
cpx #512 |
bne immu2 |
ina |
cmp #64 ; 64 MMU maps |
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 |
cpx #$FFF |
bls .0001 |
plx |
rts |
|
EnableMMUMapping: |
;------------------------------------------------------------------------------ |
; DCacheInvalidateLine: |
; |
; Call to invalidate a specific cache line in the data cache. |
; |
; Parameters: |
; r1 = data address in line to invalidate |
;------------------------------------------------------------------------------ |
; |
DCacheInvalidateLine: |
pha |
lda #1 |
sta MMU_MAPEN |
and #$FFF |
ld r0,cacheInvRout,r1 |
pla |
rts |
DisableMMUMapping: |
stz MMU_MAPEN |
|
;------------------------------------------------------------------------------ |
;------------------------------------------------------------------------------ |
InitBMP: |
ldx #0 |
ibmp1: |
tsr LFSR,r1 |
sta BMP_CLUT,x |
inx |
cpx #512 |
bne ibmp1 |
rts |
|
|
;------------------------------------------------------------------------------ |
; The ROM contents are summed up to ensure the ROM is okay. |
;------------------------------------------------------------------------------ |
823,8 → 1115,7
lda #msgTaskList |
jsr DisplayStringB |
ldy #0 |
php |
sei |
spl tcb_sema + 1 |
dtl2: |
lda QNdx0,y |
ld r4,r1 |
867,7 → 1158,7
iny |
cpy #5 |
bne dtl2 |
plp |
stz tcb_sema + 1 |
pop r4 |
ply |
plx |
887,13 → 1178,15
push r4 |
lda #msgTimeoutList |
jsr DisplayStringB |
php |
sei |
ldy #11 |
dtol2: |
lda TimeoutList |
ld r4,r1 |
bmi dtol1 |
spl tcb_sema + 1 |
dtol3: |
dey |
beq dtol1 |
ld r1,r4 |
ldx #3 |
jsr PRTNUM |
903,12 → 1196,12
jsr DisplayChar |
ld r1,r4 |
ldx #3 |
lda TCB_PrvRdy,r4 |
lda TCB_PrvTo,r4 |
jsr PRTNUM |
lda #' ' |
jsr DisplayChar |
ldx #3 |
lda TCB_NxtRdy,r4 |
lda TCB_NxtTo,r4 |
jsr PRTNUM |
lda #' ' |
jsr DisplayChar |
915,10 → 1208,10
lda TCB_Timeout,r4 |
jsr DisplayWord |
jsr CRLF |
ld r4,TCB_NxtRdy,r4 |
ld r4,TCB_NxtTo,r4 |
bpl dtol3 |
dtol1: |
plp |
stz tcb_sema + 1 |
pop r4 |
ply |
plx |
935,33 → 1228,32
pha |
phx |
phy |
php |
sei |
lda #msgIOFocusList |
jsr DisplayStringB |
spl iof_sema + 1 |
lda IOFocusNdx |
diofl2: |
bmi diofl1 |
beq diofl1 |
tay |
ldx #3 |
jsr PRTNUM |
lda #' ' |
jsr DisplayChar |
lda TCB_iof_prev,y |
lda JCB_iof_prev,y |
ldx #3 |
jsr PRTNUM |
lda #' ' |
jsr DisplayChar |
lda TCB_iof_next,y |
lda JCB_iof_next,y |
ldx #3 |
jsr PRTNUM |
jsr CRLF |
lda TCB_iof_next,y |
lda JCB_iof_next,y |
cmp IOFocusNdx |
bne diofl2 |
|
diofl1: |
plp |
stz iof_sema + 1 |
ply |
plx |
pla |
985,1095 → 1277,50
db CR,LF,"RunningTCB is bad.",CR,LF,0 |
|
;------------------------------------------------------------------------------ |
; IdleTask |
; |
; IdleTask is a low priority task that is always running. It runs when there |
; is nothing else to run. |
; This task check for tasks that are stuck in infinite loops and kills them. |
; Get the handle of the currently running job. |
;------------------------------------------------------------------------------ |
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 |
jsr KillTask |
it1: |
inc TestTask |
cli ; enable interrupts |
wai ; wait for one to happen |
bra it2 |
|
;------------------------------------------------------------------------------ |
; StartTask |
; |
; Startup a task. The task is automatically allocated a 1kW stack from the BIOS |
; stacks area. The scheduler is invoked after the task is added to the ready |
; list. |
; |
; Parameters: |
; r1 = task priority |
; r2 = start flags |
; r3 = start address |
;------------------------------------------------------------------------------ |
message "StartTask" |
StartTask: |
pha |
phx |
phy |
push r4 |
push r5 |
push r6 |
push r7 |
push r8 |
ld r6,r1 ; r6 = task priority |
ld r8,r2 ; r8 = flag register value on startup |
|
; get a free TCB |
; |
php |
sei |
lda FreeTCB ; get free tcb list pointer |
bmi stask1 |
tax |
lda TCB_NxtTCB,x |
sta FreeTCB ; update the FreeTCB list pointer |
plp |
txa ; acc = TCB index (task number) |
|
; setup the stack for the task |
; Zap the stack memory. |
ld r7,r2 |
asl r2,r2,#10 ; 1kW stack per task |
add r2,r2,#BIOS_STACKS ;+0x3ff ; add in stack base |
pha |
phx |
phy |
txy ; y = target address |
ldx #ExitTask ; x = fill value |
lda #$3FF ; acc = # words to fill -1 |
stos |
ply |
plx |
pla |
|
add r2,r2,#$3FF ; Move pointer to top of stack |
php |
tsr sp,r4 ; save off current stack pointer |
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 |
phy ; save start address on stack |
push r8 ; save processor status reg on stack |
|
; now fake pushing the register set onto the stack. Registers start up |
; in an undefined state. |
sub sp,#15 ; 15 registers |
tsx |
stx TCB_SPSave,r7 |
|
; now restore the current stack pointer |
trs r4,sp |
|
; Insert the task into the ready list |
jsr AddTaskToReadyList |
plp |
int #2 ; invoke the scheduler |
stask2: |
pop r8 |
pop r7 |
pop r6 |
pop r5 |
pop r4 |
ply |
plx |
pla |
GetCurrentJob: |
ld r1,RunningTCB |
ld r1,TCB_hJCB,r1 ; get the handle |
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. |
; Get a pointer to the JCB for the currently running task. |
;------------------------------------------------------------------------------ |
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 |
; r2 = new priority |
;------------------------------------------------------------------------------ |
SetTaskPriority: |
cmp #MAX_TASKNO ; make sure task number is reasonable |
bhi stp1 |
cpx #5 ; make sure priority is okay |
bhs stp1 |
phy |
php |
sei |
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 |
plp |
ply |
stp1: |
rts |
stp2: |
stx TCB_Priority,r1 |
plp |
ply |
rts |
|
;------------------------------------------------------------------------------ |
; AddTaskToReadyList |
; |
; The ready list is a group of five ready lists, one for each priority |
; level. Each ready list is organized as a doubly linked list to allow fast |
; insertions and removals. The list is organized as a ring (or bubble) with |
; the last entry pointing back to the first. This allows a fast task switch |
; to the next task. Which task is at the head of the list is maintained |
; in the variable QNdx for the priority level. |
; |
; Registers Affected: none |
; Parameters: |
; r1 = task number |
; Returns: |
; none |
;------------------------------------------------------------------------------ |
; |
message "AddTaskToReadyList" |
AddTaskToReadyList: |
php |
phx |
phy |
sei |
ldx #TS_READY |
stx TCB_Status,r1 |
ldy TCB_Priority,r1 |
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 |
arl3: |
ply |
plx |
plp |
GetPtrCurrentJCB: |
jsr GetCurrentJob |
and r1,r1,#NR_JCB-1 ; and convert it to a pointer |
; mul r1,r1,#JCB_Size |
asl r1,r1,#JCB_LogSize ; 256 words |
add r1,r1,#JCBs |
rts |
|
; Here the ready list was empty, so add at head |
arl5: |
sta QNdx0,y |
sta TCB_NxtRdy,r1 |
sta TCB_PrvRdy,r1 |
ply |
plx |
plp |
rts |
|
;------------------------------------------------------------------------------ |
; RemoveTaskFromReadyList |
; |
; This subroutine removes a task from the ready list. |
; |
; Registers Affected: none |
; Parameters: |
; r1 = task number |
; Returns: |
; r1 = task number |
;------------------------------------------------------------------------------ |
|
message "RemoveTaskFromReadyList" |
RemoveTaskFromReadyList: |
php ; save off interrupt mask state |
phx |
phy |
push r4 |
push r5 |
|
sei |
ldy TCB_Status,r1 ; is the task on the ready list ? |
bit r3,#TS_READY|TS_RUNNING |
beq rfr2 |
stz TCB_Status,r1 ; task status = TS_NONE |
ld r4,TCB_NxtRdy,r1 ; Get previous and next fields. |
ld r5,TCB_PrvRdy,r1 |
st r4,TCB_NxtRdy,r5 |
st r5,TCB_PrvRdy,r4 |
ldy TCB_Priority,r1 |
cmp r1,QNdx0,y ; Are we removing the QNdx task ? |
bne rfr2 |
st r4,QNdx0,y |
; Now we test for the case where the task being removed was the only one |
; on the ready list of that priority level. We can tell because the |
; NxtRdy would point to the task itself. |
cmp r4,r1 |
bne rfr2 |
ldx #-1 ; Make QNdx negative |
stx QNdx0,y |
stx TCB_NxtRdy,r1 |
stx TCB_PrvRdy,r1 |
rfr2: |
pop r5 |
pop r4 |
ply |
plx |
plp |
rts |
|
;------------------------------------------------------------------------------ |
; AddToTimeoutList |
; AddToTimeoutList adds a task to the timeout list. The task is placed in the |
; list depending on it's timeout value. |
; |
; Registers Affected: none |
; Parameters: |
; r1 = task |
; r2 = timeout value |
;------------------------------------------------------------------------------ |
message "AddToTimeoutList" |
AddToTimeoutList: |
php |
phx |
push r4 |
push r5 |
sei |
|
ld r5,#-1 |
ld r4,TimeoutList ; are there any tasks on the timeout list ? |
cmp r4,#MAX_TASKNO |
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 |
msgTimeout1: |
db CR,LF,"Adding to timeout list:",CR,LF,0 |
|
;------------------------------------------------------------------------------ |
; RemoveFromTimeoutList |
; |
; 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: |
php |
phx |
push r4 |
push r5 |
sei |
|
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 |
|
;------------------------------------------------------------------------------ |
; Sleep |
; |
; Put the currently running task to sleep for a specified time. |
; |
; Registers Affected: none |
; Parameters: |
; r1 = time duration in centi-seconds (1/100 second). |
;------------------------------------------------------------------------------ |
Sleep: |
php |
pha |
phx |
tax |
sei |
lda RunningTCB |
jsr RemoveTaskFromReadyList |
jsr AddToTimeoutList ; The scheduler will be returning to this |
int #2 ; task eventually, once the timeout expires, |
SleepRet: |
plx |
pla |
plp |
rts |
|
;------------------------------------------------------------------------------ |
; KillTask |
; |
; "Kills" a task, removing it from all system lists. If the task has the |
; IO focus, the IO focus is switched. Task #0 is immortal and cannot be |
; killed. |
; |
; Registers Affected: none |
; Parameters: |
; r1 = task number |
;------------------------------------------------------------------------------ |
; |
KillTask: |
php |
phx |
cmp #1 ; BIOS task and IDLE task are immortal |
bls kt1 |
cmp #MAX_TASKNO |
bhi kt1 |
sei |
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 |
phy |
push r4 |
ld r4,r1 |
php |
sei |
lda FreeMbx ; Get mailbox off of free mailbox list |
sta (r4) ; store off the mailbox number |
bmi ambx2 |
ldx MBX_LINK,r1 ; and update the head of the list |
stx FreeMbx |
dec nMailbox ; decrement number of available mailboxes |
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 |
ply |
plx |
lda #E_Ok |
rts |
ambx1: |
lda #E_Arg |
rts |
ambx2: |
plp |
pop r4 |
ply |
plx |
lda #E_NoMoreMbx |
rts |
|
;------------------------------------------------------------------------------ |
; r1 = message |
; r2 = mailbox |
;------------------------------------------------------------------------------ |
message "QueueMsgAtMbx" |
QueueMsgAtMbx: |
pha |
phx |
phy |
php |
sei |
ldy MBX_MQ_TAIL,x |
bmi qmam1 |
sta MBX_LINK,y |
bra qmam2 |
qmam1: |
sta MBX_MQ_HEAD,x |
qmam2: |
sta MBX_MQ_TAIL,x |
inc MBX_MQ_COUNT,x ; increase the queued message count |
ldx #-1 |
stx MSG_LINK,r1 |
plp |
ply |
plx |
pla |
rts |
|
;------------------------------------------------------------------------------ |
; Returns |
; r1 = message number |
;------------------------------------------------------------------------------ |
message "DequeueMsgFromMbx" |
DequeueMsgFromMbx: |
phx |
phy |
php |
sei |
tax ; x = mailbox index |
lda MBX_MQ_COUNT,x ; are there any messages available ? |
beq dmfm1 |
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 dmfm1 ; 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: |
plp |
ply |
plx |
rts |
|
;------------------------------------------------------------------------------ |
;------------------------------------------------------------------------------ |
DequeueThreadFromMbx: |
cpx #0 |
beq dtfm1 |
php |
sei |
push r4 |
ld r4,MBX_TQ_HEAD,r1 |
bpl dtfm2 |
pop r4 |
stz (x) |
plp |
lda #E_NoThread |
rts |
dtfm2: |
push r5 |
dec MBX_TQ_COUNT,r1 |
st r4,(x) |
ld r4,TCB_mbq_next,r4 |
st r4,MBX_TQ_HEAD,r1 |
bmi dtfm3 |
ld r5,#-1 |
st r5,TCB_mbq_prev,r4 |
bra dtfm4 |
dtfm3: |
ld r5,#-1 |
st r5,MBX_TQ_TAIL,r1 |
dtfm4: |
stz MBX_SEMA+1 |
ld r5,(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 |
plp |
lda #E_Ok |
rts |
dtfm1: |
lda #E_Arg |
rts |
|
;------------------------------------------------------------------------------ |
; r1 = handle to mailbox |
; r2 = message D1 |
; r3 = message D2 |
;------------------------------------------------------------------------------ |
message "SendMsg" |
SendMsg: |
cmp #2047 ; check the mailbox number to make sure |
bhi smsg1 ; that it's sensible |
push r4 |
push r5 |
push r6 |
php |
sei |
ld r4,MBX_OWNER,r1 |
bmi smsg2 ; error: no owner |
pha |
phx |
jsr DequeueThreadFromMbx ; r1=mbx, r2=thread (returned) |
ld r6,r2 ; r6 = thread |
plx |
pla |
cmp r6,#0 |
bpl smsg3 |
; Here there was no thread waiting at the mailbox, so a message needs to |
; be allocated |
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 |
phx |
tax ; x = mailbox |
ld r1,r4 ; acc = message |
jsr QueueMsgAtMbx |
plx |
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 |
lda #E_NoMsg |
rts |
|
;------------------------------------------------------------------------------ |
; WaitMsg |
; Wait at a mailbox for a message to arrive. This subroutine will block the |
; task until a message is available or the task times out on the timeout |
; list. |
; |
; Parameters |
; r1=mailbox |
; r2=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 |
bhi wmsg1 ; that it's sensible |
phx |
phy |
push r4 |
push r5 |
push r6 |
push r7 |
ld r6,r1 |
php |
sei |
ld r5,MBX_OWNER,r1 |
cmp r5,#MAX_TASKNO |
bhi wmsg2 ; error: no owner |
jsr DequeueMsgFromMbx |
cmp #0 |
bpl wmsg3 |
|
; Here there was no message available, remove the task from |
; the ready list, and optionally add it to the timeout list. |
; Queue the task at the mailbox. |
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 |
phy |
push r4 |
push r5 |
php |
sei |
ld r5,MBX_OWNER,r1 |
bmi cmsg2 ; error: no owner |
cmp r4,#0 ; are we to dequeue the message ? |
beq cmsg3 |
jsr DequeueMsgFromMbx |
bra cmsg4 |
cmsg3: |
lda MBX_MQ_HEAD,r1 ; peek the message at the head of the messages queue |
cmsg4: |
cmp #0 |
bmi cmsg5 |
cpx #0 |
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 |
ply |
plx |
lda #E_NoMsg |
rts |
|
;------------------------------------------------------------------------------ |
;------------------------------------------------------------------------------ |
comment ~ |
SetIOFocusBit: |
and r2,r2,#$FF |
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 |
phx |
phy |
php |
sei |
ldx RunningTCB |
cpx #MAX_TASKNO |
bhi riof1 |
ldy IOFocusNdx ; Is the focus list empty ? |
bmi riof2 |
riof4: |
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 |
pla |
rts |
|
; Here, the IO focus list was empty. So expand it. |
; 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 |
phx |
phy |
sei |
tax |
jmp rliof4 |
message "ReleaseIOFocus" |
ReleaseIOFocus: |
php |
pha |
phx |
phy |
sei |
ldx RunningTCB |
rliof4: |
cpx #MAX_TASKNO |
bhi rliof3 |
; phx |
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 |
pla |
plp |
rts |
|
;------------------------------------------------------------------------------ |
; Get the location of the screen and screen attribute memory. The location |
; depends on whether or not the task has the output focus. |
;------------------------------------------------------------------------------ |
GetScreenLocation: |
lda RunningTCB |
cmp IOFocusNdx |
beq gsl1 |
and r1,r1,#$FF |
asl r1,r1,#13 ; 8192 words per screen |
add r1,r1,#BIOS_SCREENS |
jsr GetPtrCurrentJCB |
lda JCB_pVidMem,r1 |
rts |
gsl1: |
lda #TEXTSCR |
rts |
|
GetColorCodeLocation: |
lda RunningTCB |
cmp IOFocusNdx |
beq gccl1 |
and r1,r1,#$FF |
asl r1,r1,#13 ; 8192 words per screen |
add r1,r1,#BIOS_SCREENS+4096 |
jsr GetPtrCurrentJCB |
lda JCB_pVidMemAttr,r1 |
rts |
gccl1: |
lda #TEXTSCR+$10000 |
|
GetNormAttr: |
jsr GetPtrCurrentJCB |
lda JCB_NormAttr,r1 |
rts |
|
GetCurrAttr: |
jsr GetPtrCurrentJCB |
lda JCB_CurrAttr,r1 |
rts |
|
;------------------------------------------------------------------------------ |
;------------------------------------------------------------------------------ |
message "CopyVirtualScreenToScreen" |
2082,41 → 1329,26
phx |
phy |
push r4 |
ldx IOFocusNdx ; compute virtual screen location |
beq cvss3 |
; copy screen chars |
lda #4095 ; number of words to copy-1 |
ldx IOFocusNdx ; compute virtual screen location |
bmi cvss3 |
asl r2,r2,#13 ; 8192 words per screen |
add r2,r2,#BIOS_SCREENS ; add in screens array base address |
ldx JCB_pVirtVid,x |
ldy #TEXTSCR |
mvn |
;cvss1: |
; ld r4,(x) |
; st r4,(y) |
; inx |
; iny |
; dea |
; bne cvss1 |
; now copy the color codes |
lda #4095 |
ldx IOFocusNdx |
asl r2,r2,#13 |
add r2,r2,#BIOS_SCREENS+4096 ; virtual char color array |
ldx JCB_pVirtVidAttr,x |
ldy #TEXTSCR+$10000 |
mvn |
;cvss2: |
; ld r4,(x) |
; st r4,(y) |
; inx |
; iny |
; dea |
; bne cvss2 |
cvss3: |
; reset the cursor position in the text controller |
ldy IOFocusNdx |
ldx TCB_CursorRow,y |
ldx JCB_CursorRow,y |
lda TEXTREG+TEXT_COLS |
mul r2,r2,r1 |
add r2,r2,TCB_CursorCol,y |
add r2,r2,JCB_CursorCol,y |
stx TEXTREG+TEXT_CURPOS |
pop r4 |
ply |
2132,30 → 1364,14
lda #4095 |
ldx #TEXTSCR |
ldy IOFocusNdx |
bmi csvs3 |
asl r3,r3,#13 |
add r3,r3,#BIOS_SCREENS |
beq csvs3 |
ldy JCB_pVirtVid,y |
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 |
ldy JCB_pVirtVidAttr,y |
mvn |
;csvs2: |
; ld r4,(x) |
; st r4,(y) |
; inx |
; iny |
; dea |
; bne csvs2 |
csvs3: |
pop r4 |
ply |
2186,9 → 1402,10
pla ; a is count |
pha |
stos ; clear the memory |
ld r2,ScreenColor ; x = value to use |
jsr GetCurrAttr |
tax ; x = value to use |
jsr GetColorCodeLocation |
tay ; y = targte address |
tay ; y = target address |
pla ; a = count |
stos |
ply |
2250,8 → 1467,10
phx |
phy |
push r4 |
push r5 |
ldx TEXTREG+TEXT_COLS ; x = # chars to blank out from video controller |
mul r3,r2,r1 ; y = screen index (row# * #cols) |
ld r5,r3 ; r5 = screen index |
pha |
jsr GetScreenLocation |
ld r4,r1 |
2264,6 → 1483,16
iny |
dex |
bne blnkln1 |
; reset the color codes on the display line to the normal attribute |
jsr GetColorCodeLocation |
tay ; y = destination |
add r3,r3,r5 ; add in index |
jsr GetNormAttr ; get the value to set |
tax |
lda TEXTREG+TEXT_COLS ; number of columns to blank out |
dea ; acc is one less |
stos |
pop r5 |
pop r4 |
ply |
plx |
2274,20 → 1503,16
; Convert ASCII character to screen display character. |
;------------------------------------------------------------------------------ |
; |
align 8 |
AsciiToScreen: |
and #$FF |
cmp #'A' |
bcc atoscr1 ; blt |
cmp #'Z' |
bcc atoscr1 |
beq atoscr1 |
cmp #'z'+1 |
bcs atoscr1 |
cmp #'a' |
bcc atoscr1 |
sub #$60 |
atoscr1: |
or #$100 |
bit #%00100000 ; if bit 5 isn't set |
beq .00001 |
bit #%01000000 ; or bit 6 isn't set |
beq .00001 |
and #%110011111 |
.00001: |
rts |
|
;------------------------------------------------------------------------------ |
2307,16 → 1532,22
; Set the cursor location to the top left of the screen. |
;------------------------------------------------------------------------------ |
HomeCursor: |
pha |
phx |
ldx RunningTCB |
and r2,r2,#$FF |
stz TCB_CursorRow,x |
stz TCB_CursorCol,x |
phy |
spl jcb_sema + 1 |
jsr GetPtrCurrentJCB |
tax |
stz JCB_CursorRow,x |
stz JCB_CursorCol,x |
stz jcb_sema + 1 |
cpx IOFocusNdx |
bne hc1 |
stz TEXTREG+TEXT_CURPOS |
hc1: |
ply |
plx |
pla |
rts |
|
;------------------------------------------------------------------------------ |
2326,26 → 1557,51
; |
UpdateCursorPos: |
pha |
jsr GetPtrCurrentJCB |
cmp IOFocusNdx ; update cursor position in text controller |
bne .ucp1 ; only for the task with the output focus |
ld r0,JCB_CursorOn,r4 ; only update if cursor is showing |
beq .ucp2 |
jsr CursorOn |
phx |
push r4 |
ld r4,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 |
ld r4,r1 |
lda JCB_CursorRow,r4 |
and #$3F ; limit of 63 rows |
ldx TEXTREG+TEXT_COLS |
mul r2,r2,r1 |
lda TCB_CursorCol,r4 |
lda JCB_CursorCol,r4 |
and #$7F ; limit of 127 cols |
add r2,r2,r1 |
stx TEXTREG+TEXT_CURPOS |
ucp1: |
pop r4 |
plx |
.ucp1: |
pla |
rts |
.ucp2: |
jsr CursorOff |
pla |
rts |
|
CursorOff: |
pha |
lda #5 |
bms TEXTREG+TEXT_CURCTL |
lda #6 |
bmc TEXTREG+TEXT_CURCTL |
pla |
rts |
|
CursorOn: |
pha |
lda #5 |
bmc TEXTREG+TEXT_CURCTL |
lda #6 |
bms TEXTREG+TEXT_CURCTL |
pla |
rts |
|
;------------------------------------------------------------------------------ |
; Calculate screen memory location from CursorRow,CursorCol. |
; Also refreshes the cursor location. |
2356,13 → 1612,13
CalcScreenLoc: |
phx |
push r4 |
ld r4,RunningTCB |
and r4,r4,#$FF |
lda TCB_CursorRow,r4 |
jsr GetPtrCurrentJCB |
ld r4,r1 |
lda JCB_CursorRow,r4 |
and #$3F ; limit to 63 rows |
ldx TEXTREG+TEXT_COLS |
mul r2,r2,r1 |
ld r1,TCB_CursorCol,r4 |
lda JCB_CursorCol,r4 |
and #$7F ; limit to 127 cols |
add r2,r2,r1 |
cmp r4,IOFocusNdx ; update cursor position in text controller |
2370,124 → 1626,138
stx TEXTREG+TEXT_CURPOS |
csl1: |
jsr GetScreenLocation |
add r1,r2,r1 |
add r1,r1,r2 |
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 |
; |
; Parameters: |
; r1 = char to display |
;------------------------------------------------------------------------------ |
; |
message "DisplayChar" |
DisplayChar: |
push r4 |
ld r4,RunningTCB |
and r4,r4,#$FF |
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 TCB_CursorCol,r4 ; just set cursor column to zero on a CR |
bne .dccr |
stz JCB_CursorCol,r4 ; just set cursor column to zero on a CR |
jsr UpdateCursorPos |
dcx14: |
.dcx14: |
pop r4 |
rts |
dccr: |
.dccr: |
cmp #$91 ; cursor right ? |
bne dcx6 |
bne .dcx6 |
pha |
lda TCB_CursorCol,r4 |
cmp #55 |
bcs dcx7 |
lda JCB_CursorCol,r4 |
ina |
sta TCB_CursorCol,r4 |
dcx7: |
cmp JCB_VideoCols,r4 |
bhs .dcx7 |
sta JCB_CursorCol,r4 |
.dcx7: |
jsr UpdateCursorPos |
pla |
pop r4 |
rts |
dcx6: |
.dcx6: |
cmp #$90 ; cursor up ? |
bne dcx8 |
bne .dcx8 |
pha |
lda TCB_CursorRow,r4 |
beq dcx7 |
lda JCB_CursorRow,r4 |
beq .dcx7 |
dea |
sta TCB_CursorRow,r4 |
bra dcx7 |
dcx8: |
sta JCB_CursorRow,r4 |
bra .dcx7 |
.dcx8: |
cmp #$93 ; cursor left ? |
bne dcx9 |
bne .dcx9 |
pha |
lda TCB_CursorCol,r4 |
beq dcx7 |
lda JCB_CursorCol,r4 |
beq .dcx7 |
dea |
sta TCB_CursorCol,r4 |
bra dcx7 |
dcx9: |
sta JCB_CursorCol,r4 |
bra .dcx7 |
.dcx9: |
cmp #$92 ; cursor down ? |
bne dcx10 |
bne .dcx10 |
pha |
lda TCB_CursorRow,r4 |
cmp #46 |
beq dcx7 |
lda JCB_CursorRow,r4 |
ina |
sta TCB_CursorRow,r4 |
bra dcx7 |
dcx10: |
cmp JCB_VideoRows,r4 |
bhs .dcx7 |
sta JCB_CursorRow,r4 |
bra .dcx7 |
.dcx10: |
cmp #$94 ; cursor home ? |
bne dcx11 |
bne .dcx11 |
pha |
lda TCB_CursorCol,r4 |
beq dcx12 |
stz TCB_CursorCol,r4 |
bra dcx7 |
dcx12: |
stz TCB_CursorRow,r4 |
bra dcx7 |
dcx11: |
lda JCB_CursorCol,r4 |
beq .dcx12 |
stz JCB_CursorCol,r4 |
bra .dcx7 |
.dcx12: |
stz JCB_CursorRow,r4 |
bra .dcx7 |
.dcx11: |
pha |
phx |
phy |
cmp #$99 ; delete ? |
bne dcx13 |
bne .dcx13 |
.doDel: |
jsr CalcScreenLoc |
tay ; y = screen location |
lda TCB_CursorCol,r4 ; acc = cursor column |
bra dcx5 |
dcx13 |
lda JCB_CursorCol,r4 ; acc = cursor column |
bra .dcx5 |
.dcx13 |
cmp #CTRLH ; backspace ? |
bne dcx3 |
lda TCB_CursorCol,r4 |
beq dcx4 |
bne .dcx3 |
lda JCB_CursorCol,r4 |
beq .dcx4 |
dea |
sta TCB_CursorCol,r4 |
sta JCB_CursorCol,r4 |
jsr CalcScreenLoc ; acc = screen location |
tay ; y = screen location |
lda TCB_CursorCol,r4 |
dcx5: |
lda JCB_CursorCol,r4 |
.dcx5: |
ldx $4,y |
stx (y) |
iny |
ina |
cmp TEXTREG+TEXT_COLS |
bcc dcx5 |
cmp JCB_VideoCols,r4 |
blo .dcx5 |
lda #' ' |
jsr AsciiToScreen |
dey |
sta (y) |
bra dcx4 |
dcx3: |
bra .dcx4 |
.dcx3: |
cmp #'\n' ; linefeed ? |
beq dclf |
beq .dclf |
tax ; save acc in x |
jsr CalcScreenLoc ; acc = screen location |
tay ; y = screen location |
2498,13 → 1768,13
sub r3,r3,r1 ; make y an index into the screen |
jsr GetColorCodeLocation |
add r3,r3,r1 |
lda CharColor |
jsr GetCurrAttr |
sta (y) |
jsr IncCursorPos |
bra dcx4 |
dclf: |
bra .dcx4 |
.dclf: |
jsr IncCursorRow |
dcx4: |
.dcx4: |
ply |
plx |
pla |
2511,40 → 1781,163
pop r4 |
rts |
|
; ESC processing |
.processEsc: |
cmp #(ESC<<24)+('('<<16)+(ESC<<8)+'G' |
beq .procAttr |
bit #$FF000000 ; is it some other five byte escape sequence ? |
bne .unrecogEsc |
cmp #(ESC<<16)+('('<<8)+ESC |
beq .testG |
bit #$FF0000 ; is it some other four byte escape sequence ? |
bne .unrecogEsc ; - unrecognized escape sequence |
cmp #(ESC<<8)+'`' |
beq .cursOnOff |
cmp #(ESC<<8)+'(' |
beq .testEsc |
bit #$FF00 ; is it some other three byte sequence ? |
bne .unrecogEsc ; - unrecognized escape sequence |
cmp #ESC ; check for single char escapes |
beq .esc1 |
pla ; some other garbage in the esc buffer ? |
stz JCB_esc,r4 |
pop r4 |
rts |
|
.cursOnOff: |
pla |
stz JCB_CursorOn,r4 |
and #$FF |
cmp #'0' |
beq .escRst |
inc JCB_CursorOn,r4 |
.escRst: |
stz JCB_esc,r4 ; reset escape sequence capture |
pop r4 |
rts |
|
.procAttr: |
pla |
and #$FF |
cmp #'0' |
bne .0005 |
lda JCB_NormAttr,r4 |
sta JCB_CurrAttr,r4 |
bra .escRst |
.0005: |
cmp #'4' |
bne .escRst |
phx |
lda JCB_NormAttr,r4 ; get the normal attribute |
tax |
lsr r1,r1,#5 ; swap foreground and background colors |
and #$1F |
asl r2,r2,#5 |
or r1,r1,r2 |
plx |
sta JCB_CurrAttr,r4 ; store in current attribute |
bra .escRst |
|
.esc1: |
pla |
and #$FF |
cmp #'W' ; esc 'W' - delete char under cursor |
bne .0006 |
stz JCB_esc,r4 |
pha |
phx |
phy |
bra .doDel |
.0006: |
cmp #'T' ; esc 'T' - clear to end of line |
bne .0009 |
phx |
phy |
ldx JCB_CursorCol,r4 |
jsr CalcScreenLoc ; acc = screen location |
tay |
lda #' ' |
jsr AsciiToScreen |
.0008: |
sta (y) |
iny |
inx |
cpx JCB_VideoCols,r4 |
blo .0008 |
ply |
plx |
bra .escRst |
.0009: |
cmp #'`' |
bne .0010 |
bra .stuffChar |
.0010: |
cmp #'(' |
bne .escRst |
bra .stuffChar |
|
.unrecogEsc: |
pla |
bra .escRst |
|
.testG: |
pla |
and #$FF |
cmp #'G' |
bne .escRst |
|
; stuff a character into the escape sequence |
.stuffChar: |
pha |
lda JCB_esc,r4 |
asl r1,r1,#8 |
or r1,r1,0,sp |
sta JCB_esc,r4 |
pla |
pop r4 |
rts |
|
.testEsc: |
pla |
and #$FF |
cmp #ESC |
bne .escRst |
bra .stuffChar |
|
;------------------------------------------------------------------------------ |
; Increment the cursor position, scroll the screen if needed. |
;------------------------------------------------------------------------------ |
; |
message "IncCursorPos" |
IncCursorPos: |
pha |
phx |
push r4 |
ld r4,RunningTCB |
and r4,r4,#$FF |
lda TCB_CursorCol,r4 |
jsr GetPtrCurrentJCB |
ld r4,r1 |
lda JCB_CursorCol,r4 |
ina |
sta TCB_CursorCol,r4 |
ldx TEXTREG+TEXT_COLS |
sta JCB_CursorCol,r4 |
ldx JCB_VideoCols,r4 |
cmp r1,r2 |
bcc icc1 |
stz TCB_CursorCol,r4 ; column = 0 |
blo icc1 |
stz JCB_CursorCol,r4 ; column = 0 |
bra icr1 |
IncCursorRow: |
pha |
phx |
push r4 |
ld r4,RunningTCB |
and r4,r4,#$FF |
jsr GetPtrCurrentJCB |
ld r4,r1 |
icr1: |
lda TCB_CursorRow,r4 |
lda JCB_CursorRow,r4 |
ina |
sta TCB_CursorRow,r4 |
ldx TEXTREG+TEXT_ROWS |
sta JCB_CursorRow,r4 |
ldx JCB_VideoRows,r4 |
cmp r1,r2 |
bcc icc1 |
beq icc1 |
blo icc1 |
dex ; backup the cursor row, we are scrolling up |
stx TCB_CursorRow,r4 |
stx JCB_CursorRow,r4 |
jsr ScrollUp |
icc1: |
jsr UpdateCursorPos |
2559,6 → 1952,7
; The characters are packed 4 per word |
;------------------------------------------------------------------------------ |
; |
message "DisplayStringB" |
DisplayStringB: |
pha |
phx |
2613,6 → 2007,7
; The characters are packed 1 per word |
;------------------------------------------------------------------------------ |
; |
message "DisplayStringW" |
DisplayStringW: |
pha |
phx |
2641,734 → 2036,80
rts |
|
;------------------------------------------------------------------------------ |
; Initialize keyboard |
; |
; Issues a 'reset keyboard' command to the keyboard, then selects scan code |
; set #2 (the most common one). Also sets up the keyboard buffer and |
; initializes the keyboard semaphore. |
;------------------------------------------------------------------------------ |
; |
message "KeybdInit" |
KeybdInit: |
lda #1 ; setup semaphore |
sta KEYBD_SEMA |
lda #32 |
sta LEDS |
ldx #0 |
|
lda #MAX_TASKNO |
ldx #0 |
ldy #KeybdHead |
stos |
lda #MAX_TASKNO |
ldy #KeybdTail |
stos |
lda #MAX_TASKNO |
ldy #KeybdBad |
stos |
lda #MAX_TASKNO |
ldx #1 ; turn on keyboard echo |
ldy #KeybdEcho |
stos |
|
lda PIC_IE |
or r1,r1,#$8000 ; enable kbd_irq |
sta PIC_IE |
|
lda #33 |
sta LEDS |
lda #$ff ; issue keyboard reset |
jsr SendByteToKeybd |
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 |
|
msgBadKeybd: |
db "Keyboard not responding.",0 |
|
SendByteToKeybd: |
phx |
ldx RunningTCB |
sta KEYBD |
lda #40 |
sta LEDS |
tsr TICK,r3 |
kbdi4: ; wait for transmit complete |
tsr TICK,r4 |
sub r4,r4,r3 |
cmp r4,#1000000 |
bcs kbdbad |
lda #41 |
sta LEDS |
lda KEYBD+3 |
bit #64 |
beq kbdi4 |
bra sbtk1 |
kbdbad: |
lda #42 |
sta LEDS |
lda KeybdBad,x |
bne sbtk1 |
lda #1 |
sta KeybdBad,x |
lda #43 |
sta LEDS |
lda #msgBadKeybd |
jsr DisplayStringCRLFB |
sbtk1: |
lda #44 |
sta LEDS |
plx |
rts |
|
; Wait for keyboard to respond with an ACK (FA) |
; |
WaitForKeybdAck: |
lda #64 |
sta LEDS |
tsr TICK,r3 |
wkbdack1: |
tsr TICK,r4 |
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 |
|
; Wait for keyboard to respond with an ACK (FA) |
; This routine picks up the ack status left by the |
; keyboard IRQ routine. |
; r2 = 0xFA (could also be 0xEE for echo command) |
; |
WaitForKeybdAck2: |
phx |
ldx RunningTCB |
WaitForKeybdAck2a: |
lda KeybdAck,x |
cmp r1,r2 |
bne WaitForKeybdAck2a |
stz KeybdAck,x |
plx |
rts |
|
;------------------------------------------------------------------------------ |
; KeybdIRQ |
; |
; 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" |
KeybdIRQ: |
cld |
pha |
phx |
phy |
push r4 |
|
message "TickRout" |
TickRout: |
; support EhBASIC's IRQ functionality |
; code derived from minimon.asm |
lda #15 ; Keyboard is IRQ #15 |
sta IrqSource |
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 r2,r1 |
or r1,r1,r2 |
lsr r4,r1 |
or r1,r1,r4 |
and #$E0 |
sb r1,IrqBase ; save the new IRQ flag byte |
sb r1,IrqBase |
|
ld r4,IOFocusNdx ; get the task with the input focus |
inc TEXTSCR+55 ; update IRQ live indicator on screen |
|
; flash the cursor |
jsr GetPtrCurrentJCB |
tax |
cpx IOFocusNdx ; only bother to flash the cursor for the task with the IO focus. |
bne tr1a |
lda JCB_CursorFlash,x ; test if we want a flashing cursor |
beq tr1a |
jsr CalcScreenLoc ; compute cursor location in memory |
tay |
lda $10000,y ; get color code $10000 higher in memory |
ld r4,IRQFlag ; get counter |
lsr r4,r4 |
and r4,r4,#$0F ; limit to low order nybble |
and #$F0 ; prepare to or in new value, mask off foreground color |
or r1,r1,r4 ; set new foreground color for cursor |
sta $10000,y ; store the color code back to memory |
tr1a |
rts |
|
ldx KEYBD ; get keyboard character |
ld r0,KEYBD+1 ; clear keyboard strobe (turns off the IRQ) |
txy ; check for a keyboard ACK code |
message "null.asm" |
include "null.asm" |
message "keyboard.asm" |
include "keyboard.asm" |
message "iofocus.asm" |
include "iofocus.asm" |
message "serial.asm" |
include "serial.asm" |
|
bit r3,#$800 ; test bit #11 |
bne KeybdIRQc ; ignore keyup messages for now |
bit r3,#$200 ; check for ALT-tab |
beq KeybdIrq3 |
and r3,r3,#$FF |
cmp r3,#TAB ; if we find an ALT-tab |
bne KeybdIrq3 |
jsr SwitchIOFocus |
bra KeybdIRQc ; don't store off the ALT-tab character |
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 |
pla |
rti |
|
KeybdRstIRQ: |
jmp start |
|
message "797" |
;------------------------------------------------------------------------------ |
; r1 0=echo off, non-zero = echo on |
; Display the half-word in r1 |
;------------------------------------------------------------------------------ |
SetKeyboardEcho: |
phx |
ldx RunningTCB |
sta KeybdEcho,x |
plx |
rts |
|
comment ~ |
;------------------------------------------------------------------------------ |
; Get a bit from the I/O focus table. |
;------------------------------------------------------------------------------ |
GetIOFocusBit: |
phx |
phy |
tax |
and r1,r1,#$1F ; get bit index into word |
lsr r2,r2,#5 ; get word index into table |
ldy IOFocusTbl,x |
lsr r3,r3,r1 ; extract bit |
and r1,r3,#1 |
ply |
plx |
rts |
~ |
;------------------------------------------------------------------------------ |
; ForceIOFocus |
; |
; Force the IO focus to a specific task. |
;------------------------------------------------------------------------------ |
; |
ForceIOFocus: |
php |
DisplayWord: |
pha |
phy |
ldy IOFocusNdx |
cmp r1,r3 |
beq fif1 |
jsr CopyScreenToVirtualScreen |
sta IOFocusNdx |
jsr CopyVirtualScreenToScreen |
fif1: |
ply |
lsr r1,r1,#16 |
jsr DisplayHalf |
pla |
plp |
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. |
; Display the half-word in r1 |
;------------------------------------------------------------------------------ |
; |
SwitchIOFocus: |
DisplayHalf: |
pha |
phy |
|
; First check if it's even possible to switch the focus to another |
; 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 |
lsr r1,r1,#8 |
jsr DisplayByte |
pla |
rts |
|
;------------------------------------------------------------------------------ |
; Get character from keyboard buffer |
; return character in acc or -1 if no |
; characters available. |
; Also check for ALT-TAB and switch the I/O focus. |
;------------------------------------------------------------------------------ |
message "KeybdGetChar" |
KeybdGetChar: |
php |
phx |
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 |
|
;------------------------------------------------------------------------------ |
; Check if there is a keyboard character available in the keyboard buffer. |
; Returns |
; r1 = 1, Z=0 if there is a key available, otherwise |
; r1 = 0, Z=1 if there is not a key available |
; Display the byte in r1 |
;------------------------------------------------------------------------------ |
; |
message "KeybdCheckForKey" |
KeybdCheckForKey: |
phx |
push r4 |
php |
sei |
ld r4,RunningTCB |
lda KeybdTail,r4 |
ldx KeybdHead,r4 |
sub r1,r1,r2 |
bne kcfk1 |
plp |
pop r4 |
plx |
lda #0 |
rts |
kcfk1 |
plp |
pop r4 |
plx |
lda #1 |
rts |
;------------------------------------------------------------------------------ |
; Check if there is a keyboard character available. If so return true (1) |
; otherwise return false (0) in r1. |
;------------------------------------------------------------------------------ |
; |
message "KeybdCheckForKeyDirect" |
KeybdCheckForKeyDirect: |
lda KEYBD |
and #$8000 |
beq kcfkd1 |
lda #1 |
kcfkd1 |
rts |
|
;------------------------------------------------------------------------------ |
; Get character directly from keyboard. This routine blocks until a key is |
; available. |
;------------------------------------------------------------------------------ |
; |
KeybdGetCharDirect: |
phx |
kgc1: |
lda KEYBD |
bit #$8000 |
beq kgc1 |
ld r0,KEYBD+1 ; clear keyboard strobe |
bit #$800 ; is it a keydown event ? |
bne kgc1 |
; bit #$200 ; check for ALT-tab |
; bne kgc2 |
; 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 |
|
|
;============================================================================== |
; Serial port |
;============================================================================== |
;------------------------------------------------------------------------------ |
; Initialize the serial port |
; r1 = low 28 bits = baud rate |
; 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 |
sta UART_CM1 ; set LSB |
lsr r1,r1,#8 |
sta UART_CM2 ; set middle bits |
lsr r1,r1,#8 |
sta UART_CM3 ; set MSB |
stz Uart_rxhead ; reset buffer indexes |
stz Uart_rxtail |
lda #0x1f0 |
sta Uart_foff ; set threshold for XOFF |
lda #0x010 |
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 |
|
;--------------------------------------------------------------------------------- |
; Get character directly from serial port. Blocks until a character is available. |
;--------------------------------------------------------------------------------- |
; |
SerialGetCharDirect: |
sgc1: |
lda UART_LS ; uart status |
and #rxfull ; is there a char available ? |
beq sgc1 |
lda UART |
rts |
|
;------------------------------------------------ |
; 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 |
; r1 = char to put |
;----------------------------------------- |
; |
SerialPutChar: |
phx |
phy |
push r4 |
push r5 |
|
ldx UART_MC |
or r2,r2,#3 ; assert DTR / RTS |
stx UART_MC |
ldx Uart_txrts |
beq spcb1 |
ld r4,Milliseconds |
ldy #1000 ; delay count (1 s) |
spcb3: |
ldx UART_MS |
and r2,r2,#$10 ; is CTS asserted ? |
bne spcb1 |
ld r5,Milliseconds |
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 |
ply |
plx |
rts |
|
;------------------------------------------------- |
; 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 |
phy |
push r4 |
|
ldy Uart_rxhead |
ldx Uart_rxtail |
cmp r2,r3 |
beq sgcfifo1 ; is there a char available ? |
lda Uart_rxfifo,x ; get the char from the fifo into r1 |
inx ; increment the fifo pointer |
and r2,r2,#$1ff |
stx Uart_rxtail |
ldx Uart_rxflow ; using flow control ? |
beq sgcfifo2 |
ldy Uart_fon ; enough space in Rx buffer ? |
jsr CharsInRxBuf |
cmp r4,r3 |
bpl sgcfifo2 |
stz Uart_rxflow ; flow off |
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 |
ply |
plx |
rts |
|
|
;----------------------------------------- |
; Serial port IRQ |
;----------------------------------------- |
; |
SerialIRQ: |
DisplayByte: |
pha |
phx |
phy |
push r4 |
|
lda UART_IS ; get interrupt status |
bpl sirq1 ; no interrupt |
and #0x7f ; switch on interrupt type |
cmp #4 |
beq srxirq |
cmp #$0C |
beq stxirq |
cmp #$10 |
beq smsirq |
; unknown IRQ type |
sirq1: |
pop r4 |
ply |
plx |
lsr r1,r1,#4 |
jsr DisplayNybble |
pla |
rti |
|
|
; Get the modem status and record it |
smsirq: |
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 |
;------------------------------------------------------------------------------ |
3385,41 → 2126,6
pla |
rts |
|
;------------------------------------------------------------------------------ |
; Display the byte in r1 |
;------------------------------------------------------------------------------ |
; |
DisplayByte: |
pha |
lsr r1,r1,#4 |
jsr DisplayNybble |
pla |
jmp DisplayNybble ; tail rts |
message "785" |
;------------------------------------------------------------------------------ |
; Display the half-word in r1 |
;------------------------------------------------------------------------------ |
; |
DisplayHalf: |
pha |
lsr r1,r1,#8 |
jsr DisplayByte |
pla |
jsr DisplayByte |
rts |
|
message "797" |
;------------------------------------------------------------------------------ |
; Display the half-word in r1 |
;------------------------------------------------------------------------------ |
; |
DisplayWord: |
pha |
lsr r1,r1,#16 |
jsr DisplayHalf |
pla |
jsr DisplayHalf |
rts |
message "810" |
;------------------------------------------------------------------------------ |
; Display memory pointed to by r2. |
3428,7 → 2134,7
; |
DisplayMemW: |
pha |
lda #':' |
lda #'>' |
jsr DisplayChar |
txa |
jsr DisplayWord |
3456,6 → 2162,52
pla |
rts |
|
;------------------------------------------------------------------------------ |
; Display memory pointed to by r2. |
; destroys r1,r3 |
;------------------------------------------------------------------------------ |
; |
DisplayMemBytes: |
pha |
phy |
lda #'>' |
jsr DisplayChar |
lda #'B' |
jsr DisplayChar |
lda #' ' |
jsr DisplayChar |
txa |
jsr DisplayWord |
ldy #0 |
.001: |
lda #' ' |
jsr DisplayChar |
lb r1,0,x |
jsr DisplayByte |
inx |
iny |
cpy #8 |
blo .001 |
lda #':' |
jsr DisplayChar |
ldy #0 |
sub r2,r2,#8 |
.002 |
lb r1,0,x |
cmp #26 ; convert control characters to '.' |
bhs .003 |
lda #'.' |
.003: |
jsr DisplayChar |
inx |
iny |
cpy #8 |
blo .002 |
jsr CRLF |
ply |
pla |
rts |
|
message "Monitor" |
;============================================================================== |
; System Monitor Program |
3468,7 → 2220,7
lda #0 ; turn off keyboard echo |
jsr SetKeyboardEcho |
jsr RequestIOFocus |
PromptLn: |
.PromptLn: |
jsr CRLF |
lda #'$' |
jsr DisplayChar |
3475,7 → 2227,7
|
; Get characters until a CR is keyed |
; |
Prompt3: |
.Prompt3: |
jsr RequestIOFocus |
; lw r1,#2 ; get keyboard character |
; syscall #417 |
3483,24 → 2235,27
; cmp #0 |
jsr KeybdGetChar |
cmp #-1 |
beq Prompt3 |
beq .Prompt3 |
; jsr KeybdGetCharDirect |
cmp #CR |
beq Prompt1 |
beq .Prompt1 |
jsr DisplayChar |
bra Prompt3 |
bra .Prompt3 |
|
; Process the screen line that the CR was keyed on |
; |
Prompt1: |
.Prompt1: |
lda #80 |
sta LEDS |
ldx RunningTCB |
cpx #MAX_TASKNO |
bhi Prompt3 |
ldx TCB_hJCB,x |
cpx #NR_JCB |
bhs .Prompt3 |
mul r2,r2,#JCB_Size |
add r2,r2,#JCBs |
lda #81 |
sta LEDS |
stz TCB_CursorCol,x ; go back to the start of the line |
stz JCB_CursorCol,x ; go back to the start of the line |
jsr CalcScreenLoc ; r1 = screen memory location |
tay |
lda #82 |
3507,7 → 2262,7
sta LEDS |
jsr MonGetch |
cmp #'$' |
bne Prompt2 ; skip over '$' prompt character |
bne .Prompt2 ; skip over '$' prompt character |
lda #83 |
sta LEDS |
jsr MonGetch |
3514,27 → 2269,31
|
; Dispatch based on command character |
; |
Prompt2: |
cmp #':' |
.Prompt2: |
cmp #'>' |
beq EditMem |
cmp #'M' |
bne .testDIR |
jsr MonGetch |
cmp #'B' |
beq DumpMemBytes |
dey |
bra DumpMem |
.testDIR: |
cmp #'D' |
bne Prompt8 |
jsr MonGetch |
cmp #'R' |
beq DumpReg |
bne .Prompt8 |
cmp #'I' |
beq DoDir |
dey |
bra DumpMem |
Prompt8: |
bra Monitor |
.Prompt8: |
cmp #'F' |
bne Prompt7 |
bne .Prompt7 |
jsr MonGetch |
cmp #'L' |
bne Prompt8a |
bne .Prompt8a |
jsr DumpIOFocusList |
jmp Monitor |
Prompt8a: |
.Prompt8a: |
cmp #'I' |
beq DoFig |
cmp #'M' |
3541,137 → 2300,194
beq DoFmt |
dey |
bra FillMem |
Prompt7: |
.Prompt7: |
cmp #'B' ; $B - start tiny basic |
bne Prompt4 |
lda #3 |
ldy #CSTART |
ldx #0 |
jsr StartTask |
; jsr CSTART |
bne .Prompt4 |
mStartTask #PRI_LOW,#0,#CSTART,#0,#4 |
bra Monitor |
Prompt4: |
.Prompt4: |
cmp #'b' |
bne Prompt5 |
lda #3 ; priority level 3 |
ldy #$C000 ; start address $C000 |
ldx #$00000000 ; flags: |
jsr StartTask |
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: |
.Prompt5: |
cmp #'J' ; $J - execute code |
beq ExecuteCode |
cmp #'L' ; $L - load dector |
beq LoadSector |
beq LoadBlock |
cmp #'W' |
beq WriteSector |
Prompt9: |
beq WriteBlock |
.Prompt9: |
cmp #'?' ; $? - display help |
bne Prompt10 |
bne .Prompt10 |
lda #HelpMsg |
jsr DisplayStringB |
jmp Monitor |
Prompt10: |
.Prompt10: |
cmp #'C' ; $C - clear screen |
beq TestCLS |
cmp #'r' |
bne Prompt12 |
bne .Prompt12 |
lda #4 ; priority level 4 |
ldx #0 ; zero all flags at startup |
ldy #RandomLines ; task address |
; jsr (y) |
jsr StartTask |
jsr (y) |
; jsr StartTask |
; jsr ($FFFFC004>>2) ; StartTask |
jmp Monitor |
; jmp RandomLinesCall |
Prompt12: |
Prompt13: |
.Prompt12: |
.Prompt13: |
cmp #'P' |
bne Prompt14 |
lda #2 |
ldx #0 |
ldy #Piano |
jsr StartTask |
bne .Prompt14 |
mStartTask #PRI_NORMAL,#0,#Piano,#0,#2 |
jmp Monitor |
|
Prompt14: |
.Prompt14: |
cmp #'T' |
bne Prompt15 |
bne .Prompt15 |
jsr MonGetch |
cmp #'O' |
bne Prompt14a |
bne .Prompt14a |
jsr DumpTimeoutList |
jmp Monitor |
Prompt14a: |
.Prompt14a: |
cmp #'I' |
bne Prompt14b |
bne .Prompt14b |
jsr DisplayDatetime |
jmp Monitor |
Prompt14b: |
.Prompt14b: |
cmp #'E' |
bne Prompt14c |
bne .Prompt14c |
jsr ReadTemp |
jmp Monitor |
Prompt14c: |
.Prompt14c: |
dey |
jsr DumpTaskList |
jmp Monitor |
|
Prompt15: |
.Prompt15: |
cmp #'S' |
bne Prompt16 |
bne .Prompt16 |
jsr MonGetch |
cmp #'P' |
bne Prompt18 |
bne .Prompt18 |
jsr ignBlanks |
jsr GetHexNumber |
sta SPSave |
jmp Monitor |
Prompt18: |
.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 spi_init |
jsr SDInit |
cmp #0 |
bne Monitor |
jsr spi_read_part |
jsr SDReadPart |
cmp #0 |
bne Monitor |
jsr spi_read_boot |
jsr SDReadBoot |
cmp #0 |
bne Monitor |
jsr loadBootFile |
jmp Monitor |
Prompt16: |
.Prompt16: |
cmp #'e' |
bne Prompt17 |
lda #1 |
ldx #0 |
ldy #eth_main |
jsr StartTask |
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: |
.Prompt17: |
cmp #'R' |
bne Prompt19 |
bne .Prompt19 |
jsr MonGetch |
cmp #'S' |
beq LoadSector |
beq LoadBlock |
dey |
bra SetRegValue |
jmp Monitor |
Prompt19: |
.Prompt19: |
cmp #'K' |
bne Monitor |
Prompt19a: |
bne .Prompt20 |
.Prompt19a: |
jsr MonGetch |
cmp #' ' |
bne Prompt19a |
bne .Prompt19a |
jsr ignBlanks |
jsr GetDecNumber |
jsr KillTask |
jmp Monitor |
.Prompt20: |
cmp #'8' |
bne .Prompt21 |
jsr Test816 |
jmp Monitor |
.Prompt21: |
cmp #'m' |
bne Monitor |
; lda #3 |
; ldx #0 |
; ldy #test_mbx_prg |
; jsr StartTask |
lda #PRI_LOW |
ldx #0 |
ldy #test_mbx_prg |
ld r4,#0 |
ld r5,#1 ; Job 1! |
int #4 |
db 1 |
bra Monitor |
|
message "Prompt16" |
RandomLinesCall: |
3705,10 → 2521,8
cmp #'S' |
bne Monitor |
jsr ClearScreen |
ldx RunningTCB |
stz TCB_CursorCol,x |
stz TCB_CursorRow,x |
jsr CalcScreenLoc |
jsr HomeCursor |
; jsr CalcScreenLoc |
jmp Monitor |
message "HelpMsg" |
HelpMsg: |
3715,11 → 2529,12
db "? = Display help",CR,LF |
db "CLS = clear screen",CR,LF |
db "S = Boot from SD Card",CR,LF |
db ": = Edit memory bytes",CR,LF |
db "L = Load sector",CR,LF |
db "W = Write sector",CR,LF |
db "DR = Dump registers",CR,LF |
db "D = Dump memory",CR,LF |
db "SU = supermon816",CR,LF |
db "L = Load Block",CR,LF |
db "W = Write Block",CR,LF |
db "DIR = Disk directory",CR,LF |
db "M = Dump memory words, MB = Dump memory bytes",CR,LF |
db "> = Edit memory words",CR,LF |
db "F = Fill memory",CR,LF |
db "FL = Dump I/O Focus List",CR,LF |
; db "FIG = start FIG Forth",CR,LF |
3727,7 → 2542,7
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 = Dump registers, Rn = Set register value",CR,LF |
db "r = random lines - test bitmap",CR,LF |
db "e = ethernet test",CR,LF |
db "T = Dump task list",CR,LF |
3734,7 → 2549,8
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 |
db "P = Piano",CR,LF |
db "8 = 816 test",CR,LF,0 |
|
;------------------------------------------------------------------------------ |
; Ignore blanks in the input |
3757,7 → 2573,7
EditMem: |
jsr ignBlanks |
jsr GetHexNumber |
or r5,r1,r0 |
ld r5,r1 |
ld r4,#3 |
edtmem1: |
jsr ignBlanks |
3820,7 → 2636,7
sta SRSave |
jmp Monitor |
|
LoadSector: |
LoadBlock: |
jsr ignBlanks |
jsr GetDecNumber |
pha |
3829,13 → 2645,22
tax |
phx |
; ld r2,#0x3800 |
jsr spi_init |
lda #16 ; SD Card device # |
ldx #1 ; Init |
jsr DeviceOp |
; jsr SDInit |
plx |
pla |
jsr spi_read_sector |
lda #16 ; SD Card device # |
ldx #11 ; opcode: Read blocks |
pop r5 ; r5 = pointer to data storage area |
ply ; y = block number to read |
ld r4,#1 ; 1 block to read |
jsr DeviceOp |
; jsr SDReadSector |
jmp Monitor |
|
WriteSector: |
WriteBlock: |
jsr ignBlanks |
jsr GetDecNumber |
pha |
3843,13 → 2668,14
jsr GetHexNumber |
tax |
phx |
jsr spi_init |
jsr SDInit |
plx |
pla |
jsr spi_write_sector |
jsr SDWriteSector |
jmp Monitor |
|
;------------------------------------------------------------------------------ |
; Command 'R' |
; Dump the register set. |
;------------------------------------------------------------------------------ |
message "DumpReg" |
3857,7 → 2683,7
ldy #0 |
DumpReg1: |
jsr CRLF |
lda #':' |
lda #'$' |
jsr DisplayChar |
lda #'R' |
jsr DisplayChar |
3885,10 → 2711,14
jsr DisplayWord |
jsr CRLF |
jmp Monitor |
|
;------------------------------------------------------------------------------ |
; Command 'Rn' |
;------------------------------------------------------------------------------ |
SetRegValue: |
jsr GetDecNumber |
cmp #0 |
beq DumpReg |
cmp #15 |
bpl Monitor |
pha |
3897,49 → 2727,129
ply |
sta R1Save,y |
jmp Monitor |
|
|
;------------------------------------------------------------------------------ |
; Do a memory dump of the requested location. |
;------------------------------------------------------------------------------ |
; |
DumpMem: |
GetTwoParams: |
jsr ignBlanks |
jsr GetHexNumber ; get start address of dump |
tax |
jsr ignBlanks |
jsr GetHexNumber ; get number of words to dump |
lsr ; 1/4 as many dump rows |
lsr |
bne Dumpmem2 |
lda #1 ; dump at least one row |
Dumpmem2: |
jsr GetHexNumber ; get end address of dump |
rts |
|
;------------------------------------------------------------------------------ |
; Get a range, the end must be greater or equal to the start. |
;------------------------------------------------------------------------------ |
GetRange: |
jsr GetTwoParams |
cmp r2,r1 |
bhi DisplayErr |
rts |
|
;------------------------------------------------------------------------------ |
; Command 'M' |
; Do a memory dump of the requested location. |
;------------------------------------------------------------------------------ |
; |
DumpMem: |
jsr GetRange |
jsr CRLF |
bra DumpmemW |
DumpmemW: |
jsr CheckKeys |
jsr DisplayMemW |
dea |
bne DumpmemW |
cmp r2,r1 |
bls DumpmemW |
jmp Monitor |
|
DumpMemBytes: |
jsr GetRange |
jsr CRLF |
.001: |
jsr CheckKeys |
jsr DisplayMemBytes |
cmp r2,r1 |
bls .001 |
jmp Monitor |
|
bra Monitor |
message "FillMem" |
;------------------------------------------------------------------------------ |
; CheckKeys: |
; Checks for a CTRLC or a scroll lock during long running dumps. |
;------------------------------------------------------------------------------ |
CheckKeys: |
jsr CTRLCCheck |
jmp CheckScrollLock |
|
;------------------------------------------------------------------------------ |
; CTRLCCheck |
; Checks to see if CTRL-C is pressed. If so then the current routine is |
; aborted and control is returned to the monitor. |
;------------------------------------------------------------------------------ |
|
CTRLCCheck: |
pha |
jsr KeybdGetChar |
cmp #CTRLC |
beq .0001 |
pla |
rts |
.0001: |
pla |
pla |
jmp Monitor |
|
;------------------------------------------------------------------------------ |
; CheckScrollLock: |
; Check for a scroll lock by the user. If scroll lock is active then tasks |
; are rescheduled while the scroll lock state is tested in a loop. |
;------------------------------------------------------------------------------ |
|
CheckScrollLock: |
pha |
.0002: |
jsr GetPtrCurrentJCB |
lda JCB_KeybdLocks,r1 |
bit #$4000 ; is scroll lock active ? |
beq .0001 |
int #2 ; reschedule tasks |
bra .0002 |
.0001: |
pla |
rts |
|
|
;------------------------------------------------------------------------------ |
; Command 'F' or "FB" |
; Fill memory with specified value. |
;------------------------------------------------------------------------------ |
|
FillMem: |
jsr GetRange |
txy ; y = start address |
sub r1,r1,r2 ; acc = count |
pha |
jsr ignBlanks |
jsr GetHexNumber ; get start address of dump |
jsr GetHexNumber ; get the fill byte |
tax |
pla |
stos |
jmp Monitor |
|
FillMemBytes: |
jsr GetRange |
txy |
sub r2,r1,r2 ; x = count |
inx |
jsr ignBlanks |
jsr GetHexNumber ; 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 |
jsr GetHexNumber |
.0001: |
sb r1,0,y |
iny |
dex |
bne .0001 |
jmp Monitor |
|
|
;------------------------------------------------------------------------------ |
; Get a hexidecimal number. Maximum of eight digits. |
; R3 = text pointer (updated) |
4033,7 → 2943,17
lda #-1 |
rts |
|
DisplayErr: |
lda #msgErr |
jsr DisplayStringB |
jmp Monitor |
|
msgErr: |
db "**Err",CR,LF,0 |
|
;============================================================================== |
;============================================================================== |
|
;------------------------------------------------------------------------------ |
;------------------------------------------------------------------------------ |
ClearBmpScreen: |
4040,15 → 2960,16
pha |
phx |
phy |
lda #(1364*768)>>2 ; a = # words to clear |
lda #(680*384) ; a = # bytes to clear |
ldx #0x29292929 ; acc = color for four pixels |
ldy #BITMAPSCR ; y = screen address |
ldy #BITMAPSCR;<<2 ; y = screen address |
cbmp1: |
; tsr LFSR,r2 |
; sb r2,0,y |
; iny |
; dea |
; bne cbmp1 |
stos |
;cbsj4 |
; sta (y) ; store pixel data |
; iny ; advance screen address |
; dex ; decrement pixel count and loop back |
; bne cbsj4 |
ply |
plx |
pla |
4125,6 → 3046,9
;-------------------------------------------------------------------------- |
; |
Beep: |
lda #2 ; check for a PSG |
bmt CONFIGREC |
beq .ret |
lda #15 ; master volume to max |
sta PSG+64 |
lda #13422 ; 800Hz |
4137,416 → 3061,22
sta PSGADSR0 |
lda #0x1104 ; gate, output enable, triangle waveform |
sta PSGCTRL0 |
lda #100 ; delay about 1s |
jsr Sleep |
; lda #1000 ; delay about 1s |
mSleep #1000 |
lda #0x0104 ; gate off, output enable, triangle waveform |
sta PSGCTRL0 |
lda #100 ; delay about 1s |
jsr Sleep |
; lda #1000 ; delay about 1s |
mSleep #1000 |
lda #83 |
sta LEDS |
lda #0x0000 ; gate off, output enable off, no waveform |
sta PSGCTRL0 |
.ret |
rts |
|
;-------------------------------------------------------------------------- |
;-------------------------------------------------------------------------- |
; |
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 |
include "Piano.asm" |
include "SDCard.asm" |
|
playnt1a: |
lda #7217 |
jsr Tone |
bra playnt |
playnt1b: |
lda #8101 |
jsr Tone |
bra playnt |
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: |
pha |
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 #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 |
rts |
|
;============================================================================== |
;============================================================================== |
; |
; Initialize the SD card |
; Returns |
; acc = 0 if successful, 1 otherwise |
; Z=1 if successful, otherwise Z=0 |
; |
message "spi_init" |
spi_init |
lda #SPI_INIT_SD |
sta SPIMASTER+SPI_TRANS_TYPE_REG |
lda #SPI_TRANS_START |
sta SPIMASTER+SPI_TRANS_CTRL_REG |
nop |
spi_init1 |
lda SPIMASTER+SPI_TRANS_STATUS_REG |
nop |
nop |
cmp #SPI_TRANS_BUSY |
beq spi_init1 |
lda SPIMASTER+SPI_TRANS_ERROR_REG |
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 |
rts |
|
spi_delay: |
nop |
nop |
rts |
|
|
; SPI read sector |
; |
; r1= sector number to read |
; r2= address to place read data |
; Returns: |
; r1 = 0 if successful |
; |
spi_read_sector: |
phx |
phy |
push r4 |
|
sta SPIMASTER+SPI_SD_SECT_7_0_REG |
lsr r1,r1,#8 |
sta SPIMASTER+SPI_SD_SECT_15_8_REG |
lsr r1,r1,#8 |
sta SPIMASTER+SPI_SD_SECT_23_16_REG |
lsr r1,r1,#8 |
sta SPIMASTER+SPI_SD_SECT_31_24_REG |
|
ld r4,#20 ; retry count |
|
spi_read_retry: |
; Force the reciever fifo to be empty, in case a prior error leaves it |
; in an unknown state. |
lda #1 |
sta SPIMASTER+SPI_RX_FIFO_CTRL_REG |
|
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 |
; |
; r1= sector number to write |
; r2= address to get data from |
; Returns: |
; 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 |
sta SPIMASTER+SPI_TX_FIFO_CTRL_REG |
nop ; give I/O time to respond |
nop |
|
; now fill up the transmitter fifo |
ldy #512 |
spi_write_sect1: |
lb r1,0,x |
sta SPIMASTER+SPI_TX_FIFO_DATA_REG |
nop ; give the I/O time to respond |
nop |
inx |
dey |
bne spi_write_sect1 |
|
; set the sector number in the spi master address registers |
pla |
sta SPIMASTER+SPI_SD_SECT_7_0_REG |
lsr r1,r1,#8 |
sta SPIMASTER+SPI_SD_SECT_15_8_REG |
lsr r1,r1,#8 |
sta SPIMASTER+SPI_SD_SECT_23_16_REG |
lsr r1,r1,#8 |
sta SPIMASTER+SPI_SD_SECT_31_24_REG |
|
; issue the write command |
lda #RW_WRITE_SD_BLOCK |
sta SPIMASTER+SPI_TRANS_TYPE_REG |
lda #SPI_TRANS_START |
sta SPIMASTER+SPI_TRANS_CTRL_REG |
nop |
spi_write_sect2: |
lda SPIMASTER+SPI_TRANS_STATUS_REG |
nop ; just a delay between consecutive status reg reads |
nop |
cmp #SPI_TRANS_BUSY |
beq spi_write_sect2 |
lda SPIMASTER+SPI_TRANS_ERROR_REG |
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 |
|
spi_write_ret: |
ply |
plx |
rts |
|
; SPI read multiple sector |
; |
; r1= sector number to read |
; r2= address to write data |
; r3= number of sectors to read |
; |
; Returns: |
; r1 = 0 if successful |
; |
spi_read_multiple: |
push r4 |
ld r4,#0 |
spi_rm1: |
pha |
jsr spi_read_sector |
add r4,r4,r1 |
add r2,r2,#512 |
pla |
ina |
dey |
bne spi_rm1 |
ld r1,r4 |
pop r4 |
rts |
|
; SPI write multiple sector |
; |
; r1= sector number to write |
; r2= address to get data from |
; r3= number of sectors to write |
; |
; Returns: |
; r1 = 0 if successful |
; |
spi_write_multiple: |
push r4 |
ld r4,#0 |
spi_wm1: |
pha |
jsr spi_write_sector |
add r4,r4,r1 ; accumulate an error count |
add r2,r2,#512 ; 512 bytes per sector |
pla |
ina |
dey |
bne spi_wm1 |
ld r1,r4 |
pop r4 |
rts |
|
; read the partition table to find out where the boot sector is. |
; Returns |
; r1 = 0 everything okay, 1=read error |
; also Z=1=everything okay, Z=0=read error |
; |
spi_read_part: |
phx |
stz startSector ; default starting sector |
lda #0 ; r1 = sector number (#0) |
ldx #BYTE_SECTOR_BUF ; r2 = target address (word to byte address) |
jsr spi_read_sector |
cmp #0 |
bne spi_rp1 |
lb r1,BYTE_SECTOR_BUF+$1C9 |
asl r1,r1,#8 |
orb r1,r1,BYTE_SECTOR_BUF+$1C8 |
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. |
; Make sure it's the boot sector by looking for the signature bytes 'EB' and '55AA'. |
; Returns: |
; r1 = 0 means this card is bootable |
; r1 = 1 means a read error occurred |
; r1 = 2 means the card is not bootable |
; |
spi_read_boot: |
phx |
phy |
push r5 |
lda startSector ; r1 = sector number |
ldx #BYTE_SECTOR_BUF ; r2 = target address |
jsr spi_read_sector |
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 |
plx |
lda #2 |
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 |
; |
4587,7 → 3117,7
lb r3,BYTE_SECTOR_BUF+$D ; sectors per cluster |
loadBootFile1: |
ld r1,r5 ; r1=sector to read |
jsr spi_read_sector |
jsr SDReadSector |
inc r5 ; r5 = next sector |
add r2,r2,#512 |
dec r3 |
4626,17 → 3156,14
db "SD card write error",0 |
|
do_fmt: |
jsr spi_init |
jsr SDInit |
cmp #0 |
bne fmt_abrt |
ldx #DIRBUF |
ldy #65536 |
; clear out the directory buffer |
dfmt1: |
stz (x) |
inx |
dey |
bne dfmt1 |
lda #65535 |
ldx #0 |
ldy #DIRBUF |
stos |
jsr store_dir |
fmt_abrt: |
rts |
4643,7 → 3170,7
|
do_dir: |
jsr CRLF |
jsr spi_init |
jsr SDInit |
cmp #0 |
bne dirabrt |
jsr load_dir |
4691,7 → 3218,7
lda #4000 |
ldx #DIRBUF<<2 |
ldy #64 |
jsr spi_read_multiple |
jsr SDReadMultiple |
ply |
plx |
pla |
4703,7 → 3230,7
lda #4000 |
ldx #DIRBUF<<2 |
ldy #64 |
jsr spi_write_multiple |
jsr SDWriteMultiple |
ply |
plx |
pla |
4715,7 → 3242,7
; |
do_save: |
pha |
jsr spi_init |
jsr SDInit |
cmp #0 |
bne dsavErr |
pla |
4742,7 → 3269,7
ld r1,r7 ; r1 = sector number |
lsr r3,r3,#9 ; r3/512 |
iny ; +1 |
jsr spi_write_multiple |
jsr SDWriteMultiple |
dsav3: |
rts |
; Here the filename didn't match |
4788,7 → 3315,7
|
do_load: |
pha |
jsr spi_init |
jsr SDInit |
cmp #0 |
bne dsavErr |
pla |
4817,7 → 3344,7
ld r1,r7 ; r1 = sector number |
lsr r3,r3,#9 ; r3/512 |
iny ; +1 |
jsr spi_read_multiple |
jsr SDReadMultiple |
dlod3: |
rts |
; Here the filename didn't match |
4833,628 → 3360,9
|
msgFileNotFound: |
db CR,LF,"File not found.",CR,LF |
|
;============================================================================== |
; 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: |
pha |
phx |
push r4 |
ld r4,#ETHMAC |
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 |
;include "ethernet.asm" |
|
; r1 = PHY |
; r2 = reg |
|
eth_mii_read: |
phx |
phy |
ldy #ETHMAC |
asl r2,r2,#8 |
or r1,r1,r2 |
sta ETH_MIIADDRESS,y |
lda #ETH_MIICOMMAND_RSTAT |
sta ETH_MIICOMMAND,y |
stz ETH_MIICOMMAND,y |
emir1: |
lda ETH_MIISTATUS,y |
bit #ETH_MIISTATUS_BUSY |
bne emir1 |
lda ETH_MIIRX_DATA,y |
ply |
plx |
rts |
|
ethmac_setup: |
ld r4,#ETHMAC |
lda #ETH_MIIMODER_RST |
sta ETH_MIIMODER,r4 |
lda ETH_MIIMODER,r4 |
and #~ETH_MIIMODER_RST |
sta ETH_MIIMODER,r4 |
lda #$10 ; /16=1.25MHz |
sta ETH_MIIMODER,r4 ; Clock divider for MII Management interface |
lda #ETH_MODER_RST |
sta ETH_MODER,r4 |
lda ETH_MODER,r4 |
and #~ETH_MODER_RST |
sta ETH_MODER,r4 |
|
stz ETH_MIITX_DATA,r4 |
stz ETH_MIIADDRESS,r4 |
stz ETH_MIICOMMAND,r4 |
|
lda #0xEEF0DA42 |
sta ETH_MAC_ADDR0,r4 ; MAC0 |
lda #0x00FF |
sta ETH_MAC_ADDR1,r4 ; MAC1 |
|
lda #-1 |
sta ETH_INT_SOURCE,r4 |
|
; Advertise support for 10/100 FD/HD |
lda #ETH_PHY |
ldx #ETH_MII_ADVERTISE |
jsr eth_mii_read |
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 |
; |
message "eth_init" |
eth_init: |
pha |
phy |
ldy #ETHMAC |
lda #$A003 |
sta ETH_MODER,y |
; lda #0x64 ; 100 |
; sta ETH_MIIMODER,y |
; lda #7 ; PHY address |
; sta ETH_MIIADDRESS,y |
lda #0xEEF0DA42 |
sta ETH_MAC_ADDR0,y ; MAC0 |
lda #0x00FF |
sta ETH_MAC_ADDR1,y ; MAC1 |
ply |
pla |
rts |
|
; Request a packet and display on screen |
; r1 = address where to put packet |
; |
message "eth_request_packet" |
eth_request_packet: |
phx |
phy |
push r4 |
push r5 |
ldy #ETHMAC |
ldx #4 ; clear rx interrupt |
stx ETH_INT_SOURCE,y |
sta 0x181,y ; storage address |
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 |
plx |
rts |
|
; r1 = packet address |
; |
message "eth_interpret_packet" |
eth_interpret_packet: |
phx |
phy |
lb r2,12,r1 |
lb r3,13,r1 |
cpx #8 ; 0x806 ? |
bne eth2 |
cpy #6 |
bne eth2 |
lda #2 ; return r1 = 2 for ARP |
eth5: |
ply |
plx |
rts |
eth2: |
cpx #8 |
bne eth3 ; 0x800 ? |
cpy #0 |
bne eth3 |
lb r2,23,r1 |
cpx #1 |
bne eth4 |
lda #1 |
bra eth5 ; return 1 ICMP |
eth4: |
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 |
; r2 = packet length |
; |
message "eth_send_packet" |
eth_send_packet: |
phx |
phy |
push r4 |
ldy #ETHMAC |
; 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 |
rts |
|
; Only for IP type packets (not ARP) |
; r1 = rx buffer address |
; r2 = swap flag |
; Returns: |
; r1 = data start index |
; |
message "eth_build_packet" |
eth_build_packet: |
phy |
push r4 |
push r5 |
push r6 |
push r7 |
push r8 |
push r9 |
push r10 |
|
lb r3,6,r1 |
lb r4,7,r1 |
lb r5,8,r1 |
lb r6,9,r1 |
lb r7,10,r1 |
lb r8,11,r1 |
; write to destination header |
sb r3,0,r1 |
sb r4,1,r1 |
sb r5,2,r1 |
sb r6,3,r1 |
sb r7,4,r1 |
sb r8,5,r1 |
; write to source header |
ld r3,#my_MAC1 |
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 |
ply |
rts |
|
; Compute IPv4 checksum of header |
; r1 = packet address |
; r2 = data start |
; |
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 |
ply |
rts |
|
; r1 = packet address |
; returns r1 = 1 if this IP |
; |
message "eth_verifyIP" |
eth_verifyIP: |
phx |
phy |
push r4 |
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 |
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. |
;-------------------------------------------------------------------------- |
5470,212 → 3378,8
bne rsr1 |
rts |
|
;-------------------------------------------------------------------------- |
; Draw random lines on the bitmap screen. |
;-------------------------------------------------------------------------- |
; |
message "RandomLines" |
RandomLines: |
pha |
phx |
phy |
push r4 |
push r5 |
jsr RequestIOFocus |
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 |
pla |
rts |
|
msgRandomLines: |
db CR,LF,"Random lines running - press CTRL-C to exit.",CR,LF,0 |
|
;-------------------------------------------------------------------------- |
; Draw a pixel on the bitmap screen. |
; r1 = x coordinate |
; r2 = y coordinate |
; r3 = color |
;-------------------------------------------------------------------------- |
message "DrawPixel" |
DrawPixel: |
pha |
sta GA_X0 |
stx GA_Y0 |
sty GA_PEN |
lda #1 |
sta GA_CMD |
pla |
rts |
|
comment ~ |
pha |
phx |
push r4 |
mod r2,r2,#768 |
mod r1,r1,#1364 |
mul r2,r2,#1364 ; y * 1364 |
add r1,r1,r2 ; + x |
sb r3,BITMAPSCR<<2,r1 |
pop r4 |
plx |
pla |
rts |
~ |
|
;-------------------------------------------------------------------------- |
; Draw a line on the bitmap screen. |
;-------------------------------------------------------------------------- |
;50 REM DRAWLINE |
;100 dx = ABS(xb-xa) |
;110 dy = ABS(yb-ya) |
;120 sx = SGN(xb-xa) |
;130 sy = SGN(yb-ya) |
;140 er = dx-dy |
;150 PLOT xa,ya |
;160 if xa<>xb goto 200 |
;170 if ya=yb goto 300 |
;200 ee = er * 2 |
;210 if ee <= -dy goto 240 |
;220 er = er - dy |
;230 xa = xa + sx |
;240 if ee >= dx goto 270 |
;250 er = er + dx |
;260 ya = ya + sy |
;270 GOTO 150 |
;300 RETURN |
|
message "DrawLine" |
DrawLine: |
pha |
sta GA_X0 |
stx GA_Y0 |
sty GA_X1 |
st r4,GA_Y1 |
lda LineColor |
sta GA_PEN |
lda #2 |
sta GA_CMD |
pla |
rts |
|
comment ~ |
pha |
phx |
phy |
push r4 |
push r5 |
push r6 |
push r7 |
push r8 |
push r9 |
push r10 |
push r11 |
|
sub r5,r3,r1 ; dx = abs(x2-x1) |
bpl dln1 |
sub r5,r0,r5 |
dln1: |
sub r6,r4,r2 ; dy = abs(y2-y1) |
bpl dln2 |
sub r6,r0,r6 |
dln2: |
|
sub r7,r3,r1 ; sx = sgn(x2-x1) |
beq dln5 |
bpl dln4 |
ld r7,#-1 |
bra dln5 |
dln4: |
ld r7,#1 |
dln5: |
|
sub r8,r4,r2 ; sy = sgn(y2-y1) |
beq dln8 |
bpl dln7 |
ld r8,#-1 |
bra dln8 |
dln7: |
ld r8,#1 |
|
dln8: |
sub r9,r5,r6 ; er = dx-dy |
dln150: |
phy |
ldy LineColor |
jsr DrawPixel |
ply |
cmp r1,r3 ; if (xa <> xb) |
bne dln200 ; goto 200 |
cmp r2,r4 ; if (ya==yb) |
beq dln300 ; goto 300 |
dln200: |
asl r10,r9 ; ee = er * 2 |
sub r11,r0,r6 ; r11 = -dy |
cmp r10,r11 ; if (ee <= -dy) |
bmi dln240 ; goto 240 |
beq dln240 |
sub r9,r9,r6 ; er = er - dy |
add r1,r1,r7 ; xa = xa + sx |
dln240: |
cmp r10,r5 ; if (ee >= dx) |
bpl dln150 ; goto 150 |
add r9,r9,r5 ; er = er + dx |
add r2,r2,r8 ; ya = ya + sy |
bra dln150 ; goto 150 |
|
dln300: |
pop r11 |
pop r10 |
pop r9 |
pop r8 |
pop r7 |
pop r6 |
pop r5 |
pop r4 |
ply |
plx |
pla |
rts |
~ |
|
;include "float.asm" |
include "RandomLines.asm" |
|
;-------------------------------------------------------------------------- |
; RTF65002 code to display the date and time from the date/time device. |
5728,276 → 3432,11
pla |
rts |
|
;-------------------------------------------------------------------------- |
; ReadTemp |
; Read and display the temperature from a DS1626 temperature sensor |
; device. RTF65002 source code. |
;-------------------------------------------------------------------------- |
DS1626_CMD =$FFDC0300 |
DS1626_DAT =$FFDC0301 |
; Commands |
START_CNV = $51; |
STOP_CNV = $22; |
READ_TEMP = $AA; |
READ_CONFIG = $AC; |
READ_TH = $A1; |
READ_TL = $A2; |
WRITE_TH = $01; |
WRITE_TL = $02; |
WRITE_CONFIG = $0C; |
POR = $54; |
include "ReadTemp.asm" |
|
ReadTemp: |
lda CONFIGREC ; Do we even have a temperature sensor ? |
bit #$10 |
beq rdtmp3 ; If not, output '0.000' |
rdtmp1: |
; 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 |
; completed. |
jsr rdt_busy_wait |
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 |
lda #10 |
jsr tSleep |
pla |
rdtmp4: |
jsr CRLF |
and #$FFF |
bit #$800 ; check for negative temperature |
beq rdtmp7 |
sub r1,r0,r1 ; negate the number |
and #$FFF |
pha |
lda #'-' ; output a minus sign |
jsr DisplayChar |
pla |
rdtmp7: |
pha ; save off value |
lsr r1,r1,#4 ; get rid of fractional portion |
and #$7F ; strip off sign bit |
ldx #3 ; output the whole number part |
jsr PRTNUM |
lda #'.' ; followed by a decimal point |
jsr DisplayChar |
pla ; get back temp value |
and #$0F |
mul r1,r1,#625 ; 1/16th's per degree |
pha ; save off fraction bits |
div r1,r1,#100 ; calculate the first digit |
add #'0' |
jsr DisplayChar ; output digit |
pla ; get back fractions bits |
pha ; and save again |
div r1,r1,#10 ; shift over to second digit |
mod r1,r1,#10 ; ignore high order bits |
add #'0' |
jsr DisplayChar ; display the digit |
pla ; get back fraction |
mod r1,r1,#10 ; compute low order digit |
add #'0' |
jsr DisplayChar ; display low order digit |
jsr CRLF |
rts |
rdtmp3: |
lda #0 |
bra rdtmp4 |
include "memory.asm" |
|
; Returns: |
; acc = value from data register |
; |
rdt_busy_wait: |
jsr KeybdGetChar |
cmp #CTRLC |
beq Monitor |
lda DS1626_DAT |
bit #$8000 |
bne rdt_busy_wait |
rts |
|
tSleep: |
ldx Milliseconds |
txa |
tSleep1: |
ldx Milliseconds |
sub r2,r2,r1 |
cpx #100 |
blo tSleep1 |
rts |
|
;============================================================================== |
; Memory Management routines follow. |
;============================================================================== |
MemInit: |
lda #1 ; initialize memory semaphore |
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 |
|
ReportMemFree: |
jsr CRLF |
lda #HeapEnd |
ina |
sub #HeapStart |
ldx #5 |
jsr PRTNUM |
lda #msgMemFree |
jsr DisplayStringB |
rts |
|
msgMemFree: |
db " words free",CR,LF,0 |
|
;------------------------------------------------------------------------------ |
; Allocate memory from the heap. |
;------------------------------------------------------------------------------ |
MemAlloc: |
phx |
phy |
push r4 |
memaSpin: |
ldx MEM_SEMA+1 |
beq memaSpin |
ldx #HeapStart |
mema4: |
ldy MEM_FLAG,x ; Check the flag word to see if this block is available |
cpy #$4D454D20 |
bne mema1 ; block not available, go to next block |
ld r4,MEM_NEXT,x ; compute the size of this block |
sub r4,r4,r2 |
sub r4,r4,#4 ; minus size of block header |
cmp r1,r4 ; is the block large enough ? |
bmi mema2 ; if yes, go allocate |
mema1: |
ldx MEM_NEXT,x ; go to the next block |
beq mema3 ; if no more blocks, out of memory error |
bra mema4 |
mema2: |
ldy #$6D656D20 |
sty MEM_FLAG,x |
sub r4,r4,r1 |
cmp r4,#4 ; is the block large enough to split |
bpl memaSplit |
stz MEM_SEMA+1 |
txa |
pop r4 |
ply |
plx |
rts |
mema3: ; insufficient memory |
stz MEM_SEMA+1 |
pop r4 |
ply |
plx |
lda #0 |
rts |
memaSplit: |
add r4,r1,r2 |
add r4,r4,#4 |
ldy #$4D454D20 |
sty (r4) |
sty MEM_FLAG,r4 |
stx MEM_PREV,r4 |
ldy MEM_NEXT,x |
sty MEM_NEXT,r4 |
st r4,MEM_PREV,y |
ld r1,r4 |
stz MEM_SEMA+1 |
pop r4 |
ply |
plx |
rts |
|
;------------------------------------------------------------------------------ |
; Free previously allocated memory. Recombine with next and previous blocks |
; if they are free as well. |
;------------------------------------------------------------------------------ |
MemFree: |
cmp #0 ; null pointer ? |
beq memf2 |
phx |
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 |
|
;------------------------------------------------------------------------------ |
; Bus Error Routine |
; This routine display a message then restarts the BIOS. |
;------------------------------------------------------------------------------ |
6015,7 → 3454,8
stx LEDS |
jsr CRLF |
stz RunningTCB |
stz IOFocusNdx |
lda #JCBs |
sta IOFocusNdx |
lda #msgBusErr |
jsr DisplayStringB |
tya |
6054,177 → 3494,11
|
|
;------------------------------------------------------------------------------ |
; Reschedule tasks to run without affecting the timeout list timing. |
;------------------------------------------------------------------------------ |
; |
reschedule: |
cld ; clear extended precision mode |
|
pusha ; save off regs on the stack |
|
ldx RunningTCB |
tsa ; save off the stack pointer |
sta TCB_SPSave,x |
tsr sp8,r1 ; and the eight bit mode stack pointer |
sta TCB_SP8Save,x |
tsr abs8,r1 |
sta TCB_ABS8Save,x ; 8 bit emulation base register |
jmp SelectTaskToRun |
|
strStartQue: |
db 1,0,0,0,2,0,0,0,3,0,1,0,4,0,0,0 |
; db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 |
|
;------------------------------------------------------------------------------ |
; 100 Hz interrupt |
; - takes care of "flashing" the cursor |
; - decrements timeouts for tasks on timeout list |
; - switching tasks |
;------------------------------------------------------------------------------ |
; |
p100Hz: |
; Handle every other interrupt because 100Hz interrupts may be too fast. |
pha |
lda #3 ; reset the edge sense circuit |
sta PIC_RSTE |
lda IRQFlag |
ina |
sta IRQFlag |
ror |
pla |
bcc p100Hz11 |
rti |
|
p100Hz11: |
|
cld ; clear extended precision mode |
|
pusha ; save off regs on the stack |
|
ldx RunningTCB |
tsa ; save off the stack pointer |
sta TCB_SPSave,x |
tsr sp8,r1 ; and the eight bit mode stack pointer |
sta TCB_SP8Save,x |
tsr abs8,r1 |
sta TCB_ABS8Save,x ; 8 bit emulation base register |
lda #TS_READY |
sta TCB_Status,x |
p100Hz4: |
|
; 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 |
; the list. Also decrement the timeout of the item at the head of the list. |
|
p100Hz15: |
ldx TimeoutList |
bmi p100Hz12 ; are there any entries in the timeout list ? |
lda TCB_Timeout,x |
bne p100Hz14 ; has this entry timed out ? |
txa |
jsr RemoveFromTimeoutList |
jsr AddTaskToReadyList |
bra p100Hz15 ; go back and see if there's another task to be removed |
; there could be a string of tasks to make ready. |
p100Hz14: |
dea ; decrement the entry's timeout |
sta TCB_Timeout,x |
|
p100Hz12: |
; Falls through into selecting a task to run |
|
;------------------------------------------------------------------------------ |
; Search the ready queues for a ready task. |
; The search is occasionally started at a lower priority queue in order |
; to prevent starvation of lower priority tasks. This is managed by |
; using a tick count as an index to a string containing the start que. |
;------------------------------------------------------------------------------ |
; |
SelectTaskToRun: |
ld r6,#5 ; number of queues to search |
ldy IRQFlag ; use the IRQFlag as a buffer index |
lsr r3,r3,#1 ; the LSB is always the same |
and r3,r3,#$0F ; counts from 0 to 15 |
lb r3,strStartQue,y ; get the queue to start search at |
sttr2: |
lda QNdx0,y |
bmi sttr1 |
lda TCB_NxtRdy,r1 ; Advance the queue index |
sta QNdx0,y |
; This is the only place the RunningTCB is set (except for initialization). |
sta RunningTCB |
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 |
lda CONFIGREC |
bit #4096 |
beq sttr4 |
lda TCB_mmu_map,x |
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 |
trs r0,abs8 |
lda TCB_SP8Save,x ; get back eight bit stack pointer |
trs r1,sp8 |
ldx TCB_SPSave,x ; get back stack pointer |
txs |
popa ; restore registers |
rti |
|
; Set index to check the next ready list for a task to run |
sttr1: |
iny |
cpy #5 |
bne sttr5 |
ldy #0 |
sttr5 |
dec r6 |
bne sttr2 |
|
; Here there were no tasks ready |
; This should not be able to happen, so hang the machine. |
sttr3: |
ldx #94 |
stx LEDS |
bra sttr3 |
|
;------------------------------------------------------------------------------ |
; 1000 Hz interrupt |
; This IRQ must be fast. |
; Increments the millisecond counter |
;------------------------------------------------------------------------------ |
; |
message "p1000Hz" |
p1000Hz: |
pha |
lda #2 ; reset edge sense circuit |
6238,6 → 3512,7
; 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 |
6281,6 → 3556,15
jsr CRLF |
ina |
sta 4,x ; save incremented return address back to stack |
jsr DumpHistoryTable |
ply |
plx |
pla |
rti |
|
DumpHistoryTable: |
pha |
phx |
ldx #64 |
ioi1: |
tsr hist,r1 |
6289,11 → 3573,9
jsr DisplayChar |
dex |
bne ioi1 |
jsr CRLF |
ply |
plx |
pla |
rti |
rts |
|
EmuMVP: |
push r4 |
6351,17 → 3633,21
db "Unimplemented at: ",0 |
|
brk_rout: |
lda #16 |
sta LEDS |
jsr kernel_panic |
db "Break routine",0 |
jsr DumpHistoryTable |
stp |
rti |
|
nmirout: |
pha |
phx |
lda #msgPerr |
jsr DisplayStringB |
tsx |
lda 4,x |
lda 3,sp |
jsr DisplayWord |
jsr CRLF |
plx |
pla |
rti |
|
6368,6 → 3654,1963
msgPerr: |
db "Parity error at: ",0 |
|
;============================================================================== |
; Finitron Multi-Tasking Kernel (FMTK) |
; __ |
; \\__/ o\ (C) 2013, 2014 Robert Finch, Stratford |
; \ __ / All rights reserved. |
; \/_// robfinch<remove>@opencores.org |
; || |
;============================================================================== |
message "FMTK" |
org $FFFFC000 |
syscall_vectors: |
dw MTKInitialize |
dw StartTask |
dw ExitTask |
dw KillTask |
dw SetTaskPriority |
dw Sleep |
dw AllocMbx |
dw FreeMbx |
dw PostMsg |
dw SendMsg |
dw WaitMsg |
dw CheckMsg |
|
org $FFFFC200 |
message "MTKInitialize" |
MTKInitialize: |
; Initialize semaphores |
lda #1 |
sta freetcb_sema |
sta freembx_sema |
sta freemsg_sema |
sta tcb_sema |
sta readylist_sema |
sta tolist_sema |
sta mbx_sema |
sta msg_sema |
sta jcb_sema |
|
tsr vbr,r2 |
and r2,#-2 |
lda #reschedule |
sta 2,x |
lda #syscall_int |
sta 4,x |
lda #MTKTick |
sta 448+3,x |
stz UserTick |
|
lda #-1 |
sta TimeoutList ; no entries in timeout list |
sta QNdx0 |
sta QNdx1 |
sta QNdx2 |
sta QNdx3 |
sta QNdx4 |
|
stz missed_ticks |
|
; Initialize IO Focus List |
; |
lda #7 |
ldx #0 |
ldy #IOFocusTbl |
stos |
|
; Set owning job to zero (the monitor) |
lda #255 |
ldx #0 |
ldy #TCB_hJCB |
stos |
|
; zero out JCB's |
; This will NULL out the I/O focus list pointers |
lda #NR_JCB * JCB_Size |
ldx #0 |
lea r3,JCBs |
stos |
|
; Setup default values in the JCB's |
ldy #0 |
ldx #JCBs |
ijcb1: |
sty JCB_Number,x |
sty JCB_Map,x |
stz JCB_esc,x |
lda #31 |
sta JCB_VideoRows,x |
lda #56 |
sta JCB_VideoCols,x |
lda #1 ; turn on keyboard echo |
sta JCB_KeybdEcho,x |
sta JCB_CursorOn,x |
sta JCB_CursorFlash,x |
stz JCB_CursorRow,x |
stz JCB_CursorCol,x |
stz JCB_CursorType,x |
lda #%1011_01111 ; grey on grey |
sta JCB_NormAttr,x |
sta JCB_CurrAttr,x |
ld r4,r3 |
mul r4,r4,#8192 ; 8192 words per screen |
add r4,r4,#BIOS_SCREENS |
st r4,JCB_pVirtVid,x |
st r4,JCB_pVidMem,x |
add r4,r4,#$1000 |
st r4,JCB_pVirtVidAttr,x |
st r4,JCB_pVidMemAttr,x |
cpy #0 |
bne ijcb2 |
lda #%0110_01110 ; CE =blue on blue FB = grey on grey |
sta JCB_NormAttr,x |
sta JCB_CurrAttr,x |
ld r4,#TEXTSCR |
st r4,JCB_pVidMem,x |
add r4,r4,#$10000 |
st r4,JCB_pVidMemAttr,x |
ijcb2: |
lda #8 |
sta JCB_LogSize,x |
iny |
add r2,r2,#JCB_Size |
cpy #32 |
blo ijcb1 |
|
|
; Initialize free message list |
lda #NR_MSG |
sta nMsgBlk |
stz FreeMsg |
ldx #0 |
lda #1 |
st4: |
sta MSG_LINK,x |
ina |
inx |
cpx #NR_MSG |
bne st4 |
lda #-1 |
sta MBX_LINK+NR_MSG-1 |
|
; Initialize free mailbox list |
; Note the first NR_TCB mailboxes are statically allocated to the tasks. |
; They are effectively pre-allocated. |
lda #NR_MBX-NR_TCB |
sta nMailbox |
|
ldx #NR_TCB |
stx FreeMbxHandle |
lda #NR_TCB+1 |
st3: |
sta MBX_LINK,x |
ina |
inx |
cpx #NR_MBX |
bne st3 |
lda #-1 |
sta MBX_LINK+NR_MBX-1 |
|
; Initialize the FreeJCB list |
lda #JCBs+JCB_Size ; the next available JCB |
sta FreeJCB |
tax |
add r1,r1,#JCB_Size |
ldy #NR_JCB-1 |
st5: |
sta JCB_Next,x |
add r1,r1,#JCB_Size |
add r2,r2,#JCB_Size |
dey |
bne st5 |
stz JCB_Next,x |
|
; Initialize the FreeTCB list |
lda #1 ; the next available TCB |
sta FreeTCB |
ldx #1 |
lda #2 |
st2: |
sta TCB_NxtTCB,x |
ina |
inx |
cpx #256 |
bne st2 |
lda #-1 |
sta TCB_NxtTCB+255 |
lda #4 |
sta LEDS |
|
; Manually setup the BIOS task |
stz RunningTCB ; BIOS is task #0 |
stz TCB_NxtRdy ; manually build the ready list |
stz TCB_PrvRdy |
lda #-1 |
sta TCB_NxtTo |
sta TCB_PrvTo |
stz QNdx2 ; insert at priority 2 |
; manually build the IO focus list |
lda #JCBs |
sta IOFocusNdx ; Job #0 (Monitor) has the focus |
stz JCB_iof_next,r1 |
stz JCB_iof_prev,r1 |
lda #1 |
sta IOFocusTbl ; set the job #0 request bit |
|
lda #PRI_NORMAL |
sta TCB_Priority |
stz TCB_Timeout |
lda #TS_RUNNING|TS_READY |
sta TCB_Status |
stz TCB_CursorRow |
stz TCB_CursorCol |
stz TCB_ABS8Save |
ldx #BIOS_STACKS+0x03FF ; setup stack pointer top of memory |
stx TCB_SPSave |
ldx #$1FF |
stx TCB_SP8Save |
rts |
|
;------------------------------------------------------------------------------ |
;------------------------------------------------------------------------------ |
message "startIdleTask" |
StartIdleTask: |
lda #4 |
ldx #0 |
ldy #IdleTask |
jsr StartTask |
rts |
|
;------------------------------------------------------------------------------ |
; IdleTask |
; |
; IdleTask is a low priority task that is always running. It runs when there |
; is nothing else to run. |
; This task check for tasks that are stuck in infinite loops and kills them. |
;------------------------------------------------------------------------------ |
IdleTask: |
stz TestTask |
it2: |
inc TEXTSCR+111 ; increment IDLE active flag |
ldx TestTask |
and r2,r2,#$FF |
beq it1 |
lda TCB_Status,x |
cmp #TS_SLEEP |
bne it1 |
txa |
int #4 ; KillTask function |
db 3 |
; jsr KillTask |
it1: |
inc TestTask |
cli ; enable interrupts |
wai ; wait for one to happen |
bra it2 |
|
;------------------------------------------------------------------------------ |
; Parameters: |
; r1 = job name |
; r2 = start address |
;------------------------------------------------------------------------------ |
; |
StartJob: |
pha |
|
; Get a free JCB |
spl freejcb_sema + 1 |
ld r6,FreeJCB |
beq sjob1 |
ld r7,JCB_Next,r6 |
st r7,FreeJCB |
stz freejcb_sema + 1 |
|
lea r7,JCB_Name,r6 ; r7 = address of name field |
asl r7,r7,#2 ; convert word to byte address |
ld r9,r7 ; save off buffer address |
ld r8,#0 ; r8 = count of characters (0 to 31) |
sjob3: |
lb r5,0,r1 ; get a character |
beq sjob2 ; end of string ? |
sb r5,1,r7 |
ina |
inc r7 |
inc r8 |
cmp r8,#31 ; max number of chars ? |
blo sjob3 |
sjob2: |
sb r8,0,r9 ; save name length |
|
sjob1: |
stz freejcb_sema + 1 |
pla |
rts |
|
;------------------------------------------------------------------------------ |
; StartTask |
; |
; Startup a task. The task is automatically allocated a 1kW stack from the BIOS |
; stacks area. The scheduler is invoked after the task is added to the ready |
; list. |
; |
; Parameters: |
; r1 = task priority |
; r2 = start flags |
; r3 = start address |
; r4 = start parameter |
; r5 = job handle |
;------------------------------------------------------------------------------ |
message "StartTask" |
StartTask: |
pusha |
ld r6,r1 ; r6 = task priority |
ld r8,r2 ; r8 = flag register value on startup |
|
; get a free TCB |
; |
spl freetcb_sema+1 |
lda FreeTCB ; get free tcb list pointer |
bmi stask1 |
tax |
lda TCB_NxtTCB,x |
sta FreeTCB ; update the FreeTCB list pointer |
stz freetcb_sema+1 |
lda #81 |
sta LEDS |
txa ; acc = TCB index (task number) |
sta TCB_mbx,x |
|
; setup the stack for the task |
; Zap the stack memory. |
ld r7,r2 |
asl r2,r2,#10 ; 1kW stack per task |
add r2,r2,#BIOS_STACKS ;+0x3ff ; add in stack base |
pha |
phx |
phy |
txy ; y = target address |
ldx #ExitTask ; x = fill value |
lda #$3FF ; acc = # words to fill -1 |
stos |
ply |
plx |
pla |
|
add r2,r2,#$3FF ; Move pointer to top of stack |
stx TCB_StackTop,r7 |
sub r2,r2,#128 |
tsr sp,r9 ; save off current stack pointer |
spl tcb_sema + 1 |
txs |
st r6,TCB_Priority,r7 |
stz TCB_Status,r7 |
stz TCB_Timeout,r7 |
st r5,TCB_hJCB,r7 ; save job handle |
; setup virtual video for the task |
; stz TCB_CursorRow,r7 |
; stz TCB_CursorCol,r7 |
stz TCB_mmu_map,r7 ; use mmu map |
; jsr AllocateMemPage |
pha |
lda #82 |
sta LEDS |
lda #-1 |
sta TCB_MbxList,r7 |
lda BASIC_SESSION |
cmp #1 |
bls stask3 |
asl r1,r1,#14 |
add r1,r1,#$430_0000 |
sta TCB_ABS8Save,r7 |
add r1,r1,#$1FF |
sta TCB_SP8Save,r7 |
bra stask4 |
stask3: |
lda #$1FF |
sta TCB_SP8Save,r7 |
stz TCB_ABS8Save,r7 |
stask4: |
lda #83 |
sta LEDS |
pla |
; tay |
|
; setup the initial stack image for the task |
; Cause a return to the ExitTask routine when the task does a |
; final rts. |
; fake an IRQ call by stacking the return address and processor |
; flags on the stack |
ldx #ExitTask ; save the address of the task exit routine |
phx |
phy ; save start address on stack |
push r8 ; save processor status reg on stack |
|
; now fake pushing the register set onto the stack. Registers start up |
; in an undefined state. |
; sub sp,#15 ; 15 registers |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
push r4 |
tsx |
stx TCB_SPSave,r7 |
; now restore the current stack pointer |
trs r9,sp |
|
; Insert the task into the ready list |
ld r4,#84 |
st r4,LEDS |
jsr AddTaskToReadyList |
lda #1 |
sta tcb_sema |
int #2 ; invoke the scheduler |
; GoReschedule ; invoke the scheduler |
stask2: |
popa |
rts |
stask1: |
stz freetcb_sema+1 |
jsr kernel_panic |
db "No more task control blocks available.",0 |
bra stask2 |
|
;------------------------------------------------------------------------------ |
; ExitTask |
; |
; This routine is called when the task exits with an rts instruction. OR |
; it may be invoked with a JMP ExitTask. In either case the task must be |
; running so it can't be on the timeout list. The scheduler is invoked |
; after the task is removed from the ready list. |
;------------------------------------------------------------------------------ |
message "ExitTask" |
ExitTask: |
; release any aquired resources |
; - mailboxes |
; - messages |
hoff |
spl tcb_sema + 1 |
lda RunningTCB |
cmp #MAX_TASKNO |
bhi xtsk1 |
jsr RemoveTaskFromReadyList |
jsr RemoveFromTimeoutList |
stz TCB_Status,r1 ; set task status to TS_NONE |
jsr ReleaseIOFocus |
; lda TCB_ABS8Save,x |
; jsr FreeMemPage |
; Free up all the mailboxes associated with the task. |
xtsk7: |
pha |
lda TCB_MbxList,r1 |
bmi xtsk6 |
jsr FreeMbx |
pla |
bra xtsk7 |
xtsk6: |
pla |
ldx #86 |
stx LEDS |
spl freetcb_sema+1 |
ldx FreeTCB ; add the task control block to the free list |
stx TCB_NxtTCB,r1 |
sta FreeTCB |
stz freetcb_sema+1 |
xtsk1: |
jmp SelectTaskToRun |
|
;------------------------------------------------------------------------------ |
; r1 = task number |
; r2 = new priority |
;------------------------------------------------------------------------------ |
; |
SetTaskPriority: |
cmp #MAX_TASKNO ; make sure task number is reasonable |
bhi stp1 |
phy |
spl tcb_sema + 1 |
ldy TCB_Status,r1 ; if the task is on the ready list |
bit r3,#TS_READY|TS_RUNNING ; then remove it and re-add it. |
beq stp2 ; Otherwise just go set the priority field |
jsr RemoveTaskFromReadyList |
stx TCB_Priority,r1 |
jsr AddTaskToReadyList |
bra stp3 |
stp2: |
stx TCB_Priority,r1 |
stp3: |
ldy #1 |
sty tcb_sema |
int #2 |
ply |
stp1: |
rts |
|
;------------------------------------------------------------------------------ |
; AddTaskToReadyList |
; |
; The ready list is a group of five ready lists, one for each priority |
; level. Each ready list is organized as a doubly linked list to allow fast |
; insertions and removals. The list is organized as a ring (or bubble) with |
; the last entry pointing back to the first. This allows a fast task switch |
; to the next task. Which task is at the head of the list is maintained |
; in the variable QNdx for the priority level. |
; |
; Registers Affected: none |
; Parameters: |
; r1 = task number |
; Returns: |
; none |
;------------------------------------------------------------------------------ |
; |
message "AddTaskToReadyList" |
AddTaskToReadyList: |
phx |
phy |
ldx #TS_READY |
stx TCB_Status,r1 |
ldx #-1 |
stx TCB_NxtRdy,r1 |
stx TCB_PrvRdy,r1 |
ldy TCB_Priority,r1 |
cpy #5 |
blo arl1 |
ldy #PRI_LOWEST |
arl1: |
ldx QNdx0,y |
bmi arl5 |
ldy TCB_PrvRdy,x |
sta TCB_NxtRdy,y |
sty TCB_PrvRdy,r1 |
sta TCB_PrvRdy,x |
stx TCB_NxtRdy,r1 |
ply |
plx |
rts |
|
; Here the ready list was empty, so add at head |
arl5: |
sta QNdx0,y |
sta TCB_NxtRdy,r1 |
sta TCB_PrvRdy,r1 |
ply |
plx |
rts |
|
;------------------------------------------------------------------------------ |
; RemoveTaskFromReadyList |
; |
; This subroutine removes a task from the ready list. |
; |
; Registers Affected: none |
; Parameters: |
; r1 = task number |
; Returns: |
; r1 = task number |
;------------------------------------------------------------------------------ |
|
message "RemoveTaskFromReadyList" |
RemoveTaskFromReadyList: |
phx |
phy |
push r4 |
push r5 |
|
ldy TCB_Status,r1 ; is the task on the ready list ? |
bit r3,#TS_READY|TS_RUNNING |
beq rfr2 |
and r3,r3,#~(TS_READY|TS_RUNNING) |
sty TCB_Status,r1 ; task status no longer running or ready |
ld r4,TCB_NxtRdy,r1 ; Get previous and next fields. |
ld r5,TCB_PrvRdy,r1 |
st r4,TCB_NxtRdy,r5 |
st r5,TCB_PrvRdy,r4 |
ldy TCB_Priority,r1 |
cmp r1,QNdx0,y ; Are we removing the QNdx task ? |
bne rfr2 |
st r4,QNdx0,y |
; Now we test for the case where the task being removed was the only one |
; on the ready list of that priority level. We can tell because the |
; NxtRdy would point to the task itself. |
cmp r4,r1 |
bne rfr2 |
ldx #-1 ; Make QNdx negative |
stx QNdx0,y |
stx TCB_NxtRdy,r1 |
stx TCB_PrvRdy,r1 |
rfr2: |
pop r5 |
pop r4 |
ply |
plx |
rts |
|
;------------------------------------------------------------------------------ |
; AddToTimeoutList |
; AddToTimeoutList adds a task to the timeout list. The task is placed in the |
; list depending on it's timeout value. |
; |
; Registers Affected: none |
; Parameters: |
; r1 = task |
; r2 = timeout value |
;------------------------------------------------------------------------------ |
message "AddToTimeoutList" |
AddToTimeoutList: |
phx |
push r4 |
push r5 |
|
ld r5,#-1 |
st r5,TCB_NxtTo,r1 ; these fields should already be -1 |
st r5,TCB_PrvTo,r1 |
ld r4,TimeoutList ; are there any tasks on the timeout list ? |
bmi attl_add_at_head ; If not, update head of list |
attl_check_next: |
sub r2,r2,TCB_Timeout,r4 ; is this timeout > next |
bmi attl_insert_before |
ld r5,r4 |
ld r4,TCB_NxtTo,r4 |
bpl attl_check_next |
|
; Here we scanned until the end of the timeout list and didn't find a |
; timeout of a greater value. So we add the task to the end of the list. |
attl_add_at_end: |
st r4,TCB_NxtTo,r1 ; r4 is = -1 |
st r1,TCB_NxtTo,r5 |
st r5,TCB_PrvTo,r1 |
stx TCB_Timeout,r1 |
bra attl_exit |
|
attl_insert_before: |
cmp r5,#0 |
bmi attl_insert_before_head |
st r4,TCB_NxtTo,r1 ; next on list goes after this task |
st r5,TCB_PrvTo,r1 ; set previous link |
st r1,TCB_NxtTo,r5 |
st r1,TCB_PrvTo,r4 |
bra attl_adjust_timeout |
|
; Here there is no previous entry in the timeout list |
; Add at start |
attl_insert_before_head: |
sta TCB_PrvTo,r4 |
st r5,TCB_PrvTo,r1 ; r5 is = -1 |
st r4,TCB_NxtTo,r1 |
sta TimeoutList ; update the head pointer |
attl_adjust_timeout: |
add r2,r2,TCB_Timeout,r4 ; get back timeout |
stx TCB_Timeout,r1 |
ld r5,TCB_Timeout,r4 ; adjust the timeout of the next task |
sub r5,r5,r2 |
st r5,TCB_Timeout,r4 |
bra attl_exit |
|
; Here there were no tasks on the timeout list, so we add at the |
; head of the list. |
attl_add_at_head: |
sta TimeoutList ; set the head of the timeout list |
stx TCB_Timeout,r1 |
ldx #-1 ; flag no more entries in timeout list |
stx TCB_NxtTo,r1 ; no next entries |
stx TCB_PrvTo,r1 ; and no prev entries |
attl_exit: |
ldx TCB_Status,r1 ; set the task's status as timing out |
or r2,r2,#TS_TIMEOUT |
stx TCB_Status,r1 |
pop r5 |
pop r4 |
plx |
rts |
|
;------------------------------------------------------------------------------ |
; RemoveFromTimeoutList |
; |
; This routine is called when a task is killed. The task may need to be |
; removed from the middle of the timeout list. |
; |
; On entry: the timeout list semaphore must be already set. |
; Registers Affected: none |
; Parameters: |
; r1 = task number |
;------------------------------------------------------------------------------ |
message "RemoveFromTimeoutList" |
RemoveFromTimeoutList: |
cmp #MAX_TASKNO |
bhi rftl_not_on_list2 |
phx |
push r4 |
push r5 |
|
ld r4,TCB_Status,r1 ; Is the task even on the timeout list ? |
bit r4,#TS_TIMEOUT |
beq rftl_not_on_list |
cmp TimeoutList ; Are we removing the head of the list ? |
beq rftl_remove_from_head |
ld r4,TCB_PrvTo,r1 ; adjust the links of the next and previous |
bmi rftl_empty_list ; no previous link - list corrupt? |
ld r5,TCB_NxtTo,r1 ; tasks on the list to point around the task |
st r5,TCB_NxtTo,r4 |
bmi rftl_empty_list |
st r4,TCB_PrvTo,r5 |
ldx TCB_Timeout,r1 ; update the timeout of the next on list |
add r2,r2,TCB_Timeout,r5 ; with any remaining timeout in the task |
stx TCB_Timeout,r5 ; removed from the list |
bra rftl_empty_list |
|
; Update the head of the list. |
rftl_remove_from_head: |
ld r5,TCB_NxtTo,r1 |
st r5,TimeoutList ; store next field into list head |
bmi rftl_empty_list |
ld r4,TCB_Timeout,r1 ; add any remaining timeout to the timeout |
add r4,r4,TCB_Timeout,r5 ; of the next task on the list. |
st r4,TCB_Timeout,r5 |
ld r4,#-1 ; there is no previous item to the head |
sta TCB_PrvTo,r5 |
|
; Here there is no previous or next items in the list, so the list |
; will be empty once this task is removed from it. |
rftl_empty_list: |
tax |
lda #0 ; clear timeout status (bit #0) |
bmc TCB_Status,x |
dea ; acc=-1; make sure the next and prev fields indicate |
sta TCB_NxtTo,x ; the task is not on a list. |
sta TCB_PrvTo,x |
txa |
rftl_not_on_list: |
pop r5 |
pop r4 |
plx |
rftl_not_on_list2: |
rts |
|
;------------------------------------------------------------------------------ |
; PopTimeoutList |
; |
; This subroutine is called from within the timer ISR when the task's |
; timeout expires. It's always the head of the list that's being removed in |
; the timer ISR so the removal from the timeout list is optimized. We know |
; the timeout expired, so the amount of time to add to the next task is zero. |
; This routine is written as a macro since it's only called from one place. |
; This routine is inlined. Implementing it as a macro increases performance. |
; |
; Registers Affected: acc, x, y, flags |
; Parameters: |
; x: head of timeout list |
; Returns: |
; r1 = task id of task popped from timeout list |
;------------------------------------------------------------------------------ |
; |
message "PopTimeoutList" |
macro PopTimeoutList |
ldy #-1 |
lda TCB_NxtTo,x |
sta TimeoutList ; store next field into list head |
bmi ptl1 |
sty TCB_PrvTo,r1 ; previous link = -1 |
ptl1: |
lda #0 ; clear timeout status |
bmc TCB_Status,x |
sty TCB_NxtTo,x ; make sure the next and prev fields indicate |
sty TCB_PrvTo,x ; the task is not on a list. |
txa |
endm |
|
;------------------------------------------------------------------------------ |
; Sleep |
; |
; Put the currently running task to sleep for a specified time. |
; |
; Registers Affected: none |
; Parameters: |
; r1 = time duration in centi-seconds (1/100 second). |
; Returns: none |
;------------------------------------------------------------------------------ |
; |
Sleep: |
pha |
phx |
tax |
spl tcb_sema + 1 |
lda RunningTCB |
jsr RemoveTaskFromReadyList |
jsr AddToTimeoutList ; The scheduler will be returning to this |
lda #1 |
sta tcb_sema |
int #2 ; task eventually, once the timeout expires, |
plx |
pla |
rts |
|
;------------------------------------------------------------------------------ |
; Short delay routine. |
; This routine works by reading the tick register. When a subsequent read |
; of the tick register exceeds the value of the original read by at least |
; the value passed as a parameter, then this routine returns. |
; The tick register increments at the clock rate (eg 25 MHz). |
;------------------------------------------------------------------------------ |
; |
short_delay: |
phx |
phy |
tsr tick,r2 |
usec1: |
tsr tick,r3 |
sub r3,r3,r2 |
cmp r1,r3 |
blo usec1 |
ply |
plx |
rts |
|
;------------------------------------------------------------------------------ |
; KillTask |
; |
; "Kills" a task, removing it from all system lists. If the task has the |
; IO focus, the IO focus is switched. Task #0 is immortal and cannot be |
; killed. |
; |
; Registers Affected: none |
; Parameters: |
; r1 = task number |
;------------------------------------------------------------------------------ |
; |
KillTask: |
phx |
cmp #1 ; BIOS task and IDLE task are immortal |
bls kt1 |
cmp #MAX_TASKNO |
bhi kt1 |
tax |
lda TCB_hJCB,r1 |
jsr ForceReleaseIOFocus |
txa |
spl tcb_sema + 1 |
jsr RemoveTaskFromReadyList |
jsr RemoveFromTimeoutList |
stz TCB_Status,r1 ; set task status to TS_NONE |
|
; Free up all the mailboxes associated with the task. |
kt7: |
pha |
tax |
lda TCB_MbxList,r1 |
bmi kt6 |
jsr FreeMbx2 |
pla |
bra kt7 |
kt6: |
lda #1 |
sta tcb_sema |
pla |
|
spl freetcb_sema + 1 |
ldx FreeTCB ; add the task control block to the free list |
stx TCB_NxtTCB,r1 |
sta FreeTCB |
stz freetcb_sema + 1 |
cmp RunningTCB ; keep running the current task as long as |
bne kt1 ; the task didn't kill itself. |
int #2 ; invoke scheduler to reschedule tasks |
kt1: |
plx |
rts |
|
;------------------------------------------------------------------------------ |
; Allocate a mailbox |
; Parameters: |
; r1 = pointer to place to store handle |
; Returns: |
; r1 = E_Ok means mailbox allocated properly |
; r1 = E_Arg means a NULL pointer was passed in r1 |
; r1 = E_NoMoreMbx means no more mailboxes were available |
; zf is set if everything is ok, otherwise zf is clear |
;------------------------------------------------------------------------------ |
; |
message "AllocMbx" |
AllocMbx: |
cmp #0 |
beq ambx_bad_ptr |
phx |
phy |
push r4 |
ld r4,r1 ; r4 = pointer to returned handle |
spl freembx_sema + 1 |
lda FreeMbxHandle ; Get mailbox off of free mailbox list |
sta (r4) ; store off the mailbox number |
bmi ambx_no_mbxs |
ldx MBX_LINK,r1 ; and update the head of the list |
stx FreeMbxHandle |
dec nMailbox ; decrement number of available mailboxes |
stz freembx_sema + 1 |
spl tcb_sema + 1 |
ldy RunningTCB ; Add the mailbox to the list of mailboxes |
ldx TCB_MbxList,y ; managed by the task. |
stx MBX_LINK,r1 |
sta TCB_MbxList,y |
tax |
ldy RunningTCB ; set the mailbox owner |
; bmi RunningTCBErr |
lda TCB_hJCB,y |
stz tcb_sema + 1 |
|
spl mbx_sema + 1 |
sta MBX_OWNER,x |
lda #-1 ; initialize the head and tail of the queues |
sta MBX_TQ_HEAD,x |
sta MBX_TQ_TAIL,x |
sta MBX_MQ_HEAD,x |
sta MBX_MQ_TAIL,x |
stz MBX_TQ_COUNT,x ; initialize counts to zero |
stz MBX_MQ_COUNT,x |
stz MBX_MQ_MISSED,x |
lda #8 ; set the max queue size |
sta MBX_MQ_SIZE,x ; and |
lda #MQS_NEWEST ; queueing strategy |
sta MBX_MQ_STRATEGY,x |
stz mbx_sema + 1 |
pop r4 |
ply |
plx |
lda #E_Ok |
rts |
ambx_bad_ptr: |
lda #E_Arg |
rts |
ambx_no_mbxs: |
stz freembx_sema + 1 |
pop r4 |
ply |
plx |
lda #E_NoMoreMbx |
rts |
|
;------------------------------------------------------------------------------ |
; Free up a mailbox. |
; This function frees a mailbox from the currently running task. It may be |
; called by ExitTask(). |
; |
; Parameters: |
; r1 = mailbox handle |
;------------------------------------------------------------------------------ |
; |
FreeMbx: |
phx |
ldx RunningTCB |
jsr FreeMbx2 |
plx |
rts |
|
;------------------------------------------------------------------------------ |
; Free up a mailbox. |
; This function dequeues any messages from the mailbox and adds the messages |
; back to the free message pool. The function also dequeues any threads from |
; the mailbox. |
; Called from KillTask() and FreeMbx(). |
; |
; Parameters: |
; r1 = mailbox handle |
; r2 = task handle |
; Returns: |
; r1 = E_Ok if everything ok |
; r1 = E_Arg if a bad handle is passed |
;------------------------------------------------------------------------------ |
; |
FreeMbx2: |
cmp #NR_MBX ; check mailbox handle parameter |
bhs fmbx1 |
cpx #MAX_TASKNO |
bhi fmbx1 |
phx |
phy |
spl mbx_sema + 1 |
|
; Dequeue messages from mailbox and add them back to the free message list. |
fmbx5: |
pha |
jsr DequeueMsgFromMbx |
bmi fmbx3 |
spl freemsg_sema + 1 |
phx |
ldx FreeMsg |
stx MSG_LINK,r1 |
sta FreeMsg |
stz freemsg_sema + 1 |
plx |
pla |
bra fmbx5 |
fmbx3: |
pla |
|
; Dequeue threads from mailbox. |
fmbx6: |
pha |
jsr DequeueThreadFromMbx2 |
bmi fmbx7 |
pla |
bra fmbx6 |
fmbx7: |
pla |
|
; Remove mailbox from TCB list |
ldy TCB_MbxList,x |
phx |
ldx #-1 |
fmbx10: |
cmp r1,r3 |
beq fmbx9 |
tyx |
ldy MBX_LINK,y |
bpl fmbx10 |
; ?The mailbox was not in the list managed by the task. |
plx |
bra fmbx2 |
fmbx9: |
cmp r2,r0 |
bmi fmbx11 |
ldy MBX_LINK,y |
sty MBX_LINK,x |
plx |
bra fmbx12 |
fmbx11: |
; No prior mailbox in list, update head |
ldy MBX_LINK,r1 |
plx |
sty TCB_MbxList,x |
|
fmbx12: |
; Add mailbox back to mailbox pool |
spl freembx_sema + 1 |
ldx FreeMbxHandle |
stx MBX_LINK,r1 |
sta FreeMbxHandle |
stz freembx_sema + 1 |
fmbx2: |
stz mbx_sema + 1 |
ply |
plx |
lda #E_Ok |
rts |
fmbx1: |
lda #E_Arg |
rts |
|
;------------------------------------------------------------------------------ |
; Queue a message at a mailbox. |
; On entry the mailbox semaphore is already activated. |
; |
; Parameters: |
; r1 = message |
; r2 = mailbox |
;------------------------------------------------------------------------------ |
message "QueueMsgAtMbx" |
QueueMsgAtMbx: |
cmp #0 |
beq qmam_bad_msg |
pha |
phx |
phy |
push r4 |
ld r4,MBX_MQ_STRATEGY,x |
cmp r4,#MQS_UNLIMITED |
beq qmam_unlimited |
cmp r4,#MQS_NEWEST |
beq qmam_newest |
cmp r4,#MQS_OLDEST |
beq qmam_oldest |
jsr kernel_panic |
db "Illegal message queue strategy",0 |
bra qmam8 |
; Here we assumed "unlimited" message storage. Just add the new message at |
; the tail of the queue. |
qmam_unlimited: |
ldy MBX_MQ_TAIL,x |
bmi qmam_add_at_head |
sta MSG_LINK,y |
bra qmam2 |
qmam_add_at_head: |
sta MBX_MQ_HEAD,x |
qmam2: |
sta MBX_MQ_TAIL,x |
qmam6: |
inc MBX_MQ_COUNT,x ; increase the queued message count |
ldx #-1 |
stx MSG_LINK,r1 |
pop r4 |
ply |
plx |
pla |
qmam_bad_msg: |
rts |
; Here we are queueing a limited number of messages. As new messages are |
; added at the tail of the queue, messages drop off the head of the queue. |
qmam_newest: |
ldy MBX_MQ_TAIL,x |
bmi qmam3 |
sta MSG_LINK,y |
bra qmam4 |
qmam3: |
sta MBX_MQ_HEAD,x |
qmam4: |
sta MBX_MQ_TAIL,x |
ldy MBX_MQ_COUNT,x |
iny |
cmp r3,MBX_MQ_SIZE,x |
bls qmam6 |
ldy #-1 |
sty MSG_LINK,r1 |
; Remove the oldest message which is the one at the head of the mailbox queue. |
; Add the message back to the pool of free messages. |
lda MBX_MQ_HEAD,x |
ldy MSG_LINK,r1 ; move next in queue |
sty MBX_MQ_HEAD,x ; to head of list |
qmam8: |
inc MBX_MQ_MISSED,x |
qmam1: |
spl freemsg_sema + 1 |
ldy FreeMsg ; put old message back into free message list |
sty MSG_LINK,r1 |
sta FreeMsg |
inc nMsgBlk |
stz freemsg_sema + 1 |
;GoReschedule |
pop r4 |
ply |
plx |
pla |
rts |
; Here we are buffering the oldest messages. So if there are too many messages |
; in the queue already, then the queue doesn't change and the new message is |
; lost. |
qmam_oldest: |
ldy MBX_MQ_COUNT,x ; Check if the queue is full |
cmp r3,MBX_MQ_SIZE,x |
bhs qmam8 ; If the queue is full, then lose the current message |
bra qmam_unlimited ; Otherwise add message to queue |
|
;------------------------------------------------------------------------------ |
; Dequeue a message from a mailbox. |
; |
; Returns |
; r1 = message number |
; nf set if there is no message, otherwise clear |
;------------------------------------------------------------------------------ |
message "DequeueMsgFromMbx" |
DequeueMsgFromMbx: |
phx |
phy |
tax ; x = mailbox index |
lda MBX_MQ_COUNT,x ; are there any messages available ? |
beq dmfm3 |
dea |
sta MBX_MQ_COUNT,x ; update the message count |
lda MBX_MQ_HEAD,x ; Get the head of the list, this should not be -1 |
bmi dmfm3 ; since the message count > 0 |
ldy MSG_LINK,r1 ; get the link to the next message |
sty MBX_MQ_HEAD,x ; update the head of the list |
bpl dmfm2 ; if there was no more messages then update the |
sty MBX_MQ_TAIL,x ; tail of the list as well. |
dmfm2: |
sta MSG_LINK,r1 ; point the link to the messahe itself to indicate it's dequeued |
dmfm1: |
ply |
plx |
cmp #0 |
rts |
dmfm3: |
ply |
plx |
lda #-1 |
rts |
|
;------------------------------------------------------------------------------ |
; Parameters: |
; r1 = mailbox handle |
; Returns: |
; r1 = E_arg means pointer is invalid |
; r1 = E_NoThread means no thread was queued at the mailbox |
; r2 = thead handle |
;------------------------------------------------------------------------------ |
message "DequeueThreadFromNbx" |
DequeueThreadFromMbx: |
push r4 |
ld r4,MBX_TQ_HEAD,r1 |
bpl dtfm2 |
pop r4 |
ldx #-1 |
lda #E_NoThread |
rts |
dtfm2: |
push r5 |
dec MBX_TQ_COUNT,r1 |
ld r2,r4 |
ld r4,TCB_mbq_next,r4 |
st r4,MBX_TQ_HEAD,r1 |
bmi dtfm3 |
ld r5,#-1 |
st r5,TCB_mbq_prev,r4 |
bra dtfm4 |
dtfm3: |
ld r5,#-1 |
st r5,MBX_TQ_TAIL,r1 |
dtfm4: |
; stz MBX_SEMA+1 |
ld r5,r2 |
lda TCB_Status,r5 |
bit #TS_TIMEOUT |
beq dtfm5 |
ld r1,r5 |
jsr RemoveFromTimeoutList |
dtfm5: |
ld r4,#-1 |
st r4,TCB_mbq_next,r5 |
st r4,TCB_mbq_prev,r5 |
stz TCB_hWaitMbx,r5 |
stz TCB_Status,r5 ; set task status = TS_NONE |
pop r5 |
pop r4 |
lda #E_Ok |
rts |
|
;------------------------------------------------------------------------------ |
; This function is called from FreeMbx(). It dequeues threads from the |
; mailbox without removing the thread from the timeout list. The thread will |
; then timeout waiting for a message that can never be delivered. |
; |
; Parameters: |
; r1 = mailbox handle |
; Returns: |
; r1 = E_arg means pointer is invalid |
; r1 = E_NoThread means no thread was queued at the mailbox |
; r2 = thead handle |
;------------------------------------------------------------------------------ |
message "DequeueThreadFromNbx2" |
DequeueThreadFromMbx2: |
push r4 |
ld r4,MBX_TQ_HEAD,r1 |
bpl dtfm2a |
pop r4 |
ldx #-1 |
lda #E_NoThread |
rts |
dtfm2a: |
push r5 |
dec MBX_TQ_COUNT,r1 |
ld r2,r4 |
ld r4,TCB_mbq_next,r4 |
st r4,MBX_TQ_HEAD,r1 |
bmi dtfm3a |
ld r5,#-1 |
st r5,TCB_mbq_prev,r4 |
bra dtfm4a |
dtfm3a: |
ld r5,#-1 |
st r5,MBX_TQ_TAIL,r1 |
dtfm4a: |
ld r4,#-1 |
st r4,TCB_mbq_next,x |
st r4,TCB_mbq_prev,x |
stz TCB_hWaitMbx,x |
sei |
lda #TS_WAITMSG_BIT |
bmc TCB_Status,x |
cli |
pop r5 |
pop r4 |
lda #E_Ok |
rts |
|
;------------------------------------------------------------------------------ |
; PostMsg and SendMsg are the same operation except that PostMsg doesn't |
; invoke rescheduling while SendMsg does. So they both call the same |
; SendMsgPrim primitive routine. This two wrapper functions for convenience. |
;------------------------------------------------------------------------------ |
; |
PostMsg: |
push r4 |
ld r4,#0 ; Don't invoke scheduler |
jsr SendMsgPrim |
pop r4 |
rts |
|
SendMsg: |
push r4 |
ld r4,#1 ; Do invoke scheduler |
jsr SendMsgPrim |
pop r4 |
rts |
|
;------------------------------------------------------------------------------ |
; SendMsgPrim |
; Send a message to a mailbox |
; |
; Parameters |
; r1 = handle to mailbox |
; r2 = message D1 |
; r3 = message D2 |
; r4 = scheduler flag 1=invoke,0=don't invoke |
; |
; Returns |
; r1=E_Ok everything is ok |
; r1=E_BadMbx for a bad mailbox number |
; r1=E_NotAlloc for a mailbox that isn't allocated |
; r1=E_NoMsg if there are no more message blocks available |
; zf is set if everything is okay, otherwise zf is clear |
;------------------------------------------------------------------------------ |
message "SendMsgPrim" |
SendMsgPrim: |
cmp #NR_MBX ; check the mailbox number to make sure |
bhs smsg1 ; that it's sensible |
push r5 |
push r6 |
push r7 |
|
spl mbx_sema + 1 |
ld r7,MBX_OWNER,r1 |
bmi smsg2 ; error: no owner |
pha |
phx |
jsr DequeueThreadFromMbx ; r1=mbx |
ld r6,r2 ; r6 = thread |
plx |
pla |
cmp r6,#0 |
bpl smsg3 |
; Here there was no thread waiting at the mailbox, so a message needs to |
; be allocated |
smp2: |
spl freemsg_sema + 1 |
ld r7,FreeMsg |
bmi smsg4 ; no more messages available |
ld r5,MSG_LINK,r7 |
st r5,FreeMsg |
dec nMsgBlk ; decrement the number of available messages |
stz freemsg_sema + 1 |
stx MSG_D1,r7 |
sty MSG_D2,r7 |
pha |
phx |
tax ; x = mailbox |
ld r1,r7 ; acc = message |
jsr QueueMsgAtMbx |
plx |
pla |
cmp r6,#0 ; check if there is a thread waiting for a message |
bmi smsg5 |
smsg3: |
stx TCB_MSG_D1,r6 |
sty TCB_MSG_D2,r6 |
smsg7: |
spl tcb_sema + 1 |
ld r5,TCB_Status,r6 |
bit r5,#TS_TIMEOUT |
beq smsg8 |
ld r1,r6 |
jsr RemoveFromTimeoutList |
smsg8: |
lda #TS_WAITMSG_BIT |
bmc TCB_Status,r6 |
lda #1 |
sta tcb_sema |
ld r1,r6 |
spl tcb_sema + 1 |
jsr AddTaskToReadyList |
stz tcb_sema + 1 |
cmp r4,#0 |
beq smsg5 |
stz mbx_sema + 1 |
int #2 |
;GoReschedule |
bra smsg9 |
smsg5: |
stz mbx_sema + 1 |
smsg9: |
pop r7 |
pop r6 |
pop r5 |
lda #E_Ok |
rts |
smsg1: |
lda #E_BadMbx |
rts |
smsg2: |
stz mbx_sema + 1 |
pop r7 |
pop r6 |
pop r5 |
lda #E_NotAlloc |
rts |
smsg4: |
stz freemsg_sema + 1 |
stz mbx_sema + 1 |
pop r7 |
pop r6 |
pop r5 |
lda #E_NoMsg |
rts |
|
;------------------------------------------------------------------------------ |
; WaitMsg |
; Wait at a mailbox for a message to arrive. This subroutine will block the |
; task until a message is available or the task times out on the timeout |
; list. |
; |
; Parameters |
; r1=mailbox |
; r2=timeout |
; Returns: |
; r1=E_Ok if everything is ok |
; r1=E_BadMbx for a bad mailbox number |
; r1=E_NotAlloc for a mailbox that isn't allocated |
; r2=message D1 |
; r3=message D2 |
;------------------------------------------------------------------------------ |
message "WaitMsg" |
WaitMsg: |
cmp #NR_MBX ; check the mailbox number to make sure |
bhs wmsg1 ; that it's sensible |
push r4 |
push r5 |
push r6 |
push r7 |
ld r6,r1 |
wmsg11: |
spl mbx_sema + 1 |
ld r5,MBX_OWNER,r1 |
cmp r5,#MAX_TASKNO |
bhi wmsg2 ; error: no owner |
jsr DequeueMsgFromMbx |
; cmp #0 |
bpl wmsg3 |
|
; Here there was no message available, remove the task from |
; the ready list, and optionally add it to the timeout list. |
; Queue the task at the mailbox. |
wmsg12: |
spl tcb_sema + 1 |
lda RunningTCB ; remove the task from the ready list |
jsr RemoveTaskFromReadyList |
stz tcb_sema + 1 |
wmsg13: |
spl tcb_sema + 1 |
ld r7,TCB_Status,r1 |
or r7,r7,#TS_WAITMSG ; set task status to waiting |
st r7,TCB_Status,r1 |
st r6,TCB_hWaitMbx,r1 ; set which mailbox is waited for |
ld r7,#-1 |
st r7,TCB_mbq_next,r1 ; adding at tail, so there is no next |
ld r7,MBX_TQ_HEAD,r6 ; is there a task que setup at the mailbox ? |
bmi wmsg6 |
ld r7,MBX_TQ_TAIL,r6 |
st r7,TCB_mbq_prev,r1 |
sta TCB_mbq_next,r7 |
sta MBX_TQ_TAIL,r6 |
inc MBX_TQ_COUNT,r6 ; increment number of tasks queued |
wmsg7: |
stz tcb_sema + 1 |
stz mbx_sema + 1 |
cmp r2,#0 ; check for a timeout |
beq wmsg10 |
wmsg14: |
spl tcb_sema + 1 |
jsr AddToTimeoutList |
stz tcb_sema + 1 |
int #2 ; GoReschedule ; invoke the scheduler |
wmsg10: |
; At this point either a message was sent to the task, or the task |
; timed out. If a message is still not available then the task must |
; have timed out. Return a timeout error. |
; Note that SendMsg will directly set the message D1, D2 data |
; without queing a message at the mailbox (if there is a task |
; waiting already). So we cannot just try dequeing a message again. |
ldx TCB_MSG_D1,r1 |
ldy TCB_MSG_D2,r1 |
ld r4,TCB_Status,r1 |
bit r4,#TS_WAITMSG ; Is the task still waiting for a message ? |
beq wmsg8 ; If not, go return OK status |
pop r7 ; Otherwise return timeout error |
pop r6 |
pop r5 |
pop r4 |
lda #E_Timeout |
rts |
|
; Here there were no prior tasks queued at the mailbox |
wmsg6: |
ld r7,#-1 |
st r7,TCB_mbq_prev,r1 ; no previous tasks |
st r7,TCB_mbq_next,r1 |
sta MBX_TQ_HEAD,r6 ; set both head and tail indexes |
sta MBX_TQ_TAIL,r6 |
ld r7,#1 |
st r7,MBX_TQ_COUNT,r6 ; one task queued |
bra wmsg7 ; check for a timeout value |
|
wmsg3: |
stz mbx_sema + 1 |
ldx MSG_D1,r1 |
ldy MSG_D2,r1 |
; Add the newly dequeued message to the free messsage list |
wmsg5: |
spl freemsg_sema + 1 |
ld r7,FreeMsg |
st r7,MSG_LINK,r1 |
sta FreeMsg |
inc nMsgBlk |
stz freemsg_sema + 1 |
wmsg8: |
pop r7 |
pop r6 |
pop r5 |
pop r4 |
lda #E_Ok |
rts |
wmsg1: |
lda #E_BadMbx |
rts |
wmsg2: |
stz mbx_sema + 1 |
pop r7 |
pop r6 |
pop r5 |
pop r4 |
lda #E_NotAlloc |
rts |
|
;------------------------------------------------------------------------------ |
; Check for a message at a mailbox. Does not block. This function is a |
; convenience wrapper for CheckMsg(). |
; |
; Parameters |
; r1=mailbox handle |
; Returns: |
; r1=E_Ok if everything is ok |
; r1=E_NoMsg if no message is available |
; r1=E_BadMbx for a bad mailbox number |
; r1=E_NotAlloc for a mailbox that isn't allocated |
; r2=message D1 |
; r3=message D2 |
;------------------------------------------------------------------------------ |
; |
PeekMsg: |
ld r2,#0 ; don't remove from queue |
jsr CheckMsg |
rts |
|
;------------------------------------------------------------------------------ |
; CheckMsg |
; Check for a message at a mailbox. Does not block. |
; |
; Parameters |
; r1=mailbox handle |
; r2=remove from queue if present |
; Returns: |
; r1=E_Ok if everything is ok |
; r1=E_NoMsg if no message is available |
; r1=E_BadMbx for a bad mailbox number |
; r1=E_NotAlloc for a mailbox that isn't allocated |
; r2=message D1 |
; r3=message D2 |
;------------------------------------------------------------------------------ |
CheckMsg: |
cmp #NR_MBX ; check the mailbox number to make sure |
bhs cmsg1 ; that it's sensible |
push r4 |
push r5 |
|
spl mbx_sema + 1 |
ld r5,MBX_OWNER,r1 |
bmi cmsg2 ; error: no owner |
cpx #0 ; are we to dequeue the message ? |
php |
beq cmsg3 |
jsr DequeueMsgFromMbx |
bra cmsg4 |
cmsg3: |
lda MBX_MQ_HEAD,r1 ; peek the message at the head of the messages queue |
cmsg4: |
cmp #0 |
bmi cmsg5 |
ldx MSG_D1,r1 |
ldy MSG_D2,r1 |
plp ; get back dequeue flag |
beq cmsg8 |
cmsg10: |
spl freemsg_sema + 1 |
ld r5,FreeMsg |
st r5,MSG_LINK,r1 |
sta FreeMsg |
inc nMsgBlk |
stz freemsg_sema + 1 |
cmsg8: |
stz mbx_sema + 1 |
pop r5 |
pop r4 |
lda #E_Ok |
rts |
cmsg1: |
lda #E_BadMbx |
rts |
cmsg2: |
stz mbx_sema + 1 |
pop r5 |
pop r4 |
lda #E_NotAlloc |
rts |
cmsg5: |
stz mbx_sema + 1 |
pop r5 |
pop r4 |
lda #E_NoMsg |
rts |
|
;------------------------------------------------------------------------------ |
; Spinlock interrupt |
; Go reschedule tasks if a spinlock is taking too long. |
;------------------------------------------------------------------------------ |
; |
spinlock_irq: |
cli |
ld r0,tcb_sema + 1 |
beq spi1 |
cld |
pusha |
bra resched1 |
spi1: |
rti |
|
;------------------------------------------------------------------------------ |
; System Call Interrupt |
; |
; The system call is executed using the caller's system stack. |
; |
; Stack Frame |
; 4,sp: return address |
; 3,sp: status register |
; 2,sp: r6 save |
; 1,sp: r7 save |
; 0,sp: r8 save |
;------------------------------------------------------------------------------ |
; |
syscall_int: |
cli |
cld |
push r6 ; save off some working registers |
push r7 |
push r8 |
ld r6,4,sp ; get return address into r6 |
lb r7,0,r6 ; get static call number parameter into r7 |
inc r6 ; update return address |
st r6,4,sp |
; tsr sp,r8 ; save off stack pointer |
; ld r6,RunningTCB ; load the stack pointer with the system call |
; ld r6,TCB_StackTop,r6 ; stack area |
; trs r6,sp |
ld r6,(syscall_vectors>>2),r7 ; load the vector into r6 |
jsr (r6) ; do the system function |
; trs r8,sp ; restore the stack pointer |
pop r8 |
pop r7 |
pop r6 |
rti |
|
;------------------------------------------------------------------------------ |
; Reschedule tasks to run without affecting the timeout list timing. |
;------------------------------------------------------------------------------ |
; |
reschedule: |
cli ; enable interrupts |
cld ; clear extended precision mode |
|
pusha ; save off regs on the stack |
spl tcb_sema + 1 |
resched1: |
ldx RunningTCB |
tsa |
sta TCB_SPSave,x ; save stack pointer in TCB |
tsr sp8,r1 ; and the eight bit mode stack pointer |
sta TCB_SP8Save,x |
tsr abs8,r1 |
sta TCB_ABS8Save,x ; 8 bit emulation base register |
lda #TS_RUNNING_BIT ; clear RUNNING status (bit #3) |
bmc TCB_Status,x |
; lda TCB_StackTop,x ; switch to the system call stack |
; tas |
jmp SelectTaskToRun |
|
|
strStartQue: |
db 0,0,0,1,0,0,0,2,0,1,0,3,0,0,0,4 |
; db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 |
|
;------------------------------------------------------------------------------ |
; 100 Hz interrupt |
; - takes care of "flashing" the cursor |
; - decrements timeouts for tasks on timeout list |
; - switching tasks |
;------------------------------------------------------------------------------ |
; |
MTKTick: |
pha |
lda #3 ; reset the edge sense circuit |
sta PIC_RSTE |
pla |
inc IRQFlag |
; Try and aquire the ready list and tcb. If unsuccessful it means there is |
; a system function in the process of updating the list. All we can do is |
; return to the system function and let it complete whatever it was doing. |
; As if we don't return to the system function we will be deadlocked. |
; The tick will be deferred; however if the system function was busy updating |
; the ready list, in all likelyhood it's about to call the reschedule |
; interrupt. |
ld r0,tcb_sema+1 |
bne p100Hz11 |
inc missed_ticks |
rti |
p100Hz11: |
cli |
cld ; clear extended precision mode |
|
pusha ; save off regs on the stack |
lda #96 |
sta LEDS |
lda UserTick |
beq p100Hz4 |
jsr (r1) |
cli |
p100Hz4: |
|
ldx RunningTCB |
tsa ; save off the stack pointer |
sta TCB_SPSave,x |
tsr sp8,r1 ; and the eight bit mode stack pointer |
sta TCB_SP8Save,x |
tsr abs8,r1 |
sta TCB_ABS8Save,x ; 8 bit emulation base register |
lda #TS_RUNNING_BIT |
bmc TCB_Status,x |
lda #97 |
sta LEDS |
|
; Check the timeout list to see if there are items ready to be removed from |
; the list. Also decrement the timeout of the item at the head of the list. |
p100Hz15: |
ldx TimeoutList |
bmi p100Hz12 ; are there any entries in the timeout list ? |
lda TCB_Timeout,x |
bgt p100Hz14 ; has this entry timed out ? |
PopTimeoutList |
jsr AddTaskToReadyList |
bra p100Hz15 ; go back and see if there's another task to be removed |
; there could be a string of tasks to make ready. |
p100Hz14: |
dea ; decrement the entry's timeout |
sub r1,r1,missed_ticks ; account for any missed ticks |
stz missed_ticks |
sta TCB_Timeout,x |
|
p100Hz12: |
; Falls through into selecting a task to run |
tck3: |
lda #98 |
sta LEDS |
;------------------------------------------------------------------------------ |
; Search the ready queues for a ready task. |
; The search is occasionally started at a lower priority queue in order |
; to prevent starvation of lower priority tasks. This is managed by |
; using a tick count as an index to a string containing the start que. |
;------------------------------------------------------------------------------ |
; |
SelectTaskToRun: |
ld r6,#5 ; number of queues to search |
ldy IRQFlag ; use the IRQFlag as a buffer index |
; lsr r3,r3,#1 ; the LSB is always the same |
and r3,r3,#$0F ; counts from 0 to 15 |
lb r3,strStartQue,y ; get the queue to start search at |
sttr2: |
lda QNdx0,y |
bmi sttr1 |
lda TCB_NxtRdy,r1 ; Advance the queue index |
sta QNdx0,y |
; This is the only place the RunningTCB is set (except for initialization). |
sta RunningTCB |
tax |
lda #TS_RUNNING_BIT |
bms TCB_Status,x ; flag the task as the running task |
lda #99 |
sta LEDS |
lda TCB_ABS8Save,x ; 8 bit emulation base register |
trs r1,abs8 |
lda TCB_SP8Save,x ; get back eight bit stack pointer |
trs r1,sp8 |
ldx TCB_SPSave,x ; get back stack pointer |
txs |
lda #1 |
sta tcb_sema |
ld r0,iof_switch |
beq sttr6 |
ld r0,iof_sema + 1 ; just ignore the request to switch |
beq sttr6 ; I/O focus if the semaphore can't be aquired |
stz iof_switch |
jsr SwitchIOFocus |
stz iof_sema + 1 |
sttr6: |
popa ; restore registers |
rti |
|
; Set index to check the next ready list for a task to run |
sttr1: |
iny |
cpy #5 |
bne sttr5 |
ldy #0 |
sttr5: |
dec r6 |
bne sttr2 |
|
; Here there were no tasks ready |
; This should not be able to happen, so hang the machine (in a lower |
; power mode). |
sttr3: |
ldx #94 |
stx LEDS |
jsr kernel_panic |
db "No tasks in ready queue.",0 |
; Might as well power down the clock and wait for a reset or |
; NMI. In the case of an NMI the kernel is reinitialized without |
; doing the boot reset. |
stp |
jmp MTKInitialize |
|
;------------------------------------------------------------------------------ |
; kernal_panic: |
; All this does right now is display the panic message on the screen. |
; Parameters: |
; inline: string |
;------------------------------------------------------------------------------ |
; |
kernel_panic: |
pla ; pop the return address off the stack |
push r4 ; save off r4 |
ld r4,r1 |
kpan2: |
lb r1,0,r4 ; get a byte from the code space |
add r4,#1 ; increment pointer |
and #$FF ; we want only eight bits |
beq kpan1 ; is it end of string ? |
jsr DisplayChar |
bra kpan2 |
kpan1: ; must update the return address ! |
jsr CRLF |
ld r1,r4 ; get return address into acc |
pop r4 ; restore r4 |
jmp (r1) |
|
include "DeviceDriver.asm" |
|
;------------------------------------------------------------------ |
;------------------------------------------------------------------ |
include "Test816.asm" |
include "pi_calc816.asm" |
|
;------------------------------------------------------------------ |
; Kind of a chicken and egg problem here. If there is something |
; wrong with the processor, then this code likely won't execute. |
; |
|
; put message to screen |
; tests pla,sta,ldy,inc,bne,ora,jmp,jmp(abs) |
|
putmsg |
pla ; pop the return address off the stack |
wdm ; switch to 32 bits |
xce |
cpu RTF65002 |
push r4 ; save off r4 |
or r4,r1,#$FFFF0000 ; set program bank bits; code is at $FFFFxxxx |
pm2 |
add r4,#1 ; increment pointer |
lb r1,0,r4 ; get a byte from the code space |
and #$FF ; we want only eight bits |
beq pm1 ; is it end of string ? |
jsr DisplayChar |
jmp pm2 |
pm1 ; must update the return address ! |
ld r1,r4 ; get return address into acc |
pop r4 ; restore r4 |
clc ; switch back to '816 mode |
xce |
cpu W65C816S |
rep #$30 ; mem,ndx = 16 bits |
pha |
rts |
|
cpu RTF65002 |
;------------------------------------------------------------------ |
; This test program just loop around waiting to recieve a message. |
; The message is a pointer to a string to display. |
;------------------------------------------------------------------ |
; |
test_mbx_prg: |
jsr RequestIOFocus |
lda #test_mbx ; where to put mailbox handle |
int #4 |
db 6 ; AllocMbx |
ldx #5 |
jsr PRTNUM |
; mStartTask #PRI_LOWEST,#0,#test_mbx_prg2,#0,#0 |
lda #PRI_LOWEST |
ldx #0 |
ldy #test_mbx_prg2 |
ld r4,#0 |
ld r5,#1 |
int #4 |
db 1 ; StartTask |
tmp2: |
lda test_mbx |
ldx #100 |
int #4 |
db 10 ; WaitMsg |
cmp #E_Ok |
bne tmp1 |
txa |
jsr DisplayStringB |
bra tmp2 |
tmp1: |
ldx #4 |
jsr PRTNUM |
bra tmp2 |
|
test_mbx_prg2: |
tmp2a: |
lda test_mbx |
ldx #msg_hello |
ldy #0 |
int #4 |
db 8 ; PostMsg |
bra tmp2a |
msg_hello: |
db "Hello from RTF",13,10,0 |
|
message "DOS.asm" |
include "DOS.asm" |
|
cpu RTF65002 |
|
message "1298" |
include "TinyBasic65002.asm" |
message "1640" |
/trunk/software/asm/TinyBasic65002.asm
48,32 → 48,36
XON EQU 0x11 |
XOFF EQU 0x13 |
|
CursorFlash EQU 0xFC4 |
IRQFlag EQU 0xFC6 |
CursorFlash EQU 0x7C4 |
IRQFlag EQU 0x7C6 |
|
OSSP EQU 0xF00 |
TXTUNF EQU 0xF01 |
VARBGN EQU 0xF02 |
LOPVAR EQU 0xF03 |
STKGOS EQU 0xF04 |
CURRNT EQU 0xF05 |
BUFFER EQU 0xF06 |
OUTPTR EQU 0x778 |
INPPTR EQU 0x779 |
FILENAME EQU 0x6C0 |
FILEBUF EQU 0x01F60000 |
OSSP EQU 0x700 |
TXTUNF EQU 0x701 |
VARBGN EQU 0x702 |
LOPVAR EQU 0x703 |
STKGOS EQU 0x704 |
CURRNT EQU 0x705 |
BUFFER EQU 0x706 |
BUFLEN EQU 84 |
LOPPT EQU 0xF60 |
LOPLN EQU 0xF61 |
LOPINC EQU 0xF62 |
LOPLMT EQU 0xF63 |
NUMWKA EQU 0xF64 |
STKINP EQU 0xF74 |
STKBOT EQU 0xF75 |
usrJmp EQU 0xF76 |
IRQROUT EQU 0xF77 |
LOPPT EQU 0x760 |
LOPLN EQU 0x761 |
LOPINC EQU 0x762 |
LOPLMT EQU 0x763 |
NUMWKA EQU 0x764 |
STKINP EQU 0x774 |
STKBOT EQU 0x775 |
usrJmp EQU 0x776 |
IRQROUT EQU 0x777 |
|
|
|
cpu rtf65002 |
code |
org $FFFFEC00 |
org $FFFFEC80 |
GOSTART: |
jmp CSTART ; Cold Start entry point |
GOWARM: |
93,10 → 97,10
; |
align 4 |
;THRD_AREA dw 0x04000000 ; threading switch area 0x04000000-0x40FFFFF |
;bitmap dw 0x04100000 ; bitmap graphics memory 0x04100000-0x417FFFF |
TXTBGN dw 0x04180000 ;TXT ;beginning of program memory |
ENDMEM dw 0x057FFFFF ; end of available memory |
STACKOFFS dw 0x058FFFFF ; stack offset - leave a little room for the BIOS stacks |
;bitmap dw 0x00100000 ; bitmap graphics memory 0x04100000-0x417FFFF |
TXTBGN dw 0x01800000 ;TXT ;beginning of program memory |
ENDMEM dw 0x018EFFFF ; end of available memory |
STACKOFFS dw 0x018FFFFF ; stack offset - leave a little room for the BIOS stacks |
; |
; The main interpreter starts here: |
; |
504,7 → 508,7
ld r0,IRQFlag ; was there an IRQ ? |
beq RUN1 |
stz IRQFlag |
jsr PUSHA ; the same code as a GOSUB |
jsr PUSHA_ ; the same code as a GOSUB |
push r8 |
lda CURRNT |
pha ; found it, save old 'CURRNT'... |
541,7 → 545,7
; |
GOTO |
jsr OREXPR ;evaluate the following expression |
jsr DisplayWord |
; jsr DisplayWord |
ld r5,r1 |
jsr ENDCHK ;must find end of line |
ld r1,r5 |
723,7 → 727,7
;****************************************************************** |
; |
GOSUB: |
jsr PUSHA ; save the current 'FOR' parameters |
jsr PUSHA_ ; save the current 'FOR' parameters |
jsr OREXPR ; get line number |
jsr FNDLN ; find the target line |
cmp #0 |
762,7 → 766,7
pla |
sta CURRNT ; and the old 'CURRNT' |
pop r8 ; and the old text pointer |
jsr POPA ;and the old 'FOR' parameters |
jsr POPA_ ;and the old 'FOR' parameters |
jmp FINISH ;and we are back home |
|
;****************************************************************** |
785,7 → 789,7
;****************************************************************** |
; |
FOR: |
jsr PUSHA ; save the old 'FOR' save area |
jsr PUSHA_ ; save the old 'FOR' save area |
jsr SETVAL ; set the control variable |
sta LOPVAR ; save its address |
ld r9,#TAB5 |
863,7 → 867,7
NX5: |
cmp r1,r9 |
beq NX2 ; else we check them OK, they agree |
jsr POPA ; nope, let's see the next frame |
jsr POPA_ ; nope, let's see the next frame |
bra NX0 |
NX2: |
lda (r9) ; get control variable's value |
889,7 → 893,7
ld r8,LOPPT ; saved 'CURRNT' and text pointer. |
jmp FINISH |
NXPurge: |
jsr POPA ; purge this loop |
jsr POPA_ ; purge this loop |
jmp FINISH |
|
|
1084,11 → 1088,11
jmp WSTART ; back to direct mode |
|
|
; get character from input (16 bit value) |
; get character from input (32 bit value) |
GCHAR: |
push r5 |
push r6 |
ld r6,#3 ; repeat four times |
ld r6,#8 ; repeat eight times |
ld r5,#0 |
GCHAR1: |
jsr GOAUXI ; get a char |
1096,10 → 1100,7
beq GCHAR1 |
bcc GCHAR1 |
jsr asciiToHex |
asl r5,r5 |
asl r5,r5 |
asl r5,r5 |
asl r5,r5 |
asl r5,r5,#4 |
or r5,r5,r1 |
dec r6 |
bne GCHAR1 |
1123,19 → 1124,48
and #15 ; make sure a nybble |
rts |
|
GetFilename: |
ldy #'"' |
ld r4,#gfn1 |
jsr TSTC |
ldy #0 |
gfn2: |
ld r1,(r8) ; get text character |
inc r8 |
cmp #'"' |
beq gfn3 |
cmp #0 |
beq gfn3 |
sb r1,FILENAME,y |
iny |
cpy #32 |
bne gfn2 |
rts |
gfn3: |
lda #' ' |
sb r1,FILENAME,y |
iny |
cpy #32 |
bne gfn3 |
rts |
gfn1: |
jmp WSTART |
|
LOAD3: |
jsr spi_init |
cmp #0 |
bne WSTART |
lda #5000 |
jsr GetFilename |
jsr AUXIN_INIT |
jmp LOAD |
|
; jsr OREXPR ;evaluate the following expression |
; lda #5000 |
ldx #$E00 |
jsr spi_read_sector |
lda #5001 |
jsr SDReadSector |
ina |
ldx TXTBGN>>2 |
asl r2,r2,#2 |
LOAD4: |
pha |
jsr spi_read_sector |
jsr SDReadSector |
add r2,r2,#512 |
pla |
ina |
1144,21 → 1174,24
add r4,r4,#65536 |
cmp r2,r4 |
bmi LOAD4 |
LOAD5: |
bra WSTART |
|
SAVE3: |
jsr spi_init |
cmp #0 |
bne WSTART |
lda #5000 ; starting sector |
jsr GetFilename |
jsr AUXOUT_INIT |
jmp SAVE |
|
jsr OREXPR ;evaluate the following expression |
; lda #5000 ; starting sector |
ldx #$E00 ; starting address to write |
jsr spi_write_sector |
lda #5001 |
jsr SDWriteSector |
ina |
ldx TXTBGN>>2 |
asl r2,r2,#2 |
SAVE4: |
pha |
jsr spi_write_sector |
jsr SDWriteSector |
add r2,r2,#512 |
pla |
ina |
1194,6 → 1227,7
jsr AUXOCRLF ; followed by a CR & LF |
lda #$1A ; and a control-Z to end the CP/M file |
jsr GOAUXO |
jsr AUXOUT_FLUSH |
bra WSTART ; then go do a warm start |
|
|
1212,14 → 1246,11
; tricky because of the need to reverse the order of the chars |
PWORD: |
push r5 |
ld r5,#NUMWKA+15 |
ld r5,#NUMWKA+7 |
or r4,r1,r0 ; r4 = value |
pword1: |
or r1,r4,r0 ; r1 = value |
lsr r4,r4 ; shift over to next nybble |
lsr r4,r4 |
lsr r4,r4 |
lsr r4,r4 |
lsr r4,r4,#4 ; shift over to next nybble |
jsr toAsciiHex ; convert LS nybble to ascii hex |
sta (r5) ; save in work area |
sub r5,r5,#1 |
1230,7 → 1261,7
add r5,r5,#1 |
lda (r5) ; get char to output |
jsr GOAUXO ; send it |
cmp r5,#NUMWKA+15 |
cmp r5,#NUMWKA+7 |
bcc pword2 |
pop r5 |
rts |
2157,15 → 2188,15
rts |
|
|
; 'POPA' restores the 'FOR' loop variable save area from the stack |
; 'POPA_' restores the 'FOR' loop variable save area from the stack |
; |
; 'PUSHA' stacks for 'FOR' loop variable save area onto the stack |
; 'PUSHA_' stacks for 'FOR' loop variable save area onto the stack |
; |
; Note: a single zero word is stored on the stack in the |
; case that no FOR loops need to be saved. This needs to be |
; done because PUSHA / POPA is called all the time. |
message "POPA" |
POPA: |
; done because PUSHA_ / POPA_ is called all the time. |
message "POPA_" |
POPA_: |
ply |
pla |
sta LOPVAR ; restore LOPVAR, but zero means no more |
2182,7 → 2213,7
jmp (y) |
|
|
PUSHA: |
PUSHA_: |
ply |
lda STKBOT ; Are we running out of stack room? |
add r1,r1,#5 ; we might need this many words |
2317,10 → 2348,10
sub r1,r0,r1 ; else make it positive |
dec r5 ; one less for width count |
PN2: |
ld r3,#10 |
; ld r3,#10 |
PN1: |
mod r2,r1,r3 ; r2 = r1 mod 10 |
div r1,r1,r3 ; r1 /= 10 divide by 10 |
mod r2,r1,#10 ; r2 = r1 mod 10 |
div r1,r1,#10 ; r1 /= 10 divide by 10 |
add r2,r2,#'0' ; convert remainder to ascii |
stx (r7) ; and store in buffer |
inc r7 |
2431,7 → 2462,7
ld r5,r1 ; r5 = pointer |
lda (r5) ; get the binary line number |
inc r5 |
ldx #12 ; display a 0 or more digit line no. |
ldx #5 ; display a 0 or more digit line no. |
jsr PRTNUM |
lda #' ' ; followed by a blank |
jsr GOOUT |
2672,24 → 2703,59
;* ===== Input a character from the host into register r1 (or |
;* return Zero status if there's no character available). |
;* |
AUXIN_INIT: |
stz INPPTR |
lda #FILENAME |
ldx #FILEBUF<<2 |
ldy #$10000 |
jsr do_load |
rts |
|
AUXIN: |
jsr SerialGetChar |
cmp #-1 |
beq AXIRET_ZERO |
and #$7F ;zero out the high bit |
AXIRET: |
phx |
ldx INPPTR |
lb r1,FILEBUF<<2,x |
inx |
stx INPPTR |
plx |
rts |
AXIRET_ZERO: |
lda #0 |
rts |
|
; jsr SerialGetChar |
; cmp #-1 |
; beq AXIRET_ZERO |
; and #$7F ;zero out the high bit |
;AXIRET: |
; rts |
;AXIRET_ZERO: |
; lda #0 |
; rts |
|
; ===== Output character to the host (Port 2) from register r1 |
; (Preserves all registers.) |
; |
AUXOUT |
jmp SerialPutChar ; call boot rom routine |
AUXOUT_INIT: |
stz OUTPTR |
rts |
|
AUXOUT: |
phx |
ldx OUTPTR |
sb r1,FILEBUF<<2,x |
inx |
stx OUTPTR |
plx |
rts |
|
AUXOUT_FLUSH: |
lda #FILENAME |
ldx #FILEBUF<<2 |
ldy OUTPTR |
jsr do_save |
rts |
|
; jmp SerialPutChar ; call boot rom routine |
|
|
_cls |
jsr ClearScreen |
jsr HomeCursor |