OpenCores
URL https://opencores.org/ocsvn/rtf65002/rtf65002/trunk

Subversion Repositories rtf65002

[/] [rtf65002/] [trunk/] [software/] [asm/] [bootrom.asm] - Diff between revs 34 and 39

Show entire file | Details | Blame | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.