; ; File: HALc96BIOS.a ; ; Contains: Fast Data XFer routines for BIOS/53c96 machines ; ; Written by: Clinton Bauder ; ; Copyright: © 1990-1994 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; 1/25/94 DCB Got rid of a bogus extra line in the bus error handler ; installer. ; 12/23/93 DCB Adding code to save the handshake values between calls to the ; fast read and write routines. This means scatter gather list ; elements don't have to be multiples of the smallest hanshake ; size. ; 12/9/93 DCB FastWrite left the bus error handler installed after it had ; finished. ; 11/22/93 DCB Somehow I left a totally bogus bne in the code I added last time ; This totally hosed the Bus Error Handler in the write case. ; 11/17/93 DCB Fixing the bus error handling so that we don't step on the TIB ; interpreter's globals when we remember what the data direction ; is. ; 10/29/93 DCB Adding calls to deferAndWait after calls to Wt4SCSI do reduce ; the interrupt level during transfers. Also added code to remove ; the Bus Error handler when we go asychronous so that we wouldn't ; end up chaining to our own BEH. ; 10/14/93 pdw ; 10/12/93 pdw Added support for Synchronous data transfers, rewrote State ; Machine, message handling etc. ; 9/26/93 pdw Rolled in Clinton's changes. ; 9/27/93 DCB Grrr, I missed a tst.w d0 in FastWrite96Bios which I should have ; changed to a tst.w d1. ; 9/25/93 DCB The scHandshake interpreter wasn't checking d1 after each ; iteration of FastRead_inner. I was also stepping on A0 when I ; removed the BusError Handler at the end of a data transaction. ; This was playing havoc with the Handshake intepreter because it ; was using the SCSIGlobals as a Handshake ptr. ; 9/16/93 DCB Fixing the busError handler to resolve a conflict between two ; definitions of yeOldeBusErrorVect. Also changed the handshake ; interpreter to fix a residual length discrepancy. ; 9/9/93 pdw Lots of little changes. Name changes, temporary cache_bug ; stuff. ; 8/19/93 DCB Improving the bus error handler so that disconnects at ; non-polled bytes will work properly. ; 8/13/93 pdw Trivial little IMPORT changes and stuff. ; 7/17/93 pdw Lots of little things (involving interrupt checking and ; currentPhase). ; 6/29/93 pdw Massive checkins: Change asynchronicity mechanism to CallMachine ; stack switching mechanism. Adding support for Cold Fusion. ; Rearranging HW/SW Init code. Some code optimizations. ; 5/25/93 DCB SCSI Globals are now included in XPTEqu.a ; 5/5/93 PW Converted names to meanies-friendly names. Updated with latest ; from Ludwig stuff. ; 4/8/93 joe Added EXPORTs for OneByteRead_BIOS and OneByteWrite_BIOS. The ; lack of those exports was breaking the build. ; <1> 4/8/93 DCB first checked in ; ;========================================================================== MACHINE MC68020 ; '020-level BLANKS ON ; assembler accepts spaces & tabs in operand field PRINT OFF ; do not send subsequent lines to the listing file ; don't print includes PostNOP EQU 1 ; INCLUDE 'SysErr.a' INCLUDE 'SysEqu.a' INCLUDE 'HardwarePrivateEqu.a' INCLUDE 'MC680x0.a' INCLUDE 'Debug.a' ; for NAME macro INCLUDE 'ACAM.a' INCLUDE 'SCSI.a' INCLUDE 'XPTEqu.a' INCLUDE 'SCSIEqu53c96.a' INCLUDE 'SIMCoreEqu.a' INCLUDE 'HALc96equ.a' PRINT ON ; do send subsequent lines to the listing files CASE OBJECT HALc96DataBIOS PROC EXPORT IMPORT Wt4SCSIInt, Ck4SCSIInt IMPORT Ck4DREQ IMPORT WtForFIFOData IMPORT RecordEvent IMPORT Wt4PhaseStable IMPORT DeferAndWait EXPORT FastRead_96_BIOS,FastWrite_96_BIOS EXPORT OneByteRead_BIOS EXPORT OneByteWrite_BIOS WITH HALc96GlobalRecord WITH SIM_IO, HALactions, SCSIGlobalsRec, SCSIPhase ;-------------------------------------------------------------------------- ; OneByteRead_BIOS/OneByteWrite_BIOS - proc to transfer 1 byte ; ; d0 - <-- error (if any) ; d1 - --> copy of d2 ; d1 - <-- bytes transferred ; d2 - --> number of bytes to transfer ; d3 - scratch - saved ; d4 - --> type of transfer to perform ; d5 - scratch - saved ; d6 - scratch - saved ; ; a0 - scratch - saved ; a1 - SCSI chip read base address - NON-SERIALIZED ; a2 - ptr to data buffer - saved ; a3 - SCSI chip read base address - SERIALIZED ; A4 - ptr to HAL Action PB ; a5 - ptr to HAL Globals OneByteRead_BIOS ; RecCmd $10, 'B',$01 bra.s @oneReadBot ; @oneReadTop move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers eieio bsr.w Wt4SCSIInt ; Wait for interrupt move.b rFIFO(a3), (a2)+ ; xfer byte from FIFO into input buffer @oneReadBot cmp.b #kDataInPhase, currentPhase(A5) dbne d2,@oneReadTop ; bne.s @wrongPhase moveq.l #0, D2 ; fell through db - we did all of it @wrongPhase ; fell through ne - we have the real count move.l d2,d1 ; remember how much we did rts NAME 'OneByteRead_BIOS' OneByteWrite_BIOS bra.s @oneWriteBot ; @oneWriteTop move.b (a2)+, rFIFO(a3) ; preload the FIFO @oneWriteBot dbra d2,@oneWriteTop ; RecCmd $10, 'B',$02 move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers eieio bsr.w Wt4SCSIInt ; Wait for interrupt move.b rFIFOflags(a3), d1 ; get FIFO status - how many bytes in FIFO and.w #mFIFOCount, d1 ; this is the number we didn't xfer rts NAME 'OneByteWrite_BIOS' ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ; FastRead_96_BIOS - implements FastRead ; ; Called by: Transfer ; ; All primitive data transfer routines assume: ; ; Inputs ---> ; -> D2.L number of bytes to transfer ; -> D1.L copy of d2 ; -> A2 ptr to data buffer - saved ; -> A3 SCSI chip read base address - SERIALIZED ; -> A5 ptr to SCSI Mgr globals ; ; Outputs <--- ; <- d0.W error (if any) ; <- d1.L bytes not transferred ; ; Internal --- ; D3 - trashed ; A0 - scratch - saved ; A1 - SCSI chip read base address - NON-SERIALIZED ; A2 - ptr to data buffer - saved ; A3 - SCSI chip read base address - SERIALIZED ; A5 - ptr to SCSI Mgr globals ; ; Method of Data Transfer: (uses Pseudo-DMA) ; 0) Make sure we got our intrp from the last cmd send ; 1) Parcel transfer into 64KB blocks since TC regr. handles 64KB max ; 2) Read 1st byte if input buffer is NOT word aligned ; 3) Calc. the number of 32-byte block transfers to perform ; 4) Calc. the remaining number of byte transfers to perform ; 5) Read data 32-byte blocks at a time using word MOVEs ; 6) Read remaining data a word at a time ; 7) Transfer residual byte if there is one D2_thisTime EQU D2 ; .l = number of bytes remaining in this block D3_total EQU D3 ; .l = number of bytes remaining in this request FastRead_96_BIOS ; ; Loop thru scsiHandshake structure ; move.l D2, D3_total ; save original total request count @firstHandshake lea scsiHandshake(A0), A1 ; and now with ptr to scsiHandshake tst.w (A1) ; if no handshake, beq.s FastReadBIOS_inner ; -> skip handshake, goto inner routine moveq #0, D2_thisTime move.w hdshkRemainder(a0), D2_thisTime ; Leftover Hansdhake? beq.b @next ; nope, start handshake over again move.w #0, hdshkRemainder(a0) ; don't try to do it again... moveq #0, d0 move.b hdshkIndex(a0), d0 ; find out which handshake element we were working on lsl.w #1, d0 ; adjust for word length add.w d0, A1 ; index into the handshake array bra.b @handshake ; and do it @next moveq.l #0, D2_thisTime move.w (A1)+, D2_thisTime beq.s @firstHandshake @handshake sub.l D2_thisTime, D3_total ; decrement total by this hsk count beq.s @lastOne ; if same, -> this is the last one blo.s @useTotal ; if total use the total left move.l A1, -(sp) bsr.s FastReadBIOS_inner ; else, use the hsk value as count move.l (sp)+, A1 tst.w D1 ; did we go out of phase? bne.s @didntFinish ; -> yes, adjust unxferred count and ret bra.s @next ; -> no, get the next hsk for next xfer @didntFinish add.l D3_total, D1 ; readjust total count by unxfered amount rts ; (D1 is return value) @useTotal add.l D2_thisTime, D3_total ; restore total to what it was... move.l D2_thisTime, D0 ; remember what handshake was sub.l D3_total, D0 ; and figure out the remainder was move.w D0, hdshkRemainder(A0) ; so we can remember it for later move.l A1, D0 ; get next hanshake sub.l #2, D0 ; and back up one word (element) lea scsiHandshake(A0), A1 ; get the first hanshake sub.l A1, D0 ; calculate the offset move.b D0, hdshkIndex(A0) ; and remember it for later (whew!) move.l D3_total, D2_thisTime @lastOne bsr.s FastReadBIOS_inner rts ;ΡΡΡΡΡΡΡΡΡΡΡΡ FastReadBIOS_inner IF 1 AND RECORD_ON THEN pea 'FsRd' ; EVENT = move.l D2, -(sp) ; number of bytes bsr RecordEvent addq.l #8, sp ENDIF move.l pdmaAddr(A5), a1 ; put serialized version back in moveq.l #iPhaseMsk, d0 ; load mask for phase bits and.b rSTA(a3), d0 ; are we in data-in phase? cmpi.b #iDataIn, d0 ; data-in phase bits = 001 bne.w PhaseErrAtStart ; bra. on phase err cmpi.l #3, d2 ; if not 3 or less bytes read then... bhi.s @moreThan3 ; jump into move.l loop @doSingles bsr OneByteRead_BIOS ; bra @exit @moreThan3 move.l a2,d6 ; get location of buffer and.l #3,d6 ; odd words or bytes mean alignment req'd bne.w @alignBuffer ; @aligned ; ; Install our bus error handler because we are going into a routine that blindly accesses ; the SCSI chip (and it might not always be ready) ; bsr.w InstallBEH_BIOS ; install our Bus Error Handler move.l d2, d6 ; d6 = number 64KB block to perform swap d6 ; upper word of d6 = lower word of d2 andi.l #$0000FFFF, d2 ; mask out upper word beq @2 ; if 0 then we have $10000 (64K) bytes to xfer bra.s @skipPhaseCk @next64KB moveq.l #iPhaseMsk, d0 ; load mask for phase bits and.b rSTA(a3), d0 ; are we in data-in phase? cmpi.b #iDataIn, d0 ; data-in phase bits = 001 bne.w @outOfPhase ; outta here. @skipPhaseCk move.l d2, d4 ; d4 <- d2 move.b d4, rXCL(a3) ; TC regr (least-sig. byte) <- d4.b lsr.l #8, d4 ; get upper byte of low word move.b d4, rXCM(a3) ; TC regr (most-sig. byte) <- d4.b eieio IF RECORD_ON and RECORD_rCMD THEN pea 'rCMD' ; EVENT = Wt4S move.w #$90, -(sp) ; command move.w busID(A5), -(sp) ; busID bsr RecordEvent addq.l #8, sp ENDIF move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & begin xfers ; DREQ* should be active at this time eieio move.l d2, d4 ; d4 = copy of transfer count lsr.l #7, d4 ; divide xfer count by 128 and.l #$7f,d2 ; remainder is byte count after 128 byte moves ror.l #1, d2 ; xfer byte count to word & remember odd byte move.l d2,d0 ror.l #1,d0 ; xfer word count to long & remember odd word neg.w d0 ; negate to form a backward jump offset nop ; squoosh pipeline jmp @RdLoop(d0.w*2) ; bra. into the loop @read128 ; move.l (a1),(a2)+ ; read 16 bytes move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; finished 16 bytes move.l (a1),(a2)+ ; read 16 more bytes move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; finished 32 bytes move.l (a1),(a2)+ ; read 16 more bytes move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; finished 48 bytes move.l (a1),(a2)+ ; read 16 more bytes move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; finished 64 bytes move.l (a1),(a2)+ ; read 16 more bytes move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; finished 80 bytes move.l (a1),(a2)+ ; read 16 more bytes move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; finished 96 bytes move.l (a1),(a2)+ ; read 16 more bytes move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; finished 112 bytes move.l (a1),(a2)+ ; read 16 more bytes move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; move.l (a1),(a2)+ ; finished 128 bytes @RdLoop ; dbra d4, @read128 ; d4 = # of 128-byte transfers IF PostNOP THEN nop ; squoosh pipeline ENDIF ; INT & TC bits should be TRUE at this point btst.l #31, d0 ; check if we have a residual word beq.s @chkByte ; bra. if no residual @resWord bsr.l WtForFIFOData ; returns number of bytes in FIFO move.w (a1),(a2)+ ; xfer residual word @chkByte bsr.w RemoveBEH_BIOS ; get rid of our BEH while we are async bsr.w Wt4SCSIInt ; Wait for intrp w/o timeout bne.s @disconnected ; bra. if ended up disconnected jsr DeferAndWait ; defer til interupts are enabled bsr.w InstallBEH_BIOS ; put the BEH back btst.l #31, d2 ; check if we have a residual byte beq.s @2 ; bra. if no residual @resByte bsr.l WtForFIFOData ; returns number of bytes in FIFO move.b rFIFO(a3), (a2)+ ; xfer residual byte @2 move.l #$10000, d2 ; init to transfer 64K bytes dbra d6, @next64KB ; @disconnected ; ; Remove our bus error handler ; bsr.w RemoveBEH_BIOS ; get rid of our BEH @goodFRead moveq.l #0, D1 ; d1 = # of bytes transferred moveq.l #noErr, d0 ; successful read op @exit rts ; @alignBuffer bra.s @alignLoop @misAligned IF RECORD_ON and RECORD_rCMD THEN pea 'rCMD' ; EVENT = Wt4S move.w #$10, -(sp) ; command move.w busID(A5), -(sp) ; busID bsr RecordEvent addq.l #8, sp ENDIF move.b #cIOXfer, rCMD(a3) ; load non-DMA transfer cmd & begin 1 byte xfer ; ; WtForInt ; @noTimeoutWait clr.l d5 ; move.b rSTA(a3), d5 ; = x | x | x | rSTA bpl.s @noTimeoutWait ; ...loop until intrp req is detected move.b D5, D0 and.b #iPhaseMsk, D0 move.b D0, currentPhase(A5) swap d5 ; = x | rSTA | x | x move.b rFIFOflags(a3), d5 ; = x | rSTA | x | rFOS lsl.w #8, d5 ; = x | rSTA | rFOS | x move.b rINT(a3), d5 ; = x | rSTA | rFOS | rINT move.l d5, d0 ; for checking for disconnect swap d5 ; = rFOS | rINT | x | rSTA move.b rFIFO(a3), (a2)+ ; get the byte subq.l #1, D2 ; decr. for DBRA beq.s @goodFRead ; if xfer complete - done ; get Disconnect & Bus Service bits andi.b #(1< copy of d2 ; d1 - <-- bytes transferred ; d2 - --> number of bytes to transfer ; d3 - scratch - saved ; d4 - --> type of transfer to perform ; d5 - scratch - saved ; d6 - scratch - saved ; ; a0 - scratch - saved ; a1 - SCSI chip read base address - NON-SERIALIZED ; a2 - ptr to data buffer - saved ; a3 - SCSI chip read base address - SERIALIZED ; A4 - ptr to HAL Action PB ; A5 - ptr to SCSI Mgr globals ; a5 - scratch - saved ; ; Method of Data Transfer: (uses pseudo-DMA mode) ; 0) Make sure we got our intrp from the last cmd send ; 1) Parcel transfer into 64KB blocks since TC regr. handles 64KB max ; 2) Preload FIFO with non-aligned byte; get us word aligned ; 3) Calc. the number of 32-byte block transfers to perform ; 4) Calc. the remaining number of transfers to perform ; 5) Write data 32-byte blocks at a time using word MOVEs ; 6) Write remaining data using word MOVEs ; 7) Transfer residual byte if there is one ;ΡΡΡΡΡΡΡΡΡΡΡΡ FastWrite_96_BIOS ; ; Loop thru scsiHandshake structure ; move.l D2, D3_total ; save original total request count @firstHandshake lea scsiHandshake(A0), A1 ; and now with ptr to scsiHandshake tst.w (A1) ; if no handshake, beq.s FastWrite96BIOS_inner ; -> skip handshake, goto inner routine moveq #0, D2_thisTime move.w hdshkRemainder(a0), D2_thisTime ; Leftover Hansdhake? beq.b @next ; nope, start handshake over again move.w #0, hdshkRemainder(a0) ; don't try to do it again... moveq #0, d0 move.b hdshkIndex(a0), d0 ; find out which handshake element we were working on lsl.w #1, d0 ; adjust for word length add.w d0, A1 ; index into the handshake array bra.b @handshake ; and do it @next moveq.l #0, D2_thisTime move.w (A1)+, D2_thisTime beq.s @firstHandshake @handshake sub.l D2_thisTime, D3_total ; decrement total by this hsk count beq.s @lastOne ; if same, -> this is the last one blo.s @useTotal ; if total use the total left move.l A1, -(sp) bsr.s FastWrite96BIOS_inner ; else, use the hsk value as count move.l (sp)+, A1 tst.w D1 ; did we go out of phase? bne.s @didntFinish ; -> yes, adjust unxferred count and ret bra.s @next ; -> no, get the next hsk for next xfer @didntFinish add.l D3_total, D1 ; readjust total count by unxfered amount rts ; (D1 is return value) @useTotal add.l D2_thisTime, D3_total ; restore total to what it was... move.l D2_thisTime, D0 ; remember what handshake was sub.l D3_total, D0 ; and figure out the remainder was move.w D0, hdshkRemainder(A0) ; so we can remember it for later move.l A1, D0 ; get next hanshake sub.l #2, D0 ; and back up one word (element) lea scsiHandshake(A0), A1 ; get the first hanshake sub.l A1, D0 ; calculate the offset move.b D0, hdshkIndex(A0) ; and remember it for later (whew!) move.l D3_total, D2_thisTime @lastOne bsr.s FastWrite96BIOS_inner rts ;ΡΡΡΡΡΡΡΡΡΡΡΡ FastWrite96BIOS_inner IF 1 AND RECORD_ON THEN pea 'FsWr' ; EVENT = move.l D2, -(sp) ; number of bytes bsr RecordEvent addq.l #8, sp ENDIF move.l pdmaAddr(A5), a1 ; put serialized version back in moveq.l #iPhaseMsk, d0 ; and.b rSTA(a3), d0 ; are we in data-out phase? bne.w PhaseErrAtStart ; data-out phase bits = 0, bra. on phase err cmpi.l #3, d2 ; if not 3 or less bytes write then...> bhi.s @moreThan3 ; jump into move.l loop @doSingles bsr OneByteWrite_BIOS ; Actually does up to three bytes bra @exit @moreThan3 move.l a2,d6 ; get location of buffer and.l #3,d6 ; odd words or bytes mean alignment req'd bne.w @alignLoop ; @aligned ; ; Install our bus error handler because we are going into a routine that blindly accesses ; the SCSI chip (and it might not always be ready) ; bsr.w InstallBEH_BIOS ; install our BEH move.l d2, d6 ; d6 = number 64KB block to perform swap d6 ; upper word of d6 = lower word of d2 andi.l #$0000FFFF, d2 ; mask out upper word beq @2 ; if 0 then we have $10000 (64K) bytes to xfer bra.b @skipPhaseCk @next64KB ; buffer is aligned from this point moveq.l #iPhaseMsk, d0 ; load mask for phase bits and.b rSTA(a3), d0 ; are we in data-out phase? bne.w @phaseErr ; nope, outta here. @skipPhaseCk move.l d2, d4 ; d4 <- d2 move.b d4, rXCL(a3) ; TC regr (least-sig. byte) <- d4.b lsr.l #8, d4 ; get upper byte of low word move.b d4, rXCM(a3) ; TC regr (most-sig. byte) <- d4.b eieio IF RECORD_ON and RECORD_rCMD THEN pea 'rCMD' ; EVENT = Wt4S move.w #$90, -(sp) ; command move.w busID(A5), -(sp) ; busID bsr RecordEvent addq.l #8, sp ENDIF move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & begin xfers ; DREQ* should be active at this time eieio move.l d2, d4 ; d4 = copy of transfer count lsr.l #7, d4 ; divide xfer count by 128 and.l #$7f,d2 ; remainder is byte count after 128 byte moves ror.l #1, d2 ; xfer byte count to word & remember odd byte move.l d2,d0 ror.l #1,d0 ; xfer word count to long & remember odd word neg.w d0 ; negate to form a backward jump offset nop ; squoosh pipeline jmp @WrLoop(d0.w*2) ; bra. into the loop @write128 ; move.l (a2)+,(a1) ; write 16 bytes move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; finished 16 bytes move.l (a2)+,(a1) ; write 16 bytes move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; finished 32 bytes move.l (a2)+,(a1) ; write 16 bytes move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; finished 48 bytes move.l (a2)+,(a1) ; write 16 bytes move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; finished 64 bytes move.l (a2)+,(a1) ; write 16 bytes move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; finished 80 bytes move.l (a2)+,(a1) ; write 16 bytes move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; finished 96 bytes move.l (a2)+,(a1) ; write 16 bytes move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; finished 112 bytes move.l (a2)+,(a1) ; write 16 bytes move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; move.l (a2)+,(a1) ; finished 128 bytes @WrLoop dbra d4, @write128 ; d4 = # of 128-byte tranfers eieio btst.l #31, d0 ; check if we have a residual word beq.s @chkByte ; bra. if no residual @resWord move.w (a2)+,rDMA(a3) ; xfer residual byte eieio @chkByte btst.l #31, d2 ; check if we have a residual byte beq.s @noResidual ; @resByte move.b (a2)+,rDMA(a3) ; xfer residual byte eieio @noResidual ; INT & TC maybe TRUE at this point bsr.w RemoveBEH_BIOS ; get rid our our BEH while we are async bsr.w Wt4SCSIInt ; Wait for int bne.s @goodFWrite ; bra. if ended up disconnected jsr DeferAndWait ; defer til interupts are enabled bsr.w InstallBEH_BIOS ; put the BEH back @2 move.l #$10000, d2 ; init to transfer 64K bytes dbra d6, @next64KB ; moveq.l #mFIFOCount, d2 ;
thru next
and.b rFIFOflags(a3), d2 ; add un-xferred byte in FIFO beq.s @disconnected ; moveq.l #scPhaseErr, d0 ; return a phase error bra.s @badFWrite ;
from prev
@disconnected ; ; Remove our bus error handler ; bsr.w RemoveBEH_BIOS ; get rid our our BEH @goodFWrite moveq.l #0, D1 ; d1 = # of bytes transferred moveq.l #noErr, d0 ; successful write op @exit rts ; @misAligned ; subq.l #1, d2 ; adjust for transfer count calc move.b (a2)+, rFIFO(a3) ; ...preload fifo with odd byte eieio @alignLoop dbra d6,@misAligned ; keep doing it until we are long aligned bra.w @aligned ; transfer the rest of data @phaseErr bsr.w RemoveBEH_BIOS ; get rid our our BEH add.l #1,d6 ; undo for dbra swap d6 ; d6 contains the number of 64k xfers move.w #0, D6 ; we only care about the high word move.l d6,d1 ; return the number of bytes left to be xferred beq.s @exit ; if zero, no error moveq.l #scPhaseErr, d0 ; else return a phase error bra.s @exit @badFWrite ; IF RECORD_ON and RECORD_rCMD THEN pea 'rCMD' ; EVENT = Wt4S move.w #$01, -(sp) ; command move.w busID(A5), -(sp) ; busID bsr RecordEvent addq.l #8, sp ENDIF move.b #cFlushFIFO, rCMD(a3) ; Flush FIFO eieio bra.s @exit RTSNAME 'FastWrite_96_BIOS' ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ PhaseErrAtStart move.l D2, D1 ; return same number as requested moveq.l #scPhaseErr, d0 ; return a phase error rts ; NAME 'PhaseErrAtStart' InstallBEH_BIOS move.l a0,-(sp) ; save a0 for scsiHandshake stuff move.l SCSIGlobals, A0 ; scsiGlobals move.l BusErrVct, yeOldeBusErrVct(A0) ; save previous Bus Error vector lea BusErrHandler_96_BIOS, A0 ; get address of our BEH move.l A0, BusErrVct ; put ours in there move.l (sp)+,a0 ; restore a0 rts RTSNAME 'InstallBEH_BIOS' RemoveBEH_BIOS move.l ([SCSIGlobals],yeOldeBusErrVct), BusErrVct ; restore previous Bus Error vector rts RTSNAME 'RemoveBEH_BIOS' ;___________________________________________________________________________ ; ; BusErrHandler_96 ; When the SCSI Mgr is performing a blind data transfer, it patches ; out the bus error vector. The old SCSI Mgr bus error handler ; assumed that if it got called, it must be handling a SCSI bus error. ; Unfortunately, NuBus cards could bus error while the SCSI Mgr is ; installed. To be a better bus error citizen, the SCSI bus error ; handler now checks that the fault address is the SCSI chip, and if ; not, it chains to the bus error handler that it replaced. ; ; This code returns control to Transfer_96 and not to the routine which ; caused the bus error. It does this by popping off the buserr stack ; frame and then doing an RTS, so... ; DON'T PUT ANYTHING ON THE STACK IN TRANSFER ROUTINES (FastRead, ; FastΙ, etc.). At least don't leave it there during periods where a ; buserr may be possible. ; ___________________________________________________________________________; WITH AEXFrame,SCSIGlobalsRec ;XferFrame savedRegs REG d0-d3/a0-a3/a5 ; save these registers because we need to use them savedRSize EQU 9*4 ; # bytes on stack for saved registers BusErrHandler_96_BIOS ; Is it our fault? ----- subq.l #4, sp ; make room for return addr (@notSCSIFault) movem.l savedRegs, -(sp) ; lea savedRSize+4(sp), a0 ; make A0 our AEXFrame pointer (regs+1 LW on stack) ; ; XPT should install its BusErrHandler and pass a ptr to the AEXFrame and the SIMg down into ; the SIM for every one of the SIMs that have registered a BEH. The SIM must then pass the ; HALg down here. (But we'll cheat for now since only our buses will need this BEH). move.l SCSIGlobals, A1 cmp.l berr_halg0(A1),A5 ; are we talking to our bus 0? bne.s @notSCSIFault move.l baseRegAddr(a5),a3 ; get the base address of the c96 move.l pdmaAddr(A5),d0 ; get the pseudo DMA Address cmp.l FaultAddr(A0),d0 ; compare with faulted address beq.s @SCSIFault ; if so, start processing the bus error lea BIOSAddr,a1 ; setup for testing BIOS buffer btst.b #bBIOSSCSIBERR,BIOS_PDMA(a1) ; did we buserr on access via BIOS ? bne.s @SCSIFault ; if so, start processing the bus error ; It's not our fault ------ @notSCSIFault move.l SCSIGlobals, A1 ; Get the SCSI globals again move.l yeOldeBusErrVct(A1), savedRSize(sp) ; stuff old Bus Error vector movem.l (sp)+, savedRegs ; restore regs rts ; jump to old handler, assuming it'll RTE NAME 'BusErrHandlerTOP_BIOS' ; It's all our fault (blame it on us) ------ @SCSIFault move.w sr, savedSR(a5) ; save sr ori.w #$0700, sr ; \/ \/ \/ Block Ints \/ \/ \/ ; Wait for either DREQ or INT IF RECORD_ON THEN pea 'Berr' ; move.l xPC(a0),-(sp) ; bsr RecordEvent addq.l #8, sp ENDIF move.l HALactionPB.ioPtr(A4),a1 ; get the IOPB move.l scsiFlags(A1), D0 and.l #scsiDirectionMask, D0 ; are we data in or out? cmp.l #scsiDirectionIn, D0 ; We may not want to do writebacks if we are ; writing. bne.w @notDataIn @DataIn ;**************************************** ; clean up the writebacks on the stack frame move.w WB1S(A0), d0 ; check WB1 for validity move.l WB1A(A0), A1 ; pass WB Address move.l WB1D(A0), d1 ; pass WB Data bsr.w DoWriteBack ; to routine that takes care of it move.w WB2S(A0), d0 ; check WB2 for validity move.l WB2A(A0), A1 ; pass WB Address move.l WB2D(A0), d1 ; pass WB Data bsr.w DoWriteBack ; to routine that takes care of it move.w WB3S(A0), d0 ; check WB3 for validity move.l WB3A(A0), A1 ; pass WB Address move.l WB3D(A0), d1 ; pass WB Data bsr.w DoWriteBack ; to routine that takes care of it @wtloop movem.l D1-D2/A0, -(sp) bsr Ck4DREQ ; movem.l (sp)+, D1-D2/A0 bne.s @ckBIOS movem.l D1-D2/A0, -(sp) bsr Ck4SCSIInt ; see if we have a phase change or something movem.l (sp)+, D1-D2/A0 bne.w @phzChange ; bra.b @wtloop @ckBIOS IF RECORD_ON THEN pea 'BERD' ; move.l d0,-(sp) ; bsr RecordEvent addq.l #8, sp ENDIF ; READ case for BIOS ********************************* ; we have to check the residual register in BIOS.... ; 1. if there is a residual word we have to retrieve it, read another word ; with the posted DREQ and nuke the stack to skip the move.l that buserr'd ; 2. if there is no residual then we can just rte like flint! ; lea BIOSAddr,a1 ; setup for testing BIOS buffer btst.b #bBIOSR1Cmplt,BIOS_PDMA(a1) ; is there a residual word? beq.w @doRTE ; 0=no, so restart move.l ; ; Only 1 word of the long word read made it from the controller. We have to ; move the word stored in the residual buffer within BIOS to memory and then ; manually extract the next word from the controller. ; Note: the value of a2 will be the value before the move.l postincrement. ; We also have to advance the PC so that the rte doesn't restart the faulted ; instruction. move.w BIOS_SCSI_RESID(a1),(a2)+ ; retrieve the residual word stored in BIOS move.w rDMA(a3),(a2)+ ; get word from chip and put into user's buffer

; now cleanup the stack and return to the transfer code. move.l xPC(a0),d0 ; get pc where buserr occurred addq #2,d0 ; adjust the pc to point to the next instruction

; Fake a format code 0 exception frame (4 words) to finish cleaning up clr.w PD3+2(a0) ; stuff a format code 0 (format code is really only a word) move.l d0,PD2+2(a0) ; stuff the new pc in stack move.w xSR(a0),PD2(a0) ; clr.w BIOS_PDMA(a1) ; clear the status register move.w savedSR(a5),sr ; /\ /\ /\ Un Block Ints /\ /\ /\ movem.l (sp)+, savedRegs ; restore regs addq.l #4, sp addq.l #4, a2 ; adjust user buffer because we manually did the move.l

@adjR040XFrame ; 040 Bus Error frame-cleaning done here lea aeXFrameSize-8(sp),sp ; remove 040 Access Error Exception Frame ; but leave PD3 return address IF RECORD_ON THEN pea 'RTEr' ; move.l d0,-(sp) ; bsr RecordEvent addq.l #8, sp ENDIF rte ; resume execution at next instruction @notDataIn ;**************************************** cmp.l #scsiDirectionOut, D0 bne.w @noDataXfer @1 movem.l D1-D2/A0, -(sp) bsr Ck4DREQ ; movem.l (sp)+, D1-D2/A0 bne.s @doWBacks movem.l D1-D2/A0, -(sp) bsr Ck4SCSIInt ; see if we have a phase change or something movem.l (sp)+, D1-D2/A0 bne.w @phzChange ; bra.b @1 @doWBacks ; clean up the writebacks on the stack frame move.w WB1S(A0), d0 ; check WB1 for validity move.l WB1A(A0), A1 ; pass WB Address move.l WB1D(A0), d1 ; pass WB Data bsr.w DoWriteBack ; to routine that takes care of it @2 movem.l D1-D2/A0, -(sp) bsr Ck4DREQ ; movem.l (sp)+, D1-D2/A0 beq.s @2 move.w WB2S(A0), d0 ; check WB2 for validity move.l WB2A(A0), A1 ; pass WB Address move.l WB2D(A0), d1 ; pass WB Data bsr.w DoWriteBack ; to routine that takes care of it @3 movem.l D1-D2/A0, -(sp) bsr Ck4DREQ ; movem.l (sp)+, D1-D2/A0 beq.s @3 move.w WB3S(A0), d0 ; check WB3 for validity move.l WB3A(A0), A1 ; pass WB Address move.l WB3D(A0), d1 ; pass WB Data bsr.w DoWriteBack ; to routine that takes care of it ; WRITE case for BIOS ********************************* IF RECORD_ON THEN pea 'BEWT' ; move.l d0,-(sp) ; bsr RecordEvent addq.l #8, sp ENDIF ; we have to check the residual register in BIOS.... ; 1. if there is a residual word we have to retrieve it, read another word ; with the posted DREQ and nuke the stack to skip the move.l that buserr'd ; 2. if there is no residual then we can just rte like flint! ; lea BIOSAddr,a1 ; setup for testing BIOS buffer btst.b #bBIOSW1Cmplt,BIOS_PDMA(a1) ; did one word from the long make it to the c96? beq.s @doRTE ; 0=no, so restart move.l ; ; Only 1 word of the long word write made it to the controller so we have ; to move the second word manually and increment the buffer pointer. ; Note: the value of a2 will be the value after the move.l postincrement ; so we have to move it backward a word to access the second buffer word. ; We also have to advance the PC so that the rte doesn't restart the faulted ; instruction. move.l xPC(a0),d0 ; get pc where buserr occurred ; Fake a format code 0 exception frame (4 words) to finish cleaning up clr.w PD3+2(a0) ; stuff a format code 0 (format code is really only a word) move.l d0,PD2+2(a0) ; stuff the new pc in stack move.w xSR(a0),PD2(a0) ; clr.w BIOS_PDMA(a1) ; clear the status register move.w savedSR(a5),sr ; /\ /\ /\ Un Block Ints /\ /\ /\ movem.l (sp)+, savedRegs ; restore regs addq.l #4, sp @adjW040XFrame ; 040 Bus Error frame-cleaning done here lea aeXFrameSize-8(sp),sp ; remove 040 Access Error Exception Frame ;; addq.l #2, a2 ; adjust user buffer because we manually did the move.l

IF RECORD_ON THEN pea 'RTEw' ; move.l d0,-(sp) ; bsr RecordEvent addq.l #8, sp ENDIF rte ; resume execution at next instruction @noDataXfer ;ΡΡΡΡΡΡ IfDebugStr 'In BEH but not Xfer Direction' @doRTE move.w savedSR(a5),sr ; /\ /\ /\ Un Block Ints /\ /\ /\ IF RECORD_ON THEN pea 'RTE-' ; move.l d0,-(sp) ; bsr RecordEvent addq.l #8, sp ENDIF movem.l (sp)+, savedRegs ; restore regs addq.l #4, sp rte ; restart the move.l ; if phase change or timeout, cleanup and abort the transfer ----- @phzChange @cleanup ; return SP to the exception stack frame movem.l (sp)+, savedRegs ; restore regs addq.l #4, sp ; take scratch space off stack ; get any leftover bytes out of the FIFO if we were doing a FastRead move.l HALactionPB.ioPtr(A4),a1 ; get the IOPB move.l scsiFlags(A1), D0 and.l #scsiDirectionMask, D0 ; are we data in or out? cmp.l #scsiDirectionIn, D0 bne.s @skipLeftovers move.b rFIFOflags(a3), d0 ; get FIFO status - how many bytes in FIFO and.w #mFIFOCount, d0 ; ror.l #1, d0 bra.s @btm0 @top0 move.w rDMA(a3), (a2)+ ; get word from chip and put into user's buffer @btm0 dbra d0, @top0 tst.l d0 bpl.s @4 move.b rFIFO(a3), (a2)+ ; get byte from chip and put into user's buffer @4 ; get rid of excp'n frame and create a throwaway frame for return to Transfer_96 @skipLeftovers move.w xSR(sp), d0 ; save SR for new exception frame bfextu FrameType(sp){0:4}, d1 ; get format code from stack cmp.b #AEXFrameType, d1 ; check for 040 Access Error Exception Frame beq.s @Drop040XFrame ; dispose of 040 AE exception frame cmp.b #shortBEXFrameType, d1 ; short 020/030 exception frame? bne.s @Drop46w ; no, so use larger frame adda.w #shortBEXFrameSize, sp ; dispose of the 16-word frame bra.s @DummyFrame ; and finish up @Drop040XFrame ; 040 Bus Error frame-cleaning done here add.w #aeXFrameSize, sp ; remove 040 Access Error Exception Frame bra.s @DummyFrame ; and create dummy return frame @Drop46w add.w #46*2, sp ; size of exception frame @DummyFrame ; Fake a format code 0 exception frame (4 words) to finish cleaning up move.w savedSR(a5),sr ; /\ /\ /\ Un Block Ints /\ /\ /\ clr.w -(sp) ; format code 0 pea FinishErr ; PC value move.w d0, -(sp) ; sr value IF RECORD_ON THEN pea 'RTEp' ; move.l d0,-(sp) ; bsr RecordEvent addq.l #8, sp ENDIF rte ; 'return' from the fake exception ;----------------- FinishErr ;----------------- swap d6 ; d6 contains the number of 64k xfers move.w #0, D6 ; we only care about the high word moveq #0,d0 move.b rXCM(a3),d0 ; TC regr (most-sig. byte) lsl.l #8, d0 ; shift to get lower byte from the c96 move.b rXCL(a3),d0 ; TC regr (least-sig. byte) <- d4.b add.l d0,d6 ; and get the total number of bytes left move.l d6,d1 ; The number of bytes we didn't transfer... move.l HALactionPB.ioPtr(A4),a1 ; get the IOPB move.l scsiFlags(A1), D0 and.l #scsiDirectionMask, D0 ; are we data in or out? cmp.l #scsiDirectionIn, D0 ; We don't need to check the FIFO if we ; are reading. beq.b @ErrorDone moveq.l #mFIFOCount, D0 and.b rFIFOflags(a3), D0 ; how many bytes in FIFO move.b #cFlushFIFO, rCMD(A3) ; we should flush them out of there add.l D0,D1 ; we didn't transfer these bytes sub.l D0,A2 ; adjust the buffer ptr as well @ErrorDone moveq.l #scPhaseErr, d0 ; return premature phase change ; ; Remove our bus error handler ; bsr.w RemoveBEH_BIOS ; get rid of our BEH IF RECORD_ON THEN pea 'Ber!' ; move.l d1,-(sp) ; bsr RecordEvent addq.l #8, sp ENDIF rts ; return to the outer Fast Read/Write routine ; or the HAL if no Handshaking goin on. NAME 'FinishBusErr' ;----------------- DoWriteBack ;----------------- move.l a2,-(sp) ;

btst #bValid, d0 ; if this writeback valid? beq.s @wbDone ; no - done and.w #SIZE_MSK, d0 ; yes, transfer proper size cmp.w #WB_BYTE, d0 bne.s @1 move.B d1, (a1) ; move Byte bra.s @wbDone @1 cmp.w #WB_WORD, d0 bne.s @2 move.W d1, (a1) ; move Word bra.s @wbDone @2 cmp.w #WB_LONG, d0 bne.s @wbDone lea BIOSAddr,a2 ; setup for testing BIOS buffer

thru next

btst.b #bBIOSW1Cmplt,BIOS_PDMA(a2) ; did one word from the long make it to the c96? bne.s @doWord ; move.L d1, (a1) ; move LongWord bra.s @wbDone @doWord ;

thru next

move.w d1,(a1) ; clr.w BIOS_PDMA(a2) ; clear the status register @wbDone move.l (sp)+,a2 ;

rts ENDWITH END