mirror of
https://github.com/elliotnunn/sys7.1-doc-wip.git
synced 2024-12-12 04:29:09 +00:00
2287 lines
58 KiB
Plaintext
2287 lines
58 KiB
Plaintext
;
|
|
; File: SCCIOP.aii
|
|
;
|
|
; Contains: xxx put contents here (or delete the whole line) xxx
|
|
;
|
|
; Written by: xxx put name of writer here (or delete the whole line) xxx
|
|
;
|
|
; Copyright: © 1988-1990 by Apple Computer, Inc., all rights reserved.
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <3> 1/18/90 SWC WTO/Fix bugs in InstallTask and KillTask.
|
|
; <1.4> 12/8/89 WTO Fixed bug in version message to kernel. Range check on Driver ID
|
|
; was returning error. Needed to add compare to $00.
|
|
; <1.3> 7/8/89 CCH Added EASE comments to file.
|
|
; <1.2> 6/27/89 BTO Needed for AURORA padding
|
|
; <•1.1> 2/8/89 SGS Initial release
|
|
; <1.0> 11/9/88 CCH Adding to EASE.
|
|
;
|
|
; To Do:
|
|
;
|
|
|
|
|
|
*
|
|
* File SCCIop.aii
|
|
*
|
|
* Copyright 1988 (c) Apple Computer Inc.
|
|
*
|
|
* Main routines to handle IOP. The Kernel is downloaded into the IOP
|
|
* during the IOPMgr initialization process. The IOPMgr then reads
|
|
* PRAM to set the ByPass mode to the appropriate state.
|
|
*
|
|
* Written by Bill O'Connor 2/19/88
|
|
* with ideas and code from Ron Hocksprung
|
|
* Gary Davidian
|
|
*
|
|
* Edit History
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Machine M65CX02
|
|
Longa off
|
|
Longi off
|
|
blanks on
|
|
print nopage, nomdir
|
|
|
|
include 'SCCIOPSysEqu.aii' ; IOP Sytem includes
|
|
include 'SCCIOPEqu.aii' ; IOP Kernel includes
|
|
|
|
Seg 'IOPOS'
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Vectored interrupt table
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Seg 'Vectors'
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Interrupt vector address table.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Vect Proc
|
|
|
|
Import NonMaskable, \
|
|
Reset, \
|
|
IRQ :Code
|
|
|
|
DC.W NonMaskable
|
|
DC.W Reset
|
|
DC.W IRQ
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Main entry into PIC kernel
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Seg 'Main'
|
|
|
|
Export NonMaskable, \
|
|
Reset, \
|
|
IRQ
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* NonMaskable interrupt handler.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
NonMaskable Proc
|
|
|
|
Lda #NonMask ; Let us know we are in NonMaskable Loop
|
|
Bra NonMaskable ; Should never get one
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* IRQ - save registers and jsr to ISR dispatcher.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
IRQ Proc
|
|
|
|
Import ISR_Dsptch : Code
|
|
|
|
Pha ; Save registers.
|
|
Phy
|
|
Phx
|
|
|
|
Jsr ISR_Dsptch ; Interrupt handler
|
|
|
|
Plx ; Restore registers
|
|
Ply
|
|
Pla
|
|
Rti
|
|
|
|
endproc
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Reset handler.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Reset Proc
|
|
|
|
Import Cmd_Table, \
|
|
VerInfo : Data
|
|
Import Kern_Task, \
|
|
Cmd_JmpTable, \
|
|
RxMsg_Signal, \
|
|
SCC_Dsptch, \
|
|
Timer_ISR, \
|
|
Init_Timer, \
|
|
Msg_Scan : Code
|
|
|
|
Sei ; Interrupts off
|
|
|
|
|
|
Stz Int_Host
|
|
Lda #SCC_Port|ByPass_Bit|PClk39
|
|
Sta SCC_Cntl ; Initialize SCC control reg
|
|
Lda #IO_Cntl_init ; Get initial value
|
|
Sta IO_Cntl ; Initialize I/O control reg
|
|
Lda #Host_Msk ; Enable host interrupts
|
|
Sta Int_Mask
|
|
Lda SCC_ACntl ; Sync SCC for writes
|
|
|
|
|
|
*
|
|
* Clear page zero
|
|
*
|
|
|
|
Ldx #$00 ; Index
|
|
@ClearLoop
|
|
Stz $00,x ; Zero out a byte
|
|
Dex ; Point to next byte
|
|
Bne @ClearLoop ; Clear the entire zero page
|
|
|
|
Stz DMA1_Cntl ; Init DMA1
|
|
Stz DMA2_Cntl ; Init DMA2
|
|
Stz TD_Cntl ; Init Timer DPLL control
|
|
Stz Int_Host ; Init host interrupt register
|
|
|
|
*
|
|
* Transfer the kernel command addresses to the zero page table
|
|
*
|
|
|
|
Ldx #KCmd_Table_size ; Begin transfer of command table
|
|
|
|
@Loop Lda Cmd_Table,x
|
|
Sta Krn_CmdBase,x
|
|
Dex
|
|
Bpl @Loop
|
|
|
|
|
|
*
|
|
* Initialize Zero Page variables
|
|
*
|
|
*
|
|
* IMPORTANT !!!!!
|
|
*
|
|
* Because of the manor in which the 6502 does a rts the address
|
|
* used must be the actual address minus 1
|
|
*
|
|
|
|
DvrA_Addr Set DvrA_Base-1
|
|
DvrB_Addr Set DvrB_Base-1
|
|
|
|
Lda #DvrA_ID ; Initialize the driver task ID table
|
|
Sta Dvr_TaskID
|
|
Lda #DvrB_ID
|
|
Sta Dvr_TaskID+1
|
|
|
|
Lda #>DvrA_Addr ; Install the Initialization vector for driver A
|
|
Sta Init_Vec+1
|
|
Lda #<DvrA_Addr
|
|
Sta Init_Vec
|
|
|
|
Lda #>DvrB_Addr ; Install the Initialization vector for driver B
|
|
Sta Init_Vec+3
|
|
Lda #<DvrB_Addr
|
|
Sta Init_Vec+2
|
|
|
|
Lda #>DvrA_Close ; Install the Close vector for driver A
|
|
Sta Close_Vec+1
|
|
Lda #<DvrA_Close
|
|
Sta Close_Vec
|
|
|
|
Lda #>DvrB_Close ; Install the Close vector for driver B
|
|
Sta Close_Vec+3
|
|
Lda #<DvrB_Close
|
|
Sta Close_Vec+2
|
|
|
|
Lda #$ff
|
|
Sta Bypass_Flag ; Set ByPass_Flag to on.
|
|
|
|
Jsr Init_Timer
|
|
|
|
_Reg_Ver Kern, VerInfo
|
|
_Inst_ISR HOST, Msg_Scan ; Install message scan for host interrupt
|
|
_Inst_ISR SCC, SCC_Dsptch ; Install the SCC interrupt dispatcher
|
|
_Inst_ISR TIMER, Timer_ISR
|
|
|
|
_Inst_RxMsgSgn 1, RxMsg_Signal ; Install the kernel receive message signaller
|
|
|
|
Lda #MaxRxMsg
|
|
Sta RxMsgCnt ; PIC Kernel uses 7 messages
|
|
Sta TxMsgCnt ; Both in and out
|
|
|
|
Ldx #$ff ; Stack base is $01FF
|
|
Stx Int_Reg ; Clear the interrupt register
|
|
Stx IOPAlive ; Tell IOPMgr we are Alive
|
|
Txs ; Initialize stack
|
|
|
|
Jmp Kern_Task ; Interrupts will be cleared when
|
|
; Event Wait is called.
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* ISR_Dsptch - determine interrupt source and dispatch to ISR.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
ISR_Dsptch Proc
|
|
|
|
Import ISR_table, \
|
|
ISR_Decode :Data
|
|
|
|
Ldx Int_reg ; Get the interrupt latch
|
|
Lda ISR_Decode,x
|
|
Tax
|
|
Jmp (ISR_table,x) ; Dispatch the interrupt
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* SCC_Dsptch - determine source and dispatch SCC interrupts.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
SCC_Dsptch Proc
|
|
|
|
Import SCC_table, \
|
|
UnKnown_Int :Code
|
|
|
|
Lda #Rd_Reg2 ; Read the SCC interrupt latch
|
|
Sta SCC_BCntl ; Interrupt latch is register 2 port B
|
|
Ldx SCC_BStat ; Read the status V1 V2 V3
|
|
Jmp (SCC_table,x) ; Dispatch the interrupt
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Unknown_SCCInt - unknown SCC interrupt handler.
|
|
*
|
|
* This routine handles interrupts that occur but do
|
|
* not have an SCCISR installed.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
UnKnown_SCCInt Proc
|
|
|
|
Brk
|
|
|
|
endproc
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Unknown_Int - unknown interrupt handler.
|
|
*
|
|
* This routine handles interrupts that occur but do
|
|
* not have an ISR installed.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Unknown_Int Proc
|
|
|
|
Tsx ; Get the stack pointer
|
|
Lda StackPage+6,x ; Get the psw from the stack
|
|
Bit #pswB ; Was it a break that caused the interrupt
|
|
Bne BRK_Handler ; Yes
|
|
Rts ; No, just return
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* BRK_Handler - handle a software interrupt
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
BRK_Handler Tsx ; Get the stack pointer
|
|
|
|
*
|
|
* Cycle through stack so that we can see it on the logic analyzer
|
|
*
|
|
|
|
Do_Again Inx
|
|
Beq BRK_Handler
|
|
Lda StackPage,x
|
|
Bra Do_Again
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
* Entry Points
|
|
*
|
|
* Rel_Task - release a task for one task cycle.
|
|
*
|
|
* Ev_Wait - wait for an event to happen.
|
|
*
|
|
* The return address should already be on the stack.
|
|
* Ev_Wait falls through into Ev_Dsptch.
|
|
*
|
|
* Register A - event mask.
|
|
*
|
|
* On exit register A contains the tEvent for the Task.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Rel_Task Proc
|
|
|
|
Export Ev_Wait
|
|
|
|
Import tSp, \
|
|
tNext, \
|
|
tEvent, \
|
|
tWait : Data
|
|
|
|
Lda #$80 ; This event is always set
|
|
; therefore the task will execute the
|
|
; next task cylce
|
|
|
|
Ev_Wait Ldy Cur_Task ; Get the Task ID
|
|
Sta tWait,y ; Store the event mask
|
|
|
|
*
|
|
* Ev_Dsptch - entry to task dispatcher.
|
|
*
|
|
|
|
Sei
|
|
Ldy Cur_Task ; Get the current task ID
|
|
Tsx
|
|
Txa
|
|
Sta tSp,y ; Save the task's stack pointer
|
|
Cli
|
|
|
|
Ldx #$FF
|
|
|
|
@Scan Lda tNext,y ; Get the next task ID
|
|
Tay
|
|
Lda tEvent,y ; Check the flags to see if this one is ready
|
|
And tWait,y
|
|
Stx IOPAlive ; This lets the Host know we're still alive
|
|
Beq @Scan
|
|
|
|
Sei
|
|
Sty Cur_Task ; Switch Tasks
|
|
Ldx tSp,y ; Get this task's stack pointer
|
|
Txs
|
|
Cli
|
|
|
|
Rts ; go to the task
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Ev_Signal - signal a task that an event has occurred.
|
|
*
|
|
* Events are flagged by setting bits in the tEvent byte of the
|
|
* TCB of the task to receive the signal.
|
|
*
|
|
* Register X - ID of task to signal.
|
|
* Register A - event flag.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Ev_Signal Proc
|
|
|
|
Import tEvent : Data
|
|
|
|
Php
|
|
Sei
|
|
Ora tEvent,x ; Set the event bit
|
|
Sta tEvent,x ; Save it in the task's TCB
|
|
Plp
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Ev_Reset - reset an event flag.
|
|
*
|
|
* Register A - complement of event flag.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Ev_Reset Proc
|
|
|
|
Import tEvent : Data
|
|
|
|
Php
|
|
Sei
|
|
Ldx Cur_task ; We can only reset our own task
|
|
And tEvent,x ; Reset the event bit
|
|
Sta tEvent,x ; Save it in the task's TCB
|
|
Plp
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Msg_Scan - handle interrupts from host CPU. Must be installed
|
|
* when processor is reset.
|
|
*
|
|
* Scan messages for New Message Sent and Message Complete. Each
|
|
* task installs message handlers that signal the task when new messages
|
|
* are received or messages are completed. Each task must install a
|
|
* handler for each message box it intends to use.
|
|
*
|
|
* The only message the kernel will accept when in ByPassMode
|
|
* is ByPassMode(Off).
|
|
*
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Msg_Scan Proc
|
|
|
|
Import TxMsgSignal, \
|
|
RxMsgSignal : Code
|
|
|
|
Import MsgNumTimes2 : Data
|
|
|
|
Lda #Host_Msk
|
|
Sta Int_Reg ; Clear the host interrupt
|
|
Trb Int_Mask ; Disable host interrupts
|
|
Cli ; Renable interrupts of higher priority
|
|
|
|
Lda ByPass_Flag ; We don't do anything in bypass mode
|
|
Bne Do_ByPass ; except receive on message one
|
|
|
|
*
|
|
* Service Completed Transmit Messages first
|
|
*
|
|
|
|
Ldy TxMsgCnt ; Get highest Tx message number
|
|
Lda #MsgCmplt ; Get the desired message state
|
|
|
|
@TxMsgLoop Cpa TxMsgState,y ; See if the message completed
|
|
Bne @TxMsgNext ; If no service needed goto next msg
|
|
|
|
Phy ; Preserve loop counter
|
|
Ldx MsgNumTimes2,y ; Convert message number to table index
|
|
Jsr TxHandler ; Call the handler
|
|
Ply ; Restore the message number
|
|
|
|
Lda #Idle
|
|
Sta TxMsgState,y ; Change message state to Idle
|
|
Lda #MsgCmplt ; Restore desired message state
|
|
|
|
@TxMsgNext Dey ; Point to previous message
|
|
Bne @TxMsgLoop ; Loop through all messages
|
|
|
|
Bra RxMsgStart
|
|
|
|
*
|
|
* Service Receive New Message Sent last
|
|
*
|
|
|
|
Do_ByPass Ldy #1 ; In ByPass mode we only receive message 1
|
|
Bra ByPassSkip ; for the kernel
|
|
|
|
RxMsgStart Ldy RxMsgCnt ; Get highest Rx message number
|
|
|
|
ByPassSkip Lda #NewMsgSent ; Get the desired message state
|
|
|
|
@RxMsgLoop Cpa RxMsgState,y ; See if message just arrived
|
|
Bne @RxMsgNext ; If no service needed
|
|
|
|
Lda #MsgRcv
|
|
Sta RxMsgState,y ; Change message state to Message Received
|
|
Phy ; Preserve loop counter
|
|
Ldx MsgNumTimes2,y ; Convert message number to table index
|
|
Jsr RxHandler ; Call the handler
|
|
Ply ; Restore the message number
|
|
Lda #NewMsgSent ; Restore desired message state
|
|
|
|
@RxMsgNext Dey ; Point to previous message
|
|
Bne @RxMsgLoop ; Loop through all messages
|
|
|
|
Sei
|
|
Lda #Host_Msk ; Get the host interrupt bit
|
|
Tsb Int_Mask ; Re-enable host interrupts
|
|
Rts ; All done
|
|
|
|
TxHandler Jmp (TxMsgSignal-2,x) ; Go to handler - msg's start at 1
|
|
|
|
RxHandler Jmp (RxMsgSignal-2,x) ; Go to handler - msg's start at 1
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* RxMsg_Signal - signal kernel that a message has arrived.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
RxMsg_Signal Proc
|
|
|
|
Import Cmd_JmpTable : Code
|
|
|
|
Ldx #Kern_ID ; ID of task we wish to signal
|
|
Lda #RxMsg ; Signal we want to send
|
|
Jmp Cmd_JmpTable+(SignalTask*3) ; Signal Task Command has an RTS
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Kern_Task - main kernel task routine and event dispatcher
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Kern_Task Proc
|
|
|
|
Import Dsptch_KRxMsg : Code
|
|
Import Cmd_JmpTable : Code
|
|
|
|
_Wait_Event RxMsg ; Wait until we get a receive message event
|
|
|
|
Asl A ; tEvent is already in register A
|
|
Tax
|
|
Jmp (Kern_Disp-2,x)
|
|
|
|
Kern_Disp DC.W Dsptch_KRxMsg ; Dispatch Kernel message is the only
|
|
; event we look for.
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Dsptch_KRxMsg - Decode and dispatch messages to Kernel
|
|
*
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Dsptch_KRxMsg Proc
|
|
|
|
Import MsgNumTimes32 : Data
|
|
|
|
Import Kern_Task, \
|
|
Alloc_Driver, \
|
|
DeAlloc_Driver, \
|
|
ByPass_Mode, \
|
|
Init_Driver, \
|
|
Version, \
|
|
SCC_Control, \
|
|
Cmd_JmpTable : Code
|
|
|
|
_Reset_Event RxMsg ; Reset the event
|
|
|
|
Ldy #$01 ; Kernel has only one message (msg1)
|
|
Ldx MsgNumTimes32,y ; Get the Receive message page offset
|
|
|
|
Lda ByPass_Flag ; Check for ByPass mode
|
|
Bne Do_ByPass
|
|
|
|
Procede Lda RxMsgPage,x ; Get the command byte
|
|
Beq UnKnown_Msg ; Zero is not a command
|
|
|
|
Cmp #Max_KMsg+1 ; If > the max msg #
|
|
Bge Unknown_Msg ; branch to unknown message routine
|
|
|
|
Stz RxMsgPage,x ; Clear the message byte
|
|
|
|
Asl A ; Make it a word offset
|
|
Phx
|
|
Tax
|
|
Jmp (Msg_Table-2,x) ; Dispatch the command
|
|
|
|
Do_ByPass Lda RxMsgPage,x
|
|
Cmp #ByPass ; Is it a ByPass Call
|
|
Beq Procede ; Yes, procede
|
|
|
|
Cmp #SCCCntl ; Allow the SCC Control register call.
|
|
Beq Procede ; Yes, procede with message
|
|
|
|
Stz RxMsgPage+2,x
|
|
Lda ByPass_Flag
|
|
Sta RxMsgPage+1,x ; Return ByPass client ID
|
|
Lda #InByPass
|
|
Bra Complete
|
|
|
|
UnKnown_Msg Stz RxMsgPage+2,x
|
|
Stz RxMsgPage+1,x
|
|
Lda #UnKnwnMsg ;
|
|
|
|
Complete Sta RxMsgPage,x ; Return the error
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
Msg_Table DC.W Alloc_Driver
|
|
DC.W DeAlloc_Driver
|
|
DC.W Init_Driver
|
|
DC.W ByPass_Mode
|
|
DC.W Version
|
|
DC.W SCC_Control
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Alloc_Driver
|
|
*
|
|
* Passed
|
|
*
|
|
* struct AllocMsgType {
|
|
* byte msgNum; /* Alloc_Channel msg = $01 */
|
|
* byte Driver; /* Dvr A = $00 Dvr B = $01 */
|
|
* byte ClientID; /* Port use ID > $00 */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct AllocErrType{
|
|
* byte errNum; /* NoErr = $00
|
|
* Error = -1
|
|
* DvrInUse = -4
|
|
* InBypass = -5
|
|
* */
|
|
* byte ClientID; /* ID > 0 if port in use */
|
|
* int Addr; /* Driver load address - 1 */
|
|
* }
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Alloc_Driver Proc
|
|
|
|
Import Kern_Task : Code
|
|
|
|
Plx ; Restore X - msg number * 32
|
|
Phy
|
|
Ldy RxMsgPage+1,x ; Get the channel byte (DvrA or DvrB)
|
|
Cpy #MaxDvr+1 ; Are we trying to open a nonexistant driver
|
|
Bge ERR
|
|
Cpy #DvrA
|
|
Blt ERR
|
|
Stz RxMsgPage+1,x ; Clear the Driver byte
|
|
Lda Dvr_Use,y ; See if the driver is in use
|
|
Bne InUse
|
|
|
|
Lda RxMsgPage+2,x ; Get the ClientID byte
|
|
Sta Dvr_Use,y ; Store the ID if the driver is free
|
|
Stz RxMsgPage+2,x ; Clear the ClientID
|
|
Tya ; Make the driver ID a word offset
|
|
Asl A
|
|
Tay
|
|
Lda Init_Vec,y ; Return the driver starting address
|
|
Sta RxMsgPage+3,x
|
|
Lda Init_Vec+1,y
|
|
Sta RxMsgPage+2,x
|
|
Bra Complete ; NoErr is reported because we've been
|
|
; clearing bytes as we go!!!
|
|
|
|
InUse Stz RxMsgPage+2,x ; Clear the ClientID of the message
|
|
Sta RxMsgPage+1,x ; Return the ClientID of the client using the Driver
|
|
Lda #DvrInUse
|
|
Sta RxMsgPage,x ; Return the error Driver In Use
|
|
Bra Complete
|
|
|
|
ERR Stz RxMsgPage+2,x ; Clear ClinetID
|
|
Stz RxMsgPage+1,x ; Clear driver byte
|
|
Lda #Error ; We made a boo boo
|
|
Sta RxMsgPage,x ; Return the code
|
|
|
|
Complete Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* DeAlloc_Driver
|
|
*
|
|
* Passed
|
|
*
|
|
* struct DeAllocMsgType {
|
|
* byte msgNum; /* DeAlloc_Channel msg = $02 */
|
|
* byte Driver; /* Dvr A = $00 Dvr B = $01 */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct DeAllocErrType{
|
|
* byte errNum; /* NoErr = $00
|
|
* Error = -1
|
|
* InByPass = -5
|
|
* */
|
|
* }
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
DeAlloc_Driver Proc
|
|
|
|
Import Kern_Task : Code
|
|
|
|
Plx ; Restore X - msg number * 32
|
|
Phy
|
|
Ldy RxMsgPage+1,x ; Get the driver byte (DvrA or DvrB)
|
|
Cpy #MaxDvr+1 ; Are we trying to close a nonexistant driver
|
|
Bge ERR
|
|
Cpy #DvrA
|
|
Blt ERR
|
|
Stz RxMsgPage+1,x ; clear the channel byte
|
|
|
|
Lda #00
|
|
Sta Dvr_Use,y ; Disable driver use byte
|
|
|
|
Tya
|
|
Asl A ; Make the channel number a word offset
|
|
Tax
|
|
Jsr FakeIndirX
|
|
Bra Complete
|
|
|
|
ERR Lda #Error ; Report that we made a boo boo
|
|
Sta RxMsgPage,x ; We were trying to close a nonexistant Driver
|
|
|
|
Complete Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
FakeIndirX Jmp (Close_Vec,x) ; Jump to the channel's close routine
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Init_Driver
|
|
*
|
|
* Passed
|
|
*
|
|
* struct InitMsgType {
|
|
* byte msgNum; /* Init_Channel msg = $03 */
|
|
* byte Driver; /* Dvr A = $00 Dvr B = $01 */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct InitErrType {
|
|
* byte errNum; /* NoErr = $00
|
|
* Error = -1
|
|
* InByPass = -5
|
|
* NotAlloc = -6
|
|
* */
|
|
* }
|
|
*
|
|
*
|
|
* On Entry
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
* On Exit
|
|
*
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Init_Driver Proc
|
|
|
|
Import Kern_Task, \
|
|
Cmd_JmpTable : Code
|
|
|
|
Plx ; Get the message offset
|
|
Phy ; Save the message number
|
|
|
|
Ldy RxMsgPage+1,x ; Get the driver byte (DvrA or DvrB)
|
|
Cpy #MaxDvr+1 ; Are we trying to init a nonexistant driver
|
|
Bge ERR
|
|
Cpy #DvrA
|
|
Blt ERR
|
|
|
|
Stz RxMsgPage+1,x ; Clear driver byte
|
|
|
|
Lda Dvr_Use,y ; Check to see if driver has been allocated
|
|
Beq Not_Open
|
|
|
|
Phx ; Save the message offset
|
|
Lda Dvr_TaskID,y ; Get the Task ID for this driver
|
|
Pha ; Save Task ID temp.
|
|
Tya ; Load A with the driver byte
|
|
Asl A ; Make the driver byte a word offset
|
|
Tax
|
|
Lda Init_Vec,x ; Get the address of the driver init routine
|
|
Ldy Init_Vec+1,x
|
|
Plx ; Get the task ID
|
|
Sec ; Set Carry for Install Task
|
|
|
|
Jsr Cmd_JmpTable+(InstTask*3) ; Install the task into the Q
|
|
|
|
_Wait_Event InitFin ; Wait for the task to signal it's completion
|
|
|
|
_Reset_Event InitFin ; Reset tWait so we don't get this event again
|
|
|
|
Plx ; Restore the message offset
|
|
Stz RxMsgPage,x ; Signal No Error
|
|
Bra Complete
|
|
|
|
Not_Open Lda #NotAlloc ; Not open, therefore cannot be initialized
|
|
Sta RxMsgPage,x
|
|
Bra Complete
|
|
|
|
ERR Lda #Error ; Report that we made a boo boo
|
|
Sta RxMsgPage,x ; We were trying to close a nonexistant driver
|
|
|
|
Complete Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* ByPass_Mode
|
|
*
|
|
* Passed
|
|
*
|
|
* struct ByPassMsgType {
|
|
* byte msgNum; /* ByPass_Mode msg = $04 */
|
|
* byte On_Off; /* Off = 0 On = 1 */
|
|
* byte ClientID; /* ID > 0 */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct ByPassErrType{
|
|
* byte errNum; /* NoErr = $00
|
|
* ChanInUse = -4
|
|
* InByPass = -5
|
|
* BadID = -7
|
|
* */
|
|
* Union {
|
|
* byte ByPassID; /* ID > 0 if in ByPass */
|
|
* byte ClientAID; /* ID > 0 If currently allocated */
|
|
* } switch;
|
|
* byte ClientBID; /* ID > 0 IF currently allocated */
|
|
* }
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
ByPass_Mode Proc
|
|
|
|
Import Kern_Task, \
|
|
Cmd_JmpTable : Code
|
|
|
|
Plx
|
|
Phy
|
|
Ldy RxMsgPage+1,x ; Get the On/Off byte
|
|
Beq ByPass_Off ; = 0 means we want to turn ByPass Off
|
|
Stz RxMsgPage+1,x ; Clear On/Off byte
|
|
|
|
Lda Dvr_AUse ; Check to see if Driver A is closed
|
|
Bne In_Use
|
|
|
|
Lda Dvr_BUse ; Check to see if Driver B is closed
|
|
Bne In_Use
|
|
|
|
Lda ByPass_Flag ; Check to see if we are in ByPass Mode
|
|
Bne In_ByPass
|
|
|
|
Php
|
|
Sei
|
|
Phx
|
|
Jsr OnReset
|
|
Plx
|
|
Plp
|
|
Lda #ByPass_Bit|RTXCA_36|RTXCB_36 ; Put PIC into ByPass Mode, RTXC=3.6 MHZ
|
|
Tsb SCC_Cntl
|
|
Lda RxMsgPage+2,x ; Set ByPass_Flag to ID byte
|
|
Sta ByPass_Flag ;
|
|
Bra Complete
|
|
|
|
ByPass_Off Stz RxMsgPage+1,x ; Clear on off byte
|
|
Lda RxMsgPage+2,x ; Get the ID
|
|
Stz RxMsgPage+2,x ; Clear the ID
|
|
Cmp ByPass_Flag ; Make sure who ever put us in ByPass takes us out
|
|
Bne WrongID
|
|
|
|
Php
|
|
Sei
|
|
Lda #ByPass_Bit ; Take PIC out of ByPass Mode
|
|
Trb SCC_Cntl
|
|
Jsr OffReset
|
|
Plp
|
|
Stz ByPass_Flag ; Turn ByPass mode off
|
|
Bra Complete
|
|
|
|
WrongID Lda ByPass_Flag
|
|
Sta RxMsgPage+1,x ; Return the ID of the person who turned ByPass on
|
|
Lda #BadID ; Make sure who ever put us
|
|
Sta RxMsgPage,x ; in bypass mode takes us out!!!!
|
|
Bra Complete
|
|
|
|
In_ByPass Lda ByPass_Flag
|
|
Sta RxMsgPage+1,x ; Return the ID of the person who turned ByPass on
|
|
Stz RxMsgPage+2,x ; Clear ID
|
|
Lda #InByPass
|
|
Sta RxMsgPage,x ; Return the error In ByPass mode
|
|
Bra Complete
|
|
|
|
In_Use Lda Dvr_AUse
|
|
Sta RxMsgPage+1,x ; Return driver A ClientID
|
|
Lda Dvr_BUse
|
|
Sta RxMsgPage+2,x ; Return driver B ClientID
|
|
|
|
Lda #DvrInUse ; Report that a driver is in use
|
|
Sta RxMsgPage,x
|
|
|
|
Complete Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
OffReset Lda #SCC_Msk
|
|
Tsb Int_Mask ; Enable SCC interrupts
|
|
Bra OffSkip
|
|
|
|
OnReset Lda #SCC_Msk
|
|
Trb Int_Mask
|
|
|
|
*
|
|
* Initialize the SCC
|
|
*
|
|
|
|
OffSkip Lda SCC_ACntl ; Synchronize Read/Writes
|
|
|
|
*
|
|
* Initialize the SCC
|
|
*
|
|
|
|
DoB Lda #Wr_reg9
|
|
Sta SCC_ACntl
|
|
Lda #ChanB_Reset
|
|
Sta SCC_ACntl
|
|
|
|
Ldx #0
|
|
@LoopB Lda Table,X ; Get the register number
|
|
Tay ; Transfer indicator bits
|
|
Iny ; Was it an $FF? (END OF TABLE?)
|
|
Beq DoA ; If so, exit
|
|
|
|
Sta SCC_BCntl ; Set register
|
|
Inx ; Move to next character
|
|
Lda Table,X ; Register contents
|
|
Sta SCC_BCntl
|
|
Inx
|
|
Bra @LoopB
|
|
|
|
DoA Lda #Wr_reg9
|
|
Sta SCC_ACntl
|
|
Lda #ChanA_Reset
|
|
Sta SCC_ACntl
|
|
|
|
Ldx #0
|
|
@LoopA Lda Table,X ; Get the register number
|
|
Tay ; Transfer indicator bits
|
|
Iny ; Was it an $FF? (END OF TABLE?)
|
|
Beq @Done ; If so, exit
|
|
|
|
Sta SCC_ACntl ; Set register
|
|
Inx ; Move to next character
|
|
Lda Table,X ; Register contents
|
|
Sta SCC_ACntl
|
|
Inx
|
|
Bra @LoopA
|
|
|
|
|
|
@Done Rts
|
|
|
|
Table
|
|
DC.B Wr_reg4, X16CLK|StopBit2
|
|
DC.B Wr_reg2, 00
|
|
DC.B Wr_reg3, Rx_8Bits
|
|
DC.B Wr_reg15, 00 ; Clear External status interrupts
|
|
DC.B Wr_reg0, Reset_ExtInt ; Reset external status interrupts twice ??
|
|
DC.B Wr_reg0, Reset_ExtInt
|
|
DC.B Wr_reg1, 00 ; Disable all interrupts
|
|
DC.B $ff ; End of table
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Version
|
|
*
|
|
* Passed
|
|
*
|
|
* struct VersionMsgType {
|
|
* byte msgNum; /* Version = $05 */
|
|
* byte DriverID; /* Driver A = $00 */
|
|
* /* Driver B = $01 */
|
|
* /* Kernel = $02 */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct VersionErrType{
|
|
* byte errNum; /* NoErr = $00 */
|
|
* ptr VerInfo; /* Pointer to version info */
|
|
* }
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Version Proc
|
|
|
|
Import VerInfo : Data
|
|
Import Kern_Task : Code
|
|
|
|
Plx
|
|
Phy
|
|
|
|
Lda RxMsgPage+1,x
|
|
Cmp #00 ; Do a range check on Driver ID
|
|
Blt Err
|
|
|
|
Cmp #Kern+1
|
|
Bge Err
|
|
|
|
Asl A
|
|
Tay
|
|
Lda Ver_Ptr,y ; Return the address of the version info
|
|
Sta RxMsgPage+1,x
|
|
Lda Ver_Ptr+1,y
|
|
Sta RxMsgPage+2,x
|
|
|
|
Cmplt Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
Err Lda #Error ; Report Error and complete
|
|
Sta RxMsgPage,x
|
|
Bra Cmplt
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* SCC_Control - turn the general purpose clock source on and
|
|
* off when in bypass.
|
|
*
|
|
* Passed
|
|
*
|
|
* struct SCCCntlMsgType {
|
|
* byte msgNum; /* SCC_Cntl msg = $06 */
|
|
* byte Driver; /* Dvr A = $00 Dvr B = $01 */
|
|
* byte GPI; /* GPI = 0 (Off) GPI = 1 (On) */
|
|
* }
|
|
*
|
|
* Returns
|
|
*
|
|
* struct SCCCntlErrType{
|
|
* byte errNum; /* NoErr = $00
|
|
* Error = -1
|
|
* */
|
|
* }
|
|
*
|
|
* Stack Info
|
|
* -----------------
|
|
* | Msg # * 32 |
|
|
* -----------------
|
|
* SP ->
|
|
*
|
|
* Register Y - contains message number starting at 1
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
SCC_Control Proc
|
|
|
|
Import Kern_Task : Code
|
|
|
|
Plx ; Restore X - msg number * 32
|
|
Phy
|
|
Lda ByPass_Flag ; This command only works in bypass.
|
|
Beq ERR
|
|
|
|
Stz RxMsgPage,x ; Assume no error.
|
|
Lda RxMsgPage+1,x ; Get the channel byte (DvrA or DvrB)
|
|
Cpa #DvrA ; Is it for DvrA or DvrB
|
|
Bne @Chck_B
|
|
|
|
Lda RxMsgPage+2,x ; Get the GPI
|
|
Bne @GPIA_On ; <> 0 means turn GPIA on
|
|
|
|
Lda SCC_Cntl ; Get the current value of the register.
|
|
And #≈RTXCA_GPIA ; Make the clock source for RTXCA 3.686 MHZ
|
|
Sta SCC_Cntl ; Store the new value
|
|
Bra Complete
|
|
|
|
@GPIA_On Lda #RTXCA_GPIA ; Get the bit mask.
|
|
Tsb SCC_Cntl ; Make the clock source for RTXCA GPIA
|
|
Bra Complete
|
|
|
|
@Chck_B Cpa #DvrB ; Was it Driver B
|
|
Bne ERR ; It is an error if it isn't.
|
|
|
|
Lda RxMsgPage+2,x ; Get the GPI
|
|
Bne @GPIB_On ; <> 0 means turn GPIB on
|
|
|
|
Lda SCC_Cntl ; Get the current value of the register
|
|
And #≈RTXCB_GPIA ; Make the clock source for RTXCB 3.686 MHZ
|
|
Sta SCC_Cntl ; Store the new value.
|
|
Bra Complete
|
|
|
|
@GPIB_On Lda #RTXCB_GPIA ; Get the bit mask.
|
|
Tsb SCC_Cntl ; Make the clock source for RTXB GPIB
|
|
Bra Complete
|
|
|
|
ERR Lda #Error ; We made a boo boo
|
|
Sta RxMsgPage,x ; Return the code
|
|
|
|
Complete Ply
|
|
Lda #MsgCmplt ; Tell host we've completed the message
|
|
Sta RxMsgState,y ; Store it in the message state variable
|
|
Lda #Int0 ; Load A with host interrupt bit
|
|
Tsb Int_Host
|
|
Jmp Kern_Task ; Return to wait for next event
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Kernel Subroutines called by indirect jump through table
|
|
* in the Zero Page
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Remv_RxMsgSgn - Install receive message signaller
|
|
*
|
|
* Register X - message number * 2 (msg numbers start at 1)
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Remv_RxMsgSgn Proc
|
|
|
|
Import RxMsgSignal : Data
|
|
|
|
Export NullRxMsgSgn
|
|
|
|
Bcs Inst_RxMsgSgn
|
|
Ldy #>NullRxMsgSgn
|
|
Lda #<NullRxMsgSgn
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_RxMsgSgn - Install receive message signaller
|
|
*
|
|
* Register X - message number * 2 (msg numbers start at 1)
|
|
* Register Y - High byte of message signaller address
|
|
* Register A - Low byte of message signaller address
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Inst_RxMsgSgn Php ; Save the status byte
|
|
Sei
|
|
Sta RxMsgSignal-2,x ; Store the low byte of the msg handler
|
|
Inx
|
|
Tya
|
|
Sta RxMsgSignal-2,x ; Store the high byte of the msg handler
|
|
Plp ; Restore the status byte
|
|
|
|
Rts ; Return to caller
|
|
|
|
NullRxMsgSgn Rts
|
|
|
|
EndProc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Remv_TxCmplSgn - Removel transmit completion signaller
|
|
*
|
|
* Register X - message number * 2 (msg numbers start at one)
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Remv_TxCmplSgn Proc
|
|
|
|
Import TxMsgSignal : Data
|
|
Export NullTxCmplSgn
|
|
|
|
Bcs Inst_TxCmplSgn
|
|
Ldy #>NullTxCmplSgn
|
|
Lda #<NullTxCmplSgn
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_TxCmplSgn - Install transmit completion signaller
|
|
*
|
|
* Register X - message number * 2 (msg numbers start at one)
|
|
* Register Y - High byte of completion handler
|
|
* Register A - Low byte of completion handler
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Inst_TxCmplSgn Php
|
|
Sei
|
|
Sta TxMsgSignal-2,x ; Store the low byte of the msg handler
|
|
Tya
|
|
Sta TxMsgSignal-1,x ; Store the high byte of the msg handler
|
|
Plp
|
|
|
|
Rts ; Return to caller
|
|
|
|
NullTxCmplSgn Rts
|
|
|
|
EndProc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_Task - Install task. Always installs task after Kernel.
|
|
*
|
|
* Register X - TaskID of task to install
|
|
* Register Y - High byte of task address
|
|
* Register A - Low byte of task address
|
|
*
|
|
* Carry Clear - Branch to Kill Task
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Inst_Task Proc
|
|
|
|
Import tPrev, \
|
|
tNext, \
|
|
tSp, \
|
|
tEvent, \
|
|
tWait : Data
|
|
|
|
Bcc Kill_Task
|
|
Php
|
|
Sei
|
|
|
|
Cpx #DvrA_ID
|
|
Beq Do_DvrA
|
|
|
|
Sta DvrB_Sp-1 ; Push the task address on the stack
|
|
Sty DvrB_Sp
|
|
Lda #<(DvrB_Sp-2) ; Init the stack pointer
|
|
Bra SkipA
|
|
|
|
Do_DvrA Sta DvrA_Sp-1 ; Push the task address on the stack
|
|
Sty DvrA_Sp
|
|
Lda #<(DvrA_Sp-2) ; Init the stack pointer
|
|
|
|
SkipA Sta tSp,x
|
|
|
|
Ldy #Kern_ID ; Insert the Task after the Kernel task
|
|
Lda tNext,y ; Get the Kernel's tNext link.
|
|
Sta tNext,x ; Make the new Task link to what the kernel
|
|
; was linked to.
|
|
Tay ; Make y-reg point to what the tNext of the Kernel
|
|
Txa ; Make a-reg the value of the one we are inserting
|
|
Sta tPrev,y ; Update the previous pointer of the task we are inserting
|
|
; in front of.
|
|
|
|
Ldy #Kern_ID
|
|
Sta tNext,y ; Make the Kernel TCB point to the new TCB
|
|
Tya ; Make a-reg point to Kernel
|
|
Sta tPrev,x ; Make tPrev of the task to be inserted
|
|
; point to the Kernel.
|
|
|
|
Lda #$80
|
|
Sta tEvent,x ; Set the Always Event
|
|
Sta tWait,x ; Wait for the Always event
|
|
|
|
Plp
|
|
|
|
Rts
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Kill_Task - Kill task.
|
|
*
|
|
* Register X - TaskID of the task to be removed. (ChanA_ID ChanB_ID only !)
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Kill_Task
|
|
|
|
Php
|
|
Sei
|
|
Lda tPrev,x ; Get tPrev of task to be removed.
|
|
Tay ; y-reg points to task before the task to be removed.
|
|
Lda tNext,x ; Get tNext from task to be removed
|
|
Tax ; Make x-reg point to next task.
|
|
Sta tNext,y ; Make tNext of previous task point the task
|
|
; after the task to be removed.
|
|
Tya ; Make a-reg point to prev task
|
|
Sta tPrev,x ; Make tPrev of next task point to the tNext
|
|
; of the task that is removed.
|
|
Plp
|
|
|
|
Rts
|
|
|
|
EndProc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Remv_ISR - remove an Interrupt service routine
|
|
*
|
|
* Register X - interrupt type
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Remv_ISR Proc
|
|
|
|
Export Inst_ISR
|
|
|
|
Import UnKnown_Int : Code
|
|
Import ISR_Table :Data
|
|
|
|
Bcs Inst_ISR ; Is this a Remove or Install ISR call??
|
|
Ldy #>UnKnown_Int ; For Remove we fill in values for the
|
|
Lda #<Unknown_Int ; Unknown interrupt handler.
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_ISR - make an entry into the interrupt table.
|
|
*
|
|
* Register X - interrupt type
|
|
* Register Y - High byte of interrupt address
|
|
* Register A - Low byte of interrupt address
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Inst_ISR Php
|
|
Sei
|
|
Sta ISR_table,x ; Store the low byte of the address
|
|
Tya
|
|
Sta ISR_table+1,x ; Store the high byte of the address
|
|
|
|
Plp
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Remove_ISR - remove an Interrupt service routine
|
|
*
|
|
* Carry -> Set - install SCC ISR routine
|
|
* Clear - remove SCC ISR routine
|
|
*
|
|
* Register X - interrupt type
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Remv_SCC_ISR Proc
|
|
|
|
Import UnKnown_SCCInt : Code
|
|
Import SCC_table : Data
|
|
|
|
Bcs Inst_SCC_ISR
|
|
Ldy #>UnKnown_Int
|
|
Lda #<Unknown_Int
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_SCC_ISR - make an entry into the SCC interrupt table.
|
|
*
|
|
* Register X - interrupt type
|
|
* Register Y - High byte of interrupt address
|
|
* Register A - Low byte of interrupt address
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
Inst_SCC_ISR Php
|
|
Sei
|
|
Sta SCC_table,x ; Store the low byte of the address
|
|
Tya
|
|
Sta SCC_table+1,x ; Store the high byte of the address
|
|
|
|
Plp
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Reset Channel - reset channel A or B of the SCC
|
|
*
|
|
* Carry -> Set - reset A
|
|
* Clear - reset B
|
|
*
|
|
* A Register is corrupted
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Reset_Chan Proc
|
|
|
|
Bcc ChanB ; Is this a ChanA or ChanB reset
|
|
Php ; Do ChanA reset
|
|
Sei
|
|
Lda #Wr_Reg9
|
|
Sta SCC_ACntl
|
|
Lda #ChanA_Reset|MIE|No_Vec
|
|
Sta SCC_ACntl
|
|
Plp
|
|
Rts
|
|
|
|
ChanB Php ; Do ChanB Reset
|
|
Sei
|
|
Lda #Wr_Reg9
|
|
Sta SCC_ACntl
|
|
Lda #ChanB_Reset|MIE|No_Vec
|
|
Sta SCC_ACntl
|
|
Plp
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Reg_Ver - register version info.
|
|
*
|
|
* Register A - Driver ID
|
|
* Register Y - High byte of info address
|
|
* Register X - Low byte of info address
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Reg_Ver Proc
|
|
|
|
Phx
|
|
Asl A
|
|
Tax
|
|
Pla
|
|
Sta Ver_Ptr+1, x ; Store the Low byte of the address
|
|
Sty Ver_Ptr, x ; Store the High byte of the address
|
|
Rts
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Inst_TmTask - install a timer task.
|
|
*
|
|
* Carry -> Set - Branch to cancel timer task
|
|
* Clear - execute install timer task
|
|
*
|
|
* Register A - Low byte of parameter address
|
|
* Register X - High byte of the parameter address
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Inst_TmTask Proc
|
|
|
|
Export Get_TMPB, \
|
|
Init_Timer, \
|
|
Timer_ISR
|
|
|
|
Php
|
|
Sei
|
|
Bcs Can_TmTask ; Cancel timer task if carry is set
|
|
|
|
Sta AddressLo ; Store the parameter address
|
|
Stx AddressHi
|
|
|
|
Ldy #$00
|
|
Lda (AddressLo),y
|
|
Tax
|
|
Iny
|
|
Lda (AddressLo),y ; Get the low byte of the completion routine
|
|
Sta TMPBpcL,x
|
|
Iny
|
|
Lda (AddressLo),y ; Get the high byte of the completion routine
|
|
Sta TMPBpcH,x
|
|
Iny
|
|
Lda (AddressLo),y ; Get the low byte of the time value
|
|
Sta TMPBTimeL,x
|
|
Iny
|
|
Lda (AddressLo),y ; Get the high byte of the time value
|
|
Sta TMPBTimeH,x
|
|
Txa
|
|
Tay
|
|
Jsr StartTimer
|
|
|
|
Plp
|
|
Rts
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Can_TmTask
|
|
* Inputs: Y - TMPB index
|
|
* Outputs: none
|
|
* Destroys: A, X, Y, n, v, z, c
|
|
* Size: 100 bytes
|
|
* Cycles: 15 - if it is already completed
|
|
* 50 - if it is the only running timer
|
|
* 98 + StopTimer + StartTimer - if head, with successors
|
|
* 65..94+ - if not head
|
|
* Stack Usage: 2 bytes
|
|
* Calls: Stop, Start
|
|
* Called by:
|
|
*
|
|
* Function: Cancels the timer in the indicated TMPB, and removes it
|
|
* from the timer queue.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Can_TmTask
|
|
|
|
Lda TMPBcmplt,y ; Exit if the task has completed
|
|
Beq @Exit
|
|
|
|
Cpy tmReqQhead ; See if it is running
|
|
Bne @SrchQueue ; If not head of queue
|
|
|
|
Lda TMPBnext,y ; Check for successor
|
|
Bpl @HasSucc ; If not the only entry
|
|
|
|
Sta tmReqQhead ; Set head to nil
|
|
Lda #$00
|
|
Sta TMPBpcH,y ; Indicate that the routine is complete
|
|
Lda #Timer_Msk ; Disable timer interrupts
|
|
Trb Int_Mask ; Update the interrupt mask
|
|
Plp
|
|
Rts ; All done
|
|
|
|
@HasSucc Ldx tmReqQhead ; Get queue head
|
|
Jsr Stop ; Stop the head timer
|
|
Ldy tmReqQhead ; Get index of entry to dequeue
|
|
Lda #$00
|
|
Sta TMPBpcH,y ; Indicate that the routine is complete
|
|
Ldx TMPBnext,y ; Get index of successor
|
|
Stx tmReqQhead ; Successor is new head
|
|
Jsr @AdjTime ; Adjust the delay time
|
|
Jsr Start ; Start new head timer
|
|
Plp
|
|
Rts
|
|
|
|
@SrchQueue Lda tmReqQhead ; Index tmReqQhead
|
|
Sty tmIndexTemp
|
|
|
|
@loop Tax ; Load index register
|
|
Bmi @exit ; Exit if end of list (nil)
|
|
|
|
Lda TMPBnext,x ; Get index to next TMPB
|
|
Cpa tmIndexTemp ; Check for a match
|
|
Bne @loop ; Loop if not a match
|
|
|
|
Lda TMPBnext,y ; Prepare to un-link entry
|
|
Sta TMPBnext,x ; Finish unlinking it
|
|
Bmi @exit ; If entry has no successor
|
|
|
|
Tax
|
|
@AdjTime Clc ; Clear carry for addition
|
|
lda TMPBtimeL,x ; TMPBtime,x := TMPBtime,x + TMPBtime,y
|
|
Adc TMPBtimeL,y
|
|
Sta TMPBtimeL,x
|
|
Lda TMPBtimeH,x
|
|
Adc TMPBtimeH,y
|
|
Sta TMPBtimeH,x
|
|
@exit Plp
|
|
Rts ; All done
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: StartTimer
|
|
* Inputs: Y - TMPB index
|
|
* Outputs: none
|
|
* Destroys: A, X, Y, n, v, z, c
|
|
* Size: 114 bytes
|
|
* Cycles: 39 + Start - if timer queue was empty
|
|
* 142 + Stop + StartTimer +..., otherwise
|
|
* Stack Usage: 2 bytes
|
|
* Calls: Stop, Start
|
|
* Called by:
|
|
*
|
|
* Function: Starts running the timer in the TMPB.
|
|
* The timeH and timeL fields of the TMPB must be filled in prior
|
|
* to this call.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
StartTimer
|
|
Lda #$ff
|
|
Sta TMPBcmplt,y ; Completion state is active
|
|
Ldx tmReqQhead ; Get index of head of list
|
|
Bpl @QNotEmpty ; If queue already has entries
|
|
|
|
Sty tmReqQhead ; New entry is queue head
|
|
Lda #nil ; Mark this entry as end of list
|
|
Sta TMPBnext,y ; Indicate no successor
|
|
Lda #Timer_Msk ; Enable timer interrupts
|
|
Tsb Int_Mask ; Update the interrupt mask
|
|
Tya ; Get TMPB index
|
|
Tax ; Setup TMPB index for StartTimer
|
|
Bra Start ; Start the timer and return
|
|
|
|
@QNotEmpty Jsr Stop ; Stop the currently running timer
|
|
|
|
Ldx tmReqQhead ; Index tmReqQhead
|
|
@loop Stx tmIndexTemp ; Remember index of previous TMPB
|
|
Lda TMPBnext,x ; Get index to next TMPB
|
|
Tax ; Load index register
|
|
Bmi @exit ; Exit if end of list (nil)
|
|
|
|
Sec ; Set carry for subtract
|
|
Lda TMPBtimeL,y ; TMPBtime,y := TMPBtime,y - TMPBtime,x
|
|
Sbc TMPBtimeL,x
|
|
Sta TMPBtimeL,y
|
|
Lda TMPBtimeH,y
|
|
Sbc TMPBtimeH,x
|
|
Sta TMPBtimeH,y
|
|
Bpl @loop ; Loop until TMPBtime,y is < 0
|
|
|
|
Clc ; Clear carry for addition
|
|
Lda TMPBtimeL,y ; TMPBtime,y := TMPBtime,y + TMPBtime,x
|
|
Adc TMPBtimeL,x
|
|
Sta TMPBtimeL,y
|
|
Lda TMPBtimeH,y
|
|
Adc TMPBtimeH,x
|
|
Sta TMPBtimeH,y
|
|
Sec ; Set carry for subtract
|
|
Lda TMPBtimeL,x ; TMPBtime,x := TMPBtime,x - TMPBtime,y
|
|
Sbc TMPBtimeL,y
|
|
Sta TMPBtimeL,x
|
|
Lda TMPBtimeH,x
|
|
Sbc TMPBtimeH,y
|
|
Sta TMPBtimeH,x
|
|
@exit Txa ; Prepare to store x
|
|
Sta TMPBnext,y ; Add rest of list after new entry
|
|
Ldx tmIndexTemp ; Get index of entry before new one
|
|
Tya ; Prepare to store y
|
|
Sta TMPBnext,x ; Add new entry after beginning of list
|
|
Ldx tmReqQhead ; Get index of head of list
|
|
*
|
|
* Falls into Start, which follows below
|
|
* bra Start ; Start the timer for the list head
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Start
|
|
* Inputs: X - tmReqQhead TMPB index
|
|
* Outputs: none
|
|
* Destroys: A, n, v, z, c
|
|
* Size: 75 bytes
|
|
* Cycles: 26 - when timer in range $8000..$FFFF
|
|
* 33 - when timer in range $0000..$007F
|
|
* 40 - when timer in range $0080..$00FF
|
|
* 43/49 - when timer in range $0100..$7FFF
|
|
* Stack Usage: 2 bytes
|
|
* Calls: none
|
|
* Called by: StartTmr, Timer_Int, CancelTmr
|
|
*
|
|
* Function: Loads the hardware timer with the amount of time remaining
|
|
* for the timer at the head of the timer queue, or $0080 if
|
|
* the amount is less than 1, or $7F80 if the value is greater
|
|
* than $007F. Adjusts the value in the TMPB to reflect the
|
|
* value loaded into the hardware time. The timer is then
|
|
* started and will interrupt when it reaches zero.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Start
|
|
|
|
Lda TMPBtimeH,x ; Get high bits
|
|
Bne @OORangeH ; If way too big
|
|
|
|
Lda TMPBtimeL,x ; Get low bits
|
|
Bmi @OORangeL ; Not in range $0000..$007F
|
|
|
|
Stz TMPBtimeL,x ; Clear low bits
|
|
Sta TmCnt_Hi ; Start the counter
|
|
Lda #$FF ; Set latch to $FF to re-load timer
|
|
Sta TmLatch_Hi ; with $FF when timer runs out
|
|
Rts
|
|
|
|
@OORangeH Bmi @OORangeNeg ; If negative
|
|
Lda #$7F ; Load timer with $7F80
|
|
Sta TmCnt_Hi ; Start the counter
|
|
Lda #$FF ; Set latch to $FF to re-load timer
|
|
Sta TmLatch_Hi ; with $FF when timer runs out
|
|
Sec ; Set carry for subtract
|
|
Lda TMPBtimeL,x ; Get low bits
|
|
Sbc #$7F ; TMPBtime,x := TMPBtime,x - $007F
|
|
Sta TMPBtimeL,x
|
|
Bcs @exit ; If no borrow
|
|
|
|
Dec TMPBtimeH,x ; Adjust for borrow
|
|
@exit Rts
|
|
|
|
|
|
@OORangeNeg Stz TmCnt_Hi ; Use $0080 if negative
|
|
Lda #$FF ; Set latch to $FF to re-load timer
|
|
Sta TmLatch_Hi ; with $FF when timer runs out
|
|
Rts
|
|
|
|
@OORangeL Sec ; Set carry for subtract
|
|
Sbc #$7F ; TMPBtime,x := TMPBtime,x - $007F
|
|
Sta TMPBtimeL,x
|
|
Lda #$7F ; Load timer with $7F80
|
|
Sta TmCnt_Hi ; Start the counter
|
|
Lda #$FF ; Set latch to $FF to re-load timer
|
|
Sta TmLatch_Hi ; with $FF when timer runs out
|
|
Rts
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Stop
|
|
* Inputs: X - tmReqQhead TMPB index
|
|
* Outputs: n - indicates sign of TMPBtimer,x
|
|
* Destroys: A, v, z, c
|
|
* Size: 41 bytes
|
|
* Cycles: 38..40 + 9 if close to rollover
|
|
* Stack Usage: 2 bytes
|
|
* Calls: none
|
|
* Called by: StartTimer, Timer_Int, CancelTmr
|
|
*
|
|
* Function: Reads the hardware timer, and updates the head of the
|
|
* timer queue to reflect the actual amount of time remaining.
|
|
* It also returns an indication of the sign of the timer
|
|
* remaining for the queue head.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Stop
|
|
Lda TmCnt_Lo ; Read low byte of counter
|
|
Cpa #$10 ; See if close to rollover
|
|
Blt Stop ; Wait if close to rollover
|
|
|
|
Lda TmCnt_Hi ; Read high byte of counter
|
|
Clc ; Clear carry for addition
|
|
Bpl @ZeroExt ; If high byte was positive
|
|
|
|
Adc TMPBtimeL,x ; Add to timer remaining for the
|
|
Sta TMPBtimeL,x ; Timer at head of the timer list
|
|
Bcs @TestSign ; If no borrow from high byte
|
|
|
|
Dec TMPBtimeH,x ; Adjust high byte
|
|
|
|
@TestSign Lda TMPBtimeH,x
|
|
Rts ; All done
|
|
|
|
@ZeroExt Adc TMPBtimeL,x ; Add to timer remaining for the
|
|
Sta TMPBtimeL,x ; Timer at head of the timer list
|
|
Bcc @TestSign ; If no carry into high byte
|
|
|
|
Inc TMPBtimeH,x ; Adjust high byte
|
|
Rts ; All done
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Timer_ISR
|
|
* Inputs: none
|
|
* Outputs: none
|
|
* Destroys: A, X, Y, n, v, z, c
|
|
* Size: 60 bytes
|
|
* Cycles: 15 + Stop + Start, if timer not fully expired
|
|
* 60 + Stop, if expired, no more queued,
|
|
* no completion handler
|
|
* 84 + Stop, if expired, no more queued,
|
|
* has completion handler
|
|
* 62 + Stop + Start, if expired, and more queued
|
|
* no completion handler
|
|
* 86 + Stop + Start, if expired, and more queued
|
|
* has completion handler
|
|
* Stack Usage: 2 bytes
|
|
* Calls: Stop, Start, TMPB completion handler
|
|
* Called by: Interrupt
|
|
*
|
|
* Function: Handles timer interrupts, determines if the timer at the
|
|
* head of the timer queue has fully expired, and calls the
|
|
* completion handler for that timer if one exists. Start
|
|
* any remaining timers on the timer queue.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Timer_ISR
|
|
|
|
Ldx tmReqQhead ; Get index of head of list
|
|
Bmi @unknownint ; If timer queue is empty
|
|
|
|
Jsr Stop ; Stop timer and update queue head
|
|
Bpl Start ; If not expired, restart timer and exit
|
|
|
|
Phx ; Save pointer to dequeued entry
|
|
Stz TMPBcmplt,x ; Zero completion routine indicating done.
|
|
Lda TMPBnext,x ; Get next queue entry
|
|
Sta tmReqQhead ; Dequeue head
|
|
Bmi @DisTimer ; If no successor disable timer
|
|
|
|
Tax ; Get pointer to new queue head
|
|
Jsr Start ; Start next timer
|
|
Bra @RunCmplt ; Go run the completion routine
|
|
|
|
@DisTimer Lda #Timer_Msk ; Disable timer interrupts
|
|
Trb Int_Mask ; Update the interrupt mask
|
|
|
|
@RunCmplt Plx ; Restore pointer to dequeued entry
|
|
Lda TMPBpcL,x
|
|
Sta AddressLo
|
|
Lda TMPBpcH,x
|
|
Sta AddressHi
|
|
Jmp (AddressLo) ; Jump to the completion routine. The
|
|
; return address is on the stack
|
|
|
|
@unknownint Brk ; TMR interrupt with empty Q
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Get_TMPB
|
|
* Inputs: none
|
|
* Outputs: Y - TMPB index
|
|
* n - 1 if no free TMPBs available
|
|
* Destroys: A, Y, n, z
|
|
* Size: 22 bytes
|
|
* Cycles: 34 if BCPB was available
|
|
* 13 if BCPB was not available
|
|
* Stack Usage: 2 bytes
|
|
* Calls: none
|
|
* Called by:
|
|
*
|
|
* Function: Returns the index of a free TiMer Parameter Block
|
|
* (TMPB), and removes it from the list of free TMPBs.
|
|
* A flag is returned to indicate if a free TMPB existed.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Get_TMPB
|
|
|
|
Php
|
|
Sei
|
|
Bcs Free_TMPB
|
|
|
|
Ldy availhead ; Get the available list head
|
|
Bmi @exit ; If list empty
|
|
|
|
Lda TMPBnext,y ; Get successor
|
|
Sta availhead ; Update list head
|
|
@exit Plp
|
|
Rts ; All done
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Free_TMPB
|
|
* Inputs: Y - TMPB index
|
|
* Outputs: none
|
|
* Destroys: A, n, z, c
|
|
* Size: 19 bytes
|
|
* Cycles: 27
|
|
* Stack Usage: 2 bytes
|
|
* Calls: none
|
|
* Called by:
|
|
*
|
|
* Function: Adds the TMPB that was passed in, to the list of available
|
|
* TMPBs. If the TMPB is currently active, the system crashes
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Free_TMPB
|
|
|
|
Lda availhead ; Get the available list head
|
|
Sta TMPBnext,y ; Link it to end of new entry
|
|
Sty availhead ; New entry is new list head
|
|
Lda TMPBstate,y ; Check the state
|
|
|
|
Plp
|
|
Rts ; All done
|
|
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Routine: Init_Timer
|
|
* Inputs: none
|
|
* Outputs: none
|
|
* Destroys: A, X, n, v, z, c
|
|
* Size: 40 bytes
|
|
* Cycles: 45 + 14 per TMPB
|
|
* Stack Usage: 2 bytes
|
|
* Calls: none
|
|
* Called by: Reset
|
|
*
|
|
* Function: Initializes Timer data structures.
|
|
*
|
|
* NOTE: This routine must be called with interrupts DISABLED.
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Init_Timer
|
|
|
|
last Equ TMPBsize*(MaxTMPBs-1)
|
|
|
|
Lda #0 ; Index of first TMPB
|
|
Sta availhead ; All TMPBs are now available
|
|
Clc ; Clear carry for additions
|
|
@loop Tax ; Setup index register
|
|
Adc #TMPBsize ; Point to next TMPB
|
|
Sta TMPBnext,x ; Have previous point to next
|
|
Stz TMPBcmplt,x ; Zero the completion state
|
|
Cpa #last ; Check for last TMPB
|
|
Blt @loop ; Process next TMPB
|
|
|
|
Lda #nil ; Mark empty lists
|
|
Sta last+TMPBnext ; Mark end of free list
|
|
Sta tmReqQhead ; Request queue head is empty
|
|
Lda #Timer_Msk ; Disable timer interrupts
|
|
Trb Int_Mask ; Update the interrupt mask
|
|
Lda #One_Shot ; Prepare to force one shot mode
|
|
Trb TD_Cntl ; Reset the bit
|
|
Lda #$80 ; Low byte latch is a constant
|
|
Sta TmLatch_Lo ; Initialize the low timer latch
|
|
Rts
|
|
|
|
*
|
|
* TiMer Parameter Block (TMPB)
|
|
*
|
|
|
|
|
|
TMPBnext DS.B 1 ; Next TMPB on list
|
|
TMPBstate DS.B 1 ; Request state
|
|
|
|
TMPBtimeH DS.B 1 ; High byte of 2 byte timer
|
|
TMPBtimeL DS.B 1 ; Low byte of 2 byte timer
|
|
|
|
TMPBpcH DS.B 1 ; Completion routine PC high
|
|
TMPBpcL DS.B 1 ; Completion routine PC low
|
|
TMPBcmplt DS.B 1 ; Completion state (0 = complete 0<> means running)
|
|
|
|
DS.B TMPBsize*(MaxTMPBs-1) ; Allocate remaining TMPBs
|
|
|
|
availhead DS.B 1 ; Head of TMPB free list
|
|
tmReqQhead DS.B 1 ; Head of TMPB request queue
|
|
tmIndexTemp DS.B 1 ; Temporary storage for index
|
|
|
|
endproc
|
|
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*
|
|
* Global Data Area for kernel
|
|
*
|
|
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Global_Kernel Proc
|
|
|
|
Export ISR_table, \
|
|
ISR_Decode, \
|
|
MsgNumTimes2, \
|
|
MsgNumTimes32, \
|
|
Cmd_Table, \
|
|
Cmd_JmpTable, \
|
|
SCC_Table, \
|
|
TxMsgSignal, \
|
|
RxMsgSignal, \
|
|
tPrev, \
|
|
tNext, \
|
|
tSp, \
|
|
tEvent, \
|
|
tWait, \
|
|
tTNxt, \
|
|
tTEvent, \
|
|
tTISR, \
|
|
VerInfo
|
|
|
|
Import Remv_RxMsgSgn, \
|
|
Remv_TxCmplSgn, \
|
|
Remv_ISR, \
|
|
Remv_SCC_ISR, \
|
|
Inst_Task, \
|
|
Rel_Task, \
|
|
Ev_Wait, \
|
|
Ev_Signal, \
|
|
Ev_Reset, \
|
|
TEv_Reset, \
|
|
Get_TMPB, \
|
|
Inst_TmTask, \
|
|
Reg_Ver, \
|
|
NullRxMsgSgn, \
|
|
NullTxCmplSgn, \
|
|
UnKnown_Int, \
|
|
UnKnown_SCCInt, \
|
|
Dsptch_KRxMsg
|
|
|
|
*
|
|
* TCB - Task Control Block
|
|
*
|
|
* Each active task has a TCB entry in the TCB linked list.
|
|
* The List starts out with only one active task which is the kernel task.
|
|
* The Kernel is always active.
|
|
*
|
|
|
|
tPrev DC.B Kern_ID ; Previous Task
|
|
tNext DC.B Kern_ID ; Next task ID (initially 0 for Kernel Task only)
|
|
tSp DC.B $FF ; Stack Pointer for this task
|
|
tEvent DC.B $80 ; Event flags (bit 7 event always active)
|
|
tWait DC.B $00 ; Event Mask (Wait for RxMsg1 event always)
|
|
|
|
TCB_Size Equ *-tPrev
|
|
|
|
DC.B $00 ; tPrev ChanA Task
|
|
DC.B $00 ; tNext ChanA Task
|
|
DC.B $a9 ; tSp ChanA Task
|
|
DC.B $80 ; tEvent ChanA Task (bit 7 event always active)
|
|
DC.B $00 ; tWait ChanA Task
|
|
|
|
DC.B $00 ; tPrev ChanB Task
|
|
DC.B $00 ; tNext ChanB Task
|
|
DC.B $54 ; tSp ChanB Task
|
|
DC.B $80 ; tEvent ChanB Task (bit 7 event always active)
|
|
DC.B $00 ; tWait ChanB Task
|
|
|
|
*
|
|
* 6502 and SCC interrupt service routine vector tables
|
|
*
|
|
|
|
ISR_table DC.W Unknown_Int ; 0 priority
|
|
DC.W UnKnown_Int ; DMA1
|
|
DC.W Unknown_Int ; DMA2
|
|
DC.W UnKnown_Int ; SCC
|
|
DC.W UnKnown_Int ; Timer
|
|
DC.W UnKnown_Int ; Host
|
|
|
|
ISR_Decode DC.B 0 ; %00000000
|
|
DC.B 0
|
|
DC.B DMA1 ; %00000010
|
|
DC.B 0
|
|
DC.B DMA2 ; %00000100
|
|
DC.B 0
|
|
DC.B DMA2 ; %00000110
|
|
DC.B 0
|
|
DC.B SCC ; %00001000
|
|
DC.B 0
|
|
DC.B SCC ; %00001010
|
|
DC.B 0
|
|
DC.B SCC ; %00001100
|
|
DC.B 0
|
|
DC.B SCC ; %00001110
|
|
DC.B 0
|
|
DC.B HOST ; %00010000
|
|
DC.B 0
|
|
DC.B DMA1 ; %00010010
|
|
DC.B 0
|
|
DC.B DMA2 ; %00010100
|
|
DC.B 0
|
|
DC.B DMA2 ; %00010110
|
|
DC.B 0
|
|
DC.B SCC ; %00011000
|
|
DC.B 0
|
|
DC.B SCC ; %00011010
|
|
DC.B 0
|
|
DC.B SCC ; %00011100
|
|
DC.B 0
|
|
DC.B SCC ; %00011110
|
|
DC.B 0
|
|
DC.B TIMER ; %00100000
|
|
DC.B 0
|
|
DC.B DMA1 ; %00100010
|
|
DC.B 0
|
|
DC.B DMA2 ; %00100100
|
|
DC.B 0
|
|
DC.B DMA2 ; %00100110
|
|
DC.B 0
|
|
DC.B SCC ; %00101000
|
|
DC.B 0
|
|
DC.B SCC ; %00101010
|
|
DC.B 0
|
|
DC.B SCC ; %00101100
|
|
DC.B 0
|
|
DC.B SCC ; %00101110
|
|
DC.B 0
|
|
DC.B TIMER ; %00110000
|
|
DC.B 0
|
|
DC.B DMA1 ; %00110010
|
|
DC.B 0
|
|
DC.B DMA2 ; %00110100
|
|
DC.B 0
|
|
DC.B DMA2 ; %00110110
|
|
DC.B 0
|
|
DC.B SCC ; %00111000
|
|
DC.B 0
|
|
DC.B SCC ; %00111010
|
|
DC.B 0
|
|
DC.B SCC ; %00111100
|
|
DC.B 0
|
|
DC.B SCC ; %00111110
|
|
|
|
SCC_table DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
DC.W UnKnown_SCCInt
|
|
|
|
*
|
|
* Transmit message Request Completion signal routines
|
|
*
|
|
|
|
TxMsgSignal DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
DC.W NullTxCmplSgn
|
|
|
|
*
|
|
* Receive new message signal routines
|
|
*
|
|
|
|
RxMsgSignal DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
DC.W NullRxMsgSgn
|
|
|
|
*
|
|
* Kernel Command Table - this contains the addresses of the kernel commands
|
|
* which will later be tranfered to the zero page,
|
|
* Up to twenty commands are supported. They must be
|
|
* in order of their command numbers.
|
|
*
|
|
|
|
Cmd_Table DC.W Remv_RxMsgSgn
|
|
DC.W Remv_TxCmplSgn
|
|
DC.W Remv_ISR
|
|
DC.W Remv_SCC_ISR
|
|
DC.W Inst_Task
|
|
DC.W Rel_Task
|
|
DC.W Ev_Wait
|
|
DC.W Ev_Signal
|
|
DC.W Ev_Reset
|
|
DC.W Reset_Chan
|
|
DC.W Get_TMPB
|
|
DC.W Inst_TmTask
|
|
DC.W Reg_Ver
|
|
DS.W 7
|
|
|
|
Cmd_JmpTable Jmp (Krn_CmdBase+(RemvRxMsg*2))
|
|
Jmp (Krn_CmdBase+(RemvTxCmpl*2))
|
|
Jmp (Krn_CmdBase+(RemvISR*2))
|
|
Jmp (Krn_CmdBase+(RemvSCCISR*2))
|
|
Jmp (Krn_CmdBase+(InstTask*2))
|
|
Jmp (Krn_CmdBase+(RelTask*2))
|
|
Jmp (Krn_CmdBase+(WaitEvent*2))
|
|
Jmp (Krn_CmdBase+(SignalTask*2))
|
|
Jmp (Krn_CmdBase+(ResetEvent*2))
|
|
Jmp (Krn_CmdBase+(ResetChan*2))
|
|
Jmp (Krn_CmdBase+(GetTMPB*2))
|
|
Jmp (Krn_CmdBase+(InstTmTask*2))
|
|
Jmp (Krn_CmdBase+(RegVer*2))
|
|
|
|
MsgNumTimes2 DC.B 0,2,4,6,8,10,12,14
|
|
|
|
MsgNumTimes32 DC.B 0,32,64,96,128,160,192,224
|
|
|
|
*
|
|
* Version Information
|
|
*
|
|
|
|
String Pascal
|
|
|
|
VerInfo DC.B $02
|
|
DC.B 'Enhanced Communications Controller'
|
|
DC.B 'Version 1.0A0 © Apple Computer Inc. 1988'
|
|
|
|
DCB.W 512, $00FF ; Needed for AURORA ROM resource padding
|
|
|
|
|
|
endproc
|
|
|
|
|
|
End
|
|
|
|
|