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

2255 lines
72 KiB
Plaintext

;
; File: SCSIDiskMode.a
;
; Contains: SCSI Disk Mode Transfer Routines
;
; Written by: James Blair
;
; Copyright: © 1992-1993 by Apple Computer, Inc. All rights reserved.
;
; This file is used in these builds: ROM
;
; Change History (most recent first):
;
; <SM2> 12/13/93 PN Roll in KAOs and Horror changes to support Malcom and AJ
; machines.
; <SM1> 11/20/92 SWC Added to SuperMario from Horror. Horror changes below.
; <H15> 9/8/92 jab Changed "last byte in a write" detection to be state stable
; (uses DRQ instead of REQ).
; <H14> 9/3/92 SWC jab/make sure REQ is deasserted after DMA-ing a block.
; <H13> 8/24/92 SWC jab/Moved back to the DMA code because we were breaking on a Mac
; Plus. The beginning of each block to be written will be handled
; in a polled fashion, with the rest of the block written using
; DMA.
; <H12> 8/20/92 SWC jab/Changed the dual bus code to do polled byte handshaking on
; writes to avoid problems with certain hard disks (Quantums, for
; example).
; <H11> 7/27/92 SWC Removed the temporary config ROM kludge.
; <H10> 7/13/92 SWC jab/Fine tune low power display toggling.
; <H9> 7/13/92 SWC Added a routine to put up a DeepShit alert if the user has a
; SCSI Disk Mode cable attached when we wake up.
; <H8> 7/13/92 SWC jab/Added smarter (and easier) check for LCD presence. Added
; support for inverting video on external video MainDevices.
; <H7> 7/11/92 ag Change to call the power manager that scsi disk mode is active.
; <H6> 7/6/92 SWC jab/Removed kludge that used longword access to Select Enable
; register.
; <H5> 7/1/92 ag Set condition codes on good exit of InitHWDependent.
; <H4> 6/29/92 SWC jab/Added better support for fast data transfers and fixed some
; bugs introduced in <H3>.
; <H3> 6/25/92 SWC jab/Added interrupt handling support to support /RST & SEL from
; external SCSI bus and DMA support for xfers between the DB Lite
; and its internal hard drive.
; <H2> 4/7/92 ag Change the HW Dependent section of code to invert the scsi id
; bits (the lines are active low), and power off and on the disk
; drive so the new id will be read by the disk drive controller.
; <H1> 3/3/92 jab First checked in.
;
PRINT OFF
LOAD 'StandardEqu.d'
INCLUDE 'HardwarePrivateEqu.a'
INCLUDE 'SCSI.a'
INCLUDE 'SCSIPriv.a'
INCLUDE 'PowerPrivEqu.a'
INCLUDE 'UniversalEqu.a'
INCLUDE 'Video.a'
INCLUDE 'DockingEqu.a'
INCLUDE 'SCSIDiskModeEqu.a'
PRINT ON
BLANKS ON
STRING ASIS
MACHINE MC68020
Unimplement EQU $A89F ; _Unimplemented trap
;__________________________________________________________________________________________________
;
; Routine: SCSIDiskMode
;
; Inputs: none
;
; Outputs: none
;
; Trashes: d0,d1,d2,a0,a1,a2,a3,a4,a6
;
; Function: checks if we're setup for SCSI Disk Mode, and if so, starts it up
;
SCSIDiskMode PROC EXPORT
EXPORT TestForDiskMode
IMPORT GetLevel, LCDScreenChk
WITH scsiGlobalRecord, DiskModeVars, PmgrRec, PmgrPramRec, pmCommandRec, hwCfgInfo
bsr TestForDiskMode ; is the disk mode cable plugged in?
beq.s @NoDiskMode ; -> no, return to your regularly scheduled boot
lea @ShutDownDiskMode,a0 ; point the level 4-7 interrupt vectors
move.l a0,AutoInt4 ; to the shutdown code
move.l a0,AutoInt5
move.l a0,AutoInt6
move.l a0,AutoInt7
moveq #0,zeroReg ; to comply with SCSI Mgr
link a6,#DiskModeVarsSize ; create stack frame
movea.l SCSIGlobals,a4 ; point to the SCSI Manager globals
move.l a6,sdmStack(a4) ; save our local stack pointer for intrp routines
bsr InsdmTimeTask ; install our icon redrawing timer
bsr ReadPramID ; read current virtual SCSI id from PRAM
bsr InitDiskModeDrawing ; initialize the drawing environment
beq.s @ShutDownDiskMode ; -> couldn't load one of the pictures
bsr InitBacklight ; check for and initialize backlight stuff
bsr InitControllers ; set the controller(s) to a known state
bsr InitHWDependent ; do any hardware-dependent initialization
bsr InstallIntHandler ; install the appropriate interrupt handler
IF hasPwrControls THEN ; is power controls available <H7>
bsr InformPowerMgr ; tell the power manager were in scsi disk mode <H7>
ENDIF
bsr SpinWheels ; wait to be Selected
@ShutDownDiskMode
_PowerOff ; kill the power
bra.s * ; just hang here if it comes back
@NoDiskMode rts
IF hasPwrControls THEN
;__________________________________________________________________________________________________ <H9>
;
; Routine: SCSIDiskWakeAlert
;
; Inputs: none
;
; Outputs: none
;
; Trashes: D0-D2, A0-A1
;
; Function: Checks if the SCSI Disk Mode cable is plugged in when we wake up.
; If it is, put up a DeepShit window with a message.
;
EXPORT SCSIDiskWakeAlert
SCSIDiskWakeAlert
@savedRegs REG D0-D2/A0-A1
MOVEM.L @savedregs,-(SP)
BSR TestForDiskMode ; is the disk mode cable attached?
BEQ @NoCable ; -> no, continue with wakeup
; put the alert on the screen and wait until the cable is removed...
@DrawAlert MOVE.W #dsSCSIWarn,D0 ; put up the DeepShit alert
_SysError
@NotGone MOVEA.L VIA,A0
MOVE.W #160,D0 ; wait about 160ms
MULU TimeDBRA,D0
BRA.S @InnerLoop
@OuterLoop SWAP D0
@InnerLoop TST.B (A0) ; throttle the loop with a VIA access
DBRA D0,@InnerLoop
SWAP D0
DBRA D0,@OuterLoop
BSR TestForDiskMode ; is the disk mode cable still attached?
BNE.S @NotGone ; -> yep, keep waiting
; they finally removed the cable, so clean everything up...
CLR.B DSWndUpdate ; flag GNE to remove the alert
CMPI.W #$3FFF,ROM85 ; does this machine support color QuickDraw?
BHI.S @NoCable ; -> nope
TST.L DeviceList ; any devices?
BEQ.S @NoCable ; -> nope
MOVE.L MainDevice,D0 ; how about a main device?
BEQ.S @NoCable ; -> none to be found (where is everyone?)
MOVEA.L D0,A0 ; get the GDevice handle
MOVEA.L (A0),A0 ; and de-reference it
MOVE.W gdRefNum(A0),D0 ; get the driver's refNum
BEQ.S @NoCable ; -> no driver (this is getting weird)
LINK A6,#-VDPageInfo-ioVQElSize
LEA -VDPageInfo(A6),A1 ; point to page info
CLR.W VDPageInfo.csPage(A1) ; and set to page zero
MOVEA.L SP,A0 ; point to the parameter block
MOVE.W D0,ioRefNum(A0) ; driver's refNum
MOVE.W #cscGrayScreen,csCode(A0); csCode = gray the screen
MOVE.L A1,csParam(A0) ; csParam = pointer to page info
_Control ,IMMED ; gray the screen to erase the DeepShit "window"
UNLK A6 ; clean up the stack
@NoCable MOVEM.L (SP)+,@savedRegs
RTS
;_______________________________________________________________________________________ <H7>
;
; Routine: InformPowerMgr
;
; Inputs: none
;
; Outputs: none
;
; Trashes: d0
;
; Function: inform power manager of entering scsi disk mode
;
InformPowerMgr
IF isUniversal THEN
TestFor hwCbPwrMgr ; check for power manager
beq.s @exitPwrMgr ; if no pmgr skip
ENDIF
WITH PowerDispRec,PMgrHookDispRec
move.l #((PScsiDiskMode<<16) | \
(PMgrHookDisp<<0)),d0 ; set for scsi disk mode call
_PowerDispatch ; call power manager
ENDWITH
@exitPwrMgr
RTS
ENDIF
;_______________________________________________________________________________________
;
; Routine: ReadPramID
;
; Inputs: a6 - pointer to Disk Mode stack frame
;
; Outputs: none
;
; Trashes: d0, a0
;
; Function: reads the virtual SCSI ID from PRAM
;
ReadPramID move.l #(1<<16)+(PmgrOtherFlags<<0),d0 ; get the PRAM address of the SCSI ID byte
add.b ([PMgrBase],PRAMbase),d0
clr.w -(sp) ; make space for a buffer on the stack
move.l sp,a0 ; and point to it
_ReadXPRAM ; get the PRAM byte
moveq #0,d0
move.b (sp)+,d0 ; get the ID in bits 7-5
lsr.b #5,d0 ; and move it to bits 2-0
bne.s @done ; -> non-zero, so use it as is
moveq #defaultID,D0 ; use the default virtual ID
@done move.w d0,DiskID(a6) ; store our retrieved virtual ID
rts
;_______________________________________________________________________________________ <H8>
;
; Routine: MyBitsProc
;
; Inputs: Stackframe for "Procedure StdBits(VAR srcBits; BitMap; VAR srcRect,dstRect: Rect;
; mode: INTEGER; maskRgn: RgnHandle);"
;
; Outputs: none
;
; Trashes: none
;
; Function: Patches into CQD bitsProc bottleneck to correctly set the
; foreground and background colours for drawing on a CRT (assumed CRT that is).
;
MyBitsProc
@BitsRegs REG a0-a2
@BitsRegSz EQU 3*4
subq.l #4,sp ; make space for jumping to CQD bitsProc
movem.l @BitsRegs,-(sp)
movea.l SCSIGlobals,a2 ; get pointer to SCSI Manager globals
movea.l sdmStack(a2),a2 ; get our stack back
pea RGBWhite ; set foreground color to white
pea RGBBlack ; set background color to black
_RGBBackColor ; color addresses are on the stack
_RGBForeColor ;
move.l oldBitsProcPtr(a2),@BitsRegSz(sp) ;
movem.l (sp)+,@BitsRegs
rts
;_______________________________________________________________________________________ <H8>
;
; Routine: SetupforCRT
;
; Inputs: a6 - pointer to Disk Mode stack frame
;
; Outputs: none
;
; Trashes: none
;
; Function: Installs the patch for CQD's bitsProc bottleneck.
;
SetupforCRT
@CRTRegs REG a0-a3
movem.l @CRTRegs,-(sp)
pea RGBWhite ; set foreground color to white
pea RGBBlack ; set background color to black
_RGBBackColor ; color addresses are on the stack
_RGBForeColor ;
lea myQDProcs(a6),a0
lea MyPort+grafProcs(a6),a3
move.l a0,(a3) ; install pointer to my QDProcs
move.l a0,-(sp) ; initialize the QDProc pointers
_SetStdProcs ;
movea.l (a3),a3 ; deference the QDProcs
move.l bitsProc(a3),oldBitsProcPtr(a6) ; save CQD's version of bitsProc
lea MyBitsProc,a2 ;
move.l a2,bitsProc(a3) ; stuff our bitsProc handler
movem.l (sp)+,@CRTRegs
rts
;_______________________________________________________________________________________
;
; Routine: InitDiskModeDrawing
;
; Inputs: a6 - pointer to Disk Mode stack frame
;
; Outputs: a5 - pointer to QuickDraw globals
; a6 - pointer to Disk Mode stack frame
; ccr - bne if initialization was successful
;
; Trashes: d0,d1,d2,d3,d4,a0,a1,a2,a3
;
; Function: initializes the drawing environment, loads in the DiskMode pictures,
; and calculates all bounding rectangles for drawing
;
InitDiskModeDrawing
move.l CurrentA5,saveA5(a6) ; save host a5
lea localA5(a6),a5 ; establish our own a5 for QD
move.l a5,CurrentA5 ;
pea thePort(a6) ;
_InitGraf ; ...this does just that
pea MyPort(a6) ;
_OpenPort
_HideCursor ; make sure the cursor is hidden
; we setup here to erase the main gDevice (LCD)
bsr.l LCDScreenChk ; is the Main Device and LCD? <H8>
bne.s @LCDLooking ; <H8>
bsr.s SetupforCRT ; no -> invert all drawing <H8>
@LCDLooking ; <H8>
move.l MainDevice,a0 ; get handle to main device
move.l (a0),a0 ; and de-reference it
move.l gdPMap(a0),a0 ; get its pixmap handle
move.l (a0),a0 ; and de-reference it
addq.w #bounds,a0
moveq #-2*16,d4 ; calculate the adjusted screen height for later
add.w bottom(a0),d4
sub.w top(a0),d4
move.w right(a0),maxRightX(a6) ; save the right edge of the screen for later
move.l a0,-(sp) ; erase the screen
_EraseRect
; get the DiskMode picture...
lea scsiPictHndl(a6),a2 ; point to the first picture handle
lea scsiRect(a6),a3 ; and the first bounds rectangle
clr.l topLeft(a3) ; clear the topLeft coordinate so everything will work
moveq #DiskModePICT,d0 ; go get the SCSI symbol picture
bsr.s @GetPicture
beq.s @Done ; -> couldn't get it
movea.l d0,a0 ; get the handle
movea.l (a0),a0 ; and de-reference it
addq.w #picFrame,a0 ; point to the PICT's bounding rectangle
sub.w bottom(a0),d4
add.w top(a0),d4
lsr.w #1,d4 ; vertical offset = (screenHeight - pictHeight)/2 - 16
moveq #20,d3
bsr.s @CalculateBounds ; calculate the boundsRect for drawing
; get the low battery picture...
moveq #BattPICT,d0 ; go get the low battery picture
bsr.s @GetPicture
beq.s @Done ; -> couldn't get it
moveq #BattLeft,d3
moveq #BattTop,d4
bsr.s @CalculateBounds ; calculate the boundsRect for drawing
; get the DiskMode SCSI ID picture...
moveq #DiskId_Base,d0 ; go get the SCSI ID picture
add.w DiskID(a6),d0
bsr.s @GetPicture
beq.s @Done ; -> couldn't get it
moveq #IDLeft,d3
moveq #IDTop,d4
bsr.s @CalculateBounds ; calculate the boundsRect for drawing
; get the first arrow picture...
moveq #Arrow1,d0 ; go get the first arrow picture
bsr.s @GetPicture
beq.s @Done ; -> couldn't get it
moveq #ArrowsLeft,d3
moveq #ArrowsTop,d4
bsr.s @CalculateBounds ; calculate the boundsRect for drawing
; get the second arrow picture...
moveq #Arrow2,d0 ; go get the second arrow picture
bsr.s @GetPicture
beq.s @Done ; -> couldn't get it
; get the third arrow picture...
moveq #Arrow3,d0 ; go get the third arrow picture
bsr.s @GetPicture
beq.s @Done ; -> couldn't get it
clr.w DiskStat(a6) ; clear status word and arrow index word
clr.l DrawCount(a6) ; initialize drawing delay counter
clr.l transCount(a6) ; initialize low power display toggle counter
move.w #horizDelta,deltaX(a6) ; initialize how far to move
move.l #sbArrowDelay,ArrowDelay(a6); assume we have a single bus
movea.l SCSIGlobals,a0 ; point to the SCSI Manager globals
tst.l base5380_2(a0) ; is there an external controller?
beq.s @Finished ; <H5>
move.l #dbArrowDelay,ArrowDelay(a6); yes, use the dual bus delay <H5>
@Finished moveq #1,d0 ; set the condition codes <H5>
@Done rts
@GetPicture move.w #mapTrue,RomMapInsert ; map ROM into resource chain
subq.w #4,sp
move.w d0,-(sp) ; theID
_GetPicture ; try to load the picture
move.l (sp)+,d0 ; get the handle
move.l d0,(a2)+ ; and copy it to our variables
rts
@CalculateBounds
add.w scsiRect+left(a6),d3 ; adjust position relative to the SCSI symbol picture
add.w scsiRect+top(a6),d4
movea.l d0,a0 ; get the handle
movea.l (a0),a0 ; and de-reference it
addq.w #picFrame+right,a0 ; point to the picture's bounding rectangle
move.w (a0),d0 ; calculate the bounds for drawing
move.w -(a0),d1
sub.w -(a0),d0
add.w d3,d0
sub.w -(a0),d1
add.w d4,d1
move.w d4,(a3)+ ; relTop
move.w d3,(a3)+ ; relLeft
move.w d1,(a3)+ ; relTop + bottom-top
move.w d0,(a3)+ ; relLeft + right-left
rts
;_______________________________________________________________________________________
;
; Routine: DrawNewPICT
;
; Inputs: a6 - pointer to DiskMode stack frame
;
; Outputs: none
;
; Trashes: none
;
; Function: moves the pictures a step in the current direction, checking for screen edges
;
DrawRegs REG d0-d2/a0-a1
DrawNewPICT movem.l DrawRegs,-(sp) ; <H4>
cmpi.l #iconDelay,DrawCount(a6) ; is it time to update the display? <H3>
blt.s @DontDraw ; -> no, keep waiting <H3>
clr.l DrawCount(a6) ; reset the timer and draw new icon <H3>
pea scsiRect(a6) ; erase the old picture
_EraseRect
; check if we're still on the screen...
move.w deltaX(a6),d1 ; get the offset
move.w scsiRect+left(a6),d0 ; will we move off the left edge?
add.w d1,d0
bmi.s @OtherWay ; -> yes, start moving back the other way
move.w scsiRect+right(a6),d0 ; will we move off the right edge?
add.w d1,d0
cmp.w maxRightX(a6),d0
ble.s @SameWay ; -> no, keep going
@OtherWay neg.w d1 ; switch directions
move.w d1,deltaX(a6)
; update all the bounding rectangles...
@SameWay add.w d1,scsiRect+left(a6)
add.w d1,scsiRect+right(a6)
add.w d1,battRect+left(a6)
add.w d1,battRect+right(a6)
add.w d1,scsiIDRect+left(a6)
add.w d1,scsiIDRect+right(a6)
add.w d1,arrowRect+left(a6)
add.w d1,arrowRect+right(a6)
; draw the pieces...
move.l scsiPictHndl(a6),-(sp) ; thePicture
pea scsiRect(a6) ; theRect
_DrawPicture ; draw the SCSI symbol
btst #LowPower,diskStat(a6) ; do we have to draw the battery? <H3>
beq.s @DrawID ; -> no
bchg #DrawWarning,diskStat(a6) ; yes, do we draw the battery this time?
beq.s @DrawID ; -> no
move.l battPictHndl(a6),-(sp) ; thePicture
pea battRect(a6) ; theRect
_DrawPicture ; draw the battery picture
bra.s @DontDraw
@DrawID move.l scsiIDPictHndl(a6),-(sp) ; thePicture
pea scsiIDRect(a6) ; theRect
_DrawPicture ; draw the SCSI ID
@DontDraw movem.l (sp)+,DrawRegs ; <H4>
rts
;_______________________________________________________________________________________
;
; Routine: DrawArrows
;
; Inputs: a6 - pointer to DiskMode stack frame
;
; Outputs: none
;
; Trashes: none
;
; Function: draws the next transfer arrow to show a transfer is in progress
;
DrawArrows movem.l DrawRegs,-(sp) ; <H4>
move.l ArrowDelay(a6),d0 ; is it time to update the display? <H3>
cmp.l DrawCount(a6),d0 ; <H3>
bgt.s @DontDraw ; -> no, keep waiting <H3>
clr.l DrawCount(a6) ; -> yes, reset the timer and redraw <H3>
moveq #ArrowMask,d0 ; mask off the arrow bits
and.b whichArrow(a6),d0
addq.b #1,d0 ; cycle to the next state
cmpi.b #2,d0 ; did we wrap?
ble.s @NoPin ; -> no
moveq #0,d0 ; yes, pin it at zero
@NoPin move.b d0,whichArrow(a6) ; and store the new value
lea arrow1PictHndl(a6),a0
move.l 0(a0,d0.w*4),-(sp) ; thePict
pea arrowRect(a6) ; theRect
move.l (sp),-(sp) ; erase the old arrow
_EraseRect
_DrawPicture ; and draw the new one
@DontDraw movem.l (sp)+,DrawRegs ; <H4>
rts
;_______________________________________________________________________________________
;
; Routine: InitBacklight
;
; Inputs: a6 - pointer to DiskMode stack frame
;
; Outputs: none
;
; Trashes: d0,a0,a1
;
; Function: determines if the backlight driver is installed, and if so, gets the
; minimum/maximum backlight values for this machine
;
InitBacklight
clr.w ioRefNum(a6) ; zero the refNum field (assume the driver's not open)
lea -ioQElSize(sp),sp ; put a parameter block on the stack
movea.l sp,a0 ; and point to it
lea @driverName,a1
move.l a1,ioVNPtr(a0) ; driver name
clr.b ioPermssn(a0) ; open permission = whatever's allowed
_Open ; see if the driver's there
bne.s @NoDriver ; -> it's not
move.w ioRefNum(a0),backliteRefNum(a6) ; save the driver's refNum
move.w #GetBrightRange,csCode(a0) ; csCode = return minimum/maximum brightness levels
_Status ; get the range
move.l csParam(a0),backliteMax(a6) ; save the max/min values
move.w #DisableSlideCtl,csCode(a0) ; csCode = turn slider control on/off
st csParam(a0) ; csParam = disable slider control
_Control ; put brightness on computer control
@NoDriver lea ioQElSize(sp),sp ; clean up the stack
rts
STRING PASCAL
@driverName dc.w '.Backlight'
STRING ASIS
;_______________________________________________________________________________________
;
; Routine: TurnDownBackLite
;
; Inputs: a6 - pointer to DiskMode stack frame
;
; Outputs: none
;
; Trashes: d0,d1,a0
;
; Function: turns down the backlight to its minimum value
;
TurnDownBackLite
bset.b #BackLiteOff,DiskStat(a6) ; make a note of turning the backlight off
move.w backliteMin(a6),d0 ; get the minimum value
bra.s BacklightCommon ; go set the new level
;_______________________________________________________________________________________
;
; Routine: TurnUpBackLite
;
; Inputs: a6 - pointer to DiskMode stack frame
;
; Outputs: none
;
; Trashes: d0,d1,a0
;
; Function: turns up the backlight to its maximum value
;
TurnUpBackLite
bclr.b #BackLiteOff,DiskStat(a6) ; make a note of turning the backlight on
move.w backliteMax(a6),d0 ; get the maximum value
BacklightCommon
move.w backliteRefNum(a6),d1 ; is the Backlight driver installed?
beq.s @NoDriver ; -> nope, just exit
lea -ioQElSize(sp),sp ; put a parameter block on the stack
movea.l sp,a0 ; and point to it
move.w d1,ioRefNum(a0) ; get the driver's refNum
move.w #SetBrightness,csCode(a0) ; csCode = set brightness level
move.w d0,csParam(a0) ; csParam = brightness level
_Control ; put brightness on computer control
lea ioQElSize(sp),sp ; clean up the stack
@NoDriver rts
;_______________________________________________________________________________________
;
; Routine: GetBatteryStatus
;
; Inputs: a6 - pointer to DiskMode stack frame
;
; Outputs: d0 - normalized battery level (-1 to 4)
; ccr - set based on d0
;
; Trashes: d0,d1,d2
;
; Function: returns the battery/charger state
;
GetBatteryStatus
IF hasPwrControls THEN
IF isUniversal THEN
TestFor hwCbPwrMgr ; does this machine have a Power Manager?
BEQ.S @NoPMgr ; -> nope, skip
ENDIF
movem.l d3/a0/a2,-(sp)
movea.l PMgrBase,a2 ; point to the Power Manager's globals
bsr.l GetLevel ; get the current battery level
btst #HasCharger,Charger(a2) ; is the charger connected?
movem.l (sp)+,d3/a0/a2
beq.s @NoCharger ; -> nope, return the actual level
@NoPMgr
ENDIF
moveq #-1,d0 ; default to returning "above warning level"
@NoCharger tst.w d0 ; setup the ccr
rts
;_______________________________________________________________________________________
;
; Routine: InitControllers
;
; Inputs: a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
;
; Outputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip (or -1 if none)
; a4 - pointer to SCSI Manager globals
; a6 - pointer to Disk Mode stack frame
; d7 - zero
;
; Trashes: d0
;
; Function: sets up chip base address pointers, initializes the controller registers,
; and resets the internal SCSI bus
;
InitControllers
IF hasPwrControls THEN
IF isUniversal THEN
TestFor hwCbPwrMgr ; does this machine have a Power Manager?
BEQ.S @NoPMgr ; -> nope, skip
ENDIF
MOVE.B #hdOn,-(SP) ; what to turn on
MOVE.L SP,-(SP) ; pmRBuffer
MOVE.L (SP),-(SP) ; pmSBuffer
MOVE.W #1,-(SP) ; pmLength = 1
MOVE.W #powerCntl,-(SP) ; pmCommand
MOVEA.L SP,A0 ; point to the parameter block
_PMgrOp ; turn on the HD/SCSI chip
LEA pmRBuffer+4+2(SP),SP ; toss the parameter block
@NoPMgr
ENDIF
movea.l SCSIBase,a1_Int ; load base address of internal c80
movea.l SCSI2Base,a2_Ext ; load base address of external c80
movea.l SCSIGlobals,a4 ; point to the SCSI Manager globals
bset.b #scBusy,G_State(a4) ; lockout others while testing
moveq #0,zeroReg ; to comply with SCSI Mgr
bsr.s ClearBus ; initialize the controllers
tst.l base5380_2(a4) ; is there an external controller?
beq.s @Done ; -> no, don't reset
move.b #iRST,sICR(a1_Int) ; assert SCSI reset line on internal controller
move.w TimeDBRA,d0 ; wait 250µsec
lsr.w #2,d0
@delay dbra d0,@delay
move.b zeroReg,sICR(a1_Int) ; de-assert *RST
@Done rts
;_______________________________________________________________________________________
;
; Routine: ClearBus
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip (or -1 if none)
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
;
; Outputs: same
;
; Trashes: none
;
; Function: initializes the controller registers
;
ClearBus movem.l d0/d1,-(sp) ; save ourselves
move.b zeroReg,sMR(a1_Int) ; clear arbitration bit
move.b zeroReg,sSER(a1_Int) ; clear select int. enable reg
move.b zeroReg,sICR(a1_Int) ; de-assert *RST
move.b zeroReg,sTCR(a1_Int) ; clear select int. enable reg
move.b zeroReg,sODR(a1_Int) ; clear the internal data bus
tst.l base5380_2(a4) ; is there an external controller?
beq.s @Done ; -> no, don't initialize it
move.b zeroReg,sTCR(a2_Ext) ; clear target command reg.
move.b #cTargSetup,sMR(a2_Ext) ; put in Target mode
move.w DiskID(a6),d0 ; retrieve virtual SCSI id <H3>
moveq #1,d1 ; setup for getting id position <H3>
lsl.b d0,d1
move.b d1,sSER(a2_Ext) ; we want an interrupt on a valid select <H6>
move.b zeroReg,sICR(a2_Ext) ; clear the command register
move.b zeroReg,sODR(a2_Ext) ; clear the internal data bus
@Done
movem.l (sp)+,d0/d1 ; restore ourselves
rts
;__________________________________________________________________________________________________
;
; Routine: TestForDiskMode
;
; Inputs: none
;
; Outputs: CCR - BNE if the SCSI DiskMode cable is attached
;
; Trashes: D0-D2, A0-A1
;
; Function: checks if we're setup for SCSI Disk Mode
;
TestForDiskMode
IF hasNiagra THEN
IF isUniversal THEN
TestFor NiagraExistsBit ; are we running on a Niagra system (Dartanian)?
BEQ.S @NotNiagra ; -> no, can't check for Disk Mode that way
ENDIF
MOVEA.L VIA,A0
MOVEQ #1<<vSDMCable,D0 ; mask off the cable sense bit
AND.B vBufB(A0),D0
EORI.B #1<<vSDMCable,D0 ; return BNE if the cable is attached
RTS
@NotNiagra
ENDIF
MOVE.W #Unimplement,D0 ; does the _DockingDispatch trap exist?
_GetTrapAddress ,NEWTOOL
MOVEA.L A0,A1
MOVE.W @DockTrap,D0
_GetTrapAddress ,NEWTOOL
CMPA.L A0,A1
BEQ.S @NoDocking ; -> no, we can't check for Disk Mode that way
SUBQ.W #4,SP ; result
PEA dockSCSIDiskMode ; docking selector = get SCSI Disk Mode info
CLR.L -(SP) ; params = nil
@DockTrap _DockingDispatch ; call the handler
MOVEQ #1<<dockSDMCable,D0 ; is the SCSI Disk Mode cable plugged in?
AND.L (SP)+,D0
RTS ; return BNE if the cable is attached
@NoDocking
NOP ; padding in case there's another way to check
NOP
MOVEQ #0,D0 ; any other case means no disk mode
RTS
;_______________________________________________________________________________________
;
; Routine: InitHWDependent
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip (if any)
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d7 - zero
;
; Outputs: same
;
; Trashes: d0,d1,a0
;
; Function: does any hardware-dependent initialization
;
InitHWDependent
IF hasNiagra THEN
IF isUniversal THEN
TestFor NiagraExistsBit ; are we running on a Niagra system (Dartanian)?
BNE.S @Niagra ; -> no
TestFor PrattExists ; are we running on a BlackBird?
BEQ.S @NotNiagra ; -> no
ENDIF
@Niagra MOVEA.L VIA,A0
ORI.B #(%111<<vSDMDiskID),vDirB(A0) ; make the disk ID bits outputs <H2>
MOVEQ #~(%111<<vSDMDiskID),D0 ; mask off cable bits <H2>
AND.B vBufB(A0),D0 ; d0 has the current value of the port
MOVE.W DiskID(A6),D1 ; get the SCSI ID we're going to use
ASL.B #vSDMDiskID,D1 ; … and shift it into the appropriate bit position
not d1 ; … complement for invert outputs <H2>
and.b #(%111<<vSDMDiskID),d1 ; … mask for only id bits <H2>
OR.B D1,D0 ; jam it in
MOVE.B D0,vBufB(A0) ; … and write it all back out
; cycle power to disk drive to change id
move.b #hdOff,d0 ; turn off power to drive <H2>
bsr.s SendPower ; <H2>
move #250,d1 ; loop for 250 msec <H2>
@Msloop move.w TimeDBRA,d0 ; get 1msec count <H2>
@Loop dbra d0,@Loop ; delay 1msec <H2>
dbra d1,@Msloop ; <H2>
move.b #hdOn,d0 ; turn on powr to drive <H2>
bsr.s SendPower ; <H2>
RTS
@NotNiagra
ENDIF
IF hasMSC THEN ; <H3>
IF isUniversal THEN ; |
TestFor MSCChipBit ; don't initialize interrupt handlers for external V
beq.s @notDBLite ; SCSI bus if not on a DB Lite
ENDIF
; Here we are turning off interrupt sources on the DB Lite motherboard. These sources are all
; level two sources that we have to eliminate (our own interrupts come through level two via
; the slot interrupt source).
movea.l VIA2,a0
move.b #(0<<ifIRQ)+\
(1<<RvSndIRQEn)+\
(1<<RvSCSIDRQEn),\
MSCVIA2IER(a0) ; disable all but SCSI IRQ
move.b #(0<<ifIRQ)+\
(1<<RvIRQ0En),\
MSCSlotIER(a0) ; disable all but SlotE
@notDBLite
ENDIF ; {hasMSC} <H3>
RTS
;_______________________________________________________________________________________
;
; Routine: SendPower <H2>
;
; Inputs: D0 - power command byte
;
; Outputs: none
;
; Trashes: A0,D0
;
; Function: Change the power state
;
SendPower MOVE.B D0,-(SP) ; put the power byte into a buffer
MOVE.L SP,-(SP) ; pmRBuffer
MOVE.L (SP),-(SP) ; pmSBuffer
MOVE.W #1,-(SP) ; pmLength
MOVE.W #powerCntl,-(SP) ; pmCommand
MOVEA.L SP,A0 ; point to the parameter block
_PMgrOp ; and send the power command
LEA pmRBuffer+4+2(SP),SP ; toss the parameter block and buffer
RTS
;_______________________________________________________________________________________
;
; Routine: SpinWheels
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip (if any)
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d7 - zero
;
; Outputs: none (never exits)
;
; Trashes: d0,d1,d2,d3,a0
;
; Function: spin loop to draw the pictures and handle the SCSI transactions
;
SpinWheels tst.l base5380_2(a4) ; is this a dual bus model?
bne.s @DualBusSpin ; -> yes, go do it
; single bus code to just monitor what's going on on the bus...
movea.l a1,a2 ; point the "external" chip to the internal chip
@SingleBusSpin
bsr.s @Wait4Select ; go wait for us to be selected
@DoingTransfer
bsr DrawArrows ; let user know that data xfer is occuring
btst.b #bBSY,sCSR(a1_Int) ; is BSY still asserted?
bne.s @DoingTransfer ; -> yes, wait here until it isn't
bsr SingleBusArrowInt ; turn on single bus arrow interrupts <H3>
bra.s @SingleBusSpin ; -> transfer completed, so go back to waiting
; keep the visual interface running while we wait to be selected...
@Wait4Select
bsr GetBatteryStatus ; read the battery/charger state
ble.s @enoughPower ; -> charger connected or battery's pretty charged
@weakPower ; <H3>
btst.b #LowPower,DiskStat(a6) ; are we in low power indicator state? |
bne.s @noAdjust ; yes so do nothing V
addq.l #1,transCount(a6) ; yes, so add to change counter
cmpi.l #transThres,transCount(a6) ; have we had enough of a push to change? <H10>
blt.s @noChgYet ; no, so don't switch yet
; we have determined that it is necessary to show the low power warning condition
bset.b #LowPower,DiskStat(a6) ; set indicator for flashing battery PICT
bsr TurnDownBackLite ; because we are have a weak battery
bra.s @noAdjust ; continue
@enoughPower
btst.b #LowPower,DiskStat(a6) ; are we in low power indicator state?
beq.s @noAdjust ; no so do nothing
addq.l #1,transCount(a6) ; yes, so add to transition counter
cmpi.l #transThres,transCount(a6) ; have we had enough of a push to change? <H10>
blt.s @noChgYet ; no, so don't switch yet
; we have determined that it is alright to remove the low power warning condition
bclr.b #LowPower,DiskStat(a6) ; set indicator for flashing battery PICT
bsr TurnUpBackLite ; because we are have a strong battery
@noAdjust
clr.l transCount(a6) ; reset backlight transition counter
@noChgYet ;
bsr DrawNewPICT ; draw the picture with neat animation
btst #bitSEL,DiskStat(a6) ; have we a SEL (captured via interrupt)
beq.s @Wait4Select ; -> no, keep waiting
; else,
bclr #bitSEL,DiskStat(a6) ; clear indication of SEL <H3>
rts
; dual bus code to handle passing info between buses...
@DualBusSpin
bsr.s @Wait4Select ; go wait for us to be selected
move.w sr,-(sp) ; Save current interrupt level.
ori.w #viaIntMask,sr ; interrupt level set to one <H3>
bsr DrawArrows ; let user know that data xfer is occuring
move.b sBSR(a2),d0 ; the SEL was for us so let's get ready to set BSY (address 5)
bsr IntArbSel ; Arb and Select on the int. bus
bne.s @validSel ; return to looking for Select (failed Select)
move.b zeroReg,sICR(a2_Ext) ; drop BSY because we failed the internal select
bra.s @DualBusSpin
@validSel
bsr RmvsdmTimeTask ; avoid TM task interrupts <H3>
move.b zeroReg,sICR(a1_Int) ; this should draw a REQ from the internal drive
; we are now entering either CMD or MSG-OUT phase
; wait for a REQ/ from the target before proceeding
@wait4REQ btst.b #bREQ,sCSR(a1_Int) ; check for /REQ: indicates we can read SEL id
bne.s @gotREQ
btst.b #bBSY,sCSR(a1_Int) ; check to see if more work is required
bne.s @wait4REQ ; ...branch if more to be done
bsr.w ClearBus ; we've reached bus free so clean up
move.w (sp)+,sr ; restore previous interrupt level (from viaIntMask setting).
bsr InsdmTimeTask ; restore our TM drawing counter <H3>
bra.s @DualBusSpin ; return to look for Select
@gotREQ move.b sCSR(a1_Int),d3_IntStat ; once we have /REQ we need to know what the target wants to do...
lsr.b #2,d3_IntStat ; setup to xfer /REQ, /MSG, C/D, I/O
; the lower three bits of d0 contain the index value for jumping
moveq #7,d0
and.b d3_IntStat,d0 ; mask out non indexing data
move.w sr,savedSR(a6) ; Save current interrupt level. <H3>
ori.w #hiIntMask,sr ; set interrupt level to 7 <H3>
bset.b #DoingXfer,DiskStat(a6) ; are we nested in a xfer routine? <H3>
move.w @TransferTbl(d0.w*2),d0 ; get the offset to the routine
jsr @TransferTbl(d0.w) ; and go there
move.w savedSR(a6),sr ; restore interrupt level <H3>
bra.s @wait4REQ ; return to look for REQ
@TransferTbl
DC.W DataOut_DM-@TransferTbl ; 0: Data Out
DC.W DataIn_DM-@TransferTbl ; 1: Data In
DC.W Command_DM-@TransferTbl ; 2: Command
DC.W Status_DM-@TransferTbl ; 3: Status
DC.W Unspecified_DM-@TransferTbl ; 4: Unspecified
DC.W Unspecified_DM-@TransferTbl ; 5: Unspecified
DC.W MessageOut_DM-@TransferTbl ; 6: Message Out
DC.W MessageIn_DM-@TransferTbl ; 7: Message In
;_______________________________________________________________________________________
;
; Routine: IntArbSel
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d0 - contents of external bus status register (to check for ATN)
; d7 - zero
;
; Outputs: ccr - BNE if we successfully selected on the internal bus
;
; Trashes: d1,d2
;
; Function: This routine is called after a Select is pending from the external bus.
; Once an external Select is seen we jump here to arbitrate and Select
; the internal drive.
;
IntArbSel move.b #IntArbMask,sODR(a1_Int) ; put my ID on bus
@ArbRetry move.b zeroReg,sMR(a1_Int) ; 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,d2 ; get high word
swap d2
move.b #iARB,sMR(a1_Int) ; tell 5380 to go into arbitration
@Wait4Arb btst.b #bAIP,sICR(a1_Int) ; check to make sure we're arbitrating
dbne d1,@Wait4Arb ; loop until arbitration has started or time out
dbne d2,@Wait4Arb
beq.s @ArbTimeout ; -> timed out, so go back to the spin loop
@ArbStarted bsr.s Delay22u ; wait for bus to settle
btst.b #bLA,sICR(a1_Int) ; did we lose arbitration?
bne.s @ArbRetry ; -> yes, try again
move.b zeroReg,sTCR(a1_Int) ; reset because we won't get data on bus in Select phase if it isn't zero
move.b #iSEL,sICR(a1_Int) ; set select
move.b #IntSELMask,sODR(a1_Int) ; get ready to put myID and deviceID on bus
moveq #iSEL+iBSY+iDB,d1 ; assume we didn't see ATN
btst.l #bitATN,d0 ; did we?
beq.s @noATN ; -> nope
moveq #iSEL+iBSY+iDB+iATN,d1 ; yes, assert ATN as well
@noATN move.b d1,sICR(a1_Int) ; issue /SEL and assert data bus, perhaps with ATN
move.b zeroReg,sMR(a1_Int) ; reset /ARB
move.b zeroReg,sSER(a1_Int) ; clear select int. enable reg
; we currently don't wait to drop BSY because we don't really need to arbitrate
; no one else is supposed to be on the bus!!! (this may change!!!!!)
andi.b #~iBSY,sICR(a1_Int) ; reset /BSY bit
move.w G_Async(a4),d1 ; select timeout from globals (in ms)
mulu.w TimeSCSIDB,d1 ; get # of DBRAs
move.l d1,d2 ; set up d2 as high word
swap d2
@Wait4BSY btst.b #bBSY,sCSR(a1_Int) ; loop until BSY or timeout
dbne d1,@Wait4BSY
dbne d2,@Wait4BSY
@ArbTimeout rts
;_______________________________________________________________________________________
;
; Routine: Delay22u
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d7 - zero
;
; Outputs: same
;
; Trashes: d1
;
; Function: delays roughly 2200ns
;
Delay22u moveq #0,d1
move.b TimeDBRA,d1 ; get DBRAs/ms
lsr.w #12-8,d1
@loop dbra d1, @loop
rts
;_______________________________________________________________________________________
;
; Routine: DataOut_DM
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d3 - internal bus status (/REQ, /MSG, C/D, I/O)
; d7 - zero
;
; Outputs: same
;
; Trashes: d0,d1,d2,a0,a3
;
; Function: Handles the DataOut transfer phase.
; Entry into this routine means that we have an existing /REQ on the
; internal bus from the drive. If the DataOut xfer is from a SCSIWrite or
; SCSIWBlind cdb then we use psuedo-DMA access to the internal hard drive;
; otherwise the xfer is done completely polled.
;
DataOut_DM move.w savedSR(a6),sr ; restore interrupt level
cmpi.b #$0A,savedCDB(a6) ; is this a Write cmd ?
beq.s @doitFast ; branch to hhsk if a block oriented write
cmpi.b #$2A,savedCDB(a6) ; is this an Extended Write cmd ?
bne SlowDataOut_DM ; not block oriented, so write slowly
@doitFast
move.b zeroReg,sTCR(a1_Int) ; set phase match for data in (DO = 0)
move.b zeroReg,sTCR(a2_Ext) ; send internal info to external bus
move.l BusErrVct,yeOldeBusErrVct(a4) ; keep old vector
lea DataBusErrHdlr,a0 ;
move.l a0,BusErrVct ; use this SCSI Bus Error handler
movea.l hhsk5380_1(a4),a0 ; load DMA address of internal c80
lea sODR(a0),a0
movea.l hhsk5380_2(a4),a3 ; load DMA address of external c80
lea sIDR(a3),a3 ; <H4>
bsr.s FastDataOutXfer ; we return from a bus error or xfer complete
move.b zeroReg,sMR(a1_Int) ; clear DMA mode on internal controller
move.b #cTargSetup,sMR(a2_Ext) ; set external controller to target mode
move.l yeOldeBusErrVct(a4),BusErrVct ; restore old vector
bra ClearIntSem
;_______________________________________________________________________________________
;
; Routine: FastDataOutXfer
;
; Inputs: a0 - pointer to DMA access address of internal SCSI chip
; a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a3 - pointer to DMA access address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d7 - zero
;
; Outputs: same
;
; Trashes: d0,d1,d2
;
; Function: Handles block data transfers from the Mac thru to the DB Lite internal
; hard drive. Software handshaking is performed between the Mac and the
; DB Lite and psuedo-DMA hardware handshaking is used to transfer data
; from the DB Lite to the internal hard drive. The bus error handler
; that regulates the psuedo-DMA transfers is installed by the caller of
; this routine.
;
; For this to work correctly we must avoid running DMA right to the end
; of the transfer (our last DMA byte would put another /REQ out to the
; Mac and that is bad!
;
FastDataOutXfer
moveq #0,d1
cmpi.b #$0A,savedCDB(a6) ; is this a Write cmd ?
bne.s @tenByteCmd ; branch to hhsk if a block oriented write
move.b savedCDB+4(a6),d1 ; get number of blocks to write (4 off of CDB)
beq.s @zeroSixAdjust
bra @mainBot
@tenByteCmd
move.w savedCDB+7(a6),d1 ; get number of blocks to write (4 off of CDB)
beq.s @zeroTenAdjust
bra @mainBot
@zeroSixAdjust
move.w #$FF,d1
bra.s @zeroTop
@zeroTenAdjust
moveq #-1,d1
@zeroTop
@mainLoopTop ; this loop xfers all but last block
; We do the first byte of the block manually so that any head/track delays will <H12> to next <H12>
; propagate themselves to the mac. After the first byte is transfer we finish
; the next 511 bytes using DMA. We actually exit out of the DMA loop one byte
; early so that we can turn DMA off after the 512th byte DRQ.
@NotACK
btst.b #bACK,sBSR(a2_Ext) ; wait for ACK to drop after removing REQ
bne.s @NotACK ;
@wait4REQ_DO
btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be asserted
beq.s @wait4REQ_DO ; -> no, keep waiting
move.b #iREQ,sTCR(a2_Ext) ; send internal /REQ to external bus
@wait4ACK_DO
btst.b #bACK,sBSR(a2_Ext) ; check for ACK/: indicates we can read data
beq.s @wait4ACK_DO ;
move.b sCDR(a2_Ext),sODR(a1_Int) ; get data from ext. bus and put on int.
bclr #bitREQ,sTCR(a2_Ext) ; drop REQ/ when we have latched the data
@wait4NotACK_DO
btst.b #bACK,sBSR(a2_Ext) ; wait for ACK to drop after removing REQ
bne.s @wait4NotACK_DO ;
move.b zeroReg,sTCR(a1_Int) ; set phase match for data out (DO = 0)
move.b #iACK+iDB,sICR(a1_Int) ; set ACK for data we are moving from ext. to int. bus
@wait4NotREQ_DO
btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be de-asserted by int. drive
bne.s @wait4NotREQ_DO
move.b zeroReg,sICR(a1_Int) ; clear ACK/ and data and then wait for target to respond
; This is the top of the 510 byte DMA transfer section.
@dmaTop
move.w #512-3,d2 ; we want to dma the next 510 bytes
move.b #iDMA,sMR(a1_Int) ; set internal controller to DMA mode
ori.b #iDB,sICR(a1_Int) ; activate data on bus
move.b zeroReg,sDMAtx(a1_Int) ; write to start Initiator DMA sends
move.b #iDMA+cTargSetup,sMR(a2_Ext); set external controller to DMA mode
move.b zeroReg,sTDMArx(a2_Ext) ; write to start Target DMA receives <H12>
@dmaLoop
@waitEDRQ btst #bDMAR,sBSR(a2_Ext) ; do we have the DRQ yet ?
beq @waitEDRQ ;
move.b (a3),d0 ; get byte from mac
@waitIDRQ btst.b #bDMAR,sBSR(a1_Int) ; wait for DREQ before starting to read
beq.s @waitIDRQ ;
move.b d0,(a0) ; put data on internal bus
dbra d2,@dmaLoop
; We have now transferred a total of 511 bytes (1 polled and 510 DMA) of the 512 we need...
; we now have to turn DMA off after receiving the DRQ for the 512th byte.
; The top of the loop (if we jump back) takes care of waiting for /ACK to disappear
; from the external bus.
@waitEDRQ2 btst #bDMAR,sBSR(a2_Ext) ; do we have the DRQ yet ?
beq @waitEDRQ2 ;
move.b #cTargSetup,sMR(a2_Ext) ; clear external controller DMA mode
move.b sIDR(a2_Ext),d0 ; get byte from mac
@waitIDRQ2 btst.b #bDMAR,sBSR(a1_Int) ; wait for DREQ before starting to read
beq.s @waitIDRQ2 ;
move.b d0,(a0) ; put data on internal bus
@wait4LastDRQ ; <H15>
btst.b #bDMAR,sBSR(a1_Int) ; wait for DREQ indicating the last byte was taken <H15>
beq.s @wait4LastDRQ ; <H15>
move.b zeroReg,sMR(a1_Int) ; clear internal controller DMA mode
move.b zeroReg,sICR(a1_Int) ; clear ACK/ and data and then wait for target to respond
@mainBot
dbra d1,@mainLoopTop ; we drop out of the main xfer loop
@nextPhase rts
;_______________________________________________________________________________________
;
; Routine: SlowDataOut_DM
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d3 - internal bus status (/REQ, /MSG, C/D, I/O)
; d7 - zero
;
; Outputs: same
;
; Trashes: d0
;
; Function: Handles the data out phase using polled /REQ - /ACK handshaking.
;
SlowDataOut_DM
move.w savedSR(a6),sr ; restore interrupt level
move.b zeroReg,sTCR(a2_Ext) ; get IO/ out so bus can settle
move.b zeroReg,sTCR(a1_Int) ; get IO/ out so bus can settle
@REQACKloop_DO
move.b d3_IntStat,sTCR(a2_Ext) ; send internal info to external bus
@wait4ACK_DO
btst.b #bACK,sBSR(a2_Ext) ; check for ACK/: indicates we can read data
beq.s @wait4ACK_DO ;
move.b sCDR(a2_Ext),sODR(a1_Int) ; get data from ext. bus and put on int.
bclr #bitREQ,sTCR(a2_Ext) ; drop REQ/ when we have latched the data
@wait4NotACK_DO
btst.b #bACK,sBSR(a2_Ext) ; wait for ACK to drop after removing REQ
bne.s @wait4NotACK_DO ;
move.b zeroReg,sTCR(a1_Int) ; set phase match for data out (DO = 0)
move.b #iACK+iDB,sICR(a1_Int) ; set ACK for data we are moving from ext. to int. bus
@wait4NotREQ_DO
btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be de-asserted by int. drive
bne.s @wait4NotREQ_DO
move.b zeroReg,sICR(a1_Int) ; clear ACK/ and data and then wait for target to respond
@wait4REQ_DO
btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be asserted
beq.s @wait4REQ_DO ; -> no, keep waiting
move.b sCSR(a1_Int),d3_IntStat ; save it
lsr.b #2,d3_IntStat ; setup to xfer /REQ, /MSG, C/D, I/O
moveq #7,d0
and.b d3_IntStat,d0 ;
IF phDO≠0 THEN
subq.b #phDO,d0 ; once we have /REQ we need to know what the target wants to do...
ENDIF
beq.s @REQACKloop_DO ; branch if still in Data Out phase
bra ClearIntSem
;_______________________________________________________________________________________
;
; Routine: DataIn_DM
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d3 - internal bus status (/REQ, /MSG, C/D, I/O)
; d7 - zero
;
; Outputs: same
;
; Trashes: d0,d1,d2,a0,a3
;
; Function: Handles the DataIn transfer phase.
; Entry into this routine means that we have an existing /REQ on the
; internal bus from the drive. If the DataIn xfer is from a SCSIRead or
; SCSIRBlind cdb then we use psuedo-DMA access from the internal hard drive;
; otherwise the xfer is done completely polled.
;
DataIn_DM move.w savedSR(a6),sr ; restore interrupt level
cmpi.b #$08,savedCDB(a6) ; is this a Read cmd ?
beq.s @doitFast ; branch to hhsk if a block oriented read
cmpi.b #$28,savedCDB(a6) ; is this an Extended Read cmd ?
bne SlowDataIn_DM ; not block oriented, so read slowly
@doitFast move.b #iIO,sTCR(a1_Int) ; set phase match for data in (DI = 1)
move.b #iDMA,sMR(a1_Int) ; set internal controller to DMA mode
move.b zeroReg,sIDMArx(a1_Int) ; write to start Initiator DMA reads
move.b #iIO,sTCR(a2_Ext) ; send internal info to external bus
move.b #iDMA+cTargSetup,sMR(a2_Ext); set external controller to DMA mode
ori.b #iDB,sICR(a2_Ext) ; activate data on bus
move.b zeroReg,sDMAtx(a2_Ext) ; write to start Target DMA sends
; /REQ is asserted until we write data
; to the dma address.
move.l BusErrVct,yeOldeBusErrVct(a4) ; keep old vector
lea DataBusErrHdlr,a0 ;
move.l a0,BusErrVct ; use this SCSI Bus Error handler
movea.l hhsk5380_1(a4),a0 ; load DMA address of internal c80
lea sIDR(a0),a0
movea.l hhsk5380_2(a4),a3 ; load DMA address of external c80
lea sODR(a3),a3 ; <H4>
bsr.s FastDataInXfer ; we return from a bus error or xfer complete
nop ; <H4>
nop ; <H4>
nop ; <H4>
move.b zeroReg,sMR(a1_Int) ; clear DMA mode on internal controller
move.b #cTargSetup,sMR(a2_Ext) ; set external controller to DMA mode
eori.b #iDB,sICR(a2_Ext) ; clear assert data bus <H4>
move.l yeOldeBusErrVct(a4),BusErrVct ; restore old vector
bra ClearIntSem
;_______________________________________________________________________________________
;
; Routine: FastDataInXfer
;
; Inputs: a0 - pointer to DMA access address of internal SCSI chip
; a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a3 - pointer to DMA access address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d7 - zero
;
; Outputs: same
;
; Trashes: d0,d1,d2
;
; Function: Handles block data transfers from the DB Lite internal hard drive
; thru to the Mac. Software handshaking is performed between the Mac and the
; DB Lite and psuedo-DMA hardware handshaking is used to transfer data
; from the internal hard drive to the DB Lite. The bus error handler
; that regulates the psuedo-DMA transfers is installed by the caller of
; this routine.
;
FastDataInXfer
cmpi.b #$08,savedCDB(a6) ; is this a Read cmd ?
bne.s @tenByteCmd ; branch to hhsk if a block oriented read
move.b savedCDB+4(a6),d1 ; get number of blocks to read (4 off of CDB)
bne.s @inDoIt
move.w #$FF,d1
bra.s @zeroTop
@tenByteCmd move.w savedCDB+7(a6),d1 ; get number of blocks to read (4 off of CDB)
bne.s @inDoIt
moveq #-1,d1
@zeroTop
move.w #512-1,d2
@inTop
@waitIDRQ btst.b #bDMAR,sBSR(a1_Int) ; wait for DREQ before starting to read
beq.s @waitIDRQ
move.b (a0),d0 ; get byte from internal drive
@waitEDRQ btst #bDMAR,sBSR(a2_Ext) ; do we have the DRQ yet ?
beq @waitEDRQ
move.b d0,(a3) ; put data on external bus
dbra d2,@inTop
@inDoIt move.w #512-1,d2 ; number of bytes/block
dbra d1,@inTop ; block count
@tailDRQ btst.b #bDMAR,sBSR(a2_Ext) ; next DRQ here yet
beq.s @tailDRQ ; if its here then leave
@tailACK btst.b #bACK,sBSR(a2_Ext) ; next DRQ here yet
bne.s @tailACK ; if its here then leave
@nextPhase rts
;_______________________________________________________________________________________
;
; Routine: SlowDataIn_DM
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d3 - internal bus status (/REQ, /MSG, C/D, I/O)
; d7 - zero
;
; Outputs: same
;
; Trashes: d0
;
; Function: Handles the DataIn phase using polled /REQ - /ACK handshaking.
;
SlowDataIn_DM
moveq #iIO,d0
move.b d0,sTCR(a2_Ext) ; get IO/ out so bus can settle
move.b d0,sTCR(a1_Int) ; set phase match for command
@REQACKloop_DI
move.b sCDR(a1_Int),sODR(a2_Ext) ; put internal data on external bus
ori.b #iDB,sICR(a2_Ext) ; xfer data to external bus
move.b d3_IntStat,sTCR(a2_Ext) ; send internal info to external bus
@wait4ACK_DI
btst.b #bACK,sBSR(a2_Ext) ; check for ACK/: indicates we can read data
beq.s @wait4ACK_DI ;
bclr #bitREQ,sTCR(a2_Ext) ; drop REQ/ when we see ACK/
@wait4NotACK_DI
btst.b #bACK,sBSR(a2_Ext) ; check for ACK/: indicates we can read data
bne.s @wait4NotACK_DI ;
move.b #iACK,sICR(a1_Int) ; transfer recv'd ACK/ to internal bus
@wait4NotREQ_DI
btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be de-asserted
bne.s @wait4NotREQ_DI
move.b zeroReg,sICR(a1_Int) ; clear ACK/ then wait for target to respond
@wait4REQ_DI
btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be asserted
beq.s @wait4REQ_DI
move.b sCSR(a1_Int),d3_IntStat ; save it
lsr.b #2,d3_IntStat ; setup to xfer /REQ,/MSG,C/D, I/O
moveq #7,d0
and.b d3_IntStat,d0 ;
subq.b #phDI,d0 ; once we have /REQ we need to know what the target wants to do...
beq.s @REQACKloop_DI ; branch if still in Data-In phase
bra ClearIntSem
;_______________________________________________________________________________________
;
; Routine: Command_DM
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d3 - internal bus status (/REQ, /MSG, C/D, I/O)
; d7 - zero
;
; Outputs: same
;
; Trashes: d0,d1
;
; Function: handles the command phase
;
Command_DM
move.w savedSR(a6),sr ; restore interrupt level <H3>
moveq #iCD,d0 ; <H3>
move.b d0,sTCR(a2_Ext) ; get CD/ out so bus can settle <H3>
move.b d0,sTCR(a1_Int) ; set phase match for command <H3>
moveq #0,d1
@REQACKloop move.b d3_IntStat,sTCR(a2_Ext) ; send internal info to external bus
@wait4ACK btst.b #bACK,sBSR(a2_Ext) ; check for ACK/: indicates we can read data
beq.s @wait4ACK
move.b sCDR(a2_Ext),d0 ; get byte from Mac <H3>
move.b d0,(savedCDB,a6,d1.w*1) ; store byte in CDB <H3>
addq #1,d1 ; increment CDB index <H3>
move.b d0,sODR(a1_Int) ; get data from ext. bus and put on int. <H3>
bclr #bitREQ,sTCR(a2_Ext) ; drop REQ/ when we see ACK/
@wait4NotACK
btst.b #bACK,sBSR(a2_Ext) ; check for ACK/ before giving next REQ/
bne.s @wait4NotACK
move.b #iACK+iDB,sICR(a1_Int)
@wait4NotREQ
btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be de-asserted
bne.s @wait4NotREQ
andi.b #~iACK,sICR(a1_Int) ; clear ACK/ and data and then wait for target to respond
@wait4REQ btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be asserted
beq.s @wait4REQ
move.b sCSR(a1_Int),d3_IntStat ; save it
lsr.b #2,d3_IntStat ; setup to xfer /REQ,/MSG,C/D, I/O
moveq #7,d0
and.b d3_IntStat,d0 ;
subq.b #phCmd,d0 ; once we have /REQ we need to know what the target wants to do...
beq.s @REQACKloop ; branch if still in command phase
move.b zeroReg,sTCR(a2_Ext) ; clear command on external bus
bra ClearIntSem ; <H3>
;_______________________________________________________________________________________
;
; Routine: Status_DM
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d3 - internal bus status (/REQ, /MSG, C/D, I/O)
; d7 - zero
;
; Outputs: same
;
; Trashes: d0
;
; Function: handles the status phase
;
Status_DM
move.w savedSR(a6),sr ; restore interrupt level <H3>
moveq #iIO+iCD,d0 ; <H3>
move.b d0,sTCR(a2_Ext) ; get CD/ and IO/ out so bus can settle <H3>
move.b d0,sTCR(a1_Int) ; set phase match for command <H3>
@REQACKloop move.b sCDR(a1_Int),sODR(a2_Ext) ; put internal data on external bus
ori.b #iDB,sICR(a2_Ext) ; xfer data to external bus
move.b d3_IntStat,sTCR(a2_Ext) ; send internal info to external bus
@wait4ACK btst.b #bACK,sBSR(a2_Ext) ; check for ACK/: indicates we can read data
beq.s @wait4ACK ;
bclr #bitREQ,sTCR(a2_Ext) ; drop REQ/ when we see ACK/
@wait4NotACK
btst.b #bACK,sBSR(a2_Ext) ; check for ACK/ before giving next REQ/
bne.s @wait4NotACK ;
move.b #iACK,sICR(a1_Int) ; transfer recv'd ACK/ to internal bus
@wait4NotREQ
btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be de-asserted
bne.s @wait4NotREQ
move.b zeroReg,sICR(a1_Int) ; clear ACK/ then wait for target to respond
bra ClearIntSem ; <H3>
;_______________________________________________________________________________________
;
; Routine: Unspecified_DM
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d3 - internal bus status (/REQ, /MSG, C/D, I/O)
; d7 - zero
;
; Outputs: same
;
; Trashes: none
;
; Function: handles unspecified phases
;
Unspecified_DM
nop
rts
;_______________________________________________________________________________________
;
; Routine: MessageOut_DM
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d3 - internal bus status (/REQ, /MSG, C/D, I/O)
; d7 - zero
;
; Outputs: same
;
; Trashes: d0
;
; Function: handles the message out phase
;
MessageOut_DM
move.w savedSR(a6),sr ; restore interrupt level <H3>
moveq #iMSG+iCD,d0
move.b d0,sTCR(a2_Ext) ; get CD/ and MSG/ out so bus can settle
move.b d0,sTCR(a1_Int) ; set phase match for command
@REQACKloop move.b d3_IntStat,sTCR(a2_Ext) ; send internal info to external bus
@wait4ACK btst.b #bACK,sBSR(a2_Ext) ; check for ACK/: indicates we can read data
beq.s @wait4ACK
move.b sCDR(a2_Ext),sODR(a1_Int) ; get data from ext. bus and put it on int.
bclr #bitREQ,sTCR(a2_Ext) ; drop REQ/ when we see ACK/
@wait4NotACK
btst.b #bACK,sBSR(a2_Ext) ; check for ACK/ before giving next REQ/
bne.s @wait4NotACK
move.b #iACK+iDB,sICR(a1_Int)
@wait4NotREQ
btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be de-asserted
bne.s @wait4NotREQ
move.b zeroReg,sICR(a1_Int) ; clear ACK/ and data and then wait for target to respond
@wait4REQ btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be asserted
bne.s @gotREQ
btst.b #bBSY,sCSR(a1_Int) ; check to see if more work is required
bne.s @wait4REQ ; ...branch if more to be done
bra.s ClearIntSem ; we've reached bus free so clean up <H3>
@gotREQ move.b sCSR(a1_Int),d3_IntStat ; save it
lsr.b #2,d3_IntStat ; setup to xfer /REQ,/MSG,C/D, I/O
moveq #7,d0
and.b d3_IntStat,d0 ;
subq.b #phMsgOut,d0 ; once we have /REQ we need to know what the target wants to do...
beq.s @REQACKloop ; branch if still in Message Out phase
bra.s ClearIntSem ; <H3>
;_______________________________________________________________________________________
;
; Routine: MessageIn_DM
;
; Inputs: a1 - pointer to base address of internal SCSI chip
; a2 - pointer to base address of external SCSI chip
; a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d3 - internal bus status (/REQ, /MSG, C/D, I/O)
; d7 - zero
;
; Outputs: same
;
; Trashes: d0
;
; Function: handles the message in phase
;
MessageIn_DM
move.w savedSR(a6),sr ; restore interrupt level <H3>
moveq #iMSG+iCD+iIO,d0 ; <H3>
move.b d0,sTCR(a2_Ext) ; get CD/, /MSG and IO/ out <H3>
move.b d0,sTCR(a1_Int) ; set phase match for command <H3>
@REQACKloop move.b sCDR(a1_Int),sODR(a2_Ext) ; put internal data on external bus
ori.b #iDB,sICR(a2_Ext) ; xfer data to external bus
move.b d3_IntStat,sTCR(a2_Ext) ; send internal info to external bus
@wait4ACK btst.b #bACK,sBSR(a2_Ext) ; check for ACK/: indicates we can read data
bne.s @gotACK
btst.b #bBSY,sCSR(a1_Int) ; check to see if more work is required
bne.s @wait4ACK ; ...branch if more to be done
bra.s ClearIntSem ; we've reached bus free so clean up <H3>
@gotACK bclr #bitREQ,sTCR(a2_Ext) ; drop REQ/ when we see ACK/
@wait4NotACK
btst.b #bACK,sBSR(a2_Ext) ; check for ACK/ before giving next REQ/
bne.s @wait4NotACK
move.b #iACK,sICR(a1_Int) ; transfer recv'd ACK/ to internal bus
@wait4NotREQ
btst.b #bREQ,sCSR(a1_Int) ; wait for /REQ to be de-asserted
bne.s @wait4NotREQ
move.b zeroReg,sICR(a1_Int) ; clear ACK/ then wait for target to respond
* bra.s ClearIntSem ; <H3>
;_______________________________________________________________________________________
;
; Routine: ClearIntSem
;
; Inputs: none
;
; Outputs: interrupt level set to hiIntMask (7)
;
; Trashes: none
;
; Function: Temporarily turns of interrupts so that we can clear the flag indicating
; that a data xfer is taking place.
;
ClearIntSem ; <H3>
move.w sr,savedSR(a6) ; Save current interrupt level.
ori.w #hiIntMask,sr ; set interrupt level to 7
bclr.b #DoingXfer,diskStat(a6) ; are we nested in a xfer routine?
rts
;_______________________________________________________________________________________
;
; Routine: InstallIntHandler
;
; Inputs: a4 - pointer to SCSI Manager globals
; a6 - pointer to DiskMode stack frame
; d7 - zero
;
; Outputs: same
;
; Trashes: none
;
; Function: installs the appropriate interrupt handler
;
InstallRegs REG a0-a2 ; <H4>
InstallIntHandler
movem.l InstallRegs,-(sp) ; save regs for install procs <H4>
tst.l base5380_2(a4) ; is there an external controller?
beq.s InstallSBIntrpHdlr ; -> no, install the single bus handler
subq.w #4,sp ; save room for result
pea dockSCSIDiskIRQ ; docking selector = register SCSI Intrp handler
pea IntrpHdlr ; params = address of handler
_DockingDispatch ; call the handler
addq.w #4,sp ; trash the return result
movem.l (sp)+,InstallRegs ; <H4>
RSTReturn rts
InstallSBIntrpHdlr
move.w sr,-(sp) ; save int level
ori.w #HiIntMask,sr
movea.l SCSIBase,a1
move.b zeroReg,sMR(a1) ; clear arbitration bit
move.b zeroReg,sICR(a1) ; deassert /RST if active
move.b zeroReg,sTCR(a1) ; clear target command reg.
move.b zeroReg,sODR(a1) ; clear the internal data bus
movea.l VIA2RBV,a1
move.b #$7f,vIER(a1) ; disable all level 2 interrupts
move.b #$7f,vIFR(a1) ; clear all interrupt flags
lea SBIntrpHdlr,a1
move.l a1,AutoInt2 ; stuff our handler
bsr.s SingleBusArrowInt ; turn on single bus SDM arrow interrupts
move.w (sp)+,sr ; save int level
movem.l (sp)+,InstallRegs ; regs saved for installation <H4>
rts
;_______________________________________________________________________________________
;
; Routine: SBIntrpHdlr
;
; Inputs: Interrupt exception stack frame for '030 (format $0)
;
; Outputs: This handler sets a bit signifying that the internal drive has
; been selected on a single SCSI bus SCSIDiskMode machine. This
; lets us know very quickly (approx. 90 microseconds) that the drive
; is being talked to by an initiator.
;
; Trashes: none
;
; Function: Handles the interrupt source from the SCSI controller on the main logic board.
; Note that this is only enabled for single bus SCSIDiskMode machines.
;
SBIntrpHdlr move.w sr,-(sp) ; save interrupt level
ori.w #HiIntMask,sr
move.l a0,-(sp)
movea.l SCSIGlobals,a0 ; get pointer to SCSI Manager globals
movea.l sdmStack(a0),a0 ; restore a pointer to our locals
bset #bitSEL,DiskStat(a0) ; have we a SEL (captured via interrupt)
movea.l SCSIBase,a0 ; load base address of internal c80
move.b #0,sSER(a0) ; disable the SEL interrupt
tst.b sRESET(a0) ; clear interrupt source
movea.l VIA2RBV,a0
move.b #$7f,vIER(a0) ; disable all level 2 interrupts
move.b #$7f,vIFR(a0) ; clear flags
movea.l (sp)+,a0
move.w (sp)+,sr ; restore interrupt level
rte
;_______________________________________________________________________________________
;
; Routine: SingleBusArrowInt
;
; Inputs: a6 - SpinWheels stack frame
;
; Outputs: same
;
; Trashes: none
;
; Function: Validates the SER register for receiving SEL interrupts on the internal controller
;
SingleBusArrowInt
movem.l d0/d1/a1,-(sp) ; save
movea.l SCSIBase,a1 ; setup for sSER register access
move.w DiskID(a6),d0 ; retrieve virtual SCSI id
moveq #1,d1 ; setup for getting id position
lsl.b d0,d1
move.b d1,sSER(a1) ; we want an interrupt on a valid select
movea.l VIA2RBV,a1
move.b #((1<<ifIRQ)+\ ; set the enable bit to one
(1<<ifCB2))\ ; CB2 ind input neg active edge (SCSI IRQ interrupt)
,vIER(a1) ; enable the CB2 interrupt
movem.l (sp)+,d0/d1/a1 ; restore
rts ;
;_______________________________________________________________________________________
;
; Routine: IntrpHdlr
;
; Inputs: Interrupt exception stack frame for '030 (format $0)
;
; Outputs: Dependant on the location in the DiskMode code that the interrupt occurs.
; We can assume, because we do everything at level 1 interrupt level, if we
; get here then our stackframe world is still valid
;
; Trashes: d0
;
; Function: Handles the interrupt source from the external SCSI controller.
;
savedIntrpRegs REG a1/a2/a6/d0/d1 ; registers trashed in interrupt handler
intRegSize EQU 5*4 ; size of saved regs on stack
IntrpHdlr
movem.l savedIntrpRegs,-(sp)
movea.l SCSI2Base,a2_Ext ; load base address of external c80
movea.l SCSIGlobals,a6 ; get pointer to SCSI Manager globals
movea.l sdmStack(a6),a6 ; restore a pointer to our locals
btst #bSEL,sCSR(a2_Ext) ; have we a SEL ?
bne.s @wereSelected ; we are being selected so lets set /BSY...
btst.b #bPM,sBSR(a2_Ext) ; is this a phase match interrupt?
beq.s @noRSTNeeded ; bPM=0 is phase mismatch cause intrp.
; else we know that the only other intrp source is /RST so pass it thru
movea.l SCSIBase,a1_Int ; load base address of internal c80
move.b #iRST,sICR(a1_Int) ; assert SCSI reset line on internal controller
move.w TimeDBRA,d1 ; wait 250µsec
lsr.w #2,d1
@delay dbra d1,@delay
move.b #0,sICR(a1_Int) ; de-assert *RST
move.w DiskID(a6),d0 ; retrieve virtual SCSI id
moveq #1,d1 ; setup for getting id position
lsl.b d0,d1
move.b d1,sSER(a2_Ext) ; we want an interrupt on a valid select <H6>
move.b sRESET(a2_Ext),d1 ; clear interrupt source
bclr.b #DoingXfer,diskStat(a6) ; are we nested in a xfer routine?
beq.s @noRSTNeeded ; no, the reset was enough
; Fake a format code 0 exception frame (4 words) to finish cleaning up
clr.w intRegSize+6(sp) ; format code 0
lea RSTReturn,a1
move.l a1,intRegSize+2(sp) ; PC value
bra.s @Done
@noRSTNeeded ; this was a PM intrp so just return
eori.b #iDMA,sMR(a2_Ext) ; clear DMA on external bus
@Done movem.l (sp)+,savedIntrpRegs
rte ; allow execution to continue
@wereSelected
move.w DiskID(a6),d0 ; retrieve virtual SCSI id
btst d0,sCDR(a2_Ext) ; is our ID bit turned on?
beq.s @Done ; -> no, we're done
@setBSY bset #bitSEL,DiskStat(a6) ; have we a SEL (captured via interrupt)
move.b #iBSY,sICR(a2_Ext) ; communicate that we got BSY to external bus
; we will pass on REQ as soon as it comes from the internal drive
moveq #0,d1
move.b d1,sSER(a2_Ext) ; disable the SEL interrupt <H6>
move.b sRESET(a2_Ext),d1 ; clear interrupt source
bra.s @Done
;_______________________________________________________________________________________
;
; Routine: InsdmTimeTask
;
; Inputs: a6 - SpinWheels stack frame
;
; Outputs: Installed Time Manager task.
;
; Trashes: none
;
; Function: Install TM task for setting the expiration flags for drawing code.
;
InsdmTimeTask ; <H3>
movem.l a0-a1/d0,-(sp) ; save things we use
lea.l drawTimerTsk(a6),a0 ; point to Time Mgr task record
lea.l sdmTimeTask,a1 ; point to timer interrupt handler
move.l a1,tmAddr(a0) ; point to interrupt handler
_InsXTime ; install the task record in Time Mgr queue
move.l #500,d0 ;
neg.l d0 ; for 500 micro seconds
clr.l tmWakeUp(a0) ; signal the first time
_PrimeTime ; activate the task
movem.l (sp)+,a0-a1/d0 ; restore things we used
rts ;
;_______________________________________________________________________________________
;
; Routine: RmvsdmTimeTask
;
; Inputs: a6 - SpinWheels stack frame
;
; Outputs: Time Manager task is removed from the queue.
;
; Trashes: none
;
; Function: Remove the TM Task so that we avoid getting interrupts during data transfer.
;
RmvsdmTimeTask ; <H3>
movem.l a0,-(sp) ; save things we use
lea.l drawTimerTsk(a6),a0 ; point to Time Mgr task record
_RmvTime ; deactivate the task
movem.l (sp)+,a0 ; restore things we used
rts ;
;_______________________________________________________________________________________
;
; Routine: sdmTimeTask
;
; Inputs: a1 - pointer to the Time Mgr task record
;
; Outputs:
;
; Trashes: a0,d0
;
; Function: Sets expiration flags for drawing code.
;
sdmTimeTask ; <H3>
movea.l SCSIGlobals,a0 ; get pointer to SCSI Manager globals
movea.l sdmStack(a0),a0
addq.l #1,DrawCount(a0)
movea.l a1,a0 ; point to our existing TM task
move.l #500,d0 ;
neg.l d0 ; for 500 micro seconds
_PrimeTime ; reactivate the task
rts ;
;_______________________________________________________________________________________
;
; Routine: DataBusErrHdlr
;
; Bus Error handler for data phase transfers
;
; routine: InitSCSIMgr
; file: SCSIMgrInit.a
;
; Input: a4 = ptr to SCSI globals
;
faultedAdrs EQU $10 ; offset of Faulted Address for 030
shortBEXFrameType EQU $0A ; Short bus cycle fault stack frame (020/030)
savedRegs REG d0-d1/a0 ; save these registers because we need to use them
savedRSize EQU 3*4 ; # bytes on stack for saved registers
DataBusErrHdlr ; <H3>
movem.l savedRegs,-(sp) ; save things we trash
move.l savedRSize+faultedAdrs(sp),d0 ; retrieve faulted address
clr.b d0 ; mask off variable bits
cmp.l SCSIHsk,d0 ; compare with internal SCSI hhsk address
beq.s @SCSIFault ; internal controller bus error?
; neither SCSI controller bus errored so let bus error bubble up
move.l SCSIGlobals, a0 ;
move.l yeOldeBusErrVct(a0),savedRSize(sp)
movem.l (sp)+, savedRegs
rts ; jump to old handler, assuming it'll RTE
@SCSIFault
btst.b #bPM,sBSR(a1_Int) ; are we still in phase?
beq.s @changedPhase ; branch if not still in phase
@waitDRQ btst.b #bDMAR,sBSR(a1_Int) ; do we have DRQ yet?
beq.s @SCSIFault ; no, then wait
movem.l (sp)+, savedRegs
rte ; we'll try til the end of time
@changedPhase
lea savedRSize(sp),sp ; throw away copy of d0/a0 on stack
move.w (sp),d0 ; get sr from stack
bfextu 6(sp){0:4},d1 ; get format code from stack
cmp.b #shortBEXFrameType,d1 ; short exception frame?
bne.s Drop46w ; no, so use larger frame
lea 16*2(sp),sp ; dispose of the 16-word frame
bra.s DummyFrame ; and finish up
Drop46w
lea 46*2(sp),sp ; size of exception frame
DummyFrame
;
; Fake a format code 0 exception frame (4 words) to finish cleaning up
;
clr.w -(sp) ; format code 0
pea RSTReturn ; PC value
move.w d0,-(sp) ; sr value
rte ; 'return' from the fake exception
ENDWITH
END