mac-rom/OS/SCSIMgr4pt3/HALc96DMA.a
Elliot Nunn 4325cdcc78 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 09:52:23 +08:00

1149 lines
39 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