mac-rom/Drivers/Sony/SonySWIM3.a
Elliot Nunn 4325cdcc78 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 09:52:23 +08:00

6201 lines
224 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