mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-08-29 23:28:53 +00:00
1908 lines
64 KiB
Plaintext
1908 lines
64 KiB
Plaintext
;
|
|
; File: HALc96Data.a
|
|
;
|
|
; Contains: 53c96 Hardware-Specific routines
|
|
;
|
|
; Written by: Paul Wolf
|
|
;
|
|
; Copyright: © 1990-1994 by Apple Computer, Inc., all rights reserved.
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <SM38> 1/25/94 DCB Rolled in Paul's change to stop trashing A0 in the TIB
|
|
; interpreter.
|
|
; <ML3> 1/9/94 pdw Added Clinton's new SlowWrite routine.
|
|
; <SM36> 12/19/93 DCB Added code to save the handshake values from each call to the
|
|
; fast read and write routines. This is so that scatter gather
|
|
; lists work without the sglists having to be multiples of the
|
|
; smallest handshake size.
|
|
; <SM35> 11/22/93 pdw Rolling in from <MCxx>.
|
|
; <MC10> 11/11/93 pdw Some slight optimizations to SlowWrite loop.
|
|
; <MC9> 11/10/93 pdw Added include of HardwarePrivateEqu.a
|
|
; <MC8> 11/9/93 pdw Fixed bug in scatter/gather that happened when a zero-length
|
|
; element was encountered.
|
|
; <SM34> 11/22/93 pdw Added forSTP601v1 around the BIOS hack.
|
|
; <SM33> 11/16/93 SAM Include HardwarePrivateEqu.a
|
|
; <SM32> 11/9/93 KW in SlowReadRealC96 have wombats get the 16 bytes with 4 long
|
|
; word reads. STP needs this for awhile until BTU gets reved
|
|
; <SM31> 10/29/93 pdw Fixed build - RecordCmd is being expanded in ROM but not init in
|
|
; SOME places.
|
|
; <SM30> 10/29/93 DCB Adding calls to deferAndWait after calls to Wt4SCSI do reduce
|
|
; the interrupt level during transfers. Also added code to remove
|
|
; the bus error handler when we are asychronous to avoid the
|
|
; problem of chaining to our own bus error handler.
|
|
; <MC6> 10/28/93 pdw Starting to make an attempt to fix the wrong-direction bug.
|
|
; <MC5> 10/14/93 pdw Moved stuffing of A0 with ioPtr to top of TIB loop instead of
|
|
; just before calling transfer routine.
|
|
; <SM29> 10/14/93 pdw Fixed bug.
|
|
; <SM28> 10/14/93 pdw <MC> roll-in.
|
|
; <MC4> 10/12/93 pdw Added support for Synchronous data transfers, rewrote State
|
|
; Machine, message handling etc.
|
|
; <MC3> 9/26/93 pdw Clinton's changes.
|
|
; <SM27> 9/24/93 DCB Fixing yet another residual length problem. This time with
|
|
; SlowReadRealC96.
|
|
; <SM26> 9/19/93 DCB Changing the SlowWrite routine so that it correctly returns the
|
|
; number of bytes not transferred. If it necessary to do an
|
|
; additional move.w to get a DREQ then that word wasn't accounted
|
|
; for.
|
|
; <SM25> 9/13/93 pdw Fixed Smurf by reverting to the old slow SlowRead routine if
|
|
; we're on a real c96. On a Curio, we use the new fast SlowRead
|
|
; routine.
|
|
; <SM24> 9/9/93 pdw Lots of little changes. Name changes, temporary cache_bug
|
|
; stuff.
|
|
; <SM23> 8/19/93 DCB Improving the bus error handler so that disconnects at
|
|
; non-polled bytes will work properly.
|
|
; <SM22> 8/13/93 pdw RecordCmd and eieieo stuff.
|
|
; <SM21> 7/19/93 pdw Fixed build.
|
|
; <SM20> 7/19/93 pdw Got rid of a beq.s to the next instruction to get rid of build
|
|
; warning.
|
|
; <SM19> 7/17/93 pdw Rewrote SlowRead96 routine from scratch.
|
|
; <SM18> 7/8/93 pdw Adding call to RecordError in TIB interpreter.
|
|
; <SM17> 6/29/93 pdw Massive checkins: Change asynchronicity mechanism to CallMachine
|
|
; stack switching mechanism. Adding support for Cold Fusion.
|
|
; Rearranging HW/SW Init code. Some code optimizations.
|
|
; <SM16> 5/25/93 DCB Rollin from Ludwig. (The next item below)
|
|
; <LW8> 5/20/93 DCB Changing _debuggers to DebugStrs so we can turn them off at
|
|
; compile time.
|
|
; <SM15> 5/5/93 PW Converted names to meanies-friendly names. Updated with latest
|
|
; from Ludwig stuff.
|
|
; <LW7> 5/1/93 PW Added 1,511 TIB optimization.
|
|
; <SM14> 4/8/93 DCB Fixed a bug with long transfers that don't have TIB support
|
|
; <SM13> 3/20/93 PW Rolled in Ludwig changes.
|
|
; <LW4> 3/3/93 PW Added some fixes for Quadra support.
|
|
; <LW3> 2/17/93 PW Added stuff needed to fix dataResidLen and bitbucketing bugs.
|
|
; <SM12> 1/31/93 PW Update from the latest of Ludwig. Also changes required for PDM
|
|
; (will update Ludwig with these as needed myself).
|
|
; <LW2> 1/27/93 PW Added support for new dispatches data routines based on
|
|
; scsiDataType field. Rewrote DoDataBuffer routine.
|
|
; <SM11> 12/9/92 PW Fixed test tool SCSIRead phase err crash that was revealed by
|
|
; early-return-from-cmd change that was made to XPTOldCall.
|
|
; <SM10> 11/12/92 PW Changed SlowCableMode to mSlowCableMode to correspond to changes
|
|
; in SCSIEqu53c9x.a
|
|
; <SM9> 10/30/92 DCB Various changes in interrupt handling to improve performance.
|
|
; <SM8> 10/14/92 PW Made fix for S/G bugs which showed up if premature phase change
|
|
; during DMA write.
|
|
; <SM7> 10/8/92 PW Added GrossError checks. Some trivial name changes. Added test
|
|
; for out of bounds xferType. (cb) Fixed .w vs .l direction
|
|
; parameter bug in calling of StartDMA.
|
|
; <SM6> 8/31/92 PW Changed register and command definitions to reflect changes to
|
|
; SCSIEqu53c96.
|
|
; <SM5> 8/30/92 PW Fixed some 'how many bytes were transferred' bugs.
|
|
; <SM4> 8/6/92 PW Removed installation of BusErrHandler from Transfer and put it
|
|
; in the appropriate FastRead and FastWrite routines so it only
|
|
; gets loaded on appropriate machines.
|
|
; <SM3> 7/29/92 PW Removed some debugger traps.
|
|
; <SM2> 7/27/92 PW Virtually initial check-in.
|
|
;
|
|
;==========================================================================
|
|
|
|
|
|
MACHINE MC68020 ; '020-level
|
|
BLANKS ON ; assembler accepts spaces & tabs in operand field
|
|
PRINT OFF ; do not send subsequent lines to the listing file
|
|
; don't print includes
|
|
|
|
PostNOP EQU 1
|
|
|
|
INCLUDE 'SysErr.a'
|
|
INCLUDE 'HardwarePrivateEqu.a'
|
|
INCLUDE 'Debug.a' ; for NAME macro
|
|
|
|
INCLUDE 'SCSI.a'
|
|
INCLUDE 'SCSIEqu53c96.a'
|
|
INCLUDE 'ACAM.a'
|
|
INCLUDE 'SIMCoreEqu.a'
|
|
INCLUDE 'XPTEqu.a'
|
|
|
|
INCLUDE 'HALc96equ.a'
|
|
|
|
PRINT ON ; do send subsequent lines to the listing files
|
|
CASE OBJECT
|
|
|
|
HALc96Data PROC EXPORT
|
|
|
|
EXPORT Xfer1Byte96, OneByteRead
|
|
EXPORT DoDataIn, DoDataOut, OneByteWrite
|
|
EXPORT SlowRead96, SlowReadReal96, SlowWrite96
|
|
EXPORT FastRead96, FastWrite96
|
|
|
|
EXPORT InitDataStuff
|
|
EXPORT DoSaveDataPointer
|
|
EXPORT DoRestorePointers
|
|
EXPORT DoModifyDataPointer
|
|
EXPORT InitDataSG
|
|
EXPORT InitDataBuffer
|
|
EXPORT InitDataTIB
|
|
EXPORT DoDataTIB
|
|
EXPORT DoDataBuffer
|
|
EXPORT DoDataSG
|
|
EXPORT DoBitBucket
|
|
|
|
IMPORT Wt4SCSIInt, Ck4SCSIInt
|
|
IMPORT InstallBEH96, RemoveBEH96
|
|
|
|
|
|
IMPORT Ck4DREQ
|
|
IMPORT WtForFIFOData
|
|
IMPORT RecordEvent, RecordError
|
|
|
|
IMPORT DataIn_PSC1x1, DataIn_PSCDMA, DataOut_PSC
|
|
IMPORT DeferAndWait
|
|
IMPORT QuickIntCheck, FullIntRegs
|
|
|
|
WITH HALc96GlobalRecord
|
|
WITH SIM_IO, HALactions, SCSIPhase
|
|
|
|
;==========================================================================
|
|
;
|
|
; DoDataIn and DoDataOut
|
|
;
|
|
; Called by: Toolbox Trap Dispatcher
|
|
;
|
|
; Calls: primitive data transfer routines
|
|
;
|
|
; On entry: A3 - SCSI read base address
|
|
; A4 - ptr to HALactionPB
|
|
; A5 - pointer to SCSI Manager globals
|
|
;
|
|
; Function: Performs TIB interpretation, and data transfers.
|
|
|
|
DoDataIn
|
|
DoDataOut
|
|
dataCommon
|
|
and.b #$FF-mCF1_SlowCableMode,rCF1(a3) ; turn-off Slow cable mode during data xfer
|
|
eieio
|
|
|
|
move.l dataRoutine(A0), A1
|
|
jmp (A1)
|
|
|
|
RTSNAME 'DoDataCommon'
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
;
|
|
; DoDataTIB - TIB data type handler
|
|
;
|
|
; ———————— Internal:———————————————————————————————————————————————
|
|
; in this case, data desc is S/G and handshake desc must be used (do union of SG and TIB)
|
|
HALTransferRegs REG D2-D6/A0-A1
|
|
|
|
|
|
DoDataTIB
|
|
IF 1 AND RECORD_ON THEN
|
|
pea 'TIB ' ; EVENT =
|
|
move.l sp, -(sp) ;
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
|
|
;
|
|
; What direction is data transfer request? Set up D4 (xferRoutine ptr) accordingly
|
|
;
|
|
cmp.w #kDataIn, HALactionPB.action(A4) ; are we doing a read?
|
|
beq.s @isDataIn
|
|
|
|
@isDataOut ;——————
|
|
move.l #writeOffset, D1 ; get offset to write routines
|
|
add.b scsiTransferType(A0), D1 ; add the selection to the offset
|
|
bra.s @1
|
|
|
|
@isDataIn ;——————
|
|
IF readOffset <> 0 THEN
|
|
move.l #readOffset, D1 ; get offset to read routines
|
|
add.b scsiTransferType(A0), D1 ; add the selection to the offset
|
|
ELSE
|
|
moveq.l #0, D1
|
|
move.b scsiTransferType(A0), D1 ; get the transfer type selection
|
|
ENDIF
|
|
|
|
@1
|
|
move.l (xferRoutines, A5, D1.L*4), D4 ; get routine in D4 for Transfer's use
|
|
|
|
;—————— ; <LW14> pdw Fß TOP ———
|
|
;
|
|
; See if TIB is a 1,511 type and if so, convert to a straight-512 TIB (but more general)
|
|
; check for inc,add,inc,add,loop,stop or inc,mv,inc,mv,loop,stop type TIBs
|
|
;
|
|
clr.b optimTIB(A5) ; default optimizing TIB flag = not doing it
|
|
|
|
move.l scsiDataPtr(A0), A1 ; get the TIB pointer
|
|
move.l A1, -(sp) ; save A1 since we trash it in TIB interp
|
|
move.l A2, -(sp) ; save A2 since we trash it
|
|
|
|
; check instruction 1 (second instruction)
|
|
move.w 1*scSize +scOpcode(a1), D3 ; get instr 1 into D3 for instr 3 ck
|
|
cmp.w #scMove, D3 ; is it scMove?
|
|
beq.s @keepCking1 ; yes -> check next instruction
|
|
cmp.w #scAdd, D3 ; is it scAdd?
|
|
bne.w @exec
|
|
@keepCking1
|
|
|
|
; check instruction 0
|
|
moveq.l #scInc, D1 ;
|
|
cmp.w 0*scSize +scOpcode(a1), D1 ; opcode == scInc?
|
|
bne.w @exec ; no -> go walk the TIB
|
|
|
|
; check instruction 2
|
|
;; move.w #scInc, D1 (already) ; (scInc already in D1)
|
|
cmp.w 2*scSize +scOpcode(a1), D1 ; scInc?
|
|
bne.w @exec
|
|
|
|
; check instruction 3
|
|
cmp.w 3*scSize +scOpcode(a1), D3 ; is it same as instruction 1?
|
|
bne.w @exec
|
|
|
|
cmp.w #scMove, D3 ; are they scMove?
|
|
bne.s @ck4Add ; no -> check for scAdd
|
|
; trashing A0 here
|
|
lea 0*scSize +scParam1(a1), A0 ; get addr of addr field in first scInc
|
|
cmp.l 1*scSize +scParam1(a1), A0 ; should be source address of first move
|
|
bne.w @exec
|
|
cmp.l 3*scSize +scParam2(a1), A0 ; should be dest address of second move
|
|
bne.w @exec
|
|
|
|
lea 2*scSize +scParam1(a1), A2 ; get addr of addr field in second scInc
|
|
cmp.l 1*scSize +scParam2(a1), A2 ; should be dest address of first move
|
|
bne.w @exec
|
|
cmp.l 3*scSize +scParam1(a1), A2 ; should be source address of second move
|
|
bne.w @exec
|
|
|
|
move.l 0*scSize +scParam2(a1), D0 ; instr 0 transfer count
|
|
move.l 2*scSize +scParam2(a1), D1 ; instr 2 transfer count
|
|
bra.s @keepCking3
|
|
|
|
@ck4Add
|
|
cmp.w #scAdd, D3 ; are they scAdd?
|
|
bne.s @exec
|
|
|
|
move.l 0*scSize +scParam2(a1), D0 ; instr 0 transfer count == ...
|
|
cmp.l 1*scSize +scParam2(a1), D0 ; ...instr 1 increment?
|
|
bne.s @exec
|
|
|
|
move.l 2*scSize +scParam2(a1), D1 ; instr 2 transfer count == ...
|
|
cmp.l 3*scSize +scParam2(a1), D1 ; ...instr 3 increment?
|
|
bne.s @exec
|
|
|
|
move.l 0*scSize +scParam1(a1), A0 ; get contents of addr field in first scInc
|
|
cmp.l 2*scSize +scParam1(a1), A0 ; should be same as second scInc
|
|
bne.s @exec
|
|
|
|
lea 0*scSize +scParam1(a1), A0 ; get addr of addr field in first scInc
|
|
cmp.l 3*scSize +scParam1(a1), A0 ; should be dest address of second scAdd
|
|
bne.s @exec
|
|
|
|
lea 2*scSize +scParam1(a1), A2
|
|
cmp.l 1*scSize +scParam1(a1), A2 ; check source address
|
|
bne.s @exec
|
|
|
|
@keepCking3
|
|
|
|
; check instruction 4
|
|
move.w 4*scSize +scOpcode(a1), D2
|
|
cmp.w #scLoop, D2 ; scLoop?
|
|
bne.s @exec
|
|
|
|
moveq.l #-40, D2
|
|
cmp.l 4*scSize +scParam1(a1), D2 ; jump==-40?
|
|
bne.s @exec
|
|
|
|
; check instruction 5
|
|
move.w 5*scSize +scOpcode(a1), D2
|
|
cmp.w #scStop, D2 ; scStop?
|
|
bne.s @exec
|
|
|
|
move.b D3, optimTIB(A5) ; set optimizing TIB flag (and remember add/mv)
|
|
move.l D0, firstIncCount(A5) ; remember first scInc count
|
|
move.l D1, secondIncCount(A5) ; remember second scInc count
|
|
|
|
add.l D1, D0 ; calc total block size (add transfer counts)
|
|
move.l D0, 0*scSize +scParam2(a1) ; first scInc transfers total block
|
|
clr.l 2*scSize +scParam2(a1) ; second scInc transfers none
|
|
|
|
cmp.w #scAdd, D3 ; is this an scAdd TIB?
|
|
bne.s @exec
|
|
move.l D0, 1*scSize +scParam2(a1) ; first scAdd do add of total transfer
|
|
clr.l 3*scSize +scParam2(a1) ; second scAdd do none
|
|
; ; <LW14> pdw Fß BOT ———
|
|
|
|
;——————
|
|
;
|
|
; Interpret the TIB
|
|
;
|
|
@exec
|
|
move.l HALactionPB.ioPtr(A4), A0 ; load A0 with ptr to SCSI_IO pb
|
|
bra.s @getCmd ; tighten loop by branching first
|
|
|
|
|
|
@c_inc
|
|
tst.l D2
|
|
beq.s @next_cmd
|
|
|
|
movem.l HALTransferRegs, -(sp) ; save registers
|
|
move.l D4, A1
|
|
jsr (A1) ; go to the appropriate routine
|
|
movem.l (sp)+, HALTransferRegs ; restore these guys
|
|
|
|
tst.l d1 ; set Z if all done
|
|
bne.s @earlyEnd ; if error, exit
|
|
add.l D2, scParam1(a1) ; increment the pointer by size of xfer
|
|
; FALL THROUGH to @next_cmd ; continue
|
|
|
|
@next_cmd
|
|
@c_nop ; also NOP, just skip the command
|
|
add.w #scSize, a1 ; point to the next TIB instruction
|
|
; FALL THROUGH to @exec
|
|
|
|
;
|
|
; Loop through the TIB, interpreting as we go
|
|
;
|
|
@getCmd
|
|
move.w scOpcode(a1), d1 ; get the function opcode
|
|
move.l scParam1(a1), a2 ; get the generic address
|
|
move.l scParam2(a1), d2 ; get the generic count
|
|
|
|
cmp.w #maxOpcode, d1 ; valid opcode ?
|
|
bhi.s @c_badop ; return err if not
|
|
|
|
add.w d1,d1 ; index times two
|
|
jmp @JmpTable(d1.w) ; jump into table
|
|
|
|
@JmpTable ;
|
|
bra.s @c_badop ; 0 is not a valid opcode
|
|
bra.s @c_inc ; 1
|
|
bra.s @c_noinc ; 2
|
|
bra.s @c_add ; 3
|
|
bra.s @c_move ; 4
|
|
bra.s @c_loop ; 5
|
|
bra.s @c_nop ; 6
|
|
bra.s @c_stop ; 7
|
|
bra.s @c_stop ;c_compare ; 8
|
|
|
|
@earlyEnd
|
|
moveq.l #scPhaseErr, D0
|
|
@data_error
|
|
move.l D0, -(sp)
|
|
bsr RecordError ; Err!
|
|
addq.l #4, sp
|
|
bra.s @data_end
|
|
|
|
@c_badop
|
|
moveq.l #scBadParmsErr, D0 ; bad opcode
|
|
bra.s @data_error
|
|
|
|
@c_noinc ; NOINC addr,count
|
|
tst.l D2
|
|
beq.s @next_cmd
|
|
|
|
movem.l HALTransferRegs, -(sp) ; save registers
|
|
move.l D4, A1
|
|
jsr (A1) ; go to the appropriate routine
|
|
movem.l (sp)+, HALTransferRegs ; restore these guys
|
|
|
|
tst.l d1 ; set Z if all done
|
|
bne.s @earlyEnd ; if error, exit
|
|
bra.s @next_cmd ; else process next command
|
|
|
|
@c_add ; ADD addr,data
|
|
add.l d2, (a2) ; the count added to the where
|
|
bra.s @next_cmd ; process the next command
|
|
|
|
@c_move ; MOVE addr1,addr2
|
|
move.l (a2), (ZA0,D2.L) ; simple enough
|
|
bra.s @next_cmd ; process the next command
|
|
|
|
@c_loop ; LOOP relative addr,count
|
|
tst.l d2 ; check for zero loop count
|
|
beq.s @next_cmd ; if count is already zero, quit loop
|
|
subq.l #1, d2 ; drop the count
|
|
move.l d2, scParam2(a1) ; put the count back for next time
|
|
beq.s @next_cmd ; if count exhausted, don't loop
|
|
add.l a2, a1 ; modify the command pointer
|
|
bra.s @exec ; and process the next command
|
|
|
|
@c_stop
|
|
moveq.l #noErr, D0 ; indicate no error
|
|
; FALL THROUGH to @data_end ;
|
|
|
|
|
|
@data_end
|
|
move.w D0, HALactionPB.result(A4) ; with whatever err
|
|
|
|
;=== Flush the cache line that contains location 8 (because of MOVE16 bug)
|
|
|
|
tst.b HBAhasHskPseudoDMA(A5) ; if we have a bus error handler <SM7> pdw
|
|
beq.s @skipFlushCache
|
|
movem.l D0-D2, -(sp)
|
|
moveq.l #9, D0 ; FlushCacheRange HWPriv scsiSelector
|
|
move.l #8, A0 ; starting address of flush range
|
|
move.l #4, A1 ; length of range
|
|
_HWPriv
|
|
movem.l (sp)+, D0-D2 ; restore regs
|
|
@skipFlushCache
|
|
|
|
move.l (sp)+, A2 ; restore A2 (saved before TIB interp)
|
|
move.l (sp)+, A1 ; restore A1 (saved before TIB interp) <LW14> pdw Fß
|
|
|
|
move.l HALactionPB.ioPtr(A4), A0 ; restore A0 - ptr to SCSI_IO pb
|
|
|
|
;—————— ; <LW14> pdw Fß TOP ———
|
|
;
|
|
; Replace munged TIB values if we optimized it before the interpretation
|
|
;
|
|
move.b optimTIB(A5), D3 ; test optimizing TIB flag
|
|
if DEBUGGING then
|
|
beq.w DataDone ; didn't optimize -> DataDone
|
|
else
|
|
beq.s DataDone ; didn't optimize -> DataDone
|
|
endif
|
|
|
|
cmp.w #scAdd, D3 ; is this an scAdd TIB?
|
|
bne.s @restoreMove
|
|
@restoreAdd
|
|
move.l firstIncCount(A5), D3
|
|
move.l D3, 0*scSize +scParam2(a1) ; restore first scInc count
|
|
move.l D3, 1*scSize +scParam2(a1) ; restore first scAdd count
|
|
move.l secondIncCount(A5), D3
|
|
move.l D3, 2*scSize +scParam2(a1) ; restore second scInc count
|
|
move.l D3, 3*scSize +scParam2(a1) ; restore second scAdd count
|
|
|
|
bra.s DataDone
|
|
@restoreMove
|
|
move.l firstIncCount(A5), 0*scSize +scParam2(a1) ; restore first scInc count
|
|
move.l secondIncCount(A5), 2*scSize +scParam2(a1) ; restore second scInc count
|
|
;—————— ; <LW14> pdw Fß BOT ———
|
|
|
|
bra.s DataDone
|
|
|
|
RTSNAME 'DoDataTIB'
|
|
|
|
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
;
|
|
; DoDataBuffer - buffer data type handler
|
|
;
|
|
; ———————— Internal:———————————————————————————————————————————————
|
|
D1_remaining EQU D1 ; .l = number of bytes remaining in this block
|
|
D2_xferSize EQU D2 ; .l = number of bytes remaining in this block
|
|
|
|
XferRegs REG D2/A0-A2
|
|
|
|
DoDataBuffer
|
|
IF 1 AND RECORD_ON THEN
|
|
pea 'DBfr' ; EVENT =
|
|
move.l sp, -(sp) ; number of bytes
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
;
|
|
; Set up for a call to the "transfer one block of data" routine
|
|
; D2 (number of bytes) and A2 (address of buffer) for call to transfer routine
|
|
;
|
|
move.l currentSGptr(A0), A2 ; current position in buffer
|
|
move.l currentSGcount(A0), D2_xferSize ; how far we've got to go
|
|
beq.s DataDone ; zero? if so, get out
|
|
move.l D2_xferSize, D1_remaining ; make a copy of the count
|
|
|
|
;
|
|
; Do the transfer of this block
|
|
;
|
|
movem.l XferRegs, -(sp) ; save registers
|
|
|
|
movea.l xferRoutine(A0), A1 ; get the address of the transfer routine
|
|
jsr (A1) ; go to the transfer routine
|
|
IF DEBUGGING THEN
|
|
cmp.l HALactionPB.ioPtr(A4), A0
|
|
beq.s @nobug
|
|
DebugStr 'Err! A0!=ioPtr in DoDataBuffer'
|
|
@nobug
|
|
ENDIF
|
|
|
|
movem.l (sp)+, XferRegs ; restore these guys
|
|
|
|
;
|
|
; This call got further into this block and may have gotten done with it
|
|
; Update the Current pointers to point to this block with count as returned by Data call
|
|
;
|
|
sub.l D1_remaining, D2 ; calc how many bytes we transferred
|
|
beq.s PhaseErr ; (if no bytes, then phase err)
|
|
add.l D2, currentSGptr(A0) ; and where we need to start next time
|
|
sub.l D2, scsiDataResidual(A0) ; and what our total residual is
|
|
move.l D1_remaining, currentSGcount(A0) ; and how far we've got to go
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
|
|
DataDone
|
|
;=== exit and return result
|
|
ori.b #mCF1_SlowCableMode,rCF1(a3) ; go back to Slow cable mode for all else
|
|
eieio
|
|
rts
|
|
|
|
NAME 'DoDataBuffer'
|
|
|
|
|
|
; If we are in the wrong phase at the start of the data routine that means that we must be in
|
|
; the OTHER data phase (i.e. data in vs. data out). This is because the SIM would never call
|
|
; the data routine in unless we are in one or the other. The only other possibility is that
|
|
; the target just dropped the bus on us.
|
|
|
|
PhaseErr
|
|
move.b currentPhase(A5), D0 ; was it perhaps an unexpected disconnect?
|
|
cmp.b #kBusFreePhase, D0
|
|
bne.s @ckData
|
|
move.w #SCResults.scsiUnexpectedBusFree, HALactionPB.result(A4)
|
|
bra.w DataDone
|
|
@ckData
|
|
and.b #%110, D0 ; get rid of direction bit
|
|
cmp.b #(kDataOutPhase ** %110), D0 ; a data phase?
|
|
beq.s @wrongDir
|
|
IfDebugStr 'Totally wrong phase in data routine'
|
|
move.w #SCResults.scsiWrongDirection, HALactionPB.result(A4)
|
|
bra.w DataDone
|
|
@wrongDir
|
|
move.w #SCResults.scsiWrongDirection, HALactionPB.result(A4)
|
|
bra.w DataDone
|
|
|
|
RTSNAME 'PhaseErr'
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
;
|
|
; DoDataSG - scatter/gather data type handler
|
|
;
|
|
; ———————— Internal:———————————————————————————————————————————————
|
|
;D1_remaining EQU D1 ; .l = number of bytes remaining in this block
|
|
D2_position EQU D2 ; .l = position
|
|
|
|
DoDataSG
|
|
WITH SGRecord
|
|
|
|
IF 1 AND RECORD_ON THEN
|
|
pea 'ScGt'
|
|
clr.l -(sp)
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
|
|
move.l A2, -(sp) ; save A2 since we trash it in S/G walking
|
|
;
|
|
; Set up for a call to the "transfer one block of data" routine
|
|
; D2 (number of bytes) and A2 (address of buffer) for call to transfer routine
|
|
;
|
|
move.l currentSGptr(A0), A1 ; <SM8> pdw TOP
|
|
move.l currentSGcount(A0), D2_position ; how far we've already gone into this block
|
|
|
|
@loop
|
|
move.l sgAddr(A1), A2 ; current buffer's start address
|
|
add.l D2_position, A2 ; displace start address that amount
|
|
move.l sgCount(A1), D1_remaining ; get the total buffer size
|
|
sub.l D2_position, D1_remaining ; and sub what we've done for to-do size<SM8> pdw BOT
|
|
|
|
;
|
|
; Do the transfer of this block
|
|
;
|
|
|
|
@doData
|
|
move.l D1_remaining, D2 ; D2 input parm = # of bytes to transfer <SM8> pdw
|
|
beq.s @dataDone ; if no data, skip xfer routine
|
|
movem.l XferRegs, -(sp) ; save registers
|
|
|
|
movea.l xferRoutine(A0), A1 ; get the address of the transfer routine
|
|
jsr (A1) ; go to the transfer routine
|
|
|
|
movem.l (sp)+, XferRegs
|
|
@dataDone
|
|
sub.l D1_remaining, D2 ; calc how many bytes we transferred
|
|
sub.l D2, scsiDataResidual(A0) ; and what our total residual now is
|
|
|
|
tst.l D1_remaining ; any bytes left in this block?
|
|
beq.s @nextEntry ; no, go to next entry
|
|
|
|
;
|
|
; This call got further into this block but didn't get done with it
|
|
; Update the Current pointers to point to this block with count as returned by Data call
|
|
;
|
|
|
|
@thisEntryNotDone ; <SM8> pdw TOP
|
|
move.l sgCount(A1), D2_position ; get the total buffer size
|
|
sub.l D1_remaining, D2_position ; sub # of bytes left from total count
|
|
move.l D2_position, currentSGcount(A0) ; how far we've already gone into this block
|
|
; <SM8> pdw BOT
|
|
bra.s @outOfPhaseOrError ; yes - go do them
|
|
|
|
;
|
|
; This call got done with this block
|
|
; Update the Current pointers to point to the next block
|
|
;
|
|
|
|
@nextEntry
|
|
lea sgNextBlock(A1), A1 ; point to next S/G entry
|
|
move.l A1, currentSGptr(A0) ; store in current pointer
|
|
moveq.l #0, D2_position ; <SM8> pdw TOP
|
|
move.l D2_position, currentSGcount(A0) ; init count to '0 bytes xferred so far'
|
|
|
|
;
|
|
; Now see if we're completely done
|
|
; compare new SGptr with the end of the SG list (scsiDataPtr + 8*scsiSGListCount)
|
|
;
|
|
moveq.l #0, D1 ;
|
|
move.w scsiSGListCount(A0), D1 ; how many elements in SG list
|
|
lsl.l #3, D1 ; 8 bytes per SG element
|
|
add.l scsiDataPtr(A0), D1 ; point just past last element
|
|
cmp.l A1, D1 ; are we there? <SM8> pdw BOT
|
|
bhi.s @loop ; no -> keep going
|
|
|
|
; if all done, set kbDataDone bit to tell SIM (so it will bitbucket or whatever if still data phz)
|
|
|
|
bset.b #SIMprivFlagsRecord.kbDataDone, SIMprivFlags(A0)
|
|
|
|
@outOfPhaseOrError
|
|
move.l (sp)+, A2
|
|
bra.w DataDone
|
|
|
|
RTSNAME 'DoDataSG'
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
InitDataStuff
|
|
;
|
|
; Initialize Current pointers and Saved pointers
|
|
; and, as an extra added bonus, initialize DataRoutine and XferRoutine ptrs
|
|
;
|
|
; First, figure out whether this request is for a S/G or a single data buffer
|
|
; If S/G point to first S/G element; If buffer, act like scsiDataPtr/scsiDataLength is a S/G element
|
|
;
|
|
InitDataStuffRegs REG D0-D2/A1
|
|
movem.l InitDataStuffRegs, -(sp)
|
|
;
|
|
; Decide whether we will use Buffer, S/G, or TIB then
|
|
; Init data pointer stuff accordingly, as well as residual count stuff
|
|
; Get the address of the data interpreter into dataRoutine
|
|
;
|
|
moveq.l #0, D1
|
|
move.b scsiDataType(A0), D1
|
|
move.l (dataRoutines, A5, D1.L*4), A1 ; get the data routine
|
|
move.l A1, dataRoutine(A0) ; save it in ioPB
|
|
|
|
move.l (initDataRoutines, A5, D1.L*4), A1 ; get the init data ptr routine
|
|
jmp (A1) ; jump to init data pointer routine
|
|
|
|
;———————————
|
|
InitDataTIB
|
|
moveq.l #0, D0
|
|
bra.s exitInitData ; get out (we're in Select: no data info)
|
|
|
|
InitDataSG
|
|
move.l scsiDataPtr(A0), A1 ; get start of SG list
|
|
moveq.l #0, D0
|
|
bra.s fillInPointers
|
|
InitDataBuffer
|
|
move.l scsiDataPtr(A0), A1 ; get start of buffer
|
|
move.l scsiDataLength(A0), D0 ; get byte count
|
|
fillInPointers
|
|
move.l A1, currentSGptr(A0) ; init ptr
|
|
move.l D0, currentSGcount(A0) ; init count to 0
|
|
move.l A1, savedSGptr(A0) ; init ptr
|
|
move.l D0, savedSGcount(A0) ; init count to 0
|
|
|
|
move.l scsiDataLength(A0), D0 ; get byte count
|
|
move.l D0, scsiDataResidual(A0) ; current residual ptr
|
|
move.l D0, savedResidLen(A0) ; saved residual ptr
|
|
|
|
;———————————
|
|
;
|
|
; Determine whether we're data IN, OUT or neither then
|
|
; Put the address of the data & transfer routine into xferRoutine(io) and dataRoutine(io)
|
|
;
|
|
move.l scsiFlags(A0), D0
|
|
and.l #scsiDirectionMask, D0 ; are we data in or out?
|
|
cmp.l #scsiDirectionIn, D0
|
|
beq.s @isDataIn
|
|
@notDataIn
|
|
cmp.l #scsiDirectionOut, D0
|
|
beq.s @isDataOut
|
|
|
|
@noDataXfer ;——————
|
|
moveq.l #0, D0
|
|
move.l D0, scsiDataResidual(A0) ; current residual ptr
|
|
move.l D0, savedResidLen(A0) ; saved residual ptr
|
|
move.l jvBitBucket(A5), dataRoutine(A0)
|
|
lea NoDataXfer, A1
|
|
move.l A1, xferRoutine(A0)
|
|
bra.s exitInitData
|
|
|
|
@isDataOut ;——————
|
|
move.l #writeOffset, D1 ; get offset to write routines
|
|
add.b scsiTransferType(A0), D1 ; add the selection to the offset
|
|
bra.s @1
|
|
|
|
@isDataIn ;——————
|
|
IF readOffset <> 0 THEN
|
|
move.l #readOffset, D1 ; get offset to read routines
|
|
add.b scsiTransferType(A0), D1 ; add the selection to the offset
|
|
ELSE
|
|
moveq.l #0, D1
|
|
move.b scsiTransferType(A0), D1 ; get the transfer type selection
|
|
ENDIF
|
|
|
|
@1
|
|
move.l (xferRoutines, A5, D1.L*4), A1 ; get the routine address
|
|
move.l A1, xferRoutine(A0)
|
|
|
|
exitInitData
|
|
movem.l (sp)+, InitDataStuffRegs
|
|
rts
|
|
|
|
NAME 'InitDataStuff'
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
DoSaveDataPointer
|
|
;
|
|
; Copy Current pointers into Saved
|
|
;
|
|
move.l currentSGptr(A0), savedSGptr(A0) ; copy ptr
|
|
move.l currentSGcount(A0), savedSGcount(A0) ; copy count
|
|
|
|
move.l scsiDataResidual(A0), savedResidLen(A0) ; copy residual ptr
|
|
rts
|
|
|
|
NAME 'DoSaveDataPointer'
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
DoRestorePointers
|
|
;
|
|
; Copy Saved pointers into Current
|
|
;
|
|
move.l savedSGptr(A0), currentSGptr(A0) ; copy ptr
|
|
move.l savedSGcount(A0), currentSGcount(A0) ; copy count
|
|
|
|
move.l savedResidLen(A0), scsiDataResidual(A0) ; copy residual ptr
|
|
rts
|
|
|
|
NAME 'DoRestorePointers'
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
DoModifyDataPointer
|
|
DebugStr 'Got Modify Data Pointer Message'
|
|
rts
|
|
|
|
NAME 'DoModifyDataPointer'
|
|
|
|
ENDWITH ;SGRecord
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
IllegalXfer
|
|
move.l #'Xfer', -(sp)
|
|
DebugStr 'Ileagal Xfer'
|
|
addq.l #4, sp
|
|
rts
|
|
|
|
NAME 'IllegalXfer'
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
NoDataXfer
|
|
|
|
rts
|
|
|
|
NAME 'NoDataXfer'
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
; DoBitBucket - bit buckets data in or out until no longer in data phase
|
|
;
|
|
DoBitBucket
|
|
moveq.l #iPhaseMsk, d0 ; load mask bits for phase value
|
|
and.b rSTA(a3), d0 ; get phase bits
|
|
cmp #iDataOut, d0 ;
|
|
bne.s @checkPhase ;
|
|
btst.b #7, SCSI_IO.scsiResultFlags(A0)
|
|
beq.s @checkPhase
|
|
DebugStr 'The SCSIMgr will now trash your data. Is that a problem?'
|
|
@checkPhase
|
|
moveq.l #iPhaseMsk, D0 ; load mask bits for phase value
|
|
and.b rSTA(a3), D0 ; get phase bits
|
|
|
|
cmp #iDataIn, D0 ;
|
|
beq.s @inDataIn ;
|
|
|
|
cmp #iDataOut, D0 ;
|
|
bne.s @notInDataPhase ;
|
|
@inDataOut
|
|
move.b #$EE, rFIFO(A3) ; load filler byte into FIFO
|
|
eieio
|
|
bsr Xfer1Byte96 ; xfer 1 byte and wait for intrp w/o timeout
|
|
bne.s @xferErr ; bra. on xfer error
|
|
subq.l #1, scsiDataResidual(A0) ; adjust residual length
|
|
bra.s @checkPhase
|
|
|
|
@inDataIn
|
|
bsr Xfer1Byte96 ; xfer 1 byte and wait for intrp w/o timeout
|
|
bne.s @xferErr ; bra. on xfer error
|
|
move.b rFIFO(A3), D0 ; just empty the FIFO
|
|
subq.l #1, scsiDataResidual(A0) ; adjust residual length
|
|
bra.s @checkPhase
|
|
|
|
@xferErr
|
|
@notInDataPhase
|
|
rts
|
|
|
|
NAME 'DoBitBucket'
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
; OneByteRead/OneByteWrite - proc to transfer 1 byte
|
|
;
|
|
OneByteRead
|
|
RecCmd $10, 'D',$01
|
|
move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers
|
|
eieio
|
|
|
|
bsr.w Wt4SCSIInt ; Wait for intrp w/o timeout
|
|
; on exit d5 = rFOS|rINT|rSQS|rSTA
|
|
move.b rFIFO(a3), (a2)+ ; xfer byte from FIFO into input buffer
|
|
|
|
rts
|
|
|
|
NAME 'OneByteRead'
|
|
|
|
OneByteWrite
|
|
move.b (a2)+, rFIFO(a3) ; preload the FIFO
|
|
eieio
|
|
RecCmd $10, 'D',$02
|
|
move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers
|
|
eieio
|
|
|
|
bsr.w Wt4SCSIInt ; Wait for intrp w/o timeout
|
|
; on exit d5 = rFOS|rINT|rSQS|rSTA
|
|
rts
|
|
|
|
NAME 'OneByteWrite'
|
|
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
; Xfer1Byte96 - proc to transfer 1 byte of data
|
|
;
|
|
Xfer1Byte96
|
|
RecCmd $10, 'D',$03
|
|
move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfer
|
|
eieio
|
|
|
|
bsr.w Wt4SCSIInt ; wait for interrupt
|
|
rts
|
|
|
|
NAME 'Xfer1Byte96'
|
|
|
|
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
; SlowRead96 - implements Polled Read
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
; All primitive data transfer routines assume:
|
|
;
|
|
; d1 - <-- bytes transferred
|
|
; d2 - --> number of bytes to transfer
|
|
; 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
|
|
; A5 - ptr to SCSI Mgr globals
|
|
;
|
|
; Method of Data Transfer: (pDMA and programmed IO)
|
|
; 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
|
|
; 3) Transfer all data in the FIFO and wait for the intrp
|
|
; 4) Repeat until all blocks 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
|
|
;
|
|
; The weird thing about this routine (and SlowWrite as well) is that you can get a phase
|
|
; change but not get an interrupt because there may be outstanding DREQs and the c96
|
|
; will not set bINT until all DREQs have been satisfied. This means that we have to
|
|
; keep an eye on the phase bits to see if we get a change.
|
|
;
|
|
|
|
SlowRead96
|
|
IF 1 AND RECORD_ON THEN
|
|
pea 'SwRd' ; EVENT =
|
|
move.l scsiDataResidual(A0), -(sp)
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
|
|
IF 1 AND RECORD_ON THEN
|
|
move.l D2, -(sp) ; number of bytes
|
|
move.l A2, -(sp) ;
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
|
|
tst.b HBAhasDMA(A5) ; are we on a DMA (i.e. a Curio) machine?
|
|
beq SlowReadReal96 ; no -> do SlowRead for Real 53c96 parts
|
|
|
|
cmp.b #kDataInPhase, currentPhase(A5)
|
|
bne @wrongPhase
|
|
|
|
bclr #0, D2 ; odd xfer?
|
|
beq.s @even
|
|
|
|
move.b #cIOXfer, rCMD(A3) ; move that 1 byte (guaranteed - we have REQ)
|
|
bsr Wt4SCSIInt
|
|
|
|
jsr DeferAndWait ; defer til interupts are enabled
|
|
|
|
move.b rFIFO(A3), (A2)+
|
|
cmp.b #kDataInPhase, currentPhase(A5)
|
|
bne @wrongPhase
|
|
;……………………………
|
|
@even
|
|
move.l pdmaNonSerlzdAddr(A5), A1 ; point to non-serialized chip image
|
|
lea rFIFOflags(A3), A0 ; init A0 for btsts in loop
|
|
move.l #3, D3 ; ...and D3
|
|
move.l #4, D4 ; ...and D4
|
|
|
|
move.l D2, D6
|
|
swap D6 ; .w = # of 64K chunks
|
|
tst.w D2 ; mod 64K nonzero?
|
|
beq @loop64Kbtm ; yes -> go straight to 64K loop
|
|
|
|
move.l D2, D1
|
|
move.b D1, rXCL(A3)
|
|
lsr.l #8, D1
|
|
move.b D1, rXCM(A3)
|
|
clr.w D2 ; assume we transfer those bytes
|
|
|
|
;——————————————————
|
|
@startXfer
|
|
RecCmd $90, 'D',$04
|
|
move.b #cDMAXfer, rCMD(A3) ; load DMA transfer cmd & start loading FIFO
|
|
nop
|
|
nop
|
|
nop
|
|
bra.s @ck4IntOrPhaseChange
|
|
;……………………………
|
|
@read16
|
|
move.w (a1), (a2)+ ; read 8 bytes
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
@read8
|
|
move.w (a1), (a2)+ ; read 8 bytes
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
@ckFIFO
|
|
nop
|
|
btst D3, (A0) ; FIFO half full?
|
|
nop
|
|
bne.s @read8 ; yes -> read 8 bytes
|
|
btst D4, (A0) ; FIFO full?
|
|
nop
|
|
bne.s @read16 ; yes -> read 16 bytes
|
|
btst D3, (A0) ; FIFO half full?
|
|
nop
|
|
bne.s @read8
|
|
@ck4IntOrPhaseChange
|
|
move.b rSTA(A3), D5 ; interrupt?
|
|
bmi.s @outOfPhase
|
|
btst D3, (A0) ; half full?
|
|
nop
|
|
bne.s @read8
|
|
btst D4, (A0) ; full?
|
|
nop
|
|
bne.s @read16
|
|
btst D3, (A0) ; half full?
|
|
nop
|
|
bne.s @read8
|
|
btst #bTermCount, D5 ; transfer complete?
|
|
nop
|
|
bne.s @tc
|
|
btst D3, (A0) ; half full?
|
|
nop
|
|
bne.s @read8
|
|
btst D4, (A0) ; full?
|
|
nop
|
|
bne.s @read16
|
|
btst D3, (A0) ; half full?
|
|
nop
|
|
bne.s @read8
|
|
and.b #iPhaseMsk, D5 ; and mask bits for phase value
|
|
btst D4, (A0) ; full?
|
|
nop
|
|
bne.s @read16
|
|
cmpi.b #kDataInPhase, D5 ; still in Data In phase?
|
|
bne.s @outOfPhase ; no -> take care of leftovers, get int
|
|
btst D3, (A0) ; half full?
|
|
nop
|
|
bne.s @read8
|
|
btst D4, (A0) ; full?
|
|
nop
|
|
bne.s @read16
|
|
bra.s @ckFIFO
|
|
;——————————————————
|
|
@tc
|
|
@outOfPhase
|
|
moveq.l #mFIFOCount, D0 ; use mask to get FIFO flag field
|
|
and.b rFIFOflags(A3), D0 ; get FIFO count
|
|
beq.s @noBytesInFIFO
|
|
IF 0 AND RECORD_ON THEN
|
|
pea 'ffc0'
|
|
add.b D0, 3(sp)
|
|
move.l A2, -(sp)
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
;sub.l D0, D2 ; subtract from total request
|
|
lsr.b #1, D0 ; how many words (Carry = odd byte)
|
|
bcc.s @evenFIFO ; no -> just xfer words
|
|
move.b rFIFO(a3), (a2)+ ; get odd byte out
|
|
bra.s @evenFIFO ; now get the words
|
|
@fifoWords
|
|
move.w (a1), (a2)+
|
|
@evenFIFO
|
|
dbra D0, @fifoWords
|
|
@noBytesInFIFO
|
|
IF 0 AND RECORD_ON THEN
|
|
pea 'ls12'
|
|
move.l -12(A2), -(sp)
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
IF 0 AND RECORD_ON THEN
|
|
move.l -8(A2), -(sp)
|
|
move.l -4(A2), -(sp)
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
bsr Wt4SCSIInt ; let's go get that interrupt
|
|
jsr DeferAndWait ; defer til interupts are enabled
|
|
|
|
clr.l D1
|
|
move.b rXCM(A3), D1 ; get high byte
|
|
lsl.w #8, D1
|
|
move.b rXCL(A3), D1 ; get low byte
|
|
add.l D1, D2 ; add to unxferred count
|
|
|
|
cmpi.b #kDataInPhase, currentPhase(A5) ; still data-in?
|
|
bne.s @exit ; no->exit
|
|
|
|
;——————————————————
|
|
@loop64Kbtm
|
|
dbra D6, @loop64Ktop
|
|
|
|
@wrongPhase
|
|
@exit
|
|
move.l D2, D1
|
|
move.l HALactionPB.ioPtr(A4), A0 ; restore A0 - ptr to SCSI_IO pb
|
|
rts
|
|
|
|
@loop64Ktop
|
|
clr.b rXCL(A3)
|
|
sub.l #$10000, D2 ; assume we're going to transfer all 64K
|
|
clr.b rXCM(A3)
|
|
bra.w @startXfer
|
|
|
|
RTSNAME 'SlowRead96'
|
|
|
|
|
|
SlowReadReal96 ;———————————————————————————————————————————
|
|
;
|
|
; Setup A1 to point to a non-serialized image of the c96. If such an image doesn't exist this
|
|
; value will be the same as the serialized image.
|
|
;
|
|
move.l pdmaNonSerlzdAddr(A5), A1 ; point to non-serialized chip image
|
|
|
|
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 PhaseErrAtStart ; bra. on phase err
|
|
|
|
clr.l D3 ; d3 = bytes transferred to get DREQ after
|
|
; we change phase
|
|
move.l D2, D4 ; d4 = copy of transfer count
|
|
and.l #$F, D2 ; d2 = remainder word count after 16-byte moves
|
|
lsr.l #4, D4 ; divide xfer count by 16
|
|
move.l D4, D6 ; d4.w has lower 16-byte block count
|
|
swap D6 ; d6.w has upper 16-byte word count
|
|
|
|
clr.b rXCM(a3) ; rXCM = 0, clear most-sig. byte count
|
|
move.b #$10, rXCL(a3) ; rXCL = 16 bytes, least-sig. byte value
|
|
eieio
|
|
bra.w @bottomRead16
|
|
@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 @gotPrematureInt ; no: probably in status phase - split
|
|
|
|
RecCmd $90, 'D',$05
|
|
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & start loading FIFO
|
|
nop ; currently loaded transfer count is used/reused
|
|
@1
|
|
btst.b #bTermCount, rSTA(a3) ; check if we've rcvd all the data
|
|
bne.s @4 ; if we have, go get the bytes
|
|
bsr Ck4SCSIInt ; poll for unexpected intrp while waiting
|
|
bne.w @gotPrematureInt ; ... 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 complete bit set
|
|
bra.w @prematurePhzChgSR ; transfer count not 0 so we have a premature end
|
|
|
|
; We need 8 guaranteed DREQs to safely transfer 16 bytes without bus error.
|
|
; DREQ will be active as long there are threshold number of bytes in the
|
|
; FIFO.
|
|
@4
|
|
|
|
; Why is this here? Ck4DREQ is really slow, and it seems unnecessary. If we don't have an interrupt
|
|
; and we do have a full FIFO we damn well better have a DREQ. Maybe it has something to do with the value
|
|
; of the FIFO flags field floating a bit during an xfer...
|
|
; bsr Ck4DREQ
|
|
; beq.s @1 ; loop until asserted
|
|
;
|
|
btst #4, rFIFOflags(a3) ; see if FIFO is full
|
|
beq.s @1 ; loop until asserted
|
|
|
|
|
|
IF forSTP601v1 THEN
|
|
cmp.b #pdmaTypeBIOS,HALc96GlobalRecord.dmaType(A5) ; Is this a Wombat or Primus/Optimus?
|
|
bne.s @notBIOSbased
|
|
|
|
nop
|
|
move.l (a1), (a2)+ ; read 16 bytes
|
|
move.l (a1), (a2)+
|
|
move.l (a1), (a2)+
|
|
move.l (a1), (a2)+
|
|
|
|
bra.b @endloop
|
|
@notBIOSbased
|
|
ENDIF
|
|
nop ; squoosh pipeline
|
|
move.w (a1), (a2)+ ; read 16 bytes
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
@endloop
|
|
IF PostNOP THEN
|
|
nop ; squoosh pipeline
|
|
ENDIF
|
|
|
|
; Note that intrp is asserted only after transfer count is 0, no DREQs pending,
|
|
; and the target asserts REQ for the next byte.
|
|
bsr.w QuickIntCheck ; Really Fast check for interrupt
|
|
beq.b @NoIntYet ;
|
|
@GotAnInt
|
|
move.l d1, d0 ; copy d1 since we might need to pass it to doIntRegs
|
|
andi.l #$00BF00FF, d0 ; don't care about FOS or Gross Error bit !!! use constant
|
|
cmp.l #$00910010, d0 ; Interrupt Pending, XferCnt 0, Data In, Bus Service !!! use constant
|
|
beq.b @bottomRead16 ; cool, keep going with xfer
|
|
; not what we were looking for, doIntRegs
|
|
jsr FullIntRegs ; re-enter doIntRegs d1 is the parameter
|
|
bra.b @bottomRead16 ; we just did a wt4scsi the hard way
|
|
@NoIntYet
|
|
ClearSCSIIRQ ; make sure that we don't go async with an interrupt
|
|
; pending at the VIA but not at the c96
|
|
bsr.w Wt4SCSIInt ; Do the long wait for an interrupt
|
|
bsr.w DeferAndWait ; defer til interupts are enabled
|
|
|
|
@bottomRead16
|
|
dbra D4, @read16 ; loop until done, d4 is lower word count
|
|
dbra D6, @read16 ; loop until done, d6 is upper word count
|
|
|
|
ClearSCSIIRQ ; make sure that we don't go async with an interrupt
|
|
; pending at the VIA but not at the c96
|
|
|
|
;
|
|
; Take care of "singles" - left overs less than 16 bytes. Use polled reads for this
|
|
; by issuing cIOXfer command, waiting for the interrupt then getting the byte. Must
|
|
; check for proper phase between each byte.
|
|
;
|
|
bra.s @16OrLess ; bra. to bottom of loop for zero case
|
|
@rdSingle
|
|
RecCmd $10, 'D',$06
|
|
move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers
|
|
eieio
|
|
|
|
bsr.w Wt4SCSIInt ; Wait for intrp w/o timeout
|
|
move.b rFIFO(a3), (a2)+ ; xfer byte from FIFO into input buffer
|
|
@16OrLess
|
|
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
|
|
dbne D2, @rdSingle ; read the rest of the remainders
|
|
bne.s @singlesPhaseChange
|
|
moveq.l #0, D1 ; no bytes left to transfer
|
|
rts ;
|
|
|
|
;
|
|
; Premature phase change - get leftover bytes out of FIFO
|
|
;
|
|
@prematurePhzChgSR
|
|
bsr Ck4DREQ ; more than 2 bytes in FIFO?
|
|
beq.s @5 ; if no DREQ, skip dummy rDMA access
|
|
move.w rDMA(a3), (a2)+ ; get the word out of the FIFO
|
|
addq.l #2,d3 ; account for this data in residual length
|
|
bra.s @prematurePhzChgSR ; and see if there's more
|
|
@5 ; and give us that intrp
|
|
bsr.w Wt4SCSIInt ; Clear pending intrp & get err status
|
|
; on exit d5 = rFOS|rINT|0|rSTA
|
|
@gotPrematureInt
|
|
moveq.l #mFIFOCount, D0 ; use mask to get FIFO flag field
|
|
and.b rFIFOflags(a3), D0 ; how many bytes left in FIFO?
|
|
add.l D0, D3 ; add remainder to our log
|
|
bra.s @btmLeftovers
|
|
@topLeftovers
|
|
move.b rFIFO(a3), (a2)+
|
|
@btmLeftovers
|
|
dbra D0, @topLeftovers
|
|
|
|
; calc how many bytes we've xferred...
|
|
@singlesPhaseChange
|
|
swap D6 ; calculate bytes left to transfer
|
|
move.w D4, D6 ; form long word count
|
|
addq.l #1, D6 ; undo adjustment for aborted dbra
|
|
lsl.l #4, D6 ; mult by 16
|
|
add.l D2, D6 ; add un-xferred remainder
|
|
sub.l D3, D6 ; account for bytes removed after phase chg
|
|
move.l D6, D1 ; number of bytes not transferred
|
|
rts
|
|
|
|
|
|
RTSNAME 'SlowReadReal96'
|
|
|
|
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
; FastRead96 - implements FastRead
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
; All primitive data transfer routines assume:
|
|
;
|
|
; Inputs --->
|
|
; -> D2.L number of bytes to transfer
|
|
; -> A2 ptr to data buffer - saved
|
|
; -> A3 SCSI chip read base address - SERIALIZED
|
|
; -> A5 ptr to SCSI Mgr globals
|
|
;
|
|
; Outputs <---
|
|
; <- d1.L bytes not transferred
|
|
;
|
|
; Internal ---
|
|
; D3 - trashed
|
|
; A0 - scratch - saved
|
|
; A1 - SCSI chip read base address - NON-SERIALIZED
|
|
; A2 - ptr to data buffer - saved
|
|
; A3 - SCSI chip read base address - SERIALIZED
|
|
; A5 - ptr to SCSI Mgr globals
|
|
;
|
|
; 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
|
|
|
|
D2_thisTime EQU D2 ; .l = number of bytes remaining in this block
|
|
D3_total EQU D3 ; .l = number of bytes remaining in this request
|
|
|
|
;————————————
|
|
FastRead96
|
|
;
|
|
; Loop thru scsiHandshake structure
|
|
;
|
|
move.l D2, D3_total ; save original total request count
|
|
@firstHandshake
|
|
lea scsiHandshake(A0), A1 ; and now with ptr to scsiHandshake
|
|
tst.w (A1) ; if no handshake,
|
|
beq.s FastRead96inner ; -> skip handshake, goto inner routine
|
|
|
|
moveq #0, D2_thisTime
|
|
move.w hdshkRemainder(a0), D2_thisTime ; Leftover Hansdhake?
|
|
beq.b @next ; nope, start handshake over again
|
|
|
|
move.w #0, hdshkRemainder(a0) ; don't try to do it again...
|
|
moveq #0, d0
|
|
move.b hdshkIndex(a0), d0 ; find out which handshake element we were working on
|
|
lsl.w #1, d0 ; adjust for word length
|
|
add.w d0, A1 ; index into the handshake array
|
|
bra.b @handshake ; and do it
|
|
@next
|
|
moveq.l #0, D2_thisTime
|
|
move.w (A1)+, D2_thisTime
|
|
beq.s @firstHandshake
|
|
@handshake
|
|
sub.l D2_thisTime, D3_total ; decrement total by this hsk count
|
|
beq.s @lastOne ; if same, -> this is the last one
|
|
blo.s @useTotal ; if total<hsk, -> use the total left
|
|
|
|
move.l A1, -(sp)
|
|
bsr.s FastRead96inner ; else, use the hsk value as count
|
|
move.l (sp)+, A1
|
|
|
|
tst.w D1 ; did we go out of phase?
|
|
bne.s @didntFinish ; -> yes, adjust unxferred count and ret
|
|
|
|
bra.s @next ; -> no, get the next hsk for next xfer
|
|
|
|
@didntFinish
|
|
add.l D3_total, D1 ; readjust total count by unxfered amount
|
|
rts ; (D1 is return value)
|
|
|
|
@useTotal
|
|
add.l D2_thisTime, D3_total ; restore total to what it was...
|
|
move.l D2_thisTime, D0 ; remember what handshake was
|
|
sub.l D3_total, D0 ; and figure out the remainder was
|
|
move.w D0, hdshkRemainder(A0) ; so we can remember it for later
|
|
move.l A1, D0 ; get next hanshake
|
|
sub.l #2, D0 ; and back up one word (element)
|
|
lea scsiHandshake(A0), A1 ; get the first hanshake
|
|
sub.l A1, D0 ; calculate the offset
|
|
move.b D0, hdshkIndex(A0) ; and remember it for later (whew!)
|
|
move.l D3_total, D2_thisTime
|
|
@lastOne
|
|
bsr.s FastRead96inner
|
|
rts
|
|
|
|
|
|
;————————————
|
|
FastRead96inner
|
|
IF 1 AND RECORD_ON THEN
|
|
pea 'FsRd' ; EVENT =
|
|
move.l scsiDataResidual(A0), -(sp)
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
|
|
IF 1 AND RECORD_ON THEN
|
|
move.l D2, -(sp) ; number of bytes
|
|
move.l A2, -(sp) ;
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
|
|
|
|
;
|
|
; Setup A1 to point to a non-serialized image of the c96. If such an image doesn't exist this
|
|
; value will be the same as the serialized image.
|
|
;
|
|
move.l pdmaNonSerlzdAddr(A5), A1 ; point to non-serialized chip image
|
|
|
|
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 PhaseErrAtStart ; bra. on phase err
|
|
|
|
cmpi.l #1, d2 ; special case a 1 byte transfer
|
|
bne.s @not1 ; <SM5> pdw
|
|
bsr OneByteRead ;
|
|
subq.l #1, d2
|
|
bra @goodFRead
|
|
@not1
|
|
;
|
|
; Install our bus error handler because we are going into a routine that blindly accesses
|
|
; the SCSI chip (and it might not always be ready)
|
|
;
|
|
bsr.w InstallBEH96
|
|
|
|
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
|
|
bra.s @skipPhaseCk
|
|
@next64KB
|
|
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 @outOfPhase ; outta here.
|
|
@skipPhaseCk
|
|
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)
|
|
eieio
|
|
|
|
RecCmd $90, 'D',$08
|
|
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & begin xfers
|
|
eieio
|
|
|
|
; move.l a2, d5 ;
|
|
; btst.l #0, d5 ; check if input buffer is on word boundary
|
|
; bne.s @misAligned
|
|
@aligned
|
|
move.l d2, d4 ; d4 = copy of transfer count
|
|
lsr.l #5, d4 ; divide xfer count by 32
|
|
ror.l #1, d2 ; xfer byte count to word & remember odd byte
|
|
and.w #$F, d2 ; d2 = remainder word count after 32-byte moves
|
|
neg.w d2 ; negate to form a backward jump offset
|
|
nop ; squoosh pipeline
|
|
jmp @RdLoop(d2.w*2) ; bra. into the loop
|
|
@read32 ;
|
|
move.w (a1), (a2)+ ; do 16 bytes
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
|
|
move.w (a1), (a2)+ ; do another 16 bytes
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+
|
|
move.w (a1), (a2)+ ;
|
|
@RdLoop
|
|
IF DEBUGGING THEN
|
|
tst.b rXCL(A3) ; just for analyzer
|
|
ENDIF
|
|
dbra d4, @read32 ; d4 = # of 32-byte transfers
|
|
IF PostNOP THEN
|
|
nop ; squoosh pipeline
|
|
ENDIF
|
|
bsr.w RemoveBEH96
|
|
bsr.w Wt4SCSIInt ; Wait for intrp w/o timeout
|
|
bsr.w DeferAndWait ; defer til interupts are enabled
|
|
bsr.w InstallBEH96
|
|
|
|
bclr.l #31, d2 ; check if we expected a residual byte
|
|
beq.s @2 ; bra. if no residual
|
|
|
|
moveq.l #mFIFOCount, d0 ; use mask to get FIFO flag field
|
|
and.b rFIFOflags(a3), d0 ; get # of bytes in FIFO
|
|
bne.s @3 ; get byte if FIFO is not empty
|
|
IfDebugStr 'FIFO Empty when we expected residual byte'
|
|
bra.b @2
|
|
@3
|
|
bset.l #31, d2 ; check if we expected a residual byte
|
|
move.b rFIFO(a3), (a2)+ ; xfer residual byte
|
|
@2
|
|
move.l #$10000, d2 ; init to transfer 64K bytes
|
|
dbra d6, @next64KB ;
|
|
bsr.w RemoveBEH96
|
|
;
|
|
; Remove our bus error handler
|
|
;
|
|
@goodFRead ; Don't remove bus error handler here
|
|
; because we can get here from a 1 byte read
|
|
moveq.l #0, D1 ; d1 = # of bytes transferred
|
|
@exit
|
|
rts ;
|
|
|
|
|
|
@outOfPhase
|
|
bsr.w RemoveBEH96
|
|
add.l #1,d6 ; undo for dbra
|
|
swap d6 ; d6 contains the number of 64k xfers
|
|
move.w #0, D6 ; we only care about the high word
|
|
|
|
move.l d6,d1 ; return the number of bytes left to be xferred
|
|
bra.s @exit
|
|
|
|
RTSNAME 'FastRead96'
|
|
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
PhaseErrAtStart
|
|
move.l D2, D1 ; return same number as requested
|
|
rts ;
|
|
|
|
NAME 'PhaseErrAtStart'
|
|
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
; SlowWrite96 - implements Polled Write
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
; All primitive data transfer routines assume:
|
|
;
|
|
; 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
|
|
; A5 - ptr to SCSI Mgr globals
|
|
; a5 - scratch - saved
|
|
;
|
|
;
|
|
|
|
SlowWrite96
|
|
IF 1 AND RECORD_ON THEN
|
|
pea 'SwWr' ; EVENT =
|
|
move.l scsiDataResidual(A0), -(sp)
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
|
|
IF 1 AND RECORD_ON THEN
|
|
move.l D2, -(sp) ; number of bytes
|
|
move.l A2, -(sp) ;
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
|
|
|
|
moveq #0, D3 ; d3 is a residual count, assume zero for now
|
|
|
|
tst.b currentPhase(A5) ;
|
|
bne.w PhaseErrAtStart ; data-out phase bits = 0, bra. on phase err
|
|
|
|
@skipPhaseCk
|
|
; Fill up FIFO with data and start a transfer
|
|
|
|
move.l D2, D4
|
|
lsr.l #4, D4 ; how many 16-byte loops are needed
|
|
move.l D4, D6 ; prep D4 to be low word counter
|
|
swap D6 ; prep D6 to be high word counter
|
|
and.w #$F, D2 ; d2 = remainder word count after 16-byte moves
|
|
beq.b @loopBottom ; if nothing this time do the DBRA and 16 full bytes
|
|
neg.w D2 ; negate to form a backward jump offset
|
|
jmp @bottomOfMoveBs(D2.w*4) ; bra. into the loop
|
|
|
|
@write16 ;
|
|
move.b (a2)+, rFIFO(a3) ; do 8 bytes
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
;
|
|
move.b (a2)+, rFIFO(a3) ; do 8 more bytes
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
move.b (a2)+, rFIFO(a3)
|
|
@bottomOfMoveBs
|
|
RecCmd $10, 'D',$0a
|
|
move.b #cIOXfer, rCMD(a3) ; load IO transfer cmd & begin xfers
|
|
eieio ; nonserialized I/O machines only
|
|
|
|
bsr.w QuickIntCheck ; Really Fast check for interrupt
|
|
beq.b @NoIntYet ;
|
|
|
|
move.l D1, D0 ; copy d1 since we might need to pass it to doIntRegs
|
|
andi.l #$00BF00FF, D0 ; don't care about some flags or Gross Error bit !!! use constant
|
|
cmp.l #$00900010, D0 ; Interrupt Pending, XferCnt 0, Data Out, Bus Service !!! use constant
|
|
bne.b @doFullRegs ; not cool->do the full int thing
|
|
|
|
@loopBottom
|
|
dbra D4, @write16 ; d4 = # of 16-byte tranfers (low word)
|
|
dbra D6, @write16 ; d6 = # of 16-byte tranfers (high word)
|
|
;
|
|
; We've transferred all requested data.
|
|
;
|
|
@completeSlowWrite
|
|
ClearSCSIIRQ ; make sure that we don't go async with an interrupt
|
|
; pending at the VIA but not at the c96
|
|
moveq.l #0, D1 ; D1 = # of bytes remaining
|
|
rts ;
|
|
|
|
|
|
|
|
|
|
@doFullRegs ; not what we were looking for, doIntRegs
|
|
bsr.w FullIntRegs ; re-enter doIntRegs d1 is the parameter
|
|
bra.b @afterWt4 ; we just did a wt4scsi the hard way
|
|
@NoIntYet
|
|
ClearSCSIIRQ ; make sure that we don't go async with an interrupt
|
|
; pending at the VIA but not at the c96
|
|
bsr.w Wt4SCSIInt ; Do the long wait for an interrupt
|
|
|
|
bsr.w DeferAndWait ; defer til interupts are enabled
|
|
@afterWt4
|
|
tst.b currentPhase(A5) ;
|
|
beq.s @loopBottom
|
|
|
|
moveq.l #mFIFOCount, D1 ;
|
|
and.b int_rFIFOflags(A5), D1 ; get # of un-xferred data in FIFO
|
|
bne.s @doFlush
|
|
@afterFlush
|
|
swap D6 ; move hi word of counter into hi word of long
|
|
move.w D4, D6 ; get low order word into same long
|
|
lsl.l #4, D6 ; multiply by 16
|
|
|
|
add.l D6, D1 ; add FIFOcount to total bytes left
|
|
rts ;
|
|
|
|
@doFlush
|
|
RecCmd $01, 'D',$0c
|
|
move.b #cFlushFIFO, rCMD(a3) ; Flush FIFO
|
|
eieio
|
|
bra.s @afterFlush
|
|
|
|
RTSNAME 'SlowWrite96'
|
|
|
|
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————
|
|
; FastWrite - implements Blind Write
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
; All primitive data transfer routines assume:
|
|
;
|
|
; d1 - <-- bytes transferred
|
|
; d2 - --> number of bytes to transfer
|
|
; d3 - scratch - saved
|
|
; 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
|
|
; A5 - 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
|
|
|
|
;————————————
|
|
FastWrite96
|
|
;
|
|
; Loop thru scsiHandshake structure
|
|
;
|
|
move.l D2, D3_total ; save original total request count
|
|
@firstHandshake
|
|
lea scsiHandshake(A0), A1 ; and now with ptr to scsiHandshake
|
|
tst.w (A1) ; if no handshake,
|
|
beq.s FastWrite96inner ; -> skip handshake, goto inner routine
|
|
|
|
moveq #0, D2_thisTime
|
|
move.w hdshkRemainder(a0), D2_thisTime ; Leftover Hansdhake?
|
|
beq.b @next ; nope, start handshake over again
|
|
|
|
move.w #0, hdshkRemainder(a0) ; don't try to do it again...
|
|
moveq #0, d0
|
|
move.b hdshkIndex(a0), d0 ; find out which handshake element we were working on
|
|
lsl.w #1, d0 ; adjust for word length
|
|
add.w d0, A1 ; index into the handshake array
|
|
bra.b @handshake ; and do it
|
|
@next
|
|
moveq.l #0, D2_thisTime
|
|
move.w (A1)+, D2_thisTime
|
|
beq.s @firstHandshake
|
|
@handshake
|
|
sub.l D2_thisTime, D3_total ; decrement total by this hsk count
|
|
beq.s @lastOne ; if same, -> this is the last one
|
|
blo.s @useTotal ; if total<hsk, -> use the total left
|
|
|
|
move.l A1, -(sp)
|
|
bsr.s FastWrite96inner ; else, use the hsk value as count
|
|
move.l (sp)+, A1
|
|
|
|
tst.w D1 ; did we go out of phase?
|
|
bne.s @didntFinish ; -> yes, adjust unxferred count and ret
|
|
|
|
bra.s @next ; -> no, get the next hsk for next xfer
|
|
|
|
@didntFinish
|
|
add.l D3_total, D1 ; readjust total count by unxfered amount
|
|
rts ; (D1 is return value)
|
|
|
|
@useTotal
|
|
add.l D2_thisTime, D3_total ; restore total to what it was...
|
|
move.l D2_thisTime, D0 ; remember what handshake was
|
|
sub.l D3_total, D0 ; and figure out the remainder was
|
|
move.w D0, hdshkRemainder(A0) ; so we can remember it for later
|
|
move.l A1, D0 ; get next hanshake
|
|
sub.l #2, D0 ; and back up one word (element)
|
|
lea scsiHandshake(A0), A1 ; get the first hanshake
|
|
sub.l A1, D0 ; calculate the offset
|
|
move.b D0, hdshkIndex(A0) ; and remember it for later (whew!)
|
|
move.l D3_total, D2_thisTime
|
|
@lastOne
|
|
bsr.s FastWrite96inner
|
|
rts
|
|
|
|
;————————————
|
|
FastWrite96inner
|
|
IF 1 AND RECORD_ON THEN
|
|
pea 'FsWr' ; EVENT =
|
|
move.l scsiDataResidual(A0), -(sp)
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
|
|
IF 1 AND RECORD_ON THEN
|
|
move.l D2, -(sp) ; number of bytes
|
|
move.l A2, -(sp) ;
|
|
bsr RecordEvent
|
|
addq.l #8, sp
|
|
ENDIF
|
|
|
|
|
|
;
|
|
; Setup A1 to point to a non-serialized image of the c96. If such an image doesn't exist this
|
|
; value will be the same as the serialized image.
|
|
;
|
|
move.l pdmaNonSerlzdAddr(A5), A1 ; point to non-serialized chip image
|
|
|
|
moveq.l #iPhaseMsk, d0 ;
|
|
and.b rSTA(a3), d0 ; are we in data-out phase?
|
|
bne.w PhaseErrAtStart ; data-out phase bits = 0, bra. on phase err
|
|
|
|
cmpi.l #1, d2 ; if 1 byte write then...
|
|
bne.s @not1 ; <SM5> pdw
|
|
bsr OneByteWrite ; ...do 1 byte transfer
|
|
subq.l #1, D2
|
|
bra @goodFWrite
|
|
@not1
|
|
|
|
;
|
|
; Install our bus error handler because we are going into a routine that blindly accesses
|
|
; the SCSI chip (and it might not always be ready)
|
|
;
|
|
bsr.w InstallBEH96
|
|
|
|
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
|
|
|
|
move.l a2, d5 ;
|
|
btst.l #0, d5 ; check if input buffer is on word boundary
|
|
bne.w @misAligned
|
|
bra.b @skipPhaseCk
|
|
@next64KB ; buffer is aligned from this point
|
|
moveq.l #iPhaseMsk, d0 ; load mask for phase bits
|
|
and.b rSTA(a3), d0 ; are we in data-out phase?
|
|
bne.w @phaseErr ; nope, outta here.
|
|
@skipPhaseCk
|
|
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
|
|
eieio
|
|
|
|
RecCmd $90, 'D',$0e
|
|
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & begin xfers
|
|
eieio
|
|
|
|
move.l d2, d4 ; d4 = copy of transfer count
|
|
lsr.l #5, d4 ; divide xfer count by 32
|
|
ror.l #1, d2 ; xfer byte count to word & remember odd byte
|
|
and.w #$F, d2 ; d2 = remainder word count after 32-byte moves
|
|
neg.w d2 ; negate to form a backward jump offset
|
|
nop ; squoosh pipeline
|
|
jmp @WrLoop(d2.w*2) ; bra. into the loop
|
|
@write32 ;
|
|
move.w (a2)+, (a1) ; do 16 bytes
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
|
|
move.w (a2)+, (a1) ; do another 16 bytes
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1)
|
|
move.w (a2)+, (a1) ;
|
|
@WrLoop
|
|
dbra d4, @write32 ; d4 = # of 32-byte tranfers
|
|
nop ; Force write to complete. <SM7>
|
|
; INT & TC maybe TRUE at this point
|
|
btst.l #31, d2 ; check if we have a residual byte
|
|
beq.s @noResidual ;
|
|
@residual
|
|
move.b (a2)+, rDMA(a3) ; xfer residual byte
|
|
eieio
|
|
@noResidual
|
|
bsr.w RemoveBEH96 ; remove BEH when not in data xfer routine
|
|
bsr.w Wt4SCSIInt ; Wait for intrp w/o timeout
|
|
; on exit d5 = rFOS|rINT|0|rSTA
|
|
bne.s @goodFWrite ; bra. if ended up disconnected
|
|
|
|
bsr.w DeferAndWait ; defer til interupts are enabled
|
|
bsr.w InstallBEH96
|
|
@2
|
|
move.l #$10000, d2 ; init to transfer 64K bytes
|
|
dbra d6, @next64KB ;
|
|
@disconnected
|
|
;
|
|
; Remove our bus error handler
|
|
;
|
|
bsr.w RemoveBEH96
|
|
@goodFWrite
|
|
moveq.l #0, D1 ; d1 = # of bytes transferred
|
|
@exit
|
|
rts ;
|
|
|
|
|
|
@misAligned ;
|
|
subq.l #1, d2 ; adjust for transfer count calc
|
|
move.b (a2)+, rFIFO(a3) ; ...preload fifo with odd byte
|
|
eieio
|
|
bra.w @next64KB ; now we're word aligned
|
|
@phaseErr
|
|
bsr.w RemoveBEH96
|
|
add.l #1,d6 ; undo for dbra
|
|
swap d6 ; d6 contains the number of 64k xfers
|
|
move.w #0, D6 ; we only care about the high word
|
|
|
|
move.l d6,d1 ; return the number of bytes left to be xferred
|
|
bra.s @exit
|
|
|
|
RTSNAME 'FastWrite96'
|
|
|
|
|
|
|
|
|
|
ENDWITH
|
|
|
|
END
|