supermario/base/SuperMarioProj.1994-02-09/Drivers/Sony/SonySWIM3.a
2019-06-29 23:17:50 +08:00

6201 lines
225 KiB
Plaintext

;
; File: SonySWIM3.a
;
; Written by: Gary Rensberger 1-Feb-93
;
; Copyright: © 1993-1994 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <SM22> 1/31/94 LB This file now builds a driver compatible with PDM and TNT.
; Since the SWIM3 device registers are spaced differently on the
; two machines, there is now a new vector table in the
; negative-offset portion of SWIM3Vars (SonyVars) which is
; filled-in with the addresses of the SWIM3 registers by
; SWIM3DiskOpen, and every instruction in the SWIM3 code that
; accesses a register has been changed to use an addressing mode
; that goes through the table. Also, routines which previously
; required the SWIM3 base address in a2 now require a1 to point to
; SonyVars (most did already) and routine header comments have
; been changed to reflect that.
; <SM21> 1/20/94 GMR Rolled in another readTrack optomization from MissingLink.
; Reversed the order of the vectors added in <SM20>, so the
; offsets of the orginals won't change (to make patching simpler).
; <SM20> 1/4/94 LB Added support for Tsunami/TNT. This consists of a temporary
; build-time check on forTNTDebug for the SWIM3 register offsets,
; and a runtime check for separate init code in the open routine.
; Also, I have broken out the hardware-specific DMA functions into
; separate subroutines, vectorized them in SWIM3Vars, and written
; versions for TNT's Grand Central DMA controller. These routines
; have been moved to two new files, SonySWIM3AMIC.a and
; SonySWIM3GC.a.
; <SM19> 12/22/93 GMR Make sure SWIM3 error reg is cleared before starting the DMA in
; RawTrackRead.
; <SM18> 12/22/93 GMR Rolled in the last changes from mc900ftjesus, and missinglink.
; (MC2-MC6).
; <SMG2> 9/1/93 chp Remove privately-defined AMIC constants and instead include
; AMICEqu.a. (Some names changed.) _DMAIntInstall is no more. Use
; equivalent, explicit inline code.
; <SM16> 8/16/93 GMR Fixed problem in identifying 720K mfm formats (vs GCR). SWIM3
; gave no error reading the MFM header in GCR mode. The fix was to
; change the order of format checking (try MFM first, then GCR).
; Also added vectors to the low level routines (for patching).
; <SM15> 8/9/93 GMR Fixed a copy protect bug that crept back in.
; <SM14> 8/4/93 GMR Added support for MFM tags (though we can't support some of the
; tag info on SWIM3).
; <SM13> 7/20/93 GMR Added 3 retries/track during MFM formats (GCR format changes to
; come...) to make MFM formatting reliable with VM on.
; <SM12> 7/19/93 GMR Made reads more reliable under VM and sound playing, by
; requesting for sectors in interleave order, rather then reading
; 'the next one that comes by'.
; <SM11> 6/23/93 GMR Two changes to copy protection- change how address headers are
; found, and don't enable GCR conversion in RawTrackRead.
; <SM10> 5/19/93 GMR Modified the icon installation code to get the info from
; universal tables instead of the driver.
; <SM9> 5/10/93 GMR Added back support for original SWIM3's (there are still many
; out there).
; <SM8> 4/26/93 GMR Tweaked format to work with the new SWIM3's (ver 3.5). MFM
; writes are now 1/1. Removed hack code for old SWIM3's. Update
; drive status after ejecting.
; <SM7> 4/2/93 GMR Changed the parameters for the _DMAIntInstall call to match the
; new pb.
; <SM6> 3/16/93 GMR Raw track read for GCR now reads 2 tracks of data, and parses
; for the 'search' position before copying to user buffer.
; <SM5> 3/10/93 GMR Added remaining calls (raw track read, etc) to make driver
; functionally complete. Added code to conditionally check
; SWIM version, and enable writes if new chip. Increased
; read performance; GCR reads are now 1/1 when using new chip.
; SeekTrack is now asynchronous.
; <SM4> 3/4/93 GMR Reset the floppy DMA register, so it's base offset will get
; initialized properly in the Open code.
; <SM3> 2/23/93 GMR Install DMA interrupt handler using Sams new installation trap.
; The AMIC DMA base is now setup, and no longer needs to be done
; here.
; <SM2> 2/9/93 GMR Added a missing endwith to end of file.
; <SM1> 2/5/93 GMR Added for first time.
;_______________________________________________________________________
;
; Sony SWIM3 driver
;
; This file contains the SWIM3 (DMA based floppy) driver for PDM.
;
; To do:
;_______________________________________________________________________
TITLE 'SONY SWIM3 Driver - 3.5" '
STRING ASIS
INCLUDE 'AMICEqu.a'
INCLUDE 'TrapsPrivate.a' ; for the new BlockMove OpWord
; next INCLUDES for TNT, added 12/17/93 by Monte Benaresh
INCLUDE 'DBDMA.a'
INCLUDE 'GrandCentralPriv.a'
PRINT NOMDIR
MACHINE MC68020
_GetMicroSeconds OpWord $A193 ; new time manager trap returns current time in microseconds
FAST_BMOVE EQU 1 ; 1 = use fast native blockMove which doesn't use the DCBZ instruction.
********************* NOTE: The SWIM3 register offset equates are now obsolete because *****************
********************* the driver now uses an address table filled in at runtime. *****************
;
; Current Grand Central has SWIM3 registers offset by $10 instead of $200.
;
; Added 12/22/93 by Monte Benaresh
;
;------------------------------
; SWIM3 offsets for Grand Central
;------------------------------
;Timer equ $010 ; Write the timer
;Error equ $020 ; Error register
;ParamData equ $030 ; Set the 16 parameter registers
;Phase equ $040 ; Set phase lines states and directions
;Setup equ $050 ; Set the current configuration
;Zeroes equ $060 ; Write Mode reg: 1's clr bits, 0's=don't care
;Mode equ $060 ; Read Mode (returns current mode reg value)
;Ones equ $070 ; Write Mode reg: 1's set bits, 0's=don't care
;Handshake equ $070 ; Read Handshake register
;Interrupt equ $080 ; Interrupt register
;Step equ $090 ; Step register
;CurTrack equ $0A0 ; Track register
;CurSect equ $0B0 ; Sector register
;Gap equ $0C0 ; Gap register (write)
;FormatByte equ $0C0 ; Current format byte (read)
;FirstSector equ $0D0 ; First sector to start xfer
;SectorsToXfer equ $0E0 ; Number of sectors to xfer
;IntMask equ $0F0 ; Interrupt mask register
;------------------------------
; SWIM3 offsets
;------------------------------
;Timer equ $0200 ; Write the timer
;Error equ $0400 ; Error register
;ParamData equ $0600 ; Set the 16 parameter registers
;Phase equ $0800 ; Set phase lines states and directions
;Setup equ $0A00 ; Set the current configuration
;Zeroes equ $0C00 ; Write Mode reg: 1's clr bits, 0's=don't care
;Mode equ $0C00 ; Read Mode (returns current mode reg value)
;Ones equ $0E00 ; Write Mode reg: 1's set bits, 0's=don't care
;Handshake equ $0E00 ; Read Handshake register
;Interrupt equ $1000 ; Interrupt register
;Step equ $1200 ; Step register
;CurTrack equ $1400 ; Track register
;CurSect equ $1600 ; Sector register
;Gap equ $1800 ; Gap register (write)
;FormatByte equ $1800 ; Current format byte (read)
;FirstSector equ $1A00 ; First sector to start xfer
;SectorsToXfer equ $1C00 ; Number of sectors to xfer
;IntMask equ $1E00 ; Interrupt mask register
;------------------------------
; Error register equates
;------------------------------
UnderrunFIFO equ %00000001 ; Underrun FIFO
; equ %00000010 ; ...
OverrunFIFO equ %00000100 ; Overrun FIFO
; equ %00001000 ; ...
; equ %00010000 ; ...
; equ %00100000 ; ...
CRCAddrErr equ %01000000 ; CRC error in address header
CRCDataErr equ %10000000 ; CRC error in data field
CRCAddrErrBit equ 6
;------------------------------
; Setup
;------------------------------
InvWrData equ %00000001 ; 1=invert write data
CopyProtMode equ %00000010 ; 1=copy protect mode
GCRMode equ %00000100 ; select GCR mode
ClockDiv2 equ %00001000 ; internal clock frequency / 2
DisGCRConv equ %00010000 ; 1=disable GCR conversion
IBMDrive equ %00100000 ; IBM type drive
GCRWrites equ %01000000 ; 1=GCR writes, 0=MFM writes
SoftReset equ %10000000 ; software reset
GCRModeBit equ 2 ; bit 2 is gcrmode
;------------------------------
; Handshake
;------------------------------
MarkInFIFO equ %00000001 ; next byte read from FIFO is a Mark
IntPending equ %00000010 ; an interrupt is pending
DriveRdData equ %00000100 ; state of the RdData signal from the drive
DriveSense equ %00001000 ; state of the Sense signal from the drive
DriveEnabled equ %00010000 ; a drive is explicitly enabled, or timing out
rErrorValid equ %00100000 ; error register is non-zero
Dat2Bytes equ %01000000 ; 2 bytes valid / available in the FIFO
Dat1Byte equ %10000000 ; 1 byte valid / available in the FIFO
IntPendBit equ 1
DriveRdDataBit equ 2
;------------------------------
; Zeroes / Ones / Mode
;------------------------------
EnableInts equ %00000001 ; enable interrupts
Drive1Enabled equ %00000010 ; select Drive 1
Drive2Enabled equ %00000100 ; select Drive 2
StartAction equ %00001000 ; go active
WriteMode equ %00010000 ; set the chip into Write mode
HeadSelect equ %00100000 ; select head (side) 0 or 1
FormatMode equ %01000000 ; format mode
GoStep equ %10000000 ; start stepping to desired track
HeadSelectBit equ 5 ; select head (side) 0 or 1 bit #
;------------------------------
; wIntMask
;------------------------------
timerIntNum equ 0
stepIntNum equ 1
idIntNum equ 2
doneIntNum equ 3
senseIntNum equ 4
;------------------------------
; phase lines - floppy address equates
;------------------------------
ph0 equ %00000001 ; phase line 0 output data
ph1 equ %00000010 ; phase line 1 output data
ph2 equ %00000100 ; phase line 2 output data
ph3 equ %00001000 ; phase line 3 output data
ca0 equ ph0
ca1 equ ph1
ca2 equ ph2
lstrb equ ph3
;------------------------------
; disk drive pulse addresses / data
;------------------------------
wDirNextAdr equ 0*HeadSelect+0*ca2+0*ca1+0*ca0
wDirPrevAdr equ 0*HeadSelect+1*ca2+0*ca1+0*ca0
wStepOnAdr equ 0*HeadSelect+0*ca2+0*ca1+1*ca0
wStepOffAdr equ 0*HeadSelect+1*ca2+0*ca1+1*ca0
wMotorOnAdr equ 0*HeadSelect+0*ca2+1*ca1+0*ca0
wMotorOffAdr equ 0*HeadSelect+1*ca2+1*ca1+0*ca0
wEjectOffAdr equ 0*HeadSelect+0*ca2+1*ca1+1*ca0
wEjectOnAdr equ 0*HeadSelect+1*ca2+1*ca1+1*ca0
; equ 1*HeadSelect+0*ca2+0*ca1+0*ca0 ; undefined
; equ 1*HeadSelect+1*ca2+0*ca1+0*ca0 ; undefined
wDiskInPlAdr equ 1*HeadSelect+0*ca2+0*ca1+0*ca0 ; don't know for sure if you can
wNoDiskInPlAdr equ 1*HeadSelect+1*ca2+0*ca1+0*ca0 ; really write to these addresses
wMFMModeOnAdr equ 1*HeadSelect+0*ca2+0*ca1+1*ca0
wGCRModeOffAdr equ wMFMModeOnAdr
wMFMModeOffAdr equ 1*HeadSelect+1*ca2+0*ca1+1*ca0
wGCRModeOnAdr equ wMFMModeOffAdr
; equ 1*HeadSelect+0*ca2+1*ca1+0*ca0 ; undefined
; equ 1*HeadSelect+1*ca2+1*ca1+0*ca0 ; undefined
; equ 1*HeadSelect+0*ca2+1*ca1+1*ca0 ; undefined
; equ 1*HeadSelect+1*ca2+1*ca1+1*ca0 ; undefined
; disk drive sense addresses
rDirPrevAdr equ 0*HeadSelect+0*ca2+0*ca1+0*ca0
rStepOffAdr equ 0*HeadSelect+0*ca2+0*ca1+1*ca0
rMotorOffAdr equ 0*HeadSelect+0*ca2+1*ca1+0*ca0
rEjectOnAdr equ 0*HeadSelect+0*ca2+1*ca1+1*ca0
rRdData0Adr equ 0*HeadSelect+1*ca2+0*ca1+0*ca0
rMFMDriveAdr equ 0*HeadSelect+1*ca2+0*ca1+1*ca0
rDoubleSidedAdr equ 0*HeadSelect+1*ca2+1*ca1+0*ca0
rNoDriveAdr equ 0*HeadSelect+1*ca2+1*ca1+1*ca0
rNoDiskInPlAdr equ 1*HeadSelect+0*ca2+0*ca1+0*ca0
rNoWrProtectAdr equ 1*HeadSelect+0*ca2+0*ca1+1*ca0
rNotTrack0Adr equ 1*HeadSelect+0*ca2+1*ca1+0*ca0
rNoTachPulseAdr equ 1*HeadSelect+0*ca2+1*ca1+1*ca0
rIndexPulseAdr equ rNoTachPulseAdr
rRdData1Adr equ 1*HeadSelect+1*ca2+0*ca1+0*ca0
rMFMModeOnAdr equ 1*HeadSelect+1*ca2+0*ca1+1*ca0
rGCRModeOffAdr equ rMFMModeOnAdr
rNotReadyAdr equ 1*HeadSelect+1*ca2+1*ca1+0*ca0
rNotRevisedAdr equ 1*HeadSelect+1*ca2+1*ca1+1*ca0
r1MegMediaAdr equ rNotRevisedAdr
;----------------------------------------------------------------------------------------------
DDVersion equ $0410 ; disk duplicator version 4.10 and earlier
; Drive number assignments
intDriveNumber equ 1 ; drive number 1 is internal drive
extDriveNumber equ 2 ; drive number 2 is external drive
NumberOfDrives equ extDriveNumber+2 ; drive 0 (ignored), drive 1 & 2, and drive 3 (spare)
MaxDriveNum equ extDriveNumber
; Disk Drive Kinds
;noDriveKind equ 0 ; no drive connected
;unknownDriveKind equ 1 ; unspecified drive kind
;SSGCRDriveKind equ 2 ; single sided 400K GCR disk drive
;DSGCRDriveKind equ 3 ; double sided 400K/800K GCR disk drive
;DSMFMGCRDriveKind equ 4 ; double sided 400K/800K GCR, 720K, 1440K MFM disk drive
;HD20DriveKind equ 7 ; HD20
; Disk Media Kinds
NoMediaKind equ 0 ; no disk media in drive
unknownMediaKind equ 1 ; unknown disk media
HD20MediaKind equ 2 ; HD20 20MB hard disk media
LoDenMediaKind equ 3 ; 400K, 800K, 720K Media (Low density)
HiDenMediaKind equ 4 ; 1440K Media (High density)
; Disk Format Kinds
uncheckedFormat equ 0 ; disk format has not been checked
unknownFormat equ 1 ; disk format could not be determined
HD20Format equ 2 ; HD20 20MB hard disk
GCR400Kformat equ 3 ; single sided 400K GCR disk
GCR800Kformat equ 4 ; double sided 800K GCR disk
MFM720Kformat equ 5 ; double sided 720K MFM disk
MFM1440Kformat equ 6 ; double sided 1440K MFM disk HD media
GCRonHDformat equ 7 ; 400K or 800K GCR format on HD media (user error)
; Error codes
cantReadHdr equ -401 ; couldn't read a sector header
; ———————————————————————————————————————————————————————————————————————————————————————
; driver Control codes
; ———————————————————————————————————————————————————————————————————————————————————————
;killCode equ 1 ; KillIO code
;verifyCC equ 5 ; 'verify' control code
;formatCC equ 6 ; 'format' control code
;tagBufCC equ 8 ; 'set tag buffer' control code
;tCacheCC equ 9 ; 'track cache' control
;iconIDCC equ 20 ; 'get icon id' control code
;iconCC equ 21 ; 'get icon' control code
;iconLogCC equ 22 ; 'get logical icon' code
;infoCC equ 23 ; 'get drive info' code
;GetRawDataCC equ 18244 ; 'get raw track data' code
;FmtCopyCC equ 21315 ; one-pass format/copy/verify for disk duplicator
;TotalControlCalls equ 11 ; 11 currently defined
;MaxControlCalls equ TotalControlCalls+4 ; allow 4 extra for expansion
TotalVectors equ 14 ; 14 patch vectors
; ———————————————————————————————————————————————————————————————————————————————————————
; driver Status codes
; ———————————————————————————————————————————————————————————————————————————————————————
;fmtLstCode equ 6 ; returns a list of disk formats
;drvStsCode equ 8 ; status code for drive status (in SysEqu.a)
;mfmStsCode equ 10 ; 'Get MFM status'
;DupVerSts equ 17494 ; disk duplicator version supported (to match features)
;FmtByteSts equ 21315 ; return address header format byte for disk duplicator
;TotalStatusCalls equ 5 ; 5 currently defined
;MaxStatusCalls equ TotalStatusCalls+4 ; allow 4 extra for expansion
; Flags used in decode tables
ChkDriveNum equ 7
ChkDriveExists equ 6
; Disk Format constants
LastCylinder equ 80-1 ; cylinders are numbered 0..79
MinSectors equ 9 ; minimum 9 sectors supported per head (720K)
MaxSectors equ 18 ; maximum 18 sectors supported per head (1440K)
TagSize equ 12 ; Tags are 12 bytes long
BlockSize equ 512 ; Disk blocks contain 512 data bytes
gcrDMASize equ 704+11+9 ; 704 bytes for GCR, + header + trailer
gcrDataSize equ 703 ; number of nibbles in a GCR sector
mfmDMASize equ 34+512+10 ; # bytes reserved/MFM sector in DMA buffer
mfmDataSize equ 512 ; 512 bytes/block for MFM
gcrRawTrackCnt equ 12*800*2 ; # of bytes for 2 tracks of data (max)
GCRInitialSyncCount equ 200 ; start track with 200 sync groups before first sector
GCRMinSyncCount equ 4 ; no less than 4 sync groups between GCR sectors
GCRDefaultSyncCount equ 8 ; start with 8 sync groups between GCR sectors
MFMInitialGapSize equ 80 ; size of MFM Gap between index and index mark
MFMSyncSize equ 12 ; size of MFM sync field before Index/Addr/Data marks
MFMIndexMarkGapSize equ 50 ; size of MFM Gap between index mark and sector 1
MFMAddrMarkGapSize equ 22 ; size of MFM Gap between address and data
MFM720KDataGapSize equ 80 ; size of MFM Gap following 720K sectors
MFM1440KDataGapSize equ 101 ; size of MFM Gap following 1440K sectors
MFMFinalGapSize equ 102 ; size of MFM Gap between last sector to index (act)
MFMFmtRetryCnt equ 3 ; # retries allowed during MFM format.
; Delay Timing / Timeout constants
PollRate equ 500 ; wake up and look around every 500 ms
MotorTimeoutCount equ 5 ; motor timeout is 2.0 .. 2.5 seconds (5 polls)
ReadyPollRate equ -750 ; check ready every 750 uSeconds
ReadyTimeoutCount equ 1000000/750 ; max poll time is 1 second
ReadyDelay equ 10 ; 10ms extra delay after /Ready, for WolfPack drives
MotorOnDelay equ 50 ; Delay 50ms after motor on before checking ready
ChuckingDelay equ 500 ; Delay 500ms for proper disk chucking
MotorOnSettle equ 300 ; Motor speed within 1.5 % 300ms after drive ready
MotorStopDelay equ 200 ; Delay 200ms after MotorOff to spin down
SeekDelay equ -500 ; wait 500usec after seek before checking ready
SectorDataTime equ 8 ; 8ms is approx time for the data (MFM, GCR slightly longer)
SectorTimeout equ 50 ; 50 ms timeout to find a sector header
TrackTimeout equ 300 ; 300 ms timeout for track operations
FmtTrackTime equ 600 ; 600 ms timeout to find index, then write the track
StepTimeout equ 72*160 ; ≈ 11 ms timeout to send 160 step pulses (double the max of 80 tracks)
EjectDelay equ 1500 ; 1.5 seconds for eject to complete
GCRWriteToRead equ 620 ; 620µs max time from write to read data valid again
; Sector Buffer / Track Cache constants
TotalBuffers equ 36+4 ; buffer storage for 36 sectors (+ 4 temporary buffers)
DMABufLength equ 1024*44 ; length of our floppy DMA buffer
DBDMABufSize equ 1024*44 + 2*DBDMADescriptor.size
; Error handling / recovery constants
RecalsMax equ 1 ; Max Recals allowed before hard error
IOErrorsMax equ 2 ; Max I/O errors allowed before Recal
IntBlock record 0,INCREMENT
intCompletion ds.l 1 ; our specific completion address
intRegSave ds.l 4 ; space to save registers a3,d1-d3
IntBlockSize equ * ; size of our block
endr
TimerBlock record {TMPBStart},INCREMENT
myCompletion ds.l 1 ; our specific completion address
regSave ds.l 4 ; space to save registers a3,d1-d3
TMPBStart ds.b tmXQSize ; allocate a time manager block
TimerBlockSize equ *-myCompletion ; size of our block
endr
TimeMgrOffset equ TimerBlock.TMPBStart-TimerBlock.myCompletion
MyDriveInfo record 0,increment ; drive specific variables
WriteProtected ds.b 1 ; bit7=1=write protected
DiskInPlace ds.b 1 ; 0 = no disk place, 1 or 2 = disk in place
Installed ds.b 1 ; 0 = don't know, 1=installed, $FF=not installed
Sides ds.b 1 ; bit7=0=single sided, bit7=1=double sided
DriveQElement ds.b dQDrvSz2+2
MyDriveInfoSize equ * ; size of drive specific variables
endr
DriveStatusRec record 0,increment ; disk status results
StatWriteProt ds.b 1 ; bit7=1=write protected
StatDiskInPlace ds.b 1 ; 0 = no disk place, 1 or 2 = disk in place
StatInstalled ds.b 1 ; 0 = don't know, 1=installed, $FF=not installed
StatSides ds.b 1 ; bit7=0=single sided, bit7=1=double sided
StatTwoSidedFmt ds.b 1 ; FF=2 sided format
StatNewInterface ds.b 1 ; FF=new interface, 00 = old
StatDiskErrors ds.w 1 ; errors for this drive
StatCurrentFmt ds.b 1 ; current format of disk
StatFmtAllowed ds.b 1 ; allowed formats for disk
StatDriveInfoB3 ds.b 1 ; start of data for drive info control call
StatDriveInfoB2 ds.b 1 ; high 2 bytes unused for now
StatDriveAttr ds.b 1 ; disk drive attributes
StatDriveType ds.b 1 ; type of disk drive
StatDiskSize ds.w 1 ; # of blocks on disk
DriveStatusRecSize equ *
endr
;------------------------------------------------------------------------------------
SWIM3Vars record {PhysDriveNumber},INCREMENT
SWIMVarsStart equ *
; SWIM3 device register vector table, filled-in by SWIM3DiskOpen.
; This is necessary to allow this driver to be used on machines with varying
; SWIM3 device register spacings, e.g., PDM and TNT.
;
; Added 1/25/94 by Monte Benaresh
SWIM3RegVects
vTimer ds.l 1
vError ds.l 1
vParamData ds.l 1
vPhase ds.l 1
vSetup ds.l 1
vZeroes ; shared with Mode
vMode ds.l 1
vOnes ; shared with Handshake
vHandshake ds.l 1
vInterrupt ds.l 1
vStep ds.l 1
vCurTrack ds.l 1
vCurSect ds.l 1
vGap ; shared with FormatByte
vFormatByte ds.l 1
vFirstSector ds.l 1
vSectorsToXfer ds.l 1
vIntMask ds.l 1
; Next vectors added 12/15/93 by Monte Benaresh
vClearDMAInt ds.l 1 ; clear floppy DMA interrupt
vStartDMAAction ds.l 1 ; enable floppy DMA interrupt, start SWIM3 action with timeout
vSetUpDMAXfer ds.l 1 ; set up address, count, and direction for DMA transfer
vStopDMA ds.l 1 ; stop the DMA controller
vSelectDrive ds.l 1 ; Low level routine vectors
vAddrAndStrb ds.l 1 ; (negative offset from globals ptr)
vAddrAndSense ds.l 1 ;
vSeekTrack ds.l 1 ;
vReCalibrate ds.l 1 ;
vStartReadyTimer ds.l 1 ;
vWaitDriveReady ds.l 1 ;
vTurnOnDrive ds.l 1 ;
vTurnOffDrive ds.l 1 ;
vFmtTrack ds.l 1 ;
vWriteTrack ds.l 1 ;
vReadTrack ds.l 1 ;
vRawTrackRead ds.l 1 ;
vReadSectorHdr ds.l 1 ;
; ••• drive dependent variables (start of positive offset globals)
PhysDriveNumber ds.b NumberOfDrives ; physical drive number
DriveKind ds.b NumberOfDrives ; kind of drive attached
MediaKind ds.b NumberOfDrives ; kind of disk media in drive
DiskFormat ds.b NumberOfDrives ; format of disk in drive
CylinderValid ds.b NumberOfDrives ; current Cylinder (high byte)
CurrentCylinder ds.b NumberOfDrives ; current Cylinder (low byte)
ErrorCount ds.w NumberOfDrives ; Number of errors
DriveInfoPtrs ds.l NumberOfDrives ; 0 based list of pointers to MyDriveInfo
FirstDriveInfo ds.b NumberOfDrives*MyDriveInfo.MyDriveInfoSize
CurrentStatus ds.b DriveStatusRec.DriveStatusRecSize ; our current drives status
ControlCallInfo ds.l 1 ; pointer to control call dispatching table
ds.w 1 ; number of Control Calls defined - 1
ds.w 1 ; default error if not found (ControlErr)
ds.b MaxControlCalls*8 ; 2 byte csCode, 2 bytes flags, 4 bytes routine ptr
StatusCallInfo ds.l 1 ; pointer to status call dispatching table
ds.w 1 ; number of Status Calls defined - 1
ds.w 1 ; default error if not found (StatusErr)
ds.b MaxStatusCalls*8
; drive independent variables
DCEpointer ds.l 1 ; ptr to DCE
DriverActive ds.b 1 ; idle = 0, active <> 0
CacheEnabled ds.b 1 ; $FF=enabled, $00=disabled
ReqDriveNumber ds.w 1 ; requested drive for current operation
CurrentDrive ds.w 1 ; drive currently selected or powered up
LastActiveDrive ds.w 1 ; drive number of last successfully accessed drive
SWIM3IntVectors ; vectors for the 5 SWIM3 interrupts
TimeIntPB ds.b IntBlock.IntBlockSize ; timer interrupt
StepIntPB ds.b IntBlock.IntBlockSize ; step complete interrupt
IdIntPB ds.b IntBlock.IntBlockSize ; id read interrupt
DoneIntPB ds.b IntBlock.IntBlockSize ; sector data read interrupt
SenseIntPBr ds.b IntBlock.IntBlockSize ; sense line interrupt
PollingTMPB ds.b TimerBlock.TimerBlockSize ; timer used for drive polling task
SettledTMPB ds.b TimerBlock.TimerBlockSize ; timer used for Motor speed settled ≈1.5% timing
ReadyTMPB ds.b TimerBlock.TimerBlockSize ; timer used for Drive Ready / Sleep delay processing
ReadWriteTMPB ds.b TimerBlock.TimerBlockSize ; timer used for DMA/SWIM transfer timeouts
MediaIconPtr ds.l 1 ; ptr to our media icon
Drive1PhysIcon ds.l 1 ; ptr to our primary drive icon
Drive2PhysIcon ds.l 1 ; ptr to our secondary drive icon
AsyncPc ds.l 1 ; saved PC when waiting asynchronously
ReCalPc ds.l 1 ; PC when waiting in ReCalibrate
SeekPc ds.l 1 ; PC when waiting for Seek to send pulses to drive
ReadHdrPc ds.l 1 ; PC when waiting for a header to read
SettleTime ds.l 1 ; settle time after drive ready
WaitReadyCount ds.w 1 ; timeout counter for WaitDriveReady
ALIGN 4
DMARegSave ds.l 4 ; space to save registers a3,d1-d3 during DMA xfer
DMACompletionPtr ds.l 1 ; completion routine for DMA
DMABaseAddr ds.l 1 ; ptr to DMA controller in I/O space
FloppyDMAStart ds.l 1 ; offset to start of floppy DMA buffer
TrackCacheStart ds.l 1 ; ptr to start of floppy DMA buffer (logical address)
; next vars added 12/17/93 for TNT by Monte Benaresh
DBDMABufPtr ds.l 1 ; ptr to buffer for channel command descriptors
; and track cache
DBDMADescPtr ds.l 1 ; ptr to our channel command descriptors
; Track Cache variables
SectorsToRead ds.b 2 ; # sectors needed for each head on current track (ReadTrack)
SectorsToWrite equ SectorsToRead ; sectors/side to write (SWIM3Write)
FmtCopyVerify equ SectorsToRead ; $FF = verify after format/copy operation (Format/Copy)
NumSectors ds.w 1 ; sector counter (WriteTrack)
WriteDMAPtr ds.l 1 ; temporary DMA buffer physical ptr (for writes)
WritePtr ds.l 1 ; temporary DMA buffer logical ptr (for writes and verifies)
VerifyPtr ds.l 1 ; ptr to our verify buffer (logical)
NibbilizeDMAPtr ds.l 1 ; ptr into DMA buffer for Nibbilize routine
TrackCacheDMAPtr ds.l TotalBuffers ; offsets to each DMA sector buffer for a track
TrackCachePtr ds.l TotalBuffers ; logical address of each DMA sector buffer for a track
SectorBufPtr ds.w TotalBuffers ; buffer numbers for each sector,
SectorValid equ 15-8 ; bit 15=1 if valid
SectorNeeded equ 14-8 ; bit 14=1 if needed for this read/write
TCTrackNum ds.w 1 ; track # in track cache
SearchMode ds.w 1 ; search mode (for RawTrackRead)
ByteCount ds.l 1 ; # of bytes to read (for RawTrackRead)
TagBufPtr ds.l 1 ; pointer to users tag buffer (for read/write)
CurTagPtr ds.l 1 ; pointer into users tag buffer (for read/write)
UserTagsPtr ds.l 1 ; pointer to tag buffer (for format)
UserBufPtr ds.l 1 ; pointer to data buffer (for format)
ClockBuffer equ UserTagsPtr ; pointer to clock buffer (for RawTrackRead)
DataBuffer equ UserBufPtr ; pointer to data buffer (for RawTrackRead)
SectHdrCylinder ds.b 1 ; Cylinder number from sector header
SectHdrHead ds.b 1 ; Head number from sector header
SectHdrSector ds.b 1 ; Sector number from sector header
SectHdrFmtKind ds.b 1 ; Format Kind or Block Size from sector header
SectHdrHandshake ds.b 1 ; Handshake register after reading last byte
SectHdrError ds.b 1 ; Error register after reading last byte
SeekingCylinder ds.b 1 ; cylinder number that we are seeking
StateFlags ds.b 1 ; driver state flags
TrackTimer equ 0 ; bit 0 = track timeout installed
LogicalBlock ds.l 1 ; Logical Block number input to BlockToPhysAddr
BlockCount ds.l 1 ; # of blocks to read/write
BlockAddress ds.l 1 ; address of user buffer for read/write
PhysCylinder ds.b 1 ; Physical Cylinder returned by BlockToPhysAddr
PhysHead ds.b 1 ; Physical Head returned by BlockToPhysAddr
PhysSector ds.b 1 ; Physical Sector (head relative) returned by BlockToPhysAddr
PhysCylSector ds.b 1 ; Physical Sector (cylinder relative) returned by BlockToPhysAddr
PhysSectsPerCyl ds.b 1 ; Number of Sectors per cylinder returned by BlockToPhysAddr
PhysSectsPerHead ds.b 1 ; Number of Sectors per head returned by BlockToPhysAddr
PhysCylBias ds.b 1 ; Starting Cylinder number of speed group
CurSectsPerHead ds.b 1 ; number of sectors per head for current cylinder
Interleave ds.b 1 ; sector interleave factor
ds.b 1 ; align
RecalsLeft ds.w 1 ; number of Recals remaining before hard error
IOErrorsLeft ds.w 1 ; number of I/O errors remaining before Recal
SectorGapSize ds.b 1 ; number of gap bytes or sync groups to fmt between sectors
FmtByteBlkSize ds.b 1 ; GCR Format byte / MFM block size for formatting
SectorMap ds.b MaxSectors ; sector interleave buffer for formatting
MotorTimingOut ds.b 1 ; number of polls to left before turning drive off
SWIMVarsSize equ *-SWIMVarsStart ; number of bytes of SWIMVars
endr
;==================================================================================================
;----------------------------------------------------------------------
; Decode table for Status Calls (gets copied into RAM for patching)
;----------------------------------------------------------------------
SWIM3StatusDecode
dc.w FmtLstCode ; Get Format List status call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3GetFormatList-SonyHeader
dc.w drvStsCode ; Drive Status call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3DriveStatus-SonyHeader
dc.w MFMStsCode ; MFM Status call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3MFMStatus-SonyHeader
dc.w DupVerSts ; duplicator version status call
dc.b 0
dc.b (0<<ChkDriveNum)+\
(0<<ChkDriveExists)
dc.w SWIM3DupVersion-SonyHeader
dc.w FmtByteSts ; get format byte status call
dc.b 0
dc.b (0<<ChkDriveNum)+\
(0<<ChkDriveExists)
dc.w SWIM3GetFmtByte-SonyHeader
assert (*-SWIM3StatusDecode)=(TotalStatusCalls*6)
;----------------------------------------------------------------------
; Decode table for Control Calls (gets copied into RAM for patching)
;----------------------------------------------------------------------
SWIM3ControlDecode
dc.w killCode ; KillIO control call
dc.b 0
dc.b (0<<ChkDriveNum)+\
(0<<ChkDriveExists)
dc.w SWIM3CtlKillIO-SonyHeader
dc.w VerifyCC ; Verify control call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3CtlVerify-SonyHeader
dc.w FormatCC ; Format control call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3CtlFormat-SonyHeader
dc.w EjectCode ; Eject control call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3CtlEject-SonyHeader
dc.w tgBuffCode ; Set Tag Buffer control call
dc.b 0
dc.b (0<<ChkDriveNum)+\
(0<<ChkDriveExists)
dc.w SWIM3CtlTagBuf-SonyHeader
dc.w TCacheCC ; Track Cache control call
dc.b 0
dc.b (0<<ChkDriveNum)+\
(0<<ChkDriveExists)
dc.w SWIM3CtlTrkCache-SonyHeader
dc.w IconCC ; Physical Drive Icon control call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3CtlPhysIcon-SonyHeader
dc.w IconLogCC ; Disk Media Icon control call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3CtlLogIcon-SonyHeader
dc.w InfoCC ; Get Drive Info control call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3CtlDrvInfo-SonyHeader
dc.w FmtCopyCC ; Format and copy control call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3CtlFmtCopy-SonyHeader
dc.w GetRawDataCC ; Read raw data control call
dc.b 0
dc.b (1<<ChkDriveNum)+\
(1<<ChkDriveExists)
dc.w SWIM3CtlGetRawData-SonyHeader
assert (*-SWIM3ControlDecode)=(TotalControlCalls*6)
;----------------------------------------------------------------------
; Patch vector table for low level routines
;----------------------------------------------------------------------
ptchVectTable dc.w jSelectDrive-SonyHeader ; routine offset 0
dc.w jAddrAndStrb-SonyHeader ; routine offset 1
dc.w jAddrAndSense-SonyHeader ; routine offset 2
dc.w jSeekTrack-SonyHeader ; routine offset 3
dc.w jRecalibrate-SonyHeader ; routine offset 4
dc.w jStartReadyTimer-SonyHeader ; routine offset 5
dc.w jWaitDriveReady-SonyHeader ; routine offset 6
dc.w jTurnOnDrive-SonyHeader ; routine offset 7
dc.w jTurnOffDrive-SonyHeader ; routine offset 8
dc.w jFmtTrack-SonyHeader ; routine offset 9
dc.w jWriteTrack-SonyHeader ; routine offset 10
dc.w jReadTrack-SonyHeader ; routine offset 11
dc.w jRawTrackRead-SonyHeader ; routine offset 12
dc.w jReadSectorHdr-SonyHeader ; routine offset 13
assert (*-ptchVectTable)=(TotalVectors*2)
with SWIM3Vars
;_______________________________________________________________________
;
; Routine: SWIM3DiskOpen
; Inputs: a0 - pointer to I/O ParamBlock
; a1 - pointer to Device Control Entry (DCE)
; Outputs: d0 - Result Code (noErr/openErr)
; Destroys: a0-a4,d0-d2
; Calls: none
; Called by: Device Manager
;
; Function: Driver initialization routine
;
;_______________________________________________________________________
SWIM3DiskOpen ; all regs saved by Device Manager
move.l #SWIM3Vars.SWIMVarsSize,d0
_NewPtr ,Sys,CLEAR
adda.l #PhysDriveNumber-\
SWIMVarsStart,a0 ; point to the start offset 0 of globals
move.l a0,SonyVars ; save in lowmem
move.l a1,DCEpointer(a0) ; save a copy of the DCE pointer
move.b #Version,DCtlQueue+1(a1) ; put our version number in
movea.l a0,a1 ; a1 = sonyvars ptr
clr.l DBDMABufPtr(a1) ; init to nil
TestFor AMICExists ; do we have AMIC DMA?
bne @AMICInit ; if so, go do it
@GrandCentralInit ; init for Grand Central DMA, added
; 12/17/93 by Monte Benaresh
; Fill the SWIM3 device register address table
move.l IWM,a0 ; base address of SWIM3
move.l #$00000010,d0 ; register spacing
bsr @FillSWIM3RegTable ; fill in the table
; Allocate memory for track cache and 2 DB-DMA channel command descriptors.
; The command descriptors must be aligned on an even $10 boundary, so we add
; $F in case we have to round down.
move.l #DBDMABufSize+$F,D0 ; allocate a track buffer
_NewPtr ,SYS,CLEAR
bne @openErr ; br if we couldn't get it
move.l a0,DBDMABufPtr(a1) ; save it un-aligned
movea.l #DBDMABufSize+$F,a1 ; get size again
_LockMemoryContiguous
move.l SonyVars,a1 ; restore globals ptr
tst.l d0 ; did it fail?
bne @openErr
move.l DBDMABufPtr(a1),d0
add.l #$F,d0 ; align to next $10-byte boundary
andi #$FFFFFFF0,d0
move.l d0,DBDMADescPtr(a1) ; save our descriptor ptr
add #2*DBDMADescriptor.size,d0
move.l d0,TrackCacheStart(a1) ; save logical address of floppy DMA buffer
; translate to physical address
subq.l #8, sp ; make room for physical result
move.l #DMABufLength,-(sp) ; parm: length
move.l d0, -(sp) ; parm: logical address
move.l sp, A0 ; pass in addr of log to phys table
move.l #1, A1 ; setup the count
_GetPhysical ; do the translation
addq.l #8, sp ; forget about logical addr/count
move.l (sp)+,d0 ; get physical address (result)
addq.l #4, sp ; pop physical count
move.l SonyVars,a1 ; restore globals ptr
move.l d0,FloppyDMAStart(a1) ; save starting offset to floppy DMA buffer
_GCBaseAddr ; get base addr of Grand Central DMA Chip
move.l a0,DMABaseAddr(a1) ; save in our globals
; our second channel command descriptor must be a STOP command, so
; set that up now
move.l DBDMADescPtr(a1),d0 ; get ptr to our descriptors
add #DBDMADescriptor.size,d0 ; point to 2nd one
move.l d0,a0
move.l #(STOP<<16) | $0000,d0 ; stop command / count field is reserved
lea 0,a1 ; address field is reserved
_MakeCCDescriptor
move.l SonyVars,a1 ; restore globals ptr
; install our SWIM3 and floppy DMA interrupt handlers
move.l #gcifDevSwim3,d0 ; specify int source
lea SWIM3IntDispatch,a0 ; get ptr to our handler
; a1 = refcon = SonyVars
_GCRegisterHandler
move.l #gcifDmaFloppy,d0
lea DMAInterruptHandler,a0 ; get ptr to our handler
_GCRegisterHandler
; enable SWIM3 device interrupts in Grand Central
_GCEnableInterruptSource gcifDevSwim3
; init vectors to DMA-specific routines
lea jSetUpGCDMAXfer,a0 ; get the routine address
move.l a0,vSetUpDMAXfer(a1) ; install the vector
lea jClearGCDMAInt,a0 ; get the routine address
move.l a0,vClearDMAInt(a1) ; install the vector
lea jStartGCDMAAction,a0 ; get the routine address
move.l a0,vStartDMAAction(a1) ; install the vector
lea jStopGCDMA,a0 ; get the routine address
move.l a0,vStopDMA(a1) ; install the vector
bra @genericInit ; join common init code
@AMICInit
; Fill the SWIM3 device register address table
move.l IWM,a0 ; base address of SWIM3
move.l #$00000200,d0 ; register spacing
bsr @FillSWIM3RegTable ; fill in the table
lea AMIC_DMA_BASE_REG,a0 ; get ptr to DMA Base Register
move.l a0,DMABaseAddr(a1) ; save in our globals (for now)
bset.b #DMARST,DMAFloppyCS(a0) ; reset floppy DMA reg, to init base address
move.b DMAFloppyBase+0(a0),d0 ; get the physical address of the floppy DMA buffer
asl.l #8,d0
move.b DMAFloppyBase+1(a0),d0
asl.l #8,d0
move.b DMAFloppyBase+2(a0),d0
asl.l #8,d0
move.b DMAFloppyBase+3(a0),d0
move.l d0,FloppyDMAStart(a1) ; save starting offset to floppy DMA buffer
move.b AMIC_DMA_BASE_ADDR3(a0),d1 ; get the physical address of the floppy DMA buffer
asl.l #8,d1
move.b AMIC_DMA_BASE_ADDR2(a0),d1
asl.l #8,d1
move.b AMIC_DMA_BASE_ADDR1(a0),d1
asl.l #8,d1
move.b AMIC_DMA_BASE_ADDR0(a0),d1
sub.l d1,d0 ; calc floppy DMA buffer offset from start of DMA buffer
addi.l #$61000000,d0 ; add in logical start of DMA buffer
move.l d0,TrackCacheStart(a1) ; save logical address of Floppy DMA buffer
with ExpandMemRec, DMADispGlobals
movea.l ([ExpandMem],emDMADispatchGlobals),a0
moveq #hwAmicFDC,d0
move.l a1,ddRefCon0(a0,d0.l*8) ; refcon = globals ptr
lea DMAInterruptHandler,a1
move.l a1,ddVector0(a0,d0.l*8) ; handler = our DMA handler
endwith
lea SWIM3IntDispatch,a0 ; get ptr to our handler
move.l a0,VIA2DT+(4*5) ; SWIM3 is VIA 2 interrupt, bit 5
movea.l VIA2RBV,a0 ; get ptr to VIA2/RBV base
move.b #%10100000,RvIER(a0) ; enable the floppy interrupt at the VIA
move.l SonyVars,a1 ; restore globals ptr
lea jSetUpAMICDMAXfer,a0 ; get the routine address
move.l a0,vSetUpDMAXfer(a1) ; install the vector
lea jClearAMICDMAInt,a0 ; get the routine address
move.l a0,vClearDMAInt(a1) ; install the vector
lea jStartAMICDMAAction,a0 ; get the routine address
move.l a0,vStartDMAAction(a1) ; install the vector
lea jStopAMICDMA,a0 ; get the routine address
move.l a0,vStopDMA(a1) ; install the vector
@genericInit
; Initialize the ICON pointers
movea.l SonyVars,a1
movea.l UnivInfoPtr,a2 ; <SM10>
adda.l ProductInfo.IconInfoPtr(a2),a2 ; point to icon info table for this machine <SM10>
move.l 0(a2),d0 ; offset to media icon
bsr @getIconPtr ; get the icon pointer
move.l a3,MediaIconPtr(a1) ; save it
move.l 4(a2),d0 ; offset to primary drive icon
bsr @getIconPtr ; get the icon pointer
move.l a3,Drive1PhysIcon(a1) ; save it
move.l 14(a2),d0 ; offset to secondary drive icon
bsr @getIconPtr ; get the icon pointer
move.l a3,Drive2PhysIcon(a1) ; save it
;movea.l IWM,a2 ; SWIM3 base address in a2
clr.b ([vIntMask,a1]) ; make sure the enable mask is 0
move.b #EnableInts,([vOnes,a1]) ; enable SWIM3 interrupts at the chip
; next code moved to machine-specific section above 12/17/93 by Monte Benaresh
;lea SWIM3IntDispatch,a0 ; get ptr to our handler
;move.l a0,VIA2DT+(4*5) ; SWIM3 is VIA 2 interrupt, bit 5
;movea.l VIA2RBV,a0 ; get ptr to VIA2/RBV base
;move.b #%10100000,RvIER(a0) ; enable the floppy interrupt at the VIA
; Install the vectors for Prime and Control
lea jtSWIM3DiskPrime,a0 ; get the routine address
move.l a0,JDiskPrime ; install the vector
lea jtSWIM3DiskControl,a0 ; get the routine address
move.l a0,JControl ; install the vector
; Install the vectors for low level driver routines
lea vSelectDrive(a1),a0 ; point to start of vector globals
lea ptchVectTable,a3 ; point to start of vector table
moveq #TotalVectors-1,d1
@vectLoop lea SonyHeader,a4 ; get driver base address
adda.w (a3)+,a4 ; convert offset to absolute address
move.l a4,(a0)+
dbra d1,@vectLoop ; process the whole table
; initialize the Control and Status Call Info tables
lea ControlCallInfo(a1),a0 ; a0 <- address of table in globals
lea SWIM3ControlDecode,a3 ; a3 <- address of table in ROM
moveq #controlErr,d0 ; d0 <- default error code
moveq #TotalControlCalls-1,d1 ; d1 <- loop counter
bsr.w @InstallCallInfo ; install the ControlCallInfo
lea StatusCallInfo(a1),a0 ; a0 <- address of table in globals
lea SWIM3StatusDecode,a3 ; a3 <- address of table in ROM
moveq #statusErr,d0 ; d0 <- default error code
moveq #TotalStatusCalls-1,d1 ; d1 <- loop counter
bsr @InstallCallInfo ; install the StatusCallInfo
; initialize the SWIM
* ;clr.w CurrentDrive(a1) ; initialize CurrentDrive to default invalid drive
bsr InitSWIMChip ; initialize the chip
bne.s @openErr ; couldn't init, error
; assign drive numbers, and add drive queue elements to the drive queue
with MyDriveInfo
lea FirstDriveInfo(a1),a4 ; a4 <- pointer to MyDriveInfo for drive
lea DriveInfoPtrs(a1),a3 ; a3 <- pointer to DriveInfoPtrs list entry
moveq #0,d1 ; init logical drive #
moveq #0,d2 ; init physical drive #
@floppyLoop move.w d2,d0 ; get drive # for SelectDrive
bsr SelectDrive ; attempt to select the drive
beq.s @NextFloppy ; if drive couldn't be selected
bsr FindDriveKind ; see what kind of drive we found
cmpi.b #SSGCRDriveKind+1,d0 ; see if it's a floppy drive we support
blt.s @NextFloppy ; ignore noDrive, unknown, and 400K drives
cmpi.b #DSMFMGCRDriveKind,d0 ;
bgt.s @nextFloppy ; ignore HD20's also
addq.w #1,d1 ; found a live one, count the logical drive
move.b d0,DriveKind(a1,d1.w) ; record the drive kind
move.b d2,PhysDriveNumber(a1,d1.w) ; record physical drive number
move.l a4,(a3) ; install pointer to per drive info
move.b #1,Installed(a4) ; indicate that drive is installed
move.w d1,d0 ; prepare to insert logical drive number
swap d0
move.w #SonyRefNum,d0 ; d0 <- driver RefNum in low 16 bits
lea DriveQElement(a4),a0 ; a0 <- pointer to Drive Queue Element
_AddDrive ; add the drive to the drive queue
adda.w #MyDriveInfoSize,a4 ; point to next MyDriveInfo
@nextFloppy addq.l #4,a3 ; point to next DriveInfoPtr
addq.w #1,d2 ; go onto next physical drive
cmpi.w #MaxDriveNum,d2 ; see if past the range of floppies
ble.s @floppyLoop ; loop through all floppy drive numbers
endwith
bsr TurnOffDrive ; de-select all drives
st.b CacheEnabled(a1) ; enable our track cache
bsr PollingTask ; start up our disk insert/eject polling task
moveq #0,d0 ; no error
@openExit rts
@openErr
move.l DBDMABufPtr(a1),a0 ; get command descriptor & track buffer ptr
beq @noDescPtr ; never allocated, don't dispose
movea.l #DBDMABufSize+$F,a1 ; get size
_UnlockMemory
move.l SonyVars,a1 ; restore globals ptr
move.l DBDMABufPtr(a1),a0
_DisposPtr
@noDescPtr
movea.l SonyVars,a0
_DisposPtr ; release our globals
moveq #-1,d0
move.l d0,SonyVars ; clear the lowmem ptr
moveq #OpenErr,d0
bra.s @openExit
;---------------------
@getIconPtr lea (a2,d0.l),a3 ; assume relative to table
bclr.l #0,d0 ; test & clear 'use ROM table' bit
beq.s @IconPtrOK ; branch if should use current table
movea.l UnivInfoPtr,a3 ; <SM10>
adda.l ProductInfo.IconInfoPtr(a3),a3 ; point to icon info table for this machine <SM10>
adda.w d0,a3 ; add in relative offset
@IconPtrOK rts ; ptr to icon is in a3 now
;---------------------
@InstallCallInfo
move.l a0,(a0) ; save the pointer to the table (for patching)
addq.l #4,(a0)+ ; point to loop counter
move.w d1,(a0)+ ; install the loop counter
move.w d0,(a0)+ ; install the default error code
@CallInfoLoop
move.l (a3)+,(a0)+ ; install csCode, IOProutine, Flags
lea SonyHeader,a4 ; get driver base address
adda.w (a3)+,a4 ; convert relative addr to absolute
move.l a4,(a0)+ ; install routine address
dbra d1,@CallInfoLoop ; process the whole table
rts ; all done
@FillSWIM3RegTable ; init vectors to regularly-spaced SWIM3 registers
; on entry:
; A0 = ptr to SWIM3 register base
; A1 = ptr to SonyVars
; D0 = register spacing
move.l #15-1,d1 ; do for 15 vectors
lea SWIM3RegVects(a1),a2
@SWIM3RegLoop
add.l d0,a0 ; start with 2nd physical register
move.l a0,(a2)+
dbra d1,@SWIM3RegLoop
rts
;_______________________________________________________________________
;
; Routine: SWIM3DiskClose
; Inputs: a0 - pointer to I/O ParamBlock
; a1 - pointer to Device Control Entry (DCE)
; Outputs: d0 - Result Code (noErr/openErr)
; Destroys:
; Calls: none
; Called by: Device Manager
;
; Function: Driver close routine
;_______________________________________________________________________
SWIM3DiskClose moveq #ClosErr,d0 ; report error: can not close at present
rts
;_______________________________________________________________________
;
; Routine: SWIM3Control/SWIM3Status
; Inputs: a0 - pointer to I/O ParamBlock
; a1 - pointer to Device Control Entry (DCE)
; Outputs: d0 - Result Code (noErr/openErr)
; Destroys:
; Calls: none
; Called by: Device Manager
;
; Function: Driver close routine
;_______________________________________________________________________
SWIM3DiskStatus movea.w #StatusCallInfo,a2 ; offset of decode table
bra.s SWIM3ControlStatus ; join common code (fall into it)
SWIM3DiskControl
move.l JControl,-(sp) ; all regs saved by Device Manager
rts
jtSWIM3DiskControl
movea.w #ControlCallInfo,a2 ; offset of decode table
* bra.s SWIM3ControlStatus ; join common code
SWIM3ControlStatus
movea.l SonyVars,a1 ; get pointer to globals
st.b DriverActive(a1) ; driver is busy
movea.l (a1,a2.l),a2 ; get pointer to table
move.w (a2)+,d1 ; d1 <- loop counter
move.w (a2)+,d0 ; d0 <- default error code
move.w csCode(a0),d2 ; d2 <- csCode to search for
@SearchLoop cmp.w (a2),d2 ; check for a match
addq.l #8,a2 ; point to next table entry
dbeq d1,@SearchLoop ; search through all table entries
bne.s Swim3DiskDone ; illegal csCode, return with error
moveq #NoErr,d0 ; default to no error
movea.l -(a2),a3 ; get address of routine
move.w IODrvNum(a0),d1 ; get the drive number
move.b -(a2),d2 ; get flags
assert ChkDriveNum=7 ; flag is in sign bit
bpl.s @DriveNumInRange ; if flag to check drive num is not set
tst.w d1 ; is drive num <= 0?
ble.s @badDrvNum ; yes, exit
cmpi.w #MaxDriveNum,d1 ; <= max drive num?
bls.s @DriveNumInRange ; yes, it's valid
@badDrvNum moveq #NSDrvErr,d0 ; else, bad drive number
bra.s Swim3DiskDone ; drive num out of range, return with error
@DriveNumInRange
assert ChkDriveExists=ChkDriveNum-1 ; next flag is ChkDriveExists
add.b d2,d2 ; test next flag
bpl.s @DriveExists ; if flag to check drive exists is not set
tst.l DriveInfoPtrs(a1,d1.w*4) ; test to see if it exists
bne.s @DriveExists ; if valid drive
moveq #NoDriveErr,d0 ; non-existent drive error
bra.s Swim3DiskDone ; drive doesn't exists, return with error
@DriveExists
lea csParam(a0),a0 ; a0 points to csParam
move.w d1,ReqDriveNumber(a1) ; save Drive Number in globals
;movea.l IWM,a2 ; a1=globals, a2=SWIM ptr
jmp (a3) ; jump into the routine
;--------------------------------------------
Swim3DiskDone clr.b DriverActive(a1) ; driver is free now
move.l JIODone,-(sp) ; prepare to return to IODone
movea.l DCEPointer(a1),a1 ; a1 <- DCE (param to IODone)
ext.l d0 ; check for errors
beq.s @noError ; if no error
move.w d0,DskErr ; save error in low memory global DskErr
@noError rts ; return to IODone
;_______________________________________________________________________
;
; Routine: DiskPrime
; Inputs: a0 - pointer to I/O ParamBlock
; a1 - pointer to Device Control Entry (DCE)
; Outputs: d0 - Result Code (noErr/openErr)
; Destroys:
; Calls: none
; Called by: Device Manager
;
; Function: Driver read/write routine
;_______________________________________________________________________
SWIM3DiskPrime move.l JDiskPrime,-(sp) ;
rts ;
jtSWIM3DiskPrime ;
movea.l a1,a2 ; DCE ptr in a2
movea.l SonyVars,a1 ; get ptr to our globals
st.b DriverActive(a1) ; driver is active
moveq #NSDrvErr,d0 ; assume Bad drive number
move.w IODrvNum(a0),d1 ; get the logical drive number
cmpi.w #MaxDriveNum,d1 ; see if way out of range
bgt.s @PrimeErr ; if invalid drive number
move.w d1,ReqDriveNumber(a1) ; save it
moveq #ParamErr,d0 ; assume parameter error
moveq #9,d1 ; shift amount for divide by 512
move.l dCtlPosition(a2),d2 ; get byte position
ror.l d1,d2 ; convert to block number
move.l d2,LogicalBlock(a1) ; setup block number
rol.l d1,d2 ; convert back to byte position
andi.w #$01FF,d2 ; test for mod 512
bne.s @PrimeErr ; if error in dCtlPosition parameter
move.l ioBuffer(a0),BlockAddress(a1) ; save user buffer address
move.l ioByteCount(a0),d2 ; get byte count
ror.l d1,d2 ; convert to block count
move.l d2,BlockCount(a1) ; setup block count
rol.l d1,d2 ; convert back to byte count
andi.w #$01FF,d2 ; test for mod 512
bne.s @PrimeErr ; if error in byte count parameter
;movea.l IWM,a2 ; get ptr to SWIM chip
cmpi.b #aRdCmd,ioTrap+1(a0) ; check for read
bne SWIM3Write ; if not read, must be a write
jmp SWIM3Read ; else, call our read routine
@PrimeErr bra.s Swim3DiskDone ; complete the call with error
;_______________________________________________________________________
;
; Routine: jmp SWIM3GetFormatList
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Returns a list of possible drive formats. The information for each
; possible disk format is an 8-byte record:
; Byte 0-3: disk capacity in blocks
; (is [#tracks][#heads][#sectors][#bytes/sector])
; 4: bit 7=1: number of tracks, sides, sectors is valid
; 6=1: current disk has this format
; 5=0: reserved for future expansion, should be zero
; 4=0: single density, =1: double density
; 0-3: number of heads (or zero if don't care)
; 5: number of sectors (or zero if don't care)
; 6-7: number of tracks (or zero if don't care)
;_______________________________________________________________________
SWIM3GetFormatList
with MyDriveInfo,DriveStatusRec
bsr GetDriveStatus ; update our status record
moveq #offLinErr,d0 ; default error in case no disk in place.
movea.l DriveInfoPtrs(a1,d1.w*4),a2 ; a2 = ptr to drive info for this drive
cmpi.b #2,DiskInPlace(a2) ; see if disk is online and clamped
blt Swim3DiskDone ; if no disk, or format unknown, offLinErr
lea CurrentStatus(a1),a2 ; get ptr to our status record
moveq #0,d3
moveq #0,d4
move.b StatFmtAllowed(a2),d3 ; get allowable formats bitmask
move.b StatCurrentFmt(a2),d4 ; get current format bitmask
@doList move.w (a0)+,d2 ; d2 <- max list size
ble Swim3DiskDone ; no room to return anything
movea.l (a0),a2 ; get ptr to format list buffer
lea @FmtTbl,a3 ; a3 <- pointer to list template
clr.w -(a0) ; returned list size := 0
moveq #0,d0 ; d0 <- bit index into lists
@loop bclr.l d0,d3 ; test and clear allowable format bit
beq.s @next ; bit wasn't set, try next
move.l (a3,d0.w*8),(a2)+ ; copy disk size
move.l 4(a3,d0.w*8),(a2)+ ; copy attributes
bclr.l d0,d4 ; test and clear current format bit
beq.s @currentDone ; if not the current format
bset.b #6,-4(a2) ; set the 'is current format' bit
@currentDone addq.w #1,(a0) ; increment result list count
subq.w #1,d2 ; decrement space left
ble.s @done ; if result list is now full
@next addq.w #1,d0 ; increment template list / bit index
tst.l d3 ; see if all formats found
bne.s @loop ; loop through remaining list
@done moveq #noErr,d0 ; return good status
bra Swim3DiskDone ; return
endwith
;----------------------------------------------------------------------
; The information for each possible disk format is an 8-byte record:
; Byte 0-3: disk capacity in blocks
; (is [#tracks][#heads][#sectors][#bytes/sector])
; 4: bit 7=1: number of tracks, sides, sectors is valid
; 6=1: current disk has this format
; 5=0: reserved for future expansion, should be zero
; 4=0: single density, =1: double density
; 0-3: number of heads (or zero if don't care)
; 5: number of sectors (or zero if don't care)
; 6-7: number of tracks (or zero if don't care)
@FmtTbl
dc.l 38965 ; HD-20
dc.b (%0100<<4)+0,0,0,0 ; THS invalid, current disk has this format
dc.l 400*2 ; 400K GCR
dc.b (%1000<<4)+1 ; THS valid, SD, 1 head
dc.b 10 ; 10 sectors (average)
dc.w 80 ; 80 tracks
dc.l 800*2 ; 800K GCR
dc.b (%1000<<4)+2 ; THS valid, SD, 2 heads
dc.b 10 ; 10 sectors (average)
dc.w 80 ; 80 tracks
dc.l 720*2 ; 720K (1M) MFM
dc.b (%1000<<4)+2 ; THS valid, SD, 2 heads
dc.b 9 ; 9 sectors
dc.w 80 ; 80 tracks
dc.l 1440*2 ; 1440K (2M) MFM
dc.b (%1001<<4)+2 ; THS valid, DD, 2 heads
dc.b 18 ; 9 sectors
dc.w 80 ; 80 tracks
;_______________________________________________________________________
;
; Routine: jmp SWIM3DriveStatus
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Returns the drive status
;_______________________________________________________________________
SWIM3DriveStatus
with DriveStatusRec
bsr GetDriveStatus
movea.l DriveInfoPtrs(a1,d1.w*4),a2 ; a2 = ptr to drive info for this drive
clr.b (a0)+ ; clear high byte of track
move.b CylinderValid(a1,d1.w),(a0)+; copy current track
move.l (a2)+,(a0)+ ; copy writeProt, DIP, driveInst, numSides
move.l (a2)+,(a0)+ ; copy qLink
move.l (a2)+,(a0)+ ; copy qType, dQDrive
move.l (a2)+,(a0)+ ; copy dQRefNum, dQFSID
assert StatNewInterface=StatTwoSidedFmt+1
assert StatDiskErrors=StatNewInterface+1
lea CurrentStatus(a1),a2
move.l StatTwoSidedFmt(a2),(a0)+ ; copy 2-sided format/ new interface bytes, and disk errors
moveq #noErr,d0 ; return good status
bra Swim3DiskDone
endwith
;_______________________________________________________________________
;
; Routine: jmp SWIM3MFMStatus
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Returns the MFM status:
; [0] - $FF = superdrive, 0 = 400K/800K drive
; [1] - $FF = MFM, 0 = GCR (valid only when installed)
; [2] - $FF = 1440K disk, 0 = 720K disk
; [3] - $FF = SWIM, 0 = IWM
;_______________________________________________________________________
SWIM3MFMStatus
clr.l (a0) ; set defaults
st.b 3(a0) ; we always have a SWIM
moveq #DSMFMGCRDriveKind,d0 ; see if superdrive attached
cmp.b DriveKind(a1,d1.w),d0 ;
bne.s @done
st.b (a0)+ ; superdrive, mark it and continue
move.b DiskFormat(a1,d1.w),d0
cmpi.b #MFM720Kformat,d0 ; see if the disk is MFM
beq.s @mfm ; yes, mark it
moveq #MFM1440Kformat,d0 ;
cmp.b DiskFormat(a1,d1.w),d0 ;
beq.s @mfm ; yes, mark it
addq.w #1,a0
bra.s @chkMedia
@mfm st.b (a0)+ ; MFM, mark it and continue
@chkMedia moveq #HiDenMediaKind,d0 ; MFM, see if high density
cmp.b MediaKind(a1,d1.w),d0 ;
bne.s @done ; no, we're done
st.b (a0)+ ; else, indicate 1440K disk
@done moveq #noErr,d0 ; return good status
bra Swim3DiskDone
;_______________________________________________________________________
;
; Routine: jmp SWIM3DupVersion
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Returns the version of the disk duplicator program we support
;_______________________________________________________________________
SWIM3DupVersion
move.w #DDVersion,(a0) ; return the version that we support
bra Swim3DiskDone
;_______________________________________________________________________
;
; Routine: jmp SWIM3GetFmtByte
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Returns the format byte for the current disk.
;_______________________________________________________________________
SWIM3GetFmtByte
move.b SectHdrFmtKind(a1),(a0) ; return the format byte
bra Swim3DiskDone
;_______________________________________________________________________
;
; Routine: jmp SWIM3CtlKillIO
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Returns error.
;_______________________________________________________________________
SWIM3CtlKillIO moveq #controlErr,d0
bra Swim3DiskDone
;_______________________________________________________________________
;
; Routine: jmp SWIM3CtlTagBuf
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Sets up a user tag buffer, where tags will be read from/written to.
;_______________________________________________________________________
SWIM3CtlTagBuf move.l (a0),TagBufPtr(a1) ; set or clear ptr to separate buffer
bra Swim3DiskDone
;_______________________________________________________________________
;
; Routine: jmp SWIM3CtlTrkCache
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Disables/enables the track cache.
;_______________________________________________________________________
SWIM3CtlTrkCache
movea.l a0,a3 ; save csParam ptr
bsr FlushIfDisabled ; need to flush if going from disabled to enabled
clr.b CacheEnabled(a1) ; assume disabled
tst.b 1(a3) ; get installed flag
bmi.s @exit ; disable cache if remove request
move.b (a3),CacheEnabled(a1) ; update enable flag
@exit moveq #noErr,d0 ; no errors possible
bra Swim3DiskDone
;_______________________________________________________________________
;
; Routine: jmp SWIM3CtlPhysIcon
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Returns the drives physical icon.
;_______________________________________________________________________
SWIM3CtlPhysIcon
movea.l Drive1PhysIcon(a1),a3 ; return primary drive Icon
cmpi.w #intDriveNumber,d1 ; test for primary
beq.s @gotDefault ; if it was primary
movea.l Drive2PhysIcon(a1),a3 ; else, return secondary drive Icon
@gotDefault move.l a3,(a0) ; return pointer to Icon
bra Swim3DiskDone ; return
;_______________________________________________________________________
;
; Routine: jmp SWIM3CtlLogIcon
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Returns the drives logical icon (media icon).
;_______________________________________________________________________
SWIM3CtlLogIcon
movea.l MediaIconPtr(a1),a3 ; return media Icon
move.l a3,(a0) ; return pointer to Icon
bra Swim3DiskDone ; return
;_______________________________________________________________________
;
; Routine: jmp SWIM3CtlDrvInfo
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: none
; Destroys: a0,a2,d0
; Calls: none
; Called by: Driver control dispatcher
;
; Function: Returns the drive info struct for the specified drive.
;_______________________________________________________________________
SWIM3CtlDrvInfo
with DriveStatusRec
bsr GetDriveStatus
lea CurrentStatus(a1),a2 ; get ptr to status record
move.l StatDriveInfoB3(a2),(a0) ; return drive status
moveq #noErr,d0 ; return good status
bra Swim3DiskDone
endwith
;__________________________________________________________________________________________________
;
; Routine: jmp SWIM3CtlGetRawData
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: d0 -- result code
; Destroys: d0,d2-d3,a0,a3
; Calls: SetUpDrive, SeekTrack, WaitDriveReady, RawTrackRead
; Called by: Driver control dispatcher
;
; Function: The call reads all or part of a track and returns the raw, unmassaged data it
; finds there so that applications can access a floppy disk at a very low level
; without having to directly access the hardware. The following parameters are
; passed starting at csParam:
;
; 0: clockBuffer pointer to packed bit array (MFM disks only), or nil
; 4: dataBuffer pointer to raw track data, or nil
; 8: byteCount number of bytes requested
; (dataBuffer must be able to hold this many bytes)
; 12: numDone number of bytes actually read (≤ byteCount)
; 16: searchMode when to start collecting bytes:
; 0 = as soon as spindle motor is up to speed
; 1 = after reading an address field
; 2 = after reading a data field
; 3 = at the index mark (MFM disks only)
; 18: track which track to read (0-79)
; 20: side which side to read (0-1)
; 21: sector which sector to synchronize on (0-255)
;
; If clockBitsBuffer is non-nil, it must point to a buffer that's at least 1/8th
; the size of dataBuffer. It consists of a packed array of bits signifying whether
; or not the corresponding byte in dataBuffer is a mark or data byte. If a bit
; is equal to "1", the byte is an MFM mark byte; if it's a "0", the byte is an
; MFM data byte. Bits for ASCENDING data bytes are arranged in DESCENDING order
; within a byte, i.e., bit 7 represents byte 0, bit 6 represents byte 1, etc.
;
; NOTE: If both clockBitsBuffer and dataBuffer are nil, the call will do nothing.
; This provides a way for applications to determine if the call exists
; without first having to allocate large buffers.
;__________________________________________________________________________________________________
SWIM3CtlGetRawData
move.l (a0)+,d0
move.l d0,ClockBuffer(a1) ; save ptr to clock buffer
or.l (a0),d0
beq.s @exit ; exit if both buffers are nil
move.l (a0)+,DataBuffer(a1) ; save ptr to data buffer
move.l (a0)+,ByteCount(a1) ; save byte count
movea.l a0,a3 ; save ptr to reply count
clr.l (a3) ; init the reply count to 0
addq.l #4,a0 ; skip over it
moveq #paramErr,d0 ; assume search mode out of range
move.w (a0)+,d2 ; get search mode
cmpi.b #3,d2 ; compare to our limit
bhi.s @exit ; exit if out of range
move.w d2,SearchMode(a1) ; save the search mode
move.w (a0)+,d0 ; get track #
move.b d0,PhysCylinder(a1) ; save it
move.b (a0)+,PhysHead(a1) ; save head #
move.b (a0)+,PhysSector(a1) ; save sector #
bsr SetUpDrive ; select and power up the disk drive
bne.s @exit ; if no error in SetUp
move.b PhysCylinder(a1),d0 ; get the desired cylinder number
bsr SeekTrack ; seek to it
bne.s @exit ; die on errors
moveq #paramErr,d0 ; assume invalid search mode
moveq #3,d2 ; get our last mode
cmp.w SearchMode(a1),d2 ; in range?
blo.s @exit ; no, exit
bne.s @startRead ; not 'at index', go ahead
btst.b #GCRModeBit,([vSetup,a1]) ; read from index, are we in GCR mode?
beq.s @startRead ; no, read as specified
clr.w SearchMode(a1) ; else, read immediatly
@startRead bsr WaitDriveReady ; wait for the seek to complete
bne.s @exit ; exit if error seeking
bsr RawTrackRead ; all setup, get the data (reply cnt ptr in a3)
@exit jmp Swim3DiskDone
;_______________________________________________________________________
;
; Routine: jmp SWIM3CtlEject
; Inputs: a0 - csParam ptr
; a1 - SonyVars ptr
; d1 - logical drive #
; Outputs: d0 - result code
; Destroys: d0,d2-d3,a0
; Calls: SetUpDrive, SeekTrack, ReCalibrate, WaitDriveReady, SelectDrive. AddrAndStrb,
; Sleep, AddrAndSense, TurnOffDrive
; Called by: Driver control dispatcher
;
; Function: Ejects the disk in the specified drive
;_______________________________________________________________________
SWIM3CtlEject bsr SetUpDrive ; select and power up the disk drive
beq.s @EjectSeek ; if no error in SetUp
cmpi.w #OffLinErr,d0 ; see if there is a disk to eject
beq.s @EjectOut ; if disk is already out
; Ignore some of the errors returned by SetUpDrive, since we are just going to
; eject the disk. Only errors related to drive number will be flagged
cmpi.w #nsDrvErr,d0 ; return no such drive error if out of range
beq.s @EjectDone ; report drive number errors
cmpi.w #noDriveErr,d0 ; return no drive error if drive not installed
beq.s @EjectDone ;
bra.s @EjectDiskette ; if any other error, skip seek, just eject
@EjectSeek move.b #LastCylinder/2,d0 ; position head to middle of disk before eject
bsr SeekTrack ; to prevent major data loss if heads damage media
beq.s @EjectDiskette ; if seek was ok, eject the disk
bsr ReCalibrate ; try to recalibrate if seek failed
bne.s @EjectDiskette ; if couldn't recalibrate, forget about seeking
move.b #LastCylinder/2,d0 ; position head again
bsr SeekTrack ; one last attempt at seeking
@EjectDiskette bsr WaitDriveReady ; wait for motor speed and heads to settle
; Re-Enable the drive, in case it was disabled by an error.
move.w ReqDriveNumber(a1),d0 ; see what drive is requested
move.b PhysDriveNumber(a1,d0.w),d0 ; get the new physical drive number
bsr SelectDrive ; select and enable the new drive
moveq #wMotorOffAdr,d0 ; motor off drive command
bsr AddrAndStrb ; turn off the drive motor
move.l #MotorStopDelay,d0 ; delay time
bsr Sleep ; sleep until disk stops spinning
moveq #wEjectOnAdr,d0 ; eject disk drive command
bsr AddrAndStrb ; start the eject process
move.l #EjectDelay,d0 ; wait 1.5 seconds for eject to complete
bsr Sleep ; wait the specified amount of time
@EjectOut clr.b MediaKind(a1,d1.w) ; indicate NoMediaKind
clr.b DiskFormat(a1,d1.w) ; indicate uncheckedFormat
clr.w LastActiveDrive(a1) ; indicate drive state is unknown
bsr GetDriveStatus ; update drive status
bsr TurnOffDrive ; disable the drive
moveq #noErr,d0 ; indicate success
@EjectDone jmp Swim3DiskDone ; done with the call
;_______________________________________________________________________
;
; Routine: jmp SWIM3Read
; Inputs: a0 - iopb
; a1 - SonyVars
; ReqDriveNumber - logical drive #
; LogicalBlock - block # for request
; BlockAddress - user buffer address
; BlockCount - # of blocks to xfer
; Outputs:
; Destroys: a0,a3,d0-d3
; Calls: BlocktoPhysAddr, ClearNeededTrackCache, InvalidateTrackCache
; SeekTrack, WaitDriveReady, ReadTrack, DeNibbleize, ReCalibrate
; Called by: SWIM3DiskPrime
;
; Function: Reads the requested blocks into the user buffer / track cache.
;_______________________________________________________________________
SWIM3Read move.l TagBufPtr(a1),CurTagPtr(a1) ; init our index into user tag buffer
clr.l VerifyPtr(a1) ; assume we're not verifying
btst.b #6,IOPosMode+1(a0) ; test for verify mode
beq.s @setup ; no, we're set
move.l WritePtr(a1),VerifyPtr(a1) ; yes, setup a temporary buffer for verify
@setup bsr SetUpDrive ; select and power up the disk drive
bne @readExit ; exit if error
moveq #noAdrMkErr,d0 ; assume invalid disk format
cmpi.b #unknownFormat,DiskFormat(a1,d1.w) ; does this disk have a valid format?
beq @readExit ; no, exit with error
;-------------------------------------------------
; Here we want to calculate all the needed sectors for this cylinder
;-------------------------------------------------
@nextCylinder move.l LogicalBlock(a1),d0 ; init our block#
bsr BlocktoPhysAddr ; compute starting sector/track for this format
moveq #0,d2
move.b PhysCylSector(a1),d2 ; get cylinder sector # in d2
move.l BlockCount(a1),d3 ; init our blocks remaining count
bsr ClearNeededTrackCache ; assume no sectors are needed yet
clr.w SectorsToRead(a1) ; assume no sectors needed
moveq #0,d0
move.b PhysCylinder(a1),d0 ; get desired cylinder
cmp.w TCTrackNum(a1),d0 ; does our track cache hold this one?
beq.s @sectLoop ; yes, already set
move.w d0,TCTrackNum(a1) ; no, set track cache to this track
bsr InvalidateTrackCache ; invalidate the cache
@sectLoop bset.b #SectorNeeded,\
(SectorBufPtr,a1,d2.w*2); mark this sector as needed
tst.l VerifyPtr(a1) ; are we verifying?
beq.s @cntSect ; no, see if already there
bclr.b #SectorValid,\
(SectorBufPtr,a1,d2.w*2); yes, mark this sector as not there (so we read it)
@cntSect btst.b #SectorValid,\
(SectorBufPtr,a1,d2.w*2); is this sector already in cache?
bne.s @nextSect ; yes, don't count it as needed
cmp.b PhysSectsPerHead(a1),d2 ; on head 1?
blt.s @cntHead0 ; no, flag we need side 0
addq.b #1,SectorsToRead+1(a1) ; count needed on side 1
bra.s @nextSect
@cntHead0 addq.b #1,SectorsToRead(a1) ; count needed on side 0
@nextSect addq.l #1,d2 ; next block #
subq.l #1,d3 ; count the block
ble.s @startRead ; none left, this is the last cylinder
cmp.b PhysSectsPerCyl(a1),d2 ; is this the last one?
blt.s @sectLoop ; no, get another
;-------------------------------------------------
; Now we know what sectors are needed, seek to the cylinder and read them
;-------------------------------------------------
@startRead move.b #1,Interleave(a1) ; assume 1-1 (mfm) interleave
btst.b #GCRModeBit,([vSetup,a1]) ; are we in MFM mode
beq.s @createMap ; yes, interleave correct
moveq #$1F,d0 ; mask only the interleave bits
and.b SectHdrFmtKind(a1),d0 ; get format byte from header
move.b d0,Interleave(a1) ; setup the GCR interleave factor
@createMap bsr BuildInterleaveMap ; build the interleave map
move.w #RecalsMax,RecalsLeft(a1) ; Max Recals allowed before hard error
@seektoCyl tst.w SectorsToRead(a1) ; and sectors to get (or are they all in the cache)?
beq.s @copyTrack ; no, skip the read
move.b PhysCylinder(a1),d0 ; get the desired cylinder number
bsr SeekTrack ; seek to it
move.w #IOErrorsMax,IOErrorsLeft(a1) ; Max I/O errors allowed before Recal
bsr WaitDriveReady ; wait for the seek to complete
bne @recalAndRetry ; if error seeking, try to recal
@retry clr.b PhysHead(a1)
bsr ReadTrack ; read the track
bne @readErr ; branch on errors
addq.b #1,PhysHead(a1) ; bump to head 1
bsr ReadTrack ; read the track
bne @readErr ; branch on errors
;-------------------------------------------------
; All needed sectors for this cylinder are in the DMA buffer, now denibblize/copy/verify into user buffer
;-------------------------------------------------
@copyTrack moveq #0,d3 ; start with sector 0
@nextBlock btst.b #SectorNeeded,\
(SectorBufPtr,a1,d3.w*2); do we need this sector?
bne.s @translate ; yes, translate into user buffer
addq.b #1,d3 ; no, try next one
cmp.b PhysSectsPerCyl(a1),d3 ; is this the last one?
blt.s @nextBlock ; no, repeat
bra @nextCylinder ; yes, handle next cylinder
@translate lea TagData+2,a3 ; a3 = ptr to tag buffer (lowmem)
movea.l BlockAddress(a1),a0 ; a0 = ptr to user buffer, (assume not verifying)
move.l VerifyPtr(a1),d2 ; are we verifying?
beq.s @denib ; yes, we're all set
movea.l d2,a0 ; a0 = ptr to denibbleizing buffer
@denib move.l d3,d0 ; d0 = sector#
bsr DeNibbleize ; de-nibblize it into user buffer
beq.s @chkTags ; no errors, continue
bclr.b #SectorValid,\ ;
(SectorBufPtr,a1,d3.w*2) ; error, mark this sector as not valid
bra.s @readErr
@chkTags move.l CurTagPtr(a1),d0 ; does the user have a tag buffer? (d0=0)
beq.s @chkVerify ; no, then leave tags in lowmem
movea.l d0,a0
lea TagData+2,a3 ; a3 = ptr to tag lowmem
move.l (a3)+,(a0)+ ; copy tags into user buffer
move.l (a3)+,(a0)+
move.l (a3)+,(a0)+
@chkVerify tst.l d2 ; are we verifying?
beq.s @bumpTag ; no, handle next block, if any more
movea.l d2,a0 ; a0=ptr to read data
movea.l BlockAddress(a1),a3 ; a3=ptr to expected data
moveq #(BlockSize/4)-1,d2 ; loop counter
@verifyLoop move.l (a0)+,d0 ; get next longword
cmp.l (a3)+,d0 ; compare to expected
dbne d2,@verifyLoop ; repeat for 512 bytes, or error
beq.s @bumpTag ; no error, continue
bsr InvalidateTrackCache ; invalidate the cache
moveq #dataVerErr,d0 ; verify error,
bra.s @readExit ; exit
@bumpTag bclr.b #SectorNeeded,\ ;
(SectorBufPtr,a1,d3.w*2) ; sector's ok, mark as not needed
moveq #0,d0
tst.l CurTagPtr(a1)
beq.s @bumpBuf
moveq #TagSize,d2
add.l d2,CurTagPtr(a1) ; bump tag ptr
@bumpBuf add.l #BlockSize,BlockAddress(a1) ; update buffer address
addq.l #1,LogicalBlock(a1) ; next block
subq.l #1,BlockCount(a1) ; count the block
bne @nextBlock
@done movea.l DCEPointer(a1),a3 ; get DCE pointer
movea.l DCtlQHead(a3),a0 ; get IO param block pointer
move.l ioByteCount(a0),d1 ; get byte count
move.l d1,ioNumDone(a0) ; all byte done
add.l d1,dCtlPosition(a3) ; update position pointer
bra.s @readExit ; return
@readErr addq.w #1,ErrorCount(a1,d1.w*2) ; count the error for this drive
subq.w #1,IOErrorsLeft(a1) ; count the error
bge @retry ; more retrys left, continue
@recalAndRetry subq.w #1,RecalsLeft(a1) ; see if RecalsMax exceeded
blt.s @readExit ; if too many retrys, die with hard error
bsr ReCalibrate ; move heads to track zero, init the drive
beq @seektoCyl ; if Recal OK, retry seek
@readExit bra Swim3DiskDone
;_______________________________________________________________________
;
; Routine: jmp SWIM3Write
; Inputs: a0 - iopb
; a1 - SonyVars
; ReqDriveNumber - logical drive #
; LogicalBlock - block # for request
; BlockAddress - user buffer address
; BlockCount - # of blocks to xfer
; Outputs:
; Destroys: a0,a3,d0-d2
; Calls: ReCalibrate
; Called by: DiskPrime
;
; Function: Writes the requested blocks into the user buffer / track cache.
;_______________________________________________________________________
SWIM3Write move.l TagBufPtr(a1),CurTagPtr(a1) ; init our index into user tag buffer
bsr SetUpDrive ; select and power up the disk drive
bne @writeExit ; exit if error
moveq #wPrErr,d0 ; assume write protected
movea.l DriveInfoPtrs(a1,d1.w*4),a3 ; get ptr to drive info for this drive
tst.b MyDriveInfo.WriteProtected(a3) ; see if write protected
bmi @writeExit ; yes, exit
moveq #noAdrMkErr,d0 ; assume invalid disk format
cmpi.b #unknownFormat,DiskFormat(a1,d1.w) ; does this disk have a valid format?
beq @writeExit ; no, exit with error
;-------------------------------------------------
; Here we want to calculate all the needed sectors for this cylinder, and copy
; into the DMA buffer.
;-------------------------------------------------
@nextCylinder move.l LogicalBlock(a1),d0 ; init our block#
bsr BlocktoPhysAddr ; compute starting sector/track for this format
moveq #0,d2
move.b PhysCylSector(a1),d2 ; get cylinder sector # in d2
bsr ClearNeededTrackCache ; assume no sectors are needed yet
clr.w SectorsToWrite(a1)
moveq #0,d0
move.b PhysCylinder(a1),d0 ; get desired cylinder
cmp.w TCTrackNum(a1),d0 ; does our track cache hold this one?
beq.s @sectLoop ; yes, already set
move.w d0,TCTrackNum(a1) ; no, set track cache to this track
bsr InvalidateTrackCache ; invalidate the cache
@sectLoop bset.b #SectorNeeded,\
(SectorBufPtr,a1,d2.w*2); mark this sector as needed
bclr.b #SectorValid,\
(SectorBufPtr,a1,d2.w*2); mark this sector as not valid
move.l CurTagPtr(a1),d0 ; does user have his own tag buffer?
beq.s @nib ; no, all set
movea.l d0,a0 ; yes, use it
lea TagData+2,a3 ;
move.l (a0)+,(a3)+ ; copy from user tag buffer into lowmem
move.l (a0)+,(a3)+
move.l (a0)+,(a3)+
@nib lea TagData+2,a3 ; a3 = ptr to tag buffer (assume no user buffer)
movea.l BlockAddress(a1),a0 ; a0 = ptr to user buffer,
moveq #0,d0
move.b (SectorBufPtr+1,a1,d2.w*2),d0 ; get buffer# for this sector
move.l (TrackCachePtr,a1,d0.w*4),\
NibbilizeDMAPtr(a1) ; set the DMA ptr for Nibbleize
move.b d2,d3 ; get cylinder sector #
cmp.b PhysSectsPerHead(a1),d3 ; on head 1?
blt.s @setHead0 ; no, flag we need side 0
addq.b #1,SectorsToWrite+1(a1) ; count needed on side 1
sub.b PhysSectsPerHead(a1),d3 ; and adjust for head 1
bra.s @setSect
@setHead0 addq.b #1,SectorsToWrite(a1) ; count needed on side 0
@setSect move.b d3,PhysSector(a1) ; setup the track sector #
bsr Nibbleize ; nibblize it into our DMA buffer
tst.l CurTagPtr(a1) ; does user have tag buffer?
beq.s @cntBlock ; no, don't bump it
moveq #TagSize,d0
add.l d0,CurTagPtr(a1) ; bump tag ptr
@cntBlock addi.l #BlockSize,BlockAddress(a1) ; update buffer address
addq.l #1,LogicalBlock(a1) ; next block
addq.l #1,d2 ; next sector #
subq.l #1,BlockCount(a1) ; count the block
ble.s @startTransfer ; none left, this is the last cylinder
cmp.b PhysSectsPerCyl(a1),d2 ; is this the last one?
blt.s @sectLoop ; no, get another
;-------------------------------------------------
; The sectors are marked and loaded into the cache, now
; write them to the cylinder
;-------------------------------------------------
@startTransfer move.b #1,Interleave(a1) ; assume 1-1 (mfm) interleave
btst.b #GCRModeBit,([vSetup,a1]) ; are we in MFM mode
beq.s @createMap ; yes, interleave correct
moveq #$1F,d0 ; mask only the interleave bits
and.b SectHdrFmtKind(a1),d0 ; get format byte from header
move.b d0,Interleave(a1) ; setup the GCR interleave factor
@createMap bsr BuildInterleaveMap ; build the interleave map
move.w #RecalsMax,RecalsLeft(a1) ; Max Recals allowed before hard error
@seektoCyl bsr WaitMotorSettled ; wait for motor speed to be in 1.5% tolerance
move.b PhysCylinder(a1),d0 ; get the desired cylinder number
bsr SeekTrack ; seek to it
move.w #IOErrorsMax,IOErrorsLeft(a1) ; Max I/O errors allowed before Recal
bsr WaitDriveReady ; wait for the seek to complete
bne.s @recalAndRetry ; if error seeking, try to recal
@retry clr.b PhysHead(a1)
move.b SectorsToWrite(a1),d0 ; need any from side 0?
beq.s @chkSide1 ; no, see if side 1
cmpi.b #1,d0 ; yes, see if more then 1 sector needed
bgt.s @write0 ; more than 1, leave sign bit clear (d0, bit 7)
st.b d0 ; else, set sign bit
@write0 bsr WriteTrack ; write the track
bne.s @writeErr ; branch on errors
@chkSide1 move.b SectorsToWrite+1(a1),d0 ; need any from side 0?
beq.s @anyLeft ; no, see if any more blocks remain in request
cmpi.b #1,d0 ; yes, see if more then 1 sector needed
bgt.s @write1 ; more then 1, leave sign bit clear (d0, bit 7)
st.b d0 ; else, set sign bit
@write1 addq.b #1,PhysHead(a1) ; bump to head 1
bsr WriteTrack ; write the track
bne.s @writeErr ; branch on errors
@anyLeft tst.l BlockCount(a1) ; any blocks remaining?
bne @nextCylinder ; yes, copy them
movea.l DCEPointer(a1),a3 ; get DCE pointer
movea.l DCtlQHead(a3),a0 ; get IO param block pointer
move.l ioByteCount(a0),d1 ; get byte count
move.l d1,ioNumDone(a0) ; all bytes done
add.l d1,dCtlPosition(a3) ; update position pointer
bra.s @writeExit
@writeErr addq.w #1,ErrorCount(a1,d1.w*2) ; count the error for this drive
subq.w #1,IOErrorsLeft(a1) ; count the error
bne.s @retry ; retry if possible
@recalAndRetry subq.w #1,RecalsLeft(a1) ; see if RecalsMax exceeded
blt.s @writeExit ; if too many retrys, die with hard error
bsr ReCalibrate ; move heads to track zero, init the drive
beq.s @seektoCyl ; if Recal OK, retry seek
@writeExit bra Swim3DiskDone
;_______________________________________________________________________
;
; Routine: jmp SWIM3CtlFormat
; jmp SWIM3CtlFmtCopy
; Inputs: a0 Globals
; d1.w driveNum
; Outputs: none
; Destroys: d0,d2-d3,a0
; Calls: SetUpDrive, GetDriveStatus, WaitDriveReady, CreateTrackCache,
; ReCalibrate, WaitMotorSettled, SeekTrack, UpdateTagInfo,
; UpdateDataInfo, FmtTrack, BlockToPhysAddr,GetDriveStatus
; Called by: Driver control dispatcher
;
; Function: Disk formatting routine. Used to Format/Copy as well.
;_______________________________________________________________________
SWIM3CtlFormat moveq #0,d3 ; default format
clr.l UserBufPtr(a1) ; no sector buffer ptr
clr.l UserTagsPtr(a1) ; no sector tags ptr
clr.b FmtByteBlkSize(a1) ; default Fmt byte / Block Size
clr.b FmtCopyVerify(a1) ; don't verify after format (separate call)
bra.s StartFormat
SWIM3CtlFmtCopy
move.l 2(a0),UserBufPtr(a1) ; setup sector buffer ptr
move.l 6(a0),UserTagsPtr(a1) ; setup sector tags ptr
move.b 10(a0),FmtByteBlkSize(a1) ; setup Fmt byte / Block Size
move.b 11(a0),FmtCopyVerify(a1) ; setup flag for verify
StartFormat
with DriveStatusRec, MyDriveInfo
clr.b Interleave(a1) ; setup sector interleave factor (use default)
move.w (a0),d3 ; get format kind
bsr SetUpDrive ; select and power up the disk drive
bne @exit ; if error powering up
cmpi.w #08,d3 ; see if format kind reasonable (allow a few extras for expansion)
bhi @ParamErr ; if not, it's an error
bsr GetDriveStatus ; get the status for disk size
moveq #1,d0 ; defaults are first index into current format
tst.w d3 ; check format kind
beq.s @CopyFmtList ; if zero, use defaults (d3=0) format
move.w d3,d0 ; set desired format index
moveq #StatFmtAllowed-StatCurrentFmt,d3 ; else use the allowed bitmask
@CopyFmtList lea CurrentStatus(a1),a0 ; get ptr to our status record
move.b StatCurrentFmt(a0,d3.w),d2 ; get format byte bitmask from list
moveq #0,d4
moveq #0,d3 ; initialize table index
@FmtSearchLoop move.b (FmtIndexTable,d3.w),d4 ; get the format for this index
bmi @ParamErr ; return ParamErr if formatKind too big
addq.w #1,d3 ; update index
ror.b #1,d2 ; shift the formats allowed bitmask
bcc.s @FmtSearchLoop ; loop if this format not available
subq.w #1,d0 ; decrement format kind
bne.s @FmtSearchLoop ; loop if not the desired format kind
move.w d4,d0 ; get format in d0
movea.l DriveInfoPtrs(a1,d1.w*4),a0 ; get ptr to drive info for this drive
tst.b WriteProtected(a0) ; see if write enabled
bpl.s @writeEnabled ; yes, continue
endwith
moveq #wPrErr,d0 ; indicate write protected disk
bra @exit ; return the error
@writeEnabled move.b d0,DiskFormat(a1,d1.w) ; change to new disk format
move.b (DefaultGapSize,d0.w),SectorGapSize(a1) ; setup sector gap info
move.b FmtByteBlkSize(a1),d2 ; get Fmt byte / Block Size from request
bne.s @gotFmtByte ; if non-default, use it
move.b (DefaultFmtByte,d0.w),d2 ; get default format byte / block size byte value
@gotFmtByte move.b d2,FmtByteBlkSize(a1) ; setup Fmt byte / Block Size
move.b Interleave(a1),d2 ; get sector interleave factor from request
bne.s @gotInterleave ; if non-default, use it
move.b (DefaultInterleave,d0),d2 ; get the default interleave for this format
bpl.s @gotInterleave ; if positive, use it
move.b FmtByteBlkSize(a1),d2 ; else, get the interleave from the format byte
andi.b #$1F,d2 ;
bne.s @gotInterleave ; if non-zero, use it
addq.b #1,d2 ; if zero, change it to one
@gotInterleave move.b d2,Interleave(a1) ; setup sector interleave factor
bsr ReCalibrate ; move heads to track zero, init the drive
bne.s @exit ; if error powering up
clr.l LogicalBlock(a1) ; start with block number zero
bsr WaitMotorSettled ; wait for motor speed to be in 1.5% tolerance
bra.s @Start ; start formatting
;-------------------------------------------------
@CylinderLoop move.b PhysCylinder(a1),d0 ; get the desired cylinder number
bsr SeekTrack ; seek to it
bne.s @exit ; die on errors
moveq #0,d0
move.b PhysSectsPerCyl(a1),d0 ;
add.l d0,LogicalBlock(a1) ; get first block of next cylinder
;-------------------------------------------------
; build the sector interleave map
;-------------------------------------------------
bsr.s BuildInterleaveMap ; build the interleave map
bsr WaitDriveReady ; wait for the seek to complete
beq.s @HeadLoop ; continue with next head if successful
bra.s @exit ; die on errors
@HeadLoop bsr FmtTrack ; format the track
bne.s @exit ; exit if formatting error
addq.b #1,PhysHead(a1) ; go on to the next head
move.b PhysSectsPerHead(a1),d0 ; decrement by blocks on this head
sub.b d0,PhysSectsPerCyl(a1) ; update remaining blocks on this cylinder
bne.s @HeadLoop ; loop through all of the heads
@Start move.l LogicalBlock(a1),d0
bsr BlockToPhysAddr ; convert highest block on this cylinder
beq.s @CylinderLoop ; process next cylinder if end not reached
tst.b FmtCopyVerify(a1) ; are we verifying?
bne.s SWIM3CtlVerify ; yes, call our verify routine
bsr CreateTrackCache ; create a cache using the new Format
moveq #noErr,d0 ; indicate format succeeded
@exit bra SWIM3DiskDone ; finish up by returning the drive status
@ParamErr moveq #paramErr,d0 ; format kind out of range
bra.s @exit ; return the error
DefaultInterleave
; table of interleave factors indexed by DiskFormat
dc.b 1 ; 0 - uncheckedFormat
dc.b 1 ; 1 - unknownFormat
dc.b 1 ; 2 - HD20Format
dc.b -1 ; 3 - GCR400Kformat (get interleave from FmtByte)
dc.b -1 ; 4 - GCR800Kformat (get interleave from FmtByte)
dc.b 1 ; 5 - MFM720Kformat
dc.b 1 ; 6 - MFM1440Kformat
dc.b 1 ; 7 - GCRonHDformat
dc.b 1 ; 8 - reserved for future use
dc.b 1 ; 9 - reserved for future use
dc.b 1 ; 10 - reserved for future use
ALIGN 2
DefaultGapSize
; table of inter sector gap/sync group sizes indexed by DiskFormat
dc.b 0 ; 0 - uncheckedFormat
dc.b 0 ; 1 - unknownFormat
dc.b 0 ; 2 - HD20Format
dc.b GCRDefaultSyncCount ; 3 - GCR400Kformat
dc.b GCRDefaultSyncCount ; 4 - GCR800Kformat
dc.b MFM720KDataGapSize ; 5 - MFM720Kformat
dc.b MFM1440KDataGapSize ; 6 - MFM1440Kformat
dc.b MFM1440KDataGapSize ; 7 - GCRonHDformat
dc.b 0 ; 8 - reserved for future use
dc.b 0 ; 9 - reserved for future use
dc.b 0 ; 10 - reserved for future use
ALIGN 2
DefaultFmtByte
; table of format byte / block size indexed by DiskFormat
dc.b 0 ; 0 - uncheckedFormat
dc.b 0 ; 1 - unknownFormat
dc.b 0 ; 2 - HD20Format
dc.b $02 ; 3 - GCR400Kformat 2-1 interleave
dc.b $22 ; 4 - GCR800Kformat 2-1 interleave
dc.b BlockSize/256 ; 5 - MFM720Kformat
dc.b BlockSize/256 ; 6 - MFM1440Kformat
dc.b BlockSize/256 ; 7 - GCRonHDformat
dc.b 0 ; 8 - reserved for future use
dc.b 0 ; 9 - reserved for future use
dc.b 0 ; 10 - reserved for future use
ALIGN 2
FmtIndexTable
; table of formats indexed by offset into StatCurrentFmt/StatFmtAllowed
dc.b HD20Format ; 0 - HD20Format
dc.b GCR400Kformat ; 1 - GCR400Kformat
dc.b GCR800Kformat ; 2 - GCR800Kformat
dc.b MFM720Kformat ; 3 - MFM720Kformat
dc.b MFM1440Kformat ; 4 - MFM1440Kformat
dc.b -1 ; 5 - end of list
;_______________________________________________________________________
;
; Routine: jmp BuildInterleaveMap
; Inputs: a0 Globals
; Interleave - interleave factor
; Outputs: none
; Destroys: d0,d2
; Calls: ...
; Called by: SWIM3CtlFormat, SWIM3Write
;
; Function: Builds the sector interleave map for current track.
;_______________________________________________________________________
BuildInterleaveMap
moveq #0,d0
move.b PhysSectsPerHead(a1),d0 ; number of sectors to interleave
subq.w #1,d0 ; adjust for dbra
@MapInit st.b (SectorMap,a1,d0.w) ; mark sector as available
dbra d0,@MapInit ; initialize all of the sectors
moveq #0,d2 ; sector#
@Occupied addq.w #1,d0 ; try the next sector
@MapNext cmp.b PhysSectsPerHead(a1),d0 ; see if still in range
blt.s @NoWrap ; if it didn't wrap around
sub.b PhysSectsPerHead(a1),d0 ; if wrap around, back up
@NoWrap tst (SectorMap,a1,d0.w) ; see if it is already occupied
bpl.s @Occupied ; if so, try another one
move.b d2,(SectorMap,a1,d0.w) ; if not, update the map
add.b Interleave(a1),d0 ; bump index by interleave
addq.b #1,d2 ; update sector number
cmp.b PhysSectsPerHead(a1),d2 ; see if end reached
blt.s @MapNext ; if not, keep on mapping
rts
;_______________________________________________________________________
;
; Routine: jmp SWIM3CtlVerify
; Inputs: a0 Globals
; d1.w driveNum
; Outputs: none
; Destroys: d0,d2-d3,a0
; Calls: SetUpDrive, GetDriveStatus, WaitDriveReady, InvalidateTrackCache
; SeekTrack, BlockToPhysAddr, GetDriveStatus, DeNibbleize
; Called by: Driver control dispatcher, SWIM3CtlFmtCopy
;
; Function: Disk verify routine. Called after formatting to verify
; each sector on the disk.
;_______________________________________________________________________
SWIM3CtlVerify bsr SetUpDrive ; select and power up the disk drive
bne @verifyExit ; exit if error
bsr GetDriveStatus ; get the status for disk size
bsr WaitDriveReady ; wait for motor speed and heads to settle
bne @verifyExit ; if error powering up
bsr InvalidateTrackCache ; invalidate the cache
moveq #0,d0
move.w CurrentStatus+DriveStatusRec.\
StatDiskSize(a1),d0
move.l d0,LogicalBlock(a1) ; setup highest block #
moveq #1,d0 ; setup to dec (last block is disk size - 1)
bra @Start ; start verifying
@CylinderLoop move.l LogicalBlock(a1),d0
bsr BlockToPhysAddr ; convert highest block on this cylinder
bne @verifyExit ; die on errors
;-------------------------------------------------
; mark all sectors as needed...
;-------------------------------------------------
clr.w SectorsToRead(a1) ; assume no sectors needed
moveq #0,d0
move.b PhysSectsPerCyl(a1),d0 ; init our counter
bra.s @cntSect
@sectLoop bset.b #SectorNeeded,\
(SectorBufPtr,a1,d0.w*2); mark this sector as needed
bclr.b #SectorValid,\
(SectorBufPtr,a1,d0.w*2); and as not there (force the read)
cmp.b PhysSectsPerHead(a1),d0 ; on head 1?
blt.s @cntHead0 ; no, flag we need side 0
addq.b #1,SectorsToRead+1(a1) ; count needed on side 1
bra.s @cntSect
@cntHead0 addq.b #1,SectorsToRead(a1) ; count needed on side 0
@cntSect dbra d0,@sectLoop ; repeat for entire cylinder
;-------------------------------------------------
; Now we have all sectors marked, seek to the cylinder and read them
;-------------------------------------------------
move.b #1,Interleave(a1) ; assume 1-1 (mfm) interleave
btst.b #GCRModeBit,([vSetup,a1]) ; are we in MFM mode
beq.s @createMap ; yes, interleave correct
moveq #$1F,d0 ; mask only the interleave bits
and.b SectHdrFmtKind(a1),d0 ; get format byte from header
move.b d0,Interleave(a1) ; setup the GCR interleave factor
@createMap bsr BuildInterleaveMap ; build the interleave map
move.b PhysCylinder(a1),d0 ; get the desired cylinder number
bsr SeekTrack ; seek to it
bne.s @verifyExit ; die on errors
move.w #IOErrorsMax,IOErrorsLeft(a1) ; max I/O errors allowed before failing
bsr WaitDriveReady ; wait for the seek to complete
bne.s @verifyExit ; die on errors
@retry clr.b PhysHead(a1)
bsr ReadTrack ; read the track
bne @cntErr ; branch on errors
addq.b #1,PhysHead(a1) ; bump to head 1
bsr ReadTrack ; read the track
bne.s @cntErr ; continue if no errors
;-------------------------------------------------
; All sectors have read ok, now denibbleize them
; to check for CRC errors (GCR only)
;-------------------------------------------------
@checkCRC btst.b #GCRModeBit,([vSetup,a1]) ; are we in MFM mode
beq.s @readOK ; yes, SWIM3 already did the CRC
moveq #0,d2
move.b PhysSectsPerCyl(a1),d2 ; init our counter
subq.w #1,d2 ; adjust for dbra
@crcLoop lea TagData+2,a3 ; a3 = ptr to tag buffer (lowmem)
movea.l WritePtr(a1),a0 ; a0 = ptr to denib buffer,
move.l d2,d0 ; d0 = sector#
bsr DeNibbleize ; de-nibblize it into temporary buffer
dbne d2,@crcLoop ; repeat for all sectors, or error
beq.s @readOK ; continue if no errors
bclr.b #SectorValid,\
(SectorBufPtr,a1,d2.w*2); else, force it to read again
@cntErr addq.w #1,ErrorCount(a1,d1.w*2) ; count the error for this drive
subq.w #1,IOErrorsLeft(a1) ; count the retry
bge.s @retry ; more retrys left, continue
bra.s @verifyExit
;-------------------------------------------------
; All sectors are good, continue with next cylinder
;-------------------------------------------------
@readOK moveq #0,d0
move.b PhysSectsPerCyl(a1),d0
@Start sub.l d0,LogicalBlock(a1) ; subtract out the cylinder size, update block #
bge @CylinderLoop ; continue verify if more cylinders
moveq #noErr,d0 ; verify succeeded
@verifyExit bra Swim3DiskDone
;_______________________________________________________________________
;
; Routine: jsr PollingTask
; Inputs: a1 - SonyVars
; Outputs:
; Destroys: a0,d0-d2
; Calls: StartTMPBTimer, WaitTMPBdone, PrefetchIntoCache, TurnOffDrive,
; SelectDrive, AddrAndSense, AddrAndStrb, CheckStaticAttr,
; GetDriveStatus
; Called by: DiskOpen
;
; Function: Timer task that polls all of the installed drives,
; turning off the drive motor if the timeout period has expired,
; and looks for disk inserted, or eject button events, and
; notifies the HOST processor if they are found.
;_______________________________________________________________________
PollingTask lea PollingTMPB+TimeMgrOffset(a1),a0 ; get the polling timer
move.l #PollRate,d0
bsr StartTMPBTimer ; start the timer
bsr WaitTMPBdone ; wait for it to time out
; when the timer runs out, we will wake up and snoop around
move.w sr,-(sp) ; •• save old int mask
ori.w #$0700,sr ; •• mask all interrupts (to keep others who
; might make a driver call at int time out).
tst.b DriverActive(a1) ; see if driver is active
bne.s @PollExit ; skip poll if active
move.b MotorTimingOut(a1),d0 ; see if timing out a drive motor
beq.s @PollDrives ; not timing out, do the poll
subq.b #1,MotorTimingOut(a1) ; decrement the timeout counter
bne.s @PollExit ; exit if count not expired
bsr TurnOffDrive ; it is time to turn off the drive motor
@PollDrives moveq #0,d1 ; initial drive number
@PollDriveLoop move.b DriveKind(a1,d1.w),d0 ; get the drive kind
beq.s @PollNextDrive ; skip it if not installed
tst.b MediaKind(a1,d1.w) ; see if any media in drive
beq.s @PollDiskInPlace ; no media, check for disk inserted
bra.s @PollEjectSwitch ; there is a disk, see if eject pressed
@PollDiskInPlace
cmpi.b #HD20DriveKind,d0 ; check for HD-20 (special case)
beq.s @PollNextDrive ; if HD-20, skip (for now)
move.b PhysDriveNumber(a1,d1.w),d0 ; get the physical drive number
bsr SelectDrive ; select the drive
moveq #rNoDiskInPlAdr,d0 ; prepare to test for disk in place
bsr AddrAndSense ; read the status bit
bne.s @PollNextDrive ; if still no disk in place
moveq #wNoDiskInPlAdr,d0 ; make it look like the disk ejected
bsr AddrAndStrb ; to reset the eject latch
bsr CheckStaticAttr ; check the media kind and write protect
@PostDiskInPlace
moveq #0,d0 ; upper d0 <- 0, disk inserted
bsr.s @PostEvent ; send it to the system
beq.s @PollNextDrive ; all done if not error
clr.b MediaKind(a1,d1.w) ; indicate NoMediaKind if couldn't mount
@PollNextDrive addq.w #1,d1 ; point to next logical drive
cmpi.w #NumberOfDrives,d1 ; compare to limit
blt.s @PollDriveLoop ; loop through all drives
bsr.s @DeselectDrives ; deselect both drives
@PollExit move.w (sp)+,sr ; •• restore interrupts
bra.s PollingTask ; and start another poll
@PollEjectSwitch
cmpi.b #HD20DriveKind,d0 ; check for HD-20 (special case)
beq.s @PollNextDrive ; if HD-20, can't eject, so don't check
cmpi.b #SSGCRDriveKind,d0 ; check for 400K drive (special case)
beq.s @PollNextDrive ; if 400K drive, can't eject, so don't check
move.b PhysDriveNumber(a1,d1.w),d0 ; get the physical drive number
bsr SelectDrive ; select the drive
moveq #rEjectOnAdr,d0 ; prepare to test for eject latch set
bsr AddrAndSense ; read the status bit
beq.s @PollNextDrive ; if latch not set (eject button hadn't been pushed)
moveq #wNoDiskInPlAdr,d0 ; make it look like the disk ejected
bsr AddrAndStrb ; to reset the eject latch
moveq #-1,d0 ; upper d0 <- -1, disk ejected
bsr.s @PostEvent ; send it to the host (ignore errors)
bra.s @PollNextDrive ; keep on polling
@PostEvent movea.w #DiskInsertEvt,a0 ; a0 <- event type
clr.w d0 ; zero extend drive number
move.b d1,d0 ; insert drive number into message
_PostEvent ; post the insert/eject
bsr GetDriveStatus ; update status for this drive
@DeselectDrives move.b #StartAction+WriteMode,\
([vZeroes,a1]) ; clear action and write mode (prepare for motoroff)
nop
move.b #Drive1Enabled\
+Drive2Enabled,([vZeroes,a1]) ; reset MotorOn and Enables, disabling all drives
tst.w d0 ; check error
rts ; return to caller
;_______________________________________________________________________
;
; Routine: jsr SetUpDrive
; Inputs: a1 - driver globals
; Outputs: d0.w - error status code
; Destroys: d0,d2-d3,a0
; Calls:
; Called by: SWIM3CtlEject, SWIM3Read, SWIM3Write, SWIM3CtlFormat,
; SWIM3CtlVerify, SWIM3CtlGetRawData
;
; Function: Selects and Powers up the specified drive. If the disk in
; this drive had not been powered up before, it will also
; perform a re-calibrate, and determine the format kind of
; the disk. The drive will be turned off by the polling
; task when it resumes and the timeout counter expires, or
; when TurnOffDrive is explicitly called to turn it off.
;_______________________________________________________________________
SetUpDrive move.w #nsDrvErr,d0 ; assume invalid drive #
move.w ReqDriveNumber(a1),d1 ; see what drive is requested
beq.s @setupDone ; 0 is not valid
cmpi.w #MaxDriveNum,d1 ; check high limit
bgt.s @setupDone ; exit if out of range
cmp.w CurrentDrive(a1),d1 ; see if already powered up
bne.s @notCurrentDrive ; no, then set it up
move.b #motorTimeoutCount,MotorTimingOut(a1) ; restart our motor timeout oneshot
@setupGood moveq #0,d0 ; good result
@setupDone move.w d0,-(sp) ; save result
bsr FlushIfDisabled ; flush cache if disabled
move.w (sp)+,d0 ; restore result
rts
@notCurrentDrive
move.w #noDriveErr,d0 ; assume no drive installed
move.b DriveKind(a1,d1.w),d2 ; get drive kind
beq.s @setupDone ; exit if no drive present
bsr TurnOffDrive ; turn off the old drive (whatever was selected)
bsr TurnOnDrive ; turn on the new drive
bne.s @setUpDone ; return error if couldn't turn on drive
cmp.w LastActiveDrive(a1),d1 ; see if same drive as last successful access
beq.s @setupGood ; yes, return with success, no drive change
move.w d1,LastActiveDrive(a1) ; mark this drive as last active, assuming success
move.b DiskFormat(a1,d1.w),d0 ; see if format has been determined
beq.s @firstTime ; unchecked format, first time disk is accessed.
bsr LoadSWIMparams ; load params if format is known
bra.s @fmtChangeDone ; drive changed, so create a new track cache
@firstTime move.l (sp)+,AsyncPc(a1) ; •• save callers pc
bsr CheckStaticAttr ; update media kind and write enable
lea ReadyTMPB+TimeMgrOffset(a1),a0 ; get the ready timer block
bsr WaitTMPBdone ; wait for it to time out
move.l #ChuckingDelay,d0 ; get Disk Chucking wait time
bsr StartTMPBTimer ; start the timer
moveq #0,d3 ; init our format search index
@SearchLoop move.b (FmtSearchOrder,d3.w),d0 ; get format to try
addq.w #1,d3 ; bump the index
move.b d0,DiskFormat(a1,d1.w) ; setup the disk format to try
eori.b #unknownFormat,d0 ; are we done?
beq.s @SearchDone ; unknown format found, exit
bsr ReCalibrate ; establish head position
bne.s @SearchError ; return with error if couldn't recal
clr.b PhysHead(a1) ; always read from side zero
st.b PhysSectsPerHead(a1) ; allow any sector number
bsr ReadSectorHdr ; try to read a sector address
@chkFmt beq.s @readOk ; no errors, check format
cmpi.w #seekErr,d0 ; did it read, but invalid cylinder?
bne.s @SearchLoop ; no, try next format, else, we got the correct format
@readOk move.b (FmtByteValues-1,d3.w),d0 ; get expected value
move.b SectHdrFmtKind(a1),d2 ; compare to updated index
eor.b d2,d0
and.b (FmtByteMasks-1,d3.w),d0 ; get only the valid bits
beq.s @SearchDone ; if format is correct, exit with success
bra.s @searchLoop ; otherwise, try next format
@SearchError clr.b DiskFormat(a1,d1.w) ; indicate unchecked format, couldn't recal
@SearchDone ext.w d0
move.l AsyncPc(a1),-(sp) ; •• restore callers pc
@FmtChangeDone move.w d0,-(sp) ; save result code
bsr.s CreateTrackCache ; create a cache based on the new format
move.w (sp)+,d0 ; restore result code
rts
;_______________________________________________________________________
;
; Routine: jsr CreateTrackCache
; Inputs: d1 - Drive Number
; a1 - driver globals
; Outputs: none
; Destroys: d0,d2-d3
; Calls: none
; Called by: SetupDrive
;
; Function: This routine sets up our track cache ptrs, based on this
; disk's format.
;_______________________________________________________________________
CreateTrackCache
move.l #gcrDMASize,d2 ; assume GCR bytes/DMA sector
move.b DiskFormat(a1,d1.w),d0 ; get the disk format
subq.b #GCR400Kformat,d0 ; 400K?
beq.s @initPtrs ; yes, guessed correctly
subq.b #1,d0 ; 800K?
beq.s @initPtrs ; yes, guessed correctly
move.l #mfmDMASize,d2 ; no, setup for MFM sectors
@initPtrs move.l FloppyDMAStart(a1),d0 ; physical starting offset to floppy DMA buffer
movea.l TrackCacheStart(a1),a0 ; logical start of buffer
moveq #0,d3
bra.s @setit
@loop add.l d2,d0 ; compute next offset
adda.l d2,a0 ; "
@setit move.l d0,(TrackCacheDMAPtr,a1,d3.w*4) ; stuff the ptr
move.l a0,(TrackCachePtr,a1,d3.w*4) ; stuff the ptr
move.w d3,(SectorBufPtr,a1,d3.w*2) ; init sector buffer #, mark as invalid
addq.w #1,d3 ; count the sector buffer
cmpi.w #TotalBuffers,d3 ; done?
blt.s @loop ; no, keep going...
add.l d2,d0
adda.w d2,a0
move.l d0,WriteDMAPtr(a1) ; temporary sector offset (for writes)
move.l a0,WritePtr(a1) ; temporary sector ptr (for writes)
rts
;_______________________________________________________________________
;
; Routine: bsr InvalidateTrackCache
; Inputs: a1 - driver globals
; Outputs: none
; Destroys: d0
; Calls: none
; Called by: SetupDrive,SWIM3Read
;
; Function: This routine marks the track cache as invalid.
;_______________________________________________________________________
InvalidateTrackCache
moveq #TotalBuffers-1,d0 ; clear all buffers
@loop clr.b (SectorBufPtr,a1,d0.w*2) ; mark as invalid
dbra d0,@loop
rts
;_______________________________________________________________________
;
; Routine: bsr FlushIfDisabled
; Inputs: a1 - driver globals
; Outputs: none
; Destroys: d0
; Calls: none
; Called by: SetupDrive, SWIM3CtlTrkCache
;
; Function: This routine marks the track cache as empty.
;_______________________________________________________________________
FlushIfDisabled
tst.b CacheEnabled(a1) ; are we disabled?
beq.s InvalidateTrackCache ; yes, invalidate it
rts
;_______________________________________________________________________
;
; Routine: bsr ClearNeededTrackCache
; Inputs: a1 - driver globals
; Outputs: none
; Destroys: d0
; Calls: none
; Called by: SWIM3Read
;
; Function: This routine inits all sectors to 'not needed'.
;_______________________________________________________________________
ClearNeededTrackCache
moveq #TotalBuffers-1,d0 ; all buffers
@loop bclr.b #sectorNeeded,\
(SectorBufPtr,a1,d0.w*2) ; mark as not needed
dbra d0,@loop
rts
;_______________________________________________________________________
;
; Routine: jsr GetDriveStatus
; Inputs: d1 - Drive Number
; Outputs: none
; Destroys: d0
; Calls: none
; Called by: SWIM3GetFormatList, SWIM3DriveStatus, SWIM3CtlDrvInfo,
; SWIM3CtlFormat, SWIM3CtlVerify, PollingTask
;
; Function: Gets the status of the current drive/disk into our CurrentStatus
; record.
;_______________________________________________________________________
GetDriveStatus
with DriveStatusRec,MyDriveInfo
movem.l a0/a2,-(sp) ; save this reg
moveq #0,d0
lea CurrentStatus(a1),a2 ; get ptr to our status rec
move.w ErrorCount(a1,d1.w*2),\
StatDiskErrors(a2) ; get # of errors for this drive
move.b PhysDriveNumber(a1,d1.w),d0 ; get the physical drive number
move.b (DriveAttributes,d0.w),\
StatDriveAttr(a2) ; get the drive attributes
lea DefaultStatus,a0 ; get ptr to defaults table
bsr.s TranslateStatus ; translate the Default
move.b DriveKind(a1,d1.w),d0 ; get the drive kind for the drive
lea StatusTables,a0
adda.w (DriveStatusPtrs,d0.w*2),a0 ; offset to our table
bsr.s TranslateStatus ; translate the Drive status
move.b MediaKind(a1,d1.w),d0 ; get the media kind for the drive
lea StatusTables,a0
adda.w (MediaStatusPtrs,d0.w*2),a0 ; offset to our table
bsr.s TranslateStatus ; translate the Drive status
move.b DiskFormat(a1,d1.w),d0 ; get the current disk format
lea StatusTables,a0
adda.w (FmtStatusPtrs,d0.w*2),a0 ; offset to our table
bsr.s TranslateStatus ; translate the Drive status
movea.l DriveInfoPtrs(a1,d1.w*4),a0 ; get ptr to drive info for this drive
move.b WriteProtected(a0),\
StatWriteProt(a2) ; copy writeProt into our status record
move.b StatDiskInPlace(a2),\ ; copy disk in place
DiskInPlace(a0)
move.b StatInstalled(a2),\
Installed(a0) ; copy installed
move.b StatSides(a2),\
Sides(a0) ; copy # sides
move.w StatTwoSidedFmt(a2),\ ; copy 2-sided format/ new interface bytes
DriveQElement+dQDrvSz(a0) ; into our drive queue element
movem.l (sp)+,a0/a2
rts ; all done
;--------------
XlateLoop move.b (a0)+,(a2,d0.w) ; move the data byte
TranslateStatus move.b (a0)+,d0 ; get the status rec index byte
bne.s XlateLoop ; loop through all bytes
rts ; all done
StatusTables
;---------------------------
DefaultStatus dc.b StatDiskInPlace,$01 ; Default status values returned
dc.b StatInstalled,$01
dc.b StatTwoSidedFmt,$FF
dc.b StatDriveInfoB3,0
dc.b StatDriveInfoB2,0
dc.b $00
;---------------------------
; Tables based on Drive Kind
NoDrive dc.b StatInstalled,$FF ; unknown drive kind info
dc.b StatSides,$00
dc.b StatNewInterface,$00
dc.b StatDriveType,noDriveKind
dc.b $00
HD20Drive dc.b StatSides,$00 ; HD-20 drive kind info
dc.b StatNewInterface,$00
dc.b StatFmtAllowed,$01
dc.b StatCurrentFmt,$01
dc.b StatDriveType,HD20DriveKind
dc.b $00
SSGCRDrive dc.b StatSides,$00 ; 400K GCR drive kind info
dc.b StatNewInterface,$00
dc.b StatFmtAllowed,$02
dc.b StatDriveType,SSGCRDriveKind
dc.b $00
DSGCRDrive dc.b StatSides,$FF ; 400K / 800K GCR drive kind info
dc.b StatNewInterface,$FF
dc.b StatFmtAllowed,$06
dc.b StatDriveType,SSGCRDriveKind
dc.b $00
DSMFMGCRDrive dc.b StatSides,$FF ; 400K / 800K GCR, 720K / 1440K MFM drive kind info
dc.b StatNewInterface,$FF
dc.b StatFmtAllowed,$0E
dc.b StatDriveType,DSMFMGCRDriveKind
dc.b $00
;---------------------------
; Tables based on Media Kind
NoMedia dc.b StatDiskInPlace,$00 ; no media kind info
dc.b StatFmtAllowed,$00
dc.b StatCurrentFmt,$00
dc.b $00
LoDenMedia dc.b StatCurrentFmt,$02
dc.b $00
HiDenMedia dc.b StatFmtAllowed,$10
dc.b StatCurrentFmt,$10
HD20Media dc.b $00
;---------------------------
; Tables based on Disk Format Kind
NoFmt dc.b StatDiskInPlace,$02 ; unknown format kind info
UnCheckedFmt dc.b StatTwoSidedFmt,$00 ; unchecked format kind info
dc.b StatDiskSize,$00
dc.b StatDiskSize+1,$00
dc.b $00
HD20Fmt dc.b StatDiskInPlace,$08 ; HD-20 format kind info
dc.b StatTwoSidedFmt,$00
dc.b StatDiskSize,$98 ; $9835 = 38965
dc.b StatDiskSize+1,$35
dc.b $00
GCR400KFmt dc.b StatDiskInPlace,$02 ; 400K GCR format kind info
dc.b StatDiskSize,$03 ; $0320 = 800
dc.b StatDiskSize+1,$20
dc.b $00
GCR800KFmt dc.b StatDiskInPlace,$02 ; 800K GCR format kind info
dc.b StatCurrentFmt,$04
dc.b StatDiskSize,$06 ; $0640 = 1600
dc.b StatDiskSize+1,$40
dc.b $00
MFM720KFmt dc.b StatDiskInPlace,$02 ; 720K MFM format kind info
dc.b StatCurrentFmt,$08
dc.b StatDiskSize,$05 ; $05A0 = 1440
dc.b StatDiskSize+1,$A0
dc.b $00
MFM1440KFmt dc.b StatDiskInPlace,$02 ; 1440K MFM format kind info
dc.b StatDiskSize,$0B ; $0B40 = 2880
dc.b StatDiskSize+1,$40
dc.b $00
GCRonHDFmt dc.b StatDiskInPlace,$03 ; 400K/800K GCR on HD media format kind info
dc.b StatFmtAllowed,$10
dc.b StatCurrentFmt,$10
dc.b StatDiskSize,$0B ; $0B40 = 2880
dc.b StatDiskSize+1,$40
dc.b $00
;---------------------------
DriveStatusPtrs dc.w NoDrive-StatusTables ; 0 - no drive
dc.w NoDrive-StatusTables ; 1 - unspecified drive
dc.w SSGCRDrive-StatusTables ; 2 - Single Sided 400K GCR Drive
dc.w DSGCRDrive-StatusTables ; 3 - Double Sided 400K/800K GCR Drive
dc.w DSMFMGCRDrive-StatusTables ; 4 - Double Sided GCR / MFM Drive
dc.w NoDrive-StatusTables ; 5 - unspecified drive
dc.w NoDrive-StatusTables ; 6 - unspecified drive
dc.w HD20Drive-StatusTables ; 7 - HD-20 drive
dc.w NoDrive-StatusTables ; 8 - unspecified drive
MediaStatusPtrs dc.w NoMedia-StatusTables ; 0 - No Media kind
dc.w LoDenMedia-StatusTables ; 1 - unknown media kind
dc.w HD20Media-StatusTables ; 2 - HD-20 media
dc.w LoDenMedia-StatusTables ; 3 - Low Density media
dc.w HiDenMedia-StatusTables ; 4 - High Density media
FmtStatusPtrs dc.w UnCheckedFmt-StatusTables ; 0 - uncheckedFormat
dc.w NoFmt-StatusTables ; 1 - unknownFormat
dc.w HD20Fmt-StatusTables ; 2 - HD20Format
dc.w GCR400KFmt-StatusTables ; 3 - GCR400Kformat
dc.w GCR800KFmt-StatusTables ; 4 - GCR800Kformat
dc.w MFM720KFmt-StatusTables ; 5 - MFM720Kformat
dc.w MFM1440KFmt-StatusTables ; 6 - MFM1440Kformat
dc.w GCRonHDFmt-StatusTables ; 7 - GCRonHDformat
DriveAttributes ; table of drive attributes, indexed by PhysDriveNumber
dc.b %00000000 ; drive 0, non-existent
dc.b %00000000 ; drive 1, primary int removable
dc.b %00001000 ; drive 2, secondary int removable
dc.b %00000000 ; drive 3, non-existent
dc.b %00000000 ; drive 4, non-existent
dc.b %00000101 ; drive 5, primary ext fixed HD-20
dc.b %00001101 ; drive 6, secondary ext fixed HD-20
dc.b %00001101 ; drive 7, secondary ext fixed HD-20
dc.b %00001101 ; drive 8, secondary ext fixed HD-20
dc.b 0 ; word align
endwith
;_______________________________________________________________________
;
; Routine: jsr CheckStaticAttr
; Inputs: d1 - Drive Number
; Outputs: none
; Destroys: d0
; Calls: AddrAndSense
; Called by: PollingTask, SetUpDrive
;
; Function: Checks the static attributes of the currently selected
; drive, and updates the per-drive data structures to
; reflect the current state of the attributes. Currently
; tested attributes are MediaKind, and WriteEnabled.
;_______________________________________________________________________
CheckStaticAttr
with MyDriveInfo
move.l a3,-(sp)
move.b #LoDenMediaKind,MediaKind(a1,d1.w) ; assume low density media
move.b DriveKind(a1,d1.w),d0 ; check for multi-density drive
cmpi.b #DSMFMGCRDriveKind,d0 ; see if SuperDrive
bne.s @CheckWrEnable ; if not superdrive, media density is correct
moveq #r1MegMediaAdr,d0 ; check for 1 or 2 megabyte media
bsr AddrAndSense ; test the media kind
bne.s @CheckWrEnable ; if low density media
move.b #HiDenMediaKind,MediaKind(a1,d1.w) ; indicate High Density media in drive
@CheckWrEnable movea.l DriveInfoPtrs(a1,d1.w*4),a3 ; get ptr to drive info for this drive
st.b WriteProtected(a3) ; assume write protected
moveq #rNoWrProtectAdr,d0 ; check write enabled/protected
bsr AddrAndSense ; test the write protect
beq.s @Done ; if write protected
clr.b WriteProtected(a3) ; not write protected
@Done movea.l (sp)+,a3
rts ; all done
endwith
;_______________________________________________________________________
;
; Routine: bsr BlockToPhysAddr
; Inputs: d0.l - Logical Block Number
; d1.w - active drive number
; Outputs: d0 - error status code
; ccr - based on error code
; PhysCylinder - Physical Cylinder number
; PhysHead - Physical Head number
; PhysSector - Physical Sector number
; PhysCylSector - Physical Sector number relative to beginning of cylinder
; PhysSectsPerHead- Physical Number of Sectors per head on this Cylinder
; PhysSectsPerCyl - Physical Number of Sectors on this Cylinder
; Destroys: d0,a0
; Calls: none
; Called by: ReadWriteSetUp, SetupTrackInfo, SWIM3CtlFormat, SWIM3CtlVerify
;
; Function: Converts a logical block number into a physical disk address,
; and returns the Physical Cylinder, Head, and Sector numbers,
; along with the total count of sectors on this head. Range
; checking is also performed to assure that the Block Number is
; not beyond the end of the disk.
;
; NOTE: The sector number returned will always be adjusted
; so that it is zero based, even though it may not be
; zero based on the disk (MFM is 1 based, GCR is 0 based)
;_______________________________________________________________________
BlockXlateHeadsPerCyl equ -1 ; heads per cylinder
BlockXlateSectsPerHead equ 0 ; sectors per head
BlockXlateSectsPerCyl equ 1 ; sectors per cylinder (1 head)
BlockXlateCylBias equ 2 ; starting at cylinder zero (first group of 16)
BlockXlateSpare equ 3 ; ...
BlockXlateBlockLimit equ 4 ; sectors * heads * cylinders
BlockToPhysAddr
move.l d2,-(sp) ; save d2
moveq #0,d2
move.b DiskFormat(a1,d1.w),d2 ; get format to read
lea BlockXlateTable,a0
adda.w (a0,d2*2),a0 ; a0 points to desired table
bra.s @SearchStart ; start searching for the cylinder group
@SearchLoop sub.w BlockXlateBlockLimit(a0),d0
blt.s @SearchDone ; exit if block number below limit
addq.w #6,a0 ; point to next record
@SearchStart tst.b BlockXlateSectsPerHead(a0) ; see if end if list
bne.s @SearchLoop ; not the end, keep on searching
moveq #paramErr,d0 ; indicate block number too big for this format
bra.s @exit ; return with error status
@SearchDone move.b (a0)+,PhysSectsPerHead(a1) ; update sectors per head
moveq #0,d2
move.b (a0)+,d2
move.b d2,PhysSectsPerCyl(a1) ; update sectors per cylinder
move.b (a0)+,PhysCylBias(a1) ; save starting cylinder number
addq.w #1,a0 ; skip past dummy field
add.w (a0)+,d0 ; undo subtract from loop
divu.w d2,d0 ; sector offset / sectors per cylinder
move.b d0,PhysCylinder(a1) ; save physical cylinder #
swap d0
move.b d0,PhysCylSector(a1) ; save physical sector # within cylinder
moveq #-1,d2 ; start with head 0
@HeadLoop move.b d0,PhysSector(a1) ; save sector number in case we're on head
addq.b #1,d2 ; bump head
sub.b PhysSectsPerHead(a1),d0 ; is sector < PhysSectsPerHead?
bge.s @HeadLoop ; yes, found correct head
move.b d2,PhysHead(a1) ; update head number
move.b PhysCylBias(a1),d0
add.b d0,PhysCylinder(a1) ; update cylinder number
moveq #noErr,d0 ; indicate success
@exit move.l (sp)+,d2
tst.w d0
rts ; all done
BlockXlateTable dc.w UnknownTable-BlockXlateTable ; 0 - uncheckedFormat
dc.w UnknownTable-BlockXlateTable ; 1 - unknownFormat
dc.w UnknownTable-BlockXlateTable ; 2 - HD20Format
dc.w GCR400KTable-BlockXlateTable ; 3 - GCR400Kformat
dc.w GCR800KTable-BlockXlateTable ; 4 - GCR800Kformat
dc.w MFM720KTable-BlockXlateTable ; 5 - MFM720Kformat
dc.w MFM1440KTable-BlockXlateTable ; 6 - MFM1440Kformat
dc.w GCRonHDTable-BlockXlateTable ; 7 - GCRonHDformat
dc.w UnknownTable-BlockXlateTable ; 8 - reserved for future use
dc.w UnknownTable-BlockXlateTable ; 9 - reserved for future use
dc.w UnknownTable-BlockXlateTable ; 10 - reserved for future use
dc.b 0 ; word align
dc.b 1 ; 1 head per cylinder
GCR400KTable dc.b 12 ; 12 sectors per head
dc.b 12*1 ; 12 sectors per cylinder (1 head)
dc.b 0*16 ; starting at cylinder zero (first group of 16)
dc.b 0 ; align
dc.w 12*1*16 ; 12 sectors, 1 head, 16 cylinders
dc.b 11,11*1,1*16,0
dc.w 11*1*16
dc.b 10,10*1,2*16,0
dc.w 10*1*16
dc.b 9,9*1,3*16,0
dc.w 9*1*16
dc.b 8,8*1,4*16,0
dc.w 8*1*16
dc.b 0
dc.b 2 ; 2 head per cylinder
GCRonHDTable
GCR800KTable dc.b 12,12*2,0*16,0
dc.w 12*2*16
dc.b 11,11*2,1*16,0
dc.w 11*2*16
dc.b 10,10*2,2*16,0
dc.w 10*2*16
dc.b 9,9*2,3*16,0
dc.w 9*2*16
dc.b 8,8*2,4*16,0
dc.w 8*2*16
dc.b 0
dc.b 2 ; 2 head per cylinder
MFM720KTable dc.b 9,9*2,0*80,0
dc.w 9*2*80
dc.b 0
dc.b 2 ; 2 head per cylinder
MFM1440KTable dc.b 18,18*2,0*80,0
dc.w 18*2*80
dc.b 0
dc.b 1 ; 1 head per cylinder
UnknownTable dc.b 8,8*1,0*1,0 ; 1 cylinder, 1 head, 8 sectors
dc.w 8*1*1 ; let blocks 0..7 translate, and die in
dc.b 0 ; read sector header with no address mark
RequiredMedia dc.b unknownMediaKind ; 0 - uncheckedFormat
dc.b unknownMediaKind ; 1 - unknownFormat
dc.b HD20MediaKind ; 2 - HD20Format
dc.b LoDenMediaKind ; 3 - GCR400Kformat
dc.b LoDenMediaKind ; 4 - GCR800Kformat
dc.b LoDenMediaKind ; 5 - MFM720Kformat
dc.b HiDenMediaKind ; 6 - MFM1440Kformat
dc.b HiDenMediaKind ; 7 - GCRonHDformat
dc.b unknownMediaKind ; 8 - reserved for future use
dc.b unknownMediaKind ; 9 - reserved for future use
dc.b unknownMediaKind ; 10 - reserved for future use
HeadSelectDecode
dc.b rRdData0Adr ; select head 0
dc.b rRdData1Adr ; select head 1
GCRFmtSelDecode
dc.b rStepOffAdr ; select head 0
dc.b r1MegMediaAdr ; select head 1
;_______________________________________________________________________
;
; Routine: jsr ReadSectorHdr
; Inputs: d1 - active drive number
; SeekingCylinder - Desired cylinder number
; PhysHead - Desired head number
; PhysSectsPerHead- Upper limit for sector numbers
; Outputs: d0 - error status code
; ccr.z - true if an error occured
; SectHdrCylinder - Cylinder number from sector header
; SectHdrHead - Head number from sector header
; SectHdrSector - Sector number from sector header
; SectHdrFmtKind - Format Kind / Block Size from sector header
; SectHdrHandshake- Handshake register after last byte
; SectHdrError - Error register after last byte
; AdrSearchCount - number of bytes found before header
; CurSectsPerHead - copy of PhysSectsPerHead
; Destroys: d0,d2,a0
; Calls: AddrAndSense, WaitForInterrupt
; Called by: SWIM3CtlVerify, RawTrackRead, WriteCylinder, FmtTrack
; PrefetchIntoCache, SetUpDrive
;
; Function: Reads the sector header information for the next sector
; found on the specified head, on the current cylinder.
;
; NOTE: This routine returns to it's caller while waiting for the
; read to complete.
;
; NOTE: The sector number returned will always be adjusted
; so that it is zero based, even though it may not have been
; zero based on the disk (MFM is 1 based, GCR is 0 based)
;
; NOTE: If a sector is found that is greater than or equal to
; PhysSectsPerHead, it should be skipped. The error reported
; will be SeekErr, since the header was read correctly, but there
; is no buffer for the data.
;_______________________________________________________________________
ReadSectorHdr move.l vReadSectorHdr(a1),-(sp) ; push vector
rts ; call routine
jReadSectorHdr moveq #0,d2
move.b DiskFormat(a1,d1.w),d2 ; see what format to read
move.b RequiredMedia(d2.w),d0 ; see what media is required
cmp.b MediaKind(a1,d1.w),d0 ; see if we have correct media
bne.s @ReadUnknownHdr ; if wrong kind of media
lea @ReadHdrDecode,a0
adda.w (a0,d2.w*2),a0
jmp (a0) ; dispatch to the routine
@ReadHdrDecode dc.w @ReadUnknownHdr - @ReadHdrDecode ; 0 - uncheckedFormat
dc.w @ReadUnknownHdr - @ReadHdrDecode ; 1 - unknownFormat
dc.w @ReadUnknownHdr - @ReadHdrDecode ; 2 - HD20Format
dc.w @ReadGCRHdr - @ReadHdrDecode ; 3 - GCR400Kformat
dc.w @ReadGCRHdr - @ReadHdrDecode ; 4 - GCR800Kformat
dc.w @ReadLoDenMFMHdr- @ReadHdrDecode ; 5 - MFM720Kformat
dc.w @ReadHiDenMFMHdr- @ReadHdrDecode ; 6 - MFM1440Kformat
dc.w @ReadGCRHdr - @ReadHdrDecode ; 7 - GCRonHDformat
dc.w @ReadUnknownHdr - @ReadHdrDecode ; 8 - reserved for future use
dc.w @ReadUnknownHdr - @ReadHdrDecode ; 9 - reserved for future use
dc.w @ReadUnknownHdr - @ReadHdrDecode ; 10 - reserved for future use
@ReadUnknownHdr moveq #noAdrMkErr,d0 ; unsupported format, return with error
rts ; all done
;-------------------
@ReadLoDenMFMHdr
moveq #DSMFMGCRDriveKind,d0 ; check if drive supports MFM formats
cmp.b DriveKind(a1,d1.w),d0 ;
beq.s @ReadHdr ; if correct drive, join common code
moveq #noAdrMkErr,d0 ; can't read MFM address marks with this drive
rts ; return with error
;-------------------
@ReadGCRHdr
@ReadHiDenMFMHdr
@ReadHdr move.l (sp)+,ReadHdrPc(a1) ; •• save callers pc
moveq #0,d0
move.b PhysHead(a1),d0 ; get the desired head number
move.b HeadSelectDecode(d0.w),d0 ; get the head select command
bsr AddrAndSense ; select the disk head
tst.b ([vError,a1]) ; read and clear any pending errors
move.b #StartAction+WriteMode,\
([vZeroes,a1]) ; clear action and write mode
nop
move.l #TrackTimeout,d2 ; 500ms second timeout
move.l #(StartAction<<16)+\
idIntNum,d0 ; we want the 'ID' interrupt
bsr WaitForInterrupt ; and wait for it...
bne.s @exit ; exit if error
@readHdrDone move.b #StartAction,([vZeroes,a1]) ; stop reading
move.b ([vError,a1]),SectHdrError(a1) ; save the final error register
move.b ([vFormatByte,a1]),\
SectHdrFmtKind(a1) ; get the format byte
move.b ([vCurTrack,a1]),d0 ; keep track info in d0
move.b d0,SectHdrCylinder(a1) ; update cylinder number
andi.b #$7F,SectHdrCylinder(a1) ; mask off head#
lsr.b #7,d0 ; head is in bit 7
move.b d0,SectHdrHead(a1) ; update the head #
moveq #$7F,d2
and.b ([vCurSect,a1]),d2 ; mask off the ID_valid bit
move.b d2,SectHdrSector(a1) ; update sector number
btst.b #GCRModeBit,([vSetup,a1]) ; are we in gcr mode
bne.s @chkCRCErr ; yes, don't adjust sector #
subq.b #1,SectHdrSector(a1) ; else, MFM, dec the sector #
@chkCRCErr btst.b #CRCAddrErrBit,\
SectHdrError(a1) ; was there a CRC error?
bne.s @CRCError ; yes, flag it
cmp.b PhysHead(a1),d0 ; see if we found the correct head
bne.s @SeekError ; if wrong head
move.b SectHdrCylinder(a1),d0 ; get the cylinder number we found
cmp.b SeekingCylinder(a1),d0 ; see if we found the correct cylinder
bne.s @SeekError ; if wrong cylinder
@chkRangeErr move.b SectHdrSector(a1),d0 ; get sector #
cmp.b PhysSectsPerHead(a1),d0 ; is it in range?
bhs.s @SeekError ; no, error exit
moveq #0,d0 ; else, no error
@exit move.l ReadHdrPc(a1),-(sp) ; •• restore callers pc
tst.w d0
rts ; all done
@SeekError moveq #seekErr,d0 ; report the seek error
bra.s @exit
@CRCError moveq #badCksmErr,d0 ; report the crc error
bra.s @exit
;_______________________________________________________________________
;
; Routine: jsr RawTrackRead
; Inputs: a1 - SonyVars
; a3 - ptr to reply count
; d1 - drive#
; ClockBuffer - ptr to clock buffer
; DataBuffer(a1) - ptr to data buffer
; ByteCount(a1) - # of bytes to read
; PhysHead - desired head #
; PhysSector(a1) - desired sector #
;
; Outputs: d0 - error code
; Destroys: a0,a3,d1-d3
; Calls: AddrAndSense, ReadSectorHdr, CancelTMPBTimer, StartTMPBTimer
; WaitTMPBdone, StartDMAAction
; Called by: SWIM3CtlGetRawData
;
; Function: Reads raw data, starting from a postion specified by the
; search mode value (0-3).
;_______________________________________________________________________
RawTrackRead move.l vRawTrackRead(a1),-(sp) ; push vector
rts ; call routine
jRawTrackRead moveq #0,d0
move.b PhysHead(a1),d0 ; get the desired head number
lea HeadSelectDecode,a0
move.b (a0,d0.w),d0 ; get the head select command
bsr AddrAndSense ; select the disk head
move.w SearchMode(a1),d0 ; get search mode to use
beq.s @ReadImmediate ; 0 = read now
cmpi.b #3,d0
blo.s @readAtMark ; 1,2 = read at mark
; else, 3 = read at index...
;----------------------------
@ReadAtIndex moveq #rIndexPulseAdr,d0 ; MFM only, wait for index pulse first
bsr AddrAndSense
beq.s @ReadAtIndex
move.b PhysHead(a1),d0 ; get the desired head number
lea HeadSelectDecode,a0
move.b (a0,d0.w),d0 ; get the head select command
bsr AddrAndSense ; select the disk head
bra.s @ReadImmediate ; start reading from index
;----------------------------
@ReadAtMark move.l #gcrRawTrackCnt,d0 ; prepare to DMA at least 2 revolutions worth of data
btst.b #GCRModeBit,([vSetup,a1]) ; are we in gcr mode
bne.s @ReadCountBytes ; yes, just load the whole track, process later
moveq #36,d3 ; init our counter
@tryAnother move.l (sp)+,AsyncPc(a1) ; •• save callers pc
bsr ReadSectorHdr ; find the address header
move.l AsyncPc(a1),-(sp) ; •• restore callers pc
tst.w d0 ; exit if not found
bne @exit ;
move.b ([vCurSect,a1]),d0 ; get sector #
cmp.b PhysSector(a1),d0 ; does it match the one we want?
dbeq d3,@tryAnother ; no, read next header
beq.s @chkDataAddr ; if we found it, continue to read the track
moveq #sectNFErr,d0 ; indicate sector not found (timeout)
bra @exit ; exit if not found
@chkDataAddr cmpi.w #1,SearchMode(a1) ; are we reading after address field?
beq.s @ReadImmediate ; yes, then we're set
lea ReadyTMPB+TimeMgrOffset(a1),a0 ; use the Ready timer to delay into the sector
bsr CancelTMPBTimer ; cancel any old timeout
moveq #SectorDataTime,d0 ; get time for 1 sector of data (about 8 ms)
bsr StartTMPBTimer ; start the timer
move.l (sp)+,d2 ; •• pop pc
bsr WaitTMPBdone ; wait for it to time out
move.l d2,-(sp) ; •• restore pc
;----------------------------
; we're positioned correctly, start the DMA now
;----------------------------
@ReadImmediate move.l ByteCount(a1),d0
andi.l #$7FFF,d0 ; max of 32K
@ReadCountBytes
add.w d0,d0 ; double count, to include flag bytes (for marks)
; bit 31 = 0 for read
move.l FloppyDMAStart(a1),a0 ; get 16-bit address
bsr SetUpDMAXFer ; get DMA controller ready to go
move.b #StartAction+WriteMode,\
([vZeroes,a1]) ; clear action and write mode
nop
ori.b #CopyProtMode+DisGCRConv,\
([vSetup,a1]) ; set to copy protect mode
nop
tst.b ([vError,a1]) ; read and clear any pending errors
move.l (sp)+,AsyncPc(a1) ; •• save callers pc
move.l #TrackTimeout*2,d0 ; 500ms second timeout
bsr StartDMAAction ; set action, wait for DMA to complete
move.l AsyncPc(a1),-(sp) ; •• restore callers pc
andi.b #~(CopyProtMode+DisGCRConv),\
([vSetup,a1]) ; clear copy protect
move.b #StartAction,([vZeroes,a1]) ; clear action
tst.w d0
bne.s @exit ; if we timed out, return error
@convertData movea.l TrackCacheStart(a1),a0 ; get ptr to data
btst.b #GCRModeBit,([vSetup,a1]) ; are we in gcr mode
bne.s @CopyGCR ; yes, process GCR data
;----------------------------
; MFM mode: data is read, now just transfer to user buffer,
; packing the mark flags into the clock buffer
;----------------------------
@CopyMFM move.l a4,-(sp) ; • save this reg
movea.l DataBuffer(a1),a4 ; init ptr to data buffer
movea.l ClockBuffer(a1),a2 ; init ptr to clock buffer
move.l a0,d2
addi.l #DMABufLength,d2 ; d2 = end+1 of floppy DMA buffer, for compare
@mfmLoop moveq #$01,d3 ; init our mark bit byte
@nextMark cmp.l a0,d2 ; are we at end of buffer?
ble.s @done ; yes, exit
move.b (a0)+,d0 ; get next mark flag byte
beq.s @stuffMFM ; if 0, then it's a valid mark byte
cmpi.b #$80,d0 ; or if $80,
bne.s @nextMark ; otherwise, get another (SWIM3 bug)
@stuffMFM move.b (a0)+,(a4)+ ; stuff the data byte
add.b d0,d0
roxl.b #1,d3 ; shift in next mark bit
bcc.s @nextMark ; repeat for 8 bytes
@nextByte tst.l a2 ; do we have a mark buffer?
beq.s @cntByte ; no, just count
move.b d3,(a2)+ ; stuff the 8 mark bits
@cntByte addq.l #8,(a3) ; count the 8 bytes
subq.l #8,ByteCount(a1) ; any left
cmpi.l #8,ByteCount(a1) ; more then 8 left?
bhi.s @mfmLoop ; yes, get them (don't bother with the remainder)
@done movea.l (sp)+,a4 ; • restore reg
@exit rts
;----------------------------
; GCR mode: data is read, find the start based on SearchMode,
; and copy the data into the users buffer (ignore the clock buffer).
;----------------------------
@CopyGCR tst.w SearchMode(a1) ; get search mode to use
beq.s @copyData ; 0 = read now
moveq #12*2,d2 ; init our sector counter
@nextGCRMark bsr.s @findAddrMark ; get the next address mark (in d0)
beq.s @noAddrError ; not found, exit
cmp.b PhysSector(a1),d0 ; does it match?
dbeq d2,@nextGCRMark ; no, try next
bne.s @noAddrError ; not found, exit with error
adda.w #7*2,a0 ; skip over address header
moveq #1,d0
cmp.w SearchMode(a1),d0 ; are we reading after address field?
beq.s @copyData ; yes, copy data from here
bsr.s @findBitSlip ; no, find next bit slip bytes (after data field)
beq.s @noDataError
bra.s @copyData ; and copy the data
@nextAddrMark bsr.s @findAddrMark ; find next address mark
beq.s @noAddrError ; exit if not found
@copyData moveq #0,d0
movea.l DataBuffer(a1),a2 ; init ptr to user data buffer
move.l TrackCacheStart(a1),d2 ; get start of buffer
add.l #DMABufLength,d2 ; calc end+1 of buffer
@gcrLoop cmp.l a0,d2 ; are we at end of buffer?
ble.s @gcrExit ; yes, exit
move.b (a0)+,d0 ; get next flag byte
bne.s @gcrLoop ; if not zero, then it's not a flag byte
move.b (a0)+,(a2)+ ; stuff the next byte
addq.l #1,(a3) ; count the byte
subq.l #1,ByteCount(a1) ; any left
bhi.s @gcrLoop ; yes, get them (don't bother with the remainder)
@gcrExit moveq #0,d0
rts ; all done
@noAddrError moveq #noAdrMkErr,d0 ; > 3 = unsupported format, return with error
rts ; all done
@noDataError moveq #noDtaMkErr,d0
rts ; all done
;------------------
@findAddrMark move.w #1024*2,d3 ; init our byte counter
@aLoop move.b #$D5,d0 ; next byte = 1st mark byte?
cmp.b (a0),d0
bne.s @aNextByte ; no, try next
move.b #$AA,d0 ; 2nd byte = 2nd mark byte?
cmp.b 2(a0),d0
bne.s @aNextByte ; no, try next
move.b #$96,d0 ;
cmp.b 4(a0),d0 ; 3rd byte = 'Addr mark'?
beq.s @foundAMark ; yes, we found it
@aNextByte addq.l #1,a0 ; move to next byte
dbra d3,@aLoop ; count it
@foundAMark addq.w #5,a0 ; skip over mark (if found)
moveq #0,d0
move.b 3(a0),d0 ; get the sector # (if found)
move.b (@GCRDecode-$96,d0.w),d0 ; decode it
addq.w #1,d3 ; ccr.z=1 if not found, 0 if found
rts
;------------------
@findBitSlip move.w #740*2,d3 ; init our byte counter
@bLoop move.b #$DE,d0 ; next byte = 1st bit slip byte?
cmp.b (a0),d0
bne.s @bNextByte ; no, try next
move.b #$AA,d0 ; 2nd byte = 2nd bit slip byte?
cmp.b 2(a0),d0
beq.s @foundBMark ; yes, we found it
@bNextByte addq.l #1,a0 ; move to next byte
dbra d3,@bLoop ; count it
@foundBMark addq.w #3,a0 ; skip over mark (if found)
addq.w #1,d3 ; ccr.z=1 if not found, 0 if found
rts
;------------------
@GCRDecode
dc.b $00,$01,$FF,$FF,$02,$03 ; $96,$97, , ,$9A,$9B
dc.b $FF,$04,$05,$06,$FF,$FF ; ,$9D,$9E,$9F, ,
dc.b $FF,$FF,$FF,$FF,$07,$08 ; , , , ,$A6,$A7
dc.b $FF,$FF,$FF,$09,$0A,$0B ; , , ,$AB,$AC,$AD
dc.b $0C,$0D,$FF,$FF,$0E,$0F ; $AE,$AF, , ,$B2,$B3
dc.b $10,$11,$12,$13,$FF,$14 ; $B4,$B5,$B6,$B7, ,$B9
dc.b $15,$16,$17,$18,$19,$1A ; $BA,$BB,$BC,$BD,$BE,$BF
dc.b $FF,$FF,$FF,$FF,$FF,$FF ; , , , , ,
dc.b $FF,$FF,$FF,$FF,$FF,$1B ; , , , , ,$CB
dc.b $FF,$1C,$1D,$1E,$FF,$FF ; ,$CD,$CE,$CF,
dc.b $FF,$1F,$FF,$FF,$20,$21 ; ,$D3, , ,$D6,$D7
dc.b $FF,$22,$23,$24,$25,$26 ; ,$D9,$DA,$DB,$DC,$DD
dc.b $27,$28,$FF,$FF,$FF,$FF ; $DE,$DF,
dc.b $FF,$29,$2A,$2B,$FF,$2C ; ,$E5,$E6,$E7, ,$E9
dc.b $2D,$2E,$2F,$30,$31,$32 ; $EA,$EB,$EC,$ED,$EE,$EF
dc.b $FF,$FF,$33,$34,$35,$36 ; , ,$F2,$F3,$F4,$F5
dc.b $37,$38,$FF,$39,$3A,$3B ; $F6,$F7, ,$F9,$FA,$FB
dc.b $3C,$3D,$3E,$3F ; $FC,$FD,$FE,$FF
;_______________________________________________________________________
;
; Routine: jsr ReadTrack
; Inputs: a1 - SonyVars
; PhysCylinder - Physical Cylinder number
; PhysSectsPerHead - Physical Number of Sectors per head on this Cylinder
; PhysSectsPerCyl - Physical Number of Sectors on this Cylinder
; SectorBufPtr - sector indexed based list of flags/buffer index's
;
; Outputs: d0 - error code (sector not found)
; ccr.z - set based on d0
; a0 - ptr to this sectors DMA buffer entry
; Destroys: a0,d0-d3
; Calls: AddrAndSense, WaitForInterrupt
; Called by: SWIM3Read
;
; Function: Reads desired sectors from the current cylinder.
;_______________________________________________________________________
ReadTrack move.l vReadTrack(a1),-(sp) ; push vector
rts ; call routine
jReadTrack move.l (sp)+,AsyncPc(a1) ; •• save callers pc
clr.b ([vGap,a1]) ; don't need gap bytes
move.b PhysSectsPerHead(a1),\
NumSectors(a1) ; init our counter
lea sectorBufPtr(a1),a3 ; get ptr to sector buffer table
move.w SectorsToRead(a1),d3 ; get needed counts (both sides)
ror.w #8,d3
tst.b PhysHead(a1) ; which head?
beq.s @findHeader ; 0, we're set to find our position on the disk
lsr.w #8,d3 ; else, get needed count for head 1
moveq #0,d2
move.b PhysSectsPerHead(a1),d2 ; adjust for head 1
add.l d2,d2 ; * 2 for table of words
adda.l d2,a3 ; bump to 2nd side in table
;------------------------------
; find current head positon
;------------------------------
@findHeader moveq #0,d0
tst.b d3 ; any blocks to read?
beq @exit ; no, exit
swap d3 ; yes, keep count in upper word
jsr ReadSectorHdr ; find the address header
bne @exit ; exit on errors
move.b SectHdrSector(a1),d0 ; get sector # (0 based)
clr.w d3 ; init map index
move.b PhysSectsPerHead(a1),d3
subq.w #1,d3
@findSectMap cmp.b (SectorMap,a1,d3.w),d0 ; does this sector match the one head is over?
dbeq d3,@findSectMap
beq.s @nextSector
_debugger ; ••somethings screwed...
;------------------------------
; grab the next sector (in order of interleave)
; and see if needed.
;------------------------------
@nextSector moveq #0,d0 ; no error
subq.b #1,NumSectors(a1) ; count the sector
blo @exit ; no more to check, we're done
addq.w #1,d3 ; bump to next entry
cmp.b PhysSectsPerHead(a1),d3 ; are we at end of list?
blo.s @searchLoop ; no, repeat
clr.w d3 ; yes, go back to start of list
@searchLoop moveq #0,d2
move.b (SectorMap,a1,d3.w),d2 ; get next sector #
;-------------------------------
; we found a sector to read, DMA it into memory
;-------------------------------
@found move.b d2,PhysSector(a1) ; save sector#
move.b d2,([vFirstSector,a1]) ; set the sector # to read
btst.b #GCRModeBit,([vSetup,a1]) ; are we in GCR mode
bne.s @setDMA ; yes, guessed correctly
addq.b #1,([vFirstSector,a1]) ; adjust sector numbering for MFM
@setDMA moveq #0,d0
move.b 1(a3,d2.w*2),d0 ; get index into list of buffer ptrs for this sector buffer
move.l (TrackCacheDMAPtr,a1,d0.w*4),a0 ; reset starting offset to floppy DMA buffer
move.l #$00008000,d0 ; setup to some big count
; bit 31 = 0 for read
bsr SetUpDMAXFer ; get DMA controller ready to go
@setNumSects move.b #1,([vSectorsToXfer,a1]) ; xfer 1 sector
tst.b ([vError,a1]) ; clear any pending errors
move.b #StartAction+WriteMode,([vZeroes,a1]) ; clear action and write mode
moveq #0,d2
move.b PhysHead(a1),d2 ; check the head#
move.b (SectorsToRead,a1,d2.w),d2 ; get # sectors to read for this side
move.w #TrackTimeout,d0 ; approx track time
mulu.w d0,d2 ; calculate max delay for this request (1 rev per sector)
move.l #1000,d0
cmp.l d0,d2 ; make sure we give at least 1sec minimum
bgt.s @ok
move.l d0,d2
@ok
move.l #(StartAction<<16)+\
doneIntNum,d0 ; we want the 'done' interrupt
bset.b #TrackTimer,StateFlags(a1) ; we have a timeout for the whole track
bsr WaitForInterrupt ; wait for it...
bsr StopDMA ; stop the floppy DMA channel
move.b #StartAction,([vZeroes,a1]) ; clear action
nop
moveq #0,d2 ; zero for later
tst.w d0 ; any errors (like timeout)?
bne.s @hwError ; yes, see if sector was needed
move.b ([vError,a1]),d2 ; any SWIM3 errors (chksum,DMA, etc)
beq.s @chkTrack ; no, verify track/sector
moveq #BadDCkSum,d0 ; yes, use this error code
@hwError move.b PhysSector(a1),d2 ; yes, get the sector # we read
btst.b #sectorNeeded,(a3,d2.w*2) ; was this sector needed?
bne @exit ; yes, exit with error
bra @nextSector ; no, get next sector
@chkTrack move.b ([vCurTrack,a1]),d2 ; make sure track matches one we're after
andi.b #$7F,d2
cmp.b PhysCylinder(a1),d2
beq.s @checkSect
moveq #noAdrMkErr,d0 ; no, return error
bra.s @exit
@checkSect move.b PhysSector(a1),d2 ; get the sector # we read
bset.b #sectorValid,(a3,d2.w*2) ; mark as valid
bne.s @nextSector ; if already valid, don't count again
btst.b #sectorNeeded,(a3,d2.w*2) ; was this sector needed?
beq.s @nextSector ; no, get another
swap d3
subq.b #1,d3 ; count the sector
beq.s @exit ; no more requested, exit
swap d3 ; else, save count in upper word
bra @nextSector ; and get anther
@exit move.l d0,-(sp) ; • save error code
lea ReadWriteTMPB+\
TimeMgrOffset(a1),a0 ; get ptr to our timeout timer block
_RmvTime ; remove the timer
clr.l tmAddr(a0) ; it's free now
bclr.b #TrackTimer,StateFlags(a1) ; track timeout is removed
move.l (sp)+,d0 ; • restore error code
move.l AsyncPc(a1),-(sp) ; •• restore callers pc
tst.w d0
rts
;_______________________________________________________________________
;
; Routine: jsr WriteTrack
; Inputs: d0 - !=0 = start with 1st sector of request (MFM only, ignore head pos)
; 0 = write next sector under head
; a1 - SonyVars
; PhysHead - Desired head number
; Outputs:
; Destroys: a0,d0-d3
; Calls: ReadSectorHdr, WaitForInterrupt
; Called by: SWIM3Write
;
; Function: Writes the current track/side DMA buffer to the disk. We are
; assumed to be on track. Writes all sectors for PhysHead to
; the disk.
;_______________________________________________________________________
WriteTrack move.l vWriteTrack(a1),-(sp) ; push vector
rts ; call routine
jWriteTrack move.l (sp)+,AsyncPc(a1) ; •• save callers pc
clr.b ([vGap,a1])
move.b PhysSectsPerHead(a1),\
NumSectors(a1) ; init our counter
lea sectorBufPtr(a1),a3 ; get ptr to sector buffer table
tst.b PhysHead(a1) ; check the head#
beq.s @findNextHeader ; head 0, we're pointing to correct table
moveq #0,d2
move.b PhysSectsPerHead(a1),d2 ; else, adjust for head 1
add.l d2,d2 ; * 2 for table of words
adda.l d2,a3 ; bump to 2nd side in table
;------------------------------
; find next sector header that goes by (if more then 1 sector in request)
;------------------------------
@findNextHeader tst.b d0 ; check user request; more then 1 block?
bpl.s @curHeadPos ; yes, get current head position
moveq #0,d0 ; else
move.b PhysHead(a1),d0 ; get the desired head number
lea HeadSelectDecode,a0
move.b (a0,d0.w),d0 ; get the head select command
bsr AddrAndSense ; select the disk head
moveq #0,d0 ; start with 1st (only) block of request
bra.s @findNext
@curHeadPos bsr ReadSectorHdr ; find the address header
bne @exit ; exit on errors
move.b SectHdrSector(a1),d0 ; get sector # (0 based)
@findNext moveq #0,d3 ; init map index
move.b PhysSectsPerHead(a1),d3
subq.w #1,d3
@findSectMap cmp.b (SectorMap,a1,d3.w),d0 ; does this sector match the one head is over?
dbeq d3,@findSectMap
beq.s @nextSector
_debugger ; ••somethings screwed...
;------------------------------
; we know the current head position,
; find the next sector to follow that is needed
;------------------------------
@searchLoop moveq #0,d2
move.b (SectorMap,a1,d3.w),d2 ; get next sector #
btst.b #sectorNeeded,(a3,d2.w*2) ; is this sector needed?
bne.s @found ; yes, write it
@nextSector moveq #0,d0 ; no error
subq.b #1,NumSectors(a1) ; count the sector
blo @exit ; no more to check, we're done
addq.w #1,d3 ; bump to next entry
cmp.b PhysSectsPerHead(a1),d3 ; are we at end of list?
blo.s @searchLoop ; no, repeat
moveq #0,d3 ; yes, go back to start of list
bra.s @searchLoop ; and repeat
;-------------------------------
; we found a sector that needs writing, DMA it out to the disk
;-------------------------------
@found move.b d2,PhysSector(a1) ; save sector#
move.b d2,([vFirstSector,a1]) ; set the sector #
btst.b #GCRModeBit,([vSetup,a1]) ; are we in GCR mode
bne.s @setCnt ; yes, guessed correctly
addq.b #1,([vFirstSector,a1]) ; adjust sector numbering for MFM
@setCnt
moveq #0,d0
move.b 1(a3,d2.w*2),d0 ; get index into list of buffer ptrs for this sector buffer
move.l (TrackCacheDMAPtr,a1,d0.w*4),a0 ; reset starting offset to floppy DMA buffer
move.l #$80008000,d0 ; setup to some big count
; bit 31 = 1 for write
bsr SetUpDMAXFer ; get DMA controller ready to go
move.b #1,([vSectorsToXfer,a1]) ; transfer 1 sector
tst.b ([vError,a1]) ; read and clear any pending errors
move.b #StartAction,([vZeroes,a1]) ; clear action and write mode
nop
move.b #WriteMode,([vOnes,a1]) ; set write mode
moveq #0,d2
move.b PhysHead(a1),d2 ; check the head#
move.b (SectorsToWrite,a1,d2.w),d2 ; get # sectors to write for this side
move.w #TrackTimeout,d0 ; approx track time
mulu.w d0,d2 ; calculate max delay for this request (1 rev per sector)
move.l #800,d0
cmpi.l #800,d2 ; make sure we give at least 800ms minimum
bgt.s @ok
move.l d0,d2
@ok
move.l #(StartAction<<16)+\
doneIntNum,d0 ; we want the 'done' interrupt
bset.b #TrackTimer,StateFlags(a1) ; we have a timeout for the whole track?
bsr WaitForInterrupt ; and wait for it...
bsr StopDMA ; stop the floppy DMA channel
move.b #StartAction+WriteMode,\
([vZeroes,a1]) ; clear action and write mode
tst.w d0 ; any errors?
bne.s @exit ; yes, exit
moveq #0,d2
move.b PhysSector(a1),d2 ; get sector#
bclr.b #sectorNeeded,(a3,d2.w*2) ; mark this sector not needed
bra @nextSector ; and write next sector
@exit move.l d0,-(sp) ; • save error code
lea ReadWriteTMPB+TimeMgrOffset(a1),a0 ; get ptr to our timeout timer block
_RmvTime ; remove the timer
clr.l tmAddr(a0) ; it's free now
bclr.b #TrackTimer,StateFlags(a1) ; track timeout is removed
move.l (sp)+,d0 ; • restore error code
move.l AsyncPc(a1),-(sp) ; •• restore callers pc
tst.w d0 ; check for timeout error
rts
;_______________________________________________________________________
;
; Routine: jsr DeNibbleize
; Inputs: d0.w- sector# in track cache to denibbleize
; a0 - destination buffer
; a1 - SonyVars
; a3 - tag buffer
; Outputs:
; Destroys: a0,d0
; Calls:
; Called by: DiskPrime
;
; Function: For GCR, denibblizes our DMA buffer into the tag buffer and destination
; buffer. There are 699 nibbles followed by 3 checksum nibbles. We use
; a four nibble loop for the data nibbles . . . the first 12 bytes go
; into a separate buffer, then 512 bytes are placed in the user's buffer.
;
; For MFM, this routine simply fakes out the tag data and BlockMoves
; the data into the destination buffer.
;
; Uses (GCR):
; D7 = CkSumC
; D6 = CkSumB
; D5 = CkSumA
; D4 = loop counters A4 = ptr nibblized data
; D3 = buffer
; D2 = buffer
; D1 = buffer bit pairs A1 = ptr to 12 byte tag buffer
; D0 = $C0 mask A0 = ptr to 512 byte user buffer
;_______________________________________________________________________
DeNibbleize movem.l a1/a3/a4/d1-d7,-(sp) ; •• save registers
moveq #0,d2
move.b (SectorBufPtr+1,a1,d0.w*2),d2 ; get buffer# for this sector
movea.l (TrackCachePtr,a1,d2.w*4),a4 ; get logical address of sector buffer
btst.b #GCRModeBit,([vSetup,a1]) ; are we in GCR mode
bne.s @gcr ; yes, guessed correctly
; for MFM, fill out the tag data
move.b CurrentCylinder(a1),(a3)+ ; stuff track#
clr.b (a3) ; assume side 0
cmp.b PhysSectsPerHead(a1),d0 ; is it?
blt.s @0
addq.b #1,(a3) ; no, bump to side 1
@0 addq.w #1,a3
addq.b #1,d0
move.b d0,(a3)+ ; stuff sector#
move.b SectHdrFmtKind(a1),(a3) ; stuff format byte
ori.b #$20,(a3) ; make it look double sided
movea.l a0,a1 ; destination in a1
movea.l a4,a0 ; source in a0
move.l #mfmDataSize,d0 ; 512 bytes for mfm
_BlockMove
bra @done ; and we're done
@gcr addq.w #1,a4 ; skip over sector#
movea.l a3,a1 ; A1 = tag buffer
move.b #%11000000,d0 ; mask for upper 2 bits
moveq #0,d5 ; init the checksum
moveq #0,d6
moveq #0,d7
moveq #10,d4 ; loop counter for tag bytes
; first the tag bytes (12)
@RdData2 move.b (a4)+,d1 ; D1 = [00][00][A7][A6][B7][B6][C7][C6]
rol.b #2,d1 ; D1 = [A7][A6][B7][B6][C7][C6][00][00]
move.b d1,d2 ;
and.b d0,d2 ; D2 = [A7][A6][00][00][00][00][00][00]
or.b (a4)+,d2 ; D2 = ByteA'
move.b d7,d3 ;
add.b d7,d3 ; ex <- CkSumC[7]
rol.b #1,d7 ; CkSumC' <- ROL (CkSumC)
eor.b d7,d2 ; ByteA <- ByteA' XOR CkSumC'
move.b d2,(a1)+ ; store first byte
addx.b d2,d5 ; CkSumA',ex <- CkSumA + ByteA + ex
rol.b #2,d1 ; D1 = [B7][B6][C7][C6][00][00][A7][A6]
move.b d1,d2 ;
and.b d0,d2 ; D2 = [B7][B6][00][00][00][00][00][00]
or.b (a4)+,d2 ; D2 = ByteB' (no time for bad nibs)
eor.b d5,d2 ; D2 = ByteB
move.b d2,(a1)+ ; store second byte
addx.b d2,d6 ; CkSumB',ex <- CkSumB + ByteB + ex
rol.b #2,d1 ; D1 = [C7][C6][0][0][A7][A6][B7][B6]
and.b d0,d1 ; D1 = [C7][C6][0][0][0][0][0][0]
or.b (a4)+,d1 ; D1 = ByteC'
eor.b d6,d1 ; D1 = ByteC
move.b d1,(a1)+ ; store third byte
addx.b d1,d7 ; CkSumC'' <- CkSumC' + ByteC + ex
subq.w #3,d4 ; decrement counter by 3 bytes
bpl.s @RdData2 ; loop (careful->subq changes ex bit)
; then the data portion...
move.l #510,d4 ; loop counter for data field
@RdData3 move.b (a4)+,d1 ; D1 = [00][00][A7][A6][B7][B6][C7][C6]
rol.b #2,d1 ; D1 = [A7][A6][B7][B6][C7][C6][00][00]
move.b d1,d2 ;
and.b d0,d2 ; D2 = [A7][A6][00][00][00][00][00][00]
or.b (a4)+,d2 ; D2 = ByteA'
move.b d7,d3 ;
add.b d7,d3 ; ex <- CkSumC[7]
rol.b #1,d7 ; CkSumC' <- ROL (CkSumC)
eor.b d7,d2 ; ByteA <- ByteA' XOR CkSumC'
move.b d2,(a0)+ ; store first byte
addx.b d2,d5 ; CkSumA',ex <- CkSumA + ByteA + ex
rol.b #2,d1 ; D1 = [B7][B6][C7][C6][00][00][A7][A6]
move.b d1,d2 ;
and.b d0,d2 ; D2 = [B7][B6][00][00][00][00][00][00]
or.b (a4)+,d2 ; D2 = ByteB' (no time for bad nibs)
eor.b d5,d2 ; D2 = ByteB
move.b d2,(a0)+ ; store second byte
addx.b d2,d6 ; CkSumB',ex <- CkSumB + ByteB + ex
tst.w d4 ; check counter
beq.s @RdCkSum ; we are done when counter is 0
rol.b #2,d1 ; D1 = [C7][C6][0][0][A7][A6][B7][B6]
and.b d0,d1 ; D1 = [C7][C6][0][0][0][0][0][0]
or.b (a4)+,d1 ; D1 = ByteC' (no time for bad nibs)
eor.b d6,d1 ; D1 = ByteC
move.b d1,(a0)+ ; store third byte
addx.b d1,d7 ; CkSumC'' <- CkSumC' + ByteC + ex
subq.w #3,d4 ; decrement counter by 3 bytes
bra.s @RdData3 ; loop (careful->subq changes ex bit)
; now read the four checksum nibbles and compare . . .
@RdCkSum move.b (a4)+,d1 ; D1 = [00][00][A7][A6][B7][B6][C7][C6]
rol.b #2,d1 ; D1 = [A7][A6][B7][B6][C7][C6][00][00]
move.b d1,d2 ;
and.b d0,d2 ; D2 = [A7][A6][00][00][00][00][00][00]
move.b (a4)+,d3 ; D3 = [0][0][A5][A4][A3][A2][A1][A0]
or.b d3,d2 ; D2 = CkSumA
cmp.b d2,d5 ; check against calculated value
bne.s @CkSumBad ; br if bad
rol.b #2,d1 ; D1 = [B7][B6][C7][C6][00][00][A7][A6]
move.b d1,d2 ;
and.b d0,d2 ; D2 = [B7][B6][00][00][00][00][00][00]
move.b (a4)+,d3 ; D3 = [0][0][B5][B4][B3][B2][B1][B0]
or.b d3,d2 ; D2 = CkSumB
cmp.b d2,d6 ; check against calculated value
bne.s @CkSumBad ; br if bad
rol.b #2,d1 ; D1 = [C7][C6][00][00][A7][A6][B7][B6]
and.b d0,d1 ; D1 = [C7][C6][00][00][00][00][00][00]
move.b (a4),d3 ; D3 = [00][00][C5][C4][C3][C2][C1][C0]
bmi.s @CkSumBad ; branch if bad nibble
or.b d3,d1 ; D1 = CkSumC
cmp.b d1,d7 ; check against calculated value
beq.s @done ;
@CkSumBad moveq #BadDCkSum,D0 ; bad checksum
bra.s @exit
@RdVerErr moveq #DataVerErr,D0
bra.s @exit
@done moveq #NoErr,d0
@exit movem.l (sp)+,a1/a3/a4/d1-d7 ; •• restore registers
rts
SelfSyncCount equ 6
MarkCount equ 9 ; 9 mark bytes
SelfSyncTbl
MarkTable dc.b $3F,$BF,$1E,$34,$3C,$3F ; self-sync pattern for SWIM3's converter
dc.b $D5,$AA,$0B ; data mark for SWIM3's converter
TrailMkCount equ 8 ; 4 trail bytes + 2 end bytes + 2 extras
TrailMarks dc.b $27,$AA,$3F,$3F ; bit slip and gap bytes for SWIM3's converter
dc.b $3F,$3F
dc.b $99,$08 ; end of transfer for SWIM3
ALIGN 4
MFMGapValue dc.l $4E4E4E4E ; MFM gap is string of 4E's
MFMSyncValue dc.l $00000000 ; MFM sync is string of 00's
MFMMarkSize equ 8 ; # bytes in an MFM mark pattern for SWIM3
MFMIndexMark dc.b $99,$C2,$99,$C2,$99,$C2,$99,$FC ; MFM index mark pattern for SWIM3
MFMAddrMark dc.b $99,$A1,$99,$A1,$99,$A1,$99,$FE ; MFM address mark pattern for SWIM3
MFMDataMark dc.b $99,$A1,$99,$A1,$99,$A1,$99,$FB ; MFM data mark pattern for SWIM3
;_______________________________________________________________________
;
; Routine: jsr Nibbleize
; Inputs: a0 - user buffer
; a1 - SonyVars
; a3 - tag buffer (sector #)
; NibbilizeDMAPtr - Ptr into DMA buffer for this sector
; PhysSector - sector #
; Outputs:
; Destroys: a0,d0
; Calls:
; Called by: SWIM3Write
;
; Function: Nibblizes the tag buffer and destination buffer into the
; DMA buffer pointed to by NibbilizeDMAPtr.
;
; Uses:
; D7 = CkSumC
; D6 = CkSumB
; D5 = CkSumA
; D4 = loop counts A4 = ptr to DMA buffer
; D3 = A7A6B7B6C7C6 nibble
; D2 = C5C4C3C2C1C0 nibble
; D1 = B5B4B3B2B1B0 nibble A1 = ptr to data buffer
; D0 = A5A4A3A2A1A0 nibble A0 = ptr to user buffer (to A1 after 12 bytes)
;_______________________________________________________________________
Nibbleize movem.l a1/a4/d1-d7,-(sp) ; •• save registers
movea.l NibbilizeDMAPtr(a1),a4 ; get ptr into DMA buffer
btst.b #GCRModeBit,([vSetup,a1]) ; are we in GCR mode
bne.s @gcr ; yes, guessed correctly
move.l MFMGapValue,d0
move.l d0,(a4)+ ; copy remaining 10 gap bytes
move.l d0,(a4)+
move.w d0,(a4)+
move.l MFMSyncValue,d0
move.l d0,(a4)+ ; copy 12 sync bytes
move.l d0,(a4)+
move.l d0,(a4)+
assert MFMMarkSize=8
lea MFMDataMark,a1
move.l (a1)+,(a4)+ ; copy an MFM data mark
move.l (a1)+,(a4)+
move.w #$990F,(a4)+ ; turn off escaping for next 512 bytes
movea.l a4,a1 ; destination in a1
move.l #mfmDataSize,d0 ; 512 bytes for mfm
IF FAST_BMOVE THEN
_MoveBytesNoDCBZ ; •••• move the data, without using the DCBZ instr.
; •••• which gives us a big win into non-cacheable io space
ELSE
_BlockMove
ENDIF
adda.w #mfmDataSize,a4
move.w #$9904,(a4)+ ; write CRC
move.l MFMGapValue,d1
move.w d1,(a4)+ ; make sure we flush the CRC
move.w d1,(a4)+ ; before turning off writing
move.w #$9908,(a4)+ ; done with transfer
bra @done ; and we're done
;------------------------------------
; first we write some self-sync bytes, data marks, and sector number
;------------------------------------
@gcr moveq #0,d2 ; clear high byte for indexing
move.b PhysSector(a1),d2 ; get the sector number
lea MarkTable,a1 ; get ptr to our mark buffer
moveq #MarkCount-1-1,d0 ; # mark bytes (-1 for the last one, which we write separately)
@markLp move.b (a1)+,(a4)+ ; stuff the mark bytes
dbra d0,@markLp
movea.l a3,a1 ; a1 = tag buffer
move.l #$02010009,d4 ; adjusted byte write counts for 2 buffers
moveq #0,d3 ; D3 also: note D0,D1 cleared later
moveq #0,d5 ; clear the checksum bytes
moveq #0,d6 ;
moveq #0,d7 ;
moveq #$0B,d1 ; last data mark byte not yet encoded ($AD)
sub.w d0,d0 ; leave D0=0, ex = 0
bra.s @WrData2 ; and d2 = sector#
@WrDataSw movea.l a0,a1 ; switch to user buffer
@WrData1 addx.b d2,d7 ; CSumC'' <- ByteC + CSumC' + ex
eor.b d6,d2 ; ByteC' <- ByteC XOR CSumB'
move.b d2,d3 ; D3 = [00][00][00][00][A7][A6][B7][B6]
; [C7][C7][C5][C4][C3][C2][C1][C0]
lsr.w #6,d3 ; D3 = [00][00][A7][A6][B7][B6][C7][C6]
move.b d3,(a4)+ ; nibblize and write hi-bits out
subq.w #3,d4 ; got 3 more bytes (wipes out ex bit!)
move.b d7,d3 ; D3 <- CSumC
add.b d7,d3 ; ex <- CSumC[7]
rol.b #1,d7 ; CSumC' <- ROL (CSumC)
andi.b #$3F,d0 ; D0 = [00][00][A5][A4][A3][A2][A1][A0]
move.b d0,(a4)+ ; nibblize and write low ByteA out
@WrData2 move.b (a1)+,d0 ; read next ByteA
addx.b d0,d5 ; CSumA' <- ByteA + CSumA + ex
eor.b d7,d0 ; ByteA' <- ByteA XOR CSumC'
move.b d0,d3 ; D3 = [A7][A6][A5][A4][A3][A2][A1][A0]
rol.w #2,d3 ;
andi.b #$3F,d1 ; D1 = [00][00][B5][B4][B3][B2][B1][B0]
move.b d1,(a4)+ ; nibblize and write low ByteB out
move.b (a1)+,d1 ; read next ByteB
addx.b d1,d6 ; CSumB' <- ByteB + CSumB + ex
eor.b d5,d1 ; ByteB' <- ByteB XOR CSumA'
move.b d1,d3 ;
rol.w #2,d3 ;
andi.b #$3F,d2 ; D2 = [00][00][C5][C4][C3][C2][C1][C0]
move.b d2,(a4)+ ; nibblize and write low ByteC out
move.b (a1)+,d2 ; read next ByteC
tst.w d4 ; reached end of buffer?
bne.s @WrData1 ;
swap d4
bne.s @WrDataSw ; br if we are switching to user buffer
; the last 2 data bytes are written out separately since they are odd . . .
; the missing third byte is just zero . . .
@WrLast2 clr.b d3 ; D3 = [00][00][00][00][A7][A6][B7][B6]
; [00][00][00][00][00][00][00][00]
lsr.w #6,d3 ; D3 = [00][00][A7][A6][B7][B6][00][00]
move.b d3,(a4)+ ; nibblize and write hi-bits out
move.b d5,d3 ; start preparing 1st cksum byte
rol.w #2,d3 ;
move.b d6,d3 ;
rol.w #2,d3 ;
andi.b #$3F,d0 ; D0 = [00][00][A5][A4][A3][A2][A1][A0]
move.b d0,(a4)+ ; write low ByteA out
andi.b #$3F,d1 ; D1 = [00][00][B5][B4][B3][B2][B1][B0]
move.b d1,(a4)+ ; nibblize and write low ByteB out
; now we write out the three checksum bytes as 4 nibbles
@WrCkSum move.b d7,d3 ; D3 = [0][0][0][0][A7][A6][B7][B6]
; [C7][C7][C5][C4][C3][C2][C1][C0]
lsr.w #6,d3 ; D3 = [0][0][A7][A6][B7][B6][C7][C6]
move.b d3,(a4)+ ; nibblize and write hi-bits out
andi.b #$3F,d5 ; zero high 2 bits
move.b d5,(a4)+ ; write CkSumA out
andi.b #$3F,d6 ; zero high 2 bits
move.b d6,(a4)+ ; write CkSumB out
andi.b #$3F,d7 ; zero high 2 bits
move.b d7,(a4)+ ; write CkSumC out
; now, finally, write the two bit slip marks and FF bytes
lea TrailMarks,a0 ; get ptr to our bit slip/gap bytes
moveq #TrailMkCount-1,d2 ;
@WrSlip move.b (a0)+,(a4)+
dbra d2,@WrSlip
@done move.l a4,d0 ; save DMA ptr
movem.l (sp)+,a1/a4/d1-d7 ; •• restore regs
move.l d0,NibbilizeDMAPtr(a1) ; save updated DMA ptr
moveq #0,d0 ; no errors possible
rts
;_______________________________________________________________________
;
; Routine: jsr FmtTrack
; Inputs:
; Outputs: d0.w - error status code
; ccr.z - based on result
; Destroys: a0,a3,d0,d2-d3
; Calls: FmtUnknownTrack, FmtGCRTrack, FmtMFMTrack
; Called by: SWIM3CtlFormat
;
; Function: Formats a track on the current drive.
;_______________________________________________________________________
FmtTrack move.l vFmtTrack(a1),-(sp) ; push vector
rts ; call routine
jFmtTrack move.l (sp)+,AsyncPc(a1) ; •• save Pc
moveq #0,d2
move.b DiskFormat(a1,d1.w),d2 ; see what format to write
move.b (RequiredMedia,d2.w),d0 ; see what media is required
cmp.b MediaKind(a1,d1.w),d0 ; see if we have correct media
bne.s @FmtUnknownTrack ; if wrong kind of media
lea @FmtTrackDecode,a0
adda.w (a0,d2.w*2),a0
jmp (a0) ; dispatch to the routine
@FmtUnknownTrack
moveq #paramErr,d0 ; unsupported format, return with error
@FmtTrackDone move.l AsyncPc(a1),-(sp) ; •• restore Pc
tst.w d0
rts ; all done
@FmtTrackDecode dc.w @FmtUnknownTrack-@FmtTrackDecode ; 0 - uncheckedFormat
dc.w @FmtUnknownTrack-@FmtTrackDecode ; 1 - unknownFormat
dc.w @FmtUnknownTrack-@FmtTrackDecode ; 2 - HD20Format
dc.w @FmtGCRTrack -@FmtTrackDecode ; 3 - GCR400Kformat
dc.w @FmtGCRTrack -@FmtTrackDecode ; 4 - GCR800Kformat
dc.w @FmtMFMTrack -@FmtTrackDecode ; 5 - MFM720Kformat
dc.w @FmtMFMTrack -@FmtTrackDecode ; 6 - MFM1440Kformat
dc.w @FmtUnknownTrack-@FmtTrackDecode ; 7 - GCRonHDformat
dc.w @FmtUnknownTrack-@FmtTrackDecode ; 8 - reserved for future use
dc.w @FmtUnknownTrack-@FmtTrackDecode ; 9 - reserved for future use
dc.w @FmtUnknownTrack-@FmtTrackDecode ; 10 - reserved for future use
;============================================================
@FmtGCRTrack move.l a4,-(sp) ; • save a4
movea.l TrackCachePtr(a1),a4 ; get ptr to start of DMA buffer
move.w #GCRInitialSyncCount,d0
bsr @CopySyncGroups ; first copy 200 sync groups at start of track
@GCRsectorLoop moveq #0,d0
move.b SectorGapSize(a1),d0 ; get our current # of intersector sync groups
bsr @CopySyncGroups ; copy inter-sector sync groups
bsr @CopyAddrHeader ; copy an address mark+header
movea.l UserTagsPtr(a1),a3 ; get sector tags ptr
move.l UserBufPtr(a1),d0 ; get sector buffer ptr
bne.s @copyGCRSector ; copying, branch
;------------------------------------
; Clear a GCR sector...
;------------------------------------
move.w #1,d0 ;
bsr @CopySyncGroups ; copy 1 sync group before data mark
move.w #$D5AA,(a4)+ ; copy 1st 2 bytes of data mark
move.b #$0B,(a4)+ ; and the 3rd byte
moveq #0,d0
move.b PhysSector(a1),d0 ; get sector index
move.b (SectorMap,a1,d0.w),(a4)+ ; stuff sector number before data block
move.w #gcrDataSize-1,d0 ; we're formatting, # of nibbles in a GCR sector
@fillGCRLoop clr.b (a4)+ ; clear next byte of sector
dbra d0,@fillGCRLoop
move.w #$27AA,(a4)+ ; stuff 2 bit-slip bytes
move.b #$3F,(a4)+ ; and a gap byte
bra.s @nextGCRSect
;------------------------------------
; Copy a GCR sector (disk duplicator)
;------------------------------------
@copyGCRSector move.l a4,NibbilizeDMAPtr(a1) ; set the DMA ptr for Nibbleize
movea.l d0,a0 ; a0 = ptr to user buffer,
moveq #0,d0
move.b PhysSector(a1),d0 ; get sector index
move.b (SectorMap,a1,d0.w),d0 ; get logical sector
move.w d0,d2 ; keep a copy
add.w d0,d0
lsl.w #8,d0 ; * 512 to get offset
adda.w d0,a0 ; calc offset into user buffer
mulu.w #TagSize,d2 ; * 12 to get tag offset
adda.w d2,a3 ; calc offset into tag buffer
bsr Nibbleize ; copy data mark+data (d0=0)
movea.l NibbilizeDMAPtr(a1),a4 ; update our DMA ptr reg
subq.l #3,a4 ; back up before the 99 08 and one of the gaps that Nibbilize added
@nextGCRSect addq.b #1,PhysSector(a1) ; bump sector #
move.b PhysSector(a1),d0
cmp.b PhysSectsPerHead(a1),d0 ; done with track?
blt.s @GCRsectorLoop ; yes, done
clr.b PhysSector(a1) ; reset sector # for next head (if any)
move.l #$3F3F3F3F,(a4)+ ; move some gap bytes at the end of the track
move.w #$9908,(a4)+ ; add our 'end of data' chars for SWIM3
movea.l (sp)+,a4 ; • restore a4
;------------------------------------
; now setup the DMA transfer
;------------------------------------
moveq #0,d0
move.b PhysHead(a1),d0 ; get the desired head number
lea GCRFmtSelDecode,a0
move.b (a0,d0.w),d0 ; get the head select command
bsr AddrAndSense ; select the disk head (and address that reads back a '1')
move.l TrackCacheDMAPtr(a1),a0 ; reset to start of DMA buffer
move.l #$80008000,d0 ; setup to some big count
; bit 31 = 1 for write
bsr SetUpDMAXFer ; get DMA controller ready to go
tst.b ([vError,a1]) ; read and clear any pending errors
move.b #StartAction,([vZeroes,a1]) ; clear action
nop
move.b #FormatMode,([vOnes,a1]) ; set format mode
nop
move.l #TrackTimeout,d2 ; 500 ms timeout looking for index and writing track
move.l #(StartAction<<16)+\
doneIntNum,d0 ; we want the 'done' interrupt
bsr WaitForInterrupt ; and wait for it
bsr StopDMA ; stop the floppy DMA channel
move.b #StartAction,([vZeroes,a1]) ; clear GO first,
nop
move.b #FormatMode,([vZeroes,a1]) ; then Format (SWIM3 requirement)
nop
;------------------------------------
; Now measure the gap from the end
; of last sector to sector header 0
;------------------------------------
_GetMicroSeconds ; get time at end of last sector we wrote
move.l d0,d3 ; save it
bsr ReadSectorHdr ; read next sector address
bne.s @fmtGCRDone ; error, exit immediately
tst.b SectHdrSector(a1) ; was next sector# found 0? (should be)
beq.s @chkMoreSync ; yes, see if we have room for more sync between sectors
;------------------------------------
; Gap was too short... overwrote sector 0 header
;------------------------------------
moveq #fmt1err,d0 ; assume "can't find sector 0 after format"
@decSync move.b SectorGapSize(a1),d2
subq.b #1,d2 ; use 1 less sync pattern between sectors
cmpi.b #GCRMinSyncCount,d2 ; are we at minimum # of sync patterns?
blt.s @fmtGCRDone ; yes, exit with error
move.b d2,SectorGapSize(a1) ; no, update gap size
bra @FmtGCRTrack ; and try again
@chkMoreSync _GetMicroSeconds ; get time at end of sector 0 header
sub.l d3,d0 ; compute delta from end of track to start of track
lsr.l #4,d0 ; / 16 to compute delta in terms of # of GCR nibbles
subi.l #10,d0 ; subtract off header bytes
divu.w #5,d0 ; compute # of sync groups at end
sub.b SectorGapSize(a1),d0 ; subtract num of syncs we were using
bgt.s @useMore ; branch if gap a little long
moveq #fmt2Err,d0 ; gap too short, assume "can't get enough sync"
bra.s @decSync ; and try again with less sync
;------------------------------------
; Gap was a little long...
;------------------------------------
@useMore move.w d0,d3
moveq #noErr,d0 ; enough sync, format will succeed
lsr.l #1,d3 ; divide excess sync groups by 2
cmp.b PhysSectsPerHead(a1),d3 ; see if SectorGapSize could inc by 1
blt.s @fmtGCRDone ; exit if gap sync still reasonable
addq.b #1,SectorGapSize(a1) ; use more sync next time
@fmtGCRDone tst.l UserBufPtr(a1) ; are we doing a fmt/copy?
beq @FmtTrackDone ; no, we're done
moveq #0,d2 ; yes, need to update user ptrs
move.b PhysSectsPerHead(a1),d2 ; get # sectors we stuffed
move.l d2,d3 ; keep a copy
add.w d2,d2
lsl.w #8,d2 ; * 512 to get offset
add.l d2,UserBufPtr(a1) ; update our buffer ptr
mulu.w #TagSize,d3
add.l d3,UserTagsPtr(a1) ; update user tags ptr
bra @FmtTrackDone ; done with track, exit
;------------------------------------------------------------------------
; This subroutine copies D0 self sync groups to the DMA buffer.
;------------------------------------------------------------------------
@CopySyncStart lea SelfSyncTbl,a0 ; get ptr to our self-sync buffer
move.l (a0)+,(a4)+ ; copy first 4 bytes
move.w (a0)+,(a4)+ ; copy next 2 bytes
@CopySyncGroups dbra d0,@CopySyncStart ; write requested number of sync groups
rts
;------------------------------------------------------------------------
; This subroutine copies an address header to the DMA buffer.
;------------------------------------------------------------------------
@CopyAddrHeader move.w #$D5AA,(a4)+ ; copy 1st 2 bytes of address mark
clr.b (a4)+ ; and the 3rd byte
moveq #0,d2 ; init the checksum
moveq #0,d0
move.b PhysCylinder(a1),d3 ; get track
move.b d3,d0
andi.b #%00111111,d0 ; keep low 6 bits
eor.b d0,d2 ; add into checksum
move.b d0,(a4)+ ; stuff the track[0..5]
move.b PhysSector(a1),d0 ; get sector index
move.b (SectorMap,a1,d0.w),d0 ; get the sector number
eor.b d0,d2 ; add into checksum
move.b d0,(a4)+ ; stuff sector #
lsr.b #6,d3 ; get bits 6,7 of track (8-10 are 0)
tst.b PhysHead(a1) ; which head?
beq.s @setHead ; 0, we're set
bset.l #5,d3
@setHead eor.b d3,d2
move.b d3,(a4)+ ; stuff head/track
move.b FmtByteBlkSize(a1),d0 ; get format byte
andi.b #$20,d0 ; keep just the sides bit
or.b Interleave(a1),d0 ; or in the interleave
eor.b d0,d2
move.b d0,(a4)+ ; stuff the format byte
move.b d2,(a4)+ ; stuff the checksum
move.w #$27AA,(a4)+ ; stuff the 2 bit-slip bytes
move.b #$3F,(a4)+ ; and a gap byte
rts
;============================================================
@FmtMFMTrack move.l a4,-(sp) ; •• save a4
movea.l TrackCachePtr(a1),a4 ; get ptr to start of DMA buffer
move.w #MFMInitialGapSize,d0
move.l MFMGapValue,d2 ; get our gap value
bsr @CopyMFMBytes ; first copy 80 gap bytes before index
moveq #MFMSyncSize,d0
move.l MFMSyncValue,d2 ; get our sync value
bsr @CopyMFMBytes ; copy 12 sync bytes after gap
assert MFMMarkSize=8
lea MFMIndexMark,a0 ; copy an MFM index mark
move.l (a0)+,(a4)+
move.l (a0)+,(a4)+
moveq #MFMIndexMarkGapSize,d0
move.l MFMGapValue,d2 ; get our gap value
bsr @CopyMFMBytes ; copy 50 gap bytes at after index pulse
@MFMsectorLoop moveq #MFMSyncSize,d0
move.l MFMSyncValue,d2 ; get our sync value
bsr @CopyMFMBytes ; copy 12 sync bytes after gap
assert MFMMarkSize=8
lea MFMAddrMark,a0 ; copy an MFM address mark
move.l (a0)+,(a4)+
move.l (a0)+,(a4)+
bsr @CopyMFMHeader ; copy address header
move.l UserBufPtr(a1),d0 ; get sector buffer ptr
bne.s @copyMFMSector ; copying, branch
;------------------------------------
; Clear an MFM sector...
;------------------------------------
moveq #MFMAddrMarkGapSize,d0
move.l MFMGapValue,d2 ; get our gap value
bsr @CopyMFMBytes ; copy 22 gap bytes at after address mark
moveq #MFMSyncSize,d0
move.l MFMSyncValue,d2 ; get our sync value
bsr @CopyMFMBytes ; copy 12 sync bytes after gap
assert MFMMarkSize=8
lea MFMDataMark,a0 ; copy an MFM data mark
move.l (a0)+,(a4)+
move.l (a0)+,(a4)+
move.w #$990F,(a4)+ ; turn off escaping for next 512 bytes
moveq #(mfmDataSize/4)-1,d0 ; # of bytes / MFM sector
@fillMFMData move.l #$F6F6F6F6,(a4)+ ; fill the sector with this pattern
dbra d0,@fillMFMData
move.w #$9904,(a4)+ ; write the CRC bytes
bra.s @nextMFMSect
;------------------------------------
; Copy an MFM sector (disk duplicator)
;------------------------------------
@copyMFMSector moveq #12,d0
move.l MFMGapValue,d2 ; get our pre-gap value of 12 bytes
bsr @CopyMFMBytes ; copy 12 of 22 gap bytes after address mark
move.l a4,NibbilizeDMAPtr(a1) ; set the DMA ptr for Nibbleize
movea.l UserBufPtr(a1),a0 ; a0 = ptr to user buffer
moveq #0,d0
move.b PhysSector(a1),d0 ; get sector index
move.b (SectorMap,a1,d0.w),d0 ; get logical sector
add.w d0,d0
lsl.w #8,d0 ; * 512 to get offset
adda.w d0,a0 ; calc offset into user buffer
bsr Nibbleize ; copy data mark+data (d0=0)
movea.l NibbilizeDMAPtr(a1),a4 ; update our DMA ptr reg
subq.l #2,a4 ; back up before the 99 08 that Nibbilize added
;------------------------------------
; Now fill in the gap between sectors, and repeat if more remaining
;------------------------------------
@nextMFMSect moveq #0,d0
move.b SectorGapSize(a1),d0 ; get intra-sector gap size for MFM
move.l MFMGapValue,d2 ; get our gap value
bsr @CopyMFMBytes ; copy 80/101 gap bytes at after each sector
addq.b #1,PhysSector(a1) ; bump sector #
move.b PhysSector(a1),d0
cmp.b PhysSectsPerHead(a1),d0 ; done with track?
blt @MFMsectorLoop ; yes, done
moveq #MFMFinalGapSize,d0 ; get final gap size for MFM
tst.b PhysHead(a1)
beq.s @1 ; use small gap if head 0 (so we'll overlap on head 1)
add.l d0,d0 ; use more on head 1
@1 move.l MFMGapValue,d2 ; get our gap value
bsr @CopyMFMBytes ; copy 102/204 gap bytes at end of track
move.w #$9908,(a4)+ ; add our 'end of data' chars for SWIM3
movea.l (sp)+,a4 ; •• restore a4
clr.b PhysSector(a1) ; reset sector # for next head (if any)
;------------------------------------
; Setup and start the DMA
;------------------------------------
moveq #MFMFmtRetryCnt-1,d3 ; allow 3 retries/track-side
@mfmRetry moveq #0,d0
move.b PhysHead(a1),d0 ; get the desired head number
lea HeadSelectDecode,a0
move.b (a0,d0.w),d0 ; get the head select command
bsr AddrAndSense ; select the disk head
move.l TrackCacheDMAPtr(a1),a0 ; reset to start of DMA buffer
move.l #$80008000,d0 ; setup to some big count
; bit 31 = 1 for write
bsr SetUpDMAXFer ; get DMA controller ready to go
tst.b ([vError,a1]) ; read and clear any pending errors
move.b #StartAction,([vZeroes,a1]) ; clear action
nop
move.b #FormatMode,([vOnes,a1]) ; set format mode
nop
move.l #FmtTrackTime,d2 ; 600 ms timeout looking for index and writing track
move.l #(StartAction<<16)+\
doneIntNum,d0 ; we want the 'done' interrupt
bsr WaitForInterrupt ; and wait for it...
bsr StopDMA ; stop the floppy DMA channel
move.b #StartAction,([vZeroes,a1]) ; clear action first,
nop
move.b #FormatMode,([vZeroes,a1]) ; then format mode
tst.w d0 ; any errors?
dbeq d3,@mfmRetry ; yes, count, then retry the track
bne.s @FmtTrackDone ; too many errors, exit
tst.l UserBufPtr(a1) ; are we doing a fmt/copy?
beq @FmtTrackDone ; no, we're done
moveq #0,d2 ; yes, need to update user ptrs
move.b PhysSectsPerHead(a1),d2 ; get # sectors we stuffed
add.w d2,d2
lsl.w #8,d2 ; * 512 to get offset
add.l d2,UserBufPtr(a1) ; update our buffer ptr
bra @FmtTrackDone ; done with track, exit
;------------------------------------------------------------------------
; This subroutine copies D0.L gap or sync bytes to the DMA buffer.
;------------------------------------------------------------------------
@CopyMFMBytes ror.l #2,d0 ; / 4, remainder in high byte
bra.s @Cnt4
@copyLongLp move.l d2,(a4)+ ; copy 4 bytes at a time
@Cnt4 dbra d0,@copyLongLp
addq.w #1,d0 ; d0.w = 0
rol.l #2,d0 ; d0.w = bytes remaining (0-3)
bra.s @Cnt1
@copyByteLp move.b d2,(a4)+ ; copy a byte at a time
@Cnt1 dbra d0,@copyByteLp
rts
;------------------------------------------------------------------------
; This subroutine copies the address header to the DMA buffer.
;------------------------------------------------------------------------
@CopyMFMHeader move.b PhysCylinder(a1),(a4)+ ; copy track
moveq #0,d0 ; assume side 0
tst.b PhysHead(a1) ; which head?
beq.s @setMFMHead ; 0, we're set
addq.b #1,d0
@setMFMHead move.b d0,(a4)+ ; copy head
move.b PhysSector(a1),d0 ; get physical sector
move.b (SectorMap,a1,d0.w),d0
addq.b #1,d0 ; adjust for MFM numbering
move.b d0,(a4)+ ; copy the logical sector number
move.b FmtByteBlkSize(a1),d0 ; get format byte
andi.b #$1F,d0 ; keep just the block size
move.b d0,(a4)+ ; stuff the block size byte
move.w #$9904,(a4)+ ; write the CRC bytes
rts
;_______________________________________________________________________
;
; Routine: jsr TurnOnDrive
; Inputs: a1 - driver globals
; ReqDriveNumber - Drive Number
; Outputs: d0 - error status code
; d1,CurrentDrive - Drive number, (only when noErr)
; Destroys: none
; Calls: SelectDrive, AddrAndSense, TurnOffDrive, CheckDriveMode, AddrAndStrb
; CancelTMPBtimer, StartReadyTimer
; Called by: SetUpDrive, ReCalibrate
;
; Function: Selects and Powers up the specified drive, returns error
; status if no disk in drive.
; The caller must call WaitDriveReady to wait for the drive
; to attain proper motor speed.
;_______________________________________________________________________
TurnOnDrive move.l vTurnOnDrive(a1),-(sp) ; push vector
rts ; call routine
jTurnOnDrive move.w ReqDriveNumber(a1),d0 ; see what drive is requested
move.b PhysDriveNumber(a1,d0.w),d0 ; get the new physical drive number
bsr SelectDrive ; select and enable the new drive
move.b #rNoDiskInPlAdr,d0 ; prepare to see if there is a disk
bsr AddrAndSense ; read the drive status bit
beq.s @HasMedia ; turn drive on, if there is a disk in the drive
bsr.s TurnOffDrive ; turn off and disable the drive if no diskette
moveq #OffLinErr,d0 ; indicate that the disk was off line
rts ; all done
@HasMedia move.w ReqDriveNumber(a1),\
CurrentDrive(a1) ; mark new drive as powered
move.b #MotorTimeoutCount,\
MotorTimingOut(a1) ; start timing out motor when polling resumes
move.b #rMotorOffAdr,d0 ; prepare to test the drive motor status
bsr AddrAndSense ; see if it is off
beq.s @AlreadyOn ; if already on
;------------------------------------
; we must setup the drive mode every time the drive is turned on, because
; it forgets what mode it was in when it gets turned off.
;------------------------------------
bsr CheckDriveMode ; see if mode needs to change
beq.s @TurnItOn ; no change needed, just turn on motor
bsr AddrAndStrb ; otherwise, set the drive mode first
@TurnItOn moveq #wMotorOnAdr,d0 ; prepare to turn the drive motor on
bsr AddrAndStrb ; turn it on
lea ReadyTMPB+TimeMgrOffset(a1),a0 ; get the ready timer block
bsr CancelTMPBtimer ; cancel the timer in case it was running
moveq #MotorOnDelay,d0 ; get Disk Motor On wait time
bsr StartTMPBTimer ; start the timer
move.l #MotorOnSettle,SettleTime(a1) ; set Motor speed settle time
@AlreadyOn move.w CurrentDrive(a1),d1 ; return current drive number
moveq #noErr,d0 ; return successful status
rts ; all done
;_______________________________________________________________________
;
; Routine: jsr TurnOffDrive
; Inputs: a1 - ptr to globals
; Outputs: none
; Destroys: ??
; Calls: AddrAndStrb, CancelTMPBtimer
; Called by: SWIMInitialize, PollingTask, SWIMEject, SetUpDrive, TurnOnDrive,
; WaitDriveReady, ReCalibrate
;
; Function: Immediatly turns off the motor and disables the drive
; that was currently enabled.
;_______________________________________________________________________
TurnOffDrive move.l vTurnOffDrive(a1),-(sp) ; push vector
rts ; call routine
jTurnOffDrive clr.w CurrentDrive(a1) ; indicate that no drive is powered
clr.b MotorTimingOut(a1) ; indicate timeout expired
move.b #wMotorOffAdr,d0 ; motor off drive command
bsr AddrAndStrb ; send the command to the drive
move.b #StartAction\
+WriteMode,([vZeroes,a1]) ; clear action and write mode (prepare for motoroff)
nop
move.b #Drive1Enabled+Drive2Enabled\
,([vZeroes,a1]) ; disable the drive
clr.l SettleTime(a1) ; ignore any pending settle time
lea ReadyTMPB+TimeMgrOffset(a1),a0 ; get the ready timer block
jmp CancelTMPBtimer ; cancel the timer and return
;_______________________________________________________________________
;
; Routine: jsr WaitDriveReady
; Inputs: a1 - ptr to globals
; Outputs: d0,ccr.z - error status code
; Destroys: a0,d2
; Calls: WaitTMPBdone, AddrAndSense, CancelTMPBTimer, StartTMPBTimer,
; StartReadyTimer, WaitTMPBdone,TurnOffDrive
; Called by: SWIM3CtlEject, SWIM3Read, SWIM3Write, SWIM3CtlFormat,
; SWIM3CtlVerify, SWIM3CtlGetRawData, PrefetchIntoCache, ReCalibrate,
; SeekTrack
;
; Function: Polls the currently selected drive, waiting for it to become
; ready. If it doesn't happen within 1 second, it will be turned
; off, and an error status will be returned.
;_______________________________________________________________________
WaitDriveReady move.l vWaitDriveReady(a1),-(sp) ; push vector
rts ; call routine
jWaitDriveReady move.l (sp)+,d2 ; •• pop pc
lea ReadyTMPB+TimeMgrOffset(a1),a0 ; get the ready timer block
bsr WaitTMPBdone ; wait for initial delay to time out
move.w #ReadyTimeoutCount,\
WaitReadyCount(a1) ; initialize max polls counter
@WaitReadyLoop moveq #rNotReadyAdr,d0 ; prepare to test ready
bsr AddrAndSense ; test drive ready
bne.s @NotReady ; if not ready, sleep and try again
tst.l SettleTime(a1) ; get Motor speed settle time
bne.s @StartSettleTimer ; start timer if needed
move.w WaitReadyCount(a1),d0
eori.w #ReadyTimeoutCount,d0 ; were we already ready?
beq.s @readyExit ; yes, don't need the extra 10ms delay
lea SettledTMPB+TimeMgrOffset(a1),a0 ; get the settled timer
bsr CancelTMPBTimer ; cancel any old timeout
moveq #ReadyDelay,d0 ; get extra ready delay needed for WolfPack drives
bsr StartTMPBTimer ; start the timer
bsr WaitTMPBdone ; wait for it to time out
moveq #noErr,d0 ; return No Error status if drive is ready
bra.s @readyExit ; return with sucess
@StartSettleTimer
lea SettledTMPB+TimeMgrOffset(a1),a0 ; get the settled timer
bsr CancelTMPBTimer ; cancel any old timeout
move.l SettleTime(a1),d0 ; get Motor speed settle time
@strtTimer bsr StartTMPBTimer ; start the timer
moveq #0,d0 ; return No Error status if drive is ready
move.l d0,SettleTime(a1) ; remember that timer was started
bra.s @readyExit ; return with success
@NotReady move.l #ReadyPollRate,d0 ; get poll rate
bsr.s StartReadyTimer ; start the timer
lea ReadyTMPB+TimeMgrOffset(a1),a0 ; get the ready timer block
bsr WaitTMPBdone ; wait for it to time out
subq.w #1,WaitReadyCount(a1) ; count the poll
bne.s @WaitReadyLoop ; count not exhausted, check drive ready
bsr TurnOffDrive ; timed out, turn off the drive
move.w #spdAdjErr,d0 ; timed out, indicate error
@readyExit move.l d2,-(sp) ; •• restore pc
tst.w d0
rts ; return with error code
;_______________________________________________________________________
;
; Routine: jsr WaitMotorSettled
; Inputs: none
; Outputs: none
; Destroys: a0
; Calls: WaitTMPBdone
; Called by: SWIMWrite, SWIM3CtlFormat
;
; Function: Waits for the MotorSettled timer to expire indicating that
; the drive motor speed is within 1.5% of tolerance.
; WaitDriveReady should have already been called to ensure that
; the motor was up to speed, and this extra wait is only needed
; during Format and Writes where it is desireable to keep motor
; speed variation to a minimum to improve data reliability.
;_______________________________________________________________________
WaitMotorSettled
lea SettledTMPB+TimeMgrOffset(a1),a0 ; get the settled timer
jmp WaitTMPBdone ; wait for settle time to expire
;_______________________________________________________________________
;
; Routine: jsr StartReadyTimer
; Inputs: d0.l - timer count (+ for ms, - for µs)
; a1 - ptr to globals
; Outputs: none
; Destroys: d0
; Calls: StartTMPBTimer
; Called by: SetUpDrive, TurnOnDrive, WaitDriveReady, Sleep,
; ReCalibrate, SeekTrack, ReadMFMData, WriteDataBuffer
;
; Function: Starts the Ready wait timer using the supplied values.
;_______________________________________________________________________
StartReadyTimer move.l vStartReadyTimer(a1),-(sp) ; push vector
rts ; call routine
jStartReadyTimer
lea ReadyTMPB+TimeMgrOffset(a1),a0 ; get the ready timer block
jmp StartTMPBTimer ; start the timer, and return
;_______________________________________________________________________
;
; Routine: jsr Sleep
; Inputs: d0.l - sleep count, (+ for ms, - for µs)
; Outputs: none
; Destroys: d0
; Calls: StartReadyTimer, WaitTMPBdone
; Called by: SWIM3CtlEject
;
; Function: Sleeps for the specified amount of time.
;_______________________________________________________________________
Sleep jsr StartReadyTimer ; start the timer
jmp WaitTMPBdone ; return after it times out
;_______________________________________________________________________
;
; Routine: jsr ReCalibrate
; Inputs: CurrentDrive - active drive number
; a1 - ptr to globals
; Outputs: d0 - error status code
; ccr.n,z - based on value returned in d0
; SeekingCylinder - 0 (cylinder number that we are seeking)
; Destroys: d0-d2,a0
; Calls: InitSWIMChip, TurnOnDrive, CheckDriveMode, AddrAndStrb, WaitTMPBdone,
; StartReadyTimer, WaitDriveReady, SeekTrack, AddrAndSense, TurnOffDrive
; Called by: SWIM3CtlEject, SWIM3Read, SWIM3Write, SWIM3CtlFormat, SetUpDrive
;
; Function: Re-initializes the disk controller chip, and moves the disk
; heads back to cylinder zero, reporting any errors that may
; occur. This will attempt to get all of the hardware into a
; known state, when first powering up, or when recovering from
; an error.
;_______________________________________________________________________
ReCalibrate move.l vReCalibrate(a1),-(sp) ; push vector
rts ; call routine
jReCalibrate move.l (sp)+,ReCalPc(a1) ; •• save pc
move.w CurrentDrive(a1),d1 ; get current drive number
clr.b CurrentCylinder(a1,d1.w) ; indicate current Cylinder is zero
clr.b SeekingCylinder(a1) ; indicate which cylinder we are seeking
bsr InitSWIMChip ; Re-Initialize the disk controller chip
bne.s @ReCalErrA ; return with error status if couldn't init
bsr TurnOnDrive ; re-select and power the drive
bne.s @ReCalErrA ; return with error status if couldn't turn on
bsr CheckDriveMode ; see if mode needs to change
beq.s @ReCalStart ; if mode is OK, don't change is
bsr AddrAndStrb ; change the drive mode
lea ReadyTMPB+TimeMgrOffset(a1),a0 ; get the ready timer block
bsr WaitTMPBdone ; wait for any initial delay to time out
;------------------------------------
; when switching modes with the motor running, (which will cause a motor speed
; change), Ready is not immediatly correct, so we must delay before checking ready,
; otherwise it will appear that the drive is Ready, even though the motor is not
; up to speed. We will delay as if the motor was just turned on.
;------------------------------------
moveq #MotorOnDelay,d0 ; get low byte of Disk Motor On wait time
bsr.s StartReadyTimer ; start the timer
move.l #MotorOnSettle,SettleTime(a1) ; set the settle time
@ReCalStart bsr WaitDriveReady ; wait for the motor to be ready
bne.s @ReCalErrA ; return error if motor not ready
clr.b CylinderValid(a1,d1.w) ; indicate current Cylinder is valid
move.b #LastCylinder,CurrentCylinder(a1,d1.w) ; make beleive that we are at end of disk
moveq #0,d0 ; we want to go to cylinder zero
bsr.s SeekTrack ; seek to zero
bsr WaitDriveReady ; wait for the heads to move
bne.s @ReCalErrA ; return error if couldn't seek
moveq #rNotTrack0Adr,d0 ; see if we found cylinder zero
bsr AddrAndSense ; test the flag
beq.s @ReCalNoErr ; return success if cylinder zero was found
moveq #tk0BadErr,d0 ; setup error code, just in case
bra.s @ReCalErrA
@ReCalNoErr moveq #0,d0 ; indicate no error
@ReCalErrA move.b d0,CylinderValid(a1,d1.w) ; indicate current Cylinder is zero (if no err)
move.b d0,CurrentCylinder(a1,d1.w) ; indicate current Cylinder is zero (if no err)
beq.s @ReCalDone ; no error, restore pc and return
clr.b DiskFormat(a1,d1.w) ; indicate un-checked format kind
move.l d0,-(sp) ; preserve error code
bsr TurnOffDrive ; couldn't re-cal, turn off the drive
move.l (sp)+,d0 ; restore error code
@ReCalDone move.l ReCalPc(a1),-(sp) ; •• restore pc
tst.w d0 ; test error code, set flags
rts ; all done
;_______________________________________________________________________
;
; Routine: jsr SeekTrack
; Inputs: CurrentDrive - active drive number
; d0 - Desired cylinder number
; a1 - ptr to globals
; Outputs: d0 - error status code
; ccr - based on error code in d0
; SeekingCylinder - cylinder number that we are seeking
; CurSectsPerHead - Zero, to indicate unknown
; Destroys: d0-d2
; Calls: AddrAndStrb, AddrAndSense, WaitTMPBdone, StartReadyTimer, LoadSWIMparams
; Called by: SWIM3CtlEject, SWIM3Read, SWIM3Write, SWIM3CtlFormat,
; SWIM3FmtVerify, SWIM3CtlGetRawData, ReCalibrate
;
; Function: Sends the Steps Pulses to position the disk heads to the
; desired cylinder. WaitDriveReady must be called to wait
; for the heads to actually move, and settle.
;_______________________________________________________________________
SeekTrack move.l vSeekTrack(a1),-(sp) ; push vector
rts ; call routine
jSeekTrack move.l (sp)+,SeekPc(a1) ; •• pop pc
clr.b CurSectsPerHead(a1) ; new track, don't prefetch unless ReadSectorHdr called
move.b d0,SeekingCylinder(a1) ; indicate which cylinder we are seeking
move.b d0,d2 ; cylinder in d2
moveq #ParamErr,d0 ; assume cylinder too high
cmpi.b #LastCylinder+1,d2 ; check for out of range
bge.s @SeekDone ; if out of range, return with error
move.w CurrentDrive(a1),d1 ; get the active drive number
move.b CylinderValid(a1,d1.w),d0 ; see if cylinder is valid
bne.s @SeekDone ; if invalid, return with error
move.b CurrentCylinder(a1,d1.w),d0 ; get current cylinder
move.b d2,CurrentCylinder(a1,d1.w) ; update current cylinder (to desired)
sub.b d2,d0 ; compare to desired track
beq.s @SeekDone ; exit if no seek needed
move.b d0,d2 ; # steps in d2
bmi.s @stepIn ; step in
moveq #wDirPrevAdr,d0 ; step out (towards 0)
bsr AddrAndStrb ;
bra.s @seek
@stepIn neg.b d2 ; step in
moveq #wDirNextAdr,d0 ; step in (away from 0)
bsr AddrAndStrb ;
@seek moveq #wStepOnAdr,d0 ; put step command on phase lines
bsr AddrAndSense ;
move.b d2,([vStep,a1]) ; setup the step count
tst.b ([vError,a1]) ; read and clear any pending errors
move.l #StepTimeout,d2 ; 11 ms timeout waiting for step pulses to be sent
move.l #(GoStep<<16)+stepIntNum,d0 ; we want the 'step complete' interrupt
bsr WaitForInterrupt ; and wait for it...
bne.s @SeekError
move.b #GoStep,([vZeroes,a1]) ; turn off seeking
lea ReadyTMPB+TimeMgrOffset(a1),a0 ; get the ready timer block
bsr WaitTMPBdone ; wait for prior waits to time out
move.l #SeekDelay,d0 ; Seek initial wait time
bsr StartReadyTimer ; start the timer
bsr LoadSWIMparams ; setup params based upon new cylinder
bne.s @SeekError ; branch on error
moveq #0,d0 ; else, return no error
@SeekDone move.l SeekPc(a1),-(sp) ; •• restore pc
ext.w d0 ; test error code, set flags
rts ; all done
@SeekError move.b d0,CylinderValid(a1,d1.w) ; invalidate the cylinder
move.b d0,CurrentCylinder(a1,d1.w) ; setup cylinder number too
bra.s @SeekDone
;_______________________________________________________________________
;
; Routine: jsr CheckDriveMode
; Inputs: d1 - Drive Number
; Outputs: ccr.z - 0 (bne) if drive mode change needed
; ccr.z - 1 (beq) if no drive mode change needed
; d0 - command for AddrAndStrb to change drive mode,
; only valid when z indicates change is needed
; Destroys: d0,d2
; Calls: AddrAndSense
; Called by: TurnOnDrive, ReCalibrate
;
; Function: Checks to see if the drive mode (GCR/MFM) needs to be changed
; for the currently selected drive, and returns the command
; to change the drive mode, or an indication that no change
; is needed.
;_______________________________________________________________________
CheckDriveMode move.b DriveKind(a1,d1.w),d0 ; check the drive kind
cmpi.b #DSMFMGCRDriveKind,d0 ; see if SuperDrive
bne.s @ModeIsCorrect ; only superdrive supports GCR and MFM
moveq #wMFMModeOnAdr,d2 ; assume MFM format
move.b DiskFormat(a1,d1.w),d0 ; get the format
cmpi.b #MFM720Kformat,d0 ; is it MFM 720k?
beq.s @CheckMode ; yes, branch
cmpi.b #GCRonHDformat,d0 ; GCR data on HD media needs GCR mode
beq.s @NeedGCRMode ; if GCR data on hi-density disk
move.b MediaKind(a1,d1.w),d0 ; check the media
cmpi.b #HiDenMediaKind,d0 ; High density media is almost always MFM
beq.s @CheckMode ; if High Density Media
@NeedGCRMode moveq #wGCRModeOnAdr,d2 ; otherwise set to GCR format
@CheckMode moveq #rMFMModeOnAdr,d0 ; prepare to see what mode the drive is in
bsr AddrAndSense ; read the status
bne.s @InMFMMode ; if in MFM mode
@InGCRMode move.b d2,d0
cmpi.b #wGCRModeOnAdr,d0 ; we're in GCR mode, see if we want to be
rts ; return with d0 = desired mode, z = mode is correct
@InMFMMode move.b d2,d0
cmpi.b #wMFMModeOnAdr,d0 ; we're in MFM mode, see if we want to be
rts ; return with A = desired mode, z = mode is correct
@ModeIsCorrect moveq #0,d0 ; set z, indicate mode is correct
rts ; return
;_______________________________________________________________________
;
; Routine: jsr InitSWIMChip
; Inputs: a1 - ptr to globals
; Outputs: d0 - error status code
; Destroys: d0,a0
; Calls: LoadSWIMparams
; Called by: SWIM3Open, ReCalibrate
;
; Function: Initializes the SWIM3 chip. Returns an error indication
; if initialization failed.
;_______________________________________________________________________
InitSWIMChip movea.l VIA,a0
move.b #startAction+WriteMode,([vZeroes,a1]) ; turn off ACTION/WRITE
move.b #ph0+ph2,d0
move.b d0,([vPhase,a1])
tst.b (a0)
cmp.b ([vPhase,a1]),d0 ; see if we can write/read phase lines
bne.s @error ; couldn't, must be bad SWIM3
move.b #ph1+ph2,d0
move.b d0,([vPhase,a1])
tst.b (a0)
cmp.b ([vPhase,a1]),d0 ; see if we can write/read phase lines
bne.s @error ; couldn't, must be bad SWIM3
move.b #ph0+ph1+ph2,d0
move.b d0,([vPhase,a1])
tst.b (a0)
cmp.b ([vPhase,a1]),d0 ; see if we can write/read phase lines
bne.s @error ; couldn't, must be bad SWIM
@loadParams bsr LoadSWIMparams ; setup the SWIM for current format/cylinder
bne.s @error ; didn't write, return error
moveq #0,d0 ; else, no error
bra.s @exit
@error move.w #initIWMErr,d0 ; else, indicate SWIM chip error
@exit rts
;_______________________________________________________________________
;
; Routine: jsr LoadSWIMparams
; Inputs: d1 - drive #
; a1 - ptr to globals
; Outputs: ccr.z - 1 if it wrote correctly
; Destroys: d0
; Calls: ...
; Called by: SetUpDrive, SeekTrack, InitSWIMChip
;
; Function: Loads the SWIM parameter RAM with the correct parameters
; for the current disk format, checks to see if they were
; loaded correctly, and reports any errors
;_______________________________________________________________________
LoadSWIMparams moveq #0,d0
move.b #$95,([vParamData,a1]) ; set to nominal pre-comp
move.b DiskFormat(a1,d1.w),d0 ; get format type
move.b SWIMwSetupTbl(d0.w),d0
move.b d0,([vSetup,a1]) ; setup chip for this format
nop
cmp.b ([vSetup,a1]),d0 ; did it write?
rts
SWIMwSetupTbl dc.b GCRWrites*1+IBMDrive*0+ClockDiv2*1+GCRMode*1 ; 0 - uncheckedFormat (use GCR)
dc.b GCRWrites*1+IBMDrive*0+ClockDiv2*1+GCRMode*1 ; 1 - unknownFormat (use GCR)
dc.b GCRWrites*1+IBMDrive*0+ClockDiv2*1+GCRMode*1 ; 2 - HD20Format (use GCR)
dc.b GCRWrites*1+IBMDrive*0+ClockDiv2*1+GCRMode*1 ; 3 - GCR400Kformat
dc.b GCRWrites*1+IBMDrive*0+ClockDiv2*1+GCRMode*1 ; 4 - GCR800Kformat
dc.b GCRWrites*0+IBMDrive*1+ClockDiv2*1+GCRMode*0 ; 5 - MFM720Kformat
dc.b GCRWrites*0+IBMDrive*1+ClockDiv2*1+GCRMode*0 ; 6 - MFM1440Kformat
dc.b GCRWrites*1+IBMDrive*0+ClockDiv2*1+GCRMode*1 ; 7 - GCRonHDformat
;_______________________________________________________________________
;
; Routine: jsr AddrAndSense
; jsr Sense
; Inputs: d0 - drive address [0,0, HeadSelect, 0,0, ca2,ca1,ca0]
; a1 - ptr to globals
; Outputs: ccr.z - 1 (BEQ) if DriveRdData line was zero
; ccr.z - 0 (BNE) if DriveRdData line was one
; Destroys: a0,d0
; Calls: ...
; Called by: CheckDriveMode, SeekTrack, Recalibrate, WaitDriveReady,
; TurnOffDrive, TurnOnDrive, ReadTrack, RawTrackRead, ReadSectorHdr,
; CheckStaticAttr, PollingTask, SWIM3CtlEject
;
; Function: Reads the DriveRdData line for selected register.
;_______________________________________________________________________
AddrAndSense move.l vAddrAndSense(a1),-(sp) ; push the vector
rts ; call routine
jAddrAndSense move.b #ca1+ca0,([vPhase,a1]) ; set ca1 and ca0 high before changing HeadSelect
nop
btst #HeadSelectBit,d0 ; test new head select value
beq.s @ResetHeadSelect ; if new value is zero
move.b #HeadSelect,([vOnes,a1]) ; set head select
nop
bra.s @HeadSelectDone ; join common code
@ResetHeadSelect
move.b #HeadSelect,([vZeroes,a1]) ; set head select
nop
@HeadSelectDone move.b d0,([vPhase,a1]) ; write the phase lines
@Sense movea.l VIA,a0
tst.b (a0) ; access the VIA to ensure a long pulse
tst.b (a0) ; "
nop
move.b #DriveRdData,d0 ; prepare to test bit
and.b ([vHandshake,a1]),d0 ; test the DriveRdData bit
rts ; all done
;_______________________________________________________________________
;
; Routine: jsr AddrAndStrb
; Inputs: d0 - drive address [0,0, HeadSelect, 0,0, ca2,ca1,ca0]
; a1 - ptr to globals
; Outputs: ...
; Destroys: d0
; Calls: ...
; Called by: PollingTask, SWIMCtl3Eject, TurnOnDrive, TurnOffDrive,
; ReCalibrate, SeekTrack
;
; Function: Sets up the disk register address for the currently enabled
; disk and pulses the ph3 line, causing the data from ca2 to
; be written to the addressed register.
;_______________________________________________________________________
AddrAndStrb move.l vAddrAndStrb(a1),-(sp) ; push the vector
rts ; call routine
jAddrAndStrb move.b #ca1+ca0,([vPhase,a1]) ; set ca1 and ca0 high before changing HeadSelect
btst #HeadSelectBit,d0 ; test new head select value
beq.s @ResetHeadSelect ; if new value is zero
move.b #HeadSelect,([vOnes,a1]) ; set head select
bra.s @HeadSelectDone ; join common code
@ResetHeadSelect
move.b #HeadSelect,([vZeroes,a1]) ; set head select
@HeadSelectDone nop
move.b d0,([vPhase,a1]) ; write the phase lines
ori.b #ph3,d0 ; prepare to set ph3 bit
nop
move.b d0,([vPhase,a1]) ; turn ph3 on
movea.l VIA,a0
tst.b (a0) ; access the VIA to ensure a long pulse
tst.b (a0) ; "
nop
eori.b #ph3,d0 ; prepare to reset ph3 bit
move.b d0,([vPhase,a1]) ; turn ph3 off
nop
rts ; all done
;_______________________________________________________________________
;
; Routine: jsr SelectDrive
; Inputs: d0.b - physical drive number (1 or 2)
; a1 - ptr to globals
; Outputs: ccr.z - 1 (BEQ) if illegal drive number
; ccr.z - 0 (BNE) if legal drive number
; Destroys: d0
; Calls: ...
; Called by: SWIMInitialize, PollingTask, SWIM3CtlEject, SWIM3Open, TurnOnDrive
;
; Function: Asserts the ENABLE line for the specified drive.
;_______________________________________________________________________
SelectDrive move.l vSelectDrive(a1),-(sp)
rts
jSelectDrive cmpi.b #intDriveNumber,d0 ; check for internal drive
bne.s @chkExternal ; if not internal drive
move.b #Drive2Enabled,([vZeroes,a1]) ; disable external drive
nop
move.b #Drive1Enabled,([vOnes,a1]) ; enable the internal drive
rts ; all done, return z <- false
@chkExternal move.b #rNoDriveAdr,([vPhase,a1]) ; address the drive exists reg
move.b #HeadSelect,([vZeroes,a1]) ; HeadSelect is 0 for rNoDriveAdr
move.b #Drive1Enabled,([vZeroes,a1]) ; disable internal drive
nop
move.b #Drive2Enabled,([vOnes,a1]) ; enable the external drive
cmpi.b #extDriveNumber,d0 ; check for external drive
bne.s @unknownDrive ; if not external, exit
moveq #1,d0 ; return z <- false
rts ; all done
@unknownDrive moveq #0,d0 ; return z <- true
rts ; all done
;_______________________________________________________________________
;
; Routine: jsr FindDriveKind
; Inputs: a1 - ptr to globals
; Outputs: d0 - Disk Drive Kind
; Destroys: ...
; Calls: ...
; Called by: SWIM3Open
;
; Function: Determines what kind of drive is currently selected, by
; examining 4 of the disk status values returned by the drive.
;
;
; Name HeadSel ca2 ca1 ca0 | 400K 800K HD20 SuperDr Typhoon NoDrive
; |
; Revised 1 1 1 1 | 0 1 1 x 0 1
; /DrvIn 0 1 1 1 | 0 0 1 0 0 1
; /SingleSide 0 1 1 0 | 0 1 1 1 0 1
; SuperDrive 0 1 0 1 | 0 0 0 1 1 1
;_______________________________________________________________________
FindDriveKind movem.l a0/d1-d2,-(sp) ; save regs
movea.l VIA,a0
move.b #ca2+ca1+ca0,d2 ; init our ca2-0 bits
move.b d2,([vPhase,a1]) ; prepare to test REVISED status
move.b #HeadSelect,([vOnes,a1]) ; set HeadSelect
nop
move.b ([vHandshake,a1]),d0 ; prepare to test bit
nop
move.b #HeadSelect,([vZeroes,a1]) ; Clear HeadSelect
nop
moveq #%00010000,d1 ; init drive kind index / loop counter
bra.s @start
@loop move.b d2,([vPhase,a1]) ; set the phase lines
tst.b (a0) ; access VIA to let sense line settle
nop
subq.b #1,d2 ; decrement ca2..ca0 phase values
move.b ([vHandshake,a1]),d0 ; prepare to test bit
nop
@start btst #DriveRdDataBit,d0 ; isolate the DriveRdData bit
beq.s @shift ; if 0, just shift in 0
bset #0,d1 ; else, shift in a 1
@shift add.b d1,d1 ; shift in next bit
bcc.s @loop ; loop until last bit shifted in
lsr.b #1,d1
move.b DriveKindXlate(d1.w),d0 ; translate status's to drive kind
movem.l (sp)+,a0/d1-d2 ; restore regs
rts ; all done
DriveKindXlate dc.b SSGCRDriveKind ; 0 0 0 0 - 400K GCR drive
dc.b unknownDriveKind ; 0 0 0 1 - 4MB Typhoon drive
dc.b unknownDriveKind ; 0 0 1 0 - unknown
dc.b DSMFMGCRDriveKind ; x 0 1 1 - SuperDrive
dc.b unknownDriveKind ; 0 1 0 0 - unknown
dc.b unknownDriveKind ; 0 1 0 1 - unknown
dc.b unknownDriveKind ; 0 1 1 0 - unknown
dc.b unknownDriveKind ; 0 1 1 1 - unknown
dc.b unknownDriveKind ; 1 0 0 0 - unknown
dc.b unknownDriveKind ; 1 0 0 1 - unknown
dc.b DSGCRDriveKind ; 1 0 1 0 - 800K GCR drive
dc.b DSMFMGCRDriveKind ; x 0 1 1 - SuperDrive
dc.b unknownDriveKind ; 1 1 0 0 - unknown
dc.b unknownDriveKind ; 1 1 0 1 - unknown
dc.b HD20DriveKind ; 1 1 1 0 - HD-20 drive
dc.b noDriveKind ; 1 1 1 1 - No drive installed
FmtSearchOrder dc.b MFM1440Kformat ; try 1440K first
dc.b MFM720Kformat ; then 720K
dc.b GCR800Kformat ; then 800K
dc.b GCR400Kformat ; then 400K
dc.b GCRonHDformat ; and finally 400K/800K on HD media
dc.b unknownFormat ; list terminator
FmtByteValues dc.b $02 ; 1440K (block size = 2*256)
dc.b $02 ; 720K (block size = 2*256)
dc.b $22 ; 800K GCR (2 sided)
dc.b $02 ; 400K GCR (1 sided)
dc.b $00 ; 400K/800K GCR on HD media (ignore)
FmtByteMasks dc.b $FF ; 1440K, use whole byte
dc.b $FF ; 720K, use whole byte
dc.b $20 ; 800K GCR, ignore interleave info
dc.b $20 ; 400K GCR, ignore interleave info
dc.b $00 ; 400K/800K GCR on HD media, ignore byte
ALIGN 2
;_______________________________________________________________________
;
; Routine: jsr StartTMPBtimer
; Inputs: a0 - Timer parameter block
; d0 - duration (>0 = ms, <0 = microseconds)
; Outputs: none
; Destroys: d0
; Calls: _InstallTime, _PrimeTime
; Called by: StartReadyTimer, WaitDriveReady, TurnOnDrive, RawTrackRead,
; PollingTask
;
; Function: Starts running the timer pointed to by a0.
; The tmCount field of the TMPB must be filled in prior
; to this call.
;_______________________________________________________________________
StartTMPBTimer
with TimerBlock
movem.l d0/a1,-(sp) ; save d0,a1
lea GenericTimerCompletion,a1
move.l a1,tmAddr(a0) ; set our completion routine (indicate active also)
_InsTime ; install the timer
movem.l (sp)+,d0/a1 ; restore d0,a1
_PrimeTime ; prime it
rts
GenericTimerCompletion
movea.l a1,a0 ; get the pb
_RmvTime ; remove the timer
clr.l tmAddr(a0) ; indicate we're complete
move.l myCompletion(a0),d0 ; is there a user completion to run?
beq.s @exit ; no, exit
clr.l myCompletion(a0) ;
movea.l SonyVars,a1 ; yes, setup a1
;movea.l IWM,a2 ; and a2
movem.l regSave(a0),a3/d1-d3 ; and restore others
move.l d0,-(sp) ; call users completion
@exit rts
endwith
;_______________________________________________________________________
;
; Routine: jsr WaitTMPBdone
; Inputs: a0 - Timer parameter block
; Outputs: d0 - noErr
; Destroys: ...
; Calls: ...
; Called by: SeekTrack, Recalibrate, Sleep, WaitMotorSettled, WaitDriveReady,
; RawTrackRead, PollingTask
;
; Function: If the TMPB is complete, this routine simply returns to
; its caller. If it is not complete, then it returns to
; its callers caller, and will return to its caller when
; the TMPB request has completed.
;_______________________________________________________________________
WaitTMPBdone
with TimerBlock
movem.l a3/d1-d3,regSave(a0) ; save state in the user area of pb
tst.l tmAddr(a0) ; are we complete already?
beq.s @exit ; if already done
tst.l myCompletion(a0) ; see if already waiting
bne.s @MultipleWaits ; crash if already waiting
move.l (sp)+,myCompletion(a0)
@exit moveq #noErr,d0 ; return to caller's caller with no error
rts ; all done
@MultipleWaits _debugger ; multiple waits on TMPB
endwith
;_______________________________________________________________________
;
; Routine: jsr CancelTMPBtimer
; Inputs: a0 - Timer parameter block
; Outputs: ...
; Destroys: d0
; Calls: _RmvTime
; Called by: WaitDriveReady, TurnOffDrive, TurnOnDrive, RawTrackRead
;
; Function: Cancels the timer pointed to by a0, and removes it
; from the timer queue.
;_______________________________________________________________________
CancelTMPBtimer
with TimerBlock
move.w sr,-(sp)
ori.w #$0700,sr
clr.l myCompletion(a0) ;
clr.l tmAddr(a0) ; indicate we're complete
_RmvTime ; remove the timer
move.w (sp)+,sr
rts
endwith
;_______________________________________________________________________
;
; Routine: jsr StartIntTimeout
; Inputs: d2 - duration (>0 = ms, <0 = microseconds)
; a1 - ptr to globals
; Outputs: none
; Destroys: a0
; Calls: _InstallTime, _PrimeTime
; Called by: WaitForInterrupt
;
; Function: Starts a timeout timer for the floppy operation about to begin.
; If and when it expires, it disables the floppy interrupt
; and calls the completion routine with an error code.
;_______________________________________________________________________
StartIntTimeout
with TimerBlock,IntBlock
move.l a1,-(sp)
move.l d0,-(sp)
lea ReadWriteTMPB+TimeMgrOffset(a1),a0 ; get ptr to our timeout timer block
lea @InterruptTimeout,a1
tst.l tmAddr(a0) ; is the timer already installed?
bne.s @doneInst ; yes, let it continue to run
move.l a1,tmAddr(a0) ; set our completion routine (indicate active also)
move.l #'eada',qLink(a0) ; this is a VM safe task
_InsTime ; install the timer
move.l d2,d0 ; get our wait time
_PrimeTime ; prime it
@doneInst move.l (sp)+,d0
movea.l (sp)+,a1
rts
@InterruptTimeout
move.w sr,-(sp)
ori.w #$0200,sr ; •• mask out Floppy interrupts
movea.l a1,a0 ; get the pb
_RmvTime ; remove the timer
clr.l tmAddr(a0) ; it's free now
movea.l SonyVars,a1 ; yes, setup a1
;movea.l IWM,a2 ; and a2
moveq #0,d0
move.b ([vIntMask,a1]),d0 ; get the floppy int we were waiting for
beq.s @noWaits ; exit if no bits set (why are we here?)
move.b (SWIM3IntPriority,d0.w),d0 ; convert to bit#
bclr.b d0,([vIntMask,a1]) ; disable this interrupt
bclr.b #idIntNum,([vIntMask,a1]) ; disable ID int as well (in case of read)
mulu.w #IntBlockSize,d0 ; calc offset to proper pb
lea (SWIM3IntVectors,a1,d0.w),a0; get ptr to param block
move.l intCompletion(a0),d0 ; get completion routine
clr.l intCompletion(a0) ; it's free now
move.w (sp)+,sr ; •• restore ints
tst.l d0 ; did the floppy interrupt already complete the call?
beq.s @exit
movem.l intRegSave(a0),a3/d1-d3 ; restore state
movea.l d0,a0 ; completion in a0
moveq #sectNFErr,d0 ; indicate sector not found (timeout)
jsr (a0) ; and call the users completion rtn
@exit rts
@noWaits _debugger
endwith
;_______________________________________________________________________
;
; Routine: jsr WaitForInterrupt
; Inputs: d0 - (15..0) SWIM3 interrupt # (0-4)
; d0 - (23..16) value to write into Ones(a2), to start SWIM3
; d2.l - duration (>0 = ms, <0 = microseconds)
; a1 - ptr to globals
; Outputs: none
; Destroys: a0
; Calls: StartIntTimeout
; Called by: Seek, FmtTrack, WriteTrack, ReadTrack, ReadSectorHdr
;
; Function: This routine sets up the interrupt param block, enables the SWIM3
; interrupt, writes the specified value to Ones(a2) to startup SWIM3,
; and returns to the callers caller. When the interrupt completes,
; the caller gets called (with it's state restored).
;_______________________________________________________________________
WaitForInterrupt
with IntBlock
tst.b ([vInterrupt,a1]) ; clear any pending flags
move.l d2,-(sp) ; save d2
move.w SWIM3IntPBOffset(d0.w*2),d2 ; get param block offset
lea (SWIM3IntVectors,a1,d2.w),a0; get ptr to actual param block
move.l (sp)+,d2
tst.l intCompletion(a0) ; see if already waiting
bne.s @multipleWaits ; crash if already waiting
movem.l a3/d1-d3,intRegSave(a0) ; save state in the user area of pb
move.l (sp)+,intCompletion(a0)
bsr StartIntTimeout ; start a time-out for the SWIM3 interrupt
tst.b ([vIntMask,a1]) ; are there any currently enabled ints?
bne.s @multipleWaits ; yes, break
bset.b d0,([vIntMask,a1]) ; enable the SWIM3 interrupt
nop
swap d0
move.b d0,([vOnes,a1]) ; startup the chip
moveq #noErr,d0 ; return to caller's caller with no error
rts ; all done
@multipleWaits _debugger
SWIM3IntPBOffset
dc.w IntBlockSize*0 ; timer pb offset
dc.w IntBlockSize*1 ; step pb offset
dc.w IntBlockSize*2 ; ID Read pb offset
dc.w IntBlockSize*3 ; Done pb offset
dc.w IntBlockSize*4 ; Sense pb offset
endwith
;_______________________________________________________________________
;
; Routine: jsr SWIM3IntDispatch
; Inputs: none
; Outputs: none
; Destroys: none
; Calls: a SWIM3 interrupt handler
; Called by: Level 2 interrupt dispatcher
;
; Function: Dispatches to the correct floppy interrupt vector, based on
; SWIM3 interrupt flags.
;_______________________________________________________________________
SWIM3IntDispatch
with IntBlock
;movea.l IWM,a2 ; get ptr to SWIM base
movea.l SonyVars,a1 ; get ptr to our globals
moveq #0,d0
move.b ([vInterrupt,a1]),d0 ; get (and clear) the interrupt
and.b ([vIntMask,a1]),d0 ; look only at enabled ints
beq DefIntHandler ; exit if no bits set (why are we here?)
move.b SWIM3IntPriority(d0.w),d0 ; prioritize them
bclr.b d0,([vIntMask,a1]) ; disable this interrupt
bclr.b #idIntNum,([vIntMask,a1]) ; disable ID int in case enabled
mulu.w #IntBlockSize,d0 ; calc offset to proper pb
lea (SWIM3IntVectors,a1,d0.w),a0; get ptr to param block
movem.l intRegSave(a0),a3/d1-d3 ; restore state
move.l intCompletion(a0),-(sp) ; get completion routine
clr.l intCompletion(a0) ; it's free now
btst #TrackTimer,StateFlags(a1) ; do we have a timeout for the whole track?
bne.s @exit ; yes, don't remove the task here (let it run)
lea ReadWriteTMPB+TimeMgrOffset(a1),a0 ; get ptr to our timeout timer block
_RmvTime ; remove the timer
clr.l tmAddr(a0) ; it's free now
@exit moveq #0,d0 ; no timeout error
rts ; call it
@error _debugger
SWIM3IntPriority
dc.b 0 ; dummy
dc.b 0 ; 0 0 0 0 0 0 1 bit 0 is highest priority
dc.b 1 ; 0 0 0 0 0 1 0 bit 1 is highest priority
dc.b 1 ; 0 0 0 0 0 1 1 bit 1 is highest priority
dc.b 2 ; 0 0 0 0 1 0 0 bit 2 is highest priority
dc.b 2 ; 0 0 0 0 1 0 1 bit 2 is highest priority
dc.b 2 ; 0 0 0 0 1 1 0 bit 2 is highest priority
dc.b 2 ; 0 0 0 0 1 1 1 bit 2 is highest priority
dc.b 3 ; 0 0 0 1 0 0 0 bit 3 is highest priority
dc.b 3 ; 0 0 0 1 0 0 1 bit 3 is highest priority
dc.b 3 ; 0 0 0 1 0 1 0 bit 3 is highest priority
dc.b 3 ; 0 0 0 1 0 1 1 bit 3 is highest priority
dc.b 3 ; 0 0 0 1 1 0 0 bit 3 is highest priority
dc.b 3 ; 0 0 0 1 1 0 1 bit 3 is highest priority
dc.b 3 ; 0 0 0 1 1 1 0 bit 3 is highest priority
dc.b 3 ; 0 0 0 1 1 1 1 bit 3 is highest priority
dc.b 4 ; 0 0 1 0 0 0 0 bit 4 is highest priority
dc.b 4 ; 0 0 1 0 0 0 1 bit 4 is highest priority
dc.b 4 ; 0 0 1 0 0 1 0 bit 4 is highest priority
dc.b 4 ; 0 0 1 0 0 1 1 bit 4 is highest priority
dc.b 4 ; 0 0 1 0 1 0 0 bit 4 is highest priority
dc.b 4 ; 0 0 1 0 1 0 1 bit 4 is highest priority
dc.b 4 ; 0 0 1 0 1 1 0 bit 4 is highest priority
dc.b 4 ; 0 0 1 0 1 1 1 bit 4 is highest priority
dc.b 4 ; 0 0 1 1 0 0 0 bit 4 is highest priority
dc.b 4 ; 0 0 1 1 0 0 1 bit 4 is highest priority
dc.b 4 ; 0 0 1 1 0 1 0 bit 4 is highest priority
dc.b 4 ; 0 0 1 1 0 1 1 bit 4 is highest priority
dc.b 4 ; 0 0 1 1 1 0 0 bit 4 is highest priority
dc.b 4 ; 0 0 1 1 1 0 1 bit 4 is highest priority
dc.b 4 ; 0 0 1 1 1 1 0 bit 4 is highest priority
dc.b 4 ; 0 0 1 1 1 1 1 bit 4 is highest priority
DefIntHandler _debugger
rts ; just rts
endwith
;_______________________________________________________________________
;
; Routine: jsr StartDMATimeout
; Inputs: d0 - duration (>0 = ms, <0 = microseconds)
; a1 - ptr to globals
; Outputs: none
; Destroys: d0,a0
; Calls: _InstallTime, _PrimeTime
; Called by: StartDMAAction
;
; Function: Starts a timeout timer for the DMA operation about to begin.
; If and when it completes, it turns off the DMA operation
; and calls the user completion with an error code.
;_______________________________________________________________________
StartDMATimeout
with TimerBlock
movem.l d0/a1,-(sp) ; save d0,a1
lea ReadWriteTMPB+TimeMgrOffset(a1),a0 ; get ptr to our timeout timer block
lea @DMATimeout,a1
tst.l tmAddr(a0) ; is someone using this timer?
bne.s @inUse
move.l a1,tmAddr(a0) ; set our completion routine (indicate active also)
_InsTime ; install the timer
movem.l (sp)+,d0/a1 ; restore d0,a1
_PrimeTime ; prime it
rts
@DMATimeout move.w sr,-(sp)
ori.w #$0500,sr ; mask out level 5 interrupts (DMA)
movea.l a1,a0 ; get the pb
_RmvTime ; remove the timer
clr.l tmAddr(a0) ; it's free now
movea.l SonyVars,a1 ; yes, setup a1
;movea.l IWM,a2 ; and a2
move.l DMACompletionPtr(a1),d0 ; is there a user completion to run?
clr.l DMACompletionPtr(a1) ; clear the completion so DMA ints can't screw us
move.w (sp)+,sr ; restore interrupts
tst.l d0 ; did the DMA already complete?
beq.s @exit ; yes, exit
bsr ClearDMAInt ; clear floppy DMA interrupt
movem.l DMARegSave(a1),a3/d1-d3 ; restore driver state
movea.l d0,a0
moveq #sectNFErr,d0 ; indicate sector not found (timeout)
jsr (a0) ; and call the users completion rtn
@exit rts
@inUse _debugger
endwith
;_______________________________________________________________________
;
; Routine: jsr StartDMAAction
; Inputs: d0.l- DMA timeout value
; d1 - drive#
; a1 - Globals
; Outputs: none
; Destroys: d0,a0
; Calls: none
; Called by: RawTrackRead, ReadRawHeader
;
; Function: This routine saves state in the DMARegSave area, and starts
; the DMA, returning to the caller's caller.
;_______________________________________________________________________
StartDMAAction
move.l vStartDMAAction(a1),-(sp) ; push vector
rts ; call routine
;_______________________________________________________________________
;
; Routine: jsr DMAInterruptHandler
; Inputs: none
; Outputs: none
; Destroys: none
; Calls: DMACompletionHandler
; Called by: 68020 level 5 interrupt
;
; Function: This routine removes the time-out timer, clears the floppy
; interrupt, then calls the floppy handler.
;_______________________________________________________________________
DMAInterruptHandler
movea.l SonyVars,a1
;movea.l IWM,a2
lea ReadWriteTMPB+TimeMgrOffset(a1),a0 ; get ptr to our timeout timer block
_RmvTime ; remove the timer
clr.l tmAddr(a0) ; it's free now
bsr ClearDMAInt ; clear floppy DMA interrupt
movem.l DMARegSave(a1),a3/d1-d3 ; restore driver state
move.l DMACompletionPtr(a1),d0 ; get completion routine
beq.s @exit ; exit if none
clr.l DMACompletionPtr(a1) ; clear it
movea.l d0,a0
moveq #0,d0 ; no errors
jsr (a0) ; call the user completion
@exit rts
;_______________________________________________________________________
;
; Routine: jsr SetUpDMAXFer
; Inputs: a0 - DMA transfer address
; a1 - SonyVars ptr
; d0 - bit 31 = 0 -> read
; = 1 -> write
; bits 0-30 -> transfer count
; Outputs: none
; Destroys: d0
; Calls:
; Called by:
;
; Function: Sets up address, count, and direction for a DMA
; transfer.
;_______________________________________________________________________
SetUpDMAXFer move.l vSetUpDMAXfer(a1),-(sp) ; push vector
rts ; call routine
;_______________________________________________________________________
;
; Routine: jsr ClearDMAInt
; Inputs: a1 - SonyVars ptr
; Outputs: none
; Destroys: none
; Calls:
; Called by:
;
; Function: Clears floppy DMA controller interrupt.
;_______________________________________________________________________
ClearDMAInt
move.l vClearDMAInt(a1),-(sp) ; push vector
rts ; call routine
;_______________________________________________________________________
;
; Routine: jsr StopDMA
; Inputs: a1 - SonyVars ptr
; Outputs: none
; Destroys: none
; Calls:
; Called by:
;
; Function: Stops the floppy DMA channel.
;_______________________________________________________________________
StopDMA
move.l vStopDMA(a1),-(sp) ; push vector
rts ; call routine
endwith ; SWIM3Vars