sys7.1-doc-wip/OS/SCSIMgr/SCSIMgrOld.a

1072 lines
41 KiB
Plaintext
Raw Normal View History

2019-07-27 14:37:48 +00:00
;-----------------------------------------------------------
;
; File: SCSIMgrOld.a
;
; Written by: Erich Ringewald
;
; Copyright: © 1985-1993 by Apple Computer, Inc. All rights reserved.
;
; Change History (most recent first):
;
; <SM7> 12/13/93 PN Roll in KAOs and Horror changes to support Malcom and AJ
; machines.
; <SM6> 2/5/93 CSS Rollin from Horror:
; <H2> 2/10/92 SWC Exported some labels so DBLite's SCSI Manager can use them.
; <SM5> 11/3/92 SWC Changed SCSIEqu.a->SCSI.a.
; <SM4> 6/20/92 PN Roll in patchIIciROM.a. Fix the NewSCSIBlind call
; <SM3> 5/17/92 kc Include PowerPrivEqu.a. The powerCntl equ was moved inside the
; pmCommandRec record.
; <6> 5/1/92 JSM Get rid of conditionals: dont use not forROM, this file is only
; used in ROM builds, dont use onMacXX style conditionals. This
; file now has no conditionals.
; <5> 12/27/91 RB Rolled in Terror changes. Cleaned up a bit.
; <4> 5/16/90 MSH Added hasPowerControls to hcmac conditional.
; <3> 1/18/90 JWK Needed for Zone5 - Fix SCSIStat to avoid accessing the SCSIDMA
; chip while DMAing.
; <2> 1/11/90 CCH Added include of “HardwarePrivateEqu.a”.
; <1.3> 9/11/89 jwk NEEDED FOR F19 - Added support for Mac SE and Mac Plus
; <1.2> 7/15/89 jwk NEEDED FOR AURORA - Added code review changes, routines for F19
; <1.1> 6/29/89 jwk NEEDED FOR AURORA - Fixed Tape Drive and SlowWrite timing bugs
; <1.0> 6/13/89 jwk Reorganizing SCSI sources to be Universal-ROM friendly
; <2.9> 5/29/89 GGD Fixed bug in DMA chip initialization code, unitialized base
; register.
; <2.8> 5/26/89 GGD Turned off hasNewSCSI for all machines. Converted to feature
; based conditionals. Added runtime tests for SCSI DMA chip
; support.
; <2.7> 5/16/89 GGD Merged the changes from rev 2.5 back in, after rev 2.6 blew them
; away.
; <•2.6> 5/15/89 MSH Had Jerry help do the JVC hack removal with me. Also fixed the
; Sleep Task that I thought was fixed a long time ago.
; <2.5> 4/30/89 GGD Converted interrupt enable/disable/clear/install code to be more
; universal, and to call routines in IntHnd instead.
; <2.4> 3/31/89 MSH DisEnable affects interrupts only if "hasNewSCSI".
; <2.3> 3/15/89 jwk Fixing minor bugs to make HD SC Setup work.
; <2.2> 3/9/89 MSH Hacked problem the hdsetup has with bus clearing intil jwk can
; fix it.
; <2.1> 3/6/89 MSH A0 did not have the correct pointer in it.
; <2.0> 3/6/89 GGD Changed usage of RBV register equates, since they are now
; offsets from the RBV base, instead of absolute addresses.
; <1.9> 3/3/89 MSH Moved loading of A0 to after the hcmac specific code.
; <1.8> 3/2/89 jwk Cleaning up async SCSI Mgr roll in
; <1.7> 2/28/89 jwk Continuing to roll in async SCSI Mgr
; <1.6> 2/27/89 jwk Rolling async SCSI Mgr
; <1.5> 2/8/89 MSH Sleep task now goes through hdvector to do spin down. LastHd
; gets set to -1
; <1.4> 12/14/88 MSH Load lasthd with ticks when scsiget called.
; <1.3> 11/23/88 rwh at RdDone label, changed sense of *REQ test for DMA chip.
; <1.2> 11/21/88 CCH Changed ForRam equates to ForRom.
; <1.1> 11/10/88 CCH Fixed Header.
; <1.0> 11/9/88 CCH Adding to EASE.
; <2.7> 11/7/88 rwh conditionaled out onMvMac stuff that wasn't on build system
; MvMac sources.
; <2.6> 11/6/88 GGD Added temporary change for IoMac to use CA2 instead of CB2
; <2.5> 10/17/88 jwk Fixed up "onNuMac" problems.
; <2.4> 10/11/88 jwk Added more interrupt-related equates for DisEnable.
; <2.3> 10/11/88 jwk Added interrupt-related equates for DisEnable.
; <2.2> 10/10/88 jwk SCSIGet disables interrupts, enabled after ClearState is called.
; <2.1> 9/29/88 MSH Put in missing ENDWITH at end of file and put ugly delay back
; in. Also took
; <2.0> 9/28/88 jwk Additional fixes, new register convention related to PMAB574
; <1.9> 9/28/88 jwk Rolled in PMAB574, enhancing old SCSI Mgr traps to support new
; SCSI Mgr
; <•1.8> 9/23/88 CCH Got rid of inc.sum.d and empty nFiles
; <1.7> 9/22/88 jwk Finished rolling patch PMAB466.
; <1.6> 9/22/88 jwk Rolled patch PMAB466 to perform cleaner arbitration.
; <1.5> 9/21/88 MSH Added kludge on power up and power down to keep drives happy.
; <1.4> 9/8/88 MSH New version of spin down support.
; <1.3> 8/8/88 MSH Added sleep queue support.
; <Cxxx> 7/25/88 JWK Added "Sleep queue" handling to SCSI Manager. When going to
; sleep, a Stop command is sent and power is turned off, if
; necessary. Otherwise, nothing is done. When waking up, nothing
; is done.
; <1.2> 7/19/88 MSH JWK updated HcMac version for hard disk power control.
; <Cxxx> 7/11/88 JWK Added changes for onHcMac power-cycling conditions. This
; included: Adding a Time Mgr task for shutting off the power,
; sending a START to ID 0 when SCSIGet is called, sending a STOP
; to ID 0 when the timer goes off, and starting the timer when
; SCSIComplete is done.
; <1.1> 4/18/88 CSL Added support for JVC drive for HcMac
; <1.0> 2/10/88 BBM Adding file for the first time into EASE…
; <C936> 11/6/87 JWK Rolled in patches
; PB071,PM075,PA081,S089,PM116,PM135,PM180,PB208,PMA211,
; PB220,PP296 from ROM75Fix,ROM76Fix,ROM78Fix,ROMAllFix Did not
; roll in PMAB295 -- final pass through SCSILoad for CD-ROM; It is
; intended to be executed after all the patches are loaded.
; <C896> 9/30/87 MSH Port to HcMac (Laguna)
; <C865> 8/17/87 SHF Rolled in patches from PatchIIROM.a: minor fix to RdDone; delay
; before retrying instruction which caused a bus error.
; <C859> 7/2/87 SHF Changed so new SE ROMs use the TIMESCSIDB low-mem value. Fixed
; minor bugs in TIB interpreter.
; <C853> 5/14/87 SHF Fixed minor timing-sensitive quirk in RdDone.
; <C846> 4/28/87 SHF Rolled in SCSI Mgr patches.
; <C789> 2/11/87 SHF Modified the bus error handling in DataXfer routine.
; <C780> 2/10/87 SHF Uses new TimeSCSIDB instead of TimeSCCDB for better timing.
; <C732> 2/2/87 SHF Clean up from code audit, fixed busdelay timing.
; <C718> 1/29/87 SHF Increased timeouts in wfReq and wfnReq to fix problems with
; slower devices.
; <C644> 1/15/87 SHF Don't do a SCSIReset on bootup if a bit in parameter RAM is set.
; <C478> 12/10/86 SHF Many changes for Becks ROM.
; <C406> 11/12/86 SHF Fix bug with using DACKRd on NuMac (doesn't work for Rev. 8
; boards) -- use SCSIDMA global now.
; <A395> 11/10/86 SHF Turn DMA mode back off after SCSIRead.
; <A370> 11/7/86 SHF Changed SCSITimedSel function to SCSISelAtn, commented out
; address masking for onNuMac and TMac.
; <A356> 11/6/86 SHF Fixed error-handling bug in DoSCSIComplete, bugs in blind write
; subroutine.
; <A349> 11/4/86 SHF Made CyclePhase properly return an error code.
; <A342> 11/3/86 SHF Removed unnecessary state variable checks.
; <A324> 11/1/86 SHF Fixed PRAM initialization bug for SCSI ID.
; <A303> 10/30/86 SHF Fix Aladdin bug with block sizes > 4MB, fine tune NuMac data
; transfer code a little more, mask high byte on addresses for
; 32-bit mode on NuMac and TMac.
; <A300> 10/29/86 SHF Gets SCSI ID of CPU from parameter RAM now.
; <A244> 10/18/86 SHF Handles bus errors during data transfer, uses global data area
; set up by InitSCSIMgr (called by InitIO), other numerous
; changes, bug fixes, and timing cleanups.
; <A211> 10/16/86 SHF Uses TimeSCCDB for most timing now; some conditional sections
; for transitional code.
; <C189> 10/1/86 SHF Clear d0 (error register) after blind mode transfer
; <C186> 9/30/86 SHF Fixed timeout register bug in Arbitrate.
; <C138> 9/29/86 SHF Uses Ticks for timeouts, Aladdin async. changes
; <C116> 8/11/86 SHF Unrolled loops for MacPP (Aladdin) to do 2:1 interleave
; <C85> 7/25/86 SHF Added check for not BSY in SCSIGet
; <C35> 6/2/86 SHF Fixed read register bug (on DMA reads)
; <C28> 5/30/86 CSL Added changes for Aladdin project (onMacPP), and Aladdin Front
; desk bus support.
; <C26> 5/28/86 SHF Rolled in SCSI patches (formerly SCSI Helper)
; <C1> 4/15/86 RDC Added equate and changes for new 68020 Reno project (NuMac) -
; deleted old code - Added slowdown changes and eliminate blind
; read/write
; 2/19/86 BBM Made some modifications to work under MPW
; 10/29/85 ELR Added compare function to the microinterpreter.
; 10/28/85 ELR Lowered the wait on /SEL a little to improve boot performance.
; 10/28/85 ELR Added code for a SCSIStat function.
; 10/27/85 ELR Removed the scsibootequ because it is now in newequ.
; 10/24/85 ELR Added the read/write program block interpreter to make data
; transfer much more flexible.
; 10/23/85 ELR Vastly simplify data Xfer operations in anticipation of command
; block support
; 9/27/85 ELR Now returns proper phase error on reads/writes...
; 9/18/85 ELR Added support for blind reads/writes on Mac.
; 9/17/85 RDC Added fix for handling > 127 block request
; 9/16/85 RDC Added changes for MidMac
; 9/13/85 RDC Added include of HWequ.text which now has SCSI equ's
; 9/10/85 ELR Changed StdEntry/StdExit to be more code efficient.
; 9/9/85 ELR Fixed Select so the bit ID selection is correct.
; 9/6/85 ELR New Today.
;
; To Do:
;
;-----------------------------------------------------------
;
; SCSI Manager for the Macintosh
;
; Written by Erich Ringewald
;
; (C) Copyright Apple Computer, Inc. 1985, 1986, 1987
; All Rights Reserved.
;
;-----------------------------------------------------------
BLANKS ON
STRING ASIS
PRINT OFF
LOAD 'StandardEqu.d'
INCLUDE 'HardwarePrivateEqu.a'
INCLUDE 'PowerPrivEqu.a'
INCLUDE 'UniversalEqu.a' ; <5> rb
INCLUDE 'SCSI.a'
INCLUDE 'SCSIPriv.a'
PRINT ON
SCSIOld PROC EXPORT
EXPORT SCSIMgr ; the trap entry point
EXPORT DoSCSIReset, DoSCSIGet, DoSCSISelect, DoSCSICmd, DoSCSIComplete
EXPORT DoSCSIStat, DoSCSISelAtn, DoSCSIMsgIn, DoSCSIMsgOut
EXPORT NewSCSIRead, NewSCSIWrite, Unimplemented, NewSCSIRBlind, NewSCSIWBlind ;<v1.2>
EXPORT SCSIExitCleanup,SCSIStdExit, Select, Arbitrate, NoByteExit ;<H2>
WITH scsiPB,scsiPrivPB, scsiGlobalRecord, dcInstr, machSpecRecord, PmgrRec
;-------------------------------------------------------------
;
; A SCSI Function request handler. SCSI's are invoked by params on
; stack followed by a routine selector Int, and return address.
; NOTE: SCSIMgr is a Toolbox trap & follows Pascal calling conventions.
;
; A4 is set up by StdEntry and contains a pointer to the SCSI Manager global
; area in the system heap.
;
SCSIMgr: ; "Normal" 680x0 CPU's with "SCSIGlobals" lomem
move.l (sp)+,a0 ; get the return address
move.w (sp)+,d0 ; function selector (word)
move.l a0,-(sp) ; push the return address
cmp.w #numSelectors,d0 ; valid selector? <C936/PM075/06Nov87> <v1.9>
bhs.s Unimplemented ; Sorry, Charlie <C936/PM075/06Nov87>
link a6,#LocalSize ; locals for bus error stuff
movem.l a2-a5/d2-d7,-(sp) ; save registers <v1.9><v3.0>
movea.l SCSIGlobals,a4 ; get pointer to SCSI Manager globals <v1.7>
scsiMgrCommon
move.l base5380(a4),a3 ; <A244/27Oct86> <v1.9>
moveq.l #0,zeroReg
moveq.l #0,d4 ; needed by read/write <v2.0>
move.w d0,d1 ; save a copy of selector
move.l 0(a4,d0.w*4),a0 ; indexed addressing <C846>
jmp (a0) ; and go there
;----------------------------------------------------------------
;
; Unimplemented selector (or out of range)
;
;----------------------------------------------------------------
Unimplemented
moveq.l #dsCoreErr,d0 ; unimplemented core routine
_SysError ; sorry, Charlie
@1 bra.s @1 ; not safe to continue
;--------------------------------------------------------------------------
;
; FUNCTION SCSIStat: INTEGER;
; (8)
;
; Get SCSI bus status.
;
; NOTE: With the SCSIDMA chip, it's necessary to avoid touching the chip while a <3>
; DMA operation is in progress. The routines that use the chip's DMA <3>
; capabilities set the "doingDMA" bit to inform this routine that the chip is <3>
; in use, and "Do Not Disturb". We fake a "busy" status to keep them going. <3>
;
; NOTE: If a driver is spinning waiting for its turn after a SCSIGet, we <v1.9>
; fabricate a "busy" status in order to keep VBL's from sitting <start>
; and spinning.
;
; As an example, assume a hard disk driver is currently waiting to for the
; SCSI bus. (This means the dummy SCSI PB is in the queue.) If a CD-ROM
; driver, at interrupt level, checks the bus, we fake a "busy" status to
; keep him from calling SCSIGet, which would fail.
;
DoSCSIStat
btst.b #scPending,G_State(a4) ; is an old request waiting to start ?
bne.s pending ; an old request is waiting in SCSIGet <v1.2>
btst.b #doingDMA,state1(a4) ; is the SCSIDMA chip performing DMA ? <3>
bne.s pending ; if so, don't hit the chip <3>
@normal
move.b sBSR(a3),8(a6) ; get real Bus and Status contents
move.b sCSR(a3),9(a6) ; get real Current SCSI Bus Status contents
bra.s NoByteExit ; <v1.2>
pending
move.b #iPM,8(a6) ; faked Bus and Status register (Phase Match) <end>
move.b #aBSY+aIO,9(a6) ; faked Current SCSI Bus Status (Data In Phase) <v1.9>
bra.s NoByteExit
;--------------------------------------------------------------------------
;
; FUNCTION SCSIReset: INTEGER;
; (8)
;
; Hard reset the SCSI bus.
;
DoSCSIReset
movea.l jvResetBus(a4),a0 ; get address of SCSI reset routine <v1.9>
jsr (a0) ; reset SCSI bus and kill all requests <v1.9>
move.w zeroReg,8(a6) ; always returns 0 status
bsr.s SCSIExitCleanup ; clean up to leave <SM6> CSS <v1.2>
NoByteExit
moveq.l #0,d0 ; no arguments to clean up
bra.w SCSIStdExit ; <SM6> CSS
;
; Common code when exiting old SCSI Mgr after an error <v1.2>
;
SCSIExitCleanup: ; <SM6> CSS
moveq.l #scsiOldSCSIErr,d0 ; indicate that old SCSI Mgr is calling <v1.2>
movea.l jvClearState(a4),a0 ; get addr of clear SCSI Mgr state routine <v1.9><v1.2>
jsr (a0) ; go to it <v1.9><v1.2>
movea.l jvClearIRQ(a4),a0 ; addr of "clear SCSI interrupt" routine <v1.2>
jsr (a0) ; clear the interrupt <v1.2>
moveq.l #1,d0 ; enable SCSI interrupts <v1.2>
movea.l jvDisEnable(a4),a0 ; addr of SCSI intr enable/disable routine <v2.2><v1.2>
jsr (a0) ; enable interrupts <end><v1.2>
rts
;--------------------------------------------------------------------------
;
; FUNCTION SCSIGet: INTEGER;
; (8)
;
; Arbitrate for the SCSI bus. Returns 0 for success, or error.
;
DoSCSIGet
bset.b #scBusy,G_State(a4) ; wait for the free state <v1.6>
bne.s @wasBusy ; if nonzero, SCSI Mgr was in use <v1.2>
move.w sr,-(sp) ; hold onto status register
ori.w #HiIntMask,sr ; lock out interrupts during critical section
move.w (sp),d0 ; get original status register contents
andi.w #HiIntMask,d0 ; mask out all but the interrupt level bits
beq.s @enqueueDummy ; not at interrupt level, so continue normally
tst.l scsiQHead(a4) ; is the queue currently empty ?
beq.s @enqueueDummy ; if so, we'll be at the front
@notFirst
move.w (sp)+,sr ; restore interrupt level <v1.2>
@wasBusy
moveq.l #scMgrBusyErr,d0 ; pretend SCSI Mgr was not free <v1.2>
bra.s @exit ; bail out in order to avoid deadlock <v1.2>
@enqueueDummy
lea.l dummy(a4),a1 ; point to placeholder SCSI request
moveq.l #enqNormal,d0 ; normal enqueue call
movea.l jvEnDequeue(a4),a0 ; addr of enqueue/dequeue routine
jsr (a0) ; stick the placeholder in the queue
move.w (sp)+,sr ; restore interrupt level
@wait
cmpa.l scsiQHead(a4),a1 ; are we on the front of the queue ?
bne.s @wait ; if so, keep waiting
@arbitrate
move.l zeroReg,d0 ; disable SCSI interrupts with old SCSI Mgr <v2.2>
movea.l jvDisEnable(a4),a0 ; addr of interrupt enable/disable routine <v2.2>
jsr (a0) ; disable interrupts <v2.2>
TestFor hwCbPwrMgr ; do we have a power manager? <5> rb
beq.s @PwrDone ; <5> rb
_SpinUPHardDrive
@PwrDone
bsr.w Arbitrate
beq.s @exit ; branch if no error
@error
move.l d0,-(sp) ; save d0
bsr.s SCSIExitCleanup ; clean up to leave <SM6> CSS <v1.2>
move.l (sp)+,d0 ; restore d0
@exit
move.w d0,8(a6) ; return code
bra.w NoByteExit
;--------------------------------------------------------------------------
;
; FUNCTION SCSISelect(TargID:INTEGER): INTEGER;
; FUNCTION SCSISelAtn(TargID:INTEGER): INTEGER; <A370/07Nov86>
; (8) (10)
;
; Select the target on the bus. Returns 0 for success, or error.
; Selection can be done with or without the ATN line.
DoSCSISelect
DoSCSISelAtn
moveq.l #0,d0 ; clear upper bits <v1.9>
move.w 8(a6),d0 ; get the target's ID
cmp.w #scsiSelAtn,d1 ; select with ATN? <A370/07Nov86>
bne.s @noATN ; no ATN <A370/07Nov86>
bset.l #31,d0 ; use ATN <A370/07Nov86>
@noATN
bsr.w Select ; try it
beq.s @exit ; branch if no error
@error
move.l d0,-(sp) ; save d0
bsr.w SCSIExitCleanup ; clean up to leave <SM6> CSS <v1.2>
move.l (sp)+,d0 ; restore d0
@exit
move.w d0,10(a6) ; return the result
moveq.l #2,d0 ; stack cleanup
bra.w SCSIStdExit ; <SM6> CSS
;--------------------------------------------------------------------------
;
; FUNCTION SCSICmd(Buffer:Ptr, Count:INTEGER): INTEGER;
; (10) (8) (14)
;
; Send the target the given command. Returns 0 for success, or error.
;
DoSCSICmd
move.l 10(a6),a2 ; get command buffer address <v1.9>
move.w 8(a6),d2 ; get the length <start>
bsr.w SendCMD ; send it
beq.s @exit ; OK <A342/03Nov86>
@error
move.l d0,-(sp) ; save d0
bsr.w SCSIExitCleanup ; clean up to leave <SM6> CSS <v1.2>
move.l (sp)+,d0 ; restore d0
@exit
move.w d0,14(a6) ; return the result
moveq.l #6,d0
bra SCSIStdExit ; <SM6> CSS
;
; SCSIRead, SCSIRBlind, SCSIWrite, SCSIWBlind
;
; Called by: Toolbox Trap Dispatcher
;
; Calls: Transfer, primitive data transfer routines
;
; On entry: a3 - SCSI read base address
; a4 - pointer to SCSI Manager globals
;
; Function: Performs TIB interpretation, and data transfers.
;
;patch to NewSCSIWBlind rolled in from PatchIIciROM.a <SM4><SES>
;_________________________________________________________________________________________
; Beginning code for Quantum 7.9 ROM fix
;
; Detailed description of the Quantum problem and solution:
;
; Quantum drives with the firmware version 7.9, has a problem with loosing the last byte
; of a block during a multi-block write. The problem occurs when the time between blocks
; written to the SCSI bus on the CPU side, is greater than 482 microseconds. This may
; occur in situations where there are a lot of interrupts. When conditions are right and
; the problem happens, the last byte of the previous block (in a multi-block transaction),
; is "eaten" by the drive. Any checksum or CRC calculated by the drive is correct,
; because the drive calculates it after the byte has been corrupted. This problem is
; especially frequent when email packages are installed on the Mac, since they generate
; a lot of interrupts which take a long time. This problem only occurs during fast
; writes in the SCSI manager (pseudo-dma mode).
;
; The way the SCSI manager currently works is it gets a TIB packet which contains the
; instructions on how to talk to a particular device. Included in the TIB is when to
; re-synchronize with the drive by waiting for a *REQ. When we see *REQ, the SCSI
; manager then begins the next TIB data transfer instruction and loads a byte of data
; into the 53C80's output register. After the first byte is "manually" sent, the
; hardware handshaking automatically takes care of the *REQ and *ACK handshaking. When
; the TIB write transfer is complete, pseudo-dma is disable, which releases *ACK,
; completing the handshake. This allows the target to assert *REQ when it is ready.
;
; The window of vulnerability is between the last byte of the previous block and the
; first byte of the next block. More correctly, it is between the rising edge of *ACK
; and the falling edge of the next *ACK. Because we release *ACK to synchronize, an
; interrupt may come in and delay the next transfer.
;
; The solution to the problem is to pre-load a data byte into the 53C80's data output
; register before we release *ACK. There are two ways of releasing *ACK: disable pseudo
; dma, and write a byte to the data output register. Leaving pseudo-dma enabled through
; the entire TIB will mean that whenever a *REQ occurs, there will be data available in
; the output register. This means there will be no delay between bytes because the
; hardware is not subject to interrupt delays.
;
; This patch therefore involves patching the TIB interpreter and the fast write routine
; in the SCSI manager. There are five versions of the SCSI manager to patch: Mac Plus,
; SE, Mac II, Portable, and IIci.
;
;_________________________________________________________________________________________
; QuantumWBlindIIci - patch to NewSCSIWBlind
;
; This code replaces the original NewSCSIWBlind entry point. The new entry for blind
; writes enables pseudo-dma on a per-transaction basis. Pseudo-dma was previously
; enabled only on a per-TIB-instruction basis. We jump back into the ROM to the original
; entry point to continue executing the TIB.
;
; Input: reg a3 = base of SCSI read addr
; a6 = SCSI stack frame
; d7 = zero
;
NewSCSIWBlind
move.b d7,sTCR+WrOffs(a3) ; set to match data out phase (to zero) <SM4><SES>
move.b #iDMA,sMR+WrOffs(a3) ; enable DMA in mode register <SM4><SES>
move.b #iDB,sICR+WrOffs(a3) ; assert data bus in initiator command reg <SM4><SES>
move.b d7,sDMAtx+WrOffs(a3) ; start write DMA <SM4><SES>
; Do I need to test for DRQ or *REQ at this point?
;end of NewSCSIWBlind patch rollin <SM4><SES>
moveq.l #scsiWriteFast,d4 ; select the fast write routine
bra.s dataCommon ; continue
NewSCSIWrite
moveq.l #scsiWriteSlow,d4 ; select the slow write routine
bra.s dataCommon ; continue
NewSCSIRBlind
moveq.l #scsiReadFast,d4 ; select the fast read routine
bra.s startRead ; continue
NewSCSIRead
moveq.l #scsiReadSlow,d4 ; select the slow read routine
startRead
move.b #iIO,sTCR+WrOffs(a3) ; match Data In <C478/10Dec86>
move.b #iDMA,sMR+WrOffs(a3) ; set DMA mode <C478/10Dec86>
btst.b #sSCSIDMAExists,G_Reserved0+3(a4) ; do we have a SCSI DMA chip ? <2.8>
beq.s @DMAdone ; if not, skip it <2.8>
move.l #iHSKEN,sDCTRL(a3) ; turn on hhsk <C914>
@DMAdone ; <2.8>
move.b zeroReg,sIDMArx+WrOffs(a3) ; start read DMA <C478/10Dec86>
dataCommon
move.l 8(a6),a1 ; get the TIB pointer
bra.s @exec ; tighten loop by branching first
@c_compare
bset.l #scsiCompBit,d4 ; add offset to use the compare routines
; FALL THROUGH to @c_inc ; continue
@c_inc
movea.l jvTransfer(a4),a0 ; get the address of the transfer routine
jsr (a0) ; go to the transfer routine
bne.s @data_end ; if error, bail out
add.l d1,scParam1(a1) ; increment the pointer
bclr.l #scsiCompBit,d4 ; remove offset for the compare routines
; FALL THROUGH to @next_cmd ; continue
@next_cmd
@c_nop ; also NOP, just skip the command
add.w #scSize,a1 ; point to the next TIB instruction
; FALL THROUGH to @exec
@exec
move.w scOpcode(a1),d1 ; get the function opcode
move.l scParam1(a1),a2 ; get the generic address
move.l scParam2(a1),d2 ; get the generic count
cmp.w #maxOpcode,d1 ; valid opcode ?
bhi.s @c_badop ; return err if not
jmp @JmpTable(d1.w*2) ; jump into table <C846>
@JmpTable ; <C846>
bra.s @c_badop ; 0 is not a valid opcode
bra.s @c_inc ; 1
bra.s @c_noinc ; 2
bra.s @c_add ; 3
bra.s @c_move ; 4
bra.s @c_loop ; 5
bra.s @c_nop ; 6
bra.s @c_stop ; 7
bra.s @c_compare ; 8
@c_badop
moveq.l #scBadparmsErr,d0 ; bad opcode
bra.s @data_end
@c_noinc ; NOINC addr,count
movea.l jvTransfer(a4),a0 ; get the address of the transfer routine
jsr (a0) ; go to the transfer routine
bne.s @data_end ; if error, exit
bra.s @next_cmd ; else process next command
@c_add ; ADD addr,data
add.l d2,(a2) ; the count added to the where
bra.s @next_cmd ; process the next command
@c_move ; MOVE addr1,addr2
move.l d2,a0 ; get the destination address
move.l (a2),(a0) ; simple enough
bra.s @next_cmd ; process the next command
@c_loop ; LOOP relative addr,count
tst.l d2 ; check for zero loop count
beq.s @next_cmd ; if count is already zero, quit loop
subq.l #1,d2 ; drop the count
move.l d2,scParam2(a1) ; put the count back for next time
beq.s @next_cmd ; if count exhausted, don't loop <C859>
add.l a2,a1 ; modify the command pointer
bra.s @exec ; and process the next command
@c_stop
moveq.l #noErr,d0 ; indicate no error
; FALL THROUGH to @data_end ; <C846>
@data_end
move.b zeroReg,sMR+WrOffs(a3) ; clear DMA mode <A395/10Nov86>
btst.b #sSCSIDMAExists,G_Reserved0+3(a4) ; do we have a SCSI DMA chip ? <2.8>
beq.s @DMAdone ; if not, skip it <2.8>
move.l #iINTREN,sDCTRL(a3) ; turn off hhsk <C914> <v1.2>
@DMAdone ; <2.8>
move.b zeroReg,sICR+WrOffs(a3) ; disable data bus
move.w d0,12(a6) ; return the result
moveq.l #4,d0 ; stack cleanup
bra.w SCSIStdExit ; <SM6> CSS
;--------------------------------------------------------------------------
;
; FUNCTION SCSIComplete(VAR Stat, Message: INTEGER; wait:LongInt): INTEGER;
; (16) (12) (8) (20)
;
; Complete the SCSI command. Returns 0 for success, or error.
;
DoSCSIComplete
move.w zeroReg,-(sp) ; return value <A349/04Nov86>
move.b zeroReg,sMR+WrOffs(a3) ; clear DMA mode (for safety)
btst.b #sSCSIDMAExists,G_Reserved0+3(a4) ; do we have a SCSI DMA chip ? <2.8>
beq.s @DMAdone ; if not, skip it <2.8>
move.l #iINTREN,sDCTRL(a3) ; turn of hhsk <C914> <v1.2>
@DMAdone ; <2.8>
move.l Ticks,d4 ; get current time <v2.0>
add.l 8(a6),d4 ; add count to start ticks <start>
@chkStatus
moveq.l #0,d2
bsr GetStat ; try to get the status
beq.s @gotStatus ; worked OK
cmp.b #scPhaseErr,d0 ; phase error?
bne.s @checkTime ; no, business as usual
bsr CyclePhase ; only called for phase errors
beq.s @checkTime ; keep (sp) error <A356/06Nov86>
move.w d0,(sp) ; save new error <A356/06Nov86>
@checkTime
cmp.l Ticks,d4 ; got the time, pal? <v2.0>
bhi.s @chkStatus ; still time <A356/06Nov86>
bra.s @error ; report the error <A356/06Nov86>
@gotStatus
move.l 16(a6),a1 ; get the Status ptr
move.w d2,(a1)
@chkMsg
bsr GetMsg
beq.s @gotMsg ; no error <A349/04Nov86>
@error
move.w d0,(sp) ; save error <A349/04Nov86>
bra.s @completeDone ; finish up <C789>
@gotMsg
move.l 12(a6),a1 ; get the Message ptr
move.w d2,(a1) ; return the Message byte
@completeDone
move.w (sp)+,20(a6) ; return error <v1.2>
bsr.w SCSIExitCleanup ; clean up to leave <SM6> CSS <v1.2>
TestFor hwCbPwrMgr ; do we have a power manager? <5> rb
beq.s @PwrDone2 ; if not, skip
;
; This portion of code "starts" the "Spin Down" timer for the hard disk.
; The timer value is the value of Ticks when the last SCSI I/O completed.
; Whenever the SCSI bus is used, the timer is reset. <v1.1>
; When it goes off, "DoSpinDown" turns off the hard drive power. <v1.1>
;
_HDIdleUpdate
@PwrDone2
moveq.l #12,d0 ; stack cleanup
bra.s SCSIStdExit ; <SM6> CSS
;--------------------------------------------------------------------------
;
; FUNCTION SCSIMsgIn(VAR Message: INTEGER): INTEGER;
; (8) (12)
;
; Receive a message from the target. Returns 0 for success, or error.
;
DoSCSIMsgIn ; SCSIMsgIn added <C138/29Sep86>
bsr GetMsg
bne.s @exit ; comm error
move.l 8(a6),a1 ; get the Message ptr
move.w d2,(a1) ; return the Message byte
@exit
move.w d0,12(a6) ; return error
moveq.l #4,d0 ; stack cleanup
bra.s SCSIStdExit ; <SM6> CSS
;--------------------------------------------------------------------------
;
; FUNCTION SCSIMsgOut(Message: INTEGER): INTEGER;
; (8) (10)
;
; Send a message to the target. Returns 0 for success, or error.
;
DoSCSIMsgOut ; SCSIMsgOut added <C138/29Sep86>
move.w 8(a6),d2 ; get the Message in d2
bsr SendMsg
move.w d0,10(a6) ; return error
moveq.l #2,d0 ; stack cleanup
; FALL THROUGH
SCSIStdExit ; <SM6> CSS
movem.l (sp)+,a2-a5/d2-d7 ; restore these guys <v3.0>
unlk a6
move.l (sp)+,a0 ; get return address
add.w d0,sp ; fixup the stack
jmp (a0)
;-------------------------------------------------------------------
;
; Arbitrate for the SCSI bus.
;
; Algorithm: The algorithm is taken from the NCR 5380 manual. <v1.2>
; We keep arbitrating until we win.
;
Arbitrate
move.b G_ID(a4),sODR+WrOffs(a3) ; put my ID on bus
ArbRetry
move.b zeroReg,sMR+WrOffs(a3) ; clear arbitration bit <C732>
moveq.l #0,d1 ; clear high word
move.w TimeSCSIDB,d1 ; DBRAs/ms. <C780>
lsl.l #8,d1 ; wait up to 256 ms <C780>
move.l d1,d5 ; get high word
swap d5
move.b #iARB,sMR+WrOffs(a3) ; tell 5380 to go into arbitration
@0
btst.b #bAIP,sICR(a3) ; check to make sure we're arbitrating <C780>
dbne d1,@0 ; loop until arb. started or time out
dbne d5,@0 ; high word of timeout
bne.s ArbStarted ; arbitration started
ArbErr
move.b zeroReg,sMR+WrOffs(a3) ; clear arbitration bit
moveq.l #scArbNBErr,d0 ; error code
rts
ArbStarted
bsr busdelay ; wait for bus to settle
btst.b #bLA,sICR(a3) ; did we lose arbitration? <C780>
bne.s ArbRetry
move.b sCDR(a3),d0 ; read data bus to check for higher priority dev
move.b G_ID(a4),d2
eor.b d2,d0 ; mask myid
cmp.b d0,d2 ; <v1.2>
blo.s ArbRetry ; branch if lost arb
btst.b #bLA,sICR(a3) ; look if we lost arbitration (again) <C780>
bne.s ArbRetry
moveq.l #noErr,d0 ; no error
rts
;----------------------------------------------------------------
;
; Select Device.
; Device ID is in d0. If bit 31 of d0 is set, then ATN is asserted
; during selection. This is useful for telling the target that we
; want to send it a message before the command phase.
;
Select
lea sICR+WrOffs(a3),a0 ; avoid later indexing
moveq.l #1,d1 ; build the select mask...
lsl.b d0,d1 ; shift the bit to the appropriate spot
move.l d0,d3 ; save ID and ATN flag
move.b #iSEL,(a0) ; set select
bsr busdelay ; wait for bus to settle
move.b G_ID(a4),d2 ; get ready to put myID and deviceID on bus
move.b zeroReg,sTCR+WrOffs(a3) ; de-assert *I/O to become an initiator
or.b d2,d1 ; merge myID and target ID
move.b d1,sODR+WrOffs(a3) ; get device select value
move.b #iBSY+iSEL+iDB,d4 ; issue *SEL, *BSY, assert data bus
tst.l d3 ; <C138/29Sep86> Select with ATN?
bpl.s @1 ; normal select
or.b #iATN,d4 ; select with ATN
@1
move.b d4,(a0) ; issue *SEL, *BSY, [*ATN], assert data bus
move.b zeroReg,sMR+WrOffs(a3) ; reset *ARB
move.b zeroReg,sSER+WrOffs(a3) ; clear select int. enable reg
bclr #3,d4 ; reset *BSY
move.b d4,(a0)
bsr.w busdelay ; wait for bus to settle
move.w G_Async(a4),d1 ; select timeout from globals (in ms) <v2.0>
bsr.w wfbsy ; wait for *BSY
bne.s @2 ; no BSY
bsr.w busdelay ; wait for bus to settle
and.b #iBSY+iATN,d4 ; reset *SEL and *DB bits <v1.2>
move.b d4,(a0)
moveq.l #noErr,d0 ; indicate success
rts
@2
move.b zeroReg,(a0) ; release the lines
tst.w d0
rts
;--------------------------------------------------------------------------
;
;
; Send Command. # bytes in d2. Command block pointer is in a2.
;
SendCMD
move.b #iCD,sTCR+WrOffs(a3) ; set phase match for command, dir=write
bsr wfREQ ; wait 256ms for *REQ <C846/PA081/03Mar87>
bne.s @SendCmdRts ; never a *REQ?
bsr.w CheckPhase ; everything in phase?
bne.s @SendCmdRts ; out of phase
subq.l #1,d2 ; the lovely dbra command...
@NextCmdByte
move.b (a2)+,sODR+WrOffs(a3) ; load command byte
move.b #iDB,sICR+WrOffs(a3) ; assert the data bus
bsr.w wfREQ ; wait 256ms for *REQ <C846/PA081/03Mar87>
bne.s @SendCmdRts ; nary a *REQ...
move.b #iACK+iDB,sICR+WrOffs(a3) ; set *ACK
bsr.w wfnREQ ; wait 256ms for no *REQ <C846/PA081/03Mar87>
bne.s @SendCmdRts ; didn't go away
move.b zeroReg,sICR+WrOffs(a3) ; deassert *ACK and *DB
dbra d2,@NextCmdByte ; do this for all the bytes
;
; This is a hack for the Tape Backup 40SC drive. It asserts an extra REQ on
; 10-byte commands, which appears as a request for an 11th byte (that's what
; the SCSI standard says!) On very fast CPU's, this extra REQ hangs over to
; the next phase of the transaction, causing a phase error, and causes the
; transaction to fail. This delay code will wait at least 256ms (<v1.2>)
; for a phase change before continuing. <v1.1>
;
move.l zeroReg,d0 ; clear upper word <v1.2>
move.w TimeSCSIDB,d0 ; get SCSI DBRA's/ms timing constant <v1.2>
lsl.l #8,d0 ; wait at least 256ms for the tape drive <v1.2>
move.l d0,d2 ; set up d2 as high word <v1.2>
swap d2 ; <v1.2>
@cmdHack
btst.b #bBSY,sCSR(a3) ; is BSY still asserted ? <v1.1>
beq.s @exit ; if not, leave <v1.1>
btst.b #bPM,sBSR(a3) ; are we still in phase ? <v1.1>
dbeq.w d0,@cmdHack ; loop until phase mismatch or timeout <v1.2>
dbeq.w d2,@cmdHack ; high word of timeout <v1.2>
@exit
moveq.l #noErr,d0 ; no error
@SendCmdRts
rts
;--------------------------------------------------------------------------
;
;
; read status into d2.
;
GetStat
move.b #iCD+iIO,sTCR+WrOffs(a3) ; set phase match for status
@0
bsr wfREQ ; wait for *REQ
bne.s @4 ; no req
bsr CheckPhase ; everything in phase
bne.s @4 ; oops
move.b (a3),d2 ; load status byte
move.b #iACK,sICR+WrOffs(a3) ; set *ACK
bsr wfnREQ ; wait for req to go away
bne.s @4 ; REQ didn't go away
move.b zeroReg,sICR+WrOffs(a3) ; deassert *ACK
moveq.l #noErr,d0
@4 rts
;--------------------------------------------------------------------------
;
; GetMsg - get a message byte from target
; - returned in d2
;
GetMsg
move.b #iMSG+iCD+iIO,sTCR+WrOffs(a3) ; set match for MSG phase
bsr wfREQ ; wait for *REQ
bne.s @4
bsr CheckPhase ; everything in phase
bne.s @4 ; oops
moveq.l #0,d2 ; clear upper bytes
@2 move.b (a3),d2 ; load message byte
move.b sICR(a3),d0 ; get ICR <C478/10Dec86>
bset.l #4,d0 ; set *ACK <C478/10Dec86>
move.b d0,sICR+WrOffs(a3) ; <C478/10Dec86>
bsr.s wfnREQ
bne.s @4
move.b sICR(a3),d0 ; get ICR <C478/10Dec86>
bclr.l #4,d0 ; clr *ACK <C478/10Dec86>
move.b d0,sICR+WrOffs(a3) ; <C478/10Dec86>
moveq.l #noErr,d0
@4
rts
;--------------------------------------------------------------------------
;
; SendMsg - Send a message byte to the target (in d2 on entry)
;
SendMsg ; SendMsg new <C138/29Sep86>
move.b #iMSG+iCD,sTCR+WrOffs(a3) ; phase for MSG OUT
bsr.s wfREQ ; wait for *REQ
bne.s @1
bsr CheckPhase ; everything in phase
bne.s @1 ; oops
move.b #iDB,sICR+WrOffs(a3) ; assert data bus, de-assert *ATN
move.b d2,sODR+WrOffs(a3) ; send message byte
move.b #iACK+iDB,sICR+WrOffs(a3) ; set *ACK and *DB
bsr.s wfnREQ
bne.s @1
move.b zeroReg,sICR+WrOffs(a3) ; deassert *ACK and *DB
moveq.l #noErr,d0
@1
rts
;--------------------------------------------------------------------------
;
; busdelay - delay routine for various SCSI protocol timing. Delays 1000/128
; = 7.8 microseconds. (The minimum delay values required range
; from 90 nanoseconds to 2.2 microseconds, so this should be
; sufficient for all of them.) <C732>
; WARNING: wipes out d0!
;
busdelay
move.w TimeDBRA,d0 ; get DBRAs/ms. <C732>
lsr.w #7,d0 ; divide by 128 <C780>
@1 dbra d0, @1
rts
;--------------------------------------------------------------------------
;
; WFBSY -- WAIT for BSY
; Timeout value in milliseconds is in d1.w. Clobbers d0,d1,d5
;
wfbsy ; timing reworked <A211/16Oct86>
mulu.w TimeSCSIDB,d1 ; get # of DBRAs <C780>
move.l d1,d5 ; set up d5 as high word
swap d5
moveq.l #0,d0 ; assume success
@1
btst.b #bBSY,sCSR(a3) ; test for *BSY
dbne d1,@1 ; loop until BSY or timeout
dbne d5,@1 ; high word of timeout
bne.s @2 ; BSY, so return OK
moveq.l #scCommErr,d0 ; return comm. error (timeout)
@2
tst.w d0
rts
;--------------------------------------------------------------------------
;
; WFNREQ -- WAIT for not *REQ
; Timeout value is 256 milliseconds. <C846/PA081/03Mar87> Clobbers d0,d1,d5
;
wfnREQ ; timing reworked <A211/16Oct86>
moveq.l #0,d1 ; clear upper word
move.w TimeSCSIDB,d1 ; get # of DBRAs <C780>
lsl.l #8,d1 ; multiply by 256
move.l d1,d5 ; set up d5 as high word
swap d5
moveq.l #0,d0 ; assume success
@1 btst.b #bREQ,sCSR(a3) ; test for *REQ
dbeq d1,@1 ; loop until not REQ or timeout
dbeq d5,@1 ; high word of timeout
beq.s @2 ; not REQ, so return OK
moveq.l #scCommErr,d0 ; return comm. error (timeout)
@2 tst.w d0
rts
;--------------------------------------------------------------------------
;
; WFREQ -- WAIT for *REQ
; Timeout value is 256 milliseconds. <C846/PA081/03Mar87> Clobbers d0,d1,d5
;
wfREQ ; timing reworked <A211/16Oct86>
moveq.l #0,d1 ; clear upper word
move.w TimeSCSIDB,d1 ; get # of DBRAs <C780>
lsl.l #8,d1 ; multiply by 256 <C936/PA081/03Mar87>
move.l d1,d5 ; set up d5 as high word
swap d5
moveq.l #0,d0 ; assume success
@1
btst.b #bREQ,sCSR(a3) ; test for *REQ
dbne d1,@1 ; loop until REQ or timeout
dbne d5,@1 ; high word of timeout
bne.s @2 ; REQ, so return OK
moveq.l #scCommErr,d0 ; return comm. error (timeout)
@2
tst.w d0
rts
;----------------------------------------------------------------
;
; CyclePhase -- we may have to discard data or write filler bytes to get to
; the desired phase
;
CyclePhase
move.w zeroReg,-(sp) ; initialize return value <A349/04Nov86>
CyclePhase1
bsr.s wfREQ ; wait for *REQ
beq.s @1 ; <A349/04Nov86>
move.w d0,(sp) ; save error code <A349/04Nov86>
bra.s CycleRts ; return error
@1
btst.b #bPM,sBSR(a3) ; check for phase match
bne.s CycleRts ; exit with (sp) value<A349/04Nov86><v1.2>
@MatchPhase ; <v1.2>
move.b sTCR(a3),d3 ; save our desired phase
move.b sCSR(a3),d2 ; get current phase
asr.l #2,d2
and.b #$07,d2
move.b d2,sTCR+WrOffs(a3) ; and match it
btst.b #bIO,sCSR(a3) ; direction in or out?
beq.s WriteFiller ; do write
move.b (a3),d2 ; else read byte
move.b #iACK,sICR+WrOffs(a3) ; set *ACK
bra.s CycleHndshk
WriteFiller
move.b #iDB,sICR+WrOffs(a3) ; assert the data bus
move.b #$EE,sODR+WrOffs(a3) ; write fill bytes <A349/04Nov86>
move.b #iACK+iDB,sICR+WrOffs(a3) ; set *ACK
CycleHndshk
move.w #scComplPhaseErr,(sp) ; tell caller we had to read/write junk
bsr.w wfnREQ ; let the xfer happen
bne.s CycleRts ; oops
move.b zeroReg,sICR+WrOffs(a3) ; deassert *ACK and *DB
move.b d3,sTCR+WrOffs(a3) ; replace original phase
bsr busdelay ; wait for bus to settle
btst.b #bPM,sBSR(a3) ; look if we now have a phase match
beq.s CyclePhase1 ; loop until we do...
CycleRts
move.w (sp)+,d0 ; get return code <A349/04Nov86>
rts
;----------------------------------------------------------
;
; CheckPhase. See if the phase is correct; if not, return a
; phase error.
;
CheckPhase
moveq.l #0,d0
btst.b #bPM,sBSR(a3) ; examine the phase
bne.s @0 ; -> phase matches
moveq.l #scPhaseErr,d0
@0
tst.w d0 ; set conditions
rts
ENDWITH
END