sys7.1-doc-wip/OS/SCSIMgr/SCSIMgr96.a

1540 lines
55 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;
; Hacks to match MacOS (most recent first):
;
; <Sys7.1> 8/3/92 Partly reverted <SM10>, removing check for 040+VM from MOVE16 workaround
; Ripped out the (conditional) nops from <SM8>
; 9/2/94 SuperMario ROM source dump (header preserved below)
;
;
; 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):
;
; <SM11> 9/13/93 SAM Removed a redundant definition of Translate24to32.
; <SM10> 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.
; <SM9> 11/3/92 SWC Changed SCSIEqu.a->SCSI.a.
; <SM8> 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 <jab>: Modified DREQ bit testing from global (setup in
; SCSIInit96).
; <3> 5/1/92 JSM Dont 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:
;
; <T9> 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.
; <T8> 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).
; <T7> 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).
; <T6> 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.
; <T5> 4/22/91 BG (actually JMA) Reworked DoSCSIComplete proc and fix SendCmd.
; <T4> 3/30/91 BG (actually JMA) Reworked SendCmd and DoSCSISelect procs; fixed
; SCSIStat bug; incorporated new FS hook fix but still is UNUSED.
; <T3> 2/17/91 BG (actually JMA) Added Error calls, removed _Debugger calls, fixed
; CyclePhase bug, added DoSCSIBusy_96 support & move stuff around.
; <T2> 1/5/91 BG (actually JMA) Added functionalities.
; <T1> 12/7/90 JMA Checked into TERROR for the first time.
;
;========================================================================== <T3>
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 ; <T2>
EXPORT DoSCSISelAtn_S96, DoSCSISelAtn_D96, CyclePhase_96 ; <T9>
EXPORT DoSCSIStat_96, DoSCSIMsgOut_96
EXPORT SwapMMU, SCSIpcTo32bit, Error
EXPORT Unimplemented_96
EXPORT DoSCSIReset_96, DoSCSIGet_96 ; <T2>
EXPORT DoSCSIMsgIn_96 ; <T2>
EXPORT NewSCSIRead_96, NewSCSIWrite_96
EXPORT NewSCSIWBlind_96, NewSCSIRBlind_96
EXPORT SCSIpcTo32Bit ; <T9>
EXPORT Error, SCSIErr_96 ; <T9>
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) <SM10> 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 <T4>
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 ; <T3>
;----------------------------------------------------------------
;
; Unimplemented selector (or out of range)
;
Unimplemented_96
moveq.l #dsCoreErr, d0 ; unimplemented core routine <T3>
_SysError ; sorry, Charlie <T3>
@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 <T4>
; ACK n/a
; ATN n/a
; BSY ERR n/a <T4>
; PHS MAT n/a
; INT REQ n/a (INT PEND)
; PRT ERR n/a <T4>
; DMA REQ DREQ (DMA REQ) <T4>
; 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 <T4>
move.l a3, -(sp) ; save a3
clr.l d1 ;
move.l base539x0(a4), a3 ; load ptr for SBus0 <T2>
move.b rSTA(a3), d1 ; read this bus' status <T2>
TestFor SCSI96_2Exists ; do we have a 2nd SCSI96 chip? <T2>
beq.s @noSCSI96 ; bra. if no 2nd SCSI96 <T2>
move.l base539x1(a4), a3 ; load ptr for SBus1 <T2>
or.b rSTA(a3), d1 ; OR this bus' status <T2>
@noSCSI96 ; <T2>
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 <T2>
; way for the client to specify a target bus in a dual-SCSI bus system. <T2>
;
; Uses: a0, a3, d0, d2, d3
;
; Return Codes:
; noErr
DoSCSIReset_96
move.l a3, -(sp) ; save a3 <T2>
movea.l jvResetBus(a4), a0 ; get address of SCSI reset routine <T2>
move.l base539x0(a4), a3 ; load ptr for SBus0 <T2>
jsr (a0) ; reset SCSI bus and kill all requests <T2>
TestFor SCSI96_2Exists ; do we have a second SCSI96 chip? <T2>
beq.s @noSCSI96 ; bra. if no 2nd SCSI96 <T2>
movea.l jvResetBus(a4), a0 ; get address of SCSI reset routine <T2>
move.l base539x1(a4), a3 ; load ptr for SBus1 <T2>
jsr (a0) ; reset SCSI bus and kill all requests <T2>
@noSCSI96 ; <T2>
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 <T6> thru next <T6>
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 <T6>
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 <T4>
; 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 <T2>
bne.s @isBusy ; if nonzero, SCSI Mgr is in use <T2>
; 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 ; <T2>
move.w d0, 8(a6) ; return code
bra.s NoByteExit
@isBusy ; <T2>
moveq.l #scMgrBusyErr ,d0 ; SCSI Mgr is busy <T2>
bra.s @exit ; bail out in order to avoid deadlock <T2>
;--------------------------------------------------------------------------
;
; 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 <T4>
move.w G_FakeStat(a4), -(sp) ; save fake status <T4>
move.w #aBSY, (sp) ; assume successful selection, set fake status <T4>
moveq.l #0, d6 ; clear upper bits <T6> thru next <T6>
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 <T4>
@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 <T4>
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 ; <T6> from prev <T6>
;-----------------
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 <T9>
;--------------------------------------------------------------------------
;
; 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 <T4>
move.w G_FakeStat(a4), -(sp) ; save fake status <T4>
move.w #aBSY, (sp) ; assume successful selection, set fake status <T4>
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 <T4>
@noATN
move.l d6, G_TargetID(a4) ; remember destination ID & ATN bit <T9>
move.l pdma5380(a4), a0 ; pdma5380 contains DREQ regr address
bsr.w Select_96 ; try it
bne.s CommonSelErrXit ; branch if error <T4>
@exit
move.w (sp)+, G_FakeStat(a4) ; save new fake status <T4>
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 ; <T3> 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 ; <T3>
;--------------------------------------------------------------------------
;
; 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 ; <C846>
@data_end
; ex<SM10> <Sys7.1> Don't restrict this workaround to 040s running VM
;--- 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
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 ; <T8> thru next <T8>
; ;andi.b #iPhaseMsk, d0 ; we should be in msg-in phase.
; ;cmpi.b #iMsgIn, d0 ; & *ACK still asserted
; ;bne.s @cmdErr <T8> from last <T8>
move.b #cMsgAcep, rCMD(a3) ; load msg accepted code which de-asserts *ACK
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 ; <T3>
@phaseErr ; only called for Complete phase errors
move.w #scPhaseErr, (sp) ; phase Err but try getting us to status phase <T9>
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? <T9>
; 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 ; <T5>
;--------------------------------------------------------------------------
;
; 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 <T3>
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 <T3> thru next <T3>
bra.w StdExit
@err
move.l #scsiMsgIn, d6 ; load proc ID
bsr.w Error ; call Error proc - for debug
bra.s @exit ; <T3>
;--------------------------------------------------------------------------
;
; 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 ; <T3> thru next <T3>
@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
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
bset.b #NeedCmdSent, G_State96(a4) ; flag=expect to see a COMMAND phase next <T9>
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
bset.b #NeedMsgOut, G_State96(a4) ; flag=expect to see a MESSAGE_OUT phase next <T9>
@2
bset.b #SelInProg, G_State96(a4) ; set flag->select is in prog, expect intrpt <T8><T9>
; 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 <T8> thru next <T8>
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)
move.l (a0), d5 ; read DAFB regr (a0=DAFB register addr)
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 <T9>
bclr.b #SelInProg, G_State96(a4) ; and clear the SelectInProgress flag <T9>
bclr.b #NeedMsgOut, G_State96(a4) ; and Message_Out <T9>
bclr.b #NeedCmdSent, G_State96(a4) ; and Command expected flags <T9>
; 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 ; <T8> from last <T8>
moveq.l #scCommErr, d2 ; select timeout
bra.s @exit
@timedOut
moveq.l #scArbNBErr, d2 ; select timeout <T9>
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 <T9>
subq.l #1, d2 ; adjust for DMA of last byte <T9>
bra.s @btmLoadFIFO ; bra to dbra for zero case (1 byte CDB) <T9>
@loadFIFO ; loading the FIFO w/o pseudo-DMA will simulate
move.b (a2)+, rFFO(a3) ; preloading of the FIFO...we then pDMA the
@btmLoadFIFO ; <T9>
dbra d2, @loadFIFO ; last byte in order to satisfy the c96's DMA <T2>
; circuitry & get us that intrp. <T2>
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 <T2>
bclr.b #NeedCmdSent,G_State96(a4) ; no longer need command sent and, <T9>
bset.b #FCIntPend,G_State96(a4) ; now we can expect a FunctionCmplt interrupt <T9>
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 <T8>
; phase or in SCSIComplete with a call to HandleSelInProg.
moveq.l #noErr, d0 ; no error <T9>
@cmdExit ; At this point, we're out of cmd phase
and.b #$FF-SlowCableMode,rCF1(a3) ; turn-off Slow cable mode <T9>
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 <Taa>
bclr.b #SelInProg, G_State96(a4) ; and clear the SelectInProgress flag <Taa>
bclr.b #NeedMsgOut, G_State96(a4) ; and Message_Out <Taa>
bclr.b #NeedCmdSent, G_State96(a4) ; and Command expected flags <Taa>
move.b #cFlshFFO, rCMD(a3) ; flush the FIFO of unused Command bytes
@phaseErr
moveq.l #scPhaseErr, d0 ; phase error <T5>
move.l #scsiCmd, d6 ; load proc ID <T5>
bsr.w Error ; call Error proc - for debug <T5>
bra.s @cmdExit ; <T5>
@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 <T9>
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
jsr WaitForSCSIIntrp ; Wait for intrp w/ timeout <T3>
; 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
jsr WaitForSCSIIntrp ; Wait for intrp w/ timeout <T3>
; 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 <T3>
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 <T9> to next <T9>
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
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 <T9> to next <T9>
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 <T9>
bclr.b #NeedCmdSent, G_State96(a4) ; and Command expected flags <T9>
@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! <T9> from prev <T9>
tst.w d0 ; return results in status register
rts
;-------------------------------------------------------------------------- <T3>
;
; 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 <T2>
; IDLE bus phase and DATA OUT phase. They both have phase value = 0. <T2>
;
; Uses: d0
CyclePhase_96 ; (accessed thru jvCyclePhase)
btst.b #scBusy, G_State(a4) ; check if SCSIMgr is active <T2>
beq.s @xferErr ; bra. if not
IF forPDMDebug THEN ; Till Jimmy stops ignoring DREQ... <SM10>
PEA @FuckedUpStr
_DebugStr
ENDIF
@checkNextPhase ; <T9> to next <T9>
moveq.l #iPhaseMsk, d0 ; load mask bits for phase value
and.b rSTA(a3), d0 ; get phase bits
cmp #iDataIn, d0 ;
beq.s @inDataPhase ;
cmp #iDataOut, d0 ;
beq.s @inDataPhase ;
cmp #iCommand, d0 ;
beq.s @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 <T2>
rts ; err code is saved
@xferErr ; <T3>
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
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)
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
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)
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
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 <JMA> to next <JMA>
; 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
move.l (a0), d5 ; read DAFB regr (a0=DAFB register addr)
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
move.l (a0), d5 ; read DAFB regr (a0=DAFB register addr)
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
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
; <T9> from prev <T9>
;_______________________________________________________________________ <T2> thru next <T2>
;
; 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 ; <T2>
;_______________________________________________________________________
;
; 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
move.l (a0), d5 ; read DAFB regr (a0=DAFB register addr)
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