mirror of
https://github.com/elliotnunn/boot3.git
synced 2024-12-31 23:29:50 +00:00
5b0f0cc134
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included. The Tools directory, containing mostly junk, is also excluded.
1375 lines
47 KiB
Plaintext
1375 lines
47 KiB
Plaintext
;__________________________________________________________________________________________________
|
|
;
|
|
; File: SCSIMgr96BIOS.a
|
|
;
|
|
; Contains: SCSI Mgr for the BIOS based 53c96
|
|
;
|
|
; Written by: James Blair
|
|
;
|
|
; Copyright: © 1992 by Apple Computer, Inc., all rights reserved.
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <SM1> 2/5/93 CSS Checkin from Horror.
|
|
; <H3> 10/17/92 jab Added changes to allow messaging to work successfully.
|
|
; <H2> 10/5/92 jab Fixed bug that was allowing MSG signal to remain in fake status
|
|
; byte.
|
|
; <1> 9/6/92 jab first checked in
|
|
;__________________________________________________________________________________________________
|
|
|
|
|
|
MACHINE MC68020 ; '020-level
|
|
BLANKS ON ; assembler accepts spaces & tabs in operand field
|
|
STRING ASIS ; generate string as specified
|
|
PRINT OFF ; do not send subsequent lines to the listing file
|
|
; don't print includes
|
|
|
|
|
|
LOAD 'StandardEqu.d' ; from StandardEqu.a and for building ROMs
|
|
INCLUDE 'HardwarePrivateEqu.a'
|
|
INCLUDE 'SCSI.a' ; <SM1> CSS
|
|
INCLUDE 'SCSIPriv.a'
|
|
INCLUDE 'UniversalEqu.a' ; for TestFor
|
|
INCLUDE 'SCSIEqu96.a'
|
|
|
|
PRINT ON ; do send subsequent lines to the listing files
|
|
|
|
SCSI96BIOS PROC EXPORT
|
|
|
|
EXPORT SCSIMgr_96_BIOS, DoSCSIReset_96_BIOS
|
|
EXPORT DoSCSICmd_96_BIOS, DoSCSIComplete_96_BIOS
|
|
EXPORT DoSCSISelect_S96_BIOS ;
|
|
EXPORT DoSCSISelAtn_S96_BIOS, CyclePhase_96_BIOS ;
|
|
EXPORT DoSCSIStat_96_BIOS, DoSCSIMsgOut_96_BIOS
|
|
|
|
|
|
EXPORT Unimplemented_96_BIOS
|
|
EXPORT DoSCSIGet_96_BIOS ;
|
|
EXPORT DoSCSIMsgIn_96_BIOS ;
|
|
EXPORT NewSCSIRead_96_BIOS, NewSCSIWrite_96_BIOS
|
|
EXPORT NewSCSIWBlind_96_BIOS, NewSCSIRBlind_96_BIOS
|
|
EXPORT Error_BIOS, SCSIErr_96_BIOS
|
|
|
|
IMPORT Xfer1Byte, WaitForSCSIIntrp, WaitForIntNoTime
|
|
IMPORT Wt4DREQorInt_BIOS, HandleSelInProg_BIOS
|
|
|
|
WITH scsiPB, scsiPrivPB ; access record without explicit qualification
|
|
WITH scsiGlobalRecord, dcInstr
|
|
|
|
;==========================================================================
|
|
|
|
|
|
|
|
;-------------------------------------------------------------
|
|
;
|
|
; 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_BIOS: ; "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_BIOS ; 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_BIOS
|
|
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_BIOS
|
|
and.w #$FFE2, G_FakeStat(a4) ; clear phase bits first, except SEL <H2>
|
|
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_BIOS
|
|
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
|
|
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
|
|
|
|
|
|
|
|
|
|
;-----------------
|
|
|
|
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_BIOS ; 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 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_BIOS
|
|
bset.b #scBusy, G_State(a4) ; set to SCSI busy state
|
|
bne.s @isBusy ; if nonzero, SCSI Mgr is in use
|
|
|
|
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 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_BIOS
|
|
DoSCSISelAtn_S96_BIOS
|
|
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_BIOS ; 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_BIOS
|
|
move.l 10(a6), a2 ; get command buffer address
|
|
move.w 8(a6), d2 ; get the length
|
|
bsr.w SendCMD_96_BIOS ; send it
|
|
bne.s @err ; bra. if error
|
|
@exit ;
|
|
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_BIOS ; 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_BIOS
|
|
moveq.l #scsiWriteFast, d4 ; select the fast write routine
|
|
bra.s dataCommon_96 ; continue
|
|
NewSCSIWrite_96_BIOS
|
|
moveq.l #scsiWriteSlow, d4 ; select the slow write routine
|
|
bra.s dataCommon_96 ; continue
|
|
NewSCSIRBlind_96_BIOS
|
|
moveq.l #scsiReadFast, d4 ; select the fast read routine
|
|
bra.s startRead_96 ; continue
|
|
NewSCSIRead_96_BIOS
|
|
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
|
|
;--- Flush the cache line that contains location 8 (because of MOVE16 bug)
|
|
|
|
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
|
|
|
|
;--- exit and return result
|
|
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_BIOS
|
|
move.w zeroReg, -(sp) ; assume no_error return value
|
|
bclr.b #PendBusFree,G_State96(a4) ; <H3>
|
|
bne.s @completeDone ; <H3>
|
|
move.l Ticks, d4 ; get current time
|
|
add.l 8(a6), d4 ; add count to start ticks
|
|
jsr HandleSelInProg_BIOS
|
|
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
|
|
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO, make sure it's empty <H3>
|
|
move.b #cCmdComp, rCMD(a3) ; load cmd complete code
|
|
bsr.l 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.b #cMsgAcep, rCMD(a3) ; load msg accepted code which de-asserts *ACK
|
|
bsr.l 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_BIOS ; 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_BIOS ; SCSIMsgIn added
|
|
bsr.w GetMsg_96_BIOS
|
|
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
|
|
bra.w StdExit
|
|
@err
|
|
move.l #scsiMsgIn, d6 ; load proc ID
|
|
bsr.w Error_BIOS ; 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_BIOS ; SCSIMsgOut added
|
|
move.w 8(a6), d2 ; get the Message in d2
|
|
bsr SendMsg_96_BIOS
|
|
bne.s @err ;
|
|
@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_BIOS ; 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_BIOS
|
|
; 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
|
|
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 <H3>
|
|
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
|
|
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
|
|
btst.l d0, d5 ; DREQ ?
|
|
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 ;
|
|
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.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_BIOS
|
|
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_BIOS ; 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
|
|
; <H3> thru next <H3>
|
|
bclr.b #SelMsgOut,G_State96(a4) ; should we wait for interrupt if we came from MsgOut ?
|
|
beq.s @sendBytes
|
|
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO, make sure it's empty
|
|
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 #cDMAXfer, rCMD(a3) ; issue Select w/o Atn cmd
|
|
; <H3> from prev <H3>
|
|
@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.
|
|
|
|
btst.b #NeedCmdSent, G_State96(a4) ; were we expecting a command phase here?
|
|
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
|
|
|
|
move.b (a2)+, rDMA(a3) ; Use psuedo DMA to load last byte
|
|
|
|
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?
|
|
bne.s @phaseErr ; no - then we had a phase error
|
|
@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
|
|
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 #SelMsgOut,G_State96(a4) ; should we wait for interrupt if we came from MsgOut ? <H3>
|
|
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
|
|
@phaseErr
|
|
moveq.l #scPhaseErr, d0 ; phase error
|
|
move.l #scsiCmd, d6 ; load proc ID
|
|
bsr.w Error_BIOS ; 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_BIOS
|
|
; <H3> thru next <H3>
|
|
bclr.b #NeedCmdSent,G_State96(a4) ; yes - we took care of MsgOut, now we need to send command
|
|
beq.s @getMsg
|
|
bclr.b #SelMsgOut,G_State96(a4) ; should we wait for interrupt if we came from MsgOut ?
|
|
bne.s @contMsgIn ; 1 - no
|
|
@doWait
|
|
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
|
|
@contMsgIn
|
|
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO, make sure it's empty
|
|
@getMsg ; <H3> from last <H3>
|
|
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
|
|
bsr.l WaitForIntNoTime ; Wait for intrp w/ timeout <H3>
|
|
; on exit d5 = xxxx|rSQS|rSTA|rINT
|
|
;; beq.s @timedOut ; Branch if timedout <H3>
|
|
|
|
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
|
|
bsr.l WaitForIntNoTime ; Wait for intrp w/ timeout <H3> thru next <H3>
|
|
; on exit d5 = rFOS|rINT|0|rSTA
|
|
;; beq.s @timedOut ; Branch if timedout
|
|
|
|
btst.l #bDSC+16,d5 ; was there a disconnected interrupt?
|
|
beq.s @notFree ;
|
|
bclr.b #FCIntPend, G_State96(a4) ; clear the FC Int pend flag
|
|
bclr.b #NeedMsgOut, G_State96(a4) ; and Message_Out
|
|
bset.b #PendBusFree,G_State96(a4)
|
|
bclr.b #NeedCmdSent, G_State96(a4) ; and Command expected flags
|
|
move.b zeroReg, G_State(a4) ; clear fake
|
|
move.l zeroReg, G_FakeStat(a4) ; clear fake
|
|
@notFree
|
|
bclr.b #SelMsgOut,G_State96(a4) ; <H3> from last <H3>
|
|
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_BIOS
|
|
btst.b #NeedMsgOut,G_State96(a4) ; did we send a Select w/ATN?
|
|
beq.w @unexpected ; no - just phase err out of here then
|
|
|
|
move.b #iMsgOut, d1 ; set up wait for message phase
|
|
jsr Wt4DREQorInt_BIOS
|
|
beq.w @gotInt ; if we got an interrupt, something's wrong
|
|
|
|
move.b rSTA(a3), d3 ; get phase value
|
|
and.b #iPhaseMsk, d3 ; load mask bits for phase value
|
|
cmpi.b #iMsgOut, d3 ; are we in MsgOut phase already?
|
|
bne.w @phaseErr ;
|
|
|
|
@inPhase ; We probably got here from Select w/ATN which means
|
|
; ATN is deasserted prior to transfer of msg byte
|
|
|
|
btst.l #bIDENT,d2 ; <H3> thru next <H3>
|
|
bne.s @useFIFO ; if we are sending x80 then we don't want
|
|
; to flush the fifo with the dma write
|
|
|
|
move.b d2, rDMA(a3) ; xfer msg byte
|
|
|
|
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
|
|
; on exit d5 = rFOS|rINT|0|rSTA
|
|
btst.l #bDSC+16,d5 ; was there a disconnected interrupt?
|
|
beq.s @moreMsg ;
|
|
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
|
|
bset.b #PendBusFree,G_State96(a4)
|
|
move.b zeroReg, G_State(a4) ; clear fake
|
|
move.l zeroReg, G_FakeStat(a4) ; clear fake
|
|
moveq.l #noErr, d0 ; no error
|
|
tst.w d0 ; return results in status register
|
|
rts
|
|
@useFIFO
|
|
andi.b #$BF,d2 ; get rid of disconnect priviledge
|
|
move.b d2, rFFO(a3) ; xfer msg byte
|
|
bset.b #NeedCmdSent, G_State96(a4) ; and Command expected flags
|
|
bclr.b #PendBusFree,G_State96(a4)
|
|
bra.s @noErr
|
|
@moreMsg
|
|
; if we have reached here then we have successfully sent our msg byte out and the
|
|
; drive is either going into Cmd phase or MsgIn phase (rejecting the message)
|
|
;
|
|
bset.b #SelMsgOut,G_State96(a4) ;
|
|
bclr.b #NeedMsgOut,G_State96(a4) ;
|
|
move.b d5,d3
|
|
and.b #iPhaseMsk, d3 ; load mask bits for phase value
|
|
cmpi.b #iCommand,d3
|
|
bne.s @setMsgIn
|
|
bset.b #NeedCmdSent, G_State96(a4) ; and Command expected flags
|
|
bclr.b #PendBusFree,G_State96(a4)
|
|
bra.s @noErr
|
|
@setMsgIn ;
|
|
bclr.b #NeedCmdSent, G_State96(a4) ; and Command expected flags
|
|
bset.b #PendBusFree,G_State96(a4)
|
|
bra.s @noErr ; <H3> from last <H3>
|
|
|
|
@unexpected ; wait for 256mS for correct phase
|
|
jsr HandleSelInProg_BIOS
|
|
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
|
|
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!
|
|
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_BIOS ; (accessed thru jvCyclePhase)
|
|
btst.b #scBusy, G_State(a4) ; check if SCSIMgr is active
|
|
beq.s @xferErr ; bra. if not
|
|
|
|
@checkNextPhase ;
|
|
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
|
|
rts ; err code is saved
|
|
|
|
@xferErr ;
|
|
clr.l d0 ; no recovery
|
|
rts
|
|
|
|
|
|
; 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
|
|
bsr.l 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
|
|
bsr.l 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
|
|
bsr.l WaitForSCSIIntrp ; Wait for intrp w/ timeout
|
|
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
|
|
bsr.l 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
|
|
bsr.l 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
|
|
bsr.l 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
|
|
bsr.l 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
|
|
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 SCSI DREQ
|
|
move.b G_bitDREQ(a4),d0 ; load DREQ bit position
|
|
btst.l d0, d5 ; DREQ ?
|
|
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
|
|
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
|
|
; 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 SCSI DREQ
|
|
move.b G_bitDREQ(a4),d0 ; load DREQ bit position
|
|
btst.l d0, d5 ; DREQ ?
|
|
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
|
|
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
|
|
; on exit d5 = rFOS|rINT|0|rSTA
|
|
|
|
; Check for next phase to cycle out of
|
|
|
|
@pDMADone
|
|
bra.w @checkNextPhase ; go check for next phase to cycle out of
|
|
;
|
|
|
|
|
|
;--------------------------------------------------------------------------
|
|
;
|
|
; Error_BIOS -- 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_BIOS
|
|
movea.l jvErr(a4), a0 ; get address of error routine
|
|
jsr (a0) ;
|
|
rts
|
|
|
|
|
|
;--------------------------------------------------------------------------
|
|
;
|
|
; SCSIErr -- Primarily for debugging.
|
|
;
|
|
SCSIErr_96_BIOS
|
|
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_BIOS
|
|
move.l G_SCSIDREQ(a4), a0 ; G_SCSIDREQ contains DREQ regr address
|
|
move.l (a0), d5 ; read DAFB regr
|
|
|
|
move.b G_bitDREQ(a4),d0 ; load DREQ bit position
|
|
btst.l d0, d5 ; DREQ ?
|
|
rts
|
|
|
|
|
|
|
|
|
|
;==========================================================================
|
|
|
|
ENDWITH
|
|
|
|
END
|