sys7.1-doc-wip/OS/SCSIMgr4pt3/HALc96DMA.a
2019-07-27 22:37:48 +08:00

1149 lines
40 KiB
Plaintext

;____________________________________________________________________________________
;
; File: HALc96DMA.a
;
; Contains: Stuff for 53c96 machines with some kind of DMA (Cyclone, PDM etc.)
;
; Written by: Paul Wolf
;
; Copyright: © 1990-1993 by Apple Computer, Inc., all rights reserved.
;
; NOTE! This file is INCLUDED by HALc96PSC.a and HALc96AMIC.a, it is not assembled
; separately. Each of these includers, set up the build flags according to
; the requirements of that hardware.
;
; The flags/equates used in this process are:
; Symbol ————— AMIC value ————— PSC value —————
; DMA_MinMemoryXfer 8 16
; DMA_NeedsCount FALSE TRUE
; DMA_WriteNeedsReset FALSE TRUE
; DMA_Title AMIC PSC
;
; Change History (most recent first):
;
; <SM17> 11/22/93 pdw Rolling in from <MCxx>.
; <MC8> 10/29/93 pdw Removed _debugger statements.
; <MC7> 10/29/93 pdw Fixed the buserror caused by DMAi when VM is on by using the
; logical address instead of the physical when doing alignment!
; Also enhanced the alignment code to use dmaAlignMask so it's
; accurate both for PSC and AMIC.
; <SM16> 10/29/93 pdw Got rid of _Debugger statements that screwed some things.
; <SM15> 10/29/93 DCB <MC> roll-in.
; <MC6> 10/28/93 pdw Fixed some DMA in and out residual length bugs.
; <SM14> 10/15/93 pdw Getting rid of bogus forPDMProto.
; <SM13> 10/14/93 pdw <MC> roll-in.
; <MC4> 10/12/93 pdw Changed and.b to and.l in DMAo so that the following bne.s does
; what it's meant to do.
; <MC3> 10/12/93 pdw Added support for Synchronous data transfers, rewrote State
; Machine, message handling etc.
; <MC2> 10/6/93 pdw Added forPDMProto stuff around checks for doRealDMAxxx.
; <SM12> 9/12/93 pdw Changed the size of the double-buffering buffer.
; <SM11> 9/9/93 pdw Lots of little changes. Name changes, temporary cache_bug
; stuff.
; <SM10> 8/13/93 pdw RecordCmd and eieio stuff.
; <SM9> 7/19/93 pdw Got rid of a beq.s to the next instruction to get rid of build
; warning.
; <SM8> 7/17/93 pdw Lots of little things. Some major things.
; <SM7> 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.
; <SM6> 5/26/93 PW Moving the restore of the CF3 register till after the loop in
; DataIn_DMA and DataOut_DMA.
; <SM5> 5/25/93 DCB Rollin from Ludwig. (The next item below)
; <LW7> 5/21/93 PW Changing some DebugStrs to IfDebugStrs+Syserr.
; <LW6> 5/20/93 DCB Changing _debuggers to DebugStrs so we can turn them off at
; compile time.
; <SM4> 5/5/93 PW Converted names to meanies-friendly names. Updated with latest
; from Ludwig stuff.
; <SM3> 3/29/93 PW Slight rearrangement of forPDMProto so that I could put in the
; btst for useRealDMARead and useRealDMAWrite flags. Created
; pseudo-real DMA write and read routines to use if these bits
; aren't set.
; <LW4> 3/31/93 DCB Changing the bhi's I put in last night to bhs's which is what
; they should have been.
; <LW3> 3/30/93 DCB Changing a couple of bge's to bhi's to fix a hang with large RAM
; disks. Essentially the GetPhysical translation got screwed up
; because of a very large address that was interpreted as
; negative.
; <LW2> 3/26/93 PW Fixed serious misalignment/disconnection bug by using the
; StopReadDMA result in D0 instead of rXCx when not on a PDM.
; <1+> 3/21/93 PW Removing bra to SlowWrite - using HALc96Init to configure
; instead.
; <1> 3/20/93 PW first checked in
;
; HALc96PSC.a Change History (before split into …DMA.a, …AMIC.a and …PSC.a)
; <SM19> 3/4/93 PW Made GPHYSICAL optional through -d.
; <LW6> 3/1/93 DCB Added conditional compile for GetPhysical call. This aids
; performance testing.
; <LW5> 2/17/93 PW Removed some old Terror change tags.
; <SM18> 1/31/93 PW Update from the latest of Ludwig. Also changes required for PDM
; (will update Ludwig with these as needed myself).
; <LW4> 1/27/93 DCB Added workaround to a bug in the HWPriv.a GetPhysical. Seems
; that in cases where the logical range was not contiguous the
; logical address and count in the table were not updated
; correctly. The workaround reconstructs these fields manually so
; we don't get stuck in a loop.
; <LW3> 1/27/93 PW Got rid of some bad comments.
; <LW2> 1/6/93 DCB Check kbUseDblBuffer bit in the SIMprivFlags if an address in
; the Parameter Block is in the ROM or above. This fixes the SCSI
; into NuBus bug. Radar Bug # 1059322. This prevents the PSC from
; doing DMA to addresses it can't handle by DMAing into a known
; locked down buffer and then blockmoving out to the NuBus
; address.
; <SM17> 12/5/92 PW Changed field names. Added phase check at beginning of
; DataInPSC_DMA. Cleaned up alignment setup.
; <SM16> 11/20/92 DCB Fixed minor problem where DataOutPSC might inadvertantly return
; an error (nobody was paying attention to it but still...)
; <SM15> 11/12/92 PW Added missing # sign causing failed Writes if BetterBusError
; installed.
; <SM14> 10/30/92 DCB Added support for direct DMA (!!!)
; <SM13> 10/14/92 PW Put in the real fix for the premature phase change on DMA write
; bug - flushed FIFO and subtracted unxferred count from xferred
; count.
; <SM12> 10/8/92 PW Changed BFR_SIZE to 512 until I get a real fix for premature
; phase change on write bug.
; <SM11> 10/8/92 PW Added write/read/compare CNT and ADDR register writes to help in
; discovering possible PSC bug. Note: PSC_WRITE_CHECK flag must
; be set to 1 to enable the DebugStr when miscompare occurs.
; <SM10> 10/8/92 PW Lots of trivial name changes. Added GrossError checks. (cb)
; Fixed bug with .w vs .l direction parameter to StartDMA.
; <SM9> 9/30/92 fau After talking to PW, I added a TestFor YMCADecoder to enable DMA
; Writes on EVT4's, since they all have the Curio B0.
; <SM8> 8/31/92 PW Changed register and command definitions to reflect changes to
; SCSIEqu53c96.
; <SM7> 8/31/92 PW Fixed bug that trashed Jeff Boone's drive. It was a bad
; remainder count being setup after OneByteWrite in
; DataOut_PSC1x1.
; <SM6> 8/30/92 PW Added DMA Write stuff. Currently disabled unless 'sw c0c^+200
; 0000' is issued.
; <SM5> 8/1/92 PW Fixed write bug for 1,511 TIBs. Greatly increased speed of
; alignment process. Particularly critical in 1,511 TIBs.
; <SM4> 7/29/92 PW Changed test for 1 byte transfer so that it uses OneByteRead
; instead of attempting to use DMA.
; <SM3> 7/28/92 PW Adding slight StopDMA changes.
; <SM2> 7/27/92 PW Got DMA working.
; <SM1> 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
BFR_SIZE EQU $1000 ; Size of Buffer for Double Buffering
MIN_PSC_DMA_SIZE EQU $200 ; Double Buffer at or below this value
MIN_AMIC_DMA_SIZE EQU $20 ; Double Buffer at or below this value
; Check HALc96PSC.c also!
WEIRDSHIT equ 0
wholeErrors equ 1
INCLUDE 'SysErr.a'
INCLUDE 'SysEqu.a'
INCLUDE 'HardwarePrivateEqu.a'
INCLUDE 'TrapsPrivate.a'
INCLUDE 'Debug.a' ; for NAME macro
INCLUDE 'SCSI.a'
INCLUDE 'SCSIEqu53c96.a'
INCLUDE 'ACAM.a'
INCLUDE 'SIMCoreEqu.a'
INCLUDE 'HALc96equ.a'
PRINT ON ; do send subsequent lines to the listing files
CASE OBJECT
IMPORT RecordEvent
IMPORT Wt4SCSIInt
IMPORT OneByteRead, OneByteWrite
IMPORT Ck4DREQ, Ck4SCSIInt
; IMPORT MungeScreen
IMPORT AsmInit53c9xHW
IMPORT DoSelect
IMPORT FastRead96
;--------------------------------------------------------------------------
; DataIn_DMA - implements DMA Read
;
; Called by: Transfer
;
; All primitive data transfer routines assume:
;
; Inputs --->
; -> D2.L number of bytes to transfer
; -> D1.L copy of d2
; -> 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
;
; Outputs <---
; <- D0.W error (if any)
; <- D1.L bytes transferred
; <- D5.L xxxx|xxxx|xxxx|rSTA
; <- D3 scratch - saved
; <- D4
; <- D6 scratch - saved
;
;
DataIn_DMA1x1 PROC EXPORT
WITH HALc96GlobalRecord
IF 1 AND RECORD_ON THEN
pea '1x1i' ; EVENT =
move.l D2, -(sp) ; number of bytes
bsr RecordEvent
addq.l #8, sp
ENDIF
move.l d2, d3 ;
swap d3 ; d3.w:d2.w = total#bytes to xfer
bra.s @LpReadBtm
@LpReadTop
move.b #cIOXfer, rCMD(a3) ; load non-DMA transfer cmd & begin 1 byte xfer
eieio
;
; WtForInt
;
@noTimeoutWait
clr.l d5 ;
move.b rSTA(a3), d5 ; read Status regr
bpl.s @noTimeoutWait ; ...loop until intrp req is detected
move.b D5, D0
and.b #iPhaseMsk, D0
move.b D0, currentPhase(A5)
swap d5 ;
move.b rFIFOflags(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
; get Disconnect & Bus Service bits
andi.b #(1<<bDisconnected)+(1<<bBusService), d0
cmpi.b #1<<bBusService, d0 ; expecting: not disconnected
bne.s @disconnected ; bra. if ended up disconnected
; WtForInt routine that fails!!!
;@1
; btst.b #bINT, rSTA(a3) ; INT?
; beq.s @1
; btst.b #bDisconnected, rINT(a3)
; bne.s @disconnected ; bra. if ended up disconnected
move.b rFIFO(a3), (a2)+ ; get the byte
@LpReadBtm
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.s @phaseErr ; bra. on phase err
dbra d2, @LpReadTop ; d2 = low word of count
dbra d3, @LpReadTop ; d3 = high word of count
moveq.l #0, d1
@disconnected
@good ; d1 = # of bytes transferred
rts ;
@phaseErr
swap d3 ; calculate bytes left to transfer
move.w d2, d3 ; merge words
move.l d3, d1
rts ;
NAME 'DataIn_DMA1x1'
ENDWITH
;--------------------------------------------------------------------------
;
DataIn_DMA PROC EXPORT
;
; Register Usage:
;
; ———————— On Entry:———————————————————————————————————————————————
D2_totalLeft EQU D2
A2_userBuffer EQU A2
;
; ———————— On Exit:———————————————————————————————————————————————
D1_xferSize EQU D1 ; .l =
;
;
; ———————— Internal:———————————————————————————————————————————————
D3_alignOffset EQU D3 ; .l = offset required for alignment of buffers
A1_physicalBfr EQU A1
WITH HALc96GlobalRecord
WITH SIMprivFlagsRecord
WITH SIM_IO, SCSIPhase
IF 1 AND RECORD_ON THEN
pea 'DMAi' ; EVENT =
move.l D2_totalLeft, -(sp) ; number of bytes
bsr RecordEvent
addq.l #8, sp
ENDIF
;
; Make sure that we're in DataIn phase before transferring
;
cmpi.b #kDataInPhase, currentPhase(A5)
bne @phaseErrAtStart ; bra. on phase err
;
; If we are transferring either 1 or 0 bytes, either do a OneByteRead or noTransfer
;
cmp.l #1, D2_totalLeft
bgt.s @doLots
bne @noTransfer
bsr OneByteRead
moveq.l #0, D2_totalLeft
bra @good
@doLots
;
; 1st TIME THRU ———— Alignment ————
;
; Adjust our DMA buffer addresses on 1st Time Thru to line up with the
; user's buffer start. That way BlockMove will be able to use MOVE16 for
; the body of the transfer.
;
move.l A2_userBuffer, D3 ; get user's buffer address
move.w dmaAlignMask+2(A5), D0 ; word-length alignment mask (00…11)
and.w D0, D3 ; get distance above alignment
beq.s @doneAligning ; if zero, skip alignment
eor.w D0, D3 ; number of bytes till alignment (1->E)
; doesn't require -1 for dbra
@alignLoop
RecCmd $10, 'M',$01
move.b #cIOXfer, rCMD(a3) ; load non-DMA transfer cmd & begin 1 byte xfer
eieio
;
; WtForInt
;
@noTimeoutWait
clr.l d5 ;
move.b rSTA(a3), d5 ; = x | x | x | rSTA
bpl.s @noTimeoutWait ; ...loop until intrp req is detected
move.b D5, D0
and.b #iPhaseMsk, D0
move.b D0, currentPhase(A5)
swap d5 ; = x | rSTA | x | x
move.b rFIFOflags(a3), d5 ; = x | rSTA | x | rFOS
lsl.w #8, d5 ; = x | rSTA | rFOS | x
move.b rINT(a3), d5 ; = x | rSTA | rFOS | rINT
move.l d5, d0 ; for checking for disconnect
move.b rFIFO(a3), (a2)+ ; get the byte
subq.l #1, D2_totalLeft
beq @good ; if xfer complete - done
; get Disconnect & Bus Service bits
andi.b #(1<<bDisconnected)+(1<<bBusService), d0
cmpi.b #1<<bBusService, d0 ; expecting: not disconnected
bne.w @disconnected ; bra. if ended up disconnected
;
; Make sure that we're in DataIn phase between each byte
;
cmpi.b #kDataInPhase, currentPhase(A5)
bne @good ; bra. on phase err
@alignLpBtm
dbra d3, @alignLoop ; check to see if we are done aligning
; ———— No alignment or done aligning ————
@doneAligning
; load init config. value. Either leaving it at regular or bumping to Threshold-8 mode
move.b rCF3DMAVal(A5), rCF3(a3)
; remember which xfer type we are doing (ParameterBlock)
; setup buffers the old way if we aren't doing full DMA
; else setup buffers the new way
bclr #noBlockMove,dmaFlags(a5) ; assume that we aren't going to DMA directly to the user's buffer
cmp.l minDMAsize(a5),D1_xferSize ; !!! is this a small buffer? (Size should correspond with HALc96PSC.c!)
ble.s @decidedXferType ; yep, use DMA/Blockmove for small buffers
cmp.b #SCSIOldCall,scsiFunctionCode(a0) ; Check to see if it is an old call
beq.s @decidedXferType ; use the old DMA/Blockmove method for TIBs
move.l scsiFlags(a0),d0 ; get the flags field
andi.l #scsiDataPhys,d0 ; did the client pass in a physical address?
bne.s @directDMA ; yep, do straight DMA
btst #kbUseDblBuffer,SIMprivFlags(a0) ; Are we doing direct DMA for this guy?
bne.s @decidedXferType ; nope, use DMA/Blockmove for small buffers
@directDMA
bset #noBlockMove,dmaFlags(a5) ; remember that we are going directly to the user's buffer
@decidedXferType
;
; 2nd TIME THRU ---- (and beyond that)
;
; If it's not the 1st time thru, we don't have to do any front alignment stuff since
; it's already aligned.
;
@loopTop
move.l A2_userBuffer,logBuffer(a5) ; save the logical address for later
IF forPDMProto THEN
move.l A2_userBuffer, dbug0(A5)
ENDIF
btst #noBlockMove,dmaFlags(a5) ; directly to the user's buffer?
bne.s @newWay ; yes -> get physical address
IF forPDMProto THEN
move.l logicalCopyBuffer(A5), dbug0(A5) ; buffer is our physical copy buffer
ENDIF
move.l #BFR_SIZE, D1_xferSize ; no, assume we'll xfer a whole buffer
bra.w @setupCount ; -> need to figure only how many
@newWay
move.l scsiFlags(a0),d0 ; get the flags field
andi.l #scsiDataPhys,d0 ; did the client pass in a physical address?
beq.s @checkAddress ; nope, get the physical address
move.l #$10000,D1_xferSize ; yep, get them dogies movin!
bra.s @setupCount
@doGetPhys
move.l A2_userBuffer,transLogStart(a0) ; load the address into the log to phys table
move.l D2_totalLeft,transLogCount(a0) ; load the count into the log to phys table
; trashing A0!
lea transLogStart(a0), A0 ; pass in the log to phys table
move.l #1,a1 ; setup the count
_GetPhysical ; do the translation
move.l HALactionPB.ioPtr(A4), A0 ; get the ioPtr again (lost it for parameter)
tst.w D0 ; well?
beq.s @fixAddress
IfDebugStr 'GetPhysical failed in DMA in' ; Aack!
moveq #dsIOCoreErr, D0
_SysError
@fixAddress
move.l transLogStart(a0),d0 ; get the buffer
cmp.l d0,a2 ; check to see if the GetPhysical Bug bit us <LW3>
bne.s @checkAddress ; nope, sometimes GetPhysical doesn't update the
; logical.addr and logical.cnt fields properly
add.l transPhyCount(a0),d0 ; find the next logical Address range
move.l d0,transLogStart(a0) ; stuff it back into the Xlation table
move.l transPhyCount(a0),d0 ; get the physical count
sub.l d0,transLogCount(a0) ; and update the count <LW3>
@checkAddress
move.l transLogStart(a0),d0 ; get the buffer
sub.l transPhyCount(a0),d0 ; VM memory manager changed the parameters
cmp.l d0,A2_userBuffer ; Are we above the start of the physical area?
blo.s @doGetPhys ; nope, get the pysical address for this range
cmp.l transLogStart(a0),A2_userBuffer ; are we past the end?
bhs.s @doGetPhys ; yep, get the physical address for this range
move.l #$10000,D1_xferSize ; assume a 64K transfer
move.l transLogStart(a0),d0 ; get the buffer
sub.l A2_userBuffer,d0 ; find out how much we can transfer into this memory block
cmp.l d0,D1_xferSize ; use the smaller of 64K and the physical size
blo.s @gotXferSize ; must be greater than 64K
move.l d0,D1_xferSize ; use the space we just calculated
@gotXferSize
move.b dmaAlignMask+3(A5), D0 ; byte length alignment (000...111)
not.b D0 ; 111...000
and.b D0,D1_xferSize ; line align this puppy!
move.l transLogStart(a0),d0 ; now calculate how far into the buffer we are
sub.l transPhyCount(a0),d0 ; VM memory manager changed the parameters
move.l A2_userBuffer,a1
sub.l d0,a1 ; and find the offset
move.l transPhyStart(a0),A2_userBuffer ; use the physical address
add.l a1,A2_userBuffer ; add the offset so we know where to DMA into
@setupCount
cmp.l D1_xferSize, D2_totalLeft ; Buffer bigger than amount left?
bhi.s @1 ; no - xfer size of buffer's worth
move.l D2_totalLeft, D1_xferSize ; yes - just xfer amount left
@1
cmp.b #dmaTypeAMIC, dmaType(A5) ; AMIC?
bne.s @notAMIC
move.l dmaAlignMask(A5), D0 ; get alignment (000…111)
not.l D0 ; 111…000
move.l D1_xferSize, D3 ; save xfer_size
and.l D0, D1_xferSize ; strip away %8 bytes, any left?
bne.s @notAMIC ; yes-> DMA the big chunks
; no: move remainder one at a time
move.l logBuffer(A5), A2_userBuffer ; restore the logical user buffer
bra.w @alignLpBtm
@notAMIC
;
; Prime the c96's transfer count registers (2 regs - byte wide)
;
IF RECORD_ON THEN
pea 'rXC-'
move.l D1_xferSize, -(sp)
bsr RecordEvent
addq.l #8, sp
ENDIF
move.l D1_xferSize, d4 ; d4 <- d1
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, 'M',$03
;
; Send DMA_transfer cmd to c96 to Start SCSI Transfer then adjust xferSize (for DMA)
; to indicate that only words (even #s of bytes) will be transferred from the c96
; via DMA. If the xferSize is odd, then the c96 will read the odd byte from the bus
; into its FIFO but it will not assert DREQ for that byte. It will generate an int
; as soon as the last full word is DMAed out of the c96 and there is a REQ on the
; bus for the next byte (or next phase).
;
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & begin xfers
eieio
IF GROSS_CHECK THEN
btst #bGrossError, rSTA(a3) ; check for gross error
beq.s @skipGEdebugger2
DebugStr 'Gross Error in DataIn_DMA'
@skipGEdebugger2
ENDIF
;
; Call the StartDMA routine with parms (scsiDirectionIn, size, buffer)
;
IF forPDMProto THEN
btst #doRealDMARead, dmaFlags(A5)
bne.s @doRealDMA
movem.l A1/A2, -(sp)
move.l dbug0(A5), A2 ; get logical address of where DMA was supposed to be
move.l pdmaAddr(A5), A1 ; get pseudo-DMA address
@fakeDMAloop
bsr Ck4DREQ
beq.s @ck4int
move.w (A1), (A2)+
move.w (A1), (A2)+
move.w (A1), (A2)+
move.w (A1), (A2)+
bra.s @fakeDMAloop
@ck4int
bsr Ck4SCSIInt
beq.s @fakeDMAloop
movem.l (sp)+, A1/A2
bra.s @doneWithDMA
@doRealDMA
ENDIF
move.w #true, -(sp) ; direction in
move.l D1_xferSize, D0
and.b #$FE, D0 ; c96 will only DMA even #s of bytes
move.l D0, -(sp) ; size
btst #noBlockMove,dmaFlags(a5) ; are we doing real DMA?
beq.s @useCopyBuffer ; nope, use the copy buffer
move.l a2_userBuffer,-(sp) ; do DMA straight into the user buffer!
bra.s @sDMA ; start the DMA
@useCopyBuffer
move.l physicalCopyBuffer(A5), -(sp) ; buffer is our physical copy buffer
@sDMA
jsr ([jvStartDMA,A5]) ; returns A2_SetRegs, A1_ChanlControl
lea 10(sp), sp ; get parameters off stack
;
; Wait until c96 says it's done (either because it xferred all the bytes and it's
; ready again or because there was a change of phase (no longer in data_in phase)
; Once it's done, stop the DMA and see how far it got and BlockMove that many bytes
; into the user's buffer. The c96 does not interrupt us until AFTER it has DMA'd
; all of the DMAable bytes. This means that the DMA should be stable (except for a
; possible final flush that may be in progress). The c96 may have extra byte(s) left
; over in its FIFO however, and this can be checked for and retrieved directly from
; the c96.
;
bsr.w Wt4SCSIInt ; Wait for intrp
jsr ([jvStopReadDMA,A5])
@doneWithDMA
moveq.l #0, D0 ; prepare for…
move.b rXCM(A3), D0 ; get remaining xfer count into D0
lsl.l #8, D0
move.b rXCL(A3), D0
IF RECORD_ON THEN
pea '-rXC'
move.l D0, -(sp)
bsr RecordEvent
addq.l #8, sp
ENDIF
sub.l D0, D1_xferSize ; D1 = number of bytes xferred
btst #noBlockMove,dmaFlags(a5) ; are we doing real DMA?
bne.s @afterBM ; yep, no need to do a block move!
move.l D1_xferSize, -(sp) ; save D1 --
;trashing A0
move.l D1_xferSize, D0 ; D0 = input to BlockMove: size of move
and.b #$FE, D0 ; c96 only DMA'd even #s of bytes
move.l logicalCopyBuffer(A5), A0 ; A0 = source: DMA copy buffer
move.l A2_userBuffer, A1 ; A1 = destination: user's buffer
_BlockMoveData
move.l (sp)+, D1_xferSize ; restore D1 --
move.l HALactionPB.ioPtr(A4), A0 ; get the ioPtr again (lost it for parameter)
@afterBM
move.l logBuffer(a5),A2_userBuffer ; restore the logical user buffer
;
; Displace buffer by amount transferred and also decrement totalLeft by that amount
;
add.l D1_xferSize, A2_userBuffer ; displace user's buffer by the amount xferred
sub.l D1_xferSize, D2_totalLeft ; sub done from need to do
;
; Check to see if there is an extra byte in the c96's FIFO; if so, get it
;
moveq.l #mFIFOCount, D0 ; use mask to get FIFO flag field
and.b rFIFOflags(a3), D0 ; how many bytes left in FIFO?
IF RECORD_ON THEN
pea 'rFOS'
move.l D0, -(sp)
bsr RecordEvent
addq.l #8, sp
tst.b D0
ENDIF
beq.s @3 ; any bytes? no:go see if we're done
cmp.b #1, D0 ; if 1, then get it out
beq.s @2
IfDebugStr '>1 byte left in the FIFO' ; otherwise, AACK!
moveq #dsIOCoreErr, D0
_SysError
@2 move.b rFIFO(a3), -1(A2_userBuffer) ; put data from FIFO into user's buffer
IF RECORD_ON THEN
pea '1byt'
move.l -4(A2_userBuffer), -(sp)
bsr RecordEvent
addq.l #8, sp
ENDIF
@3
tst.l D2_totalLeft
beq.s @good
;
; We still have more,
; Make sure that we're still in DataIn phase
;
@ckPhase
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.s @good
cmp.l #$0F, D2_totalLeft ; can we dma anymore?
bgt.w @loopTop ; yes - do another loop
@postDMAalign ; nope, poll the rest of the bytes
move.l D2_totalLeft, D3 ; setup alignment count
bra.w @alignLpBtm ; and go do the alignment
@disconnected ; save interrupt regs just like HAL_ISR does
move.l olderRegsRead(A5), oldestRegsRead(A5)
move.l oldRegsRead(A5), olderRegsRead(A5)
move.l newRegsRead(A5), oldRegsRead(A5)
move.l D5, newRegsRead(A5)
move.l D5, intRegsRead(A5)
@noTransfer
@good
@phaseErrAtStart
@exit
move.l D2_totalLeft, D1 ; d1 = # of bytes not transferred
move.b rCF3NormalVal(A5), rCF3(A3) ; return chip to non-t8 mode
rts ;
NAME 'DataIn_DMA'
ENDWITH ; SIM_IO
ENDWITH ; SIMprivFlagsRecord
ENDWITH
;--------------------------------------------------------------------------
;
DataOut_DMA PROC EXPORT ; <SM6> pdw - changed whole routine
;
; Register Usage:
;
; ———————— On Entry:———————————————————————————————————————————————
D2_totalLeft EQU D2
A2_userBuffer EQU A2
;
; ———————— On Exit:———————————————————————————————————————————————
D1_xferSize EQU D1 ; .l =
;
;
; ———————— Internal:———————————————————————————————————————————————
D3_alignOffset EQU D3 ; .l = offset required for alignment of buffers
A1_physicalBfr EQU A1
MACERevA2 EQU $0941
MACERevB0 EQU $0940
WITH HALc96GlobalRecord
WITH SIMprivFlagsRecord
WITH SIM_IO, SCSIPhase
IMPORT DataOut_DMA1x1, SlowWrite96 ;<SM6> pdw thru next
IF 1 AND RECORD_ON THEN
pea 'DMAo' ; EVENT =
move.l D2, -(sp) ; number of bytes
bsr RecordEvent
addq.l #8, sp
ENDIF
cmpi.b #kDataOutPhase, currentPhase(A5)
bne @phaseErr ; bra. on phase err
;
; If we are transferring either 1 or 0 bytes, either do a OneByteWrite or noTransfer
;
cmp.l #1, D2_totalLeft
bgt.s @doLots
bne @noTransfer
bsr OneByteWrite
moveq.l #0, D2_totalLeft
bra @good
@doLots
;
; 1st TIME THRU ----
;
; Adjust our DMA buffer addresses on 1st Time Thru to line up with the
; user's buffer start. That way BlockMove will be able to use MOVE16 for
; the body of the transfer. Or in the case of DMA the PSC will have a
; properly aligned buffer
;
move.l A2_userBuffer, D3 ; get user's buffer address
move.w dmaAlignMask+2(A5), D0 ; word-length alignment mask (00…11)
and.w D0, D3 ; get distance above alignment
beq.s @doneAligning ; if zero, skip alignment
eor.w D0, D3 ; number of bytes till alignment (1->E)
; doesn't require -1 for dbra
@align
;
; Make sure that we're in DataOut phase between each byte
;
move.b (a2)+, rFIFO(a3) ; put the byte
eieio
RecCmd $10, 'M',$05
move.b #cIOXfer, rCMD(a3) ; load non-DMA transfer cmd & begin 1 byte xfer
eieio
;
; WtForInt
;
@noTimeoutWait
clr.l d5 ;
move.b rSTA(a3), d5 ; read Status regr
bpl.s @noTimeoutWait ; ...loop until intrp req is detected
move.b D5, D0
and.b #iPhaseMsk, D0
move.b D0, currentPhase(A5)
swap d5 ;
move.b rFIFOflags(a3), d5 ;
lsl.w #8, d5 ;
move.b rINT(a3), d5 ;
move.l d5, d0 ; for checking for disconnect
swap d5 ; d5 = rFOS|rINT|0|rSTA
subq.l #1, D2_totalLeft
beq @good ; if xfer complete - done
cmpi.b #kDataOutPhase, currentPhase(A5)
bne @phaseErr ; bra. on phase err
@alignLpBtm
dbra D3, @align ; check to see if we are done aligning
@doneAligning
; load init config. value. Either leaving it at regular or bumping to Threshold-8 mode
move.b rCF3DMAVal(A5), rCF3(a3)
; remember which xfer type we are doing (ParameterBlock)
; setup buffers the old way if we aren't doing full DMA
; else setup buffers the new way
bclr #noBlockMove,dmaFlags(a5) ; assume that we aren't going to DMA directly to the user's buffer
cmp.l minDMAsize(a5),D1_xferSize ; !!! is this a small buffer? (Size should correspond with HALc96PSC.c!)
ble.s @decidedXferType ; yep, use DMA/Blockmove for small buffers
cmp.b #SCSIOldCall,scsiFunctionCode(a0) ; Check to see if it is an old call
beq.s @decidedXferType ; use the old DMA/Blockmove method for TIBs
move.l scsiFlags(a0),d0 ; get the flags field
andi.l #scsiDataPhys,d0 ; did the client pass in a physical address?
bne.s @directDMA ; yep, do straight DMA
btst #kbUseDblBuffer,SIMprivFlags(a0) ; Are we doing direct DMA for this guy?
bne.s @decidedXferType ; nope, use DMA/Blockmove for small buffers
@directDMA
bset #noBlockMove,dmaFlags(a5) ; remember that we are going directly to the user's buffer
@decidedXferType
;
; 2nd TIME THRU ---- (and beyond that)
;
; If it's not the 1st time thru, we don't have to do any front alignment stuff since
; it's already aligned.
;
@loopTop
move.l A2_userBuffer,logBuffer(a5) ; save the logical address for later
IF forPDMProto THEN
move.l A2_userBuffer, dbug0(A5)
ENDIF
btst #noBlockMove,dmaFlags(a5) ; directly to the user's buffer?
bne.s @newWay
IF forPDMProto THEN
move.l logicalCopyBuffer(A5), dbug0(A5)
ENDIF
move.l #BFR_SIZE, D1_xferSize ; assume we'll xfer a whole buffer
bra.w @setupCount
@newWay
move.l scsiFlags(a0),d0 ; get the flags field
andi.l #scsiDataPhys,d0 ; did the client pass in a physical address?
beq.s @checkAddress ; nope, get the physical address
move.l #$10000,D1_xferSize ; yep, get them dogies movin!
bra.s @setupCount
@doGetPhys
move.l A2_userBuffer,transLogStart(a0) ; load the address into the log to phys table
move.l D2_totalLeft,transLogCount(a0) ; load the count into the log to phys table
; trashing A0
lea transLogStart(a0),a0 ; pass in the log to phys table
move.l #1,a1 ; setup the count
_GetPhysical ; do the translation
move.l HALactionPB.ioPtr(A4),a0 ; get the ioPtr again (lost it for parameter)
tst.w D0 ; well?
beq.s @fixAddress
IfDebugStr 'GetPhysical failed in DMA out' ; Aack!
moveq #dsIOCoreErr, D0
_SysError
@fixAddress
move.l transLogStart(a0),d0 ; get the buffer
cmp.l d0,a2 ; check to see if the GetPhysical Bug bit us <LW3>
bne.s @checkAddress ; nope, sometimes GetPhysical doesn't update the
; logical.addr and logical.cnt fields properly
add.l transPhyCount(a0),d0 ; find the next logical Address range
move.l d0,transLogStart(a0) ; stuff it back into the Xlation table
move.l transPhyCount(a0),d0 ; get the physical count
sub.l d0,transLogCount(a0) ; and update the count <LW3>
@checkAddress
move.l transLogStart(a0),d0 ; get the buffer
sub.l transPhyCount(a0),d0 ; VM memory manager changed the parameters
cmp.l D0,A2_userBuffer ; Are we above the start of the physical area?
blo.s @doGetPhys ; nope, get the pysical address for this range
cmp.l transLogStart(a0),A2_userBuffer ; are we past the end?
bhs.s @doGetPhys ; yep, get the physical address for this range
move.l #$10000,D1_xferSize ; assume a 64K transfer
move.l transLogStart(a0),d0 ; get the buffer
sub.l A2_userBuffer,d0 ; find out how much we can transfer into this memory block
cmp.l d0,D1_xferSize ; use the smaller of 64K and the physical size
blt.s @gotXferSize ; must be greater than 64K
move.l d0,D1_xferSize ; use the space we just calculated
@gotXferSize
and.b #$F0,D1_xferSize ; line align this puppy!
move.l transLogStart(a0),d0 ; now calculate how far into the buffer we are
sub.l transPhyCount(a0),d0 ; VM memory manager changed the parameters
move.l A2_userBuffer,a1
sub.l d0,a1 ; and find the offset
move.l transPhyStart(a0),A2_userBuffer ; use the physical address
add.l a1,A2_userBuffer ; add the offset so we know where to DMA into
@setupCount
cmp.l D1_xferSize, D2_totalLeft ; Buffer bigger than amount left?
bhi.s @1 ; no - xfer size of buffer's worth
move.l D2_totalLeft, D1_xferSize ; yes - just xfer amount left
@1
cmp.b #dmaTypeAMIC, dmaType(A5) ; AMIC?
bne.s @notAMIC
move.l dmaAlignMask(A5), D0 ; get alignment (000…111)
not.l D0 ; 111…000
move.l D1_xferSize, D3 ; save xfer_size
and.l D0, D1_xferSize ; strip away %8 bytes, any left?
bne.s @notAMIC ; yes-> DMA the big chunks
; no: move remainder one at a time
move.l logBuffer(A5), A2_userBuffer ; restore the logical user buffer
bra.w @alignLpBtm
@notAMIC
btst #noBlockMove,dmaFlags(a5) ; are we doing real DMA?
bne.s @afterBM ; yep, no need to do a block move!
move.l D1_xferSize, -(sp) ; save D1 --
; trashing A0
move.l D1_xferSize, D0 ; D0 = input to BlockMove: size of move
move.l A2_userBuffer, A0 ; A0 = source: user's buffer
move.l logicalCopyBuffer(A5), A1 ; A1 = destination: DMA copy buffer
cmp.b #dmaTypeAMIC, dmaType(A5) ; AMIC?
bne.s @useBlockMove
_MoveBytesNoDCBZ
bra.s @bmDone
@useBlockMove
_BlockMoveData
@bmDone
move.l HALactionPB.ioPtr(A4), A0 ; get the ioPtr again (lost it for parameter)
move.l (sp)+, D1_xferSize ; restore D1 --
@afterBM
;
; Prime the c96's transfer count registers (2 regs - byte wide)
;
IF RECORD_ON THEN
pea 'rXC-'
move.l D1_xferSize, -(sp)
bsr RecordEvent
addq.l #8, sp
ENDIF
move.l D1_xferSize, d4 ; d4 <- d1
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
;
; Send DMA_transfer cmd to c96 to Start SCSI Transfer then adjust xferSize (for DMA)
; to indicate that only words (even #s of bytes) will be transferred from the c96
; via DMA. If the xferSize is odd, then the c96 will read the odd byte from the bus
; into its FIFO but it will not assert DREQ for that byte. It will generate an int
; as soon as the last full word is DMAed out of the c96 and there is a REQ on the
; bus for the next byte (or next phase).
;
RecCmd $90, 'M',$08
move.b #cDMAXfer, rCMD(a3) ; load DMA transfer cmd & begin xfers
eieio
;
; Call the StartDMA routine with parms (scsiDirectionOut, size, buffer)
;
IF forPDMProto THEN
btst #doRealDMAWrite, dmaFlags(A5)
bne.s @doRealDMA
movem.l A1/A2, -(sp)
move.l dbug0(A5), A2 ; get logical address of where DMA was supposed to be
move.l pdmaAddr(A5), A1 ; get pseudo-DMA address
@fakeDMAloop
bsr Ck4DREQ
beq.s @ck4int
move.w (A2)+, (A1)
move.w (A2)+, (A1)
move.w (A2)+, (A1)
move.w (A2)+, (A1)
bra.s @fakeDMAloop
@ck4int
bsr Ck4SCSIInt
beq.s @fakeDMAloop
movem.l (sp)+, A1/A2
bra.s @doneWithDMA
@doRealDMA
ENDIF
move.w #false, -(sp) ; false = out
move.l D1_xferSize, -(sp) ; size
btst #noBlockMove, dmaFlags(a5) ; are we doing real DMA?
beq.s @useCopyBuffer ; nope, use the copy buffer
move.l a2_userBuffer, -(sp) ; do DMA straight from the user buffer!
bra.s @sDMA ; start the DMA
@useCopyBuffer
move.l physicalCopyBuffer(A5), -(sp) ; buffer is our physical copy buffer
@sDMA
jsr ([jvStartDMA,A5]) ; returns A2_SetRegs, A1_ChanlControl
lea 10(sp), sp ; get parameters off stack
bsr.w Wt4SCSIInt ; Wait for intrp
jsr ([jvStopWriteDMA,A5])
@doneWithDMA
;
; Wait until c96 says it's done (either because it xferred all the bytes and it's
; ready again or because there was a change of phase (no longer in data_in phase)
; Once it's done, stop the DMA and see how far it got and BlockMove that many bytes
; into the user's buffer. The c96 does not interrupt us until AFTER it has DMA'd
; all of the DMAable bytes. This means that the PSC should be stable (except for a
; possible final flush that may be in progress). The c96 may have an extra byte left
; over in its FIFO however, and this can be checked for and retrieved directly from
; the c96.
;
moveq.l #0, D0 ; prepare for…
move.b rXCM(A3), D0 ; get remaining xfer count into D0
lsl.l #8, D0
move.b rXCL(A3), D0
IF RECORD_ON THEN
pea '-rXC'
move.l D0, -(sp)
bsr RecordEvent
addq.l #8, sp
ENDIF
;
; Adjust xferSize by amount not transferred by PSC and c96
;
move.l logBuffer(a5),A2_userBuffer ; restore the logical user buffer
sub.l D0, D1_xferSize ; we really didn't transfer them
moveq.l #mFIFOCount, D3 ; <SM12> pdw TOP
and.b int_rFIFOflags(A5), D3 ; if there are any bytes left in c96 FIFO,
beq.s @skipFlush
sub.l D3, D1_xferSize ; we really didn't transfer them and
RecCmd $01, 'M',$0c
move.b #cFlushFIFO, rCMD(A3) ; we should flush them out of there <SM15> pdw
eieio
@skipFlush ; <SM12> pdw BOT
;
; Displace buffer by amount transferred and also decrement totalLeft by that amount
;
add.l D1_xferSize, A2_userBuffer ; displace user's buffer by the amount xferred
sub.l D1_xferSize, D2_totalLeft ; sub done from need to do
beq.s @good
;
; We still have more,
; Make sure that we're still in DataIn phase
;
@ckPhase
cmpi.b #kDataOutPhase, currentPhase(A5)
bne.s @phaseErr ; bra. on phase err
cmp.l #$0F,D2_totalLeft ; can we dma anymore?
ble.s @postDMAalign ; nope, poll the rest of the bytes
bra.w @loopTop ; bra. if still in phase
@postDMAalign
move.l D2_totalLeft,d3 ; setup alignment count
bra.w @align ; and go do the alignment
@bytesXferd
@disconnected
@noTransfer
@good
@exit
@phaseErr
@badFWrite
move.l D2_totalLeft, D1 ; d1 = # of bytes not transferred
move.b rCF3NormalVal(A5), rCF3(A3) ; return chip to non-t8 mode
rts ;
ENDWITH
ENDWITH ; SIM_IO
ENDWITH ; SIMprivFlagsRecord
RTSNAME 'DataOut_DMA'
DataOut_DMA1x1 PROC EXPORT ; <SM6> pdw - changed whole routine
WITH HALc96GlobalRecord
IF 1 AND RECORD_ON THEN
pea '1x1o' ; EVENT =
move.l D2, -(sp) ; number of bytes
bsr RecordEvent
addq.l #8, sp
ENDIF
moveq.l #iPhaseMsk, d0 ; load mask for phase bits
and.b rSTA(a3), d0 ; are we in data-in phase?
;; cmpi.b #iDataOut, d0 ; data-out phase bits = 000
bne.s @phaseErr ; bra. on phase err
;
; If we are transferring either 1 or 0 bytes, either do a OneByteWrite or noTransfer
;
cmp.l #1, D2
bgt.s @doLots
bne.s @noTransfer
bsr OneByteWrite
moveq.l #0, D1 ; no more bytes to transfer <SM7> pdw
bra.s @good
@doLots
move.l d2, d3 ;
swap d3 ; d3.w:d2.w = total#bytes to xfer
cmp.w D0, D0 ; make sure that we're .eq.
bra.s @LpWriteBtm
@LpWriteTop
cmpi.b #iDataOut, D0 ; data-in phase bits = 001
bne.s @phaseErr ; bra. on phase err
move.b (a2)+, rFIFO(a3) ; put a byte
eieio
RecCmd $10, 'M',$0e
move.b #cIOXfer, rCMD(a3) ; load non-DMA transfer cmd & begin 1 byte xfer
eieio
; bsr.w Wt4SCSIInt ; Wait for intrp
@1
move.b rSTA(a3), D0 ; int?
bpl.s @1
and.b #iPhaseMsk, d0 ; and mask for phase bits
move.b D0, currentPhase(A5)
btst.b #bDisconnected, rINT(a3)
@LpWriteBtm
dbne d2, @LpWriteTop ; d2 = low word of count
dbne d3, @LpWriteTop ; d3 = high word of count
swap d3
move.w d2, d3
moveq.l #1, d1
add.l d3, d1
@disconnected
@noTransfer
@good ; d1 = # of bytes transferred
rts ;
@phaseErr
@bytesXferd ;
swap d3 ; calculate bytes left to transfer
move.w d2, d3 ; get low order word
move.l d3, d1
@badFWrite
rts ;
NAME 'DataOut_DMA1x1'
ENDWITH
ENDP
END