mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2025-01-01 11:29:27 +00:00
0ba83392d4
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.
1540 lines
57 KiB
Plaintext
1540 lines
57 KiB
Plaintext
;
|
|
; File: SCSIMgrHW.a
|
|
;
|
|
; Copyright: © 1989-1993 by Apple Computer, Inc. All rights reserved.
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <SM10> 2/5/93 CSS Rollin from Horror: clear flag indicating drive is now spun down.
|
|
; <SM9> 11/3/92 SWC Changed SCSIEqu.a->SCSI.a.
|
|
; <SM8> 9/9/92 CSS Changed three bra.w's to bra.l's when modular makes
|
|
; caused the files to get to far apart.
|
|
; <SM7> 6/20/92 PN Roll in patchIIciROM.a. New and improved bus errorhandler
|
|
; <SM6> 6/2/92 kc Revert to <SM4>
|
|
; <SM5> 6/1/92 CS Roll more cyclone stuff in.
|
|
; <SM4> 5/17/92 kc Include PowerPrivEqu.a. The powerCntl equ was moved inside the
|
|
; pmCommandRec record.
|
|
; <SM3> 5/4/92 FM Undo last change and let Kurt deal with it :^)
|
|
; <SM2> 5/4/92 FM Roll in changes from PatchIICiRom.a file that were "lost" in the
|
|
; review processÉ Making the SCSIBusErrHandler a little more
|
|
; robust.
|
|
; <13> 12/27/91 RB Rolled in Terror changes. Cleaned up a bit.
|
|
; <12> 10/2/91 JSM DonÕt use any onXXMac conditionals at all, donÕt use forROM,
|
|
; isUniversal, or hasPwrControls since they are always true for
|
|
; this file, donÕt check for Cpu ³ 020 since it always is.
|
|
; <11> 9/16/91 JSM Cleanup header.
|
|
; <10> 9/21/90 BG Removed <8>, <9>. 040s are behaving more reliably now.
|
|
; <9> 7/19/90 BG Found another place or two where EclipseNOPs were needed.
|
|
; <8> 6/22/90 CCH Added a NOP for flaky 68040's.
|
|
; <7> 5/16/90 MSH Added hasPowerControls to hcmac conditional.
|
|
; <6> 2/20/90 BG Added (simplistic) 040 bus error handling. Updated *Transfer* to
|
|
; decide which MaxBusErr value to use (000/020/030 vs. 040).
|
|
; <5> 1/18/90 JWK Needed for Zone5 - Fixes to old SCSI Mgr DMA routines for
|
|
; SCSIStat support and variable blind timeouts.
|
|
; <4> 1/14/90 JWK Needed for F19 - Added calls to LockMemory and UnlockMemory for
|
|
; VM support.
|
|
; <3> 1/11/90 CCH Added include of ÒHardwarePrivateEqu.aÓ.
|
|
; <2> 1/8/90 JK Needed for F19: Fixed CD-ROM bug in slow read routines - better
|
|
; loss of phase error checking.
|
|
; <1.7> 12/10/89 jwk NEEDED FOR F19 - Optimized DMA code
|
|
; <1.6> 11/2/89 MSH Rolled in updated sleeptask from hcmac reality sources.
|
|
; <1.5> 9/11/89 jwk NEEDED FOR F19 - Cleaned up DMA code, added support for SE and
|
|
; Plus
|
|
; <1.4> 7/17/89 jwk NEEDED FOR AURORA - Changed to use "padForOverPatch" conditional
|
|
; <1.3> 7/15/89 jwk NEEDED FOR AURORA - Code review changes, F19 routines
|
|
; <1.2> 6/29/89 jwk NEEDED FOR AURORA - Fixed Tape Drive and Slow Write timing bugs
|
|
; <1.1> 6/19/89 SWC Replaced bit constants for equated versions for OSS interrupt
|
|
; register tests.
|
|
; <1.0> 6/13/89 jwk Reorganizing SCSI sources to be Universal-ROM friendly
|
|
;
|
|
; Old comments are saved for historical purposes in the SCSIMgrOld.a file. <v1.3>
|
|
;
|
|
;-----------------------------------------------------------
|
|
|
|
BLANKS ON
|
|
STRING ASIS
|
|
PRINT OFF
|
|
|
|
IF (&TYPE('dbSymbols') = 'UNDEFINED') THEN
|
|
dbSymbols EQU 0 ; for debugging purposes <v1.5>
|
|
ENDIF
|
|
|
|
IF (&TYPE('phaseLogging') = 'UNDEFINED') THEN
|
|
phaseLogging EQU 0
|
|
ENDIF
|
|
|
|
LOAD 'StandardEqu.d'
|
|
INCLUDE 'HardwarePrivateEqu.a'
|
|
INCLUDE 'UniversalEqu.a' ; <13> rb
|
|
INCLUDE 'PowerPrivEqu.a'
|
|
INCLUDE 'SCSI.a'
|
|
INCLUDE 'SCSIPriv.a'
|
|
INCLUDE 'SCSIEqu96.a' ; <13> rb
|
|
|
|
PRINT ON
|
|
|
|
SCSIHW PROC EXPORT
|
|
|
|
EXPORT Arb, Sel, Delay22, SCSIIntHnd
|
|
EXPORT Transfer, SlowComp, FastComp
|
|
EXPORT SlowRead, SlowWrite, FastRead, FastWrite
|
|
EXPORT VFastReadOSS, VFastRead, VFastWriteOSS, VFastWrite
|
|
EXPORT PFastReadOSS, PFastRead, PFastWriteOSS, PFastWrite
|
|
EXPORT FastReadOSS, FastWriteOSS ;<v1.3>
|
|
EXPORT BusErrHandler ;<v1.5>
|
|
|
|
EXPORT ClearIRQ
|
|
EXPORT DisEnable
|
|
|
|
IMPORT ClearSCSIInt, EnableSCSIInts, DisableSCSIInts
|
|
|
|
WITH scsiPB,scsiPrivPB, scsiGlobalRecord, dcInstr, PmgrRec
|
|
|
|
EXPORT SleepTask, DoSpinDown
|
|
|
|
;-------------------------------------------------------------------
|
|
;
|
|
; DoSpinDown - Turn hard drive power off. <v1.1><v1.2>
|
|
;
|
|
; It is assumed that the drive power is on - the power is cut unconditionally.
|
|
;
|
|
with pmCommandRec
|
|
DoSpinDown
|
|
link a6,#-14 ; storage for Power Mgr param blk <v1.1>
|
|
@cutPower
|
|
lea.l -14(a6),a0 ; point to the parameter block <v1.1>
|
|
move.w #powerCntl,pmCommand(a0) ; power off the SCSI +5V and +12V <v1.1>
|
|
move.w #1,pmLength(a0) ; one byte of data in buffer <v1.1>
|
|
move.w #hdOff*256,-2(a6) ; hdOff command <v1.1>
|
|
pea.l -2(a6) ; address of send buffer <v1.1>
|
|
move.l (sp)+,pmSBuffer(a0) ; save the send buffer pointer <v1.1>
|
|
move.l zeroReg,pmRBuffer(a0) ; no data to be received <v1.1>
|
|
_PmgrOp ; send it off to the Power Mgr <v1.1>
|
|
tst.w d0 ; was there an error ? <v1.1>
|
|
bne.s @exit ; if there was, don't freeze the timer <v1.8>
|
|
@success
|
|
MOVE.L PmgrBase,A0 ; get pointer to pmgr locals <v2.1>
|
|
clr.l LastHd(A0) ; stop calling - disk is spun down <v1.8>
|
|
bclr.b #HDPowerOn,PmgrFlags(a0) ; clear flag indicating drive is now spun down <t11> djw <SM10> CSS
|
|
@exit
|
|
unlk a6 ; release local storage <v1.1>
|
|
rts ; <v1.1>
|
|
endwith
|
|
;-------------------------------------------------------------------
|
|
;
|
|
; SleepTask - Turn hard drive power off, if necessary <v1.2>
|
|
;
|
|
; If the Sleep timer goes off, or the user decides to go immediately to sleep, this task
|
|
; is called. If no SCSI I/O is in progress, the power is turned off to the drive. <v1.2>
|
|
;
|
|
SleepTask
|
|
MOVE.L A1,-(SP) ; save A1
|
|
cmpi.w #SleepDemand,d0 ; is it a sleep demand ? <v1.4>
|
|
beq.s @spin ; if so, do it <v1.4>
|
|
cmpi.w #SleepNow,d0 ; is it a sleep now ? <v1.4><v1.6>
|
|
beq.s @spin ; if so, do it <v1.4>
|
|
cmpi.w #SleepRequest,d0 ; is it a sleep request ? <v1.4>
|
|
bne.s @exit ; if not, ignore it <v1.4>
|
|
|
|
move.l SCSIGlobals,a1 ; point to SCSI Mgr globals <v1.4>
|
|
tst.l scsiQHead(a1) ; is the new SCSI Mgr busy ? <v1.2>
|
|
bne.s @err ; we're busy, so we won't sleep <v1.4><v1.3>
|
|
@spin
|
|
MOVE.L PmgrBase,A1 ; use pointer to spin down routine <v1.5>
|
|
MOVE.L HDvector(A1),A1
|
|
JSR (A1)
|
|
@exit
|
|
clr.l d0 ; <v1.3>
|
|
@err
|
|
move.l (sp)+,a1 ; restore a1 <v1.2><v1.3>
|
|
rts ; we're out of here <v1.4>
|
|
|
|
;
|
|
; ClearIRQ
|
|
;
|
|
; Function: Clears the SCSI IRQ bit and the VIA interrupt flag, if necessary.
|
|
;
|
|
ClearIRQ:
|
|
move.b sRESET(a3),d1 ; clear the IRQ interrupt bit
|
|
bra.l ClearSCSIInt ; <SM8> call InterruptHandlers.a to clear SCSI intr latch (if any) <v1.3>
|
|
|
|
;
|
|
; DisEnable
|
|
;
|
|
; Called by: SCSIDT, ArbSel, SCSIIntHnd, MsgIn
|
|
;
|
|
; Calls: _PrimeTime (on the Mac Plus)
|
|
;
|
|
; On entry: d0 - If zero, disable SCSI interrupts, else enable SCSI interrupts
|
|
;
|
|
; Function: Enable/disable the CPU's SCSI interrupt.
|
|
;
|
|
DisEnable:
|
|
tst.l d0 ; disable or enable ?
|
|
beq.s @disable ; if zero, disable interrupts
|
|
@enable
|
|
bra.l EnableSCSIInts ; <SM8> use routine in InterruptHandlers.a <v1.3>
|
|
@disable
|
|
bra.l DisableSCSIInts ; <SM8> use routine in InterruptHandlers.a <v1.3>
|
|
|
|
;
|
|
; BusErrHandler -- Hardware-handshaking bus error handler
|
|
;
|
|
|
|
aeXFrameType EQU $07 ; Access Error Exception Stack Frame type value <6>
|
|
aeXFrameSize EQU 30*2 ; size (words) of 040 AE Exception Frame <6>
|
|
shortBEXFrameType EQU $0A ; Short bus cycle fault stack frame (020/030) <6>
|
|
|
|
BusErrHandler:
|
|
move.l d0,-(sp) ; save d0 <PN>
|
|
moveq.l #$ffffff9c,d0 ; mask = $ffffff9c <Start> <PN>
|
|
and.l 4+$10(sp),d0 ; clear variable bits of the fault address <PN>
|
|
cmp.l SCSIHsk,d0 ; was it a SCSI chip access ? <PN>
|
|
beq.s @start ; if so, start processing the bus error <PN>
|
|
|
|
movem.l d1/a4,-(sp) ; save what we will trash <PN>
|
|
move.l SCSIGlobals,a4 ; load our globals reference <PN>
|
|
move.l SCSI2Base,d1 ; is the external SCSI chip base address valid? <PN>
|
|
beq.s @NoSCSIBus2 ; -> no, cleanup and bail <PN>
|
|
|
|
cmp.l hhsk5380_2(a4),d0 ; was it an external SCSI chip access ? <PN>
|
|
beq.s @Is2ndSCSI ; if so, start processing the bus error <PN>
|
|
|
|
@NoSCSIBus2 ; <PN>
|
|
movem.l (sp)+,d1/a4 ; restore d1/a4 <PN>
|
|
|
|
|
|
move.l (sp)+,d0 ; restore d0
|
|
move.l OldBusErrVct(a6),-(sp) ; put old bus error handler addr on stack <end>
|
|
rts ; jump to old handler, assuming it'll RTE <PN>
|
|
|
|
@Is2ndSCSI ; <PN>
|
|
movem.l (sp)+,d1/a4 ; restore d1/a4 <PN>
|
|
|
|
@start
|
|
|
|
subq.w #1,BusErrCount(a6) ; retry until we get tired
|
|
beq.s CleanUp
|
|
|
|
move.w TimeDBRA,d0 ; DBRA's per millisecond <v1.3>
|
|
lsr.w #4,d0 ; 1ms/16 = approximately 62us (was 50us) <v1.3>
|
|
@ErrWait dbra d0,@ErrWait ; recovery time (was 50us) <C865/PB208> <v1.3>
|
|
move.l (sp)+,d0 ; restore d0 <v2.0>
|
|
rte ; haven't reached max, so restart
|
|
Cleanup
|
|
addq.l #4,sp ; throw away copy of d0 on stack <29>
|
|
|
|
move.w (sp),d0 ; get sr from stack
|
|
bfextu 6(sp){0:4},d1 ; get format code from stack
|
|
cmp.b #aeXFrameType,d1 ; check for 040 Access Error Exception Frame <6>
|
|
beq.s Drop040XFrame ; dispose of 040 AE except. frame <6>
|
|
cmp.b #shortBEXFrameType,d1 ; short 020/030 exception frame?
|
|
bne.s Drop46w ; no, so use larger frame
|
|
adda.w #16*2,sp ; dispose of the 16-word frame
|
|
bra.s DummyFrame ; and finish up
|
|
|
|
Drop040XFrame ; 040 Bus Error frame-cleaning done here <6>
|
|
add.w #aeXFrameSize,sp ; remove 040 Access Error Exception Frame <6>
|
|
bra.s DummyFrame ; and create dummy return frame <6>
|
|
Drop46w
|
|
add.w #46*2,sp ; size of exception frame
|
|
DummyFrame
|
|
;
|
|
; Fake a format code 0 exception frame (4 words) to finish cleaning up
|
|
;
|
|
move.w zeroReg,-(sp) ; format code 0
|
|
pea FinishErr ; PC value
|
|
move.w d0,-(sp) ; sr value
|
|
rte ; 'return' from the fake exception
|
|
|
|
FinishErr
|
|
moveq.l #scBusTOErr, d0 ; assume slow peripheral <13> rb
|
|
TestFor SCSI96_1Exists ; use macro to check if we have <13> rb
|
|
beq.s @noSCSI96 ; a SCSI96 chip. Bra. if not <13> rb
|
|
|
|
;In c96 DMA mode, if the phase changes while transfer count > 0
|
|
; during transfer info then c96 generates a bus service interrupt.
|
|
|
|
rts ; But return SCSI Bus TimeOut for now <13> rb
|
|
@noSCSI96
|
|
btst.b #bPM, (a1) ; phase change? <13> rb
|
|
bne.s @ErrorDone ; phase matches: slow peripheral <13> rb
|
|
moveq.l #scPhaseErr, d0 ; return phase change error <13> rb
|
|
@ErrorDone
|
|
rts
|
|
|
|
|
|
;
|
|
; Arb
|
|
;
|
|
; Called by: SCSIDT
|
|
;
|
|
; Calls: Delay22.
|
|
;
|
|
; On entry: a3 - ptr to SCSI read base address
|
|
; a4 - ptr to SCSI Mgr globals
|
|
;
|
|
; On exit: d0 - result code
|
|
;
|
|
; Function: Arbitrates for the SCSI bus.
|
|
;
|
|
Arb:
|
|
movem.l a0-a3/d1-d2,-(sp) ; save registers
|
|
move.w #scsiArbitrated,scsiResult(a5) ; we're arbitrating
|
|
moveq.l #noErr,d0 ; assume success
|
|
|
|
btst.b #sOSSExists,G_Reserved0+3(a4) ; see if we really have an OSS
|
|
beq.s @notGDUOSS ; if not, skip it
|
|
btst.b #sSCSIDMAExists,G_Reserved0+3(a4) ; see if we have a SCSI DMA chip
|
|
beq.s @notGDUOSS ; not the correct combination
|
|
|
|
move.l #$FFFFFFFF,sDTIME(a3) ; set timer to an outlandish value <v1.3><v1.5>
|
|
move.l G_Reserved2(a4),sDCTRL(a3) ; enable auto-arbitration <v1.2>
|
|
move.b #iARB,sMR+WrOffs(a3) ; attempt arbitration
|
|
|
|
move.w TimeDBRA,d2 ; DBRA's per millisecond <v1.3>
|
|
lsr.w #6,d2 ; 1ms/64 = 15us <v1.3>
|
|
@delay
|
|
dbra.w d2,@delay ; delay at least 15us <v1.3>
|
|
|
|
move.l sDCTRL(a3),d1 ; get an image of the DMA control register
|
|
btst.l #bWONARB,d1 ; did we win ?
|
|
beq.s @busyGDU ; we didn't win
|
|
move.b sRESET(a3),d1 ; clear the interrupt <v1.3>
|
|
move.l #iINTREN,sDCTRL(a3) ; enable chip interrupts <v1.3>
|
|
bra.s @exit ; we won <v1.3>
|
|
@busyGDU
|
|
move.l #iINTREN,sDCTRL(a3) ; turn off auto-arbitration <v1.3>
|
|
bra.s @busy ; continue <v1.3>
|
|
@notGDUOSS
|
|
move.b G_ID(a4),sODR+WrOffs(a3) ; put CPU's ID out on the bus
|
|
move.b #iARB,sMR+WrOffs(a3) ; attempt arbitration
|
|
btst.b #bAIP,sICR(a3) ; check for AIP (Arbitration In Progress) bit
|
|
beq.s @busy ; the bus is busy
|
|
@delay22:
|
|
movea.l jvDelay22(a4),a0 ; delay at least 2.2us
|
|
jsr (a0) ; go to the routine
|
|
@checkWin:
|
|
btst.b #bLA,sICR(a3) ; did we lose arbitration ?
|
|
bne.s @busy ; if we lost, get out
|
|
move.b G_ID(a4),d2 ; CPU's SCSI ID <v1.3>
|
|
move.b sCDR(a3),d1 ; check ID's on the bus
|
|
eor.b d2,d1 ; take out our ID <v1.3>
|
|
cmp.b d2,d1 ; is there a higher ID on the bus ?
|
|
bhi.s @busy ; if so, we lost, so get out
|
|
btst.b #bLA,sICR(a3) ; check again - did we lose arbitration ?
|
|
beq.s @exit ; we won - d0 is set correctly
|
|
@busy:
|
|
bset.b #waitingForIRQ,state1(a4) ; we're waiting for a Loss of BSY interrupt <v1.5>
|
|
move.b zeroReg,sMR+WrOffs(a3) ; turn off arbitration
|
|
move.b #iMBSY,sMR+WrOffs(a3) ; set up for "Loss of BSY" interrupt
|
|
moveq.l #scArbNBErr,d0 ; we didn't win
|
|
@exit:
|
|
movem.l (sp)+,a0-a3/d1-d2 ; restore registers (d0 is not restored)
|
|
tst.l d0 ; set condition codes
|
|
rts
|
|
;
|
|
; Sel
|
|
;
|
|
; Called by: SCSIDT
|
|
;
|
|
; Calls: Delay22.
|
|
;
|
|
; On entry: a3 - ptr to SCSI read base address
|
|
; a4 - ptr to SCSI Mgr globals
|
|
; a5 - ptr to SCSI request parameter block
|
|
;
|
|
; On exit: d0 - result code
|
|
;
|
|
; Function: Selects a device.
|
|
;
|
|
Sel:
|
|
movem.l scsiRegs,-(sp) ; save registers
|
|
move.w #scsiSelection,scsiResult(a5) ; we're attempting to select
|
|
move.b #iSEL+iBSY,sICR+WrOffs(a3) ; assert SEL as well as BSY since we won
|
|
move.b zeroReg,sMR+WrOffs(a3) ; turn off arbitration
|
|
movea.l jvDelay22(a4),a0 ; delay at least 1.2us (800+400ns) <v1.3>
|
|
jsr (a0) ; go to the routine
|
|
@select: ; start selection
|
|
move.b zeroReg,sTCR+WrOffs(a3) ; clear *I/O bit to become initiator
|
|
moveq.l #1,d1 ; build the select mask...
|
|
move.b scsiReqID(a5),d2 ; Target ID
|
|
lsl.b d2,d1 ; shift the bit to the appropriate spot
|
|
or.b G_ID(a4),d1 ; merge CPU ID with Target ID
|
|
move.b d1,sODR+WrOffs(a3) ; set select mask (ready to be output onto bus)
|
|
move.b #iBSY+iSEL+iDB+iATN,sICR+WrOffs(a3) ; assert *SEL,*DB, and *ATN
|
|
move.b zeroReg,sSER+WrOffs(a3) ; disable reselection interrupts
|
|
move.l zeroReg,d0 ; disable all SCSI interrupts
|
|
move.l jvDisEnable(a4),a0 ; addr of intr enable/disable routine
|
|
jsr (a0) ; disable SCSI interrupts
|
|
move.b #iSEL+iDB+iATN,sICR+WrOffs(a3) ; *SEL, *DB, and *ATN - no *BSY
|
|
movea.l jvDelay22(a4),a0 ; bus settle delay (400ns)
|
|
jsr (a0) ; go to the routine
|
|
@timeout
|
|
move.w scsiSelTO(a5),d2 ; selection timeout in milliseconds
|
|
mulu.w TimeSCSIDB,d2 ; get number of DBRA's
|
|
move.l d2,d0 ; set up d0 as high word
|
|
swap d0
|
|
@wfbsy
|
|
btst.b #bBSY,sCSR(a3) ; test for *BSY
|
|
dbne.w d2,@wfbsy ; loop until *BSY or timeout
|
|
dbne.w d0,@wfbsy
|
|
bne.s @noerror ; *BSY is asserted
|
|
@nobsy
|
|
move.b #iSEL,sICR+WrOffs(a3) ; de-assert data bus
|
|
move.w TimeSCSIDB,d2 ; delay 200us
|
|
lsr.w #2,d2 ; 1ms/4 = 250us (greater than 200us)
|
|
@delay
|
|
dbra.w d2,@delay ; wait 250us
|
|
btst.b #bBSY,sCSR(a3) ; *BSY asserted ?
|
|
bne.s @noerror ; if asserted, we're done
|
|
@error
|
|
move.w #scsiSelectTOErr,d0 ; selection timed out <v1.3>
|
|
move.b zeroReg,sICR+WrOffs(a3) ; release the lines
|
|
bra.s @exit ; done
|
|
@noerror
|
|
movea.l jvDelay22(a4),a0 ; bus settle delay (400ns)
|
|
jsr (a0) ; go to the routine
|
|
move.b #iATN,sICR+WrOffs(a3) ; assert *ATN only
|
|
moveq.l #noErr,d0 ; indicate success
|
|
@exit:
|
|
movem.l (sp)+,scsiRegs ; restore registers (d0 is not restored)
|
|
tst.l d0 ; set condition codes
|
|
rts
|
|
|
|
;
|
|
; Delay22();
|
|
;
|
|
; Called by: Arb, Sel
|
|
;
|
|
; Calls: None.
|
|
;
|
|
; Function: Provides a delay of at least 2.2us, using three VIA accesses <v1.3>
|
|
; as a time constant. <v1.3>
|
|
;
|
|
Delay22:
|
|
movea.l VIA,a0 ; point to the VIA <v1.3>
|
|
tst.b vIFR(a0) ; access the VIA <v1.3>
|
|
tst.b vIFR(a0)
|
|
tst.b vIFR(a0)
|
|
rts
|
|
;
|
|
; SCSIIntHnd
|
|
;
|
|
; Called by: Macintosh Interrupt Dispatcher
|
|
;
|
|
; Calls: Command, Status, Data, MsgIn, MsgOut
|
|
;
|
|
; Function:
|
|
; This is the SCSI interrupt handler. (On the Mac Plus, a second timer is used to
|
|
; monitor the SCSI chip's interrupt line.)
|
|
;
|
|
|
|
SCSIIntHnd:
|
|
IF dbSymbols THEN
|
|
link a6,#0 ; no globals, no locals
|
|
nop
|
|
ENDIF
|
|
movem.l intrRegs,-(sp) ; save registers
|
|
movea.l SCSIGlobals,a4 ; get pointer to SCSI Manager globals <v1.7>
|
|
SCSIIntHndCommon
|
|
movea.l activeReq(a4),a5 ; point to the active request, if any <v1.3>
|
|
moveq.l #0,zeroReg ; clear out the zero register
|
|
move.l zeroReg,d0 ; disable all SCSI interrupts
|
|
move.l jvDisEnable(a4),a0 ; addr of intr enable/disable routine
|
|
jsr (a0) ; disable SCSI interrupts
|
|
move.l base5380(a4),a3 ; SCSI read base address
|
|
|
|
move.w sr,-(sp) ; save current status register contents
|
|
ori.w #HiIntMask,sr ; no interrupts during Reselection handshake
|
|
move.b sBSR(a3),d0 ; fetch Bus and Status Register
|
|
btst.l #bIREQ,d0 ; is the IRQ bit set ?
|
|
beq.w @Spurious ; if not, check for spurious interrupt
|
|
lsl.w #8,d0 ; put BSR in upper byte
|
|
move.b sCSR(a3),d0 ; put Current SCSI Bus Status in lower byte
|
|
|
|
move.w d0,d1 ; get a copy of BSR and CSR registers
|
|
andi.w #mReselect,d1 ; clear don't care bits
|
|
cmpi.w #iReselect,d1 ; is it a Reselection interrupt ?
|
|
bne.s @chkEOP ; if not, check for an End Of Process interrupt
|
|
@Reselect
|
|
;
|
|
; Since we're not a system with Parity implemented, we can't check for parity
|
|
; on Reselection. It's not worth the trouble to keep track of the ID's that implement
|
|
; Parity.
|
|
;
|
|
; btst.l #bDBP,d0 ; is /DBP asserted ?
|
|
; beq.w @Spurious ; if not, bad parity, don't respond
|
|
move.b sCDR(a3),d1 ; get ID of reselecting device
|
|
beq.w @Spurious ; no ID bits set
|
|
move.b G_ID(a4),d0 ; get our ID bit
|
|
eor.b d0,d1 ; remove our ID bit
|
|
beq.w @Spurious ; only our ID bit was set, invalid reselection
|
|
move.b zeroReg,sSER+WrOffs(a3) ; disable reselection interrupts
|
|
move.b sICR(a3),d0 ; assert *BSY
|
|
ori.b #iBSY,d0
|
|
move.b d0,sICR+WrOffs(a3)
|
|
moveq.l #scResel,d0 ; send a reselect message to dTask (ID mask in d1.b)
|
|
bra.s @ClearIRQ ; clear interrupt and leave
|
|
@chkEOP
|
|
move.w d0,d1 ; get a copy of BSR and CSR registers
|
|
andi.w #mEOP,d1 ; clear don't care bits
|
|
cmpi.w #iEOP,d1 ; is it an End Of Process interrupt ?
|
|
bne.s @PhaseMm ; if not, check for Phase Mismatch
|
|
@EOPIntr
|
|
move.b zeroReg,sMR(a3) ; clear EOP interrupt (and turn off pDMA)
|
|
move.b zeroReg,sICR(a3) ; deassert the data bus (for writes) <v1.3>
|
|
moveq.l #scEOP,d0 ; send an EOP message to dTask
|
|
bra.s @ClearIRQ ; clear interrupt and leave
|
|
@PhaseMm
|
|
move.w d0,d1 ; get a copy of BSR and CSR registers
|
|
andi.w #mPhaseMm,d1 ; clear don't care bits
|
|
cmpi.w #iPhaseMm,d1 ; is it a Phase Mismatch interrupt ?
|
|
bne.s @LossBsy ; if not, check for Loss of BSY <v1.3>
|
|
moveq.l #scPhaseMm,d0 ; send a Phase Mismatch message to dTask
|
|
bra.s @ClearIRQ ; clear interrupt and leave
|
|
@LossBsy
|
|
move.w d0,d1 ; get a copy of BSR and CSR registers
|
|
andi.w #mLossBsy,d1 ; clear don't care bits
|
|
cmpi.w #iLossBsy,d1 ; is it a Loss of BSY interrupt ?
|
|
bne.s @BusReset ; if not, check for SCSI Bus Reset
|
|
moveq.l #scLossBsy,d0 ; send a Loss of BSY message to dTask
|
|
bra.s @ClearIRQ ; clear interrupt and leave
|
|
@BusReset
|
|
move.w d0,d1 ; get a copy of BSR and CSR registers
|
|
andi.w #mBusReset,d1 ; clear don't care bits
|
|
cmpi.w #iBusReset,d1 ; is it a SCSI Bus Reset interrupt ?
|
|
bne.s @Parity ; if not, check for a Parity interrupt
|
|
; moveq.l #scBusReset,d0 ; send a Bus Reset message to dTask <v1.3>
|
|
bra.s @Spurious ; clear IRQ, leave as if nothing happened <v1.3>
|
|
@Parity
|
|
move.w d0,d1 ; get a copy of BSR and CSR registers
|
|
andi.w #mParity,d1 ; clear don't care bits
|
|
cmpi.w #iParity,d1 ; is it a Parity Error interrupt ?
|
|
bne.s @Spurious ; if not, spurious interrupt <v1.5>
|
|
moveq.l #scParity,d0 ; send a Parity error message to dTask
|
|
; fall through to clear interrupt request and leave <v1.3>
|
|
@ClearIRQ
|
|
movea.l jvMessage(a4),a0 ; addr of messaging routine <v1.3>
|
|
jsr (a0) ; send the message to the deferred task <v1.3>
|
|
movea.l jvClearIRQ(a4),a0 ; addr of ClearIRQ routine
|
|
jsr (a0) ; clear the SCSI interrupt (and VIA, if necessary)
|
|
move.w (sp)+,sr ; restore the sr
|
|
IF phaseLogging THEN
|
|
nop ; good place to jump to phase-logging routine
|
|
ENDIF
|
|
bra.s @inthndRTS ; we're done
|
|
@Spurious
|
|
move.w (sp)+,sr ; restore the sr
|
|
|
|
btst.b #sSCSIDMAExists,G_Reserved0+3(a4) ; do we have a SCSI DMA chip ? <v1.3>
|
|
beq.s @DMAdone ; if not, skip <v1.3>
|
|
|
|
move.l sDCTRL(a3),d0 ; get a snapshot of DMA control register <v1.3>
|
|
btst.l #bTIMEP,d0 ; did the watchdog timer go off ? <v1.3>
|
|
beq.s @DMAdone ; if not, continue <v1.3>
|
|
if dbSymbols then
|
|
_Debugger
|
|
endif
|
|
move.w #scsiBusTOErr,d0 ; report that the watchdog timer expired
|
|
movea.l jvLogError(a4),a0 ; note the SCSI error in the PB
|
|
jsr (a0) ; send it
|
|
moveq.l #-1,d0 ; clear the timer interrupt <end>
|
|
move.l d0,sDTIME(a3) ; hit the watchdog timer to clear the intr <v1.3>
|
|
@DMAdone
|
|
movea.l jvClearIRQ(a4),a0 ; addr of ClearIRQ routine
|
|
jsr (a0) ; clear the VIA interrupt, if necessary
|
|
moveq.l #1,d0 ; flag to enable interrupts
|
|
movea.l jvDisEnable(a4),a0 ; addr of interrupt enable/disable routine
|
|
jsr (a0) ; turn interrupts back on
|
|
|
|
@inthndRTS
|
|
movem.l (sp)+,intrRegs ; restore registers
|
|
IF dbSymbols THEN
|
|
unlk a6
|
|
rts ; finished
|
|
dc.b 'INTHND ' ; MacsBug label
|
|
ELSE
|
|
rts
|
|
ENDIF
|
|
;
|
|
; Transfer -
|
|
;
|
|
; Called by: Data
|
|
;
|
|
; Calls: The primitive data transfer routines
|
|
;
|
|
; Registers: d0 <-- error, if any
|
|
; d1 <-- bytes transferred
|
|
; d2 --> number of bytes to transfer
|
|
; d3 --> bDMAR (6) <v1.3>
|
|
; d4 --> type of transfer to perform (offset into "dataTable")
|
|
;
|
|
; a2 --> ptr to data buffer
|
|
;
|
|
; Function: Sets up and dispatches to the simple data-transfer routines
|
|
;
|
|
; All primitive data transfer routines assume:
|
|
;
|
|
; d0 - <-- error (if any)
|
|
; d1 - <-- bytes transferred
|
|
; d2 - --> number of bytes to transfer
|
|
; d3 - scratch - saved
|
|
; d4 - --> type of transfer to perform
|
|
; d5 - scratch - saved
|
|
; d6 - scratch - saved
|
|
;
|
|
; a0 - scratch - saved
|
|
; a1 - saved - sBSR(a3)
|
|
; a2 - ptr to data buffer - saved
|
|
; a3 - SCSI read base address
|
|
; a4 - ptr to SCSI Mgr globals
|
|
; a5 - scratch - saved
|
|
;
|
|
Transfer:
|
|
link a6,#LocalSize ; allocate local storage
|
|
moveq #MaxBusErr,d0 ; upper limit for 680x0's <v1.3><v1.5><6>
|
|
cmp.b #cpu68040,CpuFlag ; check if we're on an 040 <6>
|
|
bne.s @storeValue ; NO ... leave BusErrCount alone <6>
|
|
moveq #Max040BusErrs,d0 ; YES ... use 040 MaxBusErr value <6>
|
|
@storeValue ; <6>
|
|
move.w d0,BusErrCount(a6) ; store upper limit for allowable bus errors <6>
|
|
|
|
moveq.l #noErr,d0 ; assume no error
|
|
move.l d2,d1 ; make a copy of the count - is it zero ?
|
|
beq.s @exit ; if so, get out
|
|
|
|
movem.l a1-a5/d2-d6,-(sp) ; save registers
|
|
|
|
lea.l sBSR(a3),a1 ; save later indexing
|
|
moveq.l #bDMAR,d3 ; bit DRQ test on sBSR(a3) (for speed)
|
|
|
|
move.l BusErrVct,OldBusErrVct(a6) ; keep old vector
|
|
move.l jvBusErr(a4),BusErrVct
|
|
|
|
lea.l dataTable(a4),a0 ; point to data transfer table in globals
|
|
movea.l 0(a0,d4.l),a0 ; get the routine address
|
|
|
|
jsr (a0) ; go to the appropriate routine
|
|
|
|
@done
|
|
move.l OldBusErrVct(a6),BusErrVct ; restore previous Bus Error vector
|
|
movem.l (sp)+,a1-a5/d2-d6 ; restore these guys
|
|
tst.w d0 ; set the condition codes
|
|
@exit
|
|
unlk a6 ; release local storage
|
|
rts
|
|
|
|
;
|
|
; All primitive data transfer routines use this convention:
|
|
;
|
|
; d0 - type of transfer to perform / error (if any)
|
|
; d1 - number of bytes to transfer / bytes transferred
|
|
; d2 - number of bytes to transfer
|
|
; d3 - bDMAR (6)
|
|
; d4 - scratch - saved
|
|
; d5 - scratch - saved
|
|
; d6 - scratch - saved
|
|
;
|
|
; a0 - special I/O address (pDMA or hhsk base, plus input/output register offset)
|
|
; a1 - sBSR(a3)
|
|
; a2 - ptr to data buffer
|
|
; a3 - SCSI read base address
|
|
; a4 - ptr to SCSI Mgr globals
|
|
; a5 - scratch - saved
|
|
;
|
|
|
|
;
|
|
; SlowRead
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
SlowRead:
|
|
@RdSetup
|
|
move.l pdma5380(a4),a0 ; use the global <C406/12Nov86>
|
|
adda.l #sIDR,a0 ; input data offset <C406/12Nov86>
|
|
subq.l #1,d2 ; decr. for DBRA <C478/10Dec86>
|
|
move.l d2,d5 ; make a copy of the byte count
|
|
swap d5 ; and get hiword in d5
|
|
@wait
|
|
btst.b d3,(a1) ; DRQ ? <v1.3>
|
|
bne.s @SnRd ; if so, start the transfer <v1.3>
|
|
btst.b #bREQ,sCSR(a3) ; look for REQ <v1.3>
|
|
beq.s @wait ; can't check phase until REQ is asserted <v1.3>
|
|
btst.b #bPM,(a1) ; we have REQ, do we have a phase match ? <v1.3>
|
|
beq.s @RdPhaseErr ; if not, bail out with an error <v1.3>
|
|
;
|
|
; This is the loop for polled reads. Byte count is in d2,d5
|
|
;
|
|
@SnRd
|
|
btst.b d3,(a1) ; DRQ? <C478/10Dec86>
|
|
beq.s @SnRdChk ; (9)
|
|
move.b (a0),(a2)+ ; read bytes into user space (13)
|
|
dbra d2,@SnRd ; decrement count
|
|
dbra d5,@SnRd ; high word of count
|
|
bra.s @RdDone ; we're done
|
|
@SnRdChk
|
|
bsr.w NewPhaseCheck ; check more carefully for phase match <1>
|
|
bne.s @SnRd ; continue if it is
|
|
@RdPhaseErr
|
|
moveq.l #scPhaseErr,d0 ; return error
|
|
bra.s @exit
|
|
|
|
@RdDone
|
|
btst.b #bACK,sBSR(a3) ; check for ACK <3.0>
|
|
beq.s @exit ; if no ACK, we're done <3.0>
|
|
@ACK
|
|
@wfnREQ
|
|
btst.b #bREQ,sCSR(a3) ; test for *REQ <3.0>
|
|
beq.s @exit ; if it's gone, we're done <3.0>
|
|
btst.b #bPM,(a1) ; still in same phase? <3.0> <v1.3>
|
|
bne.s @RdDone ; if so, go back and wait <3.0>
|
|
@exit
|
|
swap d5 ; calculate bytes left to transfer
|
|
move.w d2,d5 ; lower word of count
|
|
addq.l #1,d5 ; undo adjustment for dbra
|
|
sub.l d5,d1 ; number of bytes transferred
|
|
rts
|
|
|
|
;
|
|
; SlowWrite
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
SlowWrite:
|
|
@WrSetup
|
|
move.l pdma5380(a4),a0 ; point to special address space
|
|
adda.w wrOffset(a4),a0 ; add in write offset, if any
|
|
@ckphase
|
|
btst.b #bREQ,sCSR(a3) ; check for REQ
|
|
beq.s @ckphase ; if no REQ, wait for synchronization
|
|
@ready
|
|
move.b zeroReg,sTCR+WrOffs(a3) ; match data out<C846/PM116/19Mar87>
|
|
move.b #iDMA,sMR+wrOffs(a3) ; set DMA mode
|
|
move.b #iDB,sICR+wrOffs(a3) ; assert data bus
|
|
|
|
btst.b #sSCSIDMAExists,G_Reserved0+3(a4) ; see if we really have the DMA chip <2.8>
|
|
beq.s @DMAdone ; if not, skip it <2.8>
|
|
|
|
move.l #iHSKEN+iINTREN,sDCTRL(a3) ; turn on hhsk <C914> <v1.3>
|
|
@DMAdone
|
|
move.b zeroReg,sDMAtx+wrOffs(a3) ; start write DMA
|
|
@test
|
|
btst.b #bPM,(a1) ; we have REQ, are we in phase ? <v1.3>
|
|
bne.s @wrContinue ; if in phase, continue <v1.3>
|
|
@wrPhaseErr
|
|
moveq.l #scPhaseErr,d0 ; return a phase error
|
|
bra.s @WrDone ; get out
|
|
|
|
@wrContinue
|
|
move.l d2,d1 ; make a copy of the byte count
|
|
subq.l #1,d2 ; decr. for DBRA <C478/10Dec86>
|
|
move.l d2,d5 ; make a copy of the byte count
|
|
swap d5 ; and get hiword in d5
|
|
;
|
|
; This is the loop for polled writes. Byte count is in d2,d5
|
|
;
|
|
@SnWr
|
|
btst.b d3,(a1) ; DRQ? <C478/10Dec86>
|
|
beq.s @SnWrChk
|
|
move.b (a2)+,(a0) ; write bytes
|
|
dbra d2,@SnWr ; decrement count
|
|
dbra d5,@SnWr
|
|
bra.s @WrDone ; we're finished <v2.3>
|
|
@SnWrChk
|
|
btst.b #bPM,(a1) ; phase OK? <C478/10Dec86>
|
|
bne.s @SnWr ; continue if it is
|
|
moveq.l #scPhaseErr,d0 ; return a phase error
|
|
bra.s @DMAOff ; we're done for now
|
|
|
|
@WrDone ; NOTE: we shouldn't turn off DMA mode before the last byte was taken.
|
|
; If the NCR chip asserts DRQ, then the peripheral got the byte.
|
|
; If the peripheral changes the bus phase, then it must have also got it.
|
|
; This section check is from <C138/29Sep86>. Modified <C780>.
|
|
|
|
btst.b #sSCSIDMAExists,G_Reserved0+3(a4) ; do we have a SCSI DMA chip ?
|
|
beq.s @noDMA ; if not, skip
|
|
@wrWait1
|
|
btst.b d3,sBSR(a3) ; check for DRQ
|
|
bne.s @DMAOff ; if DRQ, peripheral got the byte
|
|
btst.b #bPM,sBSR(a3) ; are we still in phase ?
|
|
bne.s @wrWait1 ; if so keep waiting
|
|
@noDMA
|
|
@wrWait2
|
|
btst.b d3,sBSR(a3) ; check for DRQ
|
|
bne.s @DMAOff ; if DRQ, peripheral got the byte
|
|
btst.b #bPM,sBSR(a3) ; are we still in phase ?
|
|
bne.s @wrWait2 ; if so, keep waiting
|
|
|
|
@DMAOff
|
|
move.b zeroReg,sICR+WrOffs(a3) ; disable data bus
|
|
move.b zeroReg,sMR+WrOffs(a3) ; clear DMA mode
|
|
btst.b #sSCSIDMAExists,G_Reserved0+3(a4) ; see if we really have the DMA chip <2.8>
|
|
beq.s @DMAdone2 ; if not, skip it <2.8>
|
|
move.l #iINTREN,sDCTRL(a3) ; turn off hhsk <C914> <v1.3>
|
|
@DMAdone2
|
|
swap d5 ; calculate number of bytes left to transfer
|
|
move.w d2,d5 ; low-order word of count
|
|
addq.l #1,d5 ; adjust for DBRA
|
|
sub.l d5,d1 ; number of bytes transferred
|
|
rts ; we're done
|
|
|
|
|
|
;
|
|
; VFastWriteOSS -- Virtual address interface to the SCSI DMA circuitry <v1.3>
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
; Function: This routine calls a service routine to get the physical translation
|
|
; for the virtual address/count pair passed in a2/d2. If the virtual
|
|
; data transfer cannot be performed in one DMA operation (crosses a page
|
|
; or memory bank boundary,) the virtual transfer will be completed later
|
|
; by a virtual transfer of the remaining bytes.
|
|
;
|
|
VFastWriteOSS: ;
|
|
movea.l scsiPrivate(a5),a0 ; get ptr to private storage record <v1.3>
|
|
lea.l scsiVAddr(a0),a0 ; point to record for GetPhysical routine <start>
|
|
move.l a2,d0 ; get the virtual address <v1.5>
|
|
jsr ([JStripAddress]) ; clean up that address <v1.5>
|
|
move.l d0,(a0) ; set the virtual address field <v1.5>
|
|
@translate
|
|
move.l d2,4(a0) ; set the virtual count field
|
|
moveq.l #1,d0 ; number of physical addr/count pairs
|
|
move.l d0,a1 ; set up for GetPhysical call
|
|
moveq.l #5,d0 ; GetPhysical selector
|
|
_MemoryDispatch ; perform the translation (a0 undisturbed)
|
|
bne.s @error ; if error, bail out (what to do ???)
|
|
@callPhysical
|
|
move.l 8(a0),a2 ; set up the physical address
|
|
move.l 12(a0),d2 ; set up the physical count
|
|
move.l d2,d1 ; number of bytes to transfer
|
|
movea.l jvPFW(a4),a0 ; address of fast physical write routine
|
|
jmp (a0) ; move the data
|
|
@error
|
|
if dbSymbols then
|
|
_Debugger
|
|
endif
|
|
@exit
|
|
rts ; we're done
|
|
|
|
|
|
;
|
|
; PFastWriteOSS -- Physical address interface to the SCSI DMA circuitry
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
PFastWriteOSS:
|
|
moveq.l #noErr,d0 ; noErr, yet
|
|
move.b zeroReg,sTCR+WrOffs(a3) ; match data out<C846/PM116/19Mar87>
|
|
move.b #iDMA,sMR+WrOffs(a3) ; set DMA mode <C478/10Dec86>
|
|
move.b #iDB,sICR+WrOffs(a3) ; assert data bus <C478/10Dec86>
|
|
move.l #iHSKEN,sDCTRL(a3) ; turn on hhsk <C914>
|
|
move.b zeroReg,sDMAtx+WrOffs(a3) ; start write DMA <C478/10Dec86>
|
|
@start ; blind write
|
|
move.l #$FFFFFFFF,sDTIME(a3) ; incredible Timer value <v1.5>
|
|
move.l #iTIMEEN+iINTREN+iDMAEN,sDCTRL(a3) ; enable true DMA
|
|
move.l a2,sDADDR(a3) ; set up DMA addr
|
|
move.l d2,sDCNT(a3) ; set up byte count
|
|
move.b #iIEOP+iDMA,sMR(a3) ; pDMA,EOP intr enabled
|
|
move.b #iDB,sICR(a3) ; assert the data bus
|
|
move.b zeroReg,sDMAtx(a3) ; start DMA write
|
|
bset.b #waitingForIRQ,state1(a4) ; we're DMAing, so bail and wait for intr <v1.5>
|
|
@exit
|
|
rts
|
|
|
|
|
|
PFastWrite: ; unimplemented on all other machines
|
|
rts
|
|
|
|
|
|
;
|
|
; VFastWrite -- Same as FastWrite for the old SCSI Manager
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
VFastWrite: ; all other machines
|
|
;
|
|
; FastWrite
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
FastWrite:
|
|
|
|
;patch roll in from PatchIIciROM.a <SMx><SES>
|
|
;_________________________________________________________________________________________
|
|
; FastWriteFix - patch to FastWrite
|
|
;
|
|
; This code replaces the low level data transfer routine for fast writes in the SCSI
|
|
; manager. It is rewritten to assume psuedo-dma is always on, and to do the device
|
|
; synchronization (looking for *REQ and DRQ), after loading a data byte into the
|
|
; output register of the 5380. We don't have to worry about zero-byte transfers. They
|
|
; are filtered out in the calling routine (Transfer).
|
|
;
|
|
; Entry: d2 = number of bytes to transfer
|
|
; a1 = addr of sBSR(a3) - by convention, and used by bus error handler
|
|
; a2 = ptr to data buffer to transfer
|
|
; a3 = base addr of NCR 53C80
|
|
; a4 = ptr to SCSI globals
|
|
;
|
|
; Exit: d1 = number of bytes actually transfered. This value is only good if
|
|
; the transfer was good with no errors. It is inaccurate when an
|
|
; error aborts the transfer.
|
|
|
|
;This code is intended for use with 5380 ONLY, so check to make sure we <SMx><SES>
|
|
;need to do it. Note that we're assuming that SCSIExists implies a 5380. <SMx><SES>
|
|
TestFor SCSIExists ; <SMx><SES>
|
|
beq @Done ; don't do it if we're not using a 5380 <SMx><SES>
|
|
|
|
move.l hhsk5380(a4),a0 ; point to addr for hardware handshaking
|
|
adda.w wrOffset(a4),a0 ; add in write offset
|
|
move.l d2,d1 ; save to return number of bytes transfered
|
|
|
|
; Pre-load the NCR 53C80's output register with a byte of data. If we are in
|
|
; the middle of a multi-block write, *ACK is currently asserted. Writing a byte
|
|
; to the output register will release *ACK, completing the handshaking, allowing
|
|
; the target to assert *REQ. Is there a chance We do not need to check for DRQ at this point (to see
|
|
; if the chip is ready for the next byte), because all data out routines always
|
|
; wait to make sure the last byte got out.
|
|
|
|
sub.l #1,d2 ; dec number of bytes to xfer
|
|
move.b (a2)+,(a0) ; write data byte to output register
|
|
|
|
; With *ACK released, determine if the target is in sync. We cannot look for
|
|
; *REQ to be asserted, because the target may have already accepted the data byte
|
|
; and released *REQ at this point. We can sync on DRQ which will signal when
|
|
; the NCR 53C80 is ready to accept a data byte, meaning a *REQ from the target
|
|
; must have already occurred and our data byte was taken.
|
|
|
|
@syncWait
|
|
btst.b #bDMAR,(a1) ; check bus & status reg for DRQ
|
|
bne.s @doWrite ; DRQ present - sync-ed up so proceed
|
|
btst.b #bREQ,sCSR(a3) ; no DRQ - is *REQ present ?
|
|
beq.s @syncWait ; no *REQ yet - wait for sync
|
|
btst.b #bPM,(a1) ; with *REQ, check phase lines
|
|
bne.s @syncWait ; still in data out phase - wait
|
|
moveq.l #0,d1 ; out of phase - did not xfer any bytes
|
|
moveq.l #scPhaseErr,d0 ; return error
|
|
bra.s @Done ; exit
|
|
|
|
; Perform the write to the SCSI chip. First align the bytes to longs, then
|
|
; align them to 32 byte chunks. Transfer the bulk of the data in 32 byte
|
|
; blocks.
|
|
; Reg d2.l = number of bytes to move
|
|
|
|
@doWrite
|
|
cmpi.l #3,d2 ; check for very short copy
|
|
bls.s @veryShort ; skip alignment if very short
|
|
|
|
move.l a2,d0 ; get addr of data buffer
|
|
andi.l #$00000003,d0 ; check for long word alignment
|
|
beq.s @Aligned ; if no alignment needed
|
|
subq.l #4,d0 ; bias by 4 to get correct index
|
|
jmp @Aligned(d0.w*2) ; do the alignment
|
|
move.b (a2)+,(a0) ; move a byte
|
|
move.b (a2)+,(a0) ; move a byte
|
|
move.b (a2)+,(a0) ; move a byte
|
|
@Aligned
|
|
add.l d0,d2 ; adjust the byte count (d0 = neg)
|
|
move.l d2,d4 ; save tail byte count
|
|
lsr.w #2,d2 ; adjust to number of longs to move
|
|
moveq.l #7,d0 ; mask for starting index
|
|
and.l d2,d0 ; number of long words to move first
|
|
neg.w d0 ; negate to index backwards
|
|
lsr.l #3,d2 ; number of 32 byte blocks to move
|
|
move.l d2,d3 ; get number of 32*64K byte blks to move
|
|
swap d3 ; count in low word
|
|
jmp @CopyStart(d0.w*2) ; jump into the loop
|
|
|
|
@CopyLoop move.l (a2)+,(a0) ; move a 32 byte block of data....
|
|
move.l (a2)+,(a0) ; ... 4 bytes at a time
|
|
move.l (a2)+,(a0)
|
|
move.l (a2)+,(a0)
|
|
move.l (a2)+,(a0)
|
|
move.l (a2)+,(a0)
|
|
move.l (a2)+,(a0)
|
|
move.l (a2)+,(a0)
|
|
@CopyStart dbra d2,@CopyLoop ; loop in chunks of 32 bytes
|
|
dbra d3,@CopyLoop ; loop in chunks of 32*64K bytes
|
|
|
|
andi.l #$00000003,d4 ; check for tail alignment
|
|
move.l d4,d2 ; d2 = number of bytes remaining
|
|
|
|
@veryShort
|
|
neg.w d2 ; negate to index backwards
|
|
jmp @Remaining(d2.w*2) ; write remaining bytes
|
|
move.b (a2)+,(a0) ; move a byte
|
|
move.b (a2)+,(a0) ; move a byte
|
|
move.b (a2)+,(a0) ; move a byte
|
|
@Remaining
|
|
|
|
; Before exiting this routine, make sure that the peripheral has actually accepted
|
|
; the data byte. Wait for a DRQ (meaning the SCSI chip is ready for another byte)
|
|
; before exiting.
|
|
|
|
@DoneWait
|
|
moveq.l #0,d0 ; set good return
|
|
|
|
btst.b #bDMAR,(a1) ; check for DRQ (a1 = sBSR(a3))
|
|
bne.s @Done ; if DRQ, peripheral got the byte
|
|
btst.b #bREQ,sCSR(a3) ; no DRQ - is *REQ present ?
|
|
beq.s @DoneWait ; no *REQ yet - wait for it
|
|
btst.b #bPM,(a1) ; are we still in phase ?
|
|
bne.s @DoneWait ; if so, keep waiting
|
|
|
|
@Done
|
|
rts ; we're done
|
|
|
|
;end FastWrite patch roll in from PatchIIciROM.a <SMx><SES>
|
|
|
|
|
|
;
|
|
; FastWriteOSS -- Old SCSI Mgr virtual address interface to the SCSI DMA circuitry
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
; NOTE:
|
|
; Because the SCSI Bus Error handler counts on the a6 linkage that is in
|
|
; effect when this routine is called, the local storage needed by this routine
|
|
; to perform a DMA operation is simply allocated by moving the stack pointer.
|
|
;
|
|
; After allocating the local storage, the stack appears as follows:
|
|
;
|
|
; SP ---> virtAddress \ The stack pointer points to the
|
|
; virtCount \ parameter block for the GetPhysical call.
|
|
; physAddress / (All values are long words.)
|
|
; physCount /
|
|
; - reserved - Extra long word
|
|
; cleanAddress Pushed after StripAddress
|
|
; returnAddr <--- SP on entry
|
|
; ...
|
|
;
|
|
FastWriteOSS:
|
|
move.l a2,d0 ; make the buffer address 32-bit clean
|
|
jsr ([JStripAddress]) ; clean up that address
|
|
move.l d0,-(sp) ; save the 32-bit clean address <4>
|
|
|
|
lea.l -AddLocalSize(sp),sp ; allocate additional storage <v1.7start>
|
|
move.l d0,vAddr(sp) ; set the virtual address field <4>
|
|
move.l d2,vCount(sp) ; set the virtual count field <4>
|
|
|
|
movea.l d0,a0 ; virtual address <4>
|
|
movea.l d2,a1 ; virtual count <4>
|
|
moveq.l #2,d0 ; LockMemory selector <4>
|
|
_MemoryDispatch ; lock the buffer (a0 undisturbed) <4>
|
|
bne.w @badAddrError ; if error, then bail out <4>
|
|
|
|
movea.l sp,a0 ; a0 -> logical/physical param block
|
|
movea.w #1,a1 ; number of physical addr/count pairs
|
|
moveq.l #5,d0 ; GetPhysical selector
|
|
_MemoryDispatch ; perform the translation (a0 undisturbed)
|
|
bne.w @badAddrError ; if error, then bail out <v1.7>
|
|
@ckphase
|
|
btst.b #bREQ,sCSR(a3) ; check for REQ
|
|
beq.s @ckphase ; if no REQ, wait (synchronization)
|
|
@ready
|
|
move.b zeroReg,sTCR+WrOffs(a3) ; match data out<C846/PM116/19Mar87>
|
|
move.b #iDMA,sMR+WrOffs(a3) ; set DMA mode <C478/10Dec86>
|
|
move.b #iDB,sICR+WrOffs(a3) ; assert data bus <C478/10Dec86>
|
|
moveq.l #iHSKEN,d5 ; <v1.7>
|
|
move.l d5,sDCTRL(a3) ; turn on hhsk <C914> <start>
|
|
move.b zeroReg,sDMAtx+WrOffs(a3) ; start write DMA <C478/10Dec86>
|
|
@wait
|
|
btst.b d3,sBSR(a3) ; is DRQ asserted ?
|
|
bne.s @start ; if so, ACK must be true (pDMA)
|
|
btst.b #bREQ,sCSR(a3) ; check for REQ for phase check
|
|
beq.s @wait ; no REQ yet
|
|
btst.b #bPM,sBSR(a3) ; are we still in phase ? <end>
|
|
bne.s @wait ; keep waiting <v1.7>
|
|
@wrPhaseErr
|
|
moveq.l #scPhaseErr,d0 ; return a phase error
|
|
bra.w @WrDone ; get out
|
|
|
|
@start ; at this point, d0 = noErr <v1.7start>
|
|
bset.b #doingDMA,state1(a4) ; we're DMAing, so don't bother the chip <5>
|
|
move.l G_Reserved1(a4),sDTIME(a3) ; set Timer value <5>
|
|
moveq.l #iTIMEEN+iINTREN+iDMAEN,d5 ;
|
|
move.l d5,sDCTRL(a3) ; enable true DMA
|
|
move.l pAddr(a0),sDADDR(a3) ; set up DMA addr
|
|
move.l pCount(a0),sDCNT(a3) ; set up byte count
|
|
move.b #iIEOP+iDMA,sMR(a3) ; pDMA,EOP intr enabled
|
|
move.b #iDB,sICR(a3) ; assert the data bus
|
|
move.b zeroReg,sDMAtx(a3) ; start DMA write
|
|
;
|
|
; The pCount field is used to flag the need to move more data.
|
|
; If pCount is zero, it reflects that no more data needs to be moved.
|
|
; If pCount is nonzero, a translation will be performed, leaving pCount nonzero.
|
|
;
|
|
move.l vCount(a0),pCount(a0) ; any bytes left to transfer ?
|
|
beq.s @noPreflight ; if not, nothing to translate
|
|
movea.w #1,a1 ; number of physical addr/count pairs
|
|
moveq.l #5,d0 ; GetPhysical selector
|
|
_MemoryDispatch ; perform the translation (a0 undisturbed)
|
|
bne.w @badAddrError ; if error, then bail out
|
|
@noPreflight
|
|
movea.l OSS,a1 ; point to base of OSS chip
|
|
@waitForIntr
|
|
btst.b #OSSIntSCSI-8,OSSIntStat(a1) ; check for a SCSI interrupt
|
|
beq.s @waitForIntr ; wait for a SCSI interrupt
|
|
move.l sDCTRL(a3),d5 ; check for success
|
|
andi.w #iDMABERR+iTIMEP,d5 ; DMA bus error or watchdog timeout ?
|
|
bne.s @badAddrError ; if so, handle it
|
|
tst.l sDCNT(a3) ; did we transfer all the data ?
|
|
bne.s @badAddrError ; if not, report an error
|
|
move.b zeroReg,sMR(a3) ; clear EOP interrupt (and turn off pDMA)
|
|
tst.b sRESET(a3) ; clear SCSI interrupt
|
|
@chkDone
|
|
tst.l pCount(a0) ; were any bytes left to transfer ? <end>
|
|
bne.s @start ; continue until all bytes transferred <v1.7>
|
|
|
|
move.l d2,d1 ; return number of bytes transferred
|
|
|
|
@WrDone ; NOTE: we shouldn't turn off DMA mode before the last byte was taken.
|
|
; If the NCR chip asserts DRQ, then the peripheral got the byte.
|
|
; If the peripheral changes the bus phase, then it must have also got it.
|
|
; This section check is from <C138/29Sep86>. Modified <C780>.
|
|
btst.b #bACK,sBSR(a3) ; check for ACK <3.0>
|
|
beq.s @DMAOff ; if no ACK, we're done <3.0>
|
|
@ACK
|
|
@wfnREQ
|
|
btst.b #bREQ,sCSR(a3) ; test for *REQ <3.0>
|
|
beq.s @DMAOff ; if it's gone, we're done <3.0>
|
|
btst.b #bPM,sBSR(a3) ; still in same phase? <3.0> <v1.7>
|
|
bne.s @WrDone ; if so, go back and wait <3.0>
|
|
|
|
@DMAOff
|
|
move.b zeroReg,sMR+WrOffs(a3) ; clear DMA mode
|
|
move.b zeroReg,sICR+WrOffs(a3) ; disable data bus
|
|
moveq.l #iINTREN,d5 ; <v1.7>
|
|
move.l d5,sDCTRL(a3) ; turn off hhsk <C914> <v1.3> <start>
|
|
|
|
bclr.b #doingDMA,state1(a4) ; we're finished DMAing <5>
|
|
|
|
lea.l AddLocalSize(sp),sp ; deallocate additional storage on the stack
|
|
|
|
movea.l (sp)+,a0 ; set virtual address <4>
|
|
movea.l d2,a1 ; set virtual count <4>
|
|
move.w d0,-(sp) ; save result from SCSI I/O <4>
|
|
moveq.l #3,d0 ; UnlockMemory selector <4>
|
|
_MemoryDispatch ; unlock the buffer (a0 undisturbed) <4>
|
|
move.w (sp)+,d0 ; restore original result <4>
|
|
|
|
rts
|
|
|
|
@badAddrError
|
|
moveq.l #scBusTOErr,d0 ; return a SCSI bus timeout error <end>
|
|
bra.s @DMAOff ; <v1.7>
|
|
rts ; we're done
|
|
|
|
|
|
;
|
|
; VFastReadOSS -- Virtual address interface to the DMA circuitry <v1.3>
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
; Function: This routine calls a service routine to get the physical translation
|
|
; for the virtual addr/count pair that is passed in a2/d2. If the virtual
|
|
; data transfer cannot be performed in one DMA operation (crosses a page or
|
|
; memory bank boundary,) the virtual transfer will be completed later by
|
|
; a virtual transfer of the remaining bytes.
|
|
;
|
|
VFastReadOSS:
|
|
movea.l scsiPrivate(a5),a0 ; get ptr to private storage record <v1.3>
|
|
lea.l scsiVAddr(a0),a0 ; point to record for GetPhysical routine <start>
|
|
move.l a2,d0 ; get the virtual address <v1.5>
|
|
jsr ([JStripAddress]) ; clean up that address <v1.5>
|
|
move.l d0,(a0) ; set the virtual address field <v1.5>
|
|
@translate
|
|
move.l d2,4(a0) ; set the virtual count field
|
|
moveq.l #1,d0 ; number of physical address/count pairs
|
|
move.l d0,a1 ; set up for GetPhysical call
|
|
moveq.l #5,d0 ; GetPhysical selector
|
|
_MemoryDispatch ; perform the translation (a0 undisturbed)
|
|
bne.s @error ; if error, then bail out (what to do ???)
|
|
@callPhysical
|
|
move.l 8(a0),a2 ; set up the physical address
|
|
move.l 12(a0),d2 ; set up the physical count
|
|
move.l d2,d1 ; number of bytes to transfer
|
|
movea.l jvPFR(a4),a0 ; address of fast physical data transfer routine
|
|
jmp (a0) ; move the data
|
|
@error
|
|
if dbSymbols then
|
|
_Debugger
|
|
endif
|
|
@exit
|
|
rts
|
|
|
|
|
|
;
|
|
; PFastRead -- Physical address interface to the DMA circuitry
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
PFastReadOSS:
|
|
moveq.l #noErr,d0 ; noErr, yet
|
|
@start
|
|
move.l #$FFFFFFFF,sDTIME(a3) ; incredible Timer value <v1.5>
|
|
move.l #iTIMEEN+iINTREN+iDMAEN,sDCTRL(a3) ; enable true DMA
|
|
move.l a2,sDADDR(a3) ; set up DMA addr
|
|
move.l d2,sDCNT(a3) ; set up byte count
|
|
move.b #iIEOP+iDMA,sMR(a3) ; pDMA,EOP intr enabled
|
|
move.b zeroReg,sIDMArx(a3) ; start DMA read
|
|
bset.b #waitingForIRQ,state1(a4) ; we're DMAing, so bail and wait for intr <v1.5>
|
|
@exit
|
|
rts
|
|
|
|
|
|
;
|
|
; PFastRead -- Physical address interface to the DMA circuitry
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
PFastRead: ; all other machines
|
|
rts ; unimplemented
|
|
|
|
|
|
|
|
|
|
;
|
|
; VFastRead -- For all other machines, same as old SCSI Mgr FastRead routine
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
VFastRead:
|
|
;
|
|
; FastRead
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
FastRead:
|
|
@RdSetup
|
|
move.l hhsk5380(a4),a0 ; use the global <C406/12Nov86>
|
|
adda.l #sIDR,a0 ; input data offset <C406/12Nov86>
|
|
@wait
|
|
btst.b d3,(a1) ; DRQ? <C478/10Dec86>
|
|
bne.s @start ; have DRQ, so let's go
|
|
btst.b #bREQ,sCSR(a3) ; test for REQ in case of phase error
|
|
beq.s @wait ; no REQ, so wait
|
|
@ckphase
|
|
btst.b #bPM,(a1) ; phase OK? <C478/10Dec86>
|
|
bne.s @start ; continue if in phase
|
|
@RdPhaseErr
|
|
moveq.l #scPhaseErr,d0 ; return error
|
|
bra.w @exit
|
|
@start
|
|
move.l d2,d1 ; copy of transfer count
|
|
moveq.l #3,d4 ; mask for long alignment
|
|
cmp.l d4,d2 ; enough bytes to align?
|
|
bhi.s @over3 ; more than three bytes
|
|
moveq.l #-1,d5 ; fudge to calculate normally
|
|
moveq.l #-1,d6 ; more fudging
|
|
bra.s @BlRdRmdr ; do them one at a time
|
|
@over3
|
|
move.l a2,d5 ; get destination address
|
|
neg.l d5 ; negate to get odd byte count
|
|
and.l d4,d5 ; mask off to get 0-3 count
|
|
sub.l d5,d2 ; d2 = count after alignment
|
|
bra.s @1 ; in case it's already aligned
|
|
@0
|
|
move.b (a0),(a2)+ ; do the leading unaligned bytes
|
|
@1
|
|
dbra d5,@0 ; loop until long-word aligned
|
|
move.l d2,d5 ; copy of the count
|
|
lsr.l #5,d5 ; divide by 32 <A356/06Nov86>
|
|
bne.s @over32 ; more than 32 bytes left
|
|
moveq.l #-1,d5 ; fudge to calculate normally
|
|
moveq.l #-1,d6 ; more fudging
|
|
bra.s @BlRdRmdr ; get the remaining bytes
|
|
@over32
|
|
subq.l #1,d5 ; adjust for dbra
|
|
move.l d5,d6 ; high order word of count
|
|
swap d6 ; number of 32-byte moves
|
|
moveq.l #$1F,d4 ; 32-1 <A356/06Nov86>
|
|
and.l d4,d2 ; remainder count after 32-byte moves
|
|
|
|
@BlRd32 ; <A356/06Nov86>
|
|
move.l (a0),(a2)+ ; do 8 aligned longs (32 bytes)
|
|
move.l (a0),(a2)+
|
|
move.l (a0),(a2)+
|
|
move.l (a0),(a2)+
|
|
move.l (a0),(a2)+
|
|
move.l (a0),(a2)+
|
|
move.l (a0),(a2)+
|
|
move.l (a0),(a2)+
|
|
|
|
dbra d5,@BlRd32 ; low word of # of 32-byte moves
|
|
dbra d6,@BlRd32 ; high order word
|
|
bra.s @BlRdRmdr
|
|
|
|
@BlRdSingle
|
|
move.b (a0),(a2)+
|
|
@BlRdRmdr
|
|
dbra d2,@BlRdSingle
|
|
swap d6 ; calculate bytes left to transfer
|
|
move.w d5,d6 ; get low order word
|
|
addq.l #1,d6 ; groups of 32
|
|
lsl.l #5,d6 ; multiply by 32
|
|
ext.l d2 ; make d2 a long
|
|
addq.l #1,d2 ; single bytes left
|
|
add.l d2,d6 ; add to total
|
|
sub.l d6,d1 ; result
|
|
|
|
@RdDone
|
|
btst.b #bACK,sBSR(a3) ; check for ACK <3.0>
|
|
beq.s @exit ; if no ACK, we're done <3.0>
|
|
@ACK
|
|
@wfnREQ
|
|
btst.b #bREQ,sCSR(a3) ; test for *REQ <3.0>
|
|
beq.s @exit ; if it's gone, we're done <3.0>
|
|
btst.b #bPM,(a1) ; still in same phase? <3.0> <v1.3>
|
|
bne.s @RdDone ; if so, go back and wait <3.0>
|
|
@exit
|
|
rts
|
|
|
|
;
|
|
; FastReadOSS -- Old SCSI Mgr virtual address interface to the DMA circuitry
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
; NOTE:
|
|
; Because the SCSI Bus Error handler counts on the a6 linkage that is in
|
|
; effect when this routine is called, the local storage needed by this routine
|
|
; to perform a DMA operation is simply allocated by moving the stack pointer.
|
|
;
|
|
; After allocating the local storage, the stack appears as follows:
|
|
;
|
|
; SP ---> virtAddress \ The stack pointer points to the
|
|
; virtCount \ parameter block for the GetPhysical call.
|
|
; physAddress / (All values are long words.)
|
|
; physCount /
|
|
; - reserved - Extra long word
|
|
; cleanAddress Pushed after StripAddress
|
|
; returnAddr <--- SP on entry
|
|
; ...
|
|
;
|
|
FastReadOSS:
|
|
move.l a2,d0 ; make the address 32-bit clean
|
|
jsr ([JStripAddress]) ; clean up that address
|
|
move.l d0,-(sp) ; save the 32-bit clean virtual address <4>
|
|
|
|
lea.l -AddLocalSize(sp),sp ; allocate additional storage <v1.7start>
|
|
move.l d0,vAddr(sp) ; set the virtual address <4>
|
|
move.l d2,vCount(sp) ; set the virtual count <4>
|
|
|
|
movea.l d0,a0 ; virtual address <4>
|
|
movea.l d2,a1 ; virtual count <4>
|
|
moveq.l #2,d0 ; LockMemory selector <4>
|
|
_MemoryDispatch ; lock the buffer (a0 undisturbed) <4>
|
|
bne.w @badAddrError ; if error, then bail out <4>
|
|
|
|
movea.l sp,a0 ; a0 -> logical/physical param block <4>
|
|
movea.w #1,a1 ; number of physical address/count pairs
|
|
moveq.l #5,d0 ; GetPhysical selector
|
|
_MemoryDispatch ; perform the translation (a0 undisturbed)
|
|
bne.w @badAddrError ; if error, then bail out
|
|
@wait
|
|
btst.b d3,sBSR(a3) ; DRQ? <C478/10Dec86>
|
|
bne.s @start ; have DRQ, so let's go
|
|
btst.b #bREQ,sCSR(a3) ; test for REQ in case of phase error
|
|
beq.s @wait ; no REQ, so wait <end>
|
|
btst.b #bPM,sBSR(a3) ; phase OK? <C478/10Dec86> <v1.7>
|
|
bne.s @start ; continue if in phase
|
|
@RdPhaseErr
|
|
moveq.l #scPhaseErr,d0 ; return error
|
|
bra.w @exit
|
|
|
|
@start ; at this point, d0 = noErr <v1.7start>
|
|
bset.b #doingDMA,state1(a4) ; we're DMAing, so don't bother the chip <5>
|
|
move.l G_Reserved1(a4),sDTIME(a3) ; set Timer value <5>
|
|
moveq.l #iTIMEEN+iINTREN+iDMAEN,d5 ;
|
|
move.l d5,sDCTRL(a3) ; enable true DMA
|
|
move.l pAddr(a0),sDADDR(a3) ; set up DMA addr
|
|
move.l pCount(a0),sDCNT(a3) ; set up byte count
|
|
move.b #iIEOP+iDMA,sMR(a3) ; pDMA,EOP intr enabled
|
|
move.b zeroReg,sIDMArx(a3) ; start DMA read
|
|
;
|
|
; The pCount field is used to flag the need to move more data.
|
|
; If pCount is zero, it reflects that no more data needs to be moved.
|
|
; If pCount is nonzero, a translation will be performed, leaving pCount nonzero.
|
|
;
|
|
move.l vCount(a0),pCount(a0) ; any bytes left to transfer ?
|
|
beq.s @noPreflight ; if not, nothing to translate
|
|
movea.w #1,a1 ; number of physical address/count pairs
|
|
moveq.l #5,d0 ; GetPhysical selector
|
|
_MemoryDispatch ; perform the translation (a0 undisturbed)
|
|
bne.w @badAddrError ; if error, then bail out
|
|
@noPreflight
|
|
movea.l OSS,a1 ; point to base of OSS chip
|
|
@waitForIntr
|
|
btst.b #OSSIntSCSI-8,OSSIntStat(a1) ; check for a SCSI interrupt
|
|
beq.s @waitForIntr ; wait for a SCSI interrupt
|
|
move.l sDCTRL(a3),d5 ; check for success
|
|
andi.w #iDMABERR+iTIMEP,d5 ; DMA bus error or watchdog timeout ?
|
|
bne.s @badAddrError ; if so, handle it
|
|
tst.l sDCNT(a3) ; did we transfer all the data ?
|
|
bne.s @badAddrError ; if not, report an error
|
|
move.b zeroReg,sMR(a3) ; clear the EOP interrupt, turn off pDMA
|
|
tst.b sRESET(a3) ; clear SCSI interrupt
|
|
move.b #iDMA,sMR(a3) ; turn on pDMA
|
|
move.b zeroReg,sIDMArx(a3) ; kick the pDMA mode
|
|
@chkDone
|
|
tst.l pCount(a0) ; any bytes left to transfer ? <end>
|
|
bne.s @start ; keep going until all bytes transferred
|
|
|
|
move.l d2,d1 ; return number of bytes transferred <v1.7>
|
|
|
|
@RdDone
|
|
btst.b #bACK,sBSR(a3) ; check for ACK <3.0>
|
|
beq.s @exit ; if no ACK, we're done <3.0>
|
|
@ACK
|
|
@wfnREQ
|
|
btst.b #bREQ,sCSR(a3) ; test for *REQ <3.0>
|
|
beq.s @exit ; if it's gone, we're done <3.0>
|
|
btst.b #bPM,sBSR(a3) ; still in same phase? <3.0> <v1.3><v1.7>
|
|
bne.s @RdDone ; if so, go back and wait <3.0>
|
|
@exit
|
|
bclr.b #doingDMA,state1(a4) ; we're finished DMAing <5>
|
|
|
|
lea.l AddLocalSize(sp),sp ; deallocate additional storage <v1.7>
|
|
|
|
movea.l (sp)+,a0 ; set virtual address <4>
|
|
movea.l d2,a1 ; set buffer length <4>
|
|
move.w d0,-(sp) ; save result from SCSI I/O <4>
|
|
moveq.l #3,d0 ; UnlockMemory selector <4>
|
|
_MemoryDispatch ; unlock the buffer (a0 undisturbed) <4>
|
|
move.w (sp)+,d0 ; restore original result <4>
|
|
|
|
rts
|
|
|
|
@badAddrError
|
|
moveq.l #scBusTOErr,d0 ; report a "bus timeout error" <v1.7>
|
|
bra.s @exit ; <end>
|
|
rts
|
|
|
|
|
|
|
|
|
|
;
|
|
; SlowComp <v1.3>
|
|
; <start>
|
|
; Called by: Transfer
|
|
;
|
|
SlowComp:
|
|
@CompSetup
|
|
move.l pdma5380(a4),a0 ; use the global <C406/12Nov86>
|
|
adda.l #sIDR,a0 ; input data offset <C406/12Nov86>
|
|
subq.l #1,d2 ; decr. for DBRA <C478/10Dec86>
|
|
move.l d2,d5 ; make a copy of the byte count
|
|
swap d5 ; and get hiword in d5
|
|
@wait
|
|
btst.b #bDMAR,(a1) ; DRQ ?
|
|
bne.s @SnComp ; if so, start the transfer
|
|
btst.b #bREQ,sCSR(a3) ; look for REQ
|
|
beq.s @wait ; can't check phase until REQ is asserted
|
|
btst.b #bPM,(a1) ; we have REQ, do we have a phase match ?
|
|
beq.s @CompPhaseErr ; if not, bail out with an error
|
|
@SnComp ; Slow compare loop. Byte count in d2,d5
|
|
btst.b #bDMAR,(a1) ; DRQ? <C478/10Dec86>
|
|
beq.s @SnCompChk ; no, so check phase <A244/27Oct86>
|
|
move.b (a0),d3 ; get data byte <29Oct85>
|
|
cmp.b (a2)+,d3 ; same? <29Oct85>
|
|
beq.s @SnComp1 ; if so, continue
|
|
moveq.l #scCompareErr,d0 ; record compare error <29Oct85>
|
|
@SnComp1
|
|
dbra d2,@SnComp ; repeat
|
|
dbra d5,@SnComp ; repeat
|
|
bra.s @RdDone ; let's get out of here
|
|
@SnCompChk
|
|
bsr.w NewPhaseCheck ; check more carefully for phase match <1>
|
|
bne.s @SnComp ; continue if it is
|
|
@CompPhaseErr
|
|
moveq.l #scPhaseErr,d0 ; return error
|
|
bra.s @exit ; let's get out here
|
|
@RdDone
|
|
btst.b #bACK,sBSR(a3) ; check for ACK <3.0>
|
|
beq.s @exit ; if no ACK, we're done <3.0>
|
|
@ACK
|
|
@wfnREQ
|
|
btst.b #bREQ,sCSR(a3) ; test for *REQ <3.0>
|
|
beq.s @exit ; if it's gone, we're done <3.0>
|
|
btst.b #bPM,(a1) ; still in same phase? <3.0>
|
|
bne.s @RdDone ; if so, go back and wait <3.0>
|
|
@exit
|
|
swap d5 ; calculate bytes left to transfer
|
|
move.w d2,d5 ; lower word of count
|
|
addq.l #1,d5 ; undo adjustment for DBRA
|
|
sub.l d5,d1 ; number of bytes transferred
|
|
moveq.l #bDMAR,d3 ; restore d3
|
|
rts
|
|
|
|
;
|
|
; FastComp
|
|
;
|
|
; Called by: Transfer
|
|
;
|
|
FastComp:
|
|
@CompSetup
|
|
movea.l hhsk5380(a4),a0 ; use the global
|
|
adda.l #sIDR,a0 ; input data register offset
|
|
subq.l #1,d2 ; decr. for DBRA <C478/10Dec86>
|
|
move.l d2,d5 ; make a copy of the byte count
|
|
swap d5 ; and get hiword in d5
|
|
@wait
|
|
btst.b #bDMAR,(a1) ; DRQ ?
|
|
bne.s @BlCmp ; have DRQ, so let's go
|
|
btst.b #bREQ,sCSR(a3) ; test for REQ in case of phase error
|
|
beq.s @wait ; no REQ, so wait
|
|
@ckPhase
|
|
btst.b #bPM,(a1) ; phase OK ?
|
|
bne.s @BlCmp ; continue if in phase
|
|
@RdPhaseErr
|
|
moveq.l #scPhaseErr,d0 ; return error
|
|
bra.s @exit ; let's get out here
|
|
|
|
@BlCmp
|
|
move.b (a0),d3 ; blind compare <29Oct85> -- no optimization
|
|
cmp.b (a2)+,d3
|
|
beq.s @BlCmp1
|
|
moveq.l #scCompareErr,d0 ; record compare error
|
|
@BlCmp1
|
|
dbra d2,@BlCmp
|
|
dbra d5,@BlCmp
|
|
|
|
@RdDone
|
|
btst.b #bACK,sBSR(a3) ; check for ACK <3.0>
|
|
beq.s @exit ; if no ACK, we're done <3.0>
|
|
@ACK
|
|
@wfnREQ
|
|
btst.b #bREQ,sCSR(a3) ; test for *REQ <3.0>
|
|
beq.s @exit ; if it's gone, we're done <3.0>
|
|
btst.b #bPM,(a1) ; still in same phase? <3.0>
|
|
bne.s @RdDone ; if so, go back and wait <3.0>
|
|
@exit
|
|
swap d5 ; calculate bytes left to transfer
|
|
move.w d2,d5 ; lower word of count
|
|
addq.l #1,d5 ; undo adjustment for DBRA
|
|
sub.l d5,d1 ; number of bytes transferred <end>
|
|
moveq.l #bDMAR,d3 ; restore d3 <v1.3>
|
|
rts
|
|
|
|
;
|
|
; This "improved" phase checking routine will fix the CD-ROM problem on Zone5. <1>
|
|
; On the SCSI DMA chip it's possible to have a DRQ without a phase match, and if an <start>
|
|
; interrupt occurs between checks of the BSR register, it appears that a disasterous
|
|
; phase change has occurred.
|
|
; To fix this problem, a snapshot is taken of the BSR register. If either DRQ or PM
|
|
; are set, then it is okay to continue. If both are cleared, a phase error has occurred.
|
|
; If DRQ or PM are set, the Zero flag is cleared.
|
|
; If neither are set, the Zero flag is set.
|
|
;
|
|
; Destroys d6
|
|
;
|
|
NewPhaseCheck:
|
|
moveq.l #iDMAR+iPM,d6 ; if DRQ or PM is set, all is well
|
|
and.b sBSR(a3),d6 ; get a snapshot of the register <end>
|
|
rts ; if either is set, then Z=0 <1>
|
|
|
|
|
|
ENDWITH
|
|
END
|
|
|