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