;----------------------------------------------------------- ; ; File: SCSIMgrOld.a ; ; Written by: Erich Ringewald ; ; Copyright: © 1985-1993 by Apple Computer, Inc. All rights reserved. ; ; Change History (most recent first): ; ; 12/13/93 PN Roll in KAOs and Horror changes to support Malcom and AJ ; machines. ; 2/5/93 CSS Rollin from Horror: ;

2/10/92 SWC Exported some labels so DBLite's SCSI Manager can use them. ; 11/3/92 SWC Changed SCSIEqu.a->SCSI.a. ; 6/20/92 PN Roll in patchIIciROM.a. Fix the NewSCSIBlind call ; 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. ; 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. ; 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… ; 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. ; 9/30/87 MSH Port to HcMac (Laguna) ; 8/17/87 SHF Rolled in patches from PatchIIROM.a: minor fix to RdDone; delay ; before retrying instruction which caused a bus error. ; 7/2/87 SHF Changed so new SE ROMs use the TIMESCSIDB low-mem value. Fixed ; minor bugs in TIB interpreter. ; 5/14/87 SHF Fixed minor timing-sensitive quirk in RdDone. ; 4/28/87 SHF Rolled in SCSI Mgr patches. ; 2/11/87 SHF Modified the bus error handling in DataXfer routine. ; 2/10/87 SHF Uses new TimeSCSIDB instead of TimeSCCDB for better timing. ; 2/2/87 SHF Clean up from code audit, fixed busdelay timing. ; 1/29/87 SHF Increased timeouts in wfReq and wfnReq to fix problems with ; slower devices. ; 1/15/87 SHF Don't do a SCSIReset on bootup if a bit in parameter RAM is set. ; 12/10/86 SHF Many changes for Becks ROM. ; 11/12/86 SHF Fix bug with using DACKRd on NuMac (doesn't work for Rev. 8 ; boards) -- use SCSIDMA global now. ; 11/10/86 SHF Turn DMA mode back off after SCSIRead. ; 11/7/86 SHF Changed SCSITimedSel function to SCSISelAtn, commented out ; address masking for onNuMac and TMac. ; 11/6/86 SHF Fixed error-handling bug in DoSCSIComplete, bugs in blind write ; subroutine. ; 11/4/86 SHF Made CyclePhase properly return an error code. ; 11/3/86 SHF Removed unnecessary state variable checks. ; 11/1/86 SHF Fixed PRAM initialization bug for SCSI ID. ; 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. ; 10/29/86 SHF Gets SCSI ID of CPU from parameter RAM now. ; 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. ; 10/16/86 SHF Uses TimeSCCDB for most timing now; some conditional sections ; for transitional code. ; 10/1/86 SHF Clear d0 (error register) after blind mode transfer ; 9/30/86 SHF Fixed timeout register bug in Arbitrate. ; 9/29/86 SHF Uses Ticks for timeouts, Aladdin async. changes ; 8/11/86 SHF Unrolled loops for MacPP (Aladdin) to do 2:1 interleave ; 7/25/86 SHF Added check for not BSY in SCSIGet ; 6/2/86 SHF Fixed read register bug (on DMA reads) ; 5/30/86 CSL Added changes for Aladdin project (onMacPP), and Aladdin Front ; desk bus support. ; 5/28/86 SHF Rolled in SCSI patches (formerly SCSI Helper) ; 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 ; EXPORT SCSIExitCleanup,SCSIStdExit, Select, Arbitrate, NoByteExit ;

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? bhs.s Unimplemented ; Sorry, Charlie link a6,#LocalSize ; locals for bus error stuff movem.l a2-a5/d2-d7,-(sp) ; save registers movea.l SCSIGlobals,a4 ; get pointer to SCSI Manager globals scsiMgrCommon move.l base5380(a4),a3 ; moveq.l #0,zeroReg moveq.l #0,d4 ; needed by read/write move.w d0,d1 ; save a copy of selector move.l 0(a4,d0.w*4),a0 ; indexed addressing 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 ; fabricate a "busy" status in order to keep VBL's from sitting ; 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 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 ; pending move.b #iPM,8(a6) ; faked Bus and Status register (Phase Match) move.b #aBSY+aIO,9(a6) ; faked Current SCSI Bus Status (Data In Phase) bra.s NoByteExit ;-------------------------------------------------------------------------- ; ; FUNCTION SCSIReset: INTEGER; ; (8) ; ; Hard reset the SCSI bus. ; DoSCSIReset movea.l jvResetBus(a4),a0 ; get address of SCSI reset routine jsr (a0) ; reset SCSI bus and kill all requests move.w zeroReg,8(a6) ; always returns 0 status bsr.s SCSIExitCleanup ; clean up to leave CSS NoByteExit moveq.l #0,d0 ; no arguments to clean up bra.w SCSIStdExit ; CSS ; ; Common code when exiting old SCSI Mgr after an error ; SCSIExitCleanup: ; CSS moveq.l #scsiOldSCSIErr,d0 ; indicate that old SCSI Mgr is calling movea.l jvClearState(a4),a0 ; get addr of clear SCSI Mgr state routine jsr (a0) ; go to it movea.l jvClearIRQ(a4),a0 ; addr of "clear SCSI interrupt" routine jsr (a0) ; clear the interrupt moveq.l #1,d0 ; enable SCSI interrupts movea.l jvDisEnable(a4),a0 ; addr of SCSI intr enable/disable routine jsr (a0) ; enable interrupts 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 bne.s @wasBusy ; if nonzero, SCSI Mgr was in use 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 @wasBusy moveq.l #scMgrBusyErr,d0 ; pretend SCSI Mgr was not free bra.s @exit ; bail out in order to avoid deadlock @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 movea.l jvDisEnable(a4),a0 ; addr of interrupt enable/disable routine jsr (a0) ; disable interrupts 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 CSS 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; ; (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 move.w 8(a6),d0 ; get the target's ID cmp.w #scsiSelAtn,d1 ; select with ATN? bne.s @noATN ; no ATN bset.l #31,d0 ; use ATN @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 CSS move.l (sp)+,d0 ; restore d0 @exit move.w d0,10(a6) ; return the result moveq.l #2,d0 ; stack cleanup bra.w SCSIStdExit ; 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 move.w 8(a6),d2 ; get the length bsr.w SendCMD ; send it beq.s @exit ; OK @error move.l d0,-(sp) ; save d0 bsr.w SCSIExitCleanup ; clean up to leave CSS move.l (sp)+,d0 ; restore d0 @exit move.w d0,14(a6) ; return the result moveq.l #6,d0 bra SCSIStdExit ; 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 ;_________________________________________________________________________________________ ; 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) move.b #iDMA,sMR+WrOffs(a3) ; enable DMA in mode register move.b #iDB,sICR+WrOffs(a3) ; assert data bus in initiator command reg move.b d7,sDMAtx+WrOffs(a3) ; start write DMA ; Do I need to test for DRQ or *REQ at this point? ;end of NewSCSIWBlind patch rollin 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 move.b #iDMA,sMR+WrOffs(a3) ; set DMA mode 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 @DMAdone ; <2.8> move.b zeroReg,sIDMArx+WrOffs(a3) ; start read DMA 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 @JmpTable ; 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 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 ; @data_end move.b zeroReg,sMR+WrOffs(a3) ; clear DMA mode 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 @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 ; 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 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 @DMAdone ; <2.8> move.l Ticks,d4 ; get current time add.l 8(a6),d4 ; add count to start ticks @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 move.w d0,(sp) ; save new error @checkTime cmp.l Ticks,d4 ; got the time, pal? bhi.s @chkStatus ; still time bra.s @error ; report the error @gotStatus move.l 16(a6),a1 ; get the Status ptr move.w d2,(a1) @chkMsg bsr GetMsg beq.s @gotMsg ; no error @error move.w d0,(sp) ; save error bra.s @completeDone ; finish up @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 bsr.w SCSIExitCleanup ; clean up to leave CSS 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. ; When it goes off, "DoSpinDown" turns off the hard drive power. ; _HDIdleUpdate @PwrDone2 moveq.l #12,d0 ; stack cleanup bra.s SCSIStdExit ; CSS ;-------------------------------------------------------------------------- ; ; FUNCTION SCSIMsgIn(VAR Message: INTEGER): INTEGER; ; (8) (12) ; ; Receive a message from the target. Returns 0 for success, or error. ; DoSCSIMsgIn ; SCSIMsgIn added 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 ; CSS ;-------------------------------------------------------------------------- ; ; FUNCTION SCSIMsgOut(Message: INTEGER): INTEGER; ; (8) (10) ; ; Send a message to the target. Returns 0 for success, or error. ; DoSCSIMsgOut ; SCSIMsgOut added 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 ; CSS movem.l (sp)+,a2-a5/d2-d7 ; restore these guys 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. ; 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 moveq.l #0,d1 ; clear high word move.w TimeSCSIDB,d1 ; DBRAs/ms. lsl.l #8,d1 ; wait up to 256 ms 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 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? 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 ; blo.s ArbRetry ; branch if lost arb btst.b #bLA,sICR(a3) ; look if we lost arbitration (again) 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 ; 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) 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 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 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 bne.s @SendCmdRts ; nary a *REQ... move.b #iACK+iDB,sICR+WrOffs(a3) ; set *ACK bsr.w wfnREQ ; wait 256ms for no *REQ 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 () ; for a phase change before continuing. ; move.l zeroReg,d0 ; clear upper word move.w TimeSCSIDB,d0 ; get SCSI DBRA's/ms timing constant lsl.l #8,d0 ; wait at least 256ms for the tape drive move.l d0,d2 ; set up d2 as high word swap d2 ; @cmdHack btst.b #bBSY,sCSR(a3) ; is BSY still asserted ? beq.s @exit ; if not, leave btst.b #bPM,sBSR(a3) ; are we still in phase ? dbeq.w d0,@cmdHack ; loop until phase mismatch or timeout dbeq.w d2,@cmdHack ; high word of timeout @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 bset.l #4,d0 ; set *ACK move.b d0,sICR+WrOffs(a3) ; bsr.s wfnREQ bne.s @4 move.b sICR(a3),d0 ; get ICR bclr.l #4,d0 ; clr *ACK move.b d0,sICR+WrOffs(a3) ; moveq.l #noErr,d0 @4 rts ;-------------------------------------------------------------------------- ; ; SendMsg - Send a message byte to the target (in d2 on entry) ; SendMsg ; SendMsg new 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.) ; WARNING: wipes out d0! ; busdelay move.w TimeDBRA,d0 ; get DBRAs/ms. lsr.w #7,d0 ; divide by 128 @1 dbra d0, @1 rts ;-------------------------------------------------------------------------- ; ; WFBSY -- WAIT for BSY ; Timeout value in milliseconds is in d1.w. Clobbers d0,d1,d5 ; wfbsy ; timing reworked mulu.w TimeSCSIDB,d1 ; get # of DBRAs 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. Clobbers d0,d1,d5 ; wfnREQ ; timing reworked moveq.l #0,d1 ; clear upper word move.w TimeSCSIDB,d1 ; get # of DBRAs 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. Clobbers d0,d1,d5 ; wfREQ ; timing reworked moveq.l #0,d1 ; clear upper word move.w TimeSCSIDB,d1 ; get # of DBRAs 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 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 CyclePhase1 bsr.s wfREQ ; wait for *REQ beq.s @1 ; move.w d0,(sp) ; save error code bra.s CycleRts ; return error @1 btst.b #bPM,sBSR(a3) ; check for phase match bne.s CycleRts ; exit with (sp) value @MatchPhase ; 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 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 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