mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2025-01-05 08:30:14 +00:00
4325cdcc78
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.
1705 lines
62 KiB
Plaintext
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
|