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

1072 lines
41 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;-----------------------------------------------------------
;
; 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