mirror of
https://github.com/elliotnunn/sys7.1-doc-wip.git
synced 2024-12-12 04:29:09 +00:00
1072 lines
41 KiB
Plaintext
1072 lines
41 KiB
Plaintext
;-----------------------------------------------------------
|
||
;
|
||
; 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: don’t use not forROM, this file is only
|
||
; used in ROM builds, don’t 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
|
||
|