; ; File: SCSIMgr96.a ; ; Contains: SCSI Mgr for the 53C96 ; ; Written by: Far Too Many People ; ; Copyright: © 1990-1993 by Apple Computer, Inc. All rights reserved. ; ; Change History (most recent first): ; ; 9/13/93 SAM Removed a redundant definition of Translate24to32. ; 1/26/93 SAM Put in a PDMDebug only mod to CyclePhases to drop into macsbug ; when it gets called. Only call HwPriv's FlushRangeCodeCache at ; the end of data xfer calls only on 040s running VM. ; 11/3/92 SWC Changed SCSIEqu.a->SCSI.a. ; 10/18/92 CCH Added nop's to support non-serial IO space, and conditionals to ; use byte accesses to the DREQ register. ; <4> 5/22/92 DTY #1029009 : Modified DREQ bit testing from global (setup in ; SCSIInit96). ; <3> 5/1/92 JSM DonÕt use isUniversal conditional, which was always commented ; out anyway. ; <2> 10/24/91 SAM Changed an include of HardwareEqu to HardwarePrivEqu. ; <1> 10/24/91 SAM Rolled in Regatta file. ; ; Regatta Change History ; ; <3> 8/21/91 SAM (PDW) Added a FlushCacheRange call of the BusErrVct (long word ; at 8) after replacing my BEH with the previous BEH. This is to ; fix VM_On/BEHandler bug which is apparently a bug with MOVE16 ; instructions that cause Access Errors (page faults). ; <2> 8/19/91 SAM (PDW) SendCmd_96 did not correctly detect a premature end of ; command phase. Added a wait for FIFO empty or phase change to ; detect this. Turned slow_cable mode off after sending command ; (like we did before B2). ; <1> 8/8/91 SAM (PDW) Checked into Regatta for the first time with lots of ; changes over the TERROR sources. ; <0> 8/8/91 SAM Split off from 7.0 GM sources. ; ; Terror Change History: ; ; 6/27/91 djw (actually PDW) Removed all traces of SCSIBusy and FreeHook ; stuff. Made minimum CDB 1 byte instead of 2. Added slow cable ; mode to command phase to improve reliability. Added multi-stage ; select modeling to handle many more odd phase transitions. ; Rewrote CyclePhase to handle all phases instead of just data ; phases and also to handle new Select sequence flags. ; Jump-vectorized CyclePhase_96. ; 6/14/91 djw (actually PDW) Made mods to Interrupt handling of Select to work ; with devices that go from Select to Status phz (like the ; LaserWriter SC). Also eliminated wait for Select complete ; interrupt at end of Command - we can do it in data phz or ; Complete. Changed Select watchdog to be longer than the timeout ; value used by the '96 (512mS vs 256mS). ; 6/9/91 BG Rolled in Paul Wolf's SCSI changes: Rearranged headers to work ; more consistently between test INIT and ROM builds. (Should not ; affect ROM build). ; 5/24/91 CCH Rolled in Paul Wolf's SCSI fixes: Reworked DoSCSISelect_D96 ; procs: Disabled automatic redirection of select if a device ; disappears. Added 512mS delay after SCSIReset to fix Quantum ; Q250 bug and to be consistent with all old CPUs. ; 4/22/91 BG (actually JMA) Reworked DoSCSIComplete proc and fix SendCmd. ; 3/30/91 BG (actually JMA) Reworked SendCmd and DoSCSISelect procs; fixed ; SCSIStat bug; incorporated new FS hook fix but still is UNUSED. ; 2/17/91 BG (actually JMA) Added Error calls, removed _Debugger calls, fixed ; CyclePhase bug, added DoSCSIBusy_96 support & move stuff around. ; 1/5/91 BG (actually JMA) Added functionalities. ; 12/7/90 JMA Checked into TERROR for the first time. ; ;========================================================================== MACHINE MC68020 BLANKS ON STRING ASIS PRINT OFF LOAD 'StandardEqu.d' INCLUDE 'HardwarePrivateEqu.a' INCLUDE 'SCSI.a' INCLUDE 'SCSIPriv.a' INCLUDE 'UniversalEqu.a' INCLUDE 'SCSIEqu96.a' PRINT ON SCSI96 PROC EXPORT EXPORT SCSIMgr_96 EXPORT DoSCSICmd_96, DoSCSIComplete_96 EXPORT DoSCSISelect_S96, DoSCSISelect_D96 ; EXPORT DoSCSISelAtn_S96, DoSCSISelAtn_D96, CyclePhase_96 ; EXPORT DoSCSIStat_96, DoSCSIMsgOut_96 EXPORT SwapMMU, SCSIpcTo32bit, Error EXPORT Unimplemented_96 EXPORT DoSCSIReset_96, DoSCSIGet_96 ; EXPORT DoSCSIMsgIn_96 ; EXPORT NewSCSIRead_96, NewSCSIWrite_96 EXPORT NewSCSIWBlind_96, NewSCSIRBlind_96 EXPORT SCSIpcTo32Bit ; EXPORT Error, SCSIErr_96 ; IMPORT Xfer1Byte, WaitForSCSIIntrp, WaitForIntNoTime IMPORT Wt4DREQorInt, HandleSelInProg WITH scsiPB, scsiPrivPB WITH scsiGlobalRecord, dcInstr VMGlobals EQU $0B78 ; Pointer to VM's globals (yeah, yeah, had to do it) SAM ;========================================================================== ;------------------------------------------------------------- ; ; A SCSI Function request handler. SCSI's are invoked by params on ; stack followed by a routine selector Int, and return address. ; NOTE: SCSIMgr is a Toolbox trap & follows Pascal calling conventions. ; ; A4 is set up by StdEntry and contains a pointer to the SCSI Manager global ; area in the system heap. ; SCSIMgr_96: ; "Normal" 680x0 CPU's with "SCSIGlobals" lomem move.l (sp)+,a0 ; get the return address move.w (sp)+,d0 ; function selector (word) move.l a0,-(sp) ; push the return address cmp.w #numSelectors,d0 ; valid selector? bhs.s Unimplemented_96 ; Sorry, Charlie link a6,#LocalSize ; locals for bus error stuff movem.l a2-a5/d2-d7,-(sp) ; save registers movea.l SCSIGlobals,a4 ; get pointer to SCSI Manager globals scsiMgrCommon move.l base5380(a4),a3 ; moveq.l #0,zeroReg moveq.l #0,d4 ; needed by read/write move.w d0,d1 ; save a copy of selector move.l 0(a4,d0.w*4),a0 ; indexed addressing jmp (a0) ; and go there ;-------------------------------------------------------------------------- ; StdExit movem.l (sp)+, a2-a5/d2-d7 ; restore these guys unlk a6 move.l (sp)+, a0 ; get return address add.w d0, sp ; fixup the stack jmp (a0) ;-------------------------------------------------------------------------- ; ; Common code when exiting old SCSI Mgr after an error ; ; Uses : d0, a0 ExitCleanup: move.b zeroReg, G_State(a4) ; clear out SCSI Mgr semaphore ; (from ClearState proc, SCSIMgrNew.a) rts ; ;---------------------------------------------------------------- ; ; Unimplemented selector (or out of range) ; Unimplemented_96 moveq.l #dsCoreErr, d0 ; unimplemented core routine _SysError ; sorry, Charlie @1 bra.s @1 ; not safe to continue ;-------------------------------------------------------------------------- ; ; FUNCTION SCSIStat: INTEGER; ; (8) ; ; Get SCSI bus status. ; ; A bit-to-bit bus and status register mapping is not possible from the 53C80 to 53C96. ; Consequently, we fake the status and return what we think the status should be. ; Also note that we OR the two SCSI bus stati(?) because only 2 situations are possbile: ; 1) One bus is active and the other idle 2) both buses are idle. ; ; The following list is the status bit map: ; signal names in parenthesis are the closest c96 status bit equivalents ; ; 53c80 Status Bit 53c96 Status Bits ; DBP n/a ; SEL n/a ; I/O I/O ; C/D C/D ; MSG MSG ; REQ n/a (1 if scBusy) ; BSY n/a (1 if scBusy) ; RST n/a ; ACK n/a ; ATN n/a ; BSY ERR n/a ; PHS MAT n/a ; INT REQ n/a (INT PEND) ; PRT ERR n/a ; DMA REQ DREQ (DMA REQ) ; END DMA n/a ; ; G_FakeStat contains fake bit values that different procs of the SCSI manager ; have set or cleared. The values in it reflect the best-guessed status of the ; bus as determined by the SCSI manager. ; ; Uses: d0, d1, d2 ; ; Return Codes: ; noErr DoSCSIStat_96 and.w #$FFF2, G_FakeStat(a4) ; clear phase bits first, except SEL move.l a3, -(sp) ; save a3 clr.l d1 ; move.l base539x0(a4), a3 ; load ptr for SBus0 move.b rSTA(a3), d1 ; read this bus' status TestFor SCSI96_2Exists ; do we have a 2nd SCSI96 chip? beq.s @noSCSI96 ; bra. if no 2nd SCSI96 move.l base539x1(a4), a3 ; load ptr for SBus1 or.b rSTA(a3), d1 ; OR this bus' status @noSCSI96 ; move.l (sp)+, a3 ; restore a3 andi.b #iPhaseMsk, d1 ; mask off the rest to get phase bits lsl.b #2, d1 ; position the MSG-CD-IO bits or.w d1, G_FakeStat(a4) ; moveq.l #aBSY+aSEL, D0 ; what are Busy and Sel signals set to? and.w G_FakeStat(a4), D0 cmp.w #aBSY, D0 ; if Busy but no Sel, bne.s @1 ; or.w #aREQ, G_FakeStat(a4) ; set REQ also bra.s @2 @1 and.w #$FFFF-aREQ, G_FakeStat(a4) ; else, clear REQ @2 move.w G_FakeStat(a4), 8(a6) ; 8(a6) <- Bus and Status bra.s NoByteExit ; 9(a6) <- Current SCSI Bus Status ;-------------------------------------------------------------------------- ; ; FUNCTION SCSIReset: INTEGER; ; (8) ; ; If we have two scsi buses, hard reset both since the interface does not provide a ; way for the client to specify a target bus in a dual-SCSI bus system. ; ; Uses: a0, a3, d0, d2, d3 ; ; Return Codes: ; noErr DoSCSIReset_96 move.l a3, -(sp) ; save a3 movea.l jvResetBus(a4), a0 ; get address of SCSI reset routine move.l base539x0(a4), a3 ; load ptr for SBus0 jsr (a0) ; reset SCSI bus and kill all requests TestFor SCSI96_2Exists ; do we have a second SCSI96 chip? beq.s @noSCSI96 ; bra. if no 2nd SCSI96 movea.l jvResetBus(a4), a0 ; get address of SCSI reset routine move.l base539x1(a4), a3 ; load ptr for SBus1 jsr (a0) ; reset SCSI bus and kill all requests @noSCSI96 ; move.l (sp)+, a3 ; restore a3 move.l #0, G_FakeStat(a4) ; clear fake status move.w zeroReg, 8(a6) ; always returns 0 status bsr.s ExitCleanup ; clean up to leave moveq.l #0, d2 ; clear upper word thru next move.w TimeDBRA, d2 ; get # of DBRAs per mS lsl.l #8, d2 ; multiply by É lsl.l #1, d2 ; É512 move.l d2, d3 ; ...a 512mS wait swap d3 @1 dbra d2, @1 ; d2 low word of counter dbra d3, @1 ; d3 high word NoByteExit moveq.l #0, d0 ; no arguments to clean up bra.w StdExit ;-------------------------------------------------------------------------- ; ; FUNCTION SCSIGet: INTEGER; ; (8) ; ; SCSI arbitration is now an integrated operation with SELECT ; in the 5394/5396. It is here only for backward compatibility. We will fake ; an asserted BSY & SEL signals just in case the user above decides to perform ; a SCSIStat. ; ; IMPORTANT: SCSIMgr clients MUST still call SCSIGet before SCSISelect. This will be enforced in ; SCSISelect. G_State will be used as a flag to indicate that SCSIGet was called. ; ; Uses: d0, d1, d2, d3 ; ; Return Codes: ; noErr DoSCSIGet_96 bset.b #scBusy, G_State(a4) ; set to SCSI busy state bne.s @isBusy ; if nonzero, SCSI Mgr is in use ; move.l zeroReg, d0 ; disable SCSI interrupts with old SCSI Mgr ; movea.l jvDisEnable(a4), a0 ; addr of interrupt enable/disable routine ; jsr (a0) ; disable interrupts move.w #aBSY+aSEL, G_FakeStat(a4) ; Set to a fake successful arb status moveq.l #noErr, d0 ; no error @exit ; move.w d0, 8(a6) ; return code bra.s NoByteExit @isBusy ; moveq.l #scMgrBusyErr ,d0 ; SCSI Mgr is busy bra.s @exit ; bail out in order to avoid deadlock ;-------------------------------------------------------------------------- ; ; FUNCTION SCSISelect(TargID:INTEGER): INTEGER; ; FUNCTION SCSISelAtn(TargID:INTEGER): INTEGER; ; (8) (10) ; ; Select the target at TargID on the "virtual" SCSI bus. Returns 0 for success, or error. ; Selection can be done with or without the ATN line. ; ; This proc sets up the proper SCSI chip addresses for Select_96 for dual-bus machines. ; It uses the G_SCSIDevMapX bitmap for each bus with 1 bit for each ID on that bus to ; decide which bus to send a selection attempt to. During initialization of the SCSI ; manager, these bitmaps are cleared, indicating that no device has been detected at ; any of the SCSI IDs for either bus. The proper bit is set the first time a successful ; select of that ID on that bus is performed. ; ; Functionally, if no device has been seen on either the internal or external buses, ; the internal bus will be checked first and if that fails, the external bus is checked. ; If both fail (if no device is at that ID on either bus) then neither bit will have ; been set. Also, if a device has been seen once at a certain ID, then that ID will ; only be accessible from that bus until the next reboot. ; ; This routine will first check the state of the requested ID's external bit. If it ; is set, the first and only selection attempt is performed on bus 1 otherwise it is ; attempted on bus 0. If the bus 0 select fails, then the bus 0 bitmap is checked to ; see if there was previously a device at that ID. If there wasn't then a selection ; is sent to bus 1 as well. DoSCSISelect_D96 DoSCSISelAtn_D96 bset.b #scBusy, G_State(a4) ; set SCSI busy state here too for those who bypass SCSIGet move.w G_FakeStat(a4), -(sp) ; save fake status move.w #aBSY, (sp) ; assume successful selection, set fake status moveq.l #0, d6 ; clear upper bits thru next move.w 8(a6), d6 ; get the target's ID cmp.w #scsiSelAtn, d1 ; select with ATN? bne.s @noATN ; no ATN ;@wATN bset.l #16, d6 ; use ATN ori.w #aATN, (sp) ; assume successful select w/ATN, set fake status @noATN move.l d6, G_TargetID(a4) ; remember destination ID & ATN bit btst.b d6, G_SCSIDevMap1(a4) ; if this device is marked as on bus 1... bne.s @SBus1 ; then try select on bus 1 @SBus0 ; else try select on bus 0 move.l base539x0(a4), a3 ; load SCSI base 0 move.l a3, base5380(a4) ; expected SCSI base adrs for this ID for SCSIRead/Write/Complete etc. move.l pdma5380(a4), a0 ; pdma5380 contains DREQ regr address move.l d6, d0 ; copy target ID bsr.w Select_96 ; try it bne.s @fldSel0 ; branch if select failed bset.b d6, G_SCSIDevMap0(a4) ; else record this device's existence bra.s @exit @fldSel0 btst.b d6, G_SCSIDevMap0(a4) ; if this device was marked as on bus 0 bne.s CommonSelErrXit ; then we return an error ; else we try bus 1 @SBus1 move.l base539x1(a4), a3 ; load SCSI base 1 move.l a3, base5380(a4) ; expected SCSI base adrs for this ID for SCSIRead/Write/Complete etc. move.l hhsk5380(a4), a0 ; hhsk5380 contains DREQ regr address move.l d6, d0 ; copy target ID bsr.w Select_96 ; try it bne.s CommonSelErrXit ; branch if select failed bset.b d6, G_SCSIDevMap1(a4) ; else record this device's existence ;; bra.s @exit @exit move.w (sp)+, G_FakeStat(a4) ; save new fake status move.l a0, G_SCSIDREQ(a4) ; save SCSI DREQ regr for SCSIRead/Write/Complete etc. move.w d0, 10(a6) ; return the result moveq.l #2, d0 ; stack cleanup bra.w StdExit ; from prev ;----------------- CommonSelErrXit ; common Select error exit clr.w (sp) ; set failed fake status move.l d0, -(sp) ; save d0 bsr.w ExitCleanup ; clean up to leave move.l (sp)+, d0 ; restore d0 move.l #scsiSelect, d6 ; load proc ID bsr.w Error ; call Error proc - for debug move.w (sp)+, G_FakeStat(a4) ; save new fake status move.w d0, 10(a6) ; return the result moveq.l #2, d0 ; stack cleanup bra.w StdExit ; special exit ;-------------------------------------------------------------------------- ; ; FUNCTION SCSISelect(TargID:INTEGER): INTEGER; ; FUNCTION SCSISelAtn(TargID:INTEGER): INTEGER; ; (8) (10) ; ; Select the target on the bus. Returns 0 for success, or error. ; Selection can be done with or without the ATN line. ; Single SCSI BUS Select Proc DoSCSISelect_S96 DoSCSISelAtn_S96 bset.b #scBusy, G_State(a4) ; set SCSI busy state here too for those who bypass SCSIGet move.w G_FakeStat(a4), -(sp) ; save fake status move.w #aBSY, (sp) ; assume successful selection, set fake status moveq.l #0, d0 ; clear upper bits move.w 8(a6), d0 ; get the target's ID cmp.w #scsiSelAtn, d1 ; select with ATN? bne.s @noATN ; no ATN bset.l #16, d0 ; use ATN ori.w #aATN, (sp) ; assume successful select w/ATN, set fake status @noATN move.l d6, G_TargetID(a4) ; remember destination ID & ATN bit move.l pdma5380(a4), a0 ; pdma5380 contains DREQ regr address bsr.w Select_96 ; try it bne.s CommonSelErrXit ; branch if error @exit move.w (sp)+, G_FakeStat(a4) ; save new fake status move.l a0, G_SCSIDREQ(a4) ; save SCSI DREQ regr move.w d0, 10(a6) ; return the result moveq.l #2, d0 ; stack cleanup bra.w StdExit ;-------------------------------------------------------------------------- ; ; FUNCTION SCSICmd(Buffer:Ptr, Count:INTEGER): INTEGER; ; (10) (8) (14) ; ; Send the target the given command. ; ; Return Codes: ; noErr, scPhaseErr, scCommErr DoSCSICmd_96 move.l 10(a6), a2 ; get command buffer address move.w 8(a6), d2 ; get the length bsr.w SendCMD_96 ; send it bne.s @err ; bra. if error @exit ; thru next T3> move.w d0, 14(a6) ; return the result moveq.l #6, d0 bra.w StdExit @err move.l d0, -(sp) ; save d0 move.l (sp)+, d0 ; restore d0 move.l #scsiCmd, d6 ; load proc ID bsr.w Error ; call Error proc - for debug bra.s @exit ; ;-------------------------------------------------------------------------- ; ; SCSIRead, SCSIRBlind, SCSIWrite, SCSIWBlind ; ; Called by: Toolbox Trap Dispatcher ; ; Calls: Transfer, primitive data transfer routines ; ; On entry: a3 - SCSI read base address ; a4 - pointer to SCSI Manager globals ; ; Internal: a1 - TIB scan pointer ; ; Function: Performs TIB interpretation, and data transfers. NewSCSIWBlind_96 moveq.l #scsiWriteFast, d4 ; select the fast write routine bra.s dataCommon_96 ; continue NewSCSIWrite_96 moveq.l #scsiWriteSlow, d4 ; select the slow write routine bra.s dataCommon_96 ; continue NewSCSIRBlind_96 moveq.l #scsiReadFast, d4 ; select the fast read routine bra.s startRead_96 ; continue NewSCSIRead_96 moveq.l #scsiReadSlow, d4 ; select the slow read routine startRead_96 dataCommon_96 move.l 8(a6), a1 ; get the TIB pointer bra.s @exec ; tighten loop by branching first @c_compare bset.l #scsiCompBit, d4 ; add offset to use the compare routines ; FALL THROUGH to @c_inc ; continue @c_inc movea.l jvTransfer(a4), a0 ; get the address of the transfer routine jsr (a0) ; go to the transfer routine bne.s @data_end ; if error, bail out add.l d1, scParam1(a1) ; increment the pointer bclr.l #scsiCompBit, d4 ; remove offset for the compare routines ; FALL THROUGH to @next_cmd ; continue @next_cmd @c_nop ; also NOP, just skip the command add.w #scSize, a1 ; point to the next TIB instruction ; FALL THROUGH to @exec @exec move.w scOpcode(a1), d1 ; get the function opcode move.l scParam1(a1), a2 ; get the generic address move.l scParam2(a1), d2 ; get the generic count cmp.w #maxOpcode, d1 ; valid opcode ? bhi.s @c_badop ; return err if not add.w d1,d1 ; index times two jmp @JmpTable(d1.w) ; jump into table @JmpTable ; bra.s @c_badop ; 0 is not a valid opcode bra.s @c_inc ; 1 bra.s @c_noinc ; 2 bra.s @c_add ; 3 bra.s @c_move ; 4 bra.s @c_loop ; 5 bra.s @c_nop ; 6 bra.s @c_stop ; 7 bra.s @c_compare ; 8 @c_badop moveq.l #scBadparmsErr, d0 ; bad opcode bra.s @data_end @c_noinc ; NOINC addr,count movea.l jvTransfer(a4), a0 ; get the address of the transfer routine jsr (a0) ; go to the transfer routine bne.s @data_end ; if error, exit bra.s @next_cmd ; else process next command @c_add ; ADD addr,data add.l d2, (a2) ; the count added to the where bra.s @next_cmd ; process the next command @c_move ; MOVE addr1,addr2 move.l d2, a0 ; get the destination address move.l (a2), (a0) ; simple enough bra.s @next_cmd ; process the next command @c_loop ; LOOP relative addr,count tst.l d2 ; check for zero loop count beq.s @next_cmd ; if count is already zero, quit loop subq.l #1, d2 ; drop the count move.l d2, scParam2(a1) ; put the count back for next time beq.s @next_cmd ; if count exhausted, don't loop add.l a2, a1 ; modify the command pointer bra.s @exec ; and process the next command @c_stop moveq.l #noErr, d0 ; indicate no error ; FALL THROUGH to @data_end ; @data_end cmpi.b #cpu68040,CPUFlag ; Do we have an 040? bne.s @TheRealExit ; -> No. No MOVE16. No Problem. cmpi.l #-1,VMGlobals ; VM running? beq.s @TheRealExit ; -> No, no VM, no problem ;--- Flush the cache line that contains location 8 (because of MOVE16 bug) <3> thru next <3> movem.l D0-D2, -(sp) moveq.l #9, D0 ; FlushCacheRange HWPriv selector move.l #8, A0 ; starting address of flush range move.l #4, A1 ; length of range _HWPriv movem.l (sp)+, D0-D2 ; restore regs <3> from last <3> ;--- exit and return result @TheRealExit move.w d0, 12(a6) ; return the result moveq.l #4, d0 ; stack cleanup bra.w StdExit ;-------------------------------------------------------------------------- ; ; FUNCTION SCSIComplete(VAR Stat, Message: INTEGER; wait:LongInt): INTEGER; ; (16) (12) (8) (20) ; ; Complete the SCSI command by: ; 1) Sending command complete sequence byte to get status & msg bytes from target ; 2) Sending message accepted command to complete the cycle ; ; Return Codes: ; noErr, scCommErr, scPhaseErr, scComplPhaseErr DoSCSIComplete_96 move.w zeroReg, -(sp) ; assume no_error return value move.l Ticks, d4 ; get current time add.l 8(a6), d4 ; add count to start ticks jsr HandleSelInProg bne.w @phaseErr @chkStatus moveq.l #iPhaseMsk, d3 ; load mask bits for phase value and.b rSTA(a3), d3 ; get phase bits cmpi.b #iStatus, d3 ; are we in status phase? bne.w @phaseErr ; @inPhase ; we should be in status phase. move.b #cCmdComp, rCMD(a3) ; load cmd complete code if nonSerializedIO Then nop ; Force write to complete. endif jsr WaitForSCSIIntrp ; Wait for intrp w/ timeout ; on exit d5 = rF0S|rINT|0|rSTA beq.w @noStatus ; Branch if timedout @gotStatus clr.l d2 move.b rFFO(a3), d2 ; read status byte from FIFO move.l 16(a6), a1 ; get the Status ptr move.w d2, (a1) ; return status byte @gotMsg move.b rFFO(a3), d2 ; read msg byte from FIFO move.l 12(a6), a1 ; get the Message ptr move.w d2,(a1) ; return the msg byte ; ;move.l d5, d0 ; thru next ; ;andi.b #iPhaseMsk, d0 ; we should be in msg-in phase. ; ;cmpi.b #iMsgIn, d0 ; & *ACK still asserted ; ;bne.s @cmdErr from last move.b #cMsgAcep, rCMD(a3) ; load msg accepted code which de-asserts *ACK if nonSerializedIO Then nop ; Force write to complete. endif jsr WaitForSCSIIntrp ; Wait for intrp w/ timeout ; on exit d5 = rFOS|rINT|0|rSTA beq.w @badAcpt ; Branch if timedout @completeDone move.l #0, G_FakeStat(a4) ; clear fake status move.w (sp)+, 20(a6) ; return error bsr.w ExitCleanup ; clean up to leave moveq.l #12, d0 ; stack cleanup bra.w StdExit ; @phaseErr ; only called for Complete phase errors move.w #scPhaseErr, (sp) ; phase Err but try getting us to status phase jsr ([jvCyclePhase,a4]) ; get us to status phase if possible beq.s @1 ; if cyclephz has no error, keep old one move.w d0, (sp) ; else save this new error in (sp) @1 moveq.l #iPhaseMsk, d3 ; load mask bits for phase value and.b rSTA(a3), d3 ; get phase bits cmpi.b #iStatus, d3 ; are we in status phase? beq.w @inPhase ; yes - go get the data @checkTime cmp.l Ticks, d4 ; got the time, pal? bhi.w @chkStatus ; bra. & check phase if we still have time... ; btst.b #FCIntPend, G_State96(a4) ; timed out; were we waiting for a slow peripheral? ; beq.s @completeDone ; no - exit with whatever status we've got @timedOut @noStatus ; @badAcpt @notDSCErr @cmdErr move.w #scCommErr, (sp) ; return status @badComp move.l (sp), d0 ; load error move.l #scsiComplete, d6 ; load proc ID bsr.w Error ; call Error proc - for debug bra.s @completeDone ; ;-------------------------------------------------------------------------- ; ; FUNCTION SCSIMsgIn(VAR Message: INTEGER): INTEGER; ; (8) (12) ; ; Receive a message from the target. ; ; Return Codes: ; noErr, scCommErr, scPhaseErr DoSCSIMsgIn_96 ; SCSIMsgIn added bsr.w GetMsg_96 bne.s @err ; bra. if error move.l 8(a6), a1 ; get the Message ptr move.w d2, (a1) ; return the Message byte @exit move.w d0, 12(a6) ; return error moveq.l #4, d0 ; stack cleanup thru next bra.w StdExit @err move.l #scsiMsgIn, d6 ; load proc ID bsr.w Error ; call Error proc - for debug bra.s @exit ; ;-------------------------------------------------------------------------- ; ; FUNCTION SCSIMsgOut(Message: INTEGER): INTEGER; ; (8) (10) ; ; Send a message to the target. ; ; Return Codes: ; noErr, scCommErr, scPhaseErr DoSCSIMsgOut_96 ; SCSIMsgOut added move.w 8(a6), d2 ; get the Message in d2 bsr SendMsg_96 bne.s @err ; thru next @exit move.w d0, 10(a6) ; return error moveq.l #2, d0 ; stack cleanup bra.w StdExit @err move.l #scsiMsgIn, d6 ; load proc ID bsr.w Error ; call Error proc - for debug bra.s @exit ;---------------------------------------------------------------- ; Select Device. ; In the 5396, arbitration, selection and command-send operations are integrated operations. ; We need the target id and the actual command descriptor block in order to exploit the ; capabilities of the chip. Unfortunately, the old SCSI manager was structured for separate ; such operations. This proc is an attempt to remain compatible to that type of operation. ; Our goal is to select a target and report the success or failure of that operation. ; This is accomplished by: ; 1) Loading transfer count, TC, with one in order to activate the select process ; 2) Using DMA mode in order to allow us to send the actual CDB during DoSCSICmd proc. ; ; Upon exit, we should be in transfer phase waiting for the client to perform either a ; command send or msg-out--if select w/ ATN. The c96 would be ready and waiting for the ; CDB or message byte because we told it to expect at least 1 byte of data. ; ; Select w/ ATN asserts the ATTENTION line during selection process. This allows the ; client to perform a 1 byte msg-out via SCSIMsgOut. The ATN line is deasserted ; prior to sending the msg byte. ; ; --> A0 = DAFB register address for this c96 (for reading DREQ state) ; d0.w --> Target ID ; d0.b16 --> If bit 16 of d0 is set, then ATN is asserted during selection. ; This is useful for telling the target that we ; want to send it a message before the command phase. ; ; Uses: d0, d1, d2, d3 Select_96 ; set MMU Mode to 32-bit for DREQ test later on move.b MMU32Bit, -(sp) ; save current mode on stack move.l d0, -(sp) ; save D0 jsr SCSIpcTo32bit ; use pc relative moveq #true32B, d0 ; switch to 32-bit mode to look at SCSI config regr jsr SwapMMU ; do it, call _SwapMMUMode jump vector move.l (sp)+, d0 ; restore D0 ; Set up chip for select process (flush FIFO, init dest ID) move.b d0, rDID(a3) ; load select bus ID w/ target ID move.b #cFlshFFO, rCMD(a3) ; Flush FIFO, make sure it's empty if nonSerializedIO Then nop ; Force write to complete. endif btst.l #16, d0 ; is this a Sel w/Atn? bne.s @withAtn ; If Select w/o Atn then set up for 1 DMA byte (last byte of command block) and start process @withoutATN move.b zeroReg, rXCM(a3) ; tell chip that we will be sending 1 move.b #1, rXCL(a3) ; DMA byte (in command phase) move.b #cDMASelWOAtn, rCMD(a3) ; issue Select w/o Atn cmd if nonSerializedIO Then nop ; Force write to complete. endif bset.b #NeedCmdSent, G_State96(a4) ; flag=expect to see a COMMAND phase next bra.s @2 ; If Select w/Atn then set up for 2 DMA bytes (msg_out byte + last byte of command block) @withATN move.b zeroReg, rXCM(a3) ; tell chip that we will be sending 2 move.b #1, rXCL(a3) ; DMA bytes (1 in msg_out, 1 in command) move.b #cDMASelWAtn, rCMD(a3) ; issue Select w/ Atn cmd if nonSerializedIO Then nop ; Force write to complete. endif bset.b #NeedMsgOut, G_State96(a4) ; flag=expect to see a MESSAGE_OUT phase next @2 bset.b #SelInProg, G_State96(a4) ; set flag->select is in prog, expect intrpt ; Set up >512mS timeout for select operation to get somewhere (probably more like 2 seconds) moveq.l #0, d1 ; clear upper word move.w TimeSCSIDB, d1 ; get # of DBRAs lsl.l #8, d1 ; multiply by 256 lsl.l #1, d1 ; multiply by 2 thru next move.l d1, d2 ; ...a 512mS wait for overall watchdog swap d2 moveq.l #-1,d3 ; for debug - last four rSTA reads moveq.l #-1,d4 ; for debug - last four rSQS reads ; We have told the chip to start it, now we wait for an indication that it has completed a ; selection. Several things can happen: ; - the target at the requested ID may not exist or may not respond to the select. In ; this case we will receive a Disconnect interrupt from the chip. ; - the target may select OK but go into something other than Command phz (if Sel w/o Atn) ; or Message_Out phz (if Sel w/Atn). Here we will see a Function Complete/Bus Service ; interrupt. ; - the target may select OK and go into the expected phz - the SUCCESS case. When this ; happens, the chip will assert DREQ because it wants the Command (or Msg_Out) byte. ; - the bus may be hung and the chip is unable to arbitrate. We detect this by timing ; out on the whole process. ; ; So basically, this is a 3-way wait for: ; 1) DREQ asserted ; 2) interrupt ; 3) loop timed out ;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ @waitLoop ; Check for a REQ from the target (within valid phase) IF forPDMDebug THEN ; move.b (a0), d5 ; read DAFB regr (a0=DAFB register addr) ELSE move.l (a0), d5 ; read DAFB regr (a0=DAFB register addr) ENDIF move.b G_bitDREQ(a4),d0 ; load DREQ bit position <4> jab btst.l d0, d5 ; DREQ ? <4> jab bne.s @gotDREQ ; yes: then we have a REQ for Msg_Out or Cmd byte ; Check for an int - either Disconnected (fld select) or Bus Service/Func. Cmplt (good select) lsl.l #8,d3 ; shift old rSTA reads up a byte move.b rSTA(a3),d3 move.b d3,d0 btst.l #bINT,d0 ; check for intrp bit ie. timeout or weird phase dbne d1, @waitLoop ; if intrp then non-normal select op so exit. dbne d2, @waitLoop ; loop until count exhausted or intrp detected ; count is up or intrp detected so we're outta' here beq @timedOut ;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ ; If we got an interrupt, it means that the select command is complete. Either the target did ; not respond (i.e. didn't exist) or it was selected but went to an unexpected phase. @gotINT ; else we got an interrupt move.b rSTA(a3), d3 ; read status for debug move.b rINT(a3), d1 ; clear intrp bit move.b d3, lastSTAread(a4) ; store in case anybody wants to knowÉ move.b d1, lastINTread(a4) ; Éwhat kind of INT we got bclr.b #FCIntPend, G_State96(a4) ; clear the FC Int pend flag bclr.b #SelInProg, G_State96(a4) ; and clear the SelectInProgress flag bclr.b #NeedMsgOut, G_State96(a4) ; and Message_Out bclr.b #NeedCmdSent, G_State96(a4) ; and Command expected flags ; Was it a disconnect interrupt? (i.e. a failed select?) btst.l #bDSC,d1 ; (i.e. timed-out then bus free) beq @goodSEL ; no - assume it is a good select then @badSEL ; from last moveq.l #scCommErr, d2 ; select timeout bra.s @exit @timedOut moveq.l #scArbNBErr, d2 ; select timeout bra @exit ; branch if our watchdog timed out @gotDREQ @goodSEL moveq.l #noErr, d2 ; successful selection @exit move.b (sp)+, d0 ; switch to previous mode jsr SwapMMU ; do it, call _SwapMMUMode jump vector move.l d2, d0 rts ; Check for non-zero sequence value - means select has completed and ;@1 lsl.l #8,d4 ; shift old rSQS reads up a byte ; move.b rSQS(a3), d4 ; determine where we are in select sequence ; move.b d4,d0 ; make copy for checking ; and.b #iSQSMsk, d0 ; did we get somewhere in the sequence? ; beq.s @zeroSSV ; i.e. a non-zero seq.step value ; If we have a non-zero phase value and non-zero sequence, then we had a good select ; lsl.l #8,d3 ; shift old rSTA reads up a byte ; move.b rSTA(a3), d3 ; get phase value ; move.b d3,d0 ; and.b #iPhaseMsk, d0 ; look for info xfer phz (i.e. non zero) ; bne.s @goodSEL ; bra if not in bus-free (we have a data_out hole here) ;-------------------------------------------------------------------------- ; ; Send Command. # bytes in d2. Command block pointer is in a2. ; ; d0 - <-- error (if any) ; d2 - --> number of command bytes ; d5 - <-- xxxx|xxxx|xxxx|rSTA ; ; a2 - ptr to command block ; a3 - SCSI read base address ; a4 - ptr to SCSI Mgr globals ; ; The command descriptor block is sent using both pseudo-DMA and non-pDMA. The non-pDMA ; send appears as preloaded data in the FIFO. The last byte is send via pDMA to satisfy ; the transfer count and DMA-mode of the c96. See SELECT proc. ; ; ; Reminder: Check this routine to make sure it still works with ; Tape Backup 40SC drive. See note in SCSIMgrOld.a, SendCMD routine. ; ; Uses: d0, d1, d3, d5 ; ; Error Codes: ; scPhaseErr, scCommErr ; ; STILL NEEDS: If somehow we end up here but did not have NeedCmdSent but we are actually in Cmd Phase, ; we should probably go ahead and do it anyway - just do the transfer with all rFFO (no rDMA). SendCMD_96 btst.b #NeedCmdSent, G_State96(a4) ; are we expecting a command phase here? beq @notExpected ; no - handle it without DMA move.b #iCommand, d1 ; set up wait for command phase jsr Wt4DREQorInt ; yes - wait till target REQs or screws up beq @gotInt ; if Int then we're not in Command phase ; else, we are, so send the bytes @sendBytes ori.b #SlowCableMode,rCF1(a3) ; set Slow cable mode for the command bytes subq.l #1, d2 ; adjust for DMA of last byte bra.s @btmLoadFIFO ; bra to dbra for zero case (1 byte CDB) @loadFIFO ; loading the FIFO w/o pseudo-DMA will simulate move.b (a2)+, rFFO(a3) ; preloading of the FIFO...we then pDMA the @btmLoadFIFO ; dbra d2, @loadFIFO ; last byte in order to satisfy the c96's DMA ; circuitry & get us that intrp. if nonSerializedIO Then nop ; Force write to complete. endif btst.b #NeedCmdSent, G_State96(a4) ; were we expecting a command phase here? <2> thru next <2> beq.s @skipDMA ; Wait for either FIFO empty or a phase change @wt4EmptyOrPhz moveq.l #iFOFMsk, d1 ; use mask to get FIFO flag field and.b rFOS(a3), d1 ; how many bytes left in FIFO? beq.s @lastCmdByte ; if none, send the last command byte moveq.l #iPhaseMsk, d1 ; and.b rSTA(a3), d1 ; are we in command phase? cmp.b #iCommand, d1 ; beq.s @wt4EmptyOrPhz ; yes - loop again @lastCmdByte moveq.l #iPhaseMsk, d1 ; and.b rSTA(a3), d1 ; get phase bits before sending last byte <2> from prev <2> move.b (a2)+, rDMA(a3) ; Use psuedo DMA to load last byte if nonSerializedIO Then nop ; Force write to complete. endif bclr.b #NeedCmdSent,G_State96(a4) ; no longer need command sent and, bset.b #FCIntPend,G_State96(a4) ; now we can expect a FunctionCmplt interrupt cmp.b #iCommand, d1 ; were we in command phz b4 last byte? <2> bne.s @phaseErr ; no - then we had a phase error <2> @skipDMA moveq.l #iPhaseMsk, d3 ; load mask bits for phase value and.b rSTA(a3), d3 ; get phase value cmpi.b #iCommand, d3 ; are we in command phase? beq.s @cmdHack ; bra. if we're still in cmd phase @cmdOK ; we no longer waste a wait here for the FC interrupt but instead do it in a data ; phase or in SCSIComplete with a call to HandleSelInProg. moveq.l #noErr, d0 ; no error @cmdExit ; At this point, we're out of cmd phase and.b #$FF-SlowCableMode,rCF1(a3) ; turn-off Slow cable mode if nonSerializedIO Then nop ; Force write to complete. endif tst.w d0 ; rts ; This is a hack for the Tape Backup 40SC drive. 40SC asserts an extra REQ on ; 10-byte commands, which appears as a request for an 11th byte (that's what ; the SCSI standard says!) On very fast CPU's, this extra REQ hangs over to ; the next phase of the transaction, causing a phase error, and causes the ; transaction to fail. This delay code will wait at most 256ms ; for a phase change before continuing. ; NOTE: On the c96, this code is probably worthless since the chip will fill ; any additional bytes (for any additional REQs) after the DREQ is fulfilled ; whether we wait for it or not. (See state diagram in Emulex lit.) @cmdHack move.l zeroReg, d0 ; clear upper word move.w TimeSCSIDB, d0 ; get SCSI DBRA's ms timing constant lsl.l #8, d0 ; wait at least 256ms for the tape drive move.l d0, d2 ; set up d2 as high word swap d2 ; @hack moveq.l #iPhaseMsk, d3 ; load mask bits for phase value and.b rSTA(a3), d3 ; get phase value cmpi.b #iCommand, d3 ; are we in command phase? dbne.w d0, @hack ; loop until not cmd phase or timeout dbne.w d2, @hack ; high word of timeout bra.s @cmdOk ; We got our select_process complete interrupt - set our state reflecting that @gotInt bclr.b #FCIntPend, G_State96(a4) ; clear the FC Int pend flag bclr.b #SelInProg, G_State96(a4) ; and clear the SelectInProgress flag bclr.b #NeedMsgOut, G_State96(a4) ; and Message_Out bclr.b #NeedCmdSent, G_State96(a4) ; and Command expected flags move.b #cFlshFFO, rCMD(a3) ; flush the FIFO of unused Command bytes if nonSerializedIO Then nop ; Force write to complete. endif @phaseErr moveq.l #scPhaseErr, d0 ; phase error move.l #scsiCmd, d6 ; load proc ID bsr.w Error ; call Error proc - for debug bra.s @cmdExit ; @notExpected moveq.l #iPhaseMsk, d3 ; load mask bits for phase value and.b rSTA(a3), d3 ; get phase value cmpi.b #iCommand, d3 ; are we in command phase? bne.w @phaseErr ; bra. on phase err addq.l #1, d2 ; adjust for DMA of last byte bra.w @sendBytes ;-------------------------------------------------------------------------- ; ; GetMsg - get a message byte from target and return in d2 ; ; Uses: d0, d2, d3 ; ; Return Codes: ; noErr, scCommErr, scPhaseErr GetMsg_96 moveq.l #iPhaseMsk, d3 ; load mask bits for phase value and.b rSTA(a3), d3 ; get phase value cmpi.b #iMsgIn, d3 ; are we in MsgIn phase already? bne.s @phaseErr ; move.b #cIOXfer, rCMD(a3) ; load Transfer cmd byte in CMD regr if nonSerializedIO Then nop ; Force write to complete. endif jsr WaitForSCSIIntrp ; Wait for intrp w/ timeout ; on exit d5 = xxxx|rSQS|rSTA|rINT beq.s @timedOut ; Branch if timedout move.b rFFO(a3), d2 ; xfer fifo msg byte into d2 w/ *ACK still asserted ; now, unconditionally accept the message byte move.b #cMsgAcep, rCMD(a3) ; load msg accepted code which de-asserts *ACK if nonSerializedIO Then nop ; Force write to complete. endif jsr WaitForSCSIIntrp ; Wait for intrp w/ timeout ; on exit d5 = rFOS|rINT|0|rSTA beq.s @timedOut ; Branch if timedout ; check for disconnect or bus service intrp ; <--- here! moveq.l #noErr, d0 rts @dscErr @timedOut moveq.l #scCommErr, d0 ; comm error bra.s @badGetMsg @phaseErr moveq.l #scPhaseErr, d0 @badGetMsg move.l #0, G_FakeStat(a4) ; Set to a fake stat tst.l d0 ; set the condition code rts ;-------------------------------------------------------------------------- ; ; SendMsg - Send a message byte to the target (in d2 on entry) ; ; This proc assumes that the ATTENTION line has been asserted via SEL w/ ATN. ; Note that only 1 msg byte can be sent via this method. ; ; Entry: ; --> d2 message byte to send ; --> a3 base address of 53c96 ; ; Exit: ; <-- d0 result code ; ; Uses: d0, d1, d3 ; ; Error Codes: ; noErr, scCommErr, scPhaseErr SendMsg_96 btst.b #NeedMsgOut,G_State96(a4) ; did we send a Select w/ATN? beq.s @unexpected ; no - just phase err out of here then move.b #iMsgOut, d1 ; set up wait for command phase jsr Wt4DREQorInt beq.s @gotInt ; if we got an interrupt, something's wrong move.b rSTA(a3), d3 ; get phase value to next and.b #iPhaseMsk, d3 ; load mask bits for phase value cmpi.b #iMsgOut, d3 ; are we in MsgOut phase already? bne.s @phaseErr ; @inPhase ; We probably got here from Select w/ATN which means ; ATN is deasserted prior to transfer of msg byte move.b d2, rFFO(a3) ; xfer msg byte if nonSerializedIO Then nop ; Force write to complete. endif bclr.b #NeedMsgOut,G_State96(a4) ; did we send a Select w/ATN? beq.s @needXfer ; no - just split bset.b #NeedCmdSent,G_State96(a4) ; yes - we took care of MsgOut, now we need to send command bra.s @noErr @needXfer jsr Xfer1Byte bra.s @noErr @unexpected ; wait for 256mS for correct phase jsr HandleSelInProg bne.s @phaseErr moveq.l #0, d1 ; clear upper word move.w TimeSCSIDB, d1 ; get # of DBRAs lsl.l #8, d1 ; multiply by 256 move.l d1, d3 ; ...a 256mS wait swap d3 @1 move.b rSTA(a3), d3 ; get phase value to next and.b #iPhaseMsk, d3 ; load mask bits for phase value cmpi.b #iMsgOut, d3 ; are we in MsgOut phase already? beq.s @inPhase ; yes - send byte dbra d1, @1 ; dbra d3, @1 ; loop until done @gotInt ; bclr.b #FCIntPend, G_State96(a4) ; clear the FC Int pend flag bclr.b #SelInProg, G_State96(a4) ; and clear the SelectInProgress flag bclr.b #NeedMsgOut, G_State96(a4) ; and Message_Out bclr.b #NeedCmdSent, G_State96(a4) ; and Command expected flags @phaseErr moveq.l #scPhaseErr, d0 ; phase error bra.s @clrATN ; @commErr moveq.l #scCommErr, d0 ; comm error bra.s @clrATN ; @noErr moveq.l #noErr, d0 ; no error @clrATN and.w #$FFFF-aATN,G_FakeStat(a4) ; clear fake ATN bit! from prev tst.w d0 ; return results in status register rts ;-------------------------------------------------------------------------- ; ; CyclePhase_96 -- we may have to discard data or write filler bytes to get to ; the desired phase, status phase. ; ; This routine is broken into two main sections; standard I/O and psuedo-DMA. The DMA ; sequence is used in case we are in the middle of a failed DMA transfer which left the ; transfer count nonzero. We only have to handle psuedo-DMA in data phase. All other ; phases (and normal I/O mode data phase) are handled 1 byte at a time by individual ; phase handling code with, after each byte, returns to check the next phase to see ; where to go next. ; ; Phase Action ; ÑÑÑÑÑ ÑÑÑÑÑÑ ; Command: We send as many EE bytes as needed to get out of phase. If the select sequence ; flag 'NeedCmdSent' is set, it turns out that the chip will do the fill by itself ; once we send 1 rDMA byte (thereby completing the transfer) using ; rDMA. We also set the flag 'FCIntPend' bit if 'NeedCmdSent' was set. ; ; Msg_Out: We send as many 08(NOP) bytes as needed to get out of phase. If the select ; sequence flag 'NeedMsgOut' flag is set we set the flag 'NeedCmdSent' and continue. ; ; Data_In: We bitbucket as many bytes as needed to clear phase. ; ; Data_Out: We send as many EE bytes as needed to get out of phase. ; ; ; Note: We need to check if the SCSIMgr is busy in order to differentiate between ; IDLE bus phase and DATA OUT phase. They both have phase value = 0. ; ; Uses: d0 CyclePhase_96 ; (accessed thru jvCyclePhase) btst.b #scBusy, G_State(a4) ; check if SCSIMgr is active beq.s @xferErr ; bra. if not IF forPDMDebug THEN ; Till Jimmy stops ignoring DREQ... PEA @FuckedUpStr _DebugStr ENDIF @checkNextPhase ; to next moveq.l #iPhaseMsk, d0 ; load mask bits for phase value and.b rSTA(a3), d0 ; get phase bits ; cmp #iDataOut, d0 ; beq.s @inDataPhase ; cmp #iDataIn, d0 ; beq.s @inDataPhase ; cmp #iCommand, d0 ; beq @shoveCommand ; cmp #iMsgOut, d0 ; beq.w @shoveMsgOut ; cmp #iMsgIn, d0 ; beq.w @bitbucketMsgIn ; cmp #iStatus, d0 ; bne.s @xferErr @okExit move.w #scComplPhaseErr, d0 ; tell SCSIComplete we had to read/write junk rts ; err code is saved @xferErr ; clr.l d0 ; no recovery rts IF forPDMDebug THEN STRING PASCAL @FuckedUpStr DC.W 'SCSIMgr is in CyclePhases. Bitbucketting Data...' STRING ASIS ENDIF ; If data phase, check if we have been transferring with psuedo-DMA, if so, continue. @inDataPhase move.b rCMD(a3), d1 ; what was our most recent command? cmp.b #cDMAXfer, d1 ; IF command was DMAXfer AND É bne.s @notDMA ; tst.b rXCL(a3) ; Éthere are outstanding pDMA transfers É bne.w @pDMA ; THEN go finish them up with psuedo-DMA tst.b rXCM(a3) bne.w @pDMA @notDMA cmp.b #iDataOut, d0 ; ELSE É what phase? beq.s @shoveDataOut ; data_out - shove data using IO xfers ;; bra.s @bitbucketData ; data_in - bit bucket using normal IO xfers ; Grab and bit bucket a data_in bytes until we've gone into another phase @bitbucketData jsr Xfer1Byte ; xfer 1 byte and wait for intrp w/o timeout ; on exit d5 = rFOS|rINT|0|rSTA bne.s @xferErr ; bra. on xfer error move.b rFFO(a3), d0 ; just empty the FIFO bra.s @checkNextPhase ; Dump out data_out bytes until we've gone into another phase @shoveDataOut move.b #$EE, rFFO(a3) ; load filler byte into FIFO if nonSerializedIO Then nop ; Force write to complete. endif jsr Xfer1Byte ; xfer 1 byte and wait for intrp w/o timeout ; on exit d5 = rFOS|rINT|0|rSTA bne.s @xferErr ; bra. on xfer error bra.s @checkNextPhase ; Dump out Command bytes until we've gone into another phase @shoveCommand bclr.b #NeedCmdSent, G_State96(a4) ; did we expect this? beq.s @nonDMA ; no - bra, do xfer using FIFO move.b #$EE, rDMA(a3) ; yes - use DMA (since chip is waiting for it) if nonSerializedIO Then nop ; Force write to complete. endif bset.b #FCIntPend,G_State96(a4) ; we took care of cmd, now expect FC interrupt jsr WaitForSCSIIntrp beq.s @timedOut ; bra if timedout bclr.b #FCIntPend, G_State96(a4) ; clear the FC Int pend flag bclr.b #SelInProg, G_State96(a4) ; clear the select in progress flag @timedOut ; if we timed out, something's haywire bra.s @2 ; trying again is as good as anything else @nonDMA move.b #$EE, rFFO(a3) ; load filler byte into FIFO if nonSerializedIO Then nop ; Force write to complete. endif jsr Xfer1Byte ; xfer 1 byte and wait for intrp w/o timeout bne.s @xferErr ; bra. on xfer error @2 bra.s @checkNextPhase ; Dump out message_out bytes until we've gone into another phase @shoveMsgOut move.b #$08, rFFO(a3) ; load filler byte into FIFO (NOP message) if nonSerializedIO Then nop ; Force write to complete. endif bclr.b #NeedMsgOut, G_State96(a4) ; did we expect this? beq.s @needXferCmd ; no - branch bset.b #NeedCmdSent,G_State96(a4) ; yes - taking care of MsgOut, now we need cmd bra.s @skipXferCmd @needXferCmd jsr Xfer1Byte ; xfer 1 byte and wait for intrp w/o timeout bne.s @xferErr ; bra. on xfer error @skipXferCmd bra.s @checkNextPhase ; Grab and bit bucket message bytes until we've gone into another phase @bitbucketMsgIn jsr Xfer1Byte ; xfer 1 byte and wait for intrp w/o timeout bne.s @xferErr ; bra. on xfer error move.b rFFO(a3), d0 ; just empty the FIFO move.b #cMsgAcep, rCMD(a3) ; load msg accepted code which de-asserts ACK if nonSerializedIO Then nop ; Force write to complete. endif jsr WaitForSCSIIntrp ; Wait for intrp w/ timeout ; on exit d5 = rFOS|rINT|0|rSTA bra.s @checkNextPhase ; ; Use pseudo-DMA to clean up pending pDMA transfers ie. recovery from bus timeout to next ; It is entirely possible that we might have to use non-pDMA to complete the cleanup ; which is what the upper half of cyclePhase does. @pDMA jsr SCSIpcTo32bit ; use pc relative move.b MMU32Bit, -(sp) ; save current mode on stack moveq #true32B, d0 ; switch to 32-bit mode to look at SCSI config regr jsr SwapMMU ; do it, call _SwapMMUMode jump vector move.l G_SCSIDREQ(a4), a0 ; G_SCSIDREQ contains DREQ regr address moveq.l #iPhaseMsk, d0 ; and.b rSTA(a3), d0 ; cmp #iDataOut, d0 ; what phase? beq.s @pDMAwrite ; data_out - pDMAwrite ;; bra.s @pDMAread ; data_in - pDMAread ; Read data bytes using DMA until DREQ goes away @pDMAread btst.b #bINT, rSTA(a3) ; poll for intrp hopefully from TC zero bne.s @readAll ; bra. if we got one IF forPDMDebug THEN ; move.b (a0), d5 ; read DAFB regr (a0=DAFB register addr) ELSE move.l (a0), d5 ; read DAFB regr (a0=DAFB register addr) ENDIF move.b G_bitDREQ(a4),d0 ; load DREQ bit position <4> jab btst.l d0, d5 ; DREQ ? <4> jab beq.s @pDMAread ; bra. if inactive move.w rDMA(a3), d5 ; bit-bucket data bra.s @pDMAread @readAll ; Intrp will occur when REQ is asserted for the next phase jsr WaitForIntNoTime ; Expecting an intrp, clear it when occurs ; on exit d5 = rFOS|rINT|0|rSTA bra.s @pDMADone ; check if we reached status phase ; Write data bytes using DMA until DREQ goes away @pDMAwrite btst.b #bINT, rSTA(a3) ; poll for intrp hopefully from TC zero bne.s @writeAll ; bra. if we filled all data IF forPDMDebug THEN ; move.b (a0), d5 ; read DAFB regr (a0=DAFB register addr) ELSE move.l (a0), d5 ; read DAFB regr (a0=DAFB register addr) ENDIF move.b G_bitDREQ(a4),d0 ; load DREQ bit position <4> jab btst.l d0, d5 ; DREQ ? <4> jab beq.s @pDMAwrite ; bra. if inactive move.w #$EEEE, rDMA(a3) ; load filler data into FIFO if nonSerializedIO Then nop ; Force write to complete. endif bra.s @pDMAwrite @writeAll ; Intrp will occur when REQ is asserted for the next phase jsr WaitForIntNoTime ; Expecting an intrp, clear it when occurs ; on exit d5 = rFOS|rINT|0|rSTA ; Return to previous MMUMode and check for next phase to cycle out of @pDMADone move.b (sp)+, d0 ; switch to previous mode jsr SwapMMU ; do it, call _SwapMMUMode jump vector bra.w @checkNextPhase ; go check for next phase to cycle out of ; from prev ;_______________________________________________________________________ thru next ; ; SCSIpcTo32Bit ; Inputs: none ; Destroys: A0, D0 ; Calls: _Translate24To32 ; ; Function: Converts the PC to a 32-bit address. ; ; Routine from EdiskDrvr.a ;_______________________________________________________________________ SCSIpcTo32Bit IF not forROM THEN ; SM is always in 32-bit mode move.l (sp), d0 ; put return address in d0 _Translate24to32 ; convert the address to 32-bit mode move.l d0, (sp) ; save the new return address ENDIF rts ; ;_______________________________________________________________________ ; ; TestForDREQ ; This is good for a one time only test. If the value of DREQ ; needs to be determined in a loop, use something else. ;_______________________________________________________________________ ; TestForDREQ jsr SCSIpcTo32bit ; use pc relative moveq #true32B, d0 ; switch to 32-bit mode to look at SCSI config regr jsr SwapMMU ; (sets up d0 with previous mode) move.l G_SCSIDREQ(a4), a0 ; G_SCSIDREQ contains DREQ regr address IF forPDMDebug THEN ; move.b (a0), d5 ; read DAFB regr (a0=DAFB register addr) ELSE move.l (a0), d5 ; read DAFB regr (a0=DAFB register addr) ENDIF jsr SwapMMU ; return to previous mode (in d0) move.b G_bitDREQ(a4),d0 ; load DREQ bit position <4> jab btst.l d0, d5 ; DREQ ? <4> jab rts ;-------------------------------------------------------------------------- ; ; Error -- proc call by most top level SCSI proc when an error occurs ; Primarily for debugging. ; ; Called by: SCSISelect(S/D,ATN), SCSICmd, SCSIRead(Slow/Fast), SCSIWrite(Slow/Fast), ; SCSIComp(Slow/Fast), SCSIMsgIn, SCSIMsgOut, SCSIComplete ; ; d0 -> SCSI error code ; d6 -> SCSI proc ID ; ; Uses: a0 Error movea.l jvErr(a4), a0 ; get address of error routine jsr (a0) ; rts ;-------------------------------------------------------------------------- ; ; SCSIErr -- Primarily for debugging. ; SCSIErr_96 rts ; ;-------------------------------------------------------------------------- ; ; SwapMMU ; Swaps MMU into mode specified in D0. Returns previous ; MMUMode in D0. ; ; Registers : D0 affected as above. No others affected. ; SwapMMU IF not forROM THEN movem.l d1/a0, -(sp) jsr ([jSwapMMU]) ; do it, call _SwapMMUMode jump vector(smashes d1/a0) movem.l (sp)+, d1/a0 ENDIF rts ;========================================================================== ENDWITH END