;____________________________________________________________________________________ ; ; 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): ; ; 11/22/93 pdw Rolling in from . ; 10/29/93 pdw Removed _debugger statements. ; 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. ; 10/29/93 pdw Got rid of _Debugger statements that screwed some things. ; 10/29/93 DCB roll-in. ; 10/28/93 pdw Fixed some DMA in and out residual length bugs. ; 10/15/93 pdw Getting rid of bogus forPDMProto. ; 10/14/93 pdw roll-in. ; 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. ; 10/12/93 pdw Added support for Synchronous data transfers, rewrote State ; Machine, message handling etc. ; 10/6/93 pdw Added forPDMProto stuff around checks for doRealDMAxxx. ; 9/12/93 pdw Changed the size of the double-buffering buffer. ; 9/9/93 pdw Lots of little changes. Name changes, temporary cache_bug ; stuff. ; 8/13/93 pdw RecordCmd and eieio stuff. ; 7/19/93 pdw Got rid of a beq.s to the next instruction to get rid of build ; warning. ; 7/17/93 pdw Lots of little things. Some major things. ; 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. ; 5/26/93 PW Moving the restore of the CF3 register till after the loop in ; DataIn_DMA and DataOut_DMA. ; 5/25/93 DCB Rollin from Ludwig. (The next item below) ; 5/21/93 PW Changing some DebugStrs to IfDebugStrs+Syserr. ; 5/20/93 DCB Changing _debuggers to DebugStrs so we can turn them off at ; compile time. ; 5/5/93 PW Converted names to meanies-friendly names. Updated with latest ; from Ludwig stuff. ; 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. ; 3/31/93 DCB Changing the bhi's I put in last night to bhs's which is what ; they should have been. ; 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. ; 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) ; 3/4/93 PW Made GPHYSICAL optional through -d. ; 3/1/93 DCB Added conditional compile for GetPhysical call. This aids ; performance testing. ; 2/17/93 PW Removed some old Terror change tags. ; 1/31/93 PW Update from the latest of Ludwig. Also changes required for PDM ; (will update Ludwig with these as needed myself). ; 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. ; 1/27/93 PW Got rid of some bad comments. ; 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. ; 12/5/92 PW Changed field names. Added phase check at beginning of ; DataInPSC_DMA. Cleaned up alignment setup. ; 11/20/92 DCB Fixed minor problem where DataOutPSC might inadvertantly return ; an error (nobody was paying attention to it but still...) ; 11/12/92 PW Added missing # sign causing failed Writes if BetterBusError ; installed. ; 10/30/92 DCB Added support for direct DMA (!!!) ; 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. ; 10/8/92 PW Changed BFR_SIZE to 512 until I get a real fix for premature ; phase change on write bug. ; 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. ; 10/8/92 PW Lots of trivial name changes. Added GrossError checks. (cb) ; Fixed bug with .w vs .l direction parameter to StartDMA. ; 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. ; 8/31/92 PW Changed register and command definitions to reflect changes to ; SCSIEqu53c96. ; 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. ; 8/30/92 PW Added DMA Write stuff. Currently disabled unless 'sw c0c^+200 ; 0000' is issued. ; 8/1/92 PW Fixed write bug for 1,511 TIBs. Greatly increased speed of ; alignment process. Particularly critical in 1,511 TIBs. ; 7/29/92 PW Changed test for 1 byte transfer so that it uses OneByteRead ; instead of attempting to use DMA. ; 7/28/92 PW Adding slight StopDMA changes. ; 7/27/92 PW Got DMA working. ; 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<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< 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 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 @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 ; 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 ; 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 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 @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 ; 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 pdw eieio @skipFlush ; 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 ; 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 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