mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-24 17:32:59 +00:00
1255 lines
41 KiB
Plaintext
1255 lines
41 KiB
Plaintext
|
;
|
||
|
; File: IOPKernel.aii
|
||
|
;
|
||
|
; Contains: I/O Processor Kernel for SWIM IOP.
|
||
|
;
|
||
|
; Written by: Gary G. Davidian
|
||
|
;
|
||
|
; Copyright: © 1987-1990 by Apple Computer, Inc., all rights reserved.
|
||
|
;
|
||
|
; Change History (most recent first):
|
||
|
;
|
||
|
; <2> 1/2/90 GGD NEEDED FOR ZONE-5: Adding call to IdleLoop to run any IdleTasks
|
||
|
; that drivers may have.
|
||
|
; <1.5> 11/19/89 GGD Needed for Zone-5 Expanded the code that is just below the
|
||
|
; interrupt vectors to make saving and restoring state even easier
|
||
|
; (for fremont diags).
|
||
|
; <1.4> 9/21/89 GGD Increased the number of BCPBs from 4 to 6. Return zero (equal)
|
||
|
; in BCPBcompRel if operation is not bcCompare. Moved the
|
||
|
; PatchWait loop to a fixed address below the interrupt vectors to
|
||
|
; make it easier for the diagnostics to save and restore the IOP
|
||
|
; state.
|
||
|
; <1.3> 7/8/89 CCH Added EASE comments to file.
|
||
|
; <•1.2> 6/15/89 GGD Updated to use equates for the latest rev of the IOP chip,
|
||
|
; re-formated tab stops in source.
|
||
|
; <1.1> 2/9/89 GGD Fixed bug in timer code that could cause a timer to go off much
|
||
|
; later than requested.
|
||
|
; <1.0> 11/9/88 CCH Adding to EASE.
|
||
|
;
|
||
|
; To Do:
|
||
|
;
|
||
|
eject
|
||
|
title 'IOP Kernel - I/O Processor Kernel for SWIM IOP'
|
||
|
|
||
|
machine m65C02
|
||
|
codechk off
|
||
|
datachk off
|
||
|
longa off
|
||
|
longi off
|
||
|
print nomdir
|
||
|
include 'IOPDefs.aii'
|
||
|
|
||
|
import InitDrivers
|
||
|
import RunIdleTasks
|
||
|
import HandleSCCInt
|
||
|
import HandleDMA1Int
|
||
|
import HandleDMA2Int
|
||
|
import HandleXMTMsg2
|
||
|
import HandleXMTMsg3
|
||
|
import HandleXMTMsg4
|
||
|
import HandleXMTMsg5
|
||
|
import HandleXMTMsg6
|
||
|
import HandleXMTMsg7
|
||
|
import HandleRCVMsg1
|
||
|
import HandleRCVMsg2
|
||
|
import HandleRCVMsg3
|
||
|
import HandleRCVMsg4
|
||
|
import HandleRCVMsg5
|
||
|
import HandleRCVMsg6
|
||
|
import HandleRCVMsg7
|
||
|
|
||
|
entry NonMaskable
|
||
|
entry Reset
|
||
|
entry UnknownInt
|
||
|
entry PatchWait
|
||
|
entry PatchDone
|
||
|
entry HandleHSTInt
|
||
|
entry HandleTMRInt
|
||
|
entry Interrupt
|
||
|
entry InitBlockCopyDriver
|
||
|
entry InitDelayTimerDriver
|
||
|
entry IntDecode
|
||
|
entry XMTMsgDecode
|
||
|
entry RCVMsgDecode
|
||
|
entry MsgNumTimes2
|
||
|
entry HandleXMTMsg1
|
||
|
entry StartTimer
|
||
|
entry StopTimer
|
||
|
title 'IOP Kernel - Initialization'
|
||
|
|
||
|
seg 'vectors'
|
||
|
Vectors proc
|
||
|
entry PatchWait
|
||
|
|
||
|
; This code is at a fixed address ($7FE5) below the vectors to allow
|
||
|
; context saving and restoring of a running IOP (used by diagnostics).
|
||
|
|
||
|
RestoreState ldx #$FF ; stack base is $01FF
|
||
|
txs ; initialize stack
|
||
|
lda #'A' ; initialize a-register
|
||
|
ldy #'Y' ; initialize y-register
|
||
|
PatchWait sei ; sync up with host, with interrupts disabled
|
||
|
ldx #MessageCompleted ; let host know we can now be patched
|
||
|
stx PatchReq ; update the patch request message state
|
||
|
@WaitLoop ldx PatchReq ; wait for patching to be completed
|
||
|
assert Idle=$00
|
||
|
bne @WaitLoop ; loop until patching complete (until Idle)
|
||
|
jmp PatchDone ; patching done, enable interrupts
|
||
|
|
||
|
; Interrupt Vectors follow at $7FFA
|
||
|
|
||
|
dc.w NonMaskable ; Non-Maskable Interrupt
|
||
|
dc.w Reset ; processor reset
|
||
|
dc.w Interrupt ; Interrupt Request
|
||
|
|
||
|
endproc
|
||
|
|
||
|
; Reset and initialization code
|
||
|
|
||
|
seg 'code'
|
||
|
Reset proc ; Reset Handler
|
||
|
ldx #$FF ; stack base is $01FF
|
||
|
txs ; initialize stack
|
||
|
stz DMA1Control ; initialize DMA1
|
||
|
stz DMA2Control ; initialize DMA2
|
||
|
stz TimerDPLLctl ; initialize TimerHostDPLL
|
||
|
stz HostControl ; initialize Host Control reg
|
||
|
stz TimerCounterL ; initialize TimerCounterL
|
||
|
stz TimerCounterH ; initialize TimerCounterH
|
||
|
stx InterruptMask ; allow all interrupts
|
||
|
stx InterruptReg ; clear all interrupts
|
||
|
|
||
|
jsr InitBlockCopyDriver
|
||
|
jsr InitDelayTimerDriver
|
||
|
jsr InitDrivers ; initialize the drivers
|
||
|
|
||
|
lda #'A' ; initialize a-register
|
||
|
ldy #'Y' ; initialize y-register
|
||
|
entry PatchDone
|
||
|
PatchDone cli ; enable interrupts
|
||
|
IdleLoop ldx #'X' ; initialize x-register
|
||
|
cpa #'A' ; see if a-reg got destroyed
|
||
|
bne IdleRegError ; Crash if a-reg changed
|
||
|
cpy #'Y' ; see if y-reg got destroyed
|
||
|
bne IdleRegError ; Crash if y-reg changed
|
||
|
cpx #'X' ; see if x-reg got destroyed
|
||
|
bne IdleRegError ; Crash if x-reg changed
|
||
|
tsx ; get stack pointer
|
||
|
cpx #$FF ; should be at stack base
|
||
|
bne IdleStackError ; Crash if stack incorrect
|
||
|
stx Alive ; Let mom know we're still alive
|
||
|
jsr RunIdleTasks ; Run any idle loop tasks that drivers have
|
||
|
ldx PatchReq ; see if host wants to patch iop memory
|
||
|
assert Idle=$00
|
||
|
beq IdleLoop ; not a patch request (still Idle)
|
||
|
jmp PatchWait ; go to wait loop at fixed address
|
||
|
|
||
|
IdleRegError brk ; register destroyed while idle
|
||
|
|
||
|
IdleStackError brk ; stack became skewed while idle
|
||
|
|
||
|
endproc
|
||
|
title 'IOP Kernel - Interrupt Handlers'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: Interrupt
|
||
|
; Inputs: none
|
||
|
; Outputs: none
|
||
|
; Destroys: none
|
||
|
; Cycles: 50 + Interrupt Handler
|
||
|
; Calls: Interrupt Handler
|
||
|
; Called by: 65C02 Hardware
|
||
|
;
|
||
|
; Function: Decodes and prioritizes the pending interrupt, and calls
|
||
|
; the handler for the highest priority interrupt.
|
||
|
; This routine saves all register state, so that the
|
||
|
; interrupt handlers may use all registers. Interrupt
|
||
|
; handlers should return to this routine using an RTS
|
||
|
; instruction (don't use RTI).
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
Interrupt proc ; Interrupt Handler
|
||
|
|
||
|
; it takes 7 clocks for the processor get here after the last instruction
|
||
|
; finished executing. (pushing state for RTI to pop).
|
||
|
|
||
|
pha ; save reg A
|
||
|
phx ; save reg X
|
||
|
phy ; save reg Y
|
||
|
|
||
|
jsr CallIntHandler ; handle the interrupt
|
||
|
|
||
|
ply ; restore reg Y
|
||
|
plx ; restore reg X
|
||
|
pla ; restore reg A
|
||
|
rti ; restore PSW and PC
|
||
|
|
||
|
|
||
|
CallIntHandler ldx InterruptReg ; get the interrupts
|
||
|
jmp (IntDecode,x) ; run the interrupt handler
|
||
|
|
||
|
|
||
|
; Non-Maskable Interrupt Handler
|
||
|
entry NonMaskable
|
||
|
NonMaskable brk ; shouldn't be any non-maskable ints
|
||
|
|
||
|
|
||
|
; Unknown Interrupt Handler
|
||
|
|
||
|
entry UnknownInt
|
||
|
UnknownInt tsx ; get stack pointer
|
||
|
lda StackPage+6,x ; get the psw from the stack
|
||
|
bit #pswB ; test the b bit
|
||
|
bne BRKinterrupt ; if it was a BRK interrupt
|
||
|
rts ; no interrupt found, just ignore it
|
||
|
|
||
|
|
||
|
; If we received a BRK interrupt, we will just loop forever, scanning the stack
|
||
|
; so that we can read the stack on a logic analyzer.
|
||
|
|
||
|
BRKinterrupt tsx ; get the stack pointer
|
||
|
ScanStack inx ; point to next stack entry
|
||
|
beq BRKinterrupt ; scan the stack forever
|
||
|
lda StackPage,x ; get the stack data
|
||
|
bra ScanStack ; process next entry
|
||
|
|
||
|
endproc
|
||
|
|
||
|
; Interrupt decode and priority table.
|
||
|
; Interrupt priority order is as follows (highest to lowest)
|
||
|
; SCC, DMA1, DMA2, TMR, HST
|
||
|
; ; D D
|
||
|
; ; T H S M M
|
||
|
; ; M S C A A
|
||
|
; ; R T C 2 1
|
||
|
; ; I I I I I
|
||
|
; ; n n n n n
|
||
|
seg 'WordConsts' ; t t t t t
|
||
|
IntDecode proc ; ---------
|
||
|
dc.w UnknownInt ; 0 0 0 0 0
|
||
|
dc.w HandleDMA1Int ; 0 0 0 0 1
|
||
|
dc.w HandleDMA2Int ; 0 0 0 1 0
|
||
|
dc.w HandleDMA1Int ; 0 0 0 1 1
|
||
|
dc.w HandleSCCInt ; 0 0 1 0 0
|
||
|
dc.w HandleSCCInt ; 0 0 1 0 1
|
||
|
dc.w HandleSCCInt ; 0 0 1 1 0
|
||
|
dc.w HandleSCCInt ; 0 0 1 1 1
|
||
|
dc.w HandleHSTInt ; 0 1 0 0 0
|
||
|
dc.w HandleDMA1Int ; 0 1 0 0 1
|
||
|
dc.w HandleDMA2Int ; 0 1 0 1 0
|
||
|
dc.w HandleDMA1Int ; 0 1 0 1 1
|
||
|
dc.w HandleSCCInt ; 0 1 1 0 0
|
||
|
dc.w HandleSCCInt ; 0 1 1 0 1
|
||
|
dc.w HandleSCCInt ; 0 1 1 1 0
|
||
|
dc.w HandleSCCInt ; 0 1 1 1 1
|
||
|
dc.w HandleTMRInt ; 1 0 0 0 0
|
||
|
dc.w HandleDMA1Int ; 1 0 0 0 1
|
||
|
dc.w HandleDMA2Int ; 1 0 0 1 0
|
||
|
dc.w HandleDMA1Int ; 1 0 0 1 1
|
||
|
dc.w HandleSCCInt ; 1 0 1 0 0
|
||
|
dc.w HandleSCCInt ; 1 0 1 0 1
|
||
|
dc.w HandleSCCInt ; 1 0 1 1 0
|
||
|
dc.w HandleSCCInt ; 1 0 1 1 1
|
||
|
dc.w HandleTMRInt ; 1 1 0 0 0
|
||
|
dc.w HandleDMA1Int ; 1 1 0 0 1
|
||
|
dc.w HandleDMA2Int ; 1 1 0 1 0
|
||
|
dc.w HandleDMA1Int ; 1 1 0 1 1
|
||
|
dc.w HandleSCCInt ; 1 1 1 0 0
|
||
|
dc.w HandleSCCInt ; 1 1 1 0 1
|
||
|
dc.w HandleSCCInt ; 1 1 1 1 0
|
||
|
dc.w HandleSCCInt ; 1 1 1 1 1
|
||
|
|
||
|
endproc
|
||
|
title 'IOP Kernel - Host Interrupt Handler'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr HandleHSTInt
|
||
|
; Inputs: none
|
||
|
; Outputs: none
|
||
|
; Destroys: A, X, Y, n, v, z, c
|
||
|
; Cycles: 40 + 12*(XMTMsgMax+RCVMsgMax) + 28 per service routine called
|
||
|
; Calls: Message service routines
|
||
|
; Called by: Interrupt
|
||
|
;
|
||
|
; Function: Scans the transmit and receive message state lists looking
|
||
|
; for a message that needs service and calls the service
|
||
|
; routine for each message that needs service.
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
HandleHSTInt proc
|
||
|
lda #HSTINT ; get the host interrupt bit
|
||
|
sta InterruptReg ; clear the host interrupt
|
||
|
trb InterruptMask ; disable host interrupts
|
||
|
cli ; allow higher priority interrupts
|
||
|
|
||
|
; service Transmit Message Completed first
|
||
|
|
||
|
ldy XMTMsgMax ; get highest XMT message number
|
||
|
lda #MessageCompleted ; get the desired message state
|
||
|
XMTMsgLoop cpa XMTMsg1State-1,y ; see if the message completed
|
||
|
bne XMTMsgNext ; if no service needed
|
||
|
phy ; preserve loop counter
|
||
|
ldx MsgNumTimes2,y ; convert message number to table index
|
||
|
sei ; disable interrupts before calling handler
|
||
|
jsr CallXMTHandler ; call the handler
|
||
|
cli ; re-enable interrupts after handler returns
|
||
|
ply ; restore the message number
|
||
|
lda #MessageCompleted ; restore desired message state
|
||
|
XMTMsgNext dey ; point to previous message
|
||
|
bne XMTMsgLoop ; loop through all messages
|
||
|
|
||
|
; service Receive New Message Sent last
|
||
|
|
||
|
ldy RCVMsgMax ; get highest RCV message number
|
||
|
lda #NewMessageSent ; get the desired message state
|
||
|
RCVMsgLoop cpa RCVMsg1State-1,y ; see if message just arrived
|
||
|
bne RCVMsgNext ; if no service needed
|
||
|
phy ; preserve loop counter
|
||
|
ldx MsgNumTimes2,y ; convert message number to table index
|
||
|
sei ; disable interrupts before calling handler
|
||
|
jsr CallRCVHandler ; call the handler
|
||
|
cli ; re-enable interrupts after handler returns
|
||
|
ply ; restore the message number
|
||
|
lda #NewMessageSent ; restore desired message state
|
||
|
RCVMsgNext dey ; point to previous message
|
||
|
bne RCVMsgLoop ; loop through all messages
|
||
|
|
||
|
sei ; disable interrupts
|
||
|
lda #HSTINT ; get the host interrupt bit
|
||
|
tsb InterruptMask ; re-enable host interrupts
|
||
|
rts ; all done
|
||
|
|
||
|
|
||
|
CallXMTHandler jmp (XMTMsgDecode-2,x) ; go to the proper handler
|
||
|
|
||
|
CallRCVHandler jmp (RCVMsgDecode-2,x) ; go to the proper handler
|
||
|
|
||
|
endproc
|
||
|
|
||
|
; Transmit message Request Completed Handlers
|
||
|
|
||
|
seg 'WordConsts'
|
||
|
XMTMsgDecode proc
|
||
|
dc.w HandleXMTMsg1
|
||
|
dc.w HandleXMTMsg2
|
||
|
dc.w HandleXMTMsg3
|
||
|
dc.w HandleXMTMsg4
|
||
|
dc.w HandleXMTMsg5
|
||
|
dc.w HandleXMTMsg6
|
||
|
dc.w HandleXMTMsg7
|
||
|
|
||
|
|
||
|
; Receive message Start New Request Handlers
|
||
|
|
||
|
entry RCVMsgDecode
|
||
|
RCVMsgDecode dc.w HandleRCVMsg1
|
||
|
dc.w HandleRCVMsg2
|
||
|
dc.w HandleRCVMsg3
|
||
|
dc.w HandleRCVMsg4
|
||
|
dc.w HandleRCVMsg5
|
||
|
dc.w HandleRCVMsg6
|
||
|
dc.w HandleRCVMsg7
|
||
|
|
||
|
endproc
|
||
|
|
||
|
|
||
|
; Table for computing Message Number * 2
|
||
|
|
||
|
seg 'constants'
|
||
|
MsgNumTimes2 proc
|
||
|
dc.b 0,2,4,6,8,10,12,14
|
||
|
|
||
|
endproc
|
||
|
title 'IOP Kernel - Cross Processor Block Copy'
|
||
|
|
||
|
seg 'data'
|
||
|
BCPBdef record
|
||
|
; Block Copy Parameter Block (BCPB)
|
||
|
|
||
|
MaxBCPBs equ 6 ; (range 1..8) <1.4>
|
||
|
|
||
|
BCPBnext ds.b 1 ; next BCPB on list
|
||
|
BCPBstate ds.b 1 ; request state
|
||
|
|
||
|
; BCPB cmd, byteCount, hostAddr, and iopAddr must be filled in before calling MakeBCPBreq
|
||
|
export BCPBcmd
|
||
|
export BCPBbyteCountH
|
||
|
export BCPBbyteCountL
|
||
|
export BCPBhostAddrB0
|
||
|
export BCPBhostAddrB1
|
||
|
export BCPBhostAddrB2
|
||
|
export BCPBhostAddrB3
|
||
|
export BCPBiopAddrH
|
||
|
export BCPBiopAddrL
|
||
|
; BCPB CompRel is returned from host
|
||
|
export BCPBcompRel ; output
|
||
|
BCPBcmd ds.b 1 ; copy command
|
||
|
BCPBbyteCountH ds.b 1 ; byte count (high)
|
||
|
BCPBbyteCountL ds.b 1 ; byte count (low)
|
||
|
BCPBhostAddrB0 ds.b 1 ; HOST RAM Address (byte 0)
|
||
|
BCPBhostAddrB1 ds.b 1 ; HOST RAM Address (byte 1)
|
||
|
BCPBhostAddrB2 ds.b 1 ; HOST RAM Address (byte 2)
|
||
|
BCPBhostAddrB3 ds.b 1 ; HOST RAM Address (byte 3)
|
||
|
BCPBiopAddrH ds.b 1 ; IOP RAM Address (high)
|
||
|
BCPBiopAddrL ds.b 1 ; IOP RAM Address (low)
|
||
|
BCPBcompRel ds.b 1 ; compare result (relationship)
|
||
|
|
||
|
BCPBpcH ds.b 1 ; completion routine PC high
|
||
|
BCPBpcL ds.b 1 ; completion routine PC low
|
||
|
BCPBsaveA ds.b 1 ; reg A save
|
||
|
BCPBsaveX ds.b 1 ; reg X save
|
||
|
|
||
|
BCPBsize equ *-BCPBnext ; bytes per BCPB
|
||
|
ds.b BCPBsize*(MaxBCPBs-1) ; allocate remaining BCPBs
|
||
|
|
||
|
nilBCPB equ $FF ; nil index value
|
||
|
availBCPBhead ds.b 1 ; head of BCPB free list
|
||
|
bcReqQhead ds.b 1 ; head of BCPB request queue
|
||
|
bcReqQtail ds.b 1 ; tail of BCPB request queue
|
||
|
activeBCPB ds.b 1 ; BCPB currently active
|
||
|
endr
|
||
|
title 'IOP Kernel - Init Block Copy Driver'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr InitBlockCopyDriver
|
||
|
; Inputs: none
|
||
|
; Outputs: none
|
||
|
; Destroys: A, X, n, v, z, c
|
||
|
; Cycles: 31 + 14 per BCPB
|
||
|
; Calls: none
|
||
|
; Called by: Reset
|
||
|
;
|
||
|
; Function: Initializes the Block Copy data structures.
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED.
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
InitBlockCopyDriver proc
|
||
|
with BCPBdef
|
||
|
last equ BCPBsize*(MaxBCPBs-1)
|
||
|
|
||
|
lda #0 ; index of first BCPB
|
||
|
sta availBCPBhead ; all BCPBs are now available
|
||
|
clc ; clear carry for additions
|
||
|
loop tax ; setup index register
|
||
|
adc #BCPBsize ; point to next BCPB
|
||
|
sta BCPBnext,x ; have previous point to next
|
||
|
cpa #last ; check for last BCPB
|
||
|
blt loop ; process next BCPB
|
||
|
lda #nilBCPB ; mark empty lists
|
||
|
sta last+BCPBnext ; mark end of free list
|
||
|
sta bcReqQhead ; request queue head is empty
|
||
|
sta bcReqQtail ; request queue tail is empty
|
||
|
sta activeBCPB ; no BCPB currently active
|
||
|
rts
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Get BCPB'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr GetBCPB
|
||
|
; Inputs: none
|
||
|
; Outputs: Y - BCPB index
|
||
|
; n - 1 if no free BCPBs available
|
||
|
; Destroys: A, Y, n, z
|
||
|
; Cycles: 32 if BCPB was available
|
||
|
; 13 if BCPB was not available
|
||
|
; Calls: none
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; Function: Returns the index of a free Block Copy Parameter Block
|
||
|
; (BCPB), and removes it from the list of free BCPBs.
|
||
|
; A flag is returned to indicate if a free BCPB existed.
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED.
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
GetBCPB proc export ; Get a free BCPB
|
||
|
with BCPBdef
|
||
|
ldy availBCPBhead ; get the available list head
|
||
|
bmi exit ; if list empty
|
||
|
lda BCPBnext,y ; get successor
|
||
|
sta availBCPBhead ; update list head
|
||
|
lda #MessageCompleted ; initialize to completed state
|
||
|
sta BCPBstate,y ; initialize the state
|
||
|
lda #$00 ; indicate no completion address
|
||
|
sta BCPBpcH,y ; initialize the PC page
|
||
|
exit rts ; all done
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Free BCPB'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr FreeBCPB
|
||
|
; Inputs: Y - BCPB index
|
||
|
; Outputs: none
|
||
|
; Destroys: A, n, z, c
|
||
|
; Cycles: 27
|
||
|
; Calls: none
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; Function: Adds the BCPB that was passed in, to the list of available
|
||
|
; BCPBs. If the BCPB is currently active, the system crashes
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
FreeBCPB proc export
|
||
|
with BCPBdef
|
||
|
lda availBCPBhead ; get the available list head
|
||
|
sta BCPBnext,y ; link it to end of new entry
|
||
|
sty availBCPBhead ; new entry is new list head
|
||
|
lda BCPBstate,y ; check the state
|
||
|
cpa #newMessageSent ; see if copy was in progress
|
||
|
beq IsActive ; crash if was active
|
||
|
rts ; all done
|
||
|
IsActive brk ; FreeBCPB on avtive BCPB
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Wait BCPB done'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr WaitBCPBdone
|
||
|
; Inputs: Y - BCPB index
|
||
|
; Outputs: none
|
||
|
; Destroys: n, z, c
|
||
|
; Cycles: 54 - if has to wait
|
||
|
; 24 - if no wait needed
|
||
|
; Calls: none
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; Function: If the BCPB is complete, this routine simply returns to
|
||
|
; its caller. If it is not complete, then it returns to
|
||
|
; its callers caller, and will return to its caller when
|
||
|
; the BCPB request has completed.
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
WaitBCPBdone proc export
|
||
|
with BCPBdef
|
||
|
sta BCPBsaveA,y ; save reg a
|
||
|
lda BCPBstate,y ; get the state
|
||
|
cpa #MessageCompleted
|
||
|
beq exit ; if already done
|
||
|
txa ; prepare to save reg x
|
||
|
sta BCPBsaveX,y ; save reg x
|
||
|
lda BCPBpcH,y ; see if already waiting
|
||
|
bne MultipleWaits ; crash if already waiting
|
||
|
pla ; get pcL
|
||
|
sta BCPBpcL,y ; save pcL
|
||
|
pla ; get pcH
|
||
|
sta BCPBpcH,y ; save pcH
|
||
|
exit lda BCPBsaveA,y ; restore reg a
|
||
|
rts ; all done
|
||
|
MultipleWaits brk ; multiple waits on BCPB
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Check BCPB done'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr CheckBCPBdone
|
||
|
; Inputs: Y - BCPB index
|
||
|
; Outputs: z - 1 if BCPB request has completed
|
||
|
; z - 0 if BCPB request in progress, or not started
|
||
|
; Destroys: A, n, c
|
||
|
; Cycles: 12
|
||
|
; Calls: none
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; Function: Returns an indication as to whether the BCPB request has
|
||
|
; completed.
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
CheckBCPBdone proc export
|
||
|
with BCPBdef
|
||
|
lda BCPBstate,y ; get the state
|
||
|
cpa #MessageCompleted ; see if it is done
|
||
|
rts ; all done
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Make BCPB request'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr MakeBCPBreq
|
||
|
; Inputs: Y - BCPB index
|
||
|
; Outputs: none
|
||
|
; Destroys: A, n, v, z, c
|
||
|
; Cycles: 41 - if queue is not empty
|
||
|
; 41 - if queue is empty, but message slot busy
|
||
|
; 122 - if queue is empty, and message slot available
|
||
|
; Calls: none
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; Function: Sends the block copy request to the host precessor, or queues
|
||
|
; the request if the host processor is still processing
|
||
|
; a prior request. The cmd, byteCount, hostAddr, and iopAddr fields
|
||
|
; of the BCPB must be filled in prior to this call.
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
MakeBCPBreq proc export
|
||
|
with BCPBdef
|
||
|
lda #nilBCPB ; mark entry as end of list
|
||
|
sta BCPBnext,y ; BCPBnext := nil
|
||
|
lda #NewMessageSent ; get the new message state
|
||
|
sta BCPBstate,y ; mark the BCPB as active
|
||
|
lda bcReqQhead ; get request queue head
|
||
|
bmi QueueEmpty ; if nobody queued
|
||
|
tya ; save BCPB index
|
||
|
ldy bcReqQtail ; get old tail
|
||
|
sta BCPBnext,y ; link to end of queue
|
||
|
sta bcReqQtail ; establish new tail
|
||
|
rts ; return with BCPB queued
|
||
|
|
||
|
QueueEmpty lda BCxmtState ; check the message state
|
||
|
assert Idle=$00
|
||
|
beq SendReq ; if idle, send it now
|
||
|
sty bcReqQhead ; otherwise it is Qhead
|
||
|
sty bcReqQtail ; and Qtail
|
||
|
rts ; return with BCPB queued
|
||
|
|
||
|
SendReq lda BCPBcmd,y ; get the command
|
||
|
sta bcReqCmd ; move it to the request packet
|
||
|
lda BCPBbyteCountH,y ; copy the byte count
|
||
|
sta bcReqByteCount+0
|
||
|
lda BCPBbyteCountL,y
|
||
|
sta bcReqByteCount+1
|
||
|
lda BCPBhostAddrB0,y ; copy the host address
|
||
|
sta bcReqHostAddr+0
|
||
|
lda BCPBhostAddrB1,y
|
||
|
sta bcReqHostAddr+1
|
||
|
lda BCPBhostAddrB2,y
|
||
|
sta bcReqHostAddr+2
|
||
|
lda BCPBhostAddrB3,y
|
||
|
sta bcReqHostAddr+3
|
||
|
lda BCPBiopAddrH,y ; copy the IOP address
|
||
|
sta bcReqIopAddr+0
|
||
|
lda BCPBiopAddrL,y
|
||
|
sta bcReqIopAddr+1
|
||
|
lda #NewMessageSent ; get the new message state
|
||
|
sta BCxmtState ; set the new message state
|
||
|
sty activeBCPB ; remember who it was for
|
||
|
lda #NewMsgSentINT ; prepare to interrupt the host
|
||
|
sta HostControl ; interrupt the host
|
||
|
rts
|
||
|
title 'IOP Kernel - Handle XMT Message 1'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr HandleXMTMsg1
|
||
|
; Inputs: none
|
||
|
; Outputs: none
|
||
|
; Destroys: A, X, Y, n, v, z, c
|
||
|
; Cycles: 49 - nothing queued, no completion routine
|
||
|
; 73 - nothing queued, has completion routine
|
||
|
; 165 - entry was queued, no completion routine
|
||
|
; 189 - entry was queued, has completion routine
|
||
|
; + 3 clocks if bcCompare
|
||
|
; Calls: SendReq, BCPB completion handler
|
||
|
; Called by: HandleHSTInt
|
||
|
;
|
||
|
; Function: Dequeues and sends the next block copy request, if any
|
||
|
; to the host precessor and then calls the completion
|
||
|
; routine, if any, for the request that had just completed.
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
entry HandleXMTMsg1 ; a block copy has completed
|
||
|
HandleXMTMsg1
|
||
|
assert Idle=$00
|
||
|
stz BCxmtState ; Acknowledge the message
|
||
|
ldy activeBCPB ; find who completed
|
||
|
lda BCPBcmd,y ; get the command that completed
|
||
|
assert (bcIOPtoHOST>>1)=0
|
||
|
assert (bcHOSTtoIOP>>1)=0
|
||
|
assert (bcCompare>>1)<>0
|
||
|
lsr a ; test for bcCompare
|
||
|
beq @compRelDone ; if not compare, return zero result (equal)
|
||
|
lda bcReqCompRel ; get the compare result
|
||
|
@compRelDone sta BCPBcompRel,y ; return it in the BCPB
|
||
|
lda #MessageCompleted ; mark the BCPB as completed
|
||
|
sta BCPBstate,y ; mark the BCPB as done
|
||
|
lda bcReqQhead ; see if anything to be sent
|
||
|
bmi CheckCompletion ; if nothing queued
|
||
|
phy ; save completed BCPB
|
||
|
tay ; point to queued BCPB
|
||
|
lda BCPBnext,y ; get next in queue
|
||
|
sta bcReqQhead ; and make it the new head
|
||
|
jsr SendReq ; send old queue head request
|
||
|
ply ; restore completed BCPB
|
||
|
CheckCompletion lda BCPBpcH,y ; get high byte of comp. addr
|
||
|
beq NoComp ; if no completion routine
|
||
|
pha ; restore return pcH
|
||
|
lda BCPBpcL,y ; get low byte of comp. addr
|
||
|
pha ; restore return pcL
|
||
|
lda #$00 ; indicate no completion address
|
||
|
sta BCPBpcH,y ; to indicate that the wait completed
|
||
|
lda BCPBsaveA,y ; restore reg-a
|
||
|
ldx BCPBsaveX,y ; restore reg-y
|
||
|
NoComp rts ; all done
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Delay Timer Management'
|
||
|
|
||
|
seg 'data'
|
||
|
TMPBdef record
|
||
|
; TiMer Parameter Block (TMPB)
|
||
|
|
||
|
MaxTMPBs equ 4 ; (range 1..15)
|
||
|
|
||
|
TMPBnext ds.b 1 ; next TMPB on list
|
||
|
TMPBstate ds.b 1 ; request state
|
||
|
|
||
|
; TMPBtimeH/L are to be filled in by user before calling StartTMPBtimer
|
||
|
export TMPBtimeH
|
||
|
export TMPBtimeL
|
||
|
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
|
||
|
TMPBsaveA ds.b 1 ; reg A save
|
||
|
TMPBsaveX ds.b 1 ; reg X save
|
||
|
|
||
|
TMPBsize equ *-TMPBnext ; bytes per TMPB
|
||
|
ds.b TMPBsize*(MaxTMPBs-1) ; allocate remaining TMPBs
|
||
|
|
||
|
nilTMPB equ $FF ; nil index value
|
||
|
availTMPBhead ds.b 1 ; head of TMPB free list
|
||
|
tmReqQhead ds.b 1 ; head of TMPB request queue
|
||
|
tmIndexTemp ds.b 1 ; temp used for indexing
|
||
|
endr
|
||
|
title 'IOP Kernel - Init Delay Timer Driver'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr InitDelayTimerDriver
|
||
|
; Inputs: none
|
||
|
; Outputs: none
|
||
|
; Destroys: A, X, n, v, z, c
|
||
|
; Cycles: 45 + 14 per TMPB
|
||
|
; Calls: none
|
||
|
; Called by: Reset
|
||
|
;
|
||
|
; Function: Initializes the Delay Timer data structures.
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED.
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
InitDelayTimerDriver proc
|
||
|
with TMPBdef
|
||
|
last equ TMPBsize*(MaxTMPBs-1)
|
||
|
|
||
|
lda #0 ; index of first TMPB
|
||
|
sta availTMPBhead ; 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
|
||
|
cpa #last ; check for last TMPB
|
||
|
blt loop ; process next TMPB
|
||
|
lda #nilTMPB ; mark empty lists
|
||
|
sta last+TMPBnext ; mark end of free list
|
||
|
sta tmReqQhead ; request queue head is empty
|
||
|
lda #TMRENI ; disable timer interrupts
|
||
|
trb InterruptMask ; update the interrupt mask
|
||
|
lda #CONT ; prepare to force one shot mode
|
||
|
trb TimerDPLLctl ; reset the bit
|
||
|
lda #$80 ; low byte latch is a constant
|
||
|
sta TimerLatchL ; initialize the low timer latch
|
||
|
rts
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Get TMPB'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr GetTMPB
|
||
|
; Inputs: none
|
||
|
; Outputs: Y - TMPB index
|
||
|
; n - 1 if no free TMPBs available
|
||
|
; Destroys: A, Y, n, z
|
||
|
; Cycles: 34 if BCPB was available
|
||
|
; 13 if BCPB was not available
|
||
|
; Calls: none
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; 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.
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
GetTMPB proc export ; Get a free TMPB
|
||
|
with TMPBdef
|
||
|
ldy availTMPBhead ; get the available list head
|
||
|
bmi exit ; if list empty
|
||
|
lda TMPBnext,y ; get successor
|
||
|
sta availTMPBhead ; update list head
|
||
|
lda #MessageCompleted ; initialize to completed state
|
||
|
sta TMPBstate,y ; initialize the state
|
||
|
lda #$00 ; indicate no completion address
|
||
|
sta TMPBpcH,y ; initialize the PC page
|
||
|
exit rts ; all done
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Free TMPB'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr FreeTMPB
|
||
|
; Inputs: Y - TMPB index
|
||
|
; Outputs: none
|
||
|
; Destroys: A, n, z, c
|
||
|
; Cycles: 27
|
||
|
; Calls: none
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; 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
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
FreeTMPB proc export
|
||
|
with TMPBdef
|
||
|
lda availTMPBhead ; get the available list head
|
||
|
sta TMPBnext,y ; link it to end of new entry
|
||
|
sty availTMPBhead ; new entry is new list head
|
||
|
lda TMPBstate,y ; check the state
|
||
|
cpa #newMessageSent ; see if copy was in progress
|
||
|
beq IsActive ; crash if was active
|
||
|
rts ; all done
|
||
|
IsActive brk ; FreeTMPB on avtive TMPB
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Wait TMPB done'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr WaitTMPBdone
|
||
|
; Inputs: Y - TMPB index
|
||
|
; Outputs: none
|
||
|
; Destroys: n, z, c
|
||
|
; Cycles: 54 - if has to wait
|
||
|
; 24 - if no wait needed
|
||
|
; Calls: none
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; Function: If the TMPB is complete, this routine simply returns to
|
||
|
; its caller. If it is not complete, then it returns to
|
||
|
; its callers caller, and will return to its caller when
|
||
|
; the TMPB request has completed.
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
WaitTMPBdone proc export
|
||
|
with TMPBdef
|
||
|
sta TMPBsaveA,y ; save reg a
|
||
|
lda TMPBstate,y ; get the state
|
||
|
cpa #MessageCompleted
|
||
|
beq exit ; if already done
|
||
|
txa ; prepare to save reg x
|
||
|
sta TMPBsaveX,y ; save reg x
|
||
|
lda TMPBpcH,y ; see if already waiting
|
||
|
bne MultipleWaits ; crash if already waiting
|
||
|
pla ; get pcL
|
||
|
sta TMPBpcL,y ; save pcL
|
||
|
pla ; get pcH
|
||
|
sta TMPBpcH,y ; save pcH
|
||
|
exit lda TMPBsaveA,y ; restore reg a
|
||
|
rts ; all done
|
||
|
MultipleWaits brk ; multiple waits on TMPB
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Check TMPB done'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr CheckTMPBdone
|
||
|
; Inputs: Y - TMPB index
|
||
|
; Outputs: z - 1 if TMPB request has completed
|
||
|
; z - 0 if TMPB request in progress, or not started
|
||
|
; Destroys: A, n, c
|
||
|
; Cycles: 12
|
||
|
; Calls: none
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; Function: Returns an indication as to whether the TMPB request has
|
||
|
; completed.
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
CheckTMPBdone proc export
|
||
|
with TMPBdef
|
||
|
lda TMPBstate,y ; get the state
|
||
|
cpa #MessageCompleted ; see if it is done
|
||
|
rts ; all done
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Start TMPB timer'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr StartTMPBtimer
|
||
|
; Inputs: Y - TMPB index
|
||
|
; Outputs: none
|
||
|
; Destroys: A, X, Y, n, v, z, c
|
||
|
; Cycles: 39 + StartTimer - if timer queue was empty
|
||
|
; 142 + StopTimer + StartTimer +..., otherwise
|
||
|
; Calls: StopTimer, StartTimer
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; 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
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
StartTMPBtimer proc export
|
||
|
with TMPBdef
|
||
|
lda #NewMessageSent ; get the new message state
|
||
|
sta TMPBstate,y ; mark the TMPB as active
|
||
|
ldx tmReqQhead ; get index of head of list
|
||
|
bpl QueueNotEmpty ; if queue already has entries
|
||
|
sty tmReqQhead ; new entry is queue head
|
||
|
lda #nilTMPB ; mark this entry as end of list
|
||
|
sta TMPBnext,y ; indicate no successor
|
||
|
lda #TMRENI ; enable timer interrupts
|
||
|
tsb InterruptMask ; update the interrupt mask
|
||
|
tya ; get TMPB index
|
||
|
tax ; setup TMPB index for StartTimer
|
||
|
bra StartTimer ; start the timer and return
|
||
|
|
||
|
QueueNotEmpty jsr StopTimer ; stop the currently running timer
|
||
|
bmi loop ; if head is negative, begin after it
|
||
|
ldx #tmReqQhead-TMPBnext; fake index to 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 StartTimer, which follows below
|
||
|
* bra StartTimer ; start the timer for the list head
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Start Timer'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr StartTimer
|
||
|
; Inputs: X - tmReqQhead TMPB index
|
||
|
; Outputs: none
|
||
|
; Destroys: A, n, v, z, c
|
||
|
; 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
|
||
|
; Calls: none
|
||
|
; Called by: StartTMPBtimer, HandleTMR2Int, CancelTMPBtimer
|
||
|
;
|
||
|
; 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
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
StartTimer proc
|
||
|
with TMPBdef
|
||
|
lda TMPBtimeH,x ; get high bits
|
||
|
bne OutOfRangeH ; if way too big
|
||
|
lda TMPBtimeL,x ; get low bits
|
||
|
bmi OutOfRangeL ; not in range $0000..$007F
|
||
|
stz TMPBtimeL,x ; clear low bits
|
||
|
sta TimerCounterH ; start the counter
|
||
|
lda #$FF ; Set latch to $FF to re-load timer
|
||
|
sta TimerLatchH ; with $FF when timer runs out
|
||
|
rts
|
||
|
|
||
|
OutOfRangeH bmi OutOfRangeNeg ; if negative
|
||
|
lda #$7F ; load timer with $7F80
|
||
|
sta TimerCounterH ; start the counter
|
||
|
lda #$FF ; Set latch to $FF to re-load timer
|
||
|
sta TimerLatchH ; 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
|
||
|
|
||
|
|
||
|
OutOfRangeNeg stz TimerCounterH ; use $0080 if negative
|
||
|
lda #$FF ; Set latch to $FF to re-load timer
|
||
|
sta TimerLatchH ; with $FF when timer runs out
|
||
|
rts
|
||
|
|
||
|
OutOfRangeL sec ; set carry for subtract
|
||
|
sbc #$7F ; TMPBtime,x := TMPBtime,x - $007F
|
||
|
sta TMPBtimeL,x
|
||
|
lda #$7F ; load timer with $7F80
|
||
|
sta TimerCounterH ; start the counter
|
||
|
lda #$FF ; Set latch to $FF to re-load timer
|
||
|
sta TimerLatchH ; with $FF when timer runs out
|
||
|
rts
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Stop Timer'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr StopTimer
|
||
|
; Inputs: X - tmReqQhead TMPB index
|
||
|
; Outputs: n - indicates sign of TMPBtimer,x
|
||
|
; Destroys: A, v, z, c
|
||
|
; Cycles: 38..40 + 9 if close to rollover
|
||
|
; Calls: none
|
||
|
; Called by: StartTMPBtimer, HandleTMR2Int, CancelTMPBtimer
|
||
|
;
|
||
|
; 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
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
StopTimer proc
|
||
|
with TMPBdef
|
||
|
lda TimerCounterL ; read low byte of counter
|
||
|
cpa #$10 ; see if close to rollover
|
||
|
blt StopTimer ; wait if close to rollover
|
||
|
lda TimerCounterH ; read high byte of counter
|
||
|
clc ; clear carry for addition
|
||
|
bpl ZeroExtended ; 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 TestResultSign ; if no borrow from high byte
|
||
|
dec TMPBtimeH,x ; adjust high byte
|
||
|
rts ; all done
|
||
|
|
||
|
TestResultSign lda TMPBtimeH,x ; return the timer sign
|
||
|
rts ; all done
|
||
|
|
||
|
ZeroExtended adc TMPBtimeL,x ; add to timer remaining for the
|
||
|
sta TMPBtimeL,x ; timer at head of the timer list
|
||
|
bcc TestResultSign ; if no carry into high byte
|
||
|
inc TMPBtimeH,x ; adjust high byte
|
||
|
rts ; all done
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Handle TMR Interrupt'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr HandleTMRInt
|
||
|
; Inputs: none
|
||
|
; Outputs: none
|
||
|
; Destroys: A, X, Y, n, v, z, c
|
||
|
; Cycles: 15 + StopTimer + StartTimer, if timer not fully expired
|
||
|
; 60 + StopTimer, if expired, no more queued,
|
||
|
; no completion handler
|
||
|
; 84 + StopTimer, if expired, no more queued,
|
||
|
; has completion handler
|
||
|
; 62 + StopTimer + StartTimer, if expired, and more queued
|
||
|
; no completion handler
|
||
|
; 86 + StopTimer + StartTimer, if expired, and more queued
|
||
|
; has completion handler
|
||
|
; Calls: StopTimer, StartTimer, 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
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
HandleTMRInt proc ; timer interrupt handler
|
||
|
with TMPBdef
|
||
|
ldx tmReqQhead ; get index of head of list
|
||
|
bmi unknownTMRint ; if timer queue is empty
|
||
|
jsr StopTimer ; stop timer and update queue head
|
||
|
bpl StartTimer ; if not expired, restart timer and exit
|
||
|
phx ; save pointer to dequeued entry
|
||
|
lda TMPBnext,x ; get next queue entry
|
||
|
sta tmReqQhead ; dequeue head
|
||
|
bmi DisableTimer ; if no successor
|
||
|
tax ; get pointer to new queue head
|
||
|
jsr StartTimer ; start next timer
|
||
|
bra RunCompletion ; go run the completion routine
|
||
|
DisableTimer lda #TMRENI ; disable timer interrupts
|
||
|
trb InterruptMask ; update the interrupt mask
|
||
|
RunCompletion ply ; restore pointer to dequeued entry
|
||
|
lda #MessageCompleted ; mark the TMPB as completed
|
||
|
sta TMPBstate,y ; mark the TMPB as done
|
||
|
lda TMPBpcH,y ; get high byte of comp. addr
|
||
|
beq NoComp ; if no completion routine
|
||
|
pha ; restore return pcH
|
||
|
lda TMPBpcL,y ; get low byte of comp. addr
|
||
|
pha ; restore return pcL
|
||
|
lda #$00 ; indicate no completion address
|
||
|
sta TMPBpcH,y ; to indicate that the wait completed
|
||
|
lda TMPBsaveA,y ; restore reg-a
|
||
|
ldx TMPBsaveX,y ; restore reg-y
|
||
|
NoComp rts ; all done
|
||
|
unknownTMRint brk ; TMR interrupt with empty Q
|
||
|
endwith
|
||
|
endproc
|
||
|
title 'IOP Kernel - Cancel TMPB timer'
|
||
|
|
||
|
;_______________________________________________________________________
|
||
|
;
|
||
|
; Routine: jsr CancelTMPBtimer
|
||
|
; Inputs: Y - TMPB index
|
||
|
; Outputs: none
|
||
|
; Destroys: A, X, Y, n, v, z, c
|
||
|
; 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
|
||
|
; Calls: StopTimer, StartTimer
|
||
|
; Called by: Drivers
|
||
|
;
|
||
|
; Function: Cancels the timer in the indicated TMPB, and removes it
|
||
|
; from the timer queue.
|
||
|
;
|
||
|
; NOTE: This routine must be called with interrupts DISABLED
|
||
|
;
|
||
|
;_______________________________________________________________________
|
||
|
|
||
|
seg 'code'
|
||
|
CancelTMPBtimer proc export
|
||
|
with TMPBdef
|
||
|
lda #MessageCompleted ; prepare to check for done
|
||
|
cpa TMPBstate,y ; see if already completed
|
||
|
beq exit ; exit if already completed
|
||
|
sta TMPBstate,y ; indicate completed
|
||
|
lda #$00 ; indicate no completion address
|
||
|
sta TMPBpcH,y ; to indicate that the wait completed
|
||
|
cpy tmReqQhead ; see if it is running
|
||
|
bne SearchQueue ; if not head of queue
|
||
|
lda TMPBnext,y ; check for successor
|
||
|
bpl HasSuccessor ; if not the only entry
|
||
|
sta tmReqQhead ; set head to nil
|
||
|
lda #TMRENI ; disable timer interrupts
|
||
|
trb InterruptMask ; update the interrupt mask
|
||
|
rts ; all done
|
||
|
|
||
|
HasSuccessor ldx tmReqQhead ; get queue head
|
||
|
jsr StopTimer ; stop the head timer
|
||
|
ldy tmReqQhead ; get index of entry to dequeue
|
||
|
ldx TMPBnext,y ; get index of successor
|
||
|
stx tmReqQhead ; successor is new head
|
||
|
jsr AdjustTime ; adjust the delay time
|
||
|
jmp StartTimer ; start new head timer
|
||
|
|
||
|
SearchQueue lda #tmReqQhead-TMPBnext; fake index to tmReqQhead
|
||
|
sty tmIndexTemp ; save index of canceled timer
|
||
|
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
|
||
|
AdjustTime 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 rts ; all done
|
||
|
endwith
|
||
|
endproc
|
||
|
|
||
|
end
|