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