; ; 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