mac-rom/OS/SCSIMgr/SCSIMgrHW96BIOS.a
Elliot Nunn 4325cdcc78 Bring in CubeE sources
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.
2017-12-26 09:52:23 +08:00

1705 lines
62 KiB
Plaintext

;
; File: SCSIMgrHW96BIOS.a
;
; Contains: SCSI Manager BIOS based 53c96 Hardware-Specific routines
;
; Written by: James Blair
;
; Copyright: © 1992, 1994 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <SM2> 1/26/94 rab Removed padForOverpatch stuff from the end of this file
; (SuperMario does not use itÉ).
; <SM1> 2/5/93 CSS Checkin from Horror.
; <H5> 10/17/92 jab Added fixes for doing OneByte read/writes and fixed the lea
; looping problem.
; <H4> 10/5/92 jab Fixed few byte read/write and bus error handler.
; <H3> 9/9/92 jab Fixed word transfer if alignment requires an odd word.
; <H2> 9/9/92 jab Fixed FastRead odd word/byte problem. Now accessing the FIFO
; instead of the DMA register.
; <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
PostNOP EQU 1 ;
LOAD 'StandardEqu.d' ; from StandardEqu.a and for building ROMs
INCLUDE 'HardwarePrivateEqu.a'
INCLUDE 'UniversalEqu.a' ; for VIA bit and -TestFor- definitions
INCLUDE 'SCSI.a' ; <SM1> CSS
INCLUDE 'SCSIPriv.a'
INCLUDE 'SCSIEqu96.a'
INCLUDE 'MC680x0.a'
PRINT ON ; do send subsequent lines to the listing files
SCSIHW96BIOS PROC EXPORT
EXPORT SlowRead_96_BIOS, Transfer_96_BIOS
EXPORT Wt4DREQorInt_BIOS, HandleSelInProg_BIOS
EXPORT ResetBus_96_BIOS
EXPORT BusErrHandler_96_BIOS ;
EXPORT InitHW_SCSI96_BIOS
EXPORT SlowWrite_96_BIOS, SlowComp_96_BIOS ;
EXPORT FastRead_96_BIOS, FastWrite_96_BIOS, FastComp_96_BIOS ;
IMPORT ClearSCSIInt ; from InterruptHandlers.a
; From SCSIMgr96BIOS.a ---
IMPORT Error_BIOS ; from SCSIMgr96BIOS.a
; From SCSIMgr96HW.a ---
IMPORT WaitForIntNoTime, WtForFIFOData ; <H5>
WITH scsiPB, scsiPrivPB ; access record without explicit qualification
WITH scsiGlobalRecord, dcInstr
WITH DecoderInfo, DecoderKinds, ProductInfo ;
;--------------------------------------------------------------------------
;
; InitHW_SCSI96_BIOS - reset and initialize the 53C96 SCSI controller once the glibbly loads.
; This routine resets all functions in the chip and returns
; it to a disconnected state.
;
; INPUTS
; a3 -> pointer to SCSI port base address
;
; OUTPUTS
; d0 <- rINT(a3)
;
; trashes: d0, d1
InitHW_SCSI96_BIOS
move.b #cRstSChp, rCMD(a3) ; load reset-scsi-chip cmd, this cmd has
; the same effect as a hw reset
move.b #cNOP, rCMD(a3) ; NOP required after HW or SW reset
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
move.b #initCF1, rCF1(a3) ; load init config. value which affects:
; busID, SCSI reset reporting & parity checking
move.b #initCF2, rCF2(a3) ; load init config. value
move.b #initCF3, rCF3(a3) ; load init config. value
; Check whether or not we're 25 or 33MHz and set SCSI bus speed values appropriately
; Get machine type + CPU speed information from VIA1
;
; Useful VIA1 PortA bits to read: PA6, PA4, PA2, PA1 ($56)
;
; PA6 = Lego (1), or Frigidaire (0) plastics for Wombat
; PA4, PA2 = CPU Speed. 0=20MHz, 1=25MHz, 2=33MHz, 3=40MHz
; PA1 = WLCD (0) or NOT! [Reserved] (1)
;
; Retrieve CPU Speed information from VIA1 Port A
movem.l a0/a2/d0/d2/d3,-(sp) ; save stuff
movea.l UnivInfoPtr,a0 ; point to the DecoderInfo table
adda.l ProductInfo.DecoderInfoPtr(a0),a0
movea.l VIA1Addr(a0),a2 ; get VIA1 address to get machine/cpu_speed info
moveq #%00101000,d3 ; force VIA1 VDirA to have the correct directions
move.b d3,VDirA(a2) ; ... so we can read the CPU ID extension info
moveq #%00010100,d3 ; get VBufA, bits PA4, PA2 (dont need PA6, PA1)
and.b VBufA(a2),d3 ; get plastics_type/cpu_speed information
lea BIOSAddr,a2 ; get BIOS address for ConfigSonic_SCSI setup
move.b BIOS_SONIC_SCSI(a2),d2 ; save Sonic bit but trash
andi.b #BIOSSCSIFilter,d2 ; everything else
lsr.b #2,d3 ; shift PA4,PA2 down to bits 2-0
bne.s @25MHz ; 0=20MHz, otherwise check higher
@20MHz
ori.b #BIOScfg20MHz,d2 ; setup for and'ing the correct SCSI cfg bits
bra.s @contSetup
@25MHz
subq.b #4,d3 ; split the rest of the values
beq.s @33MHz
bgt.s @40MHz
ori.b #BIOScfg25MHz,d2 ; setup for and'ing the correct SCSI cfg bits
bra.s @contSetup
@33MHz
ori.b #BIOScfg33MHz,d2 ; setup for and'ing the correct SCSI cfg bits
bra.s @contSetup
@40MHz
ori.b #BIOScfg40MHz,d2 ; setup for and'ing the correct SCSI cfg bits
@contSetup
move.b d2,BIOS_SONIC_SCSI(a2) ;
; the rest of this stuff is used to configure the internals of the c96...
move.b #ccf20to25MHz, rCKF(a3) ; load clock conversion factor (CCF) value
move.b #SelTO25Mhz, rSTO(a3) ; load select/reselect timeout value
move.b #initOp, rSYO(a3) ; select syn or async operation; if sync then
; sync offset value must be nonzero
; Set synch xfer period and offset if using
; synch data xfer
move.b rINT(a3), d0 ; read & clear rFOS, rSTA & rINT into throwaway
movem.l (sp)+,a0/a2/d0/d2/d3 ; restore stuff
rts ;
;-------------------------------------------------------------------------- <H5> thru next <H5>
; OneByteRead_BIOS/OneByteWrite_BIOS - proc to transfer 1 byte
;
; d0 - <-- error (if any)
; d1 - --> copy of d2
; d1 - <-- bytes transferred
; d2 - --> number of bytes to transfer
; d3 - scratch - saved
; d4 - --> type of transfer to perform
; d5 - scratch - saved
; d6 - scratch - saved
;
; a0 - scratch - saved
; a1 - SCSI chip read base address - NON-SERIALIZED
; a2 - ptr to data buffer - saved
; a3 - SCSI chip read base address - SERIALIZED
; a4 - ptr to SCSI Mgr globals
; a5 - scratch - saved
OneByteRead_BIOS ;
bra.s @oneReadBot ;
@oneReadTop
move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
; on exit d5 = rFOS|rINT|0|rSTA
bne.w xferErr ; bra. on xfer error
move.b rFFO(a3), (a2)+ ; xfer byte from FIFO into input buffer
@oneReadBot
dbra d2,@oneReadTop ;
move.b rINT(a3), d3 ; read Intrp regr & clear rSTA, rSQS & rINT
btst.l #bDSC, d3 ; check for disconnected intrp
bne.w xferErr ; bra. on xfer error
moveq.l #noErr, d0 ; successful read op
rts
OneByteWrite_BIOS
bra.s @oneWriteBot ;
@oneWriteTop
move.b (a2)+, rFFO(a3) ; preload the FIFO with d2 bytes
@oneWriteBot
dbra d2,@oneWriteTop ;
move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
; on exit d5 = rFOS|rINT|0|rSTA
bne.w xferErr ; bra. on xfer error
moveq.l #noErr, d0 ; successful write op
rts ; <H5> from last <H5>
;--------------------------------------------------------------------------
; Transfer_96 -
;
; Called by: dataCommon_96
; (NewSCSIWBlind_96, NewSCSIWrite_96, NewSCSIRBlind_96, NewSCSIRead_96)
;
; Calls: The primitive data transfer routines
;
; Registers: d0 <-- error, if any
; d1 <-- bytes transferred
; d2 --> number of bytes to transfer
; d4 --> type of transfer to perform (offset into "dataTable")
;
; a2 --> ptr to data buffer
; a3 --> SCSI chip read base address - SERIALIZED
; a4 --> ptr to SCSI Mgr globals
;
; Function: Sets up and dispatches to the simple data-transfer routines
;
; All primitive data transfer routines called by this routine assume:
;
; d0 - <-- error (if any)
; d1 - <-- bytes transferred
; d2 - --> number of bytes to transfer
; d3 - scratch - saved
; d4 - --> type of transfer to perform
; d5 - scratch - saved
; d6 - scratch - saved
;
; a0 - scratch - saved
; a1 - SCSI chip read base address - NON-SERIALIZED
; a2 - ptr to data buffer - saved
; a3 - SCSI chip read base address - SERIALIZED
; a4 - ptr to SCSI Mgr globals
; a5 - scratch - saved
;
;
; Stack frame definitions (frame allocated in StdEntry)
;
XferFrame RECORD {LINK},DECR
returnAddr ds.l 1 ; return address
; no parameters
LINK ds.l 1 ; location of old A6 (after LINK A6)
; local variables (set up and checked in BusErrHandler)
BusErrTO ds.l 1 ; when to stop retrying bus errs
BusErrAddr ds.l 1 ; user's data address when bus err happens
linkSize EQU *
ENDR
WITH XferFrame
Transfer_96_BIOS:
link a6, #linkSize ; allocate local storage
moveq #Max020030BusErrs,d0 ; upper limit for 020s and 030s
cmp.b #cpu68040,CpuFlag ; check if we're on an 040
bne.s @storeValue ; NO ... leave BusErrCount alone
moveq #Max040BusErrs,d0 ; YES ... use 040 MaxBusErr value
@storeValue ;
move.l #0, BusErrAddr(a6) ; init so first bus err is seen as a new one
moveq.l #noErr, d0 ; assume no error
move.l d2, d1 ; make a copy of the count - is it zero ?
beq.w @exit ; if so, get out
movem.l a1-a5/d2-d6, -(sp) ; save registers
movea.l a3,a1 ; point to serialized chip image
adda.l #nonSerlzdDisp,a1 ; point to non-serialized chip image
bsr HandleSelInProg_BIOS ; handle unfinished select command
bne.w @phaseErr ; if it is stuck, we are not in data phase
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
move.l BusErrVct,yeOldeBusErrVct(a4) ; keep old vector in low-mem
move.l jvBusErr(a4), BusErrVct ; install our BE Handler
@1
bset.b #HandleBusErrs, G_State96(a4) ; signal our bus err handler to be operative
move.w d4, transferType(a4) ; store the type of transfer (only word is worth anything)
lea.l dataTable(a4), a0 ; point to data transfer table in globals
movea.l 0(a0,d4.l), a0 ; get the routine address
jsr (a0) ; go to the appropriate routine
@done
bclr.b #HandleBusErrs, G_State96(a4) ; signal our bus err handler to be unoperative
move.l yeOldeBusErrVct(a4),BusErrVct ; restore previous Bus Error vector
movem.l (sp)+, a1-a5/d2-d6 ; restore these guys
tst.w d0 ; set the condition codes
@exit
unlk a6 ; release local storage
rts ;
@phaseErr
moveq.l #scPhaseErr, d0 ; return a phase error
moveq.l #0, d1 ; number of bytes transferred
bra.s @done ;
ENDWITH
phaseErr1
move.l #$F0, d6 ; load proc ID, generic trnasfer
moveq.l #scPhaseErr, d0 ; return a phase error
errExit
jsr Error_BIOS ; call Error proc - for debug
clr.l d1 ; no bytes transferred
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
rts ;
;--------------------------------------------------------------------------
; SlowRead - implements Polled Read
;
; Called by: Transfer
;
; All primitive data transfer routines assume:
;
; d0 - <-- error (if any)
; d1 - --> copy of d2
; d1 - <-- bytes transferred
; d2 - --> number of bytes to transfer
; d3 - scratch - saved
; d4 - --> type of transfer to perform
; d5 - scratch - saved
; d6 - scratch - saved
;
; a0 - scratch - saved
; a1 - SCSI chip read base address - NON-SERIALIZED
; a2 - ptr to data buffer - saved
; a3 - SCSI chip read base address - SERIALIZED
; a4 - ptr to SCSI Mgr globals
; a5 - scratch - saved
;
; Method of Data Transfer: (pDMA and programmed IO)
; 0) Make sure we got our intrp from the last cmd send
; 1) calculate # of 16-byte block transfers to perform using pDMA & remember the remainder
; 2) Enable c96 DMA and wait till the 16-byte FIFO is full and DREQ is asserted for the 17th byte
; 3) Transfer all data in the FIFO and wait for the intrp
; 4) Repeat until all block have been transferred
; 5) Transfer remaining data using non-DMA transfer command byte then
; Wait and poll for byte-in-fifo interrupt
; 6) Transfer data from fifo to input buffer
; 7) Repeat process until all remaining bytes have been transferred
;
SlowRead_96_BIOS
bsr HandleSelInProg_BIOS ; handle unfinished select command
bne.w @phaseErr ; if it is stuck, we are not in data phase
@doRead
moveq.l #iPhaseMsk, d0 ; load mask for phase bits
and.b rSTA(a3), d0 ; are we in data-in phase?
cmpi #iDataIn, d0 ; data-in phase bits = 001
bne.s phaseErr1 ; bra. on phase err
lea rDMA(a1),a1 ; <H5>
clr.l d6 ;
move.l d2, d4 ; d4 = copy of transfer count
lsr.l #4, d4 ; divide xfer count by 16
beq.w @16orLess ; bra. if < 16 bytes
subq.l #1, d4 ; adjust for DBRA
move.l d4, d6 ; d4.w has lower 16-byte block count
swap d6 ; d6.w has upper 16-byte word count
@16orMore
move.l G_SCSIDREQ(a4), a0 ; load SCSI DREQ regr
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
move.b #0, rXCM(a3) ; rXCM = 0, clear most-sig. byte count
move.b #$10, rXCL(a3) ; rXCL = 16 bytes, least-sig. byte value
and.l #$F, d2 ; d2 = remainder word count after 16-byte moves
@read16
moveq.l #iPhaseMsk, d5 ; load mask bits for phase value
and.b rSTA(a3), d5 ; are we still in data-in phase?
cmpi.b #iDataIn, d5 ; data-in phase bits = 001
bne.w @premature2 ; no: probably in status phase - split
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & start loading FIFO
nop ; currently loaded transfer count is used/reused
@1
btst.b #bTRC, rSTA(a3) ; check if we've rcvd all the data
bne.s @4 ; if we have, go get the bytes
btst.b #bINT, rSTA(a3) ; poll for unexpected intrp while waiting
bne.w @prematureEnd ; ... maybe disconnected or something catastrophic.
; premature phase change won't generate intrp bit 'cuz of outstanding DREQ...
; ... we have to check this condition explicitly
moveq.l #iPhaseMsk, d5 ; load mask bits for phase value
and.b rSTA(a3), d5 ; are we still in data-in phase?
cmpi.b #iDataIn, d5 ; data-in phase bits = 001
beq.s @1 ; yes, bra. & keep polling
tst.b rXCL(a3) ; not data-in anymore, have we xferred all data (XCL = 0)?
beq.s @1 ; if yes then there MUST be a transfer count zero bit set
bra.w @prematureEnd ; transfer count not 0 so we have a premature end
; We need 16 guaranteed DREQs to safely transfer 16 bytes without bus error.
; Ideally, DREQ should be active as long there are threshold number of bytes in the
; FIFO--as the c96 user's guide imply. But the c96 implementation also requires that
; REQ be active in order to get DREQ. This is why we must wait for the 17th REQ from
; the target--and it must remain active--before we proceed with the 16-byte transfer.
@4
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 @1 ; loop until asserted
btst #4, rFOS(a3) ; see if FIFO is full
beq.s @1 ; loop until asserted
@10
nop ; squoosh pipeline
move.l (a1),(a2)+ ; read 16 bytes <H4> thru next <H4>
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ; <H4> from last <H4>
IF PostNOP THEN
nop ; squoosh pipeline
ENDIF
; Note that intrp is asserted only after transfer count is 0, FIFO is empty
; and the target asserts REQ for the next byte.
@2
btst.b #bINT, rSTA(a3) ; check for c96 INTRP
beq.s @2 ; loop until we get the intrp
move.b rINT(a3), d5 ; read Intrp regr & clear rSTA, rSQS & rINT
btst.l #bDSC, d5 ; check for disconnected intrp
bne.s @premature2 ; Branch if transfer error
dbra d4, @read16 ; loop until done, d4 is lower word count
dbra d6, @read16 ; loop until done, d6 is upper word count
bra.s @16OrLess ; take care of remaining data, if any
@rdSingle ; use non-pDMA for remainder
moveq.l #iPhaseMsk, d5 ; load mask bits for phase value
and.b rSTA(a3), d5 ; are we still in data-in phase?
cmpi.b #iDataIn, d5 ; data-in phase bits = 001
bne.s @phaseErr ; bra. on phase err
move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers
@3
btst.b #bINT, rSTA(a3) ; check for c96 INTRP
beq.s @3 ; loop until we get an intrp
move.b rFFO(a3), (a2)+ ; xfer byte from FIFO into input buffer
move.b rINT(a3), d5 ; read Intrp regr & clear rSTA, rSQS & rINT
btst.l #bDSC, d5 ; check for disconnected intrp
bne.s @xferErr ; Branch if transfer error
@16OrLess
dbra d2, @rdSingle ; read the rest of the remainders
@goodSRead ; d1 = # of bytes transferred
moveq.l #noErr, d0 ; successful read op
rts ;
; Premature phase change - get leftover bytes out of FIFO, clear DREQ and INTRPT
@prematureEnd
moveq.l #iFOFMsk, d0 ; use mask to get FIFO flag field
and.b rFOS(a3), d0 ; how many bytes left in FIFO?
bra.s @btmLeftovers
@topLeftovers
move.b rFFO(a3), (a2)+
@btmLeftovers
dbra d0,@topLeftovers
@removeDREQ
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 @5 ; if no DREQ, skip dummy rDMA access
move.w rDMA(a3), d5 ; remove that outstanding DREQ (magic),
bra @removeDREQ ; and see if there's more (more magic)
@5 ; and give us that intrp
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
; on exit d5 = rFOS|rINT|0|rSTA
; Premature phase change with no leftover bytes
@premature2
; calc how many bytes we've xferred...
addq.w #1, d4 ; undo adjustment for dbra
swap d6 ; calculate bytes left to transfer
move.w d4, d6 ; form long word count
lsl.l #4, d6 ; mult by 16
and.b #iPhaseMsk, d5 ; are we still in data-in phase?
cmpi.b #iDataIn, d5 ; data-in phase bits = 001
beq.s @xferErr ; bra. to check for disconnect
@phaseErr
moveq.l #scPhaseErr, d0 ; return a phase error
bra.s @badSRead ;
@xferErr ; anything else is a comm. err
moveq.l #scCommErr, d0 ; transfer error
@badSRead
add.l d2, d6 ; add un-xferred remainder
sub.l d6, d1 ; number of bytes transferred
move.l #scsiRead, d6 ; load proc ID
jsr Error_BIOS ; call Error proc - for debug
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
rts ;
;--------------------------------------------------------------------------
; SlowWrite - implements Polled Write
;
; Called by: Transfer
;
; All primitive data transfer routines assume:
;
; d0 - <-- error (if any)
; d1 - --> copy of d2
; d1 - <-- bytes transferred
; d2 - --> number of bytes to transfer
; d3 - scratch - saved
; d4 - --> type of transfer to perform
; d5 - scratch - saved
; d6 - scratch - saved
;
; a0 - scratch - saved
; a1 - SCSI chip read base address - NON-SERIALIZED
; a2 - ptr to data buffer - saved
; a3 - SCSI chip read base address - SERIALIZED
; a4 - ptr to SCSI Mgr globals
; a5 - scratch - saved
;
; Method of Data Transfer: (uses pseudo-DMA mode)
; 0) Make sure we got our intrp from the last cmd send
; 1) Parcel transfer into 64KB blocks since TC regr. handles 64KB max
; 2) Calc. the number of 16-byte block transfers to perform
; 3) Calc. the remaining number of transfers to perform
; 4) Write data 16-byte blocks at a time using word MOVEs
; 5) Wait without timeouts until FIFO is empty ie. 16-byte transfer completed
; 6) Transfer residual byte if there is one
;
SlowWrite_96_BIOS
bsr HandleSelInProg_BIOS ; handle unfinished select command
bne.w @phaseErr ; if it is stuck, we are not in data phase
@doWrite
lea rDMA(a1),a1 ; <H5>
moveq.l #iPhaseMsk, d0 ;
and.b rSTA(a3), d0 ; are we in data-out phase?
bne.w phaseErr1 ; data-out phase bits = 0, bra. on phase err
cmpi.l #3, d2 ; if not 3 or less bytes write then... <H5>
bgt.s @moreThan3 ; jump into move.l loop <H5>
@doSingles
bra.l OneByteWrite_BIOS ; <H5>
@moreThan3
move.l a2,d6 ; get location of buffer
and.l #3,d6 ; odd words or bytes mean alignment req'd
bne.w @alignLoop ;
@aligned
move.l d2, d6 ; d6 = number 64KB block to perform
swap d6 ; upper word of d6 = lower word of d2
andi.l #$0000FFFF, d2 ; mask out upper word
beq.s @2 ; if 0 then we have $10000 (64K) bytes to xfer
@next64KB
moveq.l #iPhaseMsk, d3 ; load mask bits for phase value
and.b rSTA(a3), d3 ; are we still in data-out phase?
bne.w @phaseErr ; bra. if phase err
move.l d2, d4 ; d4 <- d2
move.b d4, rXCL(a3) ; TC regr (least-sig. byte) <- d4.b
lsr.l #8, d4 ; get upper byte of low word
move.b d4, rXCM(a3) ; TC regr (most-sig. byte) <- d4.b
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & begin xfers
nop ; squoosh pipeline
; DREQ* should be active at this time
move.l d2, d4 ; d4 <- d2
lsr.l #4, d4 ; divide xfer count by 16
and.l #15,d2 ; remainder is byte count after 16 byte moves <H5>
ror.l #1, d2 ; xfer byte count to word & remember odd byte
move.l d2,d5 ;
ror.l #1,d5 ; xfer word count to long & remember odd word
neg.w d5
jmp @emptyWait(d5.w*2) ; bra. into the loop
@write16 ;
nop ; squoosh pipeline
move.l (a2)+, (a1) ; do 16 bytes
move.l (a2)+, (a1) ;
move.l (a2)+, (a1) ;
move.l (a2)+, (a1) ;
@emptyWait
IF PostNOP THEN
nop ; squoosh pipeline
ENDIF
btst.b #bINT, rSTA(a3) ; ...poll for unexpected intrp while waiting
bne.s @prematureEnd ; ... maybe disconnected, phase changed, etc.
moveq.l #iPhaseMsk, d3 ; load mask bits for phase value
and.b rSTA(a3), d3 ; are we still in data-out phase?
bne.s @prematureEnd ; bra. if phase err
moveq.l #iFOFMsk, d0 ; use mask to get FIFO flag field
and.b rFOS(a3), d0 ; get # of bytes in FIFO
bne.s @emptyWait ; bra. if FIFO is empty else...
@1
dbra d4, @write16 ; d4 = # of 16-byte tranfers
btst.l #31, d5 ; check if we have a residual word
beq.s @chkByte ; bra. if no residual
@resWord
move.w (a2)+,rDMA(a3) ; xfer residual byte
@chkByte
btst.l #31, d2 ; check if we have a residual byte
beq.s @noResidual ;
@residual
move.b (a2)+, rDMA(a3) ; xfer residual byte
@noResidual
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
; on exit d5 = rFOS|rINT|0|rSTA
bne.s @xferErr ; bra. if xfer err
@2
move.l #$10000, d2 ; init to transfer 64K bytes
dbra d6, @next64KB ;
@goodSWrite ; d1 = # of bytes transferred
moveq.l #noErr, d0 ; successful write op
rts ;
@misAligned ;
subq.l #1, d2 ; adjust for transfer count calc
move.b (a2)+, rFFO(a3) ; ...preload fifo with odd byte
@alignLoop
dbra d6,@misAligned ; keep doing it until we are long aligned
bra.s @aligned ; transfer the rest of data
@prematureEnd ;
; 3 reasons to get an intrp 1) when TC=0 (bus service), xfer done 2) premature
; phase changes (bus service) 3) premature disconnect (disconnect)
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
; on exit d5 = rFOS|rINT|0|rSTA
btst.l #bTRC, d5 ; Maybe we're done xferring data ie. TC=1
bne.s @2 ; ...so proceed with next 64Kb block
moveq.l #iPhaseMsk, d3 ; load mask bits for phase value
and.b d5, d3 ; are we still in data-out phase? (d5 from WaitFor call)
cmpi.b #iDataOut, d3 ; data-out phase bits = 000
beq.s @xferErr ; bra. if not phase err
@phaseErr
moveq.l #scPhaseErr, d0 ; return a phase error
move.l #0, d1 ; no bytes transferred
move.l #0, G_FakeStat(a4) ; Return a fake status
bra.s @bytesXferd
@xferErr
moveq.l #scCommErr, d0 ; comm error
@bytesXferd
lsl.l #4, d4 ; multiply by 16
swap d6 ; calculate bytes left to transfer
move.w d4, d6 ; get low order word
moveq.l #iFOFMsk, d2 ;
and.b rFOS(a3), d2 ; add # of un-xferred data in FIFO
add.l d2, d6 ;
sub.l d6, d1 ; d1 = bytes xferred
@badSWrite
move.l #0, G_FakeStat(a4) ; Return a fake status
move.l #scsiWrite, d6 ; load proc ID
jsr Error_BIOS ; call Error proc - for debug
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
rts ;
;--------------------------------------------------------------------------
; FastWrite - implements Blind Write
;
; Called by: Transfer
;
; All primitive data transfer routines assume:
;
; d0 - <-- error (if any)
; d1 - --> copy of d2
; d1 - <-- bytes transferred
; d2 - --> number of bytes to transfer
; d3 - scratch - saved
; d4 - --> type of transfer to perform
; d5 - scratch - saved
; d6 - scratch - saved
;
; a0 - scratch - saved
; a1 - SCSI chip read base address - NON-SERIALIZED
; a2 - ptr to data buffer - saved
; a3 - SCSI chip read base address - SERIALIZED
; a4 - ptr to SCSI Mgr globals
; a5 - scratch - saved
;
; Method of Data Transfer: (uses pseudo-DMA mode)
; 0) Make sure we got our intrp from the last cmd send
; 1) Parcel transfer into 64KB blocks since TC regr. handles 64KB max
; 2) Preload FIFO with non-aligned byte; get us word aligned
; 3) Calc. the number of 32-byte block transfers to perform
; 4) Calc. the remaining number of transfers to perform
; 5) Write data 32-byte blocks at a time using word MOVEs
; 6) Write remaining data using word MOVEs
; 7) Transfer residual byte if there is one
FastWrite_96_BIOS
bsr HandleSelInProg_BIOS ; handle unfinished select command
bne.w @phaseErr ; if it is stuck, we are not in data phase
@doWrite
lea rDMA(a1),a1 ; <H5>
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
moveq.l #iPhaseMsk, d0 ;
and.b rSTA(a3), d0 ; are we in data-out phase?
bne.w phaseErr1 ; data-out phase bits = 0, bra. on phase err
cmpi.l #3, d2 ; if not 3 or less bytes write then... <H5>
bgt.s @moreThan3 ; jump into move.l loop <H5>
@doSingles
bra.l OneByteWrite_BIOS ; <H5>
@moreThan3
move.l a2,d6 ; get location of buffer
and.l #3,d6 ; odd words or bytes mean alignment req'd
bne.w @alignLoop ;
@aligned
move.l d2, d6 ; d6 = number 64KB block to perform
swap d6 ; upper word of d6 = lower word of d2
andi.l #$0000FFFF, d2 ; mask out upper word
beq @2 ; if 0 then we have $10000 (64K) bytes to xfer
@next64KB ; buffer is aligned from this point
move.l d2, d4 ; d4 <- d2
move.b d4, rXCL(a3) ; TC regr (least-sig. byte) <- d4.b
lsr.l #8, d4 ; get upper byte of low word
move.b d4, rXCM(a3) ; TC regr (most-sig. byte) <- d4.b
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & begin xfers
; DREQ* should be active at this time
move.l d2, d4 ; d4 = copy of transfer count <H5>
lsr.l #7, d4 ; divide xfer count by 128 <H5>
and.l #$7f,d2 ; remainder is byte count after 128 byte moves <H5>
ror.l #1, d2 ; xfer byte count to word & remember odd byte
move.l d2,d3
ror.l #1,d3 ; xfer word count to long & remember odd word
neg.w d3 ; negate to form a backward jump offset
nop ; squoosh pipeline
jmp @WrLoop(d3.w*2) ; bra. into the loop
@write128 ;
move.l (a2)+,(a1) ; write 16 bytes
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ; finished 16 bytes
move.l (a2)+,(a1) ; write 16 bytes
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ; finished 32 bytes
move.l (a2)+,(a1) ; write 16 bytes
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ; finished 48 bytes
move.l (a2)+,(a1) ; write 16 bytes
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ; finished 64 bytes
move.l (a2)+,(a1) ; write 16 bytes
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ; finished 80 bytes
move.l (a2)+,(a1) ; write 16 bytes
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ; finished 96 bytes
move.l (a2)+,(a1) ; write 16 bytes
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ; finished 112 bytes
move.l (a2)+,(a1) ; write 16 bytes
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ;
move.l (a2)+,(a1) ; finished 128 bytes
@WrLoop
dbra d4, @write128 ; d4 = # of 128-byte tranfers
IF PostNOP THEN
nop ; squoosh pipeline
ENDIF
btst.l #31, d3 ; check if we have a residual word
beq.s @chkByte ; bra. if no residual
@resWord
move.w (a2)+,rDMA(a3) ; xfer residual byte
@chkByte
btst.l #31, d2 ; check if we have a residual byte
beq.s @noResidual ;
@resByte
move.b (a2)+,rDMA(a3) ; xfer residual byte
@noResidual ; INT & TC maybe TRUE at this point
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
; on exit d5 = rFOS|rINT|0|rSTA
bne.s @xferErr ; bra. if xfer err
@2
move.l #$10000, d2 ; init to transfer 64K bytes
dbra d6, @next64KB ;
moveq.l #iFOFMsk, d2 ; <H5> thru next <H5>
and.b rFOS(a3), d2 ; add un-xferred byte in FIFO
beq.s @goodFWrite ;
moveq.l #scPhaseErr, d0 ; return a phase error
bra.s @badFWrite ; <H5> from prev <H5>
@goodFWrite ; d1 = # of bytes transferred
moveq.l #noErr, d0 ; successful write op
rts ;
@misAligned ;
subq.l #1, d2 ; adjust for transfer count calc
move.b (a2)+, rFFO(a3) ; ...preload fifo with odd byte
@alignLoop
dbra d6,@misAligned ; keep doing it until we are long aligned
bra.s @aligned ; transfer the rest of data
@phaseErr
moveq.l #scPhaseErr, d0 ; return a phase error
bra.s @bytesXferd ;
@xferErr
moveq.l #scCommErr, d0 ; comm error
@bytesXferd ;
swap d6 ; calculate bytes left to transfer
move.w d4, d6 ; get low order word
lsl.l #5, d6 ; multiply by 32
ext.l d2 ; make d2 a long
addq.l #1, d2 ; undo adjustment for dbra
add.l d2, d6 ; add to total
moveq.l #iFOFMsk, d2 ;
and.b rFOS(a3), d2 ; add un-xferred byte in FIFO
add.w d2, d6 ;
sub.l d6, d1 ; result
@badFWrite ;
move.l #0, G_FakeStat(a4) ; Return a fake status
move.l #scsiWBlind, d6 ; load proc ID
jsr Error_BIOS ; call Error proc - for debug
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
rts ;
;--------------------------------------------------------------------------
; FastRead - implements FastRead
;
; Called by: Transfer
;
; All primitive data transfer routines assume:
;
; d0 - <-- error (if any)
; d1 - --> copy of d2
; d1 - <-- bytes transferred
; d2 - --> number of bytes to transfer
; d3 - scratch - saved
; d4 - --> type of transfer to perform
; d5 - <-- xxxx|xxxx|xxxx|rSTA
; d6 - scratch - saved
;
; a0 - scratch - saved
; a1 - SCSI chip read base address - NON-SERIALIZED
; a2 - ptr to data buffer - saved
; a3 - SCSI chip read base address - SERIALIZED
; a4 - ptr to SCSI Mgr globals
; a5 - scratch - saved
;
; Method of Data Transfer: (uses Pseudo-DMA)
; 0) Make sure we got our intrp from the last cmd send
; 1) Parcel transfer into 64KB blocks since TC regr. handles 64KB max
; 2) Read 1st byte if input buffer is NOT word aligned
; 3) Calc. the number of 32-byte block transfers to perform
; 4) Calc. the remaining number of byte transfers to perform
; 5) Read data 32-byte blocks at a time using word MOVEs
; 6) Read remaining data a word at a time
; 7) Transfer residual byte if there is one
FastRead_96_BIOS
bsr HandleSelInProg_BIOS ; handle unfinished select command
bne.w @phaseErr ; if it is stuck, we are not in data phase
@doRead
lea rDMA(a1),a1 ; <H5>
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
moveq.l #iPhaseMsk, d0 ; load mask for phase bits
and.b rSTA(a3), d0 ; are we in data-in phase?
cmpi.b #iDataIn, d0 ; data-in phase bits = 001
bne.w phaseErr1 ; bra. on phase err
cmpi.l #3, d2 ; if not 3 or less bytes read then... <H5>
bgt.s @moreThan3 ; jump into move.l loop <H5>
@doSingles
bra.l OneByteRead_BIOS ; <H5>
@moreThan3
move.l a2,d6 ; get location of buffer
and.l #3,d6 ; odd words or bytes mean alignment req'd
bne.w @alignLoop ;
@aligned
move.l d2, d6 ; d6 = number 64KB block to perform
swap d6 ; upper word of d6 = lower word of d2
andi.l #$0000FFFF, d2 ; mask out upper word
beq @2 ; if 0 then we have $10000 (64K) bytes to xfer
@next64KB
move.l d2, d4 ; d4 <- d2
move.b d4, rXCL(a3) ; TC regr (least-sig. byte) <- d4.b
lsr.l #8, d4 ; get upper byte of low word
move.b d4, rXCM(a3) ; TC regr (most-sig. byte) <- d4.b
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & begin xfers
; DREQ* should be active at this time
move.l d2, d4 ; d4 = copy of transfer count <H5>
lsr.l #7, d4 ; divide xfer count by 128 <H5>
and.l #$7f,d2 ; remainder is byte count after 128 byte moves <H5>
ror.l #1, d2 ; xfer byte count to word & remember odd byte
move.l d2,d3
ror.l #1,d3 ; xfer word count to long & remember odd word
neg.w d3 ; negate to form a backward jump offset
nop ; squoosh pipeline
jmp @RdLoop(d3.w*2) ; bra. into the loop
@read128 ;
move.l (a1),(a2)+ ; read 16 bytes
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ; finished 16 bytes
move.l (a1),(a2)+ ; read 16 more bytes
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ; finished 32 bytes
move.l (a1),(a2)+ ; read 16 more bytes
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ; finished 48 bytes
move.l (a1),(a2)+ ; read 16 more bytes
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ; finished 64 bytes
move.l (a1),(a2)+ ; read 16 more bytes
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ; finished 80 bytes
move.l (a1),(a2)+ ; read 16 more bytes
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ; finished 96 bytes
move.l (a1),(a2)+ ; read 16 more bytes
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ; finished 112 bytes
move.l (a1),(a2)+ ; read 16 more bytes
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ;
move.l (a1),(a2)+ ; finished 128 bytes
@RdLoop ;
dbra d4, @read128 ; d4 = # of 128-byte transfers
IF PostNOP THEN
nop ; squoosh pipeline
ENDIF
@finshMv16 ; INT & TC bits should be TRUE at this point
btst.l #31, d3 ; check if we have a residual word
beq.s @chkByte ; bra. if no residual
@resWord
bsr.l WtForFIFOData ; returns number of bytes in FIFO <2>
beq.s @timedOut ;
move.w (a1),(a2)+ ; xfer residual word <H3>
@chkByte
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
; on exit d5 = rFOS|rINT|0|rSTA
bne.s @xferErr ; bra. if xfer err
btst.l #31, d2 ; check if we have a residual byte
beq.s @2 ; bra. if no residual
@resByte
bsr.l WtForFIFOData ; returns number of bytes in FIFO
beq.s @timedOut ;
move.b rFFO(a3), (a2)+ ; xfer residual byte
@2
move.l #$10000, d2 ; init to transfer 64K bytes
dbra d6, @next64KB ;
@goodFRead ; d1 = # of bytes transferred
moveq.l #noErr, d0 ; successful read op
rts ;
@misAligned
move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
; on exit d5 = rFOS|rINT|0|rSTA
bne.s @xferErr ; bra. on xfer error
move.b rFFO(a3), (a2)+ ; xfer byte from FIFO into input buffer
move.b rINT(a3), d3 ; read Intrp regr & clear rSTA, rSQS & rINT
btst.l #bDSC, d3 ; check for disconnected intrp
bne.l @xferErr ; bra. on xfer error
subq.l #1, d2 ; decr. for DBRA
@alignLoop
dbra d6,@misAligned ; keep doing it until we are long aligned
bra.s @aligned ; transfer the rest of data
@timedOut ;
moveq.l #scBusTOErr, d0 ; if we timed out, return error
bra.s @bytesXferd ;
@phaseErr
moveq.l #scPhaseErr, d0 ; return a phase error
bra.s @bytesXferd ;
@xferErr
moveq.l #scCommErr, d0 ; comm error
@bytesXferd ;
swap d6 ; calculate bytes left to transfer
move.w d4, d6 ; get low order word
lsl.l #5, d6 ; multiply by 32
ext.l d2 ; make d2 a long
addq.l #1, d2 ; undo adjustment for dbra
add.l d2, d6 ; add to total
sub.l d6, d1 ; d1 = xfer count - bytes remaining to xfer
@badFRead
move.l #0, G_FakeStat(a4) ; Return a fake status
move.l #scsiRBlind, d6 ; load proc ID
jsr Error_BIOS ; call Error proc - for debug
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
rts ;
xferErr ; <H4> thru next <H4>
move.l #$F0, d6 ; load proc ID, generic transfer
moveq.l #scCommErr, d0 ; transfer error
bra.s errExit ; <H4> from last <H4>
;--------------------------------------------------------------------------
;
; ResetBus_96_BIOS - Reset the SCSI bus by asserting the SCSI Reset Output signal
; for some number of uS as determined by the clock conv. factor (CCF)
;
; a3 - SCSI chip read base address - SERIALIZED
;
ResetBus_96_BIOS
; disable all intrps
move.b #cRstSBus, rCMD(a3) ; load reset scsi bus cmd
; re-enable all intrps
rts
;--------------------------------------------------------------------------
;
; HandleSelInProg
;
; 0 if no select in progress or if select is now complete
; 1 if a select is still in progress (i.e. in cmd or msg_out phase)
;
HandleSelInProg_BIOS
btst.b #SelInProg, G_State96(a4) ; is Select cmd still in progress?
beq.w @skipIt ; no - skip it
btst.b #NeedMsgOut, G_State96(a4) ; are we expecting a Msg_Out phase?
beq.s @chkCmd ; no - see about Command
move.b #iMsgOut, d1 ; yes - wait for this phase or interrupt
bra.s @doWait
@chkCmd
btst.b #NeedCmdSent, G_State96(a4) ; are we expecting a Command phase?
beq.s @wtForFC ; no - wait for an interrupt then
move.b #iCommand, d1 ; yes - wait for this phase or interrupt
@doWait
bsr.s Wt4DREQorInt_BIOS
bne.s @gotDREQ
@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
tst.b d0 ; setup result again
@gotDREQ
@skipIt
rts
@wtForFC
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
moveq.l #0, d0
bra.s @gotInt
;--------------------------------------------------------------------------
;
; Wt4DREQorInt - infinite loop to wait for a DREQ signal or SCSI chip intrp.
;
; Uses: d3, d5
;
; Entry:
; --> d1 = phase to wait for (concurrent with DREQ)
;
; --> G_SCSIDREQ(a4) = addr of DAFB reg (for reading value of
;
; Exit:
; <-- d5 = rFOS|rINT|0|rSTA, byte values from the Seq.Step, Status & INT regrs.
; <-- d0 = 1 if DREQ, 0 if Int
;
;-----------------
Wt4DREQorInt_BIOS
; Check for interrupt first (to avoid unnecessary dog-slow DREQ check)
@noTimeout
clr.l d5 ;
move.b rSTA(a3), d5 ; read Status regr
btst.l #bINT, d5 ; poll intrp on status regr. for pending intrp
bne.s @gotInt ;
; If no Interrupt, check for DREQ
move.l G_SCSIDREQ(a4), a0 ; G_SCSIDREQ contains DREQ regr address
move.l (a0), d5 ; read DAFB regr
; DREQ?
move.b G_bitDREQ(a4),d0 ; load DREQ bit position
btst.l d0, d5 ; DREQ ?
beq.s @noTimeout ; no: try again
@gotDREQ
move.b rSTA(a3), d3 ; get phase value
and.b #iPhaseMsk, d3 ; load mask bits for phase value
cmp.b d3, d1 ; are we in requested phase?
bne.s @noTimeout ;
moveq.l #1, d3 ; return value = Got DREQ
bra.s @exit
; Get sequence and FIFO status registers into D5 (already got rSTA)
@gotInt
swap d5 ;
move.b rFOS(a3), d5 ; read FIFO flag/Sequence Step regr
lsl.w #8, d5 ; shift left by 1 byte
move.b rINT(a3), d5 ; read & clear rFOS, rSTA & rINT
move.l d5, d0 ; we got here because of an intrp
swap d5 ; d5 = rFOS|rINT|0|rSTA
moveq.l #0, d3 ; return value = Got Interrup
@exit
move.l d3, d0
rts ;
;--------------------------------------------------------------------------
; FastCompare - implements FastRead
;
; Called by: Transfer
;
; All primitive data transfer routines assume:
;
; d0 - <-- error (if any)
; d1 - --> copy of d2
; d1 - <-- bytes transferred
; d2 - --> number of bytes to transfer
; d3 - scratch - saved
; d4 - --> type of transfer to perform
; d5 - <-- xxxx|xxxx|xxxx|rSTA
; d6 - scratch - saved
;
; a0 - scratch - saved
; a1 - SCSI chip read base address - NON-SERIALIZED
; a2 - ptr to data buffer - saved
; a3 - SCSI chip read base address - SERIALIZED
; a4 - ptr to SCSI Mgr globals
; a5 - scratch - saved
;
; Method of Data Transfer: (uses Pseudo-DMA)
; 0) Make sure we got our intrp from the last cmd send
; 1) Parcel transfer into 64KB blocks since TC regr. handles 64KB max
; 2) Read 1st byte if input buffer is NOT word aligned
; 3) Calc. the number of 32-byte block transfers to perform
; 4) Calc. the remaining number of byte transfers to perform
; 5) Read data 32-byte blocks at a time using word MOVEs
; 6) Read remaining data a word at a time
; 7) Transfer residual byte if there is one
FastComp_96_BIOS
bsr HandleSelInProg_BIOS ; handle unfinished select command
bne.w @phaseErr ; if it is stuck, we are not in data phase
@doRead
moveq.l #iPhaseMsk, d0 ; load mask for phase bits
and.b rSTA(a3), d0 ; are we in data-in phase?
cmpi.b #iDataIn, d0 ; data-in phase bits = 001
bne.w phaseErr1 ; bra. on phase err
cmpi.l #1, d2 ; special case a 1 byte compare
beq.w SlowComp_96_BIOS ;
clr.l d3 ; init compare status
move.l d2, d6 ; d6 = number 64KB block to perform
swap d6 ; upper word of d6 = lower word of d2
andi.l #$0000FFFF, d2 ; mask out upper word
beq.s @2 ; if 0 then we have $10000 (64K) bytes to xfer
@next64KB
move.l d2, d4 ; d4 <- d2
move.b d4, rXCL(a3) ; TC regr (least-sig. byte) <- d4.b
lsr.l #8, d4 ; get upper byte of low word
move.b d4, rXCM(a3) ; TC regr (most-sig. byte) <- d4.b
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & begin xfers
; DREQ* should be active at this time
ror.l #1, d2 ; xfer byte count to word & remember odd byte
subq.w #1, d2 ; adjust for DBRA
@RdAndCmp
move.w rDMA(a3), d0 ;
cmp.w (a2)+, d0 ; compare a word at a time
beq.s @Ok
moveq.l #scCompareErr, d3 ; record a compare error
@Ok
dbra d2, @RdAndCmp ; loop until done
; INT & TC bits should be TRUE at this point
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
; on exit d5 = rFOS|rINT|0|rSTA
bne.s @xferErr ; bra. if xfer err
btst.l #31, d2 ; check if we have a residual byte
beq.s @2 ; bra. if no residual
@residual
move.b rFFO(a3), d0 ; read byte data from FIFO
cmp.b (a2)+, d0 ; xfer byte from FIFO into input buffer
beq.s @2
moveq.l #scCompareErr, d3 ; record a compare error
@2
move.l #$10000, d2 ; init to transfer 64K bytes
dbra d6, @next64KB ;
@ExitFCmp ; d1 = # of bytes transferred
move.l d3, d0 ; return status
rts ;
@phaseErr
moveq.l #scPhaseErr, d0 ; return a phase error
bra.s @bytesXferd
@xferErr
moveq.l #scCommErr, d0 ; comm error
@bytesXferd
swap d6 ; calculate bytes left to transfer
move.w d4, d6 ; get low order word
lsl.l #5, d6 ; multiply by 32
ext.l d2 ; make d2 a long
addq.l #1, d2 ; undo adjustment for dbra
add.l d2, d6 ; add to total
sub.l d6, d1 ; d1 = xfer count - bytes remaining to xfer
@badFRead
move.l #0, G_FakeStat(a4) ; Return a fake status
move.l #scsiRBlind+$F0, d6 ; load proc ID, Fast compare
jsr Error_BIOS ; call Error proc - for debug
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
rts ;
;--------------------------------------------------------------------------
; SlowComp - implements Polled Read
;
; Called by: Transfer
;
; All primitive data transfer routines assume:
;
; d0 - <-- error (if any)
; d1 - --> copy of d2
; d1 - <-- bytes transferred
; d2 - --> number of bytes to transfer
; d3 - scratch - saved
; d4 - --> type of transfer to perform
; d5 - scratch - saved
; d6 - scratch - saved
;
; a0 - scratch - saved
; a1 - SCSI chip read base address - NON-SERIALIZED
; a2 - ptr to data buffer - saved
; a3 - SCSI chip read base address - SERIALIZED
; a4 - ptr to SCSI Mgr globals
; a5 - scratch - saved
;
; Method of Data Transfer: (pDMA and programmed IO)
; 0) Make sure we got our intrp from the last cmd send
; 1) calculate # of 16-byte block transfers to perform using pDMA & remember the remainder
; 2) Enable c96 DMA and wait till the 16-byte FIFO is full and DREQ is asserted for the 17th byte
; 3) Transfer all data in the FIFO and wait for the intrp
; 4) Repeat until all block have been transferred
; 5) Transfer remaining data using non-DMA transfer command byte then
; Wait and poll for byte-in-fifo interrupt
; 6) Transfer data from fifo to input buffer
; 7) Repeat process until all remaining bytes have been transferred
SlowComp_96_BIOS
bsr HandleSelInProg_BIOS ; handle unfinished select command
bne.w @phaseErr ; if it is stuck, we are not in data phase
@doRead
moveq.l #iPhaseMsk, d0 ; load mask for phase bits
and.b rSTA(a3), d0 ; are we in data-in phase?
cmpi #iDataIn, d0 ; data-in phase bits = 001
bne.w phaseErr1 ; bra. on phase err
clr.l d3 ; init compare status
move.l d2, d4 ; d4 = copy of transfer count
lsr.l #4, d4 ; divide xfer count by 16
beq.w @16orLess ; bra. if < 16 bytes
subq.l #1, d4 ; adjust for DBRA
move.l d4, d6 ; d4.w has lower 16-byte block count
swap d6 ; d6.w has upper 16-byte word count
@16orMore
move.l G_SCSIDREQ(a4), a0 ; load SCSI DREQ regr
move.b #0, rXCM(a3) ; rXCM = 0, clear most-sig. byte count
move.b #$10, rXCL(a3) ; rXCL = 16 bytes, least-sig. byte value
and.l #$F, d2 ; d2 = remainder word count after 16-byte moves
@read16
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & start loading FIFO
@1 ; currently loaded transfer count is used/reused
btst.b #bINT, rSTA(a3) ; poll for unexpected intrp while waiting
bne.s @prematureEnd ; ... maybe disconnected, phase changed, etc.
btst.b #bTRC, rSTA(a3) ; check if we've rcvd all the data
beq.s @1 ; loop until FIFO is full
; We need 16 guaranteed DREQs to safely transfer 16 bytes without bus error.
; Ideally, DREQ should be active as long there are threshold number of bytes in the
; FIFO--as the c96 user's guide imply. But the c96 implementation also requires that
; REQ be active in order to get DREQ. This is why we must wait for the 17th REQ from
; the target--and it must remain active--before we proceed with the 16-byte transfer.
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 @1 ; loop until asserted
move.w #$7, d5 ; load loop counter, 8 words
@cmpFFO
move.w rDMA(a3), d0 ;
cmp.w (a2)+, d0 ; compare a word at a time
beq.s @Ok1
moveq.l #scCompareErr, d3 ; record a compare error
@Ok1 dbra d5, @cmpFFO ; continue with compare operation
@2 ; Intrp should occur after ALL data have been
; read out of the FIFO
btst.b #bINT, rSTA(a3) ; check for c96 INTRP
beq.s @2 ; loop until we get the intrp
move.b rINT(a3), d5 ; read Intrp regr & clear rSTA, rSQS & rINT
btst.l #bDSC, d5 ; check for disconnected intrp
bne.s @premature2 ; Branch if transfer error
dbra d4, @read16 ; loop until done, d4 is lower word count
dbra d6, @read16 ; loop until done, d6 is upper word count
bra.s @16OrLess ; take care of remaining data, if any
@rdSingle ; use non-pDMA for remainder
moveq.l #iPhaseMsk, d5 ; load mask bits for phase value
and.b rSTA(a3), d5 ; are we still in data-in phase?
cmpi.b #iDataIn, d5 ; data-in phase bits = 001
bne.s @phaseErr ; bra. on phase err
move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers
@3
btst.b #bINT, rSTA(a3) ; check for c96 INTRP
beq.s @3 ; loop until we get an intrp
move.b rFFO(a3), d0 ; read byte data from FIFO
cmp.b (a2)+, d0 ; xfer byte from FIFO into input buffer
beq.s @Ok2
moveq.l #scCompareErr, d3 ; record a compare error
@Ok2
move.b rINT(a3), d5 ; read Intrp regr & clear rSTA, rSQS & rINT
btst.l #bDSC, d5 ; check for disconnected intrp
bne.s @xferErr ; Branch if transfer error
@16OrLess
dbra d2, @rdSingle ; read the rest of the remainders
@ExitCmp ; d1 = # of bytes transferred
move.l d3, d0 ; return status
rts ;
@prematureEnd
bsr.l WaitForIntNoTime ; Wait for intrp w/o timeout
; on exit d5 = rFOS|rINT|0|rSTA
@premature2
addq.w #1, d4 ; undo adjustment for dbra
swap d6 ; calculate bytes left to transfer
move.w d4, d6 ; form long word count
lsl.l #4, d6 ; mult by 16
and.b #iPhaseMsk, d5 ; are we still in data-in phase?
cmpi.b #iDataIn, d5 ; data-in phase bits = 001
beq.s @xferErr ; bra. to check for disconnect
@phaseErr
moveq.l #scPhaseErr, d0 ; return a phase error
bra.s @badSCmp ;
@xferErr ; anything else is a comm. err
moveq.l #scCommErr, d0 ; transfer error
@badSCmp
add.l d2, d6 ; add un-xferred remainder
sub.l d6, d1 ; number of bytes transferred
move.l #scsiRead+$F0, d6 ; load proc ID, Slow compare
jsr Error_BIOS ; call Error proc - for debug
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO
rts ;
;___________________________________________________________________________
;
; BusErrHandler_96
; When the SCSI Mgr is performing a blind data transfer, it patches
; out the bus error vector. The old SCSI Mgr bus error handler
; assumed that if it got called, it must be handling a SCSI bus error.
; Unfortunately, NuBus cards could bus error while the SCSI Mgr is
; installed. To be a better bus error citizen, the SCSI bus error
; handler now checks that the fault address is the SCSI chip, and if
; not, it chains to the bus error handler that it replaced.
;
; This code returns control to Transfer_96 and not to the routine
; caused the bus error. It does this by popping off the buserr stack
; frame and then doing an RTS, so...
; DON'T PUT ANYTHING ON THE STACK IN TRANSFER ROUTINES (FastRead,
; FastÉ, etc.). At least don't leave it there during periods where a
; buserr may be possible.
;
___________________________________________________________________________;
WITH AEXFrame, XferFrame
savedRegs REG d0-d3/a0-a3 ; save these registers because we need to use them
savedRSize EQU 8*4 ; # bytes on stack for saved registers
BusErrHandler_96_BIOS
; Is it our fault? -----
subq.l #4, sp ; make room for return addr (@notSCSIFault)
movem.l savedRegs, -(sp) ;
move.l SCSIBase,a3 ; save DMA access address <H4>
lea savedRSize+4(sp), a0 ; make A0 our AEXFrame pointer (regs+1 LW on stack)
cmp.l SCSIGlobals, a4 ; equal if this is from our transfer routine
bne.s @notSCSIFault
btst.b #HandleBusErrs, G_State96(A4) ; are we supposed to be active?
beq.s @notSCSIFault ; no - not SCSI's fault
lea BIOSAddr,a1 ; setup for testing BIOS buffer
btst.b #bBIOSSCSIBERR,BIOS_PDMA(a1) ; did we buserr on access via BIOS ?
bne.s @SCSIFault ; if so, start processing the bus error
; It's not our fault ------
@notSCSIFault
move.l SCSIGlobals, a0 ; put entry point to prev BEH on stack
move.l yeOldeBusErrVct(a0), savedRSize(sp) ; (registers saved beneath return addr)
movem.l (sp)+, savedRegs ; restore regs
rts ; jump to old handler, assuming it'll RTE
; It's all our fault (blame it on us) ------
@SCSIFault
; Wait for either DREQ, or INT or a timeout (from blindBusTO value)
move.l G_SCSIDREQ(a4), a1 ; G_SCSIDREQ contains DREQ regr address
move.b G_bitDREQ(a4),d3 ; load DREQ bit position
@DREQloop move.l (a1), d0 ; read DAFB regr
btst.l d3, d0 ; DREQ ?
bne.s @retry
btst #bINT, rSTA(A3) ; see if we have a phase change or something <H4>
bne @phzChange
bra.s @DREQloop
; if DREQ, retry the transfer -----
@retry
; Clean up the writebacks on the stack frame
move.w WB1S(a0), d0 ; check WB1 for validity
move.l WB1A(a0), a1 ; pass WB Address
move.l WB1D(a0), d1 ; pass WB Data
bsr.w DoWriteBack ; to routine that takes care of it
move.w WB2S(a0), d0 ; check WB2 for validity
move.l WB2A(a0), a1 ; pass WB Address
move.l WB2D(a0), d1 ; pass WB Data
bsr.w DoWriteBack ; to routine that takes care of it
move.w WB3S(a0), d0 ; check WB3 for validity
move.l WB3A(a0), a1 ; pass WB Address
move.l WB3D(a0), d1 ; pass WB Data
bsr.w DoWriteBack ; to routine that takes care of it
; we have to check the residual register in BIOS....
; 1. if there is a residual word we have to retrieve it, read another word
; with the posted DREQ and nuke the stack to skip the move.l that buserr'd
; 2. if there is no residual then we can just rte like flint!
;
lea BIOSAddr,a1 ; setup for testing BIOS buffer
cmp.w #scsiReadFast, transferType(a4) ; did we buserr from a read?
beq.s @chkRead ; 0=yes, 1=no
@chkWrite
btst.b #bBIOSW1Cmplt,BIOS_PDMA(a1) ; did one word from the long make it to the c96?
beq.s @doRTE ; 0=no, so restart move.l
;
; Only 1 word of the long word write made it to the controller so we have
; to move the second word manually and increment the buffer pointer.
; Note: the value of a2 will be the value after the move.l postincrement
; so we have to move it backward a word to access the second buffer word.
; We also have to advance the PC so that the rte doesn't restart the faulted
; instruction.
move.l xPC(a0),d0 ; get pc where buserr occurred
; Fake a format code 0 exception frame (4 words) to finish cleaning up
clr.w PD3+2(a0) ; stuff a format code 0 (format code is really only a word)
move.l d0,PD2+2(a0) ; stuff the new pc in stack
move.w xSR(a0),PD2(a0) ;
clr.w BIOS_PDMA(a1) ; clear the status register
movem.l (sp)+, savedRegs ; restore regs
addq.l #4, sp
@adjW040XFrame ; 040 Bus Error frame-cleaning done here
lea aeXFrameSize-8(sp),sp ; remove 040 Access Error Exception Frame
;; addq.l #2, a2 ; adjust user buffer because we manually did the move.l <H4>
rte ; resume execution at next instruction
@chkRead
btst.b #bBIOSR1Cmplt,BIOS_PDMA(a1) ; is there a residual word?
beq.s @doRTE ; 0=no, so restart move.l
;
; Only 1 word of the long word read made it from the controller. We have to
; move the word stored in the residual buffer within BIOS to memory and then
; manually extract the next word from the controller.
; Note: the value of a2 will be the value before the move.l postincrement.
; We also have to advance the PC so that the rte doesn't restart the faulted
; instruction.
move.w BIOS_SCSI_RESID(a1),(a2)+ ; retrieve the residual word stored in BIOS
move.w rDMA(a3),(a2)+ ; get word from chip and put into user's buffer <H4>
; now cleanup the stack and return to the transfer code.
move.l xPC(a0),d0 ; get pc where buserr occurred
addq #2,d0 ; adjust the pc to point to the next instruction <H4>
; Fake a format code 0 exception frame (4 words) to finish cleaning up
clr.w PD3+2(a0) ; stuff a format code 0 (format code is really only a word)
move.l d0,PD2+2(a0) ; stuff the new pc in stack
move.w xSR(a0),PD2(a0) ;
clr.w BIOS_PDMA(a1) ; clear the status register
movem.l (sp)+, savedRegs ; restore regs
addq.l #4, sp
addq.l #4, a2 ; adjust user buffer because we manually did the move.l <H4>
@adjR040XFrame ; 040 Bus Error frame-cleaning done here
lea aeXFrameSize-8(sp),sp ; remove 040 Access Error Exception Frame
; but leave PD3 return address
rte ; resume execution at next instruction
@doRTE
movem.l (sp)+, savedRegs ; restore regs
addq.l #4, sp
rte ; haven't reached max retry count, so restart
; if phase change or timeout, cleanup and abort the transfer -----
@phzChange
@cleanup
; return SP to the exception stack frame
movem.l (sp)+, savedRegs ; restore regs
addq.l #4, sp ; take scratch space off stack
; get any leftover bytes out of the FIFO if we were doing a FastRead
cmp.w #scsiReadFast, transferType(a4)
bne.s @skipLeftovers
move.b rFOS(a3), d0 ; get FIFO status - how many bytes in FIFO
and.w #iFOFMsk, d0 ;
ror.l #1, d0
bra.s @btm0
@top0
move.w rDMA(a3), (a2)+ ; get word from chip and put into user's buffer
@btm0
dbra d0, @top0
tst.l d0
bpl.s @2
move.b rFFO(a3), (a2)+ ; get byte from chip and put into user's buffer
@2
; get rid of excp'n frame and create a throwaway frame for return to Transfer_96
@skipLeftovers
move.b #cFlshFFO, rCMD(a3) ; Flush FIFO <H5>
move.w xSR(sp), d0 ; save SR for new exception frame
bfextu FrameType(sp){0:4}, d1 ; get format code from stack
cmp.b #AEXFrameType, d1 ; check for 040 Access Error Exception Frame
beq.s @Drop040XFrame ; dispose of 040 AE exception frame
cmp.b #shortBEXFrameType, d1 ; short 020/030 exception frame?
bne.s @Drop46w ; no, so use larger frame
adda.w #shortBEXFrameSize, sp ; dispose of the 16-word frame
bra.s @DummyFrame ; and finish up
@Drop040XFrame ; 040 Bus Error frame-cleaning done here
add.w #aeXFrameSize, sp ; remove 040 Access Error Exception Frame
bra.s @DummyFrame ; and create dummy return frame
@Drop46w
add.w #46*2, sp ; size of exception frame
@DummyFrame
; Fake a format code 0 exception frame (4 words) to finish cleaning up
move.w zeroReg, -(sp) ; format code 0
pea FinishErr ; PC value
move.w d0, -(sp) ; sr value
rte ; 'return' from the fake exception
; If we busErr due to a slow peripheral then the c96 is still expecting to transfer
; data since it has no concept of bus error. Hopefully, the client upon seeing busTOErr
; will do the right thing and call SCSIComplete to clean up the bus.
;-----------------
FinishErr
;-----------------
; What we really need to do here is to first empty the FIFO the call Wt4DREQorINT
; then do the right thing. %%%
moveq.l #scBusTOErr, d0 ; assume bus timeout
btst.b #bINT, rSTA(a3) ; poll for intrp due to premature phase change
beq.s @ErrorDone ; bra. if no SCSI intrp 'cuz we busTO
move.b rINT(a3), d5 ; got intrp so check cause also read & clear rFOS, rSTA & rINT
btst.l #bBSS, d5 ; test for bus service intrp
beq.s @ErrorDone ; bra. if not bus service
moveq.l #scPhaseErr, d0 ; yup it's a premature phase change
@ErrorDone
rts ; return status in d0 to the Transfer routine
;-----------------
DoWriteBack
;-----------------
move.l a2,-(sp) ; <H4>
btst #bValid, d0 ; if this writeback valid?
beq.s @wbDone ; no - done
and.w #SIZE_MSK, d0 ; yes, transfer proper size
cmp.w #WB_BYTE, d0
bne.s @1
move.B d1, (a1) ; move Byte
bra.s @wbDone
@1
cmp.w #WB_WORD, d0
bne.s @2
move.W d1, (a1) ; move Word
bra.s @wbDone
@2
cmp.w #WB_LONG, d0
bne.s @wbDone
lea BIOSAddr,a2 ; setup for testing BIOS buffer <H4> thru next <H4>
btst.b #bBIOSW1Cmplt,BIOS_PDMA(a2) ; did one word from the long make it to the c96?
bne.s @doWord ;
move.L d1, (a1) ; move LongWord
bra.s @wbDone
@doWord ; <H4> thru next <H4>
move.w d1,(a1) ;
clr.w BIOS_PDMA(a2) ; clear the status register
@wbDone
move.l (sp)+,a2 ; <H4>
rts
ENDWITH
END