mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-12-28 16:31:01 +00:00
4325cdcc78
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included. The Tools directory, containing mostly junk, is also excluded.
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
|
|
|