mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-28 13:52:37 +00:00
7719 lines
303 KiB
Plaintext
7719 lines
303 KiB
Plaintext
;
|
|
; File: SWIMDriver.aii
|
|
;
|
|
; Contains: IOP Based SWIM/SuperDrive Floppy Disk Driver.
|
|
;
|
|
; Written by: Gary G. Davidian Created: 01-Aug-87
|
|
;
|
|
; Copyright: © 1987-1990 by Apple Computer, Inc., all rights reserved.
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <3> 1/14/90 GGD NEEDED FOR ZONE-5: Fixing occasional problem with Raw Track Read
|
|
; getting an overrun when doing an immediate GCR track dump.
|
|
; Adjusted MFMreWriteDelay timing to give proper write splices.
|
|
; Changed Polling Task to disable the drive before calling the
|
|
; 68030 to post disk inserted events, to prevent clicking noises
|
|
; during the early booting stages.
|
|
; <2> 1/2/90 GGD NEEDED FOR ZONE-5:Adding support for Raw Track I/O to better
|
|
; support copy protection. Fixed bug in GCR formatting that wrote
|
|
; 199 bad sync patterns at the beginning of the track, but since
|
|
; it gets mostly overwritten by the last sector on the track, and
|
|
; was followed by 8 good ones, it was pretty harmless. This was
|
|
; introduced by change <1.8>. Re-organized and optimized code to
|
|
; regain some additional RAM space. Improved the performance of
|
|
; reporting GCRonMFMErr, eliminated the retries (which took a long
|
|
; time), since it is not a recoverable error.
|
|
; <2.0> 12/11/89 GGD NEEDED FOR ZONE-5 Added support for the Disk Duplicator.
|
|
; Implemented a dummy routine for the Get Raw Data track dump
|
|
; command which just returns ControlErr for now. Added some extra
|
|
; entries to the request dispatch table to allow easier patching.
|
|
; <1.9> 11/19/89 GGD NEEDED FOR ZONE-5 Changed StartPolling and implemented
|
|
; StopPolling, to more closely match how they are documented in
|
|
; the ERS. Added a prefetcher task to fill the cache with data
|
|
; that is under the heads during the last second before turning
|
|
; off the drive motor. Added TimeOut checks in DMA transfer loops
|
|
; to prevent infinate loops if DMA hangs.
|
|
; <1.8> 11/2/89 GGD NEEDED FOR ZONE-5 Added DMA support to GCR Format, MFM
|
|
; Read/Write/Format so that ADB can get more processor time while
|
|
; the floppy is in use. This feature will result in a smoother
|
|
; mouse/cursor if the user moves the mouse while the floppy is
|
|
; being used, which will boost the sales of ZONE-5's and will have
|
|
; a direct impact on profit sharing.
|
|
; <1.7> 10/10/89 GGD Reorganized some code to allow more sharing between routines and
|
|
; save space. I/O routines now look for recent ADB activity, and
|
|
; try to give ADB some more time to make the mouse smoother. Added
|
|
; seek optimization for large I/O requests to start at the end of
|
|
; the request that is closest to the current head position.
|
|
; <1.6> 9/22/89 GGD Implemented first pass at Track Cacheing and Error Recovery.
|
|
; <1.5> 7/8/89 CCH Added EASE comments to file.
|
|
; <•1.4> 6/15/89 GGD Updated to use equates for the latest rev of the IOP chip,
|
|
; re-formated tab stops in source.
|
|
; <1.3> 2/9/89 GGD Added temporary version of track buffer support to greatly
|
|
; improve Floppy performance. Also coded around SWIM problem with
|
|
; MFM writes not always getting 12 bytes of zeros for a sync
|
|
; field.
|
|
; <1.2> 2/5/89 GGD Implemented Format/Verify support. Rolled in changes to
|
|
; SWIM/SuperDrive support that were uncovered during Mac IIx
|
|
; development (new SWIM params, GCR on HD media, etc).
|
|
; <1.1> 11/16/88 GGD Fixed bug with incorrect sensing of eject button in some cases.
|
|
; <1.0> 11/9/88 CCH Adding to EASE.
|
|
;
|
|
; To Do:
|
|
;
|
|
eject
|
|
title 'IOP SWIM Driver'
|
|
; File: SWIMDriver.aii - IOP code for IOP Based SWIM driver.
|
|
;
|
|
;______________________________________________________________________________
|
|
;
|
|
; IOP Based SWIM/SuperDrive Floppy Disk Driver.
|
|
;
|
|
; written by Gary G. Davidian Created: 01-Aug-87
|
|
;
|
|
; Copyright © Apple Computer, Inc. 1987-1989
|
|
;
|
|
;______________________________________________________________________________
|
|
|
|
machine m65C02
|
|
codechk off
|
|
datachk off
|
|
longa off
|
|
longi off
|
|
print nomdir
|
|
include 'IOPDefs.aii'
|
|
include 'SWIMDefs.aii'
|
|
|
|
import ADBActivity
|
|
|
|
import GetTMPB
|
|
import StartTMPBTimer
|
|
import WaitTMPBdone
|
|
import CancelTMPBtimer
|
|
import TMPBtimeH
|
|
import TMPBtimeL
|
|
|
|
import GetBCPB
|
|
import MakeBCPBreq
|
|
import WaitBCPBdone
|
|
import BCPBcmd
|
|
import BCPBiopAddrH
|
|
import BCPBiopAddrL
|
|
import BCPBbyteCountH
|
|
import BCPBbyteCountL
|
|
import BCPBhostAddrB0
|
|
import BCPBhostAddrB1
|
|
import BCPBhostAddrB2
|
|
import BCPBhostAddrB3
|
|
import BCPBcompRel
|
|
|
|
entry InitSWIMChip
|
|
entry AdrAndSense
|
|
entry AdrAndStrb
|
|
entry GetDriveStatus
|
|
entry DiskSelect
|
|
entry FindDriveKind
|
|
entry TurnOffDrive
|
|
entry CheckStaticAttr
|
|
entry SetUpDrive
|
|
entry ReadSectorHdr
|
|
entry ReadHdrSetup
|
|
entry ReadSectorData
|
|
entry WriteSectorData
|
|
entry SkipSectorData
|
|
entry BlockToPhysAddr
|
|
entry Seek
|
|
entry ReCalibrate
|
|
entry WaitDriveReady
|
|
entry WaitMotorSettled
|
|
entry SleepLong
|
|
entry SleepShort
|
|
entry TurnOnDrive
|
|
entry LoadSwimParams
|
|
entry StartReadyTimer
|
|
entry Sense
|
|
entry Pulse
|
|
entry CreateTrackCache
|
|
entry FlushTrackCache
|
|
entry FlushIfDisabled
|
|
entry FindCacheEntry
|
|
title 'IOP SWIM Driver - Global Variables'
|
|
|
|
; Conditional assembly switches
|
|
|
|
CheckEncodings equ 0 ; don't check gcr nibbles for legal encodings in loops
|
|
UseDmaHardware equ 1 ; use the DMA Hardware where appropriate
|
|
|
|
|
|
; 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
|
|
|
|
GCRAddrMarkSize equ 3 ; size of GCR Address Mark (D5 AA 96)
|
|
GCRAddrSlipMarkSize equ 3 ; size of GCR Address Slip Mark (DE AA FF)
|
|
GCRGapSyncSize equ 6 ; size of GCR Gap Sync (FF 3F CF F3 FC FF)
|
|
GCRDataMarkSize equ 3 ; size of GCR Data Mark (D5 AA AD)
|
|
GCRDataSlipMarkSize equ 4 ; size of GCR Data Slip Mark (DE AA FF FF)
|
|
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
|
|
MFMIndexMarkSize equ 4 ; size of MFM Index Mark (C2 C2 C2 FC)
|
|
MFMIndexMarkGapSize equ 50 ; size of MFM Gap between index mark and sector 1
|
|
MFMAddrMarkSize equ 4 ; size of MFM Address Mark (A1 A1 A1 FE)
|
|
MFMAddrMarkGapSize equ 22 ; size of MFM Gap between address and data
|
|
MFMDataMarkSize equ 4 ; size of MFM Data Mark (A1 A1 A1 FB)
|
|
MFM720KDataGapSize equ 80 ; size of MFM Gap following 720K sectors
|
|
MFM1440KDataGapSize equ 101 ; size of MFM Gap following 1440K sectors
|
|
MFMFinalGapSize equ 1000 ; size of MFM Gap between last sector to index (max)
|
|
|
|
|
|
; Delay Timing / Timeout constants
|
|
|
|
PollRate equ TMPB1second/2 ; wake up and look around every 1/2 second
|
|
MotorTimeoutCount equ 5 ; motor timeout is 2.0 .. 2.5 seconds (5 polls)
|
|
|
|
ReadyPollRate equ TMPB1ms*3/4 ; check ready every 750 uSeconds
|
|
ReadyTimeoutCount equ TMPB1second\
|
|
/ReadyPollRate ; max poll time is 1 second
|
|
|
|
MotorOnDelay equ TMPB1second/20 ; Delay 50ms after motor on before checking ready
|
|
ChuckingDelay equ TMPB1second/2\
|
|
-MotorOnDelay ; Delay 500ms for proper disk chucking
|
|
MotorOnSettle equ TMPB100ms*3 ; Motor speed within 1.5 % 300ms after drive ready
|
|
MotorStopDelay equ TMPB1second/5 ; Delay 200ms after MotorOff to spin down
|
|
SeekDelay equ TMPB1ms/2 ; wait 500usec after seek before checking ready
|
|
DMAxferDelay equ 2000*TMPB1second/1000000 ; go idle for 2.0 ms during DMA xfer
|
|
GCRSectorDataDelay equ 12124*TMPB1second/1000000 ; 12.124 ms is time of data & gap portion of GCR sector
|
|
MFMSectorDataDelay equ 9472*TMPB1second/1000000 ; 9.472 ms is time of data & gap portion of MFM sector
|
|
TypicalADBDelay equ 3800*TMPB1second/1000000 ; 3.800 ms is typical time of ADB poll with 2 data bytes
|
|
|
|
SectorDelay equ TMPB10ms+TMPB1ms; 11ms is approx time of 1 sector
|
|
|
|
EjectPollRate equ TMPB100ms ; check eject done every 100 ms
|
|
EjectTimeoutCount equ TMPB100ms*15\
|
|
/EjectPollRate ; give up after 1.5 seconds
|
|
EjectSettleDelay equ TMPB1ms*15/100 ; leave drive enabled 150usec after eject
|
|
|
|
GCRWriteToRead equ 620 ; 620µs max time from write to read data valid again
|
|
|
|
AddrSearchMax equ 1500 ; search 1500 bytes looking for an address mark
|
|
BytePollMax equ 8 ; give up on a byte after 8 polls
|
|
DataSearchMax equ 48 ; search 48 bytes looking for data mark
|
|
|
|
; MFM bytes are 16µsec, which is 31.3344 clocks @1.9584MHz.
|
|
; About 204 clocks of overhead is already consumed by the routine.
|
|
; Delay loop is 5 clocks per iteration.
|
|
MFMreWriteDelay equ (((MFMAddrMarkGapSize*313344)/10000)-204)/5
|
|
|
|
GiveADBTimeMask equ %11111111 ; Consider the last 8 ADB polls for activity
|
|
|
|
|
|
; Sector Buffer / Track Cache constants
|
|
|
|
TotalBuffers equ 36 ; buffer storage for 36 sectors
|
|
MaxCacheEntries equ TotalBuffers\
|
|
/MinSectors ; Max number of tracks that fit into cache
|
|
MinCacheEntries equ TotalBuffers\
|
|
/MaxSectors ; Min number of tracks that fit into cache
|
|
|
|
|
|
; Raw I/O Buffer constants
|
|
|
|
RawTempBufferNum equ 0 ; 1 temp buffer needed to read sector data
|
|
RawDataBufferNum equ 1 ; large buffer space for raw track data
|
|
RawClockBufferNum equ (TotalBuffers-1)*8/9+RawDataBufferNum
|
|
RawByteCountMax equ (RawClockBufferNum-RawDataBufferNum)\
|
|
*BlockSize ; max number of bytes for Raw I/O
|
|
|
|
|
|
; Error handling / recovery constants
|
|
|
|
RecalsMax equ 1 ; Max Recals allowed before hard error
|
|
IOErrorsMax equ 9 ; Max I/O errors allowed before Recal
|
|
WrongSectsMax equ 64 ; Max wrong sectors allowed before Recal
|
|
|
|
|
|
seg 'PageData'
|
|
SectorBuffers record ; Sector Buffer Storage (must be Page Aligned)
|
|
MFMGapBuffer equ * ; MFM format gap buffer for DMA
|
|
TagBufStart equ * ; start of tag buffer
|
|
ds.b 4 ; padding so that a tag doesn't cross a page
|
|
TagBuffers ds.b TotalBuffers\
|
|
*TagSize ; sector tag storage
|
|
ds.b 4 ; get back to a page boundary
|
|
TagBufEnd equ * ; end of tag buffer
|
|
|
|
BufferStatus ds.b TotalBuffers ; status flags for each buffer
|
|
ReadDataValid equ %10000000 ; Valid disk data has been read into the buffer
|
|
IORequested equ %01000000 ; Buffer needs to be Read/Written for current request.
|
|
|
|
TagPtrs ds.b TotalBuffers ; offsets divided by 2 to tag buffers
|
|
|
|
DataBuffers ds.b TotalBuffers\
|
|
*BlockSize ; sector data storage
|
|
DataBufSize equ *-DataBuffers ; number of bytes in data buffers
|
|
GCRGapBuffer equ *-$500 ; GCR format gap sync buffer for DMA
|
|
endr
|
|
|
|
seg 'zdata'
|
|
SWIMVarsZ record
|
|
|
|
SWIMVarsZStart equ *
|
|
|
|
DriverActive ds.b 1 ; idle = 0, active <> 0
|
|
CurrentDrive ds.b 1 ; drive currently selected or powered up
|
|
LastActiveDrive ds.b 1 ; drive number of last successfully accessed drive
|
|
StatusMsgPtr ds.w 1 ; Pointer to message that gets drive status
|
|
TagBufPtr ds.w 1 ; pointer to 12 byte tag buffer
|
|
DataBufPtr ds.w 1 ; pointer to 512 byte data buffer
|
|
GCRCkSumA ds.b 1 ; byte A of GCR data checksum
|
|
GCRCkSumB ds.b 1 ; byte B of GCR data checksum
|
|
GCRCkSumC ds.b 1 ; byte C of GCR data checksum
|
|
|
|
WrTagBytesA ds.b 4 ; bytes 10, 7, 4, 1 of processed tag bytes
|
|
WrTagBytesB ds.b 4 ; bytes 11, 8, 5, 2 of processed tag bytes
|
|
WrTagBytesC ds.b 4 ; bytes 12, 9, 6, 3 of processed tag bytes
|
|
|
|
SectHdrFmtIndex ds.b 1 ; DiskFormat*2 (jump table index)
|
|
AdrSearchCountH ds.b 1 ; high byte of address mark search timeout
|
|
AdrSearchCountL ds.b 1 ; low byte of address mark search timeout
|
|
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
|
|
SectHdrChkSumH ds.b 1 ; high byte of Check Sum from sector header
|
|
SectHdrChkSumL ds.b 1 ; low byte of Check Sum from sector header
|
|
SectHdrHandshake ds.b 1 ; Handshake register after reading last byte
|
|
SectHdrError ds.b 1 ; Error register after reading last byte
|
|
|
|
StepCount ds.b 1 ; Head Step counter for Seek/ReCalibrate
|
|
SeekingCylinder ds.b 1 ; cylinder number that we are seeking
|
|
SeekingHead ds.b 1 ; head number that we are seeking
|
|
SeekDirection ds.b 1 ; seek direction for request, > 0 is forward, < 0 is reverse
|
|
SkipBlockToPhys ds.b 1 ; Skip first call to BlockToPhys when <> 0
|
|
|
|
LogicalBlockH ds.b 1 ; Logical Block number input to BlockToPhysAddr
|
|
LogicalBlockL ds.b 1 ; Logical Block number input to BlockToPhysAddr
|
|
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
|
|
Interleave ds.b 1 ; sector interleave factor
|
|
WritingForFmt ds.b 1 ; flag to indicate that write sector data code called from Format
|
|
SectorsNeeded ds.b 1 ; number of sectors needed on this track
|
|
BaseBufferNum ds.b 1 ; Buffer number for sector 0 of this track
|
|
SectBufferNum ds.b 1 ; Buffer number for current sector of this track
|
|
TagBlockNumL ds.b 1 ; low byte of running block number for tag writes
|
|
TagBlockNumH ds.b 1 ; high byte of running block number for tag writes
|
|
BlocksToDoH ds.b 1 ; remaining blocks to read/write (high)
|
|
BlocksToDoL ds.b 1 ; remaining blocks to read/write (low)
|
|
RecalsLeft ds.b 1 ; number of Recals remaining before hard error
|
|
IOErrorsLeft ds.b 1 ; number of I/O errors remaining before Recal
|
|
WrongSectsLeft ds.b 1 ; number of wrong sectors remaining before Recal
|
|
DMARetryStatus ds.b 1 ; > 0 if ints during DMA took more than xfer time
|
|
RawIOFmtMFM ds.b 1 ; $FF if MFM, $00 if GCR, disk format for raw I/O
|
|
|
|
SwimWriteReg ds.b 2 ; pointer to SWIM reg (usually wData)
|
|
|
|
LoopCountL ds.b 1 ; low byte of general purpose loop counter
|
|
LoopCountH ds.b 1 ; high byte of general purpose loop counter
|
|
|
|
AsyncPcH ds.b 1 ; saved high byte of PC when waiting asynchronously
|
|
AsyncPcL ds.b 1 ; saved low byte of PC when waiting asynchronously
|
|
ReadyTMPB ds.b 1 ; timer used for Drive Ready / Sleep delay processing
|
|
|
|
CacheMRUPtr ds.b 1 ; index of the Most Recently Used Cache Entry
|
|
DataBCPB ds.b 1 ; Block Copy Paramater Block for data transfers
|
|
DataBCPB2 ds.b 1 ; Block Copy Paramater Block for data transfers
|
|
PrevDataBCPB ds.b 1 ; Previous Block Copy Paramater Block for data transfers
|
|
CurDataBCPB ds.b 1 ; Current Block Copy Paramater Block for data transfers
|
|
TagBCPB ds.b 1 ; Block Copy Paramater Block for tag transfers
|
|
TagBCPB2 ds.b 1 ; Block Copy Paramater Block for tag transfers
|
|
PrevTagBCPB ds.b 1 ; Previous Block Copy Paramater Block for tag transfers
|
|
CurTagBCPB ds.b 1 ; Current Block Copy Paramater Block for tag transfers
|
|
|
|
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
|
|
|
|
PollingDriveNum ds.b 1 ; drive currently being polled
|
|
MotorTimingOut ds.b 1 ; number of polls to left before turning drive off
|
|
ADBActivityMask ds.b 1 ; mask to use to see if ADB deserves time
|
|
|
|
SectHdrInfo ds.b 5 ; buffer used in formatting, for sector header info
|
|
|
|
SWIMVarsZSize equ *-SWIMVarsZStart; number of bytes of SWIMVarsZ
|
|
endr
|
|
|
|
seg 'data'
|
|
SWIMVars record
|
|
|
|
SWIMVarsStart equ *
|
|
|
|
; drive independent variables
|
|
OpenStatus ds.b 1 ; Flag indicating Initialization status
|
|
PassThruBroken ds.b 1 ; HD20 pass through connector not working
|
|
DrivesFound ds.b 1 ; Number of valid disk drives found
|
|
CacheEnabled ds.b 1 ; disabled = 0, enabled <> 0
|
|
TagBuffHostAddr ds.b 4 ; host address of separate tag buffer
|
|
TagBuffEnabled ds.b 1 ; disabled = 0, enabled <> 0
|
|
HFSTagAddr ds.b 4 ; host address of HFS tag buffer
|
|
HFSTagAddrValid ds.b 1 ; invalid = 0, valid <> 0
|
|
PollingEnabled ds.b 1 ; disabled = 0, enabled <> 0
|
|
PollingTMPB ds.b 1 ; TMPB index of drive polling task
|
|
PollingPcH ds.b 1 ; high byte of PC when waiting for host to post event
|
|
PollingPcL ds.b 1 ; low byte of PC when waiting for host to post event
|
|
SettledTMPB ds.b 1 ; timer used for Motor speed settled ≈1.5% timing
|
|
SettleTimeL ds.b 1 ; low byte of settle time after drive ready
|
|
SettleTimeH ds.b 1 ; high byte of settle time after drive ready
|
|
ReCalPcH ds.b 1 ; high byte of PC when waiting in ReCalibrate
|
|
ReCalPcL ds.b 1 ; low byte of PC when waiting in ReCalibrate
|
|
WaitReadyCount ds.b 2 ; timeout counter for WaitDriveReady
|
|
EjectPollCount ds.b 1 ; timeout counter for SWIMEject
|
|
LastParamSet ds.b 1 ; index if last set of parameters loaded
|
|
FmtSearchIndex ds.b 1 ; index into table of formats to search
|
|
Sect0CkSum ds.b 1 ; GCR header Checksum for sector zero when formatting
|
|
PrefetchActive ds.b 1 ; Cache Prefetching Active when <> 0
|
|
RequestPending ds.b 1 ; <> 0 if request received while DriverActive <> 0
|
|
CurHeadsPerCyl ds.b 1 ; number of heads per cylinder for current disk
|
|
CurSectsPerHead ds.b 1 ; number of sectors per head for current cylinder
|
|
PrefetchCachePtr ds.b 1 ; cache index for entry being prefetched
|
|
|
|
; drive dependent variables
|
|
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)
|
|
ErrorCountH ds.b NumberOfDrives ; Number of errors (high byte)
|
|
ErrorCountL ds.b NumberOfDrives ; Number of errors (low byte)
|
|
|
|
WriteEnabledFlag equ %10000000 ; disk is not write protected
|
|
DriveFlags ds.b NumberOfDrives ; flag bits
|
|
|
|
|
|
; Track Cache variables
|
|
CacheNextMRU ds.b MaxCacheEntries ; offset to next Most Recently Used entry
|
|
CacheCylinder ds.b MaxCacheEntries ; Cylinder number of cached track
|
|
CacheHead ds.b MaxCacheEntries ; Head number of cached track
|
|
CacheBufferNum ds.b MaxCacheEntries ; First Buffer number for cached track
|
|
CacheInvalidCount ds.b MaxCacheEntries ; Number of entries that need to be read (-1=all)
|
|
|
|
SWIMVarsSize equ *-SWIMVarsStart ; number of bytes of SWIMVars
|
|
endr
|
|
title 'IOP SWIM Driver - Request Decode / Dispatching'
|
|
|
|
seg 'code'
|
|
SWIMDriver proc ; SWIM driver requests use MSG 2
|
|
entry UnKnownSWIMReq
|
|
export HandleRCVMsg2
|
|
export SWIMReqDone
|
|
|
|
entry SWIMReqDecode
|
|
entry SWIMReqStart
|
|
entry SWIMReqEnd
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
HandleRCVMsg2 lda #MessageReceived ; indicate that the message
|
|
sta RCVState ; was received
|
|
lda #$FF ; indicate driver active
|
|
tsb <DriverActive ; mark driver as running, check prior state
|
|
bne BusyReturn ; if was active (prefetching), wait until it is done.
|
|
trb PrefetchActive ; clear prefetching, see if it was active
|
|
beq @PrefetchIdle ; if idle, don't touch the timer
|
|
ldy <ReadyTMPB ; get the Ready timer TMPB index
|
|
jsr CancelTMPBtimer ; cancel prefetch sleep timer, preventing future prefetches
|
|
@PrefetchIdle stz <ADBActivityMask ; give disk priority next time
|
|
lda ReqKind ; get the request Kind
|
|
cpa #(SWIMReqEnd-SWIMReqStart)/2 ; range check it
|
|
bge UnKnownSWIMReq ; if request kind out of range
|
|
asl a ; compute the table index
|
|
tax ; load the index register
|
|
lda OpenStatus ; see if driver has been opened
|
|
jmp (SWIMReqDecode,x) ; dispatch to the routine
|
|
|
|
BusyReturn sta RequestPending ; remember to re-run the request
|
|
rts
|
|
|
|
|
|
UnKnownSWIMReq lda #paramErr ; parameter error
|
|
SWIMReqDone sta ReqErrorCode+1 ; lower byte of error code
|
|
beq ErrHDone ; if noErr
|
|
stz <LastActiveDrive ; abnormal completion, force drive change path
|
|
bmi ErrNeg ; all errors are negative, and most fit in 1 byte
|
|
lda #>gcrOnMfmErr ; except this one, which needs 2 bytes
|
|
bra ErrHDone ; store the upper byte
|
|
ErrNeg lda #-1 ; sign extend error code
|
|
ErrHDone sta ReqErrorCode ; upper byte of error code
|
|
lda #MessageCompleted ; message completed state
|
|
sta RCVState ; indicate that it is done
|
|
stz <DriverActive ; indicate that driver is no longer running
|
|
lda #MsgCompletedINT ; get the interrupt bit
|
|
sta HostControl ; interrupt the main CPU
|
|
rts ; all done
|
|
endwith
|
|
endwith
|
|
endproc
|
|
|
|
seg 'WordConsts'
|
|
SWIMReqDecode proc
|
|
entry SWIMReqStart
|
|
entry SWIMReqEnd
|
|
|
|
entry SWIMInitialize
|
|
entry SWIMShutDown
|
|
entry SWIMStartPolling
|
|
entry SWIMStopPolling
|
|
entry SWIMSetHFSTagAddr
|
|
entry SWIMDriveStatus
|
|
entry SWIMEject
|
|
entry SWIMFormat
|
|
entry SWIMFormatVerify
|
|
entry SWIMWrite
|
|
entry SWIMRead
|
|
entry SWIMReadVerify
|
|
entry SWIMCacheControl
|
|
entry SWIMTagBufferControl
|
|
entry SWIMGetIcon
|
|
entry SWIMDiskDupInfo
|
|
entry SWIMGetRawData
|
|
|
|
SWIMReqStart dc.w UnKnownSWIMReq ; $00 - reserved
|
|
dc.w SWIMInitialize ; $01 - Initialize
|
|
dc.w SWIMShutDown ; $02 - ShutDown
|
|
dc.w SWIMStartPolling ; $03 - StartPolling
|
|
dc.w SWIMStopPolling ; $04 - StopPolling
|
|
dc.w SWIMSetHFSTagAddr ; $05 - SetHFSTagAddr
|
|
dc.w SWIMDriveStatus ; $06 - DriveStatus
|
|
dc.w SWIMEject ; $07 - Eject
|
|
dc.w SWIMFormat ; $08 - Format
|
|
dc.w SWIMFormatVerify ; $09 - FormatVerify
|
|
dc.w SWIMWrite ; $0A - Write
|
|
dc.w SWIMRead ; $0B - Read
|
|
dc.w SWIMReadVerify ; $0C - ReadVerify
|
|
dc.w SWIMCacheControl ; $0D - CacheControl
|
|
dc.w SWIMTagBufferControl; $0E - TagBufferControl
|
|
dc.w SWIMGetIcon ; $0F - GetIcon
|
|
dc.w SWIMDiskDupInfo ; $10 - DiskDupInfo
|
|
dc.w SWIMGetRawData ; $11 - GetRawData
|
|
dc.w UnKnownSWIMReq ; $12 - unknown
|
|
dc.w UnKnownSWIMReq ; $13 - unknown
|
|
dc.w UnKnownSWIMReq ; $14 - unknown
|
|
dc.w UnKnownSWIMReq ; $15 - unknown
|
|
dc.w UnKnownSWIMReq ; $16 - unknown
|
|
dc.w UnKnownSWIMReq ; $17 - unknown
|
|
dc.w UnKnownSWIMReq ; $18 - unknown
|
|
SWIMReqEnd
|
|
endproc
|
|
|
|
seg 'code'
|
|
Unimplemented proc
|
|
export HandleDMA2Int
|
|
HandleDMA2Int
|
|
brk
|
|
bra Unimplemented
|
|
|
|
entry SWIMShutDown
|
|
entry SWIMGetIcon
|
|
SWIMShutDown ; unimplemented
|
|
SWIMGetIcon ; unimplemented
|
|
bra UnKnownSWIMReq
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Initialize'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMInitialize
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: InitSWIMChip, DiskSelect, FindDriveKind, TurnOffDrive
|
|
; PollingTask, GetTMPB, GetBCPB
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Initializes the IOP based SWIM Driver variables, and
|
|
; returns a list of installed drives to the Host processor.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMInitialize proc
|
|
entry PollingTask
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
eor #openErr ; check for already open
|
|
bne InitErr ; return error if already initialized
|
|
|
|
lda #BYPASS ; prepare to turn off bypass mode
|
|
trb SCCControlReg ; let IOP control the SWIM chip now
|
|
|
|
ldx #SWIMVarsZSize ; size of page zero globals space
|
|
InitVarsZloop dex ; dec loop counter / index
|
|
stz <SWIMVarsZStart,x ; zero a byte
|
|
bne InitVarsZloop ; initialize all of the bytes
|
|
|
|
ldx #SWIMVarsSize ; size of globals space
|
|
InitVarsloop dex ; dec loop counter / index
|
|
stz SWIMVarsStart,x ; zero a byte
|
|
bne InitVarsloop ; initialize all of the bytes
|
|
|
|
; Initialize the TagPtrs array to point to the corresponding TagBuffers entry
|
|
|
|
with SectorBuffers
|
|
lda #((TagBuffers-SectorBuffers)+TotalBuffers*TagSize)/2
|
|
ldx #TotalBuffers-1 ; loop count/index
|
|
@tagPtrLoop sec ; setup for subtract
|
|
sbc #TagSize/2 ; point to previous buffer
|
|
sta TagPtrs,x ; initialize the pointer
|
|
dex ; update index
|
|
bpl @tagPtrLoop ; loop for each buffer
|
|
endwith
|
|
|
|
ldx #<RCVData ; get low byte of RCV / XMT Data address
|
|
stx <StatusMsgPtr ; initialize low byte of status message pointer
|
|
|
|
ldx #<wData ; get low byte of SWIM wData reg
|
|
stx <SwimWriteReg ; initialize low byte of SWIM Write Reg pointer
|
|
ldx #>wData ; get high byte of SWIM wData reg
|
|
stx <SwimWriteReg+1 ; initialize high byte of SWIM Write Reg pointer
|
|
|
|
* stz <CurrentDrive ; initialize CurrentDrive to default invalid drive
|
|
jsr InitSWIMChip ; initialize the chip
|
|
beq InitCheckConfig ; check configuration if chip initialized
|
|
ldx #openErr ; not opened status value
|
|
stx OpenStatus ; indicate not opened yet (in case of error)
|
|
InitErr jmp SWIMReqDone ; return with error status
|
|
|
|
InitCheckConfig jsr GetTMPB ; get a TiMer Parameter Block
|
|
sty <ReadyTMPB ; timer used for Drive Ready / Sleep delay processing
|
|
jsr GetTMPB ; get a TiMer Parameter Block
|
|
sty SettledTMPB ; timer used for Motor speed settled ≈1.5% timing
|
|
jsr GetTMPB ; get a TiMer Parameter Block
|
|
sty PollingTMPB ; timer used for Motor timeout/Insert/Eject polling
|
|
jsr GetBCPB ; get a Block Copy Parameter Block
|
|
sty <DataBCPB ; Block Copy Paramater Block for data transfers
|
|
jsr GetBCPB ; get a Block Copy Parameter Block
|
|
sty <DataBCPB2 ; Block Copy Paramater Block for data transfers
|
|
jsr GetBCPB ; get a Block Copy Parameter Block
|
|
sty <TagBCPB ; Block Copy Paramater Block for tag transfers
|
|
jsr GetBCPB ; get a Block Copy Parameter Block
|
|
sty <TagBCPB2 ; Block Copy Paramater Block for tag transfers
|
|
|
|
; if the HD-20 pass through connector is not working, then there will appear
|
|
; to be an infinate number of HD-20s connected (really, the first one that
|
|
; doesn't pass-through will be continually addressed)
|
|
; stz PassThruBroken ; initialize PassThru flag, assume it works
|
|
ldx #MaxDriveNumber+5 ; a large drive number
|
|
jsr DiskSelect ; try to select it
|
|
jsr FindDriveKind ; see what kind of drive we find
|
|
cpa #HD20DriveKind ; see if it's an HD-20
|
|
bne PassThruWorks ; if its not an HD-20, then the pass thru works
|
|
dec PassThruBroken ; it was an HD-20, pass thru must be broke.
|
|
PassThruWorks
|
|
|
|
* stz DrivesFound ; no drives found yet
|
|
FloppyLoop ldx <CurrentDrive ; get the drive number
|
|
jsr DiskSelect ; attempt to select the drive
|
|
beq NextFloppy ; if drive couldn't be selected
|
|
jsr FindDriveKind ; see what kind of drive we found
|
|
cpa #SSGCRDriveKind+1 ; see if it's a floppy drive we support
|
|
blt NextFloppy ; ignore noDrive, unknown, and 400K drives
|
|
cpa #HD20DriveKind ; ignore HD20s too
|
|
beq NextFloppy ; if HD20
|
|
|
|
inc DrivesFound ; found a live one, count it
|
|
ldx DrivesFound ; get the count
|
|
sta DriveKind,x ; record the drive kind
|
|
lda <CurrentDrive ; get the physical drive number
|
|
sta PhysDriveNumber,x ; record it
|
|
|
|
NextFloppy inc <CurrentDrive ; go onto next drive
|
|
lda <CurrentDrive ; prepare to test for end
|
|
cpa #FirstHD20DriveNumber ; see if past the range of floppies
|
|
blt FloppyLoop ; loop through all floppy drive numbers
|
|
|
|
HD20Loop ldx <CurrentDrive ; get the drive number
|
|
jsr DiskSelect ; attempt to select the drive
|
|
beq NextHD20 ; if drive couldn't be selected
|
|
jsr FindDriveKind ; see what kind of drive we found
|
|
cpa #HD20DriveKind ; look for HD20s
|
|
bne NextHD20 ; if HD20
|
|
|
|
inc DrivesFound ; found a live one, count it
|
|
ldx <CurrentDrive ; get the drive number
|
|
sta DriveKind,x ; record the drive kind
|
|
txa ; get the physical drive number
|
|
sta PhysDriveNumber,x ; record it
|
|
|
|
NextHD20 inc <CurrentDrive ; go onto next drive
|
|
lda <CurrentDrive ; prepare to test for end
|
|
cpa #MaxDriveNumber+1 ; see if past the range of HD20s
|
|
blt HD20Loop ; loop through all HD20 drive numbers
|
|
|
|
jsr TurnOffDrive ; de-select all drives
|
|
|
|
ldx #28-1 ; return 28 bytes of drive kinds
|
|
DriveKindsLoop lda #noDriveKind ; assume no drive
|
|
cpx #NumberOfDrives ; see if in range of drives supported
|
|
bge ReturnDriveKind ; use default if out of range
|
|
lda DriveKind,x ; get the drive kind
|
|
ReturnDriveKind sta ReqDriveKinds,x ; return the drive kind
|
|
dex ; dec count / index
|
|
bpl DriveKindsLoop ; loop through all possible drives
|
|
|
|
lda #noErr ; return no error
|
|
* sta OpenStatus ; indicate successful Initialization (noErr)
|
|
jsr SWIMReqDone ; let the HOST know we're done
|
|
jmp PollingTask ; start the polling task
|
|
|
|
export InitSWIMDrvr
|
|
InitSWIMDrvr lda #openErr ; not opened status value
|
|
sta OpenStatus ; indicate not opened yet
|
|
rts
|
|
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Drive Status'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMDriveStatus
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, z, c
|
|
; Calls: GetDriveStatus
|
|
; Called by: SWIM Request Dispatcher, SWIMEject, SWIMFormat
|
|
;
|
|
; Function: Returns the drive / media status information for the
|
|
; drive specified in the Drive Status request message
|
|
; returns a list of installed drives to the Host processor.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMDriveStatus proc
|
|
bne StatusDone ; return openErr if SWIM Driver not initialized
|
|
lda #nsDrvErr ; return no such drive error if out of range
|
|
ldx ReqDriveNumber ; see what drive is requested
|
|
cpx #MaxDriveNumber+1 ; see if in range
|
|
bge StatusDone ; return nsDrvErr if out of range
|
|
lda #>ReqDriveStatus ; get page number of status message buffer
|
|
jsr GetDriveStatus ; get the status
|
|
lda #noErr ; indicate successful completion
|
|
StatusDone jmp SWIMReqDone ; let the HOST know we're done
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Disk Duplicator Info'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMDiskDupInfo
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, n, z
|
|
; Calls: none
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Returns the Disk Duplicator Version number, and the format byte
|
|
; for the last sector that was read.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMDiskDupInfo proc
|
|
with SWIMVarsZ
|
|
bne DupInfoDone ; return openErr if SWIM Driver not initialized
|
|
lda #>$0410 ; Current Version is 4.1
|
|
sta ReqDupVersion+0 ; upper byte of version
|
|
lda #<$0410 ; get low byte
|
|
sta ReqDupVersion+1 ; lower byte of version
|
|
lda <SectHdrFmtKind ; get the Format Kind / Block Size from sector header
|
|
sta ReqHdrFmtKind ; return the Format Kind from the last sector hdr
|
|
lda #noErr ; indicate successful completion
|
|
DupInfoDone jmp SWIMReqDone ; let the HOST know we're done
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Cache Control'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMCacheControl
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: FlushIfDisabled
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Enables / Disables the track cache.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMCacheControl proc
|
|
with SWIMVars
|
|
bne CacheError ; return openErr if SWIM Driver not initialized
|
|
jsr FlushIfDisabled ; Need to flush if going from disabled to enabled
|
|
stz CacheEnabled ; assume disabled
|
|
ldx ReqAdditionalParams+1 ; get install flag
|
|
bmi CacheDone ; disable cache if remove request
|
|
ldx ReqAdditionalParams+0 ; get enable flag
|
|
stx CacheEnabled ; update enable flag
|
|
CacheDone lda #noErr ; indicate successful completion
|
|
CacheError jmp SWIMReqDone ; let the HOST know we're done
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Set HFS Tag Addr'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMSetHFSTagAddr
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, n, z
|
|
; Calls: none
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Sets the HOST address of the HFS tag buffer area.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMSetHFSTagAddr proc
|
|
with SWIMVars
|
|
bne HFStagDone ; return openErr if SWIM Driver not initialized
|
|
stz HFSTagAddrValid ; initialize enable flag
|
|
ldx #3 ; loop counter / index
|
|
loop lda ReqRAMAddress,x ; get buffer address
|
|
sta HFSTagAddr,x ; update buffer address
|
|
tsb HFSTagAddrValid ; update enable flag
|
|
dex ; point to previous byte
|
|
bpl loop ; loop through all bytes
|
|
lda #noErr ; indicate successful completion
|
|
HFStagDone jmp SWIMReqDone ; let the HOST know we're done
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Tag Buffer Control'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMTagBufferControl
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, n, z
|
|
; Calls: none
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Enables / Disables a separate tag buffer.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMTagBufferControl proc
|
|
with SWIMVars
|
|
bne TagBufferDone ; return openErr if SWIM Driver not initialized
|
|
stz TagBuffEnabled ; initialize enable flag
|
|
ldx #3 ; loop counter / index
|
|
loop lda ReqAdditionalParams,x ; get buffer address
|
|
sta TagBuffHostAddr,x ; update buffer address
|
|
tsb TagBuffEnabled ; update enable flag
|
|
dex ; point to previous byte
|
|
bpl loop ; loop through all bytes
|
|
lda #noErr ; indicate successful completion
|
|
TagBufferDone jmp SWIMReqDone ; let the HOST know we're done
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Start Polling'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMStartPolling
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, n, z
|
|
; Calls: None
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Indicates that Disk Insert/Eject polling should be enabled.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMStartPolling proc
|
|
with SWIMVars
|
|
bne StartPollDone ; return openErr if SWIM Driver not initialized
|
|
dea ; indicate that Insert/Eject polling is enabled
|
|
sta PollingEnabled ; set enable to $FF
|
|
lda #noErr ; indicate successful completion
|
|
StartPollDone jmp SWIMReqDone ; let the HOST know we're done
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Start Polling'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMStopPolling
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, n, z
|
|
; Calls: None
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Indicates that Disk Insert/Eject polling should be disabled.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMStopPolling proc
|
|
with SWIMVars
|
|
bne StopPollDone ; return openErr if SWIM Driver not initialized
|
|
stz PollingEnabled ; indicate that Insert/Eject polling is disabled
|
|
* lda #noErr ; indicate successful completion
|
|
StopPollDone jmp SWIMReqDone ; let the HOST know we're done
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Polling Task'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp PollingTask
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: StartTMPBTimer, WaitTMPBdone, PrefetchIntoCache, TurnOffDrive,
|
|
; DiskSelect, AdrAndSense, AdrAndStrb, CheckStaticAttr,
|
|
; GetDriveStatus
|
|
; Called by: SWIMInitialize
|
|
;
|
|
; 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.
|
|
; Starts prefetching data when the motor timeout is half expired.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
PollingTask proc
|
|
entry PrefetchIntoCache
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
ldy PollingTMPB ; get the TMPB index
|
|
lda #>PollRate ; get high byte of poll rate
|
|
sta TMPBtimeH,y ; store it into the TMPB
|
|
lda #<PollRate ; get low byte of poll rate
|
|
sta TMPBtimeL,y ; store it into the TMPB
|
|
jsr StartTMPBTimer ; start the timer
|
|
ldy PollingTMPB ; get the TMPB index
|
|
jsr WaitTMPBdone ; wait for it to time out
|
|
|
|
; when the timer runs out, we will wake up and snoop around
|
|
|
|
lda <DriverActive ; see if driver is active
|
|
bne PollingTask ; skip poll if active
|
|
lda <MotorTimingOut ; see if timing out a drive motor
|
|
beq PollDrives ; not timing out, do the poll
|
|
cpa #((MotorTimeoutCount+1)/2)+1 ; see if half left
|
|
bne @noPrefetch ; start prefetching at half way point
|
|
jsr PrefetchIntoCache ; prefetch into the cache while idle
|
|
@noPrefetch dec <MotorTimingOut ; decrement the timeout counter
|
|
bne PollingTask ; exit if count not expired
|
|
|
|
jsr TurnOffDrive ; it is time to turn off the drive motor
|
|
|
|
PollDrives ldx #0 ; initial drive number
|
|
PollDriveLoop ldy DriveKind,x ; get the drive kind
|
|
beq PollNextDrive ; skip it if not installed
|
|
stx <PollingDriveNum ; save drive number
|
|
lda MediaKind,x ; see if any media in drive
|
|
beq PollDiskInPlace ; no media, check for disk inserted
|
|
jmp PollEjectSwitch ; there is a disk, see if eject pressed
|
|
|
|
PollDiskInPlace lda PhysDriveNumber,x ; get the physical drive number
|
|
tax ; setup drive number for DiskSelect
|
|
cpy #HD20DriveKind ; check for HD-20 (special case)
|
|
beq PollHD20InPlace ; if HD-20
|
|
jsr DiskSelect ; select the drive
|
|
ldx <PollingDriveNum ; restore drive number
|
|
lda #rNoDiskInPlAdr ; prepare to test for disk in place
|
|
jsr AdrAndSense ; read the status bit
|
|
bne ThisPollDone ; if still no disk in place
|
|
lda #wNoDiskInPlAdr ; make it look like the disk ejected
|
|
jsr AdrAndStrb ; to reset the eject latch
|
|
|
|
jsr CheckStaticAttr ; check the media kind and write protect
|
|
|
|
PostDiskInPlace lda #DiskInsertedXmtReq ; post the inserted event
|
|
jsr PostEvent ; send it to the host
|
|
beq EventPosted ; all done if not error
|
|
ldx <PollingDriveNum ; restore drive number
|
|
stz MediaKind,x ; indicate NoMediaKind if couldn't mount
|
|
|
|
EventPosted lda <DriverActive ; see if driver is active
|
|
bne PollingExit ; exit poll if active
|
|
ThisPollDone ldx <PollingDriveNum ; restore drive number
|
|
PollNextDrive inx ; point to next
|
|
cpx #NumberOfDrives ; compare to limit
|
|
blt PollDriveLoop ; loop through all drives
|
|
PollingExit jsr DeselectDrives ; deselect both drives
|
|
jmp PollingTask ; keep on polling
|
|
|
|
PollHD20InPlace jsr DiskSelect ; select the drive
|
|
ldx <PollingDriveNum ; restore drive number
|
|
lda #rMotorOffAdr ; prepare to test /busy
|
|
jsr AdrAndSense ; read the status bit
|
|
beq ThisPollDone ; if /busy = 0, don't post event
|
|
lda #HD20MediaKind ; indicate HD-20 media in drive
|
|
sta MediaKind,x ; update media kind
|
|
lda #HD20Format ; indicate HD-20 format
|
|
sta DiskFormat,x ; update format kind
|
|
lda DriveFlags,x ; get the flags
|
|
ora #WriteEnabledFlag ; force write enabled
|
|
sta DriveFlags,x ; update flags
|
|
bra PostDiskInPlace ; post the event
|
|
|
|
PollEjectSwitch lda DriveKind,x ; see what kind of drive we have
|
|
cpa #HD20DriveKind ; check for HD-20 (special case)
|
|
beq ThisPollDone ; if HD-20, can't eject, so don't check
|
|
cpa #SSGCRDriveKind ; check for 400K drive (special case)
|
|
beq ThisPollDone ; if 400K drive, can't eject, so don't check
|
|
|
|
lda PhysDriveNumber,x ; get the physical drive number
|
|
tax ; setup drive number for DiskSelect
|
|
jsr DiskSelect ; select the drive
|
|
lda #rEjectOnAdr ; prepare to test for eject latch set
|
|
jsr AdrAndSense ; read the status bit
|
|
beq ThisPollDone ; if latch not set (eject button hadn't been pushed)
|
|
lda #wNoDiskInPlAdr ; make it look like the disk ejected
|
|
jsr AdrAndStrb ; to reset the eject latch
|
|
lda #DiskEjectedXmtReq ; post the dismount event
|
|
jsr PostEvent ; send it to the host (ignore errors)
|
|
bra EventPosted ; keep on polling
|
|
|
|
PostEvent ldy PollingEnabled ; see if polling is enabled
|
|
beq DeselectDrives ; if not, don't post any events, return noErr
|
|
ply ; pop pcL
|
|
sty PollingPcL ; save pcL
|
|
ply ; pop pcH
|
|
sty PollingPcH ; save pcH
|
|
sta XmtKind ; store the command kind
|
|
stx XmtDriveNumber ; store the drive number
|
|
lda #>XmtDriveStatus ; get page number of status message buffer
|
|
jsr GetDriveStatus ; return the drive status
|
|
lda #NewMessageSent ; get the new message state
|
|
sta XMTState ; set the new message state
|
|
lda #NewMsgSentINT ; prepare to interrupt the host
|
|
sta HostControl ; interrupt the host
|
|
DeselectDrives lda #StartAction+WriteMode ; prepare to clear action and write mode
|
|
sta wZeroes ; clear action and write mode (prepare for motoroff)
|
|
lda #MotorOn+Drive1Enabled+Drive2Enabled ; prepare to disable the drive
|
|
sta wZeroes ; reset MotorOn and Enables, disabling all drives
|
|
rts ; return to callers caller
|
|
|
|
export HandleXmtMsg2 ; resume when Host is done
|
|
HandleXmtMsg2 lda #Idle ; Indicate that response was received
|
|
sta XMTState ; set the new message state
|
|
lda PollingPcH ; get pcH
|
|
pha ; push pcH
|
|
lda PollingPcL ; get pcL
|
|
pha ; push pcL
|
|
lda XmtErrorCode ; get the high byte of the error code
|
|
ora XmtErrorCode+1 ; or with low byte (to test for non-zero)
|
|
rts ; return to polling loop
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Eject'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMEject
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: SetUpDrive, Seek, ReCalibrate, WaitDriveReady, DiskSelect. AdrAndStrb,
|
|
; SleepLong, AdrAndSense, SleepShort, TurnOffDrive, SWIMDriveStatus
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Ejects the disk in the specified drive
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMEject proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
bne EjectError ; return if errors found
|
|
jsr SetUpDrive ; select and power up the disk drive
|
|
beq EjectSeek ; if no error in SetUp
|
|
|
|
cpa #OffLinErr ; see if there is a disk to eject
|
|
beq 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
|
|
cpa #nsDrvErr ; return no such drive error if out of range
|
|
beq EjectError ; report drive number errors
|
|
cpa #noDriveErr ; return no drive error if drive not installed
|
|
bne EjectDiskette ; if any other error, skip seek, just eject
|
|
EjectError tax ; set flags based upon error code
|
|
jmp SWIMReqDone ; let the HOST know we're done
|
|
|
|
EjectSeek ldy #LastCylinder ; position head to last cylinder before eject
|
|
jsr Seek ; to prevent major data loss if heads damage media
|
|
beq EjectDiskette ; if seek was ok, eject the disk
|
|
|
|
jsr ReCalibrate ; try to recalibrate if seek failed
|
|
bne EjectDiskette ; if couldn't recalibrate, forget about seeking
|
|
|
|
ldy #LastCylinder ; position head to last cylinder before eject
|
|
jsr Seek ; one last attempt at seeking
|
|
|
|
EjectDiskette jsr WaitDriveReady ; wait for motor speed and heads to settle
|
|
|
|
; Re-Enable the drive, in case it was disabled by an error.
|
|
ldy ReqDriveNumber ; see what drive is requested
|
|
ldx PhysDriveNumber,y ; get the new physical drive number
|
|
jsr DiskSelect ; select and enable the new drive
|
|
|
|
lda #wMotorOffAdr ; motor off drive command
|
|
jsr AdrAndStrb ; turn off the drive motor
|
|
|
|
lda #<MotorStopDelay ; low byte of delay time
|
|
ldx #>MotorStopDelay ; high byte of delay time
|
|
jsr SleepLong ; sleep until disk stops spinning
|
|
|
|
lda #wEjectOnAdr ; eject disk drive command
|
|
jsr AdrAndStrb ; start the eject process
|
|
|
|
lda #EjectTimeoutCount ; max number of polls
|
|
sta EjectPollCount ; setup polling counter
|
|
|
|
EjectPollLoop lda #<EjectPollRate ; low byte of delay time
|
|
ldx #>EjectPollRate ; high byte of delay time
|
|
jsr SleepLong ; sleep waiting for disk to eject
|
|
|
|
lda #rNoDiskInPlAdr ; prepare to see if disk is out
|
|
jsr AdrAndSense ; read the drive status bit
|
|
bne EjectOut ; disk is out, wait for things to settle
|
|
|
|
dec EjectPollCount ; decrement poll timeout counter
|
|
bne EjectPollLoop ; loop if more polls left
|
|
|
|
EjectOut lda #<EjectSettleDelay ; delay before killing enable
|
|
jsr SleepShort ; wait the specified amount of time
|
|
|
|
ldx <CurrentDrive ; get the drive number
|
|
stz MediaKind,x ; indicate NoMediaKind
|
|
stz DiskFormat,x ; indicate uncheckedFormat
|
|
stz DriveFlags,x ; clear all flags
|
|
stz <LastActiveDrive ; indicate drive state is unknown
|
|
|
|
jsr TurnOffDrive ; disable the drive
|
|
lda #noErr ; indicate success
|
|
jmp SWIMDriveStatus ; finish up by returning the drive status
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Read / ReadVerify'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMRead / SWIMReadVerify
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: SetUpDrive, ReadWriteSetUp, WaitBCPBdone, SetupTrackInfo,
|
|
; UpdateTagInfo, UpdateDataInfo, UpdateBlockInfo, WaitDriveReady,
|
|
; Seek, ReadSectorHdr, ReadSectorData, MakeBCPBreq, ReCalibrate,
|
|
; SkipSectorData
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Reads data from the specified drive, at the specified
|
|
; block number, and transfers or compares the data to the host.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMRead proc
|
|
entry SWIMReadVerify
|
|
entry ReadWriteSetUp
|
|
entry SetupTrackInfo
|
|
entry UpdateTagInfo
|
|
entry UpdateDataInfo
|
|
entry UpdateBlockInfo
|
|
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
with SectorBuffers
|
|
|
|
SWIMReadVerify jsr SetUpDrive ; select and power up the disk drive
|
|
bne ReadError ; if error powering up
|
|
|
|
ldx <CacheMRUPtr ; get index of MRU entry
|
|
lda CacheNextMRU,x ; get index of next (if any)
|
|
asl a ; single buffer if next entry is nil
|
|
jsr ReadWriteSetUp ; get the paramaters, and setup data structures
|
|
beq ReadTrackLoop ; if no errors, start reading
|
|
|
|
ReadDone ldy <PrevTagBCPB ; get the prior tag copy request
|
|
bmi @TagXferDone ; skip it if not enabled
|
|
jsr WaitBCPBdone ; wait for buffer to be free before returning
|
|
ldy <CurTagBCPB ; get the current tag copy request
|
|
jsr WaitBCPBdone ; wait for buffer to be free before returning
|
|
@TagXferDone
|
|
ldy <PrevDataBCPB ; get the prior data copy request
|
|
jsr WaitBCPBdone ; wait for buffer to be free before returning
|
|
ldx BCPBcompRel,y ; see if it compared ok
|
|
beq @prevOK ; of so, leave error code alone
|
|
lda #dataVerErr ; indicate compare failed
|
|
@prevOK ldy <CurDataBCPB ; get the current data copy request
|
|
jsr WaitBCPBdone ; wait for buffer to be free before returning
|
|
ldx BCPBcompRel,y ; see if it compared ok
|
|
beq @curOK ; of so, leave error code alone
|
|
lda #dataVerErr ; indicate compare failed
|
|
@curOK tax ; set flags based on error code
|
|
ReadError jmp SWIMReqDone ; return the error
|
|
|
|
|
|
|
|
; Read the data looping through each track of the request
|
|
|
|
ReadTrackLoop jsr SetupTrackInfo ; find the track address and cache buffers
|
|
bcs ReadDone ; if none left, or error, request is complete
|
|
|
|
|
|
; Update the TAG data transfer addresses and counts
|
|
|
|
ldy <PrevTagBCPB ; get the prior tag copy request
|
|
bmi @skipTagXfer ; if not enabled, skip it to save time
|
|
jsr WaitBCPBdone ; wait for buffer to be free before re-using it
|
|
jsr UpdateTagInfo ; update the TAG data transfer addresses and counts
|
|
@skipTagXfer
|
|
|
|
|
|
; Update the DATA data transfer addresses and counts
|
|
|
|
ldy <PrevDataBCPB ; get the prior data copy request
|
|
jsr WaitBCPBdone ; wait for buffer to be free before re-using it
|
|
ldx BCPBcompRel,y ; get the compare relation (0 means equal)
|
|
bne ReadDone ; return with error if compared not equal
|
|
jsr UpdateDataInfo ; update the DATA data transfer addresses and counts
|
|
|
|
|
|
; Update the DISK block addresses and counts
|
|
|
|
jsr UpdateBlockInfo ; update the DISK block addresses and counts
|
|
|
|
|
|
; Update the Buffer Status for each sector and check for cache hits
|
|
|
|
ldy <SectorsNeeded ; setup loop counter
|
|
ldx <SectBufferNum ; starting buffer number
|
|
@sectorLoop lda BufferStatus,x ; get the status for this sector buffer
|
|
assert ReadDataValid=$80 ; sign bit is valid bit
|
|
bmi @cacheHit ; if cache hit, no need to read it in
|
|
@NeedsRead ora #IORequested ; indicate the Read Request
|
|
sta BufferStatus,x ; update the buffer status
|
|
bra @nextSector ; go on to the next sector
|
|
|
|
@cacheHit ; x = last buffer number for tag copy at ReadXfer
|
|
dec <SectorsNeeded ; since it is a hit, we don't need to read it
|
|
beq ReadXfer ; if all sectors hit in the cache, skip the read
|
|
@nextSector inx ; update buffer number
|
|
dey ; decrement loop count
|
|
bne @sectorLoop ; loop through all sectors of the request
|
|
|
|
; Seek to the requested cylinder
|
|
|
|
lda #RecalsMax ; Max Recals allowed before hard error
|
|
sta <RecalsLeft ; number of Recals remaining before hard error
|
|
|
|
SeekToCyl jsr WaitDriveReady ; wait for motor speed and heads to settle
|
|
bne RecalAndRetry ; if error powering up
|
|
lda <PhysHead ; get the head we want
|
|
sta <SeekingHead ; pass it to ReadSectorHdr
|
|
ldy <PhysCylinder ; get the cylinder number
|
|
jsr Seek ; Seek to the requested cylinder
|
|
lda #IOErrorsMax ; Max I/O errors allowed before Recal
|
|
sta <IOErrorsLeft ; number of I/O errors remaining before Recal
|
|
jsr WaitDriveReady ; wait for the seek to complete
|
|
bne RecalAndRetry ; if error seeking, try to recal
|
|
|
|
; Read the sectors requested for this track
|
|
|
|
ReadHeader lda #WrongSectsMax ; successful read, re-init Wrong Sector Count
|
|
sta <WrongSectsLeft ; number of wrong sectors remaining before Recal
|
|
ReadAfterSkip jsr ReadSectorHdr ; read the next sector header that comes along
|
|
bcs ReadAddrError ; handle sector address errors
|
|
|
|
assert ReadDataValid=$80 ; sign bit is valid bit
|
|
assert IORequested=$40 ; overflow bit is requested bit
|
|
bvc ReadIntoCache ; if not requested, just fill the cache
|
|
|
|
jsr ReadSectorData ; read the data into IOP memory
|
|
bne ReadDataError ; if I/O error, handle sector data errors
|
|
|
|
ldx <CacheMRUPtr ; get index to current cache entry
|
|
dec CacheInvalidCount,x ; decrement the invalid entry count
|
|
|
|
ldx <SectBufferNum ; get the buffer number for this sector
|
|
assert (IORequested<<1)=ReadDataValid
|
|
asl BufferStatus,x ; set ReadDataValid, clear IORequested
|
|
|
|
dec <SectorsNeeded ; account for this sector
|
|
bne ReadHeader ; if more to go, keep reading
|
|
|
|
; Transfer the data for this track to the Host buffer
|
|
|
|
ReadXfer ; x = the last buffer number transferred
|
|
lda TagPtrs,x ; get the tag buffer pointer for this sector
|
|
asl a ; unpack 9th bit into carry
|
|
sta <TagBufPtr ; setup low byte of tag pointer
|
|
lda #0 ; setup to add in tag addr carry
|
|
adc #>TagBuffers ; add in starting page of tag buffers
|
|
sta <TagBufPtr+1 ; setup page number
|
|
ldy #TagSize-1 ; index / loop counter
|
|
@TagCopyLoop lda (<TagBufPtr),y ; get tag byte from tag buffer
|
|
sta ReqMfsTagData,y ; return the last tag read in the message
|
|
dey ; update index / count
|
|
bpl @TagCopyLoop ; loop through all of the bytes
|
|
|
|
ldy <CurDataBCPB ; the the BCPB index
|
|
jsr MakeBCPBreq ; issue the copy request
|
|
|
|
ldy <CurTagBCPB ; the the BCPB index
|
|
bmi @TagXferDone ; skip it if not enabled
|
|
jsr MakeBCPBreq ; issue the copy request
|
|
@TagXferDone jmp ReadTrackLoop ; transfer the data, continue with the next track
|
|
|
|
|
|
; Load the cache with sectors that were not part of the request
|
|
|
|
ReadIntoCache bmi SkipSector ; if already in cache, skip it
|
|
jsr ReadSectorData ; read the data into the cache buffer
|
|
bne IgnoreData ; if error, don't validate it, just skip it
|
|
|
|
ldx <CacheMRUPtr ; get index to current cache entry
|
|
dec CacheInvalidCount,x ; decrement the invalid entry count
|
|
|
|
ldx <SectBufferNum ; get the buffer number for this sector
|
|
lda #ReadDataValid ; mark the cache entry valid
|
|
sta BufferStatus,x ; set ReadDataValid, clear IORequested
|
|
|
|
IgnoreData dec <WrongSectsLeft ; adjust count of sectors searched
|
|
bpl ReadAfterSkip ; if count not exhausted, keep on reading
|
|
lda #sectNFErr ; assume sector not found
|
|
RecalAndRetry dec <RecalsLeft ; see if RecalsMax exceeded
|
|
bmi ToReadDone ; if too many retrys, die with hard error
|
|
jsr ReCalibrate ; move heads to track zero, init the drive
|
|
beq SeekToCyl ; if Recal OK, retry seek
|
|
ToReadDone jmp ReadDone ; if Recal failed, die with hard error
|
|
|
|
|
|
; Error handling and recovery
|
|
|
|
ReadAddrError beq SkipSector ; if sect num out of range, just ignore it
|
|
cpa #SeekErr ; see if it was a seek error
|
|
beq RecalAndRetry ; if so, recal, and then retry
|
|
DataError ldx <CurrentDrive ; get current drive number
|
|
inc ErrorCountL,x ; update the soft error count for this drive
|
|
bne @ErrorCountDone ; if no carry, skip high byte
|
|
inc ErrorCountH,x ; update the high byte of the error count
|
|
@ErrorCountDone dec <IOErrorsLeft ; number of I/O errors remaining before Recal
|
|
bmi RecalAndRetry ; if too many I/O errors, recal and try again
|
|
bra ReadHeader ; if more to go, keep reading
|
|
|
|
ReadDataError cpa <DMARetryStatus ; see if it was a DMA timeout error
|
|
beq IgnoreData ; if so, just re-try it without ints during DMA
|
|
cpa #<gcrOnMfmErr ; see if GCR data on High Density Disk
|
|
bne DataError ; if real I/O error, handle sector data errors
|
|
bra ToReadDone ; if GCR on MFM, don't waste time retrying
|
|
|
|
SkipSector jsr SkipSectorData ; give ADB some time
|
|
bra IgnoreData ; ignore this sector
|
|
endwith
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Write'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMWrite
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: SetUpDrive, ReadWriteSetUp, SetupTrackInfo,
|
|
; UpdateTagInfo, UpdateDataInfo, UpdateBlockInfo, MakeBCPBreq,
|
|
; WaitDriveReady, WaitMotorSettled, Seek, WaitBCPBdone, ReadSectorHdr,
|
|
; WriteSectorData, ReadSectorData, ReCalibrate, SkipSectorData
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Write data to the specified drive, at the specified
|
|
; block number, and transfers data from the host.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMWrite proc
|
|
entry ReadWriteSetUp
|
|
entry SetupTrackInfo
|
|
entry UpdateTagInfo
|
|
entry UpdateDataInfo
|
|
entry UpdateBlockInfo
|
|
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
with SectorBuffers
|
|
stz <WritingForFmt ; we are not writing for format
|
|
jsr SetUpDrive ; select and power up the disk drive
|
|
bne WriteError ; if error powering up
|
|
|
|
ldx <CurrentDrive ; get drive number
|
|
lda #WriteEnabledFlag ; prepare to test write enable
|
|
bit DriveFlags,x ; test the enable bit
|
|
beq WriteProtected ; if write protected, return the error
|
|
sec ; single buffer only
|
|
jsr ReadWriteSetUp ; get the paramaters, and setup data structures
|
|
beq WriteTrackLoop ; if no errors, start the write request
|
|
|
|
WriteDone tax ; set flags based on error code
|
|
WriteError jmp SWIMReqDone ; return the error
|
|
WriteProtected lda #wPrErr ; indicate write protected disk
|
|
bra WriteError ; return the error
|
|
|
|
|
|
|
|
; Write the data looping through each track of the request
|
|
|
|
WriteTrackLoop jsr SetupTrackInfo ; find the track address and cache buffers
|
|
bcs WriteDone ; if none left, or error, request is complete
|
|
|
|
|
|
; Update the DATA data transfer addresses and counts
|
|
|
|
ldy <PrevDataBCPB ; get the prior data copy request
|
|
jsr UpdateDataInfo ; update the DATA data transfer addresses and counts
|
|
jsr MakeBCPBreq ; issue the copy request
|
|
|
|
|
|
; Update the DISK block addresses and counts
|
|
|
|
jsr UpdateBlockInfo ; update the DISK block addresses and counts
|
|
|
|
|
|
; Update the Buffer Status for each sector
|
|
|
|
lda <SectorsNeeded ; setup loop counter
|
|
sta <LoopCountL ; initialize loop counter
|
|
ldx <SectBufferNum ; starting buffer number
|
|
bra @loopStart ; skip the tag block increment the first time
|
|
@sectorLoop inc ReqMfsTagData+7 ; increment tag block number (low byte)
|
|
bne @loopStart ; if no carry out
|
|
inc ReqMfsTagData+6 ; increment tag block number (high byte)
|
|
@loopStart lda BufferStatus,x ; get the status for this sector buffer
|
|
assert ReadDataValid=$80 ; sign bit is valid bit
|
|
bpl @setupTag ; if already invalid, don't need to invalidate
|
|
txa ; preserve x
|
|
ldx <CacheMRUPtr ; get index to current cache entry
|
|
inc CacheInvalidCount,x ; increment the invalid entry count
|
|
tax ; restore x
|
|
@setupTag lda TagPtrs,x ; get the tag buffer pointer for this sector
|
|
asl a ; unpack 9th bit into carry
|
|
sta <TagBufPtr ; remember it for the tag copy
|
|
lda #0 ; setup to add in tag addr carry
|
|
adc #>TagBuffers ; add in starting page of tag buffers
|
|
sta <TagBufPtr+1 ; setup page number
|
|
ldy #TagSize-1 ; index / loop counter
|
|
@TagCopyLoop lda ReqMfsTagData,y ; setup the tag from the message request
|
|
sta (<TagBufPtr),y ; store the tags into the tag buffer
|
|
dey ; update index / count
|
|
bpl @TagCopyLoop ; loop through all of the bytes
|
|
lda #IORequested ; indicate the Write Request, clear valid flag
|
|
sta BufferStatus,x ; update the buffer status
|
|
@nextSector inx ; update buffer number
|
|
dec <LoopCountL ; decrement loop count
|
|
bne @sectorLoop ; loop through all sectors of the request
|
|
|
|
|
|
; Update the TAG data transfer addresses and counts
|
|
|
|
ldy <PrevTagBCPB ; get the prior tag copy request
|
|
bmi @skipTagXfer ; if not enabled, skip it to save time
|
|
jsr UpdateTagInfo ; update the TAG data transfer addresses and counts
|
|
jsr MakeBCPBreq ; issue the copy request
|
|
@skipTagXfer
|
|
|
|
|
|
; Seek to the requested cylinder
|
|
|
|
lda #RecalsMax ; Max Recals allowed before hard error
|
|
sta <RecalsLeft ; number of Recals remaining before hard error
|
|
|
|
SeekToCyl jsr WaitDriveReady ; wait for motor speed and heads to settle
|
|
bne RecalAndRetry ; if error powering up
|
|
jsr WaitMotorSettled ; wait for motor speed to be in 1.5% tolerance
|
|
lda <PhysHead ; get the head we want
|
|
sta <SeekingHead ; pass it to WriteSectorHdr
|
|
ldy <PhysCylinder ; get the cylinder number
|
|
jsr Seek ; Seek to the requested cylinder
|
|
|
|
; Transfer the data for this track from the Host buffer
|
|
|
|
ldy <CurDataBCPB ; get the DATA BCPB index
|
|
jsr WaitBCPBdone ; wait for the data to arrive
|
|
ldy <CurTagBCPB ; get the TAG BCPB index
|
|
bmi @TagXferDone ; skip it if not enabled
|
|
jsr WaitBCPBdone ; wait for the data to arrive
|
|
@TagXferDone
|
|
lda #IOErrorsMax ; Max I/O errors allowed before Recal
|
|
sta <IOErrorsLeft ; number of I/O errors remaining before Recal
|
|
jsr WaitDriveReady ; wait for the seek to complete
|
|
bne RecalAndRetry ; if error seeking, try to recal
|
|
|
|
; Write the sectors requested for this track
|
|
|
|
ReadHeader lda #WrongSectsMax ; successful write, re-init Wrong Sector Count
|
|
sta <WrongSectsLeft ; number of wrong sectors remaining before Recal
|
|
ReadAfterSkip jsr ReadSectorHdr ; read the next sector header that comes along
|
|
bcs ReadAddrError ; handle sector address errors
|
|
|
|
assert ReadDataValid=$80 ; sign bit is valid bit
|
|
assert IORequested=$40 ; overflow bit is requested bit
|
|
bvc ReadIntoCache ; if not requested, just fill the cache
|
|
|
|
jsr WriteSectorData ; write the data from IOP memory
|
|
beq @writeOK ; if successfull write, update sector info
|
|
|
|
cpa <DMARetryStatus ; see if it was a DMA timeout error
|
|
beq IgnoreData ; if so, just re-try it without ints during DMA
|
|
bra WriteDataError ; if real I/O error, handle sector data errors
|
|
|
|
@writeOK ldx <SectBufferNum ; get the buffer number for this sector
|
|
stz BufferStatus,x ; clear ReadDataValid, clear IORequested
|
|
|
|
dec <SectorsNeeded ; account for this sector
|
|
bne ReadHeader ; if more to go, keep writing
|
|
|
|
; If there are no valid entries left on this track, we will invalidate the entire
|
|
; track so that it will be reused soonest even though it is MRU.
|
|
ldx <CacheMRUPtr ; get index to current cache entry
|
|
lda CacheInvalidCount,x ; get the invalid entry count
|
|
cpa <PhysSectsPerHead ; see if all sectors are invalid
|
|
blt @validDone ; if some sectors valid, leave the track valid
|
|
lda #-1 ; if all invalid, invalidate the entire track
|
|
sta CacheInvalidCount,x ; decrement the invalid entry count
|
|
@validDone jmp WriteTrackLoop ; transfer the data, continue with the next track
|
|
|
|
|
|
; Load the cache with sectors that were not part of the request
|
|
|
|
ReadIntoCache bmi SkipSector ; if already in cache, skip it
|
|
ldy #GiveADBTimeMask ; see if ADB has been active recently
|
|
and <ADBActivity ; check against the ADB activity history
|
|
bne SkipSector ; if so, don't fill the cache, give ADB some time instead
|
|
jsr ReadSectorData ; read the data into the cache buffer
|
|
bne IgnoreData ; if error, don't validate it, just skip it
|
|
|
|
ldx <CacheMRUPtr ; get index to current cache entry
|
|
dec CacheInvalidCount,x ; decrement the invalid entry count
|
|
|
|
ldx <SectBufferNum ; get the buffer number for this sector
|
|
lda #ReadDataValid ; mark the cache entry valid
|
|
sta BufferStatus,x ; set ReadDataValid, clear IORequested
|
|
|
|
IgnoreData dec <WrongSectsLeft ; adjust count of sectors searched
|
|
bpl ReadAfterSkip ; if count not exhausted, keep on reading
|
|
lda #sectNFErr ; assume sector not found
|
|
RecalAndRetry dec <RecalsLeft ; see if RecalsMax exceeded
|
|
bmi ToWriteDone ; if too many retrys, die with hard error
|
|
jsr ReCalibrate ; move heads to track zero, init the drive
|
|
beq SeekToCyl ; if Recal OK, retry seek
|
|
ToWriteDone jmp WriteDone ; if Recal failed, die with hard error
|
|
|
|
|
|
; Error handling and recovery
|
|
|
|
ReadAddrError beq SkipSector ; if sect num out of range, just ignore it
|
|
cpa #SeekErr ; see if it was a seek error
|
|
beq RecalAndRetry ; if so, recal, and then retry
|
|
WriteDataError ldx <CurrentDrive ; get current drive number
|
|
inc ErrorCountL,x ; update the soft error count for this drive
|
|
bne @ErrorCountDone ; if no carry, skip high byte
|
|
inc ErrorCountH,x ; update the high byte of the error count
|
|
@ErrorCountDone
|
|
; if there was a problem reading a header, or writing data, recalibrate and retry
|
|
; first, to reduce the amount of data that will be written if the head is slightly
|
|
; off track. If all of the recals have been used, then we will stay on track and
|
|
; use the remaining retries on this track.
|
|
ldx <RecalsLeft ; see if RecalsMax reached
|
|
bne RecalAndRetry ; if not, recal, and then retry
|
|
dec <IOErrorsLeft ; number of I/O errors remaining before Recal
|
|
bmi RecalAndRetry ; if too many I/O errors, recal and try again
|
|
bra ReadHeader ; if more to go, keep reading
|
|
|
|
SkipSector jsr SkipSectorData ; give ADB some time
|
|
bra IgnoreData ; ignore this sector
|
|
endwith
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Read / Write Setup'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr ReadWriteSetUp
|
|
; Inputs: c - Cleared if double buffering allowed
|
|
; ReqKind - (WriteReq,ReadReq,ReadVerifyReq)
|
|
; Outputs: A, n, z - error status code
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: SetupBCPB, BlockToPhysAddr
|
|
; Called by: SWIMRead, SWIMReadVerify, SWIMWrite
|
|
;
|
|
; Function: Utility routines used by Read, ReadVerify, and Write
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
ReadWriteSetUp proc
|
|
entry TagCopyCmd
|
|
entry DataCopyCmd
|
|
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
with SectorBuffers
|
|
|
|
; setup the fields in the TagBCPB
|
|
|
|
ldx #-1 ; assume no tag buffer
|
|
ldy #-1 ; assume no tag buffer
|
|
lda TagBuffEnabled ; see if we need tag buffer copying
|
|
beq @skipTagSetup ; if not, skip it to save time
|
|
ldx ReqKind ; get the I/O command
|
|
lda TagCopyCmd-WriteReq,x ; get the copy command
|
|
ldy <TagBCPB ; get the BCPB index
|
|
sta BCPBcmd,y ; setup the block copy operation
|
|
tya ; assume single buffer
|
|
tax ; both prev and current are the same
|
|
bcs @singleTagBuff ; if only 1 cache entry, don't double buffer reads
|
|
ldx <TagBCPB2 ; use second buffer if enough cache entries
|
|
@singleTagBuff
|
|
lda TagBuffHostAddr+0 ; setup the initial HOST RAM Address
|
|
sta BCPBhostAddrB0,y
|
|
lda TagBuffHostAddr+1
|
|
sta BCPBhostAddrB1,y
|
|
lda TagBuffHostAddr+2
|
|
sta BCPBhostAddrB2,y
|
|
lda TagBuffHostAddr+3
|
|
sta BCPBhostAddrB3,y
|
|
|
|
lda #0 ; initialize count to zero
|
|
sta BCPBbyteCountH,y
|
|
sta BCPBbyteCountL,y
|
|
@skipTagSetup
|
|
sty <CurTagBCPB ; setup current
|
|
stx <PrevTagBCPB ; setup previous
|
|
|
|
; setup the fields in the DataBCPB
|
|
|
|
ldy <DataBCPB ; get the BCPB index
|
|
sty <CurDataBCPB ; setup current
|
|
tya ; assume single buffer
|
|
bcs @singleDataBuff ; if only 1 entry, don't double buffer reads
|
|
lda <DataBCPB2 ; get the second BCPB index
|
|
@singleDataBuff sta <PrevDataBCPB ; setup previous
|
|
tax ; get prev buffer
|
|
stz BCPBcompRel,x ; assume verify good for previous buffer
|
|
|
|
ldx ReqKind ; get the request kind
|
|
lda DataCopyCmd-WriteReq,x ; get the copy command
|
|
|
|
ldx #ReqRAMAddress-RCVData
|
|
jsr SetupBCPB ; setup the BCPB fields, use ReqRAMAddress
|
|
|
|
; get remaining parameters from the request message
|
|
|
|
lda ReqBlockNumber+2 ; use low word of long block number
|
|
sta <LogicalBlockH
|
|
lda ReqBlockNumber+3
|
|
sta <LogicalBlockL
|
|
|
|
lda ReqBlockCount+2 ; use low word of long block count
|
|
sta <BlocksToDoH
|
|
lda ReqBlockCount+3
|
|
sta <BlocksToDoL
|
|
|
|
lda ReqMfsTagData+6 ; get high byte of tag block number
|
|
sta <TagBlockNumH ; initialize the count
|
|
lda ReqMfsTagData+7 ; get low byte of tag block number
|
|
sta <TagBlockNumL ; initialize the count
|
|
|
|
; check high word of block number and block count
|
|
|
|
lda ReqBlockNumber+0
|
|
ora ReqBlockNumber+1
|
|
ora ReqBlockCount+0
|
|
ora ReqBlockCount+1
|
|
beq SetupDirection ; if block number / block count in range
|
|
lda #paramErr ; Parameter Error if out of range
|
|
SetupError rts ; return with result
|
|
|
|
; Determine the Seek Direction, and adjust addresses if needed
|
|
|
|
SetupDirection
|
|
lda #1 ; indicate incrementing track order
|
|
sta <SeekDirection ; setup the default direction
|
|
sta <SkipBlockToPhys ; skip first call to BlockToPhys
|
|
jsr BlockToPhysAddr ; translate the starting block number
|
|
bne SetupError ; die on errors
|
|
|
|
; See if requested data does not cross cylinders (no seeks within the request)
|
|
|
|
lda <PhysSectsPerCyl ; Number of Sectors per cylinder
|
|
sec ; setup for subtraction
|
|
sbc <PhysCylSector ; compute sectors remaining before seek needed
|
|
cpa <BlocksToDoL ; compare to number of sectors requested
|
|
lda #0 ; setup to compare high byte too
|
|
sbc <BlocksToDoH ; see if sectors till end of cyl >= BlocksToDo
|
|
bge @setupDone ; if request fits on 1 cylinder, seek direction doesn't matter
|
|
|
|
; See if we would need to seek ahead to reach the first cylinder of the request
|
|
|
|
ldx <CurrentDrive ; get the active drive number
|
|
lda <PhysCylinder ; get desired cylinder
|
|
sec ; setup for subtraction
|
|
sbc CurrentCylinder,x ; step count := desired - current
|
|
bge @setupDone ; if seeking ahead, use incrementing track order
|
|
ldy CylinderValid,x ; see if cylinder is valid
|
|
bne @setupDone ; if invalid, just use default
|
|
|
|
; See which end of the request the head is closest to
|
|
|
|
asl a ; negative step count * 2 (also set carry for subtract)
|
|
sbc <PhysCylinder ; compute negative end cyl where seek ahead is a win
|
|
sta <LoopCountL ; save the negative cylinder number
|
|
ldx <BlocksToDoH ; get high byte
|
|
lda <BlocksToDoL ; get low byte
|
|
bne @noBlockBorrow ; if non-zero, decrement won't borrow
|
|
dex ; if borrow, decrement upper byte
|
|
@noBlockBorrow dea ; compute BlocksToDo - 1
|
|
clc ; clear carry for addition
|
|
adc <LogicalBlockL ; LogicalBlock := LogicalBlock + BlocksToDo - 1
|
|
sta <LogicalBlockL
|
|
txa ; get BlocksToDoH
|
|
adc <LogicalBlockH
|
|
sta <LogicalBlockH
|
|
jsr BlockToPhysAddr ; translate the ending block number
|
|
bne SetupError ; die on errors
|
|
lda <LoopCountL ; get the negative cylinder number
|
|
clc ; setup carry for addition
|
|
adc <PhysCylinder ; see if ending cylinder is closer to head
|
|
blt @seekReverse ; if so, start at end, use decrementing track order
|
|
|
|
; Head is closer to beginning of request
|
|
|
|
stz <SkipBlockToPhys ; Don't skip first call to BlockToPhys
|
|
lda ReqBlockNumber+2 ; restore starting logical block number
|
|
sta <LogicalBlockH
|
|
lda ReqBlockNumber+3
|
|
sta <LogicalBlockL
|
|
|
|
@setupDone lda #noErr ; indicate success
|
|
rts ; return with result
|
|
|
|
|
|
; Head is closer to end of request
|
|
|
|
@seekReverse lda #-1 ; indicate decrementing track order
|
|
sta <SeekDirection ; setup the reverse direction
|
|
|
|
; correct the address in the TagBCPB
|
|
|
|
ldx <CurTagBCPB ; get current tag BCPB, see if tags enabled
|
|
bmi @skipTagSetup ; if not enabled, skip it to save time
|
|
ldy #TagSize ; multiplier/loop counter
|
|
@tagMultLoop clc ; clear carry for addition
|
|
lda BCPBhostAddrB3,x ; get low byte
|
|
adc <BlocksToDoL ; add in count
|
|
sta BCPBhostAddrB3,x ; update low byte
|
|
lda BCPBhostAddrB2,x ; get next byte
|
|
adc <BlocksToDoH ; add in count
|
|
sta BCPBhostAddrB2,x ; update next byte
|
|
bcc @noTagCarry ; see if carry out
|
|
inc BCPBhostAddrB1,x ; propagate the carry
|
|
bne @noTagCarry ; see if carry out
|
|
inc BCPBhostAddrB0,x ; propagate the carry
|
|
@noTagCarry dey ; update loop counter
|
|
bne @tagMultLoop ; loop until multiply is done
|
|
@skipTagSetup
|
|
|
|
|
|
; correct the address in the DataBCPB
|
|
|
|
ldx <CurDataBCPB ; get current data BCPB
|
|
ldy #BlockSize/256 ; multiplier/loop counter
|
|
@dataMultLoop clc ; clear carry for addition
|
|
lda BCPBhostAddrB2,x ; get low byte
|
|
adc <BlocksToDoL ; add in count
|
|
sta BCPBhostAddrB2,x ; update low byte
|
|
lda BCPBhostAddrB1,x ; get next byte
|
|
adc <BlocksToDoH ; add in count
|
|
sta BCPBhostAddrB1,x ; update next byte
|
|
bcc @noDataCarry ; see if carry out
|
|
inc BCPBhostAddrB0,x ; propagate the carry
|
|
@noDataCarry dey ; update loop counter
|
|
bne @dataMultLoop ; loop until multiply is done
|
|
|
|
|
|
; correct the Tag Block Numbers
|
|
|
|
clc ; clear carry for addition
|
|
lda <BlocksToDoL ; get low byte
|
|
adc <TagBlockNumL ; TagBlockNum := TagBlockNum + BlocksToDo
|
|
sta <TagBlockNumL
|
|
lda <BlocksToDoH ; get high byte
|
|
adc <TagBlockNumH
|
|
sta <TagBlockNumH
|
|
bra @setupDone ; indicate success and return
|
|
title 'IOP SWIM Driver - Copy Host Address'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr SetupBCPB
|
|
; Inputs: A - BCPB command value
|
|
; X - Offset into RVCData of address to copy
|
|
; Y - BCPB index of destination BCPB
|
|
; Outputs: Y - set to $FF if HostAddr was zero, otherwise remains unchanged
|
|
; Destroys: A, Y, n, v, z, c, LoopCountL
|
|
; Calls: none
|
|
; Called by: ReadWriteSetUp, SWIMFormat, SWIMGetRawData
|
|
;
|
|
; Function: BCPBcmd[y] := A
|
|
; BCPBbyteCount[y] := $0000
|
|
; BCPBcompRel[y] := $00
|
|
; BCPBhostAddr[y] := RVCData[x]
|
|
; if RVCData[x] = 0 then y := $FF
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
entry SetupBCPB
|
|
SetupBCPB sta BCPBcmd,y ; setup the block copy operation
|
|
|
|
lda #0 ; initialize count to zero
|
|
sta BCPBbyteCountH,y
|
|
sta BCPBbyteCountL,y
|
|
|
|
sta BCPBcompRel,y ; assume verify good for current buffer
|
|
|
|
lda RCVData+0,x ; setup the Sector Data HOST RAM Address
|
|
sta BCPBhostAddrB0,y
|
|
sta <LoopCountL ; or the bytes to check for nil
|
|
lda RCVData+1,x
|
|
sta BCPBhostAddrB1,y
|
|
tsb <LoopCountL ; or the bytes to check for nil
|
|
lda RCVData+2,x
|
|
sta BCPBhostAddrB2,y
|
|
tsb <LoopCountL ; or the bytes to check for nil
|
|
lda RCVData+3,x
|
|
sta BCPBhostAddrB3,y
|
|
ora <LoopCountL ; or the bytes to check for nil
|
|
bne @notNil ; if valid address, use it
|
|
ldy #-1 ; otherwise, remember that it is disabled
|
|
@notNil rts ; all done
|
|
title 'IOP SWIM Driver - Inc/Dec Host Address'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr IncHostAddr/DecHostAddr
|
|
; Inputs: x - Previous BCPB index
|
|
; y - Current BCPB index
|
|
; Outputs: none
|
|
; Destroys: A, n, v, z, c
|
|
; Calls: none
|
|
; Called by: UpdateTagInfo, UpdateDataInfo
|
|
;
|
|
; Function: BCPBhostAddr[y] := BCPBhostAddr[x] + BCPBbyteCount[x]
|
|
; BCPBcmd[y] := BCPBcmd[x]
|
|
;
|
|
; BCPBhostAddr[y] := BCPBhostAddr[x] - BCPBbyteCount[y]
|
|
; BCPBcmd[y] := BCPBcmd[x]
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
entry IncHostAddr
|
|
IncHostAddr clc ; setup carry for addition
|
|
lda BCPBhostAddrB3,x ; update the RAM Address
|
|
adc BCPBbyteCountL,x ; by the prior byte count
|
|
sta BCPBhostAddrB3,y
|
|
lda BCPBhostAddrB2,x
|
|
adc BCPBbyteCountH,x
|
|
sta BCPBhostAddrB2,y
|
|
lda BCPBhostAddrB1,x
|
|
adc #0
|
|
sta BCPBhostAddrB1,y
|
|
lda BCPBhostAddrB0,x
|
|
adc #0
|
|
sta BCPBhostAddrB0,y
|
|
lda BCPBcmd,x ; setup the block copy operation
|
|
sta BCPBcmd,y
|
|
rts ; all done
|
|
|
|
entry DecHostAddr
|
|
DecHostAddr sec ; setup carry for subtraction
|
|
lda BCPBhostAddrB3,x ; update the RAM Address
|
|
sbc BCPBbyteCountL,y ; by the prior byte count
|
|
sta BCPBhostAddrB3,y
|
|
lda BCPBhostAddrB2,x
|
|
sbc BCPBbyteCountH,y
|
|
sta BCPBhostAddrB2,y
|
|
lda BCPBhostAddrB1,x
|
|
sbc #0
|
|
sta BCPBhostAddrB1,y
|
|
lda BCPBhostAddrB0,x
|
|
sbc #0
|
|
sta BCPBhostAddrB0,y
|
|
lda BCPBcmd,x ; setup the block copy operation
|
|
sta BCPBcmd,y
|
|
rts ; all done
|
|
title 'IOP SWIM Driver - Setup Track Info'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr SetupTrackInfo
|
|
; Inputs: BlocksToDoH/L - remaining blocks to read/write
|
|
; CurrentDrive - active drive number
|
|
; LogicalBlockH/L - Logical Block Number
|
|
; Outputs: c - true if error returned, or no more blocks
|
|
; A - error status code
|
|
; n,z - based on value returned in A
|
|
; PhysCylinder - Physical Cylinder number
|
|
; PhysHead - Physical Head number
|
|
; PhysSector - Physical Sector number
|
|
; PhysSectsPerHead- Physical Number of Sectors per head on this Cylinder
|
|
; PhysSectsPerCyl - Physical Number of Sectors on this Cylinder
|
|
; CacheMRUPtr - Index of cache entry for this Cylinder/Head
|
|
; BaseBufferNum - Buffer number for sector 0 of this track
|
|
; SectBufferNum - Buffer number for current sector of this track
|
|
; SectorsNeeded - number of sectors needed on this track
|
|
; Destroys: X, Y, n, v, z, c
|
|
; Calls: BlockToPhysAddr, FindCacheEntry
|
|
; Called by: SWIMRead, SWIMReadVerify, and SWIMWrite
|
|
;
|
|
; Function: Sets up the variables that describe this track of the request.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
entry SetupTrackInfo
|
|
SetupTrackInfo lda <BlocksToDoH ; lots of blocks left?
|
|
ora <BlocksToDoL ; how many left in the request
|
|
beq @Done ; if none left, request is complete
|
|
|
|
lda <SkipBlockToPhys ; see if we should skip the BlockToPhys call
|
|
stz <SkipBlockToPhys ; Don't skip it next time
|
|
bne @skipped ; if already called, save time by not calling again
|
|
jsr BlockToPhysAddr ; translate the starting block number
|
|
bne @Done ; die on errors
|
|
@skipped jsr FindCacheEntry ; search the cache for this track
|
|
|
|
lda CacheBufferNum,x ; get starting buffer number for this track
|
|
sta <BaseBufferNum ; remember the starting buffer for this track
|
|
bit <SeekDirection ; see which way we are going
|
|
bmi @decrementing ; if decrementing, handle it specially
|
|
|
|
clc ; setup for addition
|
|
adc <PhysSector ; get starting sector number of request
|
|
sta <SectBufferNum ; save the starting buffer number
|
|
|
|
sec ; setup for subtract
|
|
lda <PhysSectsPerHead ; max sectors needed
|
|
sbc <PhysSector ; compute sectors till end of track
|
|
sta <SectorsNeeded ; remember how many sectors are needed
|
|
lda <BlocksToDoH ; lots of blocks left?
|
|
bne @GotNeeded ; if so, Sectors needed is correct
|
|
lda <BlocksToDoL ; how many left in the request
|
|
cpa <SectorsNeeded ; does track have more than we need
|
|
bge @GotNeeded ; if not, use remainder of track
|
|
sta <SectorsNeeded ; otherwise, use just what was requested
|
|
@GotNeeded clc ; indicate track exists
|
|
rts ; all done
|
|
@Done sec ; indicate error, or no more blocks
|
|
rts ; all done
|
|
|
|
@decrementing sta <SectBufferNum ; save the starting buffer number
|
|
lda <PhysSector ; get ending sector number of request
|
|
ina ; + 1 to get sector count
|
|
sta <SectorsNeeded ; remember how many sectors are needed
|
|
ldx <BlocksToDoH ; lots of blocks left?
|
|
bne @GotNeeded ; if so, Sectors needed is correct
|
|
|
|
sec ; setup for subtract
|
|
sbc <BlocksToDoL ; see if SectorsNeeded < BlocksToDo
|
|
blt @GotNeeded ; if so, Sectors Needed is correct
|
|
clc ; setup for addition
|
|
adc <BaseBufferNum ; get starting buffer number of track
|
|
sta <SectBufferNum ; save the starting buffer number
|
|
lda <BlocksToDoL ; how many left in the request
|
|
sta <SectorsNeeded ; remember how many sectors are needed
|
|
* clc ; indicate track exists
|
|
rts ; all done
|
|
title 'IOP SWIM Driver - Update Tag Info'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr UpdateTagInfo
|
|
; Inputs: CurTagBCPB - Current BCPB index
|
|
; y, PrevTagBCPB - Previous BCPB index
|
|
; SectBufferNum - Buffer number for current sector of this track
|
|
; SectorsNeeded - number of sectors needed on this track
|
|
; Outputs: y, CurTagBCPB - former PrevTagBCPB
|
|
; PrevTagBCPB - former CurTagBCPB
|
|
; BCPBiopAddrH/L - IOP address of first tag to transfer for this track
|
|
; BCPBhostAddr - Updated to point to corresponding Host address of tag buffer
|
|
; BCPBbyteCountH/L- Number of bytes of tag data for this track
|
|
; Destroys: A, X, n, v, z, c
|
|
; Calls: IncHostAddr, DecHostAddr
|
|
; Called by: SWIMRead, SWIMReadVerify, SWIMWrite, SWIMFormat
|
|
;
|
|
; Function: Updates the variables associated with the tag data transfer for this track.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
entry UpdateTagInfo ; update the TAG data transfer addresses and counts
|
|
UpdateTagInfo ldx <CurTagBCPB ; get the current tag copy request
|
|
bit <SeekDirection ; see which way we are going
|
|
bmi @decrementing ; if decrementing, handle it specially
|
|
jsr IncHostAddr ; point to new host address
|
|
@common stx <PrevTagBCPB ; swap buffers cur -> prev
|
|
sty <CurTagBCPB ; swap buffers prev -> cur
|
|
ldx <SectBufferNum ; get the starting buffer number
|
|
lda TagPtrs,x ; get the tag buffer offset
|
|
asl a ; unpack 9th bit into carry
|
|
sta BCPBiopAddrL,y ; remember the tag offset
|
|
lda #0 ; setup to add in tag addr carry
|
|
sta BCPBbyteCountH,y ; high byte of byte count always zero
|
|
adc #>TagBuffers ; add in starting page of tag buffers
|
|
sta BCPBiopAddrH,y ; remember the tag page
|
|
assert TagSize=12 ; multiply by 12 is hard coded
|
|
lda <SectorsNeeded ; sector count for this track
|
|
asl a ; SectorsNeeded*2
|
|
adc <SectorsNeeded ; SectorsNeeded*3
|
|
ldx <CurTagBCPB ; x := y := CurTagBCPB
|
|
asl a ; SectorsNeeded*6
|
|
rol BCPBbyteCountH,x ; shift into high byte of byte count
|
|
asl a ; SectorsNeeded*12
|
|
rol BCPBbyteCountH,x ; shift into high byte of byte count
|
|
sta BCPBbyteCountL,y ; setup low byte of byte count
|
|
rts ; all done
|
|
|
|
@decrementing jsr @common ; do everything but increment
|
|
ldx <PrevTagBCPB ; get the previous tag copy request
|
|
jmp DecHostAddr ; exit through DecHostAddr
|
|
title 'IOP SWIM Driver - Update Data Info'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr UpdateDataInfo
|
|
; Inputs: CurDataBCPB - Current BCPB index
|
|
; y, PrevTagBCPB - Previous BCPB index
|
|
; SectBufferNum - Buffer number for current sector of this track
|
|
; SectorsNeeded - number of sectors needed on this track
|
|
; Outputs: y, CurDataBCPB - former PrevTagBCPB
|
|
; PrevTagBCPB - former CurTagBCPB
|
|
; BCPBiopAddrH/L - IOP address of first sector to transfer for this track
|
|
; BCPBhostAddr - Updated to point to corresponding Host address of sector buffer
|
|
; BCPBbyteCountH/L- Number of bytes of sector data for this track
|
|
; Destroys: A, X, n, v, z, c
|
|
; Calls: IncHostAddr, DecHostAddr
|
|
; Called by: SWIMRead, SWIMReadVerify, SWIMWrite, SWIMFormat
|
|
;
|
|
; Function: Updates the variables associated with the sector data transfer for this track.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
entry UpdateDataInfo ; update the DATA data transfer addresses and counts
|
|
UpdateDataInfo ldx <CurDataBCPB ; get the current data copy request
|
|
bit <SeekDirection ; see which way we are going
|
|
bmi @decrementing ; if decrementing, handle it specially
|
|
jsr IncHostAddr ; point to new host address
|
|
@common stx <PrevDataBCPB ; swap buffers cur -> prev
|
|
sty <CurDataBCPB ; swap buffers prev -> cur
|
|
lda <SectBufferNum ; get the starting buffer number
|
|
assert BlockSize=(2*256) ; multiply by 2 is hard coded
|
|
asl a ; buffer number * 2 for 2*256 byte blocks
|
|
adc #>DataBuffers ; add in starting page of data buffers
|
|
sta BCPBiopAddrH,y ; remember the data page
|
|
lda <SectorsNeeded ; sector count for this track
|
|
asl a ; SectorsNeeded*2 for 2*256 byte blocks
|
|
sta BCPBbyteCountH,y ; setup high byte of byte count
|
|
lda #0 ; setup data buffer offset
|
|
sta BCPBiopAddrL,y ; data is page aligned in the buffer
|
|
sta BCPBbyteCountL,y ; setup low byte of byte count
|
|
rts ; all done
|
|
|
|
@decrementing jsr @common ; do everything but increment
|
|
ldx <PrevDataBCPB ; get the previous data copy request
|
|
jmp DecHostAddr ; exit through DecHostAddr
|
|
title 'IOP SWIM Driver - Update Block Info'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr UpdateBlockInfo
|
|
; Inputs: LogicalBlockH/L - Logical Block Number
|
|
; SectorsNeeded - number of sectors needed on this track
|
|
; BlocksToDoH/L - remaining blocks to read/write
|
|
; TagBlockNumH/L - tag buffer
|
|
; Outputs: LogicalBlockH/L - Logical Block Number
|
|
; BlocksToDoH/L - remaining blocks to read/write
|
|
; Destroys: A, n, v, z, c
|
|
; Calls: none
|
|
; Called by: SWIMRead, SWIMReadVerify, SWIMWrite
|
|
;
|
|
; Function: Updates the variables associated with the block addressing for this track.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
entry UpdateBlockInfo ; update the DISK block addresses and counts
|
|
UpdateBlockInfo lda <BlocksToDoL ; get low byte of block count
|
|
sec ; setup for subtraction
|
|
sbc <SectorsNeeded ; account for blocks on this track
|
|
sta <BlocksToDoL ; update block count
|
|
bcs @BlockCountDone ; if no borrow
|
|
dec <BlocksToDoH ; update high byte
|
|
@BlockCountDone
|
|
bit <SeekDirection ; see which way we are going
|
|
bmi @decrementing ; if decrementing, handle it specially
|
|
lda <LogicalBlockL ; get block number
|
|
clc ; setup for addition
|
|
adc <SectorsNeeded ; account for blocks on this track
|
|
sta <LogicalBlockL ; update block number
|
|
bcc @BlockNumIncDone ; if no carry
|
|
inc <LogicalBlockH
|
|
@BlockNumIncDone
|
|
lda <TagBlockNumL ; get low byte of tag block number
|
|
sta ReqMfsTagData+7 ; initialize the count
|
|
clc ; setup for addition
|
|
adc <SectorsNeeded ; account for blocks on this track
|
|
sta <TagBlockNumL ; update tag block number
|
|
lda <TagBlockNumH ; get high byte of tag block number
|
|
sta ReqMfsTagData+6 ; initialize the count
|
|
adc #0 ; add in carry
|
|
sta <TagBlockNumH ; update tag block number
|
|
rts ; all done
|
|
|
|
|
|
@decrementing lda <LogicalBlockL ; get block number
|
|
sec ; setup for subtraction
|
|
sbc <SectorsNeeded ; account for blocks on this track
|
|
sta <LogicalBlockL ; update block number
|
|
bcs @BlockNumDecDone ; if no borrow
|
|
dec <LogicalBlockH
|
|
@BlockNumDecDone
|
|
lda <TagBlockNumL ; get low byte of tag block number
|
|
sec ; setup for subtraction
|
|
sbc <SectorsNeeded ; account for blocks on this track
|
|
sta ReqMfsTagData+7 ; initialize the count
|
|
sta <TagBlockNumL ; update tag block number
|
|
lda <TagBlockNumH ; get high byte of tag block number
|
|
sbc #0 ; subtract borrow
|
|
sta ReqMfsTagData+6 ; initialize the count
|
|
sta <TagBlockNumH ; update tag block number
|
|
rts ; all done
|
|
endwith
|
|
endwith
|
|
endwith
|
|
endproc
|
|
|
|
seg 'constants'
|
|
TagCopyCmd proc
|
|
; table of Tag Copy BCPBcmd bytes indexed by (ReqKind)-WriteReq
|
|
dc.b bcHOSTtoIOP ; 0 - WriteReq
|
|
dc.b bcIOPtoHOST ; 1 - ReadReq
|
|
dc.b bcIOPtoHOST ; 2 - ReadVerifyReq
|
|
|
|
entry DataCopyCmd
|
|
DataCopyCmd
|
|
; table of Data Copy BCPBcmd bytes indexed by (ReqKind)-WriteReq
|
|
dc.b bcHOSTtoIOP ; 0 - WriteReq
|
|
dc.b bcIOPtoHOST ; 1 - ReadReq
|
|
dc.b bcCompare ; 2 - ReadVerifyReq
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Format'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMFormat
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: SetUpDrive, SetupBCPB, GetDriveStatus, WaitDriveReady,
|
|
; CreateTrackCache, ReCalibrate, WaitMotorSettled, Seek,
|
|
; UpdateTagInfo, MakeBCPBreq, WaitBCPBdone, UpdateDataInfo,
|
|
; FormatTrack, BlockToPhysAddr, SWIMDriveStatus
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Disk formatting routine
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMFormat proc
|
|
entry FmtIndexTable
|
|
entry DefaultInterleave
|
|
entry DefaultGapSize
|
|
entry DefaultFmtByte
|
|
entry FormatTrack
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
jsr SetUpDrive ; select and power up the disk drive
|
|
bne @Done ; if error powering up
|
|
|
|
ldy <DataBCPB ; get the BCPB index
|
|
lda #bcHOSTtoIOP ; setup the command
|
|
ldx #ReqFmtDataAddress\
|
|
-RCVData ; offset of start of address field
|
|
jsr SetupBCPB ; copy the FmtDataAddress
|
|
sty <CurDataBCPB ; setup current
|
|
|
|
ldy <TagBCPB ; get the BCPB index
|
|
lda #bcHOSTtoIOP ; setup the command
|
|
ldx #ReqFmtTagAddress\
|
|
-RCVData ; offset of start of address field
|
|
jsr SetupBCPB ; copy the ReqFmtTagAddress
|
|
sty <CurTagBCPB ; setup current
|
|
|
|
lda ReqHdrFmtKind ; get the desired HdrFmtKind byte
|
|
sta <FmtByteBlkSize ; setup Fmt byte / Block Size
|
|
|
|
lda ReqFmtInterleave ; get the desired Interleave factor
|
|
sta <Interleave ; setup sector interleave factor
|
|
|
|
lda ReqFormatKind ; get high byte of format kind
|
|
bne @ParamErr ; if non-zero, it's an error
|
|
lda ReqFormatKind+1 ; get low byte of format kind
|
|
pha ; save for later
|
|
lda #>ReqDriveStatus ; get page number of status message buffer
|
|
ldx <CurrentDrive ; get the drive number
|
|
jsr GetDriveStatus ; get the status for disk size
|
|
lda #1 ; defaults are first index into current format
|
|
plx ; restore low byte of format kind
|
|
beq @CopyFmtList ; if zero, use defaults (x=0)
|
|
txa ; set desired format index
|
|
ldx #StatFmtAllowedH\
|
|
-StatCurrentFmtH ; use formats allowed list
|
|
@CopyFmtList ldy RCVData\
|
|
+StatCurrentFmtH,x ; get high byte of list
|
|
sty <LoopCountH ; save it in a temp
|
|
ldy RCVData\
|
|
+StatCurrentFmtL,x ; get low byte of list
|
|
sty <LoopCountL ; save it in a temp
|
|
ldx #0 ; initialize table index
|
|
@FmtSearchLoop ldy FmtIndexTable,x ; get the format for this index
|
|
bmi @ParamErr ; return ParamErr if formatKind too big
|
|
inx ; update index
|
|
ror <LoopCountH ; shift high byte
|
|
ror <LoopCountL ; shift low byte
|
|
bcc @FmtSearchLoop ; loop if this format not available
|
|
dea ; decrement format kind
|
|
bne @FmtSearchLoop ; loop if not the desired format kind
|
|
|
|
ldx <CurrentDrive ; get drive number
|
|
lda #WriteEnabledFlag ; prepare to test write enable
|
|
bit DriveFlags,x ; test the enable bit
|
|
bne @WriteEnabled ; if ok to write
|
|
lda #wPrErr ; indicate write protected disk
|
|
bra @Done ; return the error
|
|
|
|
@ParamErr lda #paramErr ; format kind out of range
|
|
@Done stz <WritingForFmt ; formatting complete, not writing for format now
|
|
jmp SWIMReqDone ; return the error
|
|
|
|
@WriteEnabled tya ; get new format
|
|
sta DiskFormat,x ; change to new disk format
|
|
lda DefaultGapSize,y ; get the default inter sector gap size/sync count
|
|
sta <SectorGapSize ; setup sector gap info
|
|
|
|
lda <FmtByteBlkSize ; get Fmt byte / Block Size from request
|
|
bne @gotFmtByte ; if non-default, use it
|
|
lda DefaultFmtByte,y ; get default format byte / block size byte value
|
|
@gotFmtByte sta <FmtByteBlkSize ; setup Fmt byte / Block Size
|
|
|
|
ldx <Interleave ; get sector interleave factor from request
|
|
bne @gotInterleave ; if non-default, use it
|
|
ldx DefaultInterleave,y ; get the default interleave for this format
|
|
bpl @gotInterleave ; if positive, use it
|
|
and #$1F ; get the interleave from the format byte
|
|
tax ; setup to store it
|
|
bne @gotInterleave ; if non-zero, use it
|
|
inx ; if zero, change it to one
|
|
@gotInterleave stx <Interleave ; setup sector interleave factor
|
|
|
|
jsr CreateTrackCache ; create a cache using the new Format
|
|
jsr ReCalibrate ; move heads to track zero, init the drive
|
|
bne @Done ; if error powering up
|
|
|
|
lda #1 ; indicate incrementing track order
|
|
sta <SeekDirection ; setup the default direction
|
|
|
|
stz <LogicalBlockL ; start with block number zero
|
|
stz <LogicalBlockH ; setup high byte
|
|
|
|
jsr WaitMotorSettled ; wait for motor speed to be in 1.5% tolerance
|
|
dec <WritingForFmt ; we be formatting now, tell with world
|
|
bra @Start ; start formatting
|
|
|
|
@CylinderLoop ldy <PhysCylinder ; get the cylinder number
|
|
jsr Seek ; start the seek to the cylinder
|
|
bne @Done ; die on errors
|
|
|
|
lda <LogicalBlockL ; get first block of this cylinder
|
|
clc ; setup for addition
|
|
adc <PhysSectsPerCyl ; add in the cylinder size
|
|
sta <LogicalBlockL ; update low byte
|
|
bcc @BlockNumOK ; if no carry into high byte
|
|
inc <LogicalBlockH ; update high byte
|
|
@BlockNumOK
|
|
|
|
; build the sector interleave map
|
|
|
|
ldy <PhysSectsPerHead ; number of sectors to interleave
|
|
lda #$FF ; flag for available sector
|
|
@MapInit dey ; decrement sector count
|
|
sta SectorMap,y ; mark sector as available
|
|
bne @MapInit ; initialize all of the sectors
|
|
@Occupied ina ; try the next sector
|
|
@MapNext cpa <PhysSectsPerHead ; see if still in range
|
|
blt @NoWrap ; if it didn't wrap around
|
|
; sec ; carry set for subtract from CPA above
|
|
sbc <PhysSectsPerHead ; if wrap around, back up
|
|
clc ; clear carry for future addition
|
|
@NoWrap tax ; setup map index
|
|
bit <SectorMap,x ; see if it is already occupied
|
|
bpl @Occupied ; if so, try another one
|
|
sty <SectorMap,x ; if not, update the map
|
|
adc <Interleave ; bump index by interleave
|
|
iny ; update sector number
|
|
cpy <PhysSectsPerHead ; see if end reached
|
|
blt @MapNext ; if not, keep on mapping
|
|
|
|
stz <SectBufferNum ; starting buffer number is always zero
|
|
lda <PhysSectsPerCyl ; get number of blocks on this cylinder
|
|
sta <SectorsNeeded ; sector count for this track
|
|
|
|
; get the tag data
|
|
|
|
ldy <CurTagBCPB ; get the current tag copy info
|
|
bmi @tagXferDone ; if not enabled, skip it
|
|
jsr UpdateTagInfo ; update the data xfer info
|
|
jsr MakeBCPBreq ; issue the copy request
|
|
ldy <CurTagBCPB ; get the current tag copy info
|
|
jsr WaitBCPBdone ; wait for it to complete
|
|
@tagXferDone
|
|
|
|
; get the sector data
|
|
|
|
ldy <CurDataBCPB ; get the current data copy info
|
|
bmi @dataXferDone ; if not enabled, skip it
|
|
jsr UpdateDataInfo ; update the data xfer info
|
|
jsr MakeBCPBreq ; issue the copy request
|
|
ldy <CurDataBCPB ; get the current data copy info
|
|
jsr WaitBCPBdone ; wait for it to complete
|
|
@dataXferDone
|
|
|
|
jsr WaitDriveReady ; wait for the seek to complete
|
|
beq @HeadLoop ; continue with next head if successful
|
|
@toDone jmp @Done ; die on errors
|
|
@HeadLoop
|
|
jsr FormatTrack ; format the track
|
|
bne @toDone ; exit if formatting error
|
|
inc <PhysHead ; go on to the next head
|
|
lda <PhysSectsPerCyl ; get remaining blocks on this cylinder
|
|
sec ; setup for subtract
|
|
sbc <PhysSectsPerHead ; decrement by blocks on this head
|
|
sta <PhysSectsPerCyl ; update remaining blocks on this cylinder
|
|
bne @HeadLoop ; loop through all of the heads
|
|
|
|
@Start jsr BlockToPhysAddr ; convert highest block on this cylinder
|
|
beq @CylinderLoop ; process next cylinder if end not reached
|
|
jsr CreateTrackCache ; create a cache using the new Format
|
|
stz <WritingForFmt ; formatting complete, not writing for format now
|
|
lda #noErr ; indicate format succeeded
|
|
jmp SWIMDriveStatus ; finish up by returning the drive status
|
|
endwith
|
|
endwith
|
|
endproc
|
|
|
|
seg 'constants'
|
|
DefaultInterleave proc
|
|
; 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
|
|
|
|
entry DefaultGapSize
|
|
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
|
|
|
|
entry DefaultFmtByte
|
|
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
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Format Verify'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMFormatVerify
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: SetUpDrive, GetDriveStatus, WaitDriveReady, FlushTrackCache,
|
|
; BlockToPhysAddr, Seek, FindCacheEntry, SkipSectorData, ReadSectorHdr,
|
|
; ReadSectorData
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Verify that the disk is correctly formatted and all blocks
|
|
; can be read without errors.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMFormatVerify proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
with SectorBuffers
|
|
jsr SetUpDrive ; select and power up the disk drive
|
|
bne @Done ; if error powering up
|
|
|
|
lda #>ReqDriveStatus ; get page number of status message buffer
|
|
ldx <CurrentDrive ; get the drive number
|
|
jsr GetDriveStatus ; get the status for disk size
|
|
|
|
jsr WaitDriveReady ; wait for motor speed and heads to settle
|
|
bne @Done ; if error powering up
|
|
|
|
jsr FlushTrackCache ; Flush the cache to force reads without retries
|
|
lda #1 ; setup to dec (last block is disk size - 1)
|
|
sta <PhysSectsPerCyl ; setup decrement value
|
|
lda RCVData\
|
|
+StatDiskSizeB1 ; get high byte of disk size
|
|
sta <LogicalBlockH ; setup highest logical block number
|
|
lda RCVData\
|
|
+StatDiskSizeB0 ; get low byte of disk size
|
|
bra @Start ; start verifying
|
|
|
|
@HdrErr bne @Done ; if sector out of range, die with verErr
|
|
@VerErr lda #verErr ; verify failed, sector not found
|
|
@Done jmp SWIMReqDone ; return the error
|
|
|
|
@CylinderLoop jsr BlockToPhysAddr ; convert highest block on this cylinder
|
|
bne @Done ; die on errors
|
|
|
|
ldy <PhysCylinder ; get the cylinder number
|
|
jsr Seek ; start the seek to the cylinder
|
|
bne @Done ; die on errors
|
|
jsr WaitDriveReady ; wait for the seek to complete
|
|
bne @Done ; die on errors
|
|
|
|
@HeadLoop lda <PhysHead ; get the head we want
|
|
sta <SeekingHead ; pass it to ReadSectorHdr
|
|
jsr FindCacheEntry ; search the cache for this track
|
|
lda CacheBufferNum,x ; get starting buffer number for this track
|
|
sta <BaseBufferNum ; remember the starting buffer for this track
|
|
lda <PhysSectsPerHead ; max sectors needed
|
|
sta <SectorsNeeded ; remember how many sectors are needed
|
|
lda #WrongSectsMax ; number of wrong sectors allowed before error
|
|
sta <WrongSectsLeft ; number of wrong sectors remaining before error
|
|
bra @VerifyLoop ; start reading the track
|
|
|
|
@SkipSector jsr SkipSectorData ; give ADB some time
|
|
@VerifyLoop dec <WrongSectsLeft ; count the block
|
|
beq @VerErr ; if count exhausted, die with verErr
|
|
jsr ReadSectorHdr ; look for a sector
|
|
bcs @HdrErr ; die on all errors
|
|
|
|
assert ReadDataValid=$80 ; sign bit is valid bit
|
|
bmi @SkipSector ; if already in cache, just ignore it
|
|
|
|
jsr ReadSectorData ; read the data into the cache
|
|
beq @readOK ; if successfull read, update sector info
|
|
|
|
cpa <DMARetryStatus ; see if it was a DMA timeout error
|
|
beq @VerifyLoop ; if so, just re-try it without ints during DMA
|
|
bra @Done ; if real I/O error, handle sector data errors
|
|
|
|
@readOK ldx <CacheMRUPtr ; get index to current cache entry
|
|
dec CacheInvalidCount,x ; decrement the invalid entry count
|
|
|
|
ldx <SectBufferNum ; get the buffer number for this sector
|
|
lda #ReadDataValid ; mark the buffer data valid
|
|
sta BufferStatus,x ; set ReadDataValid, clear IORequested
|
|
|
|
dec <SectorsNeeded ; decrement blocks to find
|
|
bne @VerifyLoop ; if more needed, continue searching
|
|
|
|
dec <PhysHead ; go on to the next head
|
|
bpl @HeadLoop ; loop through all of the heads
|
|
|
|
lda <LogicalBlockL ; get last block of this cylinder
|
|
@Start sec ; setup for subtract
|
|
sbc <PhysSectsPerCyl ; subtract out the cylinder size
|
|
sta <LogicalBlockL ; update low byte
|
|
lda <LogicalBlockH ; get high byte
|
|
sbc #0 ; subtract high byte
|
|
sta <LogicalBlockH ; update high byte
|
|
bge @CylinderLoop ; continue verify if more cylinders
|
|
|
|
lda #noErr ; verify succeeded
|
|
bra @Done ; return the error
|
|
endwith
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM Get Raw Track Data'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp SWIMGetRawData
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: SetUpDrive, Seek, WaitDriveReady, CreateTrackCache, AdrAndSense,
|
|
; Sense, ReadSectorData, ReadHdrSetup, MakeBCPBreq, WaitBCPBdone,
|
|
; ReadSectorHdr, SetupBCPB
|
|
; Called by: SWIM Request Dispatcher
|
|
;
|
|
; Function: Reads Raw data from the disk, starting at a user selectable point.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SWIMGetRawData proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
with SectorBuffers
|
|
|
|
; The read loop would average about 30.643 clocks per byte (15.647µsec) which doesn't
|
|
; allow 5% speed variation in MFM mode (16µsec - 5% = 15.2µsec).
|
|
; To allow better margin for speed variation, self modifying code is used in the loop
|
|
; to get the average byte time down to 29.647 clocks (15.138µsec)
|
|
|
|
FasterReads equ 1 ; enable faster (and self modifying) read loop
|
|
|
|
jsr SetUpDrive ; select and power up the disk drive
|
|
bne RawIOError ; if error powering up
|
|
|
|
jsr SetupByteCount ; get the byte count, make sure it is in range
|
|
|
|
clc ; indicate data buffer
|
|
ldy <DataBCPB ; get the BCPB index
|
|
ldx #ReqRawDataAddress\
|
|
-RCVData ; offset of start of address field
|
|
lda #>(DataBuffers+(RawDataBufferNum*BlockSize)+RawByteCountMax+$FF)
|
|
jsr SetupRawBCPB ; copy the ReqRawDataAddress
|
|
sty <CurDataBCPB ; setup data buffer copy request
|
|
|
|
sec ; indicate clock buffer
|
|
ldy <TagBCPB ; get the BCPB index
|
|
ldx #ReqRawClockAddress\
|
|
-RCVData ; offset of start of address field
|
|
lda #>(DataBuffers+(RawClockBufferNum*BlockSize)+RawByteCountMax/8+$FF)
|
|
jsr SetupRawBCPB ; copy the ReqRawClockAddress
|
|
sty <CurTagBCPB ; setup clock buffer copy request
|
|
|
|
lda #paramErr ; default to Parameter Error if out of range
|
|
ldx ReqRawSearchMode+0 ; check high byte of search mode
|
|
bne RawIOError ; must be zero
|
|
ldx ReqRawSearchMode+1 ; check low byte of search mode
|
|
cpx #4 ; must be 0…3
|
|
bge RawIOError ; param error if out of range
|
|
|
|
ldx ReqRawHead ; get the requested head number
|
|
stx <SeekingHead ; remember which head we want
|
|
cpx CurHeadsPerCyl ; check against max heads
|
|
bge RawIOError ; param error if out of range
|
|
|
|
ldy <CurrentDrive ; get the active drive number
|
|
ldx DiskFormat,y ; see what format we have
|
|
stz <RawIOFmtMFM ; $00 if GCR
|
|
cpx #MFM1440Kformat ; check for MFM (1440K)
|
|
beq @mfmDisk ; if MFM, adjust sector number
|
|
cpx #MFM720Kformat ; check for MFM (720K)
|
|
bne @formatFound ; if not MFM, use GCR numbering
|
|
@mfmDisk dec ReqRawSector ; MFM sectors are 1 based, we need zero based
|
|
dec <RawIOFmtMFM ; $FF if MFM
|
|
@formatFound
|
|
|
|
ldx ReqRawCylinder ; get high byte of cylinder
|
|
bne RawIOError ; param error if out of range
|
|
ldy ReqRawCylinder+1 ; get low byte of cylinder
|
|
jsr Seek ; move the heads
|
|
bne RawIOError ; die on errors
|
|
jsr WaitDriveReady ; wait for the seek to complete
|
|
beq StartSearching ; if successful, search for the starting point
|
|
|
|
RawIOError ldx #4-1 ; loop count/index
|
|
@errLoop stz ReqRawByteCount,x ; indicate that no bytes were transferred
|
|
dex ; update loop count/index
|
|
bpl @errLoop ; clear all 4 bytes of the byte count
|
|
RawIODone ldx #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
stx wZeroes ; clear action and write mode (read done)
|
|
stz CurSectsPerHead ; disable prefetching, sects per head unknown
|
|
pha ; save error code
|
|
jsr CreateTrackCache ; cache is trash, re-create it
|
|
pla ; restore error code, set conditions
|
|
jmp SWIMReqDone ; return the error
|
|
|
|
|
|
|
|
StartSearching ldx ReqRawSearchMode+1 ; check low byte of search mode
|
|
assert SearchImmediate=0
|
|
beq ReadImmediate ; if SearchImmediate, select the head and start reading
|
|
|
|
assert SearchForAddress=1
|
|
dex
|
|
beq FindAddress ; if SearchForAddress, starting reading after sect hdr
|
|
|
|
assert SearchForData=2
|
|
dex
|
|
beq FindData ; if SearchForData, starting reading after sect data
|
|
|
|
assert SearchForIndex=3
|
|
FindIndex lda <RawIOFmtMFM ; see if MFM
|
|
beq ReadImmediate ; if GCR, no index pulse, searchImmediate
|
|
|
|
stz <LoopCountH ; init outer loop counter 65K times ± 256
|
|
lda #rIndexPulseAdr ; sense the index signal
|
|
jsr AdrAndSense ; sense it
|
|
@indexLoop tax ; remember index signal from last sample
|
|
jsr Sense ; test the index signal
|
|
beq @noIndex ; if index not active then keep waiting
|
|
txa ; index is active, see if inactive last time
|
|
beq ReadImmediate ; if inactive to active transition, start reading
|
|
@noIndex iny ; update low byte of timeout counter
|
|
bne @indexLoop ; if time remaining, keep checking
|
|
inc <LoopCountH ; update high byte of timeout counter
|
|
bne @indexLoop ; if time ramaining, keep checking
|
|
lda #noIndexErr ; timed out searching for index
|
|
ToRawIOError bra RawIOError ; return the error
|
|
|
|
|
|
FindAddress jsr FindSector ; search for the sector
|
|
bcs RawIOError ; if not found, report the error
|
|
lda <RawIOFmtMFM ; see if MFM or GCR
|
|
beq ContinueReading ; if GCR, continue reading, without re-syncing
|
|
bra ReadImmediate ; if MFM, re-sync, and start reading
|
|
|
|
|
|
FindData jsr FindSector ; search for the sector
|
|
bcs RawIOError ; if not found, report the error
|
|
lda #>(DataBuffers+\
|
|
(RawTempBufferNum*BlockSize)) ; page number of temp buffer
|
|
sta <DataBufPtr+1 ; setup page number for data to be ignored
|
|
sta <TagBufPtr+1 ; dump the tags there too.
|
|
jsr ReadSectorData ; read the data, ignore errors
|
|
|
|
|
|
ReadImmediate jsr ReadHdrSetup ; select the head, start shifting in data
|
|
ContinueReading ldx <TagBCPB ; get the BCPB index
|
|
lda BCPBiopAddrH,x ; get high byte of clock buffer start address
|
|
sta <TagBufPtr+1 ; setup high byte of buffer pointer
|
|
lda BCPBiopAddrL,x ; get low byte of clock buffer start address
|
|
sta <TagBufPtr ; setup low byte of buffer pointer
|
|
|
|
ldx <DataBCPB ; get the BCPB index
|
|
lda BCPBiopAddrH,x ; get high byte of data buffer start address
|
|
ldy BCPBiopAddrL,x ; get low byte of data buffer start address
|
|
if FasterReads then
|
|
sta StoreDataInst+2 ; setup high byte of buffer pointer
|
|
else
|
|
sta <DataBufPtr+1 ; setup high byte of buffer pointer
|
|
stz <DataBufPtr ; setup low byte of buffer pointer
|
|
endif
|
|
|
|
ldx #$01 ; constant to cause carry out after 8 shifts
|
|
stx <LoopCountH ; initialize shift temporary to $01
|
|
|
|
txa ; outer loop counter
|
|
bit rData ; empty a garbage byte out of the FIFO,
|
|
bit rError ; read and clear errors
|
|
@findByte bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bmi ReadLoopStart ; if so, go ahead and start reading
|
|
inc <LoopCountL ; update inner loop counter
|
|
bne @findByte ; loop until a byte is found
|
|
ina ; update outer loop counter
|
|
bne @findByte ; loop until a byte is found, or timeout
|
|
lda #noNybErr ; indicate that no bytes were found
|
|
bra ToRawIOError ; return the error
|
|
|
|
WaitNextByte lda rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl WaitNextByte ; loop until byte is ready
|
|
assert MarkInFIFO=$01
|
|
lsr a ; carry := mark/data indicator
|
|
lda rMark ; read the mark/data byte
|
|
if FasterReads then
|
|
StoreDataInst sta $FF00,y ; store the raw data (page number gets modified)
|
|
else
|
|
sta (<DataBufPtr),y ; store the raw data
|
|
endif
|
|
rol <LoopCountH ; shift in the mark/data indicator
|
|
iny ; update the buffer offset
|
|
bcc WaitNextByte ; loop until a group of 8 bytes completes
|
|
|
|
bne @DataPageOK ; if page boundary not reached
|
|
if FasterReads then
|
|
inc StoreDataInst+2 ; if page crossing, update the page number
|
|
else
|
|
inc <DataBufPtr+1 ; if page crossing, update the page number
|
|
endif
|
|
@DataPageOK
|
|
lda <LoopCountH ; get the mark/data indicators for 8 bytes
|
|
stx <LoopCountH ; re-initialize to $01 for next pass
|
|
sta (<TagBufPtr) ; store the indicators
|
|
inc <TagBufPtr ; update the buffer offset
|
|
bne WaitNextByte ; loop until page crossing
|
|
|
|
inc <TagBufPtr+1 ; if page crossing, update the page number
|
|
ReadLoopStart lda <TagBufPtr+1 ; get the updated page number
|
|
cpa #>(DataBuffers+\
|
|
(RawClockBufferNum*BlockSize)+\
|
|
RawByteCountMax/8+$FF) ; see if end has been reached
|
|
bne WaitNextByte ; keep looping until end of buffer
|
|
|
|
ldy <CurDataBCPB ; setup data buffer copy request
|
|
jsr SendData ; copy the buffer
|
|
ldy <CurTagBCPB ; setup clock buffer copy request
|
|
jsr SendData ; copy the buffer
|
|
lda #noErr ; indicate success
|
|
jmp RawIODone ; if all done, return success
|
|
|
|
|
|
SendData bmi @noData ; if no buffer, skip it
|
|
phy ; save the BCPB index
|
|
jsr MakeBCPBreq ; issue the copy request
|
|
ply ; restore the BCPB index
|
|
jmp WaitBCPBdone ; wait for it to complete and return
|
|
@noData rts ; all done
|
|
|
|
|
|
FindSector inc <DMARetryStatus ; force 1 re-try to prevent ints during DMA
|
|
lda #WrongSectsMax+1 ; successful read, re-init Wrong Sector Count
|
|
sta <WrongSectsLeft ; number of wrong sectors remaining before Recal
|
|
lda #$FF ; allow any sector number
|
|
sta <PhysSectsPerHead ; setup for sector number range checking
|
|
@tryAgain lda #sectNFErr ; assume it isn't found
|
|
sec ; return carry := 1, to indicate error
|
|
dec <WrongSectsLeft ; update the retry count
|
|
bmi @done ; if no retries left, report the error
|
|
jsr ReadSectorHdr ; read the next sector header that comes along
|
|
bcs @tryAgain ; if any error, try again
|
|
lda <SectHdrSector ; get the sector number that was found
|
|
eor ReqRawSector ; see if it is the one that we want
|
|
bne @tryAgain ; if not, try the next one
|
|
@done rts ; all done
|
|
|
|
|
|
|
|
SetupByteCount lda ReqRawByteCount+0 ; get high byte of longword byte count
|
|
ora ReqRawByteCount+1 ; get next byte of longword byte count
|
|
bne @ReduceByteCount ; if too big, reduce it
|
|
ldy ReqRawByteCount+3 ; get low byte of longword byte count
|
|
cpy #<RawByteCountMax ; compare to max
|
|
ldx ReqRawByteCount+2 ; get middle byte of longword byte count
|
|
txa ; setup for compare
|
|
sbc #>RawByteCountMax ; compare to max
|
|
blt @ByteCountOK ; if less than max then use it, else use max
|
|
@ReduceByteCount
|
|
stz ReqRawByteCount+0 ; clear high byte of longword byte count
|
|
stz ReqRawByteCount+1 ; clear next byte of longword byte count
|
|
ldx #>RawByteCountMax ; get high byte of max byte count
|
|
ldy #<RawByteCountMax ; get low byte of max byte count
|
|
@ByteCountOK stx ReqRawByteCount+2 ; setup middle byte of longword byte count
|
|
sty ReqRawByteCount+3 ; setup low byte of longword byte count
|
|
tya ; get the low byte
|
|
clc ; setup for addition
|
|
adc #$07 ; round up to mod 8 boundary
|
|
and #$F8 ; clear low bits
|
|
bcc @rounded ; if no carry to propagate, store the count
|
|
inx ; propagate the carry
|
|
@rounded stx <BlocksToDoH ; setup high byte of loop counter
|
|
sta <BlocksToDoL ; setup low byte of loop counter
|
|
rts ; all done
|
|
|
|
|
|
SetupRawBCPB php ; save the flags
|
|
pha ; save the ending page number
|
|
lda #0 ; ending page offset is always zero
|
|
sec ; setup for subtraction
|
|
sbc <BlocksToDoL ; subtract the byte count
|
|
sta BCPBiopAddrL,y ; remember the page offset (page aligned)
|
|
pla ; get the page number again
|
|
sbc <BlocksToDoH ; subtract the byte count
|
|
sta BCPBiopAddrH,y ; remember the page number
|
|
lda #bcIOPtoHOST ; setup the command
|
|
jsr SetupBCPB ; copy the ReqRawClockAddress
|
|
ldx <BlocksToDoH ; high byte of byte count
|
|
lda <BlocksToDoL ; low byte of byte count
|
|
|
|
lsr <BlocksToDoH ; shift byte count right by 3 bits (div by 8)
|
|
ror <BlocksToDoL ; shift low byte
|
|
lsr <BlocksToDoH
|
|
ror <BlocksToDoL
|
|
lsr <BlocksToDoH
|
|
ror <BlocksToDoL
|
|
|
|
plp ; restore the carry flag
|
|
phy ; see if buffer valid
|
|
ply
|
|
bmi @done ; if not, don't need to setup byte count
|
|
bcs @storeCount ; if clock buffer, store rounded count div 8
|
|
|
|
ldx ReqRawByteCount+2 ; for data buffer, use the actual byte count
|
|
lda ReqRawByteCount+3
|
|
|
|
@storeCount sta BCPBbyteCountL,y ; setup low byte of byte count
|
|
txa ; get high byte
|
|
sta BCPBbyteCountH,y ; setup high byte of byte count
|
|
@done rts ; all set up
|
|
endwith
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Create Track Cache'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr CreateTrackCache
|
|
; Inputs: CurrentDrive - active drive number
|
|
; Outputs: CurHeadsPerCyl - Number of heads per cylinder for this disk
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: FlushTrackCache
|
|
; Called by: SWIMFormat, SWIMGetRawData, SetUpDrive
|
|
;
|
|
; Function: Creates a track cache for the current drive, allocating buffer
|
|
; space according to the format of the disk in the drive.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
CreateTrackCache proc
|
|
entry BlockXlateTable
|
|
entry BlockXlateHeadsPerCyl
|
|
entry BlockXlateSectsPerHead
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
ldy <CurrentDrive ; get the active drive number
|
|
ldx DiskFormat,y ; see what format to read
|
|
ldy BlockXlateTable,x ; Get the translate table index
|
|
lda BlockXlateHeadsPerCyl,y ; get number of heads per cylinder for this format
|
|
sta CurHeadsPerCyl ; remember it for prefetching
|
|
|
|
ldx #-1 ; cache index
|
|
lda #TotalBuffers ; index of last buffer + 1
|
|
sec ; setup for subtract (remains set within loop)
|
|
bra @BufferStart ; enter the loop
|
|
@BufferLoop pha ; save the buffer number
|
|
txa ; get current element index
|
|
inx ; point to the next cache entry
|
|
sta CacheNextMRU,x ; initialize the link
|
|
pla ; restore the buffer number
|
|
sta CacheBufferNum,x ; initialize the starting buffer number
|
|
@BufferStart sbc BlockXlateSectsPerHead,y ; allocate a track buffer
|
|
bge @BufferLoop ; loop until all buffer space used
|
|
|
|
stx <CacheMRUPtr ; make this the new first element
|
|
* (fall into) bra FlushTrackCache ; now invalidate all of the elements
|
|
title 'IOP SWIM Driver - Flush Track Cache / Flush If Disabled'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr FlushTrackCache
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, X, n, z
|
|
; Calls: none
|
|
; Called by: SWIMFormatVerify, CreateTrackCache, FlushIfDisabled
|
|
;
|
|
; Function: Flushes the existing track cache for the current drive by
|
|
; invalidating all of the cache entries.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
entry FlushTrackCache
|
|
|
|
FlushTrackCache ldx #MaxCacheEntries-1 ; index of last entry / loop counter
|
|
lda #-1 ; invalid count
|
|
@invalidLoop sta CacheInvalidCount,x ; mark all entries invalid
|
|
dex ; index through each element
|
|
bpl @invalidLoop ; loop until all done
|
|
rts ; all done
|
|
|
|
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr FlushIfDisabled
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, X, n, z
|
|
; Calls: FlushTrackCache
|
|
; Called by: SWIMCacheControl, SetUpDrive
|
|
;
|
|
; Function: Checks to see if Track Cacheing is enabled, and if not, calls
|
|
; FlushTrackCache to invalidate it.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
entry FlushIfDisabled
|
|
|
|
FlushIfDisabled lda CacheEnabled ; see if cache is enabled
|
|
beq FlushTrackCache ; if disabled, flush it
|
|
rts ; if enabled, leave it alone
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Find Cache Entry'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr FindCacheEntry
|
|
; Inputs: PhysCylinder - Physical Cylinder number
|
|
; PhysHead - Physical Head number
|
|
; PhysSectsPerHead- Physical Number of Sectors per head on this Cylinder
|
|
; Outputs: CacheMRUPtr - Index of cache entry for this Cylinder/Head
|
|
; X - loaded with CacheMRUPtr
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: none
|
|
; Called by: SetupTrackInfo, SWIMFormatVerify
|
|
;
|
|
; Function: Searches the track cache for an entry matching this cyl/head.
|
|
; If it is not found in the cache, an entry will be created in the
|
|
; cache (replacing an invalid entry, or the least recently used
|
|
; entry if all entries were valid). The cache entry for this
|
|
; track will be updated to be the most recently used entry.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
FindCacheEntry proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
with SectorBuffers
|
|
|
|
ldy #-1 ; indicate that no invalid entries found
|
|
lda <CacheMRUPtr ; search the cache in MRU order
|
|
bra @searchStart ; enter the search loop
|
|
|
|
@invalid tay ; remember the last invalid entry found
|
|
@searchNext lda CacheNextMRU,x ; get index of next entry to search
|
|
bmi @searchEnd ; if end of list, exit
|
|
@searchStart tax ; setup index to current entry to check
|
|
bit CacheInvalidCount,x ; see if entry is valid
|
|
bmi @invalid ; if invalid, don't bother with compare
|
|
lda CacheCylinder,x ; get the cylinder number of the cache entry
|
|
cpa <PhysCylinder ; see if it matches
|
|
bne @searchNext ; if not, go on to next entry
|
|
lda CacheHead,x ; get the head number of the cache entry
|
|
cpa <PhysHead ; see if it matches
|
|
bne @searchNext ; if not, go on to next entry
|
|
bra @match ; if both match, we found the entry
|
|
|
|
; No match was found, so we need to create an entry, and reclaim an existing entry.
|
|
; We will first try to reclaim an invalid entry, but if all entries were valid,
|
|
; we will reclaim the least recently used valid entry.
|
|
|
|
@searchEnd tya ; see if any invalids found
|
|
bmi @createEntry ; if not, then just use the last entry
|
|
tax ; if invalid found, use it
|
|
@createEntry lda <PhysCylinder ; get the Cylinder for the entry
|
|
sta CacheCylinder,x ; update the cache entry
|
|
lda <PhysHead ; get the Head for the entry
|
|
sta CacheHead,x ; update the cache entry
|
|
lda <PhysSectsPerHead ; get the sector count
|
|
sta CacheInvalidCount,x ; all of the sectors are invalid
|
|
|
|
; invalidate the buffer status of each sector on this track
|
|
|
|
tay ; setup loop counter
|
|
clc ; setup for addition
|
|
adc CacheBufferNum,x ; point past end of buffer list
|
|
phx ; preserve cache index
|
|
tax ; setup status index
|
|
@statusLoop dex ; point to previous entry
|
|
stz BufferStatus,x ; clear the status
|
|
dey ; decrement the buffer count
|
|
bne @statusLoop ; loop for all buffers
|
|
plx ; restore cache index
|
|
|
|
; We now have a valid entry in the cache, and we must promote it to be the head
|
|
; of the Most Recently Used (MRU) list.
|
|
|
|
@match cpx <CacheMRUPtr ; see if already MRU
|
|
beq @done ; of so, we're all done
|
|
txa ; setup index to search for
|
|
ldy #-1 ; loop counter/index
|
|
@MRUloop iny ; inc counter/index
|
|
cpa CacheNextMRU,y ; look for entry that points to new MRU
|
|
bne @MRUloop ; loop until it is found
|
|
|
|
lda CacheNextMRU,x ; get successor of new MRU
|
|
sta CacheNextMRU,y ; remove new MRU entry from chain
|
|
lda <CacheMRUPtr ; get pointer to old MRU entry
|
|
sta CacheNextMRU,x ; new MRU entry points to old MRU entry
|
|
stx <CacheMRUPtr ; mark new entry as MRU
|
|
@done rts ; return with valid MRU entry
|
|
endwith
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Prefetch Into Cache'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr PrefetchIntoCache
|
|
; Inputs: CacheEnabled - Track cache enabled flag
|
|
; CurrentDrive - Drive that is currently spinning (zero if none)
|
|
; LastActiveDrive - Drive that was most recently spinning (zero if none)
|
|
; PrefetchActive - Flag indicating that prefetcher is active
|
|
; DriverActive - Flag indicating that driver is active
|
|
; RequestPending - Flag indicating that a request is pending
|
|
; CurSectsPerHead - Sectors per head for current track (zero if unknown)
|
|
; CurHeadsPerCyl - Number of heads per cylinder for this disk
|
|
; CacheMRUPtr - Index of cache entry for this Cylinder/Head
|
|
; Outputs: Prefetch data is read into the cache (validating the entries)
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: WaitDriveReady, SkipSectorData, ReadSectorHdr, ReadSectorData
|
|
; Called by: PollingTask
|
|
;
|
|
; Function: Reads sectors from all of the heads on the current cylinder
|
|
; of the spinning disk drive, into the track cache.
|
|
;
|
|
; The heads are read in Cache MRU order. Invalid tracks in
|
|
; the cache will be validated with any remaining track that
|
|
; were not already in the cache (without moving the head).
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
PrefetchIntoCache proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
with SectorBuffers
|
|
entry SkipSectorData
|
|
|
|
lda CacheEnabled ; see if cache is enabled
|
|
beq PrefetchDone ; if disabled, don't prefetch
|
|
lda <CurrentDrive ; see which drive is spining
|
|
beq PrefetchDone ; if none, quit
|
|
cpa <LastActiveDrive ; see if it matches last active drive
|
|
beq PrefetchTrack ; if so, find a track to prefetch
|
|
PrefetchDone stz PrefetchActive ; indicate that prefetcher is idle
|
|
stz <DriverActive ; let driver run
|
|
lda #$FF ; mask to check all bits
|
|
trb RequestPending ; see if a request is pending
|
|
beq @exit ; if not, just return
|
|
jmp HandleRCVMsg2 ; if pending, go run it
|
|
@exit rts
|
|
|
|
|
|
PrefetchTrack lda CurSectsPerHead ; get sectors per head for this track
|
|
beq PrefetchDone ; if invalid, don't prefetch
|
|
sta <PhysSectsPerHead ; remember it for ReadSectorHdr
|
|
lda #-1 ; starting head - 1
|
|
@nextHead ina ; try next higher head
|
|
cpa CurHeadsPerCyl ; see if still in range
|
|
bge PrefetchDone ; exit if all heads searched
|
|
sta <SeekingHead ; remember head to read from
|
|
ldy #-1 ; indicate that no invalid entries found
|
|
lda <CacheMRUPtr ; search the cache in MRU order
|
|
bra @searchStart ; enter the search loop
|
|
|
|
@invalid tay ; remember the last invalid entry found
|
|
@searchNext lda CacheNextMRU,x ; get index of next entry to search
|
|
bmi @searchEnd ; if end of list, exit
|
|
@searchStart tax ; setup index to current entry to check
|
|
bit CacheInvalidCount,x ; see if entry is valid
|
|
bmi @invalid ; if invalid, don't bother with compare
|
|
lda <SeekingCylinder ; get the current cylinder number
|
|
cpa CacheCylinder,x ; see if it matches
|
|
bne @searchNext ; if not, go on to next entry
|
|
lda CacheInvalidCount,x ; see if any invalid entries need to be filled
|
|
bne @searchFound ; if so, fill this cache entry
|
|
lda CacheHead,x ; get the head number of the cache entry
|
|
cpa <SeekingHead ; see if it matches
|
|
beq @nextHead ; if so, go on to next head
|
|
bra @searchNext ; otherwise, go on to next entry
|
|
|
|
@searchEnd tya ; see if any invalids found
|
|
bmi PrefetchDone ; if not, then just use the last entry
|
|
tax ; if invalid found, use it
|
|
lda <SeekingCylinder ; get the current cylinder number
|
|
sta CacheCylinder,x ; update the cylinder in case we use this entry
|
|
lda <SeekingHead ; get the head number to fill
|
|
sta CacheHead,x ; validate the head
|
|
lda <PhysSectsPerHead ; get number of sectors for this head
|
|
sta CacheInvalidCount,x ; mark all of the sectors invalid
|
|
|
|
tay ; setup loop counter
|
|
clc ; setup for addition
|
|
adc CacheBufferNum,x ; point past end of buffer list
|
|
phx ; preserve cache index
|
|
tax ; setup status index
|
|
@invalidateLoop dex ; point to previous entry
|
|
stz BufferStatus,x ; clear the status
|
|
dey ; decrement the buffer count
|
|
bne @invalidateLoop ; loop for all buffers
|
|
plx ; restore cache index
|
|
|
|
@searchFound lda CacheHead,x ; get the head of the matching entry
|
|
sta <SeekingHead ; mark it as the head to read
|
|
lda CacheBufferNum,x ; get starting buffer number for this track
|
|
sta <BaseBufferNum ; remember the starting buffer for this track
|
|
stx PrefetchCachePtr ; remember the cache entry pointer
|
|
|
|
lda #$FF ; give lots of sectors left
|
|
sta <WrongSectsLeft ; to force sleep
|
|
sta PrefetchActive ; indicate that we are sleeping during prefetch
|
|
sta <DriverActive ; driver is active while waiting ready
|
|
jsr WaitDriveReady ; make sure the drive is ready
|
|
bne @toPrefetchDone ; if not, don't bother prefetching
|
|
stz <DriverActive ; let driver run during sleep
|
|
jsr SkipSectorData ; sleep to give time to other tasks
|
|
lda <CurrentDrive ; see which drive is spining
|
|
beq @toPrefetchDone ; if none, quit
|
|
cpa <LastActiveDrive ; see if it matches last active drive
|
|
bne @toPrefetchDone ; if not, quit
|
|
|
|
lda #$FF ; awake again, driver active
|
|
sta <DriverActive ; mark active in case of ints during DMA
|
|
jsr ReadSectorHdr ; look for a sector
|
|
bcs @nextSect ; ignore all errors
|
|
|
|
assert ReadDataValid=$80 ; sign bit is valid bit
|
|
bmi @nextSect ; if already in cache, just ignore it
|
|
|
|
jsr ReadSectorData ; read the data into the cache
|
|
bne @nextSect ; ignore all errors
|
|
ldx PrefetchCachePtr ; get index to current cache entry
|
|
dec CacheInvalidCount,x ; decrement the invalid entry count
|
|
|
|
ldx <SectBufferNum ; get the buffer number for this sector
|
|
lda #ReadDataValid ; mark the buffer data valid
|
|
sta BufferStatus,x ; set ReadDataValid, clear IORequested
|
|
@nextSect jmp PrefetchIntoCache ; loop until timeout
|
|
@toPrefetchDone jmp PrefetchDone
|
|
endwith
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Get Drive Status'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr GetDriveStatus
|
|
; Inputs: A - High byte of StatusMsg address
|
|
; X - DriveNumber
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, z, c
|
|
; Cycles: 693..757
|
|
; Calls: TranslateStatus (internal routine)
|
|
; Called by: SWIMDriveStatus, PollingTask, SWIMFormat, SWIMFormatVerify
|
|
;
|
|
; Function: Given a drive number and a pointer to a Message Buffer,
|
|
; this routine returns the status information for that drive
|
|
; and the disk that may be inserted in that drive.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
GetDriveStatus proc
|
|
entry StatusTables
|
|
entry DefaultStatus
|
|
entry DriveStatusPtrs
|
|
entry MediaStatusPtrs
|
|
entry FmtStatusPtrs
|
|
entry DriveAttributes
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
ldy <StatusMsgPtr+1 ; save old status message pointer
|
|
phy ; push it onto the stack
|
|
sta <StatusMsgPtr+1 ; create new status message pointer
|
|
|
|
lda DiskFormat,x ; get the disk format for the disk
|
|
pha ; save it for later
|
|
lda MediaKind,x ; get the media kind for the disk
|
|
pha ; save it for later
|
|
lda DriveKind,x ; get the drive kind for the drive
|
|
pha ; save it for later
|
|
|
|
lda CylinderValid,x ; get the current Cylinder (high byte)
|
|
ldy #StatTrackH ; setup the index into the status message
|
|
sta (<StatusMsgPtr),y ; move it to the status message
|
|
|
|
lda CurrentCylinder,x ; get the current Cylinder (low byte)
|
|
ldy #StatTrackL ; setup the index into the status message
|
|
sta (<StatusMsgPtr),y ; move it to the status message
|
|
|
|
lda ErrorCountH,x ; get the error count (high byte)
|
|
ldy #StatDiskErrorsH ; setup the index into the status message
|
|
sta (<StatusMsgPtr),y ; move it to the status message
|
|
|
|
lda ErrorCountL,x ; get the error count (low byte)
|
|
ldy #StatDiskErrorsL ; setup the index into the status message
|
|
sta (<StatusMsgPtr),y ; move it to the status message
|
|
|
|
ldy PhysDriveNumber,x ; get the Physical Drive number
|
|
lda DriveAttributes,y ; get the drive attributes
|
|
ldy #StatDriveAttr ; setup the index into the status message
|
|
sta (<StatusMsgPtr),y ; move it to the status message
|
|
|
|
lda DriveFlags,x ; get the Drive Flags
|
|
and #WriteEnabledFlag ; see if write enabled
|
|
beq UpdateWrProt ; branch if write protected (a = 0)
|
|
lda #$01 ; return $00 if write enabled
|
|
UpdateWrProt dea ; return $FF if write protected
|
|
ldy #StatWrProtected ; setup the index into the status message
|
|
sta (<StatusMsgPtr),y ; move it to the status message
|
|
|
|
ldx #DefaultStatus-StatusTables ; get ptr to defaults table
|
|
jsr TranslateStatus ; translate the Default
|
|
|
|
ply ; get the drive kind for the drive
|
|
ldx DriveStatusPtrs,y ; find start of table for this drive kind
|
|
jsr TranslateStatus ; translate the Drive status
|
|
|
|
ply ; get the media kind for the disk
|
|
ldx MediaStatusPtrs,y ; find start of table for this media kind
|
|
jsr TranslateStatus ; translate the Format status
|
|
|
|
ply ; get the disk format for the drive
|
|
ldx FmtStatusPtrs,y ; find start of table for this disk format
|
|
jsr TranslateStatus ; translate the Format status
|
|
|
|
ply ; pop old status message pointer
|
|
sty <StatusMsgPtr+1 ; restore it
|
|
rts ; all done
|
|
|
|
XlateLoop inx ; point to data byte
|
|
lda StatusTables,x ; get the data byte
|
|
sta (<StatusMsgPtr),y ; move it to the status message
|
|
inx ; point to next index byte
|
|
TranslateStatus ldy StatusTables,x ; get the message index byte
|
|
bne XlateLoop ; loop through all bytes
|
|
rts ; all done
|
|
endwith
|
|
endwith
|
|
endproc
|
|
|
|
seg 'constants'
|
|
StatusConstants proc
|
|
|
|
entry StatusTables
|
|
entry DefaultStatus
|
|
entry DriveStatusPtrs
|
|
entry MediaStatusPtrs
|
|
entry FmtStatusPtrs
|
|
entry DriveAttributes
|
|
entry FmtIndexTable
|
|
StatusTables
|
|
|
|
; Default status values returned
|
|
DefaultStatus dc.b StatDiskInPlace,$01
|
|
dc.b StatInstalled,$01
|
|
dc.b StatTwoSidedFmt,$FF
|
|
dc.b StatDriveInfoB3,$00
|
|
dc.b StatDriveInfoB2,$00
|
|
dc.b StatMfmDrive,$00
|
|
dc.b StatMfmDisk,$00
|
|
dc.b StatMfmFormat,$00
|
|
dc.b StatDiskCtlr,$FF
|
|
dc.b StatCurrentFmtH,$00
|
|
dc.b StatFmtAllowedH,$00
|
|
dc.b StatDiskSizeB3,$00
|
|
dc.b StatDiskSizeB2,$00
|
|
dc.b StatIconFlags,$00
|
|
dc.b StatSpare,$00
|
|
dc.b $00
|
|
|
|
|
|
; Tables based on Drive Kind
|
|
|
|
; unknown drive kind info
|
|
NoDrive dc.b StatInstalled,$FF
|
|
dc.b StatSides,$00
|
|
dc.b StatNewIntFace,$00
|
|
dc.b StatDriveType,noDriveKind
|
|
dc.b $00
|
|
|
|
; HD-20 drive kind info
|
|
HD20Drive dc.b StatSides,$00
|
|
dc.b StatNewIntFace,$00
|
|
dc.b StatDriveType,HD20DriveKind
|
|
dc.b StatFmtAllowedL,$01
|
|
dc.b StatCurrentFmtL,$01
|
|
dc.b StatIconFlags,$03
|
|
dc.b $00
|
|
|
|
; 400K GCR drive kind info
|
|
SSGCRDrive dc.b StatSides,$00
|
|
dc.b StatNewIntFace,$00
|
|
dc.b StatDriveType,SSGCRDriveKind
|
|
dc.b StatFmtAllowedL,$02
|
|
dc.b $00
|
|
|
|
; 400K / 800K GCR drive kind info
|
|
DSGCRDrive dc.b StatSides,$FF
|
|
dc.b StatNewIntFace,$FF
|
|
dc.b StatDriveType,DSGCRDriveKind
|
|
dc.b StatFmtAllowedL,$06
|
|
dc.b $00
|
|
|
|
; 400K / 800K GCR, 720K / 1440K MFM drive kind info
|
|
DSMFMGCRDrive dc.b StatSides,$FF
|
|
dc.b StatNewIntFace,$FF
|
|
dc.b StatDriveType,DSMFMGCRDriveKind
|
|
dc.b StatMfmDrive,$FF
|
|
dc.b StatFmtAllowedL,$0E
|
|
dc.b $00
|
|
|
|
|
|
; Tables based on Media Kind
|
|
|
|
; no media kind info
|
|
NoMedia dc.b StatDiskInPlace,$00
|
|
dc.b StatFmtAllowedL,$00
|
|
dc.b StatCurrentFmtL,$00
|
|
dc.b $00
|
|
|
|
LoDenMedia dc.b StatCurrentFmtL,$02
|
|
dc.b $00
|
|
|
|
HiDenMedia dc.b StatFmtAllowedL,$10
|
|
dc.b StatCurrentFmtL,$10
|
|
HD20Media dc.b $00
|
|
|
|
|
|
; Tables based on Disk Format Kind
|
|
|
|
; unknown format kind info
|
|
NoFmt dc.b StatDiskInPlace,$02
|
|
; unchecked format kind info
|
|
UnCheckedFmt dc.b StatTwoSidedFmt,$00
|
|
dc.b StatDiskSizeB1,$00
|
|
dc.b StatDiskSizeB0,$00
|
|
dc.b $00
|
|
|
|
; HD-20 format kind info
|
|
HD20Fmt dc.b StatDiskInPlace,$08
|
|
dc.b StatTwoSidedFmt,$00
|
|
dc.b StatDiskSizeB1,$98 ; $9835 = 38965
|
|
dc.b StatDiskSizeB0,$35
|
|
dc.b $00
|
|
|
|
; 400K GCR format kind info
|
|
GCR400KFmt dc.b StatDiskInPlace,$02
|
|
dc.b StatTwoSidedFmt,$00
|
|
dc.b StatDiskSizeB1,$03 ; $0320 = 800
|
|
dc.b StatDiskSizeB0,$20
|
|
dc.b $00
|
|
|
|
; 800K GCR format kind info
|
|
GCR800KFmt dc.b StatDiskInPlace,$02
|
|
dc.b StatCurrentFmtL,$04
|
|
dc.b StatDiskSizeB1,$06 ; $0640 = 1600
|
|
dc.b StatDiskSizeB0,$40
|
|
dc.b $00
|
|
|
|
; 720K MFM format kind info
|
|
MFM720KFmt dc.b StatDiskInPlace,$02
|
|
dc.b StatMfmDisk,$FF
|
|
dc.b StatCurrentFmtL,$08
|
|
dc.b StatDiskSizeB1,$05 ; $05A0 = 1440
|
|
dc.b StatDiskSizeB0,$A0
|
|
dc.b $00
|
|
|
|
; 1440K MFM format kind info
|
|
MFM1440KFmt dc.b StatDiskInPlace,$02
|
|
dc.b StatMfmDisk,$FF
|
|
dc.b StatMfmFormat,$FF
|
|
dc.b StatDiskSizeB1,$0B ; $0B40 = 2880
|
|
dc.b StatDiskSizeB0,$40
|
|
dc.b $00
|
|
|
|
; 400K/800K GCR on HD media format kind info
|
|
GCRonHDFmt dc.b StatDiskInPlace,$03
|
|
dc.b StatMfmDisk,$FF
|
|
dc.b StatMfmFormat,$FF
|
|
dc.b StatFmtAllowedL,$10
|
|
dc.b StatCurrentFmtL,$10
|
|
dc.b StatDiskSizeB1,$0B ; $0B40 = 2880
|
|
dc.b StatDiskSizeB0,$40
|
|
dc.b $00
|
|
|
|
DriveStatusPtrs dc.b NoDrive-StatusTables ; 0 - no drive
|
|
dc.b NoDrive-StatusTables ; 1 - unspecified drive
|
|
dc.b SSGCRDrive-StatusTables ; 2 - Single Sided 400K GCR Drive
|
|
dc.b DSGCRDrive-StatusTables ; 3 - Double Sided 400K/800K GCR Drive
|
|
dc.b DSMFMGCRDrive-StatusTables ; 4 - Double Sided GCR / MFM Drive
|
|
dc.b NoDrive-StatusTables ; 5 - unspecified drive
|
|
dc.b NoDrive-StatusTables ; 6 - unspecified drive
|
|
dc.b HD20Drive-StatusTables ; 7 - HD-20 drive
|
|
dc.b NoDrive-StatusTables ; 8 - unspecified drive
|
|
|
|
MediaStatusPtrs dc.b NoMedia-StatusTables ; 0 - No Media kind
|
|
dc.b LoDenMedia-StatusTables ; 1 - unknown media kind
|
|
dc.b HD20Media-StatusTables ; 2 - HD-20 media
|
|
dc.b LoDenMedia-StatusTables ; 3 - Low Density media
|
|
dc.b HiDenMedia-StatusTables ; 4 - High Density media
|
|
|
|
FmtStatusPtrs dc.b UnCheckedFmt-StatusTables ; 0 - uncheckedFormat
|
|
dc.b NoFmt-StatusTables ; 1 - unknownFormat
|
|
dc.b HD20Fmt-StatusTables ; 2 - HD20Format
|
|
dc.b GCR400KFmt-StatusTables ; 3 - GCR400Kformat
|
|
dc.b GCR800KFmt-StatusTables ; 4 - GCR800Kformat
|
|
dc.b MFM720KFmt-StatusTables ; 5 - MFM720Kformat
|
|
dc.b MFM1440KFmt-StatusTables ; 6 - MFM1440Kformat
|
|
dc.b 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
|
|
|
|
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
|
|
|
|
endproc
|
|
title 'IOP SWIM Driver - Check Static Attributes'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr CheckStaticAttr
|
|
; Inputs: X - Drive Number
|
|
; Outputs: none
|
|
; Destroys: A, Y, n, z, c
|
|
; Calls: AdrAndSense
|
|
; 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.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
CheckStaticAttr proc
|
|
with SWIMVars
|
|
|
|
lda #LoDenMediaKind ; assume low density media
|
|
sta MediaKind,x ; indicate disk is in place
|
|
lda DriveKind,x ; check for multi-density drive
|
|
cpa #DSMFMGCRDriveKind ; see if SuperDrive
|
|
bne CheckWrEnable ; if not superdrive, media density is correct
|
|
lda #r1MegMediaAdr ; check for 1 or 2 megabyte media
|
|
jsr AdrAndSense ; test the media kind
|
|
bne CheckWrEnable ; if low density media
|
|
lda #HiDenMediaKind ; indicate High Density media in drive
|
|
sta MediaKind,x ; update media kind
|
|
|
|
CheckWrEnable lda DriveFlags,x ; get the flags
|
|
ora #WriteEnabledFlag ; assume write enabled
|
|
sta DriveFlags,x ; update flags
|
|
lda #rNoWrProtectAdr ; check write enabled/protected
|
|
jsr AdrAndSense ; test the write protect
|
|
bne Done ; if write enabled
|
|
lda DriveFlags,x ; get the flags
|
|
and #$FF-WriteEnabledFlag ; reset write enabled
|
|
sta DriveFlags,x ; update flags
|
|
Done rts ; all done
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Set Up Drive'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr SetUpDrive
|
|
; Inputs: A - OpenStatus error code
|
|
; n, z - status of the OpenStatus flag
|
|
; Outputs: A, n, z - error status code
|
|
; Destroys: A, X, Y, n, z
|
|
; Calls: FlushIfDisabled, TurnOffDrive, TurnOnDrive, LoadSWIMparams,
|
|
; CheckStaticAttr, WaitTMPBdone, StartReadyTimer, ReCalibrate,
|
|
; ReadSectorHdr, CreateTrackCache
|
|
; Called by: SWIMEject, SWIMRead, SWIMReadVerify, SWIMWrite, SWIMFormat,
|
|
; SWIMFormatVerify, SWIMGetRawData
|
|
;
|
|
; 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.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SetUpDrive proc
|
|
entry FmtSearchOrder
|
|
entry FmtByteValues
|
|
entry FmtByteMasks
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
bne SetUpDone ; return openErr if SWIM Driver not initialized
|
|
ldy ReqDriveNumber ; see what drive is requested
|
|
beq NotCurrentDrive ; zero is not a valid drive number
|
|
cpy <CurrentDrive ; see if already powered up
|
|
bne NotCurrentDrive ; if not currently powered drive
|
|
ldy #MotorTimeoutCount ; setup count of polls to wait
|
|
sty <MotorTimingOut ; start timing out motor when polling resumes
|
|
SetUpDone tay ; preserve error code
|
|
jsr FlushIfDisabled ; flush the cache if it is disabled
|
|
tya ; restore error code, set flags
|
|
rts ; drive already set up, just return
|
|
|
|
NotCurrentDrive lda #nsDrvErr ; return no such drive error if out of range
|
|
cpy #MaxDriveNumber+1 ; see if in range
|
|
bge SetUpDone ; return nsDrvErr if out of range
|
|
|
|
lda #noDriveErr ; return no drive error if drive not installed
|
|
ldx DriveKind,y ; get the drive kind
|
|
beq SetUpDone ; return noDriveErr if not installed
|
|
|
|
jsr TurnOffDrive ; turn off the old drive (whatever was selected)
|
|
jsr TurnOnDrive ; turn on the new drive
|
|
bne SetUpDone ; return error if couldn't turn on drive
|
|
|
|
txa ; get drive number
|
|
eor <LastActiveDrive ; see if same drive as last successful access
|
|
beq SetUpDone ; return with success, no drive change
|
|
|
|
stx <LastActiveDrive ; mark this drive as last active, assuming success
|
|
ldy DiskFormat,x ; see if format has been determined
|
|
beq @FirstTime ; unchecked format, first time disk is accessed
|
|
jsr LoadSWIMparams ; Load params if format is known
|
|
bra @FmtChangeDone ; drive changed, so create a new cache
|
|
|
|
@FirstTime ply ; pop pcL
|
|
sty <AsyncPcL ; save PcL
|
|
ply ; pop pcH
|
|
sty <AsyncPcH ; save PcH
|
|
|
|
jsr CheckStaticAttr ; update media kind and write enable
|
|
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
jsr WaitTMPBdone ; wait for it to time out
|
|
ldx #>ChuckingDelay ; get high byte of Disk Chucking wait time
|
|
lda #<ChuckingDelay ; get low byte of Disk Chucking wait time
|
|
jsr StartReadyTimer ; start the timer
|
|
|
|
stz FmtSearchIndex ; initialize the index
|
|
@SearchLoop ldy FmtSearchIndex ; get the index
|
|
inc FmtSearchIndex ; update the index
|
|
lda FmtSearchOrder,y ; get the format to try
|
|
ldx <CurrentDrive ; get the drive number
|
|
sta DiskFormat,x ; setup the disk format
|
|
eor #unknownFormat ; are we done
|
|
beq @SearchDone ; unknown format found, exit
|
|
|
|
jsr ReCalibrate ; establish head position
|
|
bne @SearchError ; return with error if couldn't recalibrate
|
|
|
|
stz <SeekingHead ; always read from size zero
|
|
lda #$FF ; allow any sector number
|
|
sta <PhysSectsPerHead ; setup for sector number range checking
|
|
jsr ReadSectorHdr ; try to read a sector address
|
|
bcs @SearchLoop ; if couldn't read, try next format
|
|
|
|
ldy FmtSearchIndex ; get the updated index
|
|
lda <SectHdrFmtKind ; get the disk format found
|
|
eor FmtByteValues-1,y ; compare to expected value
|
|
and FmtByteMasks-1,y ; get only the valid bits
|
|
beq @SearchDone ; if format correct, exit with success
|
|
bra @SearchLoop ; otherwise, try next format
|
|
|
|
@SearchError ldx <CurrentDrive ; get the drive number
|
|
stz DiskFormat,x ; indicate unchecked format, couldn't re-cal
|
|
|
|
@SearchDone ldy <AsyncPcH ; get PcH
|
|
phy ; restore PcH
|
|
ldy <AsyncPcL ; get PcL
|
|
phy ; restore PcL
|
|
@FmtChangeDone pha ; preserve the error code
|
|
jsr CreateTrackCache ; create a cache based on the new format
|
|
pla ; restore error code, set flags
|
|
rts ; all done
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Check Drive Mode'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr CheckDriveMode
|
|
; Inputs: X - Drive Number
|
|
; Outputs: z - 0 (bne) if drive mode change needed
|
|
; z - 1 (beq) if no drive mode change needed
|
|
; A - command for AdrAndStrb to change drive mode,
|
|
; only valid when z indicates change is needed
|
|
; Destroys: Y, v, c, n, z
|
|
; Calls: AdrAndSense
|
|
; 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.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
CheckDriveMode proc
|
|
with SWIMVars
|
|
lda DriveKind,x ; check the drive kind
|
|
cpa #DSMFMGCRDriveKind ; see if SuperDrive
|
|
bne ModeIsCorrect ; only superdrive supports GCR and MFM
|
|
|
|
lda #wMFMModeOnAdr ; assume MFM format
|
|
ldy DiskFormat,x ; check the format
|
|
cpy #MFM720Kformat ; 720K is only lo-den MFM format
|
|
beq CheckMode ; if 720K MFM disk
|
|
|
|
cpy #GCRonHDformat ; GCR data on HD media needs GCR mode
|
|
beq NeedGCRMode ; if GCR data on hi-density disk
|
|
|
|
ldy MediaKind,x ; check the media
|
|
cpy #HiDenMediaKind ; High density media is almost always MFM
|
|
beq CheckMode ; if High Density Media
|
|
|
|
NeedGCRMode lda #wGCRModeOnAdr ; otherwise set to GCR format
|
|
|
|
CheckMode pha ; save desired mode
|
|
lda #rMFMModeOnAdr ; prepare to see what mode the drive is in
|
|
jsr AdrAndSense ; read the status
|
|
bne InMFMMode ; if in MFM mode
|
|
|
|
InGCRMode pla ; restore desired mode
|
|
cpa #wGCRModeOnAdr ; we're in GCR mode, see if we want to be
|
|
rts ; return with A = desired mode, z = mode is correct
|
|
|
|
InMFMMode pla ; restore desired mode
|
|
cpa #wMFMModeOnAdr ; we're in MFM mode, see if we want to be
|
|
rts ; return with A = desired mode, z = mode is correct
|
|
|
|
ModeIsCorrect lda #0 ; set z, indicate mode is correct
|
|
rts ; return
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Turn On Drive'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr TurnOnDrive
|
|
; Inputs: ReqDriveNumber - Drive Number
|
|
; Outputs: A, n, z - error status code
|
|
; X, CurrentDrive - Drive number, (only when noErr)
|
|
; Destroys: Y, v, c
|
|
; Calls: DiskSelect, AdrAndSense, TurnOffDrive, CheckDriveMode, AdrAndStrb
|
|
; 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.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
TurnOnDrive proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
ldy ReqDriveNumber ; see what drive is requested
|
|
ldx PhysDriveNumber,y ; get the new physical drive number
|
|
jsr DiskSelect ; select and enable the new drive
|
|
lda #rNoDiskInPlAdr ; prepare to see if there is a disk
|
|
jsr AdrAndSense ; read the drive status bit
|
|
beq HasMedia ; turn drive on, if there is a disk in the drive
|
|
jsr TurnOffDrive ; turn off and disable the drive if no diskette
|
|
lda #OffLinErr ; indicate that the disk was off line
|
|
rts ; all done
|
|
|
|
HasMedia ldx ReqDriveNumber ; see what drive is requested
|
|
stx <CurrentDrive ; mark new drive as powered
|
|
lda #MotorTimeoutCount ; setup count of polls to wait
|
|
sta <MotorTimingOut ; start timing out motor when polling resumes
|
|
|
|
lda #rMotorOffAdr ; prepare to test the drive motor status
|
|
jsr AdrAndSense ; see if it is off
|
|
beq 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.
|
|
jsr CheckDriveMode ; see if mode needs to change
|
|
beq TurnItOn ; no change needed, just turn on motor
|
|
jsr AdrAndStrb ; otherwise, set the drive mode first
|
|
|
|
TurnItOn lda #wMotorOnAdr ; prepare to turn the drive motor on
|
|
jsr AdrAndStrb ; turn it on
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
jsr CancelTMPBtimer ; cancel the timer in case it was running
|
|
ldx #>MotorOnDelay ; get high byte of Disk Motor On wait time
|
|
lda #<MotorOnDelay ; get low byte of Disk Motor On wait time
|
|
jsr StartReadyTimer ; start the timer
|
|
|
|
lda #>MotorOnSettle ; get high byte of settle time
|
|
sta SettleTimeH ; set high byte of Motor speed settle time
|
|
lda #<MotorOnSettle ; get low byte of settle time
|
|
sta SettleTimeL ; set low byte of Motor speed settle time
|
|
|
|
AlreadyOn ldx <CurrentDrive ; return current drive number
|
|
lda #noErr ; return successful status
|
|
rts ; all done
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Turn Off Drive'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr TurnOffDrive
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, Y, n, z
|
|
; Calls: AdrAndStrb, CancelTMPBtimer
|
|
; Called by: SWIMInitialize, PollingTask, SWIMEject, SetUpDrive, TurnOnDrive,
|
|
; WaitDriveReady, ReCalibrate
|
|
;
|
|
; Function: Immediatly turns off the motor and disables the drive
|
|
; that was currently enabled.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
TurnOffDrive proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
stz <CurrentDrive ; indicate that no drive is powered
|
|
stz <MotorTimingOut ; indicate timeout expired
|
|
lda #wMotorOffAdr ; motor off drive command
|
|
jsr AdrAndStrb ; send the command to the drive
|
|
lda #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
sta wZeroes ; clear action and write mode (prepare for motoroff)
|
|
lda #MotorOn+Drive1Enabled\
|
|
+Drive2Enabled ; prepare to disable the drive
|
|
sta wZeroes ; clear the enables
|
|
stz SettleTimeH ; ignore any pending settle time
|
|
stz SettleTimeL ; ignore any pending settle time
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
jmp CancelTMPBtimer ; cancel the timer and return
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Wait Drive Ready'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr WaitDriveReady
|
|
; Inputs: none
|
|
; Outputs: A, n, z - error status code
|
|
; Destroys: A, X, Y, n, z
|
|
; Calls: WaitTMPBdone, AdrAndSense, CancelTMPBTimer, StartTMPBTimer,
|
|
; StartReadyTimer, WaitTMPBdone,TurnOffDrive
|
|
; Called by: SWIMEject, SWIMRead, SWIMReadVerify, SWIMWrite, SWIMFormat,
|
|
; SWIMFormatVerify, SWIMGetRawData, PrefetchIntoCache, ReCalibrate,
|
|
; Seek
|
|
;
|
|
; 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.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
WaitDriveReady proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
pla ; pop pcL
|
|
plx ; pop pcH
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
jsr WaitTMPBdone ; wait for initial delay to time out
|
|
phx ; push pcH
|
|
pha ; push pcL
|
|
|
|
lda #<0-ReadyTimeoutCount ; low byte of max polls counter
|
|
sta WaitReadyCount ; initialize low byte of counter
|
|
lda #>0-ReadyTimeoutCount ; high byte of max polls counter
|
|
sta WaitReadyCount+1 ; initialize high byte of counter
|
|
|
|
WaitReadyLoop lda #rNotReadyAdr ; prepare to test ready
|
|
jsr AdrAndSense ; test drive ready
|
|
bne NotReady ; if not ready, sleep and try again
|
|
|
|
lda SettleTimeH ; get high byte of Motor speed settle time
|
|
ora SettleTimeL ; get low byte of Motor speed settle time
|
|
bne StartSettleTimer ; start timer if needed
|
|
* lda #noErr ; return No Error status if drive is ready
|
|
rts ; return with success
|
|
|
|
StartSettleTimer
|
|
ldy SettledTMPB ; get the TMPB index
|
|
jsr CancelTMPBTimer ; cancel any old timeout
|
|
ldy SettledTMPB ; get the TMPB index
|
|
ldx SettleTimeH ; get high byte of Motor speed settle time
|
|
lda SettleTimeL ; get low byte of Motor speed settle time
|
|
jsr StartTMPBTimer ; start the timer
|
|
stz SettleTimeH ; remember that timer was started
|
|
stz SettleTimeL ; remember that timer was started
|
|
lda #noErr ; return No Error status if drive is ready
|
|
rts ; return with success
|
|
|
|
NotReady ldx #>ReadyPollRate ; get high byte of poll rate
|
|
lda #<ReadyPollRate ; get low byte of poll rate
|
|
jsr StartReadyTimer ; start the timer
|
|
pla ; pop pcL
|
|
plx ; pop pcH
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
jsr WaitTMPBdone ; wait for it to time out
|
|
phx ; push pcH
|
|
pha ; push pcL
|
|
|
|
inc WaitReadyCount ; increment the timeout counter
|
|
bne WaitReadyLoop ; no carry out, check drive ready
|
|
inc WaitReadyCount+1 ; increment high byte of counter
|
|
bne WaitReadyLoop ; count not exhausted, check drive ready
|
|
jsr TurnOffDrive ; timed out, turn off the drive
|
|
lda #spdAdjErr ; timed out, indicate error
|
|
rts ; return with error
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Wait Motor Settled'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr WaitMotorSettled
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: Y, n, z
|
|
; Calls: WaitTMPBdone
|
|
; Called by: SWIMWrite, SWIMFormat
|
|
;
|
|
; 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.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
WaitMotorSettled proc
|
|
with SWIMVars
|
|
ldy SettledTMPB ; get the TMPB index
|
|
jmp WaitTMPBdone ; wait for settle time to expire
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Start Ready Timer'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr StartReadyTimer
|
|
; Inputs: A - low byte of sleep count
|
|
; X - high byte of sleep count
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: StartTMPBTimer
|
|
; Called by: SetUpDrive, TurnOnDrive, WaitDriveReady, SleepShort, SleepLong,
|
|
; ReCalibrate, Seek, ReadMFMData, WriteDataBuffer
|
|
;
|
|
; Function: Starts the Ready wait timer using the supplied values.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
StartReadyTimer proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
stz <ADBActivityMask ; give disk priority next time
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
sta TMPBtimeL,y ; store low byte into the TMPB
|
|
txa ; get high byte of poll rate
|
|
sta TMPBtimeH,y ; store it into the TMPB
|
|
jmp StartTMPBTimer ; start the timer, and return
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SleepShort / SleepLong'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr SleepShort, SleepLong
|
|
; Inputs: A - low byte of sleep count
|
|
; X - high byte of sleep count (SleepLong only, > 33.333333 ms)
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: StartReadyTimer, WaitTMPBdone
|
|
; Called by: SWIMEject, SkipSectorData, FmtMFMTrack
|
|
;
|
|
; Function: Sleeps for the specified amount of time.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SleepShort proc
|
|
entry SleepLong
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
ldx #0 ; high byte is zero (0 .. 33.333333 ms)
|
|
SleepLong jsr StartReadyTimer ; start the timer
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
jmp WaitTMPBdone ; return after it times out
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Re-Calibrate heads to cylinder 0'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr ReCalibrate
|
|
; Inputs: CurrentDrive - active drive number
|
|
; Outputs: A - error status code
|
|
; n,z - based on value returned in A
|
|
; SeekingCylinder - 0 (cylinder number that we are seeking)
|
|
; Destroys: A, X, Y, n, z, c
|
|
; Calls: InitSWIMChip, TurnOnDrive, CheckDriveMode, AdrAndStrb, WaitTMPBdone,
|
|
; StartReadyTimer, WaitDriveReady, Seek, AdrAndSense, TurnOffDrive
|
|
; Called by: SWIMEject, SWIMRead, SWIMReadVerify, SWIMWrite, SWIMFormat, 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.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
ReCalibrate proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
pla ; pop pcL
|
|
sta ReCalPcL ; save pcL
|
|
pla ; pop pcH
|
|
sta ReCalPcH ; save pcH
|
|
|
|
ldx <CurrentDrive ; get current drive number
|
|
stz CurrentCylinder,x ; indicate current Cylinder is zero
|
|
stz <SeekingCylinder ; indicate which cylinder we are seeking
|
|
jsr InitSWIMChip ; Re-Initialize the disk controller chip
|
|
bne ReCalErrA ; return with error status if couldn't init
|
|
jsr TurnOnDrive ; re-select and power the drive
|
|
bne ReCalErrA ; return with error status if couldn't turn on
|
|
|
|
jsr CheckDriveMode ; see if mode needs to change
|
|
beq ReCalStart ; if mode is OK, don't change is
|
|
|
|
jsr AdrAndStrb ; change the drive mode
|
|
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
jsr WaitTMPBdone ; wait for 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.
|
|
ldx #>MotorOnDelay ; get high byte of Disk Motor On wait time
|
|
lda #<MotorOnDelay ; get low byte of Disk Motor On wait time
|
|
jsr StartReadyTimer ; start the timer
|
|
|
|
lda #>MotorOnSettle ; get high byte of settle time
|
|
sta SettleTimeH ; set high byte of Motor speed settle time
|
|
lda #<MotorOnSettle ; get low byte of settle time
|
|
sta SettleTimeL ; set low byte of Motor speed settle time
|
|
|
|
ReCalStart jsr WaitDriveReady ; wait for the motor to be ready
|
|
bne ReCalErrA ; return error if motor not ready
|
|
|
|
ldx <CurrentDrive ; get current drive number
|
|
stz CylinderValid,x ; indicate current Cylinder is valid
|
|
lda #LastCylinder ; worst case is that we are at the end
|
|
sta CurrentCylinder,x ; make beleive that we are there
|
|
ldy #0 ; we want to go to cylinder zero
|
|
jsr Seek ; seek to zero
|
|
jsr WaitDriveReady ; wait for the heads to move
|
|
bne ReCalErrA ; return error if couldn't seek
|
|
|
|
ldx #noErr ; setup success code, just in case
|
|
lda #rNotTrack0Adr ; see if we found cylinder zero
|
|
jsr AdrAndSense ; test the flag
|
|
beq ReCalErrX ; return success if cylinder zero was found
|
|
|
|
ldx #tk0BadErr ; setup error code, just in case
|
|
|
|
ReCalErrX txa ; get error code
|
|
ReCalErrA ldx <CurrentDrive ; get current drive number
|
|
sta CylinderValid,x ; indicate current Cylinder is zero (if no err)
|
|
sta CurrentCylinder,x ; indicate current Cylinder is zero (if no err)
|
|
tay ; test error code
|
|
beq ReCalDone ; no error, restore pc and return
|
|
stz DiskFormat,x ; indicate un-checked format kind
|
|
pha ; preserve error code
|
|
jsr TurnOffDrive ; couldn't re-cal, turn off the drive
|
|
pla ; restore error code
|
|
ReCalDone ldx ReCalPcH ; get pcH
|
|
phx ; restore pcH
|
|
ldx ReCalPcL ; get pcL
|
|
phx ; restore pcL
|
|
tax ; test error code, set flags
|
|
rts ; all done
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Seek heads to cylinder'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr Seek
|
|
; Inputs: CurrentDrive - active drive number
|
|
; Y - Desired cylinder number
|
|
; Outputs: A - error status code
|
|
; n,z - based on value returned in A
|
|
; SeekingCylinder - cylinder number that we are seeking
|
|
; CurSectsPerHead - Zero, to indicate unknown
|
|
; Destroys: X, Y, n, z, c
|
|
; Calls: AdrAndStrb, AdrAndSense, Sense, Pulse, WaitTMPBdone,
|
|
; StartReadyTimer, LoadSWIMparams
|
|
; Called by: SWIMEject, SWIMRead, SWIMReadVerify, SWIMWrite, SWIMFormat,
|
|
; SWIMFormatVerify, SWIMGetRawData, 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.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
Seek proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
stz <DMARetryStatus ; new track, allow ints during DMA requests
|
|
stz CurSectsPerHead ; new track, don't prefetch unless ReadSectorHdr called
|
|
sty <SeekingCylinder ; indicate which cylinder we are seeking
|
|
lda #ParamErr ; return paramater error if cylinder too high
|
|
cpy #LastCylinder+1 ; check for out of range
|
|
bge SeekDone ; if out of range, return with error
|
|
|
|
ldx <CurrentDrive ; get the active drive number
|
|
lda CylinderValid,x ; see if cylinder is valid
|
|
bne SeekDone ; if invalid, return with error
|
|
|
|
lda CurrentCylinder,x ; get current cylinder
|
|
sta <StepCount ; setup current cylinder for subtraction
|
|
tya ; get desired cylinder
|
|
sta CurrentCylinder,x ; update current cylinder (to desired)
|
|
sec ; setup carry for subtract
|
|
sbc <StepCount ; step count := desired - current
|
|
beq SeekDone ; no seek needed if on cylinder (A=0, noErr)
|
|
|
|
ldy #wDirNextAdr ; set direction away from cylinder zero
|
|
bge SeekStart ; start seeking ahead if positive
|
|
|
|
eor #$FF ; prepare to negate
|
|
ina ; step count := current - desired
|
|
ldy #wDirPrevAdr ; set direction towards cylinder zero
|
|
|
|
SeekStart sta <StepCount ; save step count
|
|
tya ; get the step direction
|
|
jsr AdrAndStrb ; set the step direction
|
|
|
|
lda #rStepOffAdr ; see if last step pulse accepted
|
|
jsr AdrAndSense ; test the flag
|
|
ldx #1 ; 1 poll, must be right the first time
|
|
|
|
; The drive can accept step pulses spaced at 72µsec max. We turn step on, and the drive
|
|
; will turn it off when it is ready to accept the next step pulse. There appears to be
|
|
; an undocumented drive spec which causes the drive to ignore step pulses if they are sent
|
|
; less than 18µsec after the drive indicated that it was OK to sent the next step. To avoid
|
|
; this problem, we add an additional short (but not > 72µs) delay before sending each step.
|
|
|
|
SeekLoop jsr Sense ; see if OK to send a step pulse
|
|
beq SeekPoll ; wait until OK to send a step pulse
|
|
|
|
dec <StepCount ; decrement the loop counter
|
|
bmi SeekWait ; start waiting when no more left to do
|
|
|
|
ldx #4 ; ≈10µsec additional delay (to wait at least 18µsec)
|
|
StepDelay dex ; decrement delay count
|
|
bne StepDelay ; wait a short time before sending step pulse
|
|
|
|
assert rStepOffAdr=wStepOnAdr
|
|
jsr Pulse ; send a step pulse
|
|
ldx #10+1 ; allow 10 polls before timeout
|
|
|
|
SeekPoll dex ; decrement poll count
|
|
bne SeekLoop ; try again until count runs out
|
|
|
|
lda #cantStepErr ; couldn't send a step pulse
|
|
SeekError ldx <CurrentDrive ; get the active drive number
|
|
sta CylinderValid,x ; invalidate the cylinder
|
|
sta CurrentCylinder,x ; setup cylinder number too
|
|
SeekDone tax ; test error code, set flags
|
|
rts ; all done
|
|
|
|
SeekWait pla ; pop pcL
|
|
plx ; pop pcH
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
jsr WaitTMPBdone ; wait for prior waits to time out
|
|
phx ; push pcH
|
|
pha ; push pcL
|
|
ldx #>SeekDelay ; get high byte of Seek initial wait time
|
|
lda #<SeekDelay ; get low byte of Seek initial wait time
|
|
jsr StartReadyTimer ; start the timer
|
|
jsr LoadSWIMparams ; setup params based upon new cylinder
|
|
bne SeekError ; if couldn't load params
|
|
rts ; return with success
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Block To Physical Address'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr BlockToPhysAddr
|
|
; Inputs: CurrentDrive - active drive number
|
|
; LogicalBlockH/L - Logical Block Number
|
|
; Outputs: A - error status code
|
|
; n,z - based on value returned in A
|
|
; 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: X, Y, n, z, c
|
|
; Calls: none
|
|
; Called by: ReadWriteSetUp, SetupTrackInfo, SWIMFormat, SWIMFormatVerify
|
|
;
|
|
; 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)
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
BlockToPhysAddr proc
|
|
entry BlockXlateTable
|
|
entry BlockXlateHeadsPerCyl
|
|
entry BlockXlateSectsPerHead
|
|
entry BlockXlateSectsPerCyl
|
|
entry BlockXlateCylBias
|
|
entry BlockXlateBlockLimit
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
ldy <CurrentDrive ; get the active drive number
|
|
ldx DiskFormat,y ; see what format to read
|
|
ldy BlockXlateTable,x ; Get the translate table index
|
|
ldx <LogicalBlockL ; get low byte of block number
|
|
lda <LogicalBlockH ; get high byte of block number
|
|
sta <PhysSector ; update high byte of block number
|
|
bra @SearchStart ; start searching for the cylinder group
|
|
|
|
@SearchLoop txa ; get low byte of block number
|
|
sec ; setup carry for subtraction
|
|
sbc BlockXlateBlockLimit+0,y ; compare to / subtract limit
|
|
tax ; save updated block number
|
|
lda <PhysSector ; get high byte of block number
|
|
sbc BlockXlateBlockLimit+1,y ; compare to / subtract limit
|
|
bcc @SearchDone ; exit if block number below limit
|
|
sta <PhysSector ; update high byte of block number
|
|
tya ; point to next record (y := y + 5)
|
|
adc #5-1 ; (carry set from above)
|
|
tay ; y := y + 5
|
|
@SearchStart lda BlockXlateSectsPerHead,y ; see if end if list
|
|
bne @SearchLoop ; not the end, keep on searching
|
|
|
|
lda #paramErr ; indicate block number too big for this format
|
|
rts ; return with error status
|
|
|
|
@SearchDone lda BlockXlateSectsPerCyl,y ; get sectors per cylinder
|
|
sta <PhysSectsPerCyl ; save sectors per cylinder
|
|
lda BlockXlateSectsPerHead,y; get sectors per head
|
|
sta <PhysSectsPerHead ; save sectors per head
|
|
lda BlockXlateCylBias,y ; get starting cylinder number
|
|
sta <PhysCylBias ; save starting cylinder number
|
|
txa ; get low byte of block offset
|
|
adc BlockXlateBlockLimit+0,y ; undo subtract from loop
|
|
|
|
asl a ; shift to setup loop
|
|
sta <PhysCylinder ; Low byte of numerator, quotient
|
|
lda <PhysSector ; get high byte of block number
|
|
ldy #8-1 ; divide loop counter
|
|
@CylinderLoop rol a ; shift high byte of numerator
|
|
cpa <PhysSectsPerCyl ; compare to denominator
|
|
bcc @CylinderShift ; if N < D, don't subtract, just shift
|
|
sbc <PhysSectsPerCyl ; if N >= D, subtract denominator
|
|
@CylinderShift rol <PhysCylinder ; shift in quotient bit
|
|
dey ; decrement loop counter
|
|
bpl @CylinderLoop ; loop through all 8 bits
|
|
|
|
sta <PhysCylSector ; sector number relative to beginning of cylinder
|
|
sec ; setup carry for subtraction
|
|
@HeadLoop iny ; move on to next head
|
|
tax ; save sector number for exit
|
|
sbc <PhysSectsPerHead ; adjust sector number
|
|
bcs @HeadLoop ; if sector >= PhysSectsPerHead, loop
|
|
|
|
sty <PhysHead ; update head number
|
|
stx <PhysSector ; update sector number
|
|
lda <PhysCylinder ; get cylinder offset
|
|
adc <PhysCylBias ; bias by starting cylinder number
|
|
sta <PhysCylinder ; update cylinder number
|
|
lda #noErr ; indicate success
|
|
rts ; all done
|
|
endwith
|
|
endwith
|
|
endproc
|
|
|
|
seg 'constants'
|
|
BlockXlateTable proc
|
|
entry BlockXlateHeadsPerCyl
|
|
entry BlockXlateSectsPerHead
|
|
entry BlockXlateSectsPerCyl
|
|
entry BlockXlateCylBias
|
|
entry BlockXlateBlockLimit
|
|
|
|
dc.b UnknownTable-TableBase ; 0 - uncheckedFormat
|
|
dc.b UnknownTable-TableBase ; 1 - unknownFormat
|
|
dc.b UnknownTable-TableBase ; 2 - HD20Format
|
|
dc.b GCR400KTable-TableBase ; 3 - GCR400Kformat
|
|
dc.b GCR800KTable-TableBase ; 4 - GCR800Kformat
|
|
dc.b MFM720KTable-TableBase ; 5 - MFM720Kformat
|
|
dc.b MFM1440KTable-TableBase ; 6 - MFM1440Kformat
|
|
dc.b GCRonHDTable-TableBase ; 7 - GCRonHDformat
|
|
dc.b UnknownTable-TableBase ; 8 - reserved for future use
|
|
dc.b UnknownTable-TableBase ; 9 - reserved for future use
|
|
dc.b UnknownTable-TableBase ; 10 - reserved for future use
|
|
|
|
BlockXlateHeadsPerCyl
|
|
dc.b 1 ; 1 head per cylinder
|
|
TableBase
|
|
GCR400KTable
|
|
BlockXlateSectsPerHead
|
|
dc.b 12 ; 12 sectors per head
|
|
BlockXlateSectsPerCyl
|
|
dc.b 12*1 ; 12 sectors per cylinder (1 head)
|
|
BlockXlateCylBias
|
|
dc.b 0*16 ; starting at cylinder zero (first group of 16)
|
|
BlockXlateBlockLimit
|
|
dc.w 12*1*16 ; 12 sectors, 1 head, 16 cylinders
|
|
|
|
dc.b 11,11*1,1*16
|
|
dc.w 11*1*16
|
|
|
|
dc.b 10,10*1,2*16
|
|
dc.w 10*1*16
|
|
|
|
dc.b 9,9*1,3*16
|
|
dc.w 9*1*16
|
|
|
|
dc.b 8,8*1,4*16
|
|
dc.w 8*1*16
|
|
|
|
dc.b 0
|
|
|
|
|
|
dc.b 2 ; 2 head per cylinder
|
|
GCRonHDTable
|
|
GCR800KTable dc.b 12,12*2,0*16
|
|
dc.w 12*2*16
|
|
|
|
dc.b 11,11*2,1*16
|
|
dc.w 11*2*16
|
|
|
|
dc.b 10,10*2,2*16
|
|
dc.w 10*2*16
|
|
|
|
dc.b 9,9*2,3*16
|
|
dc.w 9*2*16
|
|
|
|
dc.b 8,8*2,4*16
|
|
dc.w 8*2*16
|
|
|
|
dc.b 0
|
|
|
|
|
|
dc.b 2 ; 2 head per cylinder
|
|
MFM720KTable dc.b 9,9*2,0*80
|
|
dc.w 9*2*80
|
|
|
|
dc.b 0
|
|
|
|
|
|
dc.b 2 ; 2 head per cylinder
|
|
MFM1440KTable dc.b 18,18*2,0*80
|
|
dc.w 18*2*80
|
|
|
|
dc.b 0
|
|
|
|
|
|
dc.b 1 ; 1 head per cylinder
|
|
UnknownTable dc.b 8,8*1,0*1 ; 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
|
|
endproc
|
|
title 'IOP SWIM Driver - Encoding / Decoding Tables'
|
|
|
|
seg 'PageConsts'
|
|
DiskDataXlate proc
|
|
|
|
; Nibblizing Table ($00 thru $3F converted to nibbles $96 thru $FF)
|
|
; table is repeated 4 times, since upper 2 bits are not masked to zero
|
|
|
|
entry EncodeGCRnibble
|
|
EncodeGCRnibble dc.b $96,$97,$9A,$9B ; x x 0 0 0 0 x x
|
|
dc.b $9D,$9E,$9F,$A6 ; x x 0 0 0 1 x x
|
|
dc.b $A7,$AB,$AC,$AD ; x x 0 0 1 0 x x
|
|
dc.b $AE,$AF,$B2,$B3 ; x x 0 0 1 1 x x
|
|
dc.b $B4,$B5,$B6,$B7 ; x x 0 1 0 0 x x
|
|
dc.b $B9,$BA,$BB,$BC ; x x 0 1 0 1 x x
|
|
dc.b $BD,$BE,$BF,$CB ; x x 0 1 1 0 x x
|
|
dc.b $CD,$CE,$CF,$D3 ; x x 0 1 1 1 x x
|
|
dc.b $D6,$D7,$D9,$DA ; x x 1 0 0 0 x x
|
|
dc.b $DB,$DC,$DD,$DE ; x x 1 0 0 1 x x
|
|
dc.b $DF,$E5,$E6,$E7 ; x x 1 0 1 0 x x
|
|
dc.b $E9,$EA,$EB,$EC ; x x 1 0 1 1 x x
|
|
dc.b $ED,$EE,$EF,$F2 ; x x 1 1 0 0 x x
|
|
dc.b $F3,$F4,$F5,$F6 ; x x 1 1 0 1 x x
|
|
dc.b $F7,$F9,$FA,$FB ; x x 1 1 1 0 x x
|
|
dc.b $FC,$FD,$FE,$FF ; x x 1 1 1 1 x x
|
|
|
|
dc.b $96,$97,$9A,$9B ; x x 0 0 0 0 x x
|
|
dc.b $9D,$9E,$9F,$A6 ; x x 0 0 0 1 x x
|
|
dc.b $A7,$AB,$AC,$AD ; x x 0 0 1 0 x x
|
|
dc.b $AE,$AF,$B2,$B3 ; x x 0 0 1 1 x x
|
|
dc.b $B4,$B5,$B6,$B7 ; x x 0 1 0 0 x x
|
|
dc.b $B9,$BA,$BB,$BC ; x x 0 1 0 1 x x
|
|
dc.b $BD,$BE,$BF,$CB ; x x 0 1 1 0 x x
|
|
dc.b $CD,$CE,$CF,$D3 ; x x 0 1 1 1 x x
|
|
dc.b $D6,$D7,$D9,$DA ; x x 1 0 0 0 x x
|
|
dc.b $DB,$DC,$DD,$DE ; x x 1 0 0 1 x x
|
|
dc.b $DF,$E5,$E6,$E7 ; x x 1 0 1 0 x x
|
|
dc.b $E9,$EA,$EB,$EC ; x x 1 0 1 1 x x
|
|
dc.b $ED,$EE,$EF,$F2 ; x x 1 1 0 0 x x
|
|
dc.b $F3,$F4,$F5,$F6 ; x x 1 1 0 1 x x
|
|
dc.b $F7,$F9,$FA,$FB ; x x 1 1 1 0 x x
|
|
dc.b $FC,$FD,$FE,$FF ; x x 1 1 1 1 x x
|
|
|
|
dc.b $96,$97,$9A,$9B ; x x 0 0 0 0 x x
|
|
dc.b $9D,$9E,$9F,$A6 ; x x 0 0 0 1 x x
|
|
dc.b $A7,$AB,$AC,$AD ; x x 0 0 1 0 x x
|
|
dc.b $AE,$AF,$B2,$B3 ; x x 0 0 1 1 x x
|
|
dc.b $B4,$B5,$B6,$B7 ; x x 0 1 0 0 x x
|
|
dc.b $B9,$BA,$BB,$BC ; x x 0 1 0 1 x x
|
|
dc.b $BD,$BE,$BF,$CB ; x x 0 1 1 0 x x
|
|
dc.b $CD,$CE,$CF,$D3 ; x x 0 1 1 1 x x
|
|
dc.b $D6,$D7,$D9,$DA ; x x 1 0 0 0 x x
|
|
dc.b $DB,$DC,$DD,$DE ; x x 1 0 0 1 x x
|
|
dc.b $DF,$E5,$E6,$E7 ; x x 1 0 1 0 x x
|
|
dc.b $E9,$EA,$EB,$EC ; x x 1 0 1 1 x x
|
|
dc.b $ED,$EE,$EF,$F2 ; x x 1 1 0 0 x x
|
|
dc.b $F3,$F4,$F5,$F6 ; x x 1 1 0 1 x x
|
|
dc.b $F7,$F9,$FA,$FB ; x x 1 1 1 0 x x
|
|
dc.b $FC,$FD,$FE,$FF ; x x 1 1 1 1 x x
|
|
|
|
dc.b $96,$97,$9A,$9B ; x x 0 0 0 0 x x
|
|
dc.b $9D,$9E,$9F,$A6 ; x x 0 0 0 1 x x
|
|
dc.b $A7,$AB,$AC,$AD ; x x 0 0 1 0 x x
|
|
dc.b $AE,$AF,$B2,$B3 ; x x 0 0 1 1 x x
|
|
dc.b $B4,$B5,$B6,$B7 ; x x 0 1 0 0 x x
|
|
dc.b $B9,$BA,$BB,$BC ; x x 0 1 0 1 x x
|
|
dc.b $BD,$BE,$BF,$CB ; x x 0 1 1 0 x x
|
|
dc.b $CD,$CE,$CF,$D3 ; x x 0 1 1 1 x x
|
|
dc.b $D6,$D7,$D9,$DA ; x x 1 0 0 0 x x
|
|
dc.b $DB,$DC,$DD,$DE ; x x 1 0 0 1 x x
|
|
dc.b $DF,$E5,$E6,$E7 ; x x 1 0 1 0 x x
|
|
dc.b $E9,$EA,$EB,$EC ; x x 1 0 1 1 x x
|
|
dc.b $ED,$EE,$EF,$F2 ; x x 1 1 0 0 x x
|
|
dc.b $F3,$F4,$F5,$F6 ; x x 1 1 0 1 x x
|
|
dc.b $F7,$F9,$FA,$FB ; x x 1 1 1 0 x x
|
|
dc.b $FC,$FD,$FE,$FF ; x x 1 1 1 1 x x
|
|
|
|
|
|
; transform [A7] [A6] [A5] [A4] [A3] [A2] [A1] [A0]
|
|
; to [00] [00] [A7] [A6] [00] [00] [00] [00]
|
|
|
|
entry EncodeGCRHighA
|
|
EncodeGCRHighA dcb.b 64,%00000000 ; 0 0 X X X X X X
|
|
dcb.b 64,%00010000 ; 0 1 X X X X X X
|
|
dcb.b 64,%00100000 ; 1 0 X X X X X X
|
|
dcb.b 64,%00110000 ; 1 1 X X X X X X
|
|
|
|
|
|
; transform [B7] [B6] [B5] [B4] [B3] [B2] [B1] [B0]
|
|
; to [00] [00] [00] [00] [B7] [B6] [00] [00]
|
|
|
|
entry EncodeGCRHighB
|
|
EncodeGCRHighB dcb.b 64,%00000000 ; 0 0 X X X X X X
|
|
dcb.b 64,%00000100 ; 0 1 X X X X X X
|
|
dcb.b 64,%00001000 ; 1 0 X X X X X X
|
|
dcb.b 64,%00001100 ; 1 1 X X X X X X
|
|
|
|
|
|
; transform [C7] [C6] [C5] [C4] [C3] [C2] [C1] [C0]
|
|
; to [00] [00] [00] [00] [00] [00] [C7] [C6]
|
|
|
|
entry EncodeGCRHighC
|
|
EncodeGCRHighC dcb.b 64,%00000000 ; 0 0 X X X X X X
|
|
dcb.b 64,%00000001 ; 0 1 X X X X X X
|
|
dcb.b 64,%00000010 ; 1 0 X X X X X X
|
|
dcb.b 64,%00000011 ; 1 1 X X X X X X
|
|
|
|
|
|
|
|
; Denibblizing Table (nibbles $96 thru $FF converted to $00 thru $3F)
|
|
; $FF means illegal nibble:
|
|
|
|
entry DecodeGCRnibble
|
|
DecodeGCRnibble dcb.b $94,$FF ; $00..$93 are illegal
|
|
dc.b $FF,$FF,$00,$01 ; , ,$96,$97
|
|
dc.b $FF,$FF,$02,$03 ; , ,$9A,$9B
|
|
dc.b $FF,$04,$05,$06 ; ,$9D,$9E,$9F
|
|
dc.b $FF,$FF,$FF,$FF ; , , ,
|
|
dc.b $FF,$FF,$07,$08 ; , ,$A6,$A7
|
|
dc.b $FF,$FF,$FF,$09 ; , , ,$AB
|
|
dc.b $0A,$0B,$0C,$0D ; $AC,$AD,$AE,$AF
|
|
dc.b $FF,$FF,$0E,$0F ; , ,$B2,$B3
|
|
dc.b $10,$11,$12,$13 ; $B4,$B5,$B6,$B7
|
|
dc.b $FF,$14,$15,$16 ; ,$B9,$BA,$BB
|
|
dc.b $17,$18,$19,$1A ; $BC,$BD,$BE,$BF
|
|
dc.b $FF,$FF,$FF,$FF ; , , ,
|
|
dc.b $FF,$FF,$FF,$FF ; , , ,
|
|
dc.b $FF,$FF,$FF,$1B ; , , ,$CB
|
|
dc.b $FF,$1C,$1D,$1E ; ,$CD,$CE,$CF
|
|
dc.b $FF,$FF,$FF,$1F ; , , ,$D3
|
|
dc.b $FF,$FF,$20,$21 ; , ,$D6,$D7
|
|
dc.b $FF,$22,$23,$24 ; ,$D9,$DA,$DB
|
|
dc.b $25,$26,$27,$28 ; $DC,$DD,$DE,$DF
|
|
dc.b $FF,$FF,$FF,$FF ; , , ,
|
|
dc.b $FF,$29,$2A,$2B ; ,$E5,$E6,$E7
|
|
dc.b $FF,$2C,$2D,$2E ; ,$E9,$EA,$EB
|
|
dc.b $2F,$30,$31,$32 ; $EC,$ED,$EE,$EF
|
|
dc.b $FF,$FF,$33,$34 ; , ,$F2,$F3
|
|
dc.b $35,$36,$37,$38 ; $F4,$F5,$F6,$F7
|
|
dc.b $FF,$39,$3A,$3B ; ,$F9,$FA,$FB
|
|
dc.b $3C,$3D,$3E,$3F ; $FC,$FD,$FE,$FF
|
|
|
|
|
|
; transform [00] [00] [A7] [A6] [B7] [B6] [C7] [C6]
|
|
; to [A7] [A6] [00] [00] [00] [00] [00] [00]
|
|
|
|
entry DecodeGCRHighA
|
|
DecodeGCRHighA dcb.b 16,%00000000 ; 0 0 0 0 X X X X
|
|
dcb.b 16,%01000000 ; 0 0 0 1 X X X X
|
|
dcb.b 16,%10000000 ; 0 0 1 0 X X X X
|
|
dcb.b 16,%11000000 ; 0 0 1 1 X X X X
|
|
|
|
|
|
; transform [00] [00] [A7] [A6] [B7] [B6] [C7] [C6]
|
|
; to [B7] [B6] [00] [00] [00] [00] [00] [00]
|
|
|
|
entry DecodeGCRHighB
|
|
DecodeGCRHighB dcb.b 4,%00000000 ; 0 0 0 0 0 0 X X
|
|
dcb.b 4,%01000000 ; 0 0 0 0 0 1 X X
|
|
dcb.b 4,%10000000 ; 0 0 0 0 1 0 X X
|
|
dcb.b 4,%11000000 ; 0 0 0 0 1 1 X X
|
|
dcb.b 4,%00000000 ; 0 0 0 1 0 0 X X
|
|
dcb.b 4,%01000000 ; 0 0 0 1 0 1 X X
|
|
dcb.b 4,%10000000 ; 0 0 0 1 1 0 X X
|
|
dcb.b 4,%11000000 ; 0 0 0 1 1 1 X X
|
|
dcb.b 4,%00000000 ; 0 0 1 0 0 0 X X
|
|
dcb.b 4,%01000000 ; 0 0 1 0 0 1 X X
|
|
dcb.b 4,%10000000 ; 0 0 1 0 1 0 X X
|
|
dcb.b 4,%11000000 ; 0 0 1 0 1 1 X X
|
|
dcb.b 4,%00000000 ; 0 0 1 1 0 0 X X
|
|
dcb.b 4,%01000000 ; 0 0 1 1 0 1 X X
|
|
dcb.b 4,%10000000 ; 0 0 1 1 1 0 X X
|
|
dcb.b 4,%11000000 ; 0 0 1 1 1 1 X X
|
|
|
|
|
|
; transform [00] [00] [A7] [A6] [B7] [B6] [C7] [C6]
|
|
; to [C7] [C6] [00] [00] [00] [00] [00] [00]
|
|
|
|
entry DecodeGCRHighC
|
|
DecodeGCRHighC dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 0 0 0 0 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 0 0 0 1 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 0 0 1 0 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 0 0 1 1 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 0 1 0 0 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 0 1 0 1 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 0 1 1 0 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 0 1 1 1 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 1 0 0 0 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 1 0 0 1 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 1 0 1 0 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 1 0 1 1 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 1 1 0 0 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 1 1 0 1 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 1 1 1 0 X X
|
|
dc.b %00000000,%01000000,%10000000,%11000000 ; 0 0 1 1 1 1 X X
|
|
|
|
|
|
entry SectorTemplates
|
|
SectorTemplates
|
|
|
|
entry GCRAddrMark
|
|
GCRAddrMark dc.b $D5,$AA,$96 ; GCR address mark byte sequence
|
|
assert GCRAddrMarkSize=(*-GCRAddrMark)
|
|
|
|
entry GCRAddrSlipMark
|
|
GCRAddrSlipMark dc.b $DE,$AA,$FF ; GCR address bit slip mark byte sequence
|
|
assert GCRAddrSlipMarkSize=(*-GCRAddrSlipMark)
|
|
|
|
entry GCRGapSync
|
|
GCRGapSync dc.b $FF,$3F,$CF,$F3,$FC,$FF ; GCR gap sync byte sequence
|
|
assert GCRGapSyncSize=(*-GCRGapSync)
|
|
|
|
entry GCRDataMark
|
|
GCRDataMark dc.b $D5,$AA,$AD ; GCR data mark byte sequence
|
|
assert GCRDataMarkSize=(*-GCRDataMark)
|
|
|
|
entry GCRDataSlipMark
|
|
GCRDataSlipMark dc.b $DE,$AA ; GCR data bit slip mark byte sequence
|
|
entry GCRGapValue
|
|
GCRGapValue dc.b $FF,$FF ; GCR gap is string of FF's
|
|
assert GCRDataSlipMarkSize=(*-GCRDataSlipMark)
|
|
|
|
entry GCRFmtFillValue
|
|
GCRFmtFillValue dc.b $96 ; GCR format data is string of 96's (00's)
|
|
|
|
|
|
entry MFMIndexMark
|
|
MFMIndexMark dc.b $C2,$C2,$C2,$FC ; MFM index mark byte sequence
|
|
assert MFMIndexMarkSize=(*-MFMIndexMark)
|
|
|
|
entry MFMAddrMark
|
|
MFMAddrMark dc.b $A1,$A1,$A1,$FE ; MFM address mark byte sequence
|
|
assert MFMAddrMarkSize=(*-MFMAddrMark)
|
|
|
|
entry MFMDataMark
|
|
MFMDataMark dc.b $A1,$A1,$A1,$FB ; MFM data mark byte sequence
|
|
assert MFMDataMarkSize=(*-MFMDataMark)
|
|
|
|
entry MFMGapValue
|
|
MFMGapValue dc.b $4E ; MFM gap is string of 4E's
|
|
|
|
entry MFMSyncValue
|
|
MFMSyncValue dc.b $00 ; MFM sync is string of 00's
|
|
|
|
entry MFMFmtFillValue
|
|
MFMFmtFillValue dc.b $F6 ; MFM format data is string of F6's
|
|
|
|
|
|
entry FmtSearchOrder
|
|
FmtSearchOrder dc.b GCR800Kformat ; try 800K first
|
|
dc.b MFM1440Kformat ; then 1440K
|
|
dc.b GCR400Kformat ; then 400K
|
|
dc.b MFM720Kformat ; then 720K
|
|
dc.b GCRonHDformat ; and finally 400K/800K on HD media
|
|
dc.b unknownFormat ; list terminator
|
|
|
|
entry FmtByteValues
|
|
FmtByteValues dc.b $22 ; 800K GCR (2 sided)
|
|
dc.b $02 ; 1440K (block size = 2*256)
|
|
dc.b $02 ; 400K GCR (1 sided)
|
|
dc.b $02 ; 720K (block size = 2*256)
|
|
dc.b $00 ; 400K/800K GCR on HD media (ignore)
|
|
|
|
entry FmtByteMasks
|
|
FmtByteMasks dc.b $20 ; 800K GCR, ignore interleave info
|
|
dc.b $FF ; 1440K, use whole byte
|
|
dc.b $20 ; 400K GCR, ignore interleave info
|
|
dc.b $FF ; 720K, use whole byte
|
|
dc.b $00 ; 400K/800K GCR on HD media, ignore byte
|
|
|
|
|
|
entry RequiredMedia
|
|
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
|
|
|
|
entry HeadSelectDecode
|
|
HeadSelectDecode
|
|
dc.b rRdData0Adr ; select head 0
|
|
dc.b rRdData1Adr ; select head 1
|
|
|
|
if (*-SectorTemplates) > 64 then
|
|
aerror 'DiskDataXlate constants overflowed page boundary'
|
|
elseif (*-SectorTemplates) < 64 then
|
|
dcb.b 64-(*-SectorTemplates),0 ; pad out rest of page
|
|
endif
|
|
endproc
|
|
title 'IOP SWIM Driver - Read Sector Header'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr ReadSectorHdr
|
|
; Inputs: CurrentDrive - active drive number
|
|
; SeekingCylinder - Desired cylinder number
|
|
; SeekingHead - Desired head number
|
|
; PhysSectsPerHead- Upper limit for sector numbers
|
|
; BaseBufferNum - Starting buffer number for this track
|
|
; Outputs: c - true if an error occured
|
|
; A - error status code (if carry set)
|
|
; n,z - based on value returned in A (if carry set)
|
|
; n,v - buffer status for this sector (if carry clear)
|
|
; 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
|
|
; SectHdrChkSumH/L- Checksum from sector header
|
|
; SectHdrHandshake- Handshake register after last byte
|
|
; SectHdrError - Error register after last byte
|
|
; AdrSearchCountH/L - number of bytes found before header
|
|
; SectBufferNum - Buffer number for the sector that was found
|
|
; DataBufPtr - Address of DATA buffer for this sector
|
|
; TagBufPtr - Address of TAG buffer for this sector
|
|
; CurSectsPerHead - copy of PhysSectsPerHead
|
|
; Destroys: X, Y, n, z, c
|
|
; Calls: ReadUnknownHdr, ReadGCRHdr, ReadLoDenMFMHdr, ReadHiDenMFMHdr
|
|
; Called by: SWIMRead, SWIMReadVerify, SWIMWrite, SWIMFormatVerify, SWIMGetRawData,
|
|
; PrefetchIntoCache, SetUpDrive, FmtGCRTrack
|
|
;
|
|
; Function: Reads the sector header information for the next sector
|
|
; found on the specified head, on the current cylinder.
|
|
;
|
|
; 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. Carry will be set,
|
|
; but the error status returned will be noErr, since the header
|
|
; was read correctly, but there is no buffer for the data.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
ReadSectorHdr proc
|
|
entry ReadHdrDecode
|
|
entry ReadHdrSetup
|
|
entry ReadUnknownHdr
|
|
entry ReadGCRHdr
|
|
entry ReadLoDenMFMHdr
|
|
entry ReadHiDenMFMHdr
|
|
entry SetupBufferPtrs
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
with SectorBuffers
|
|
|
|
stz <DataBufPtr+0 ; setup data buffer offset
|
|
ldy <CurrentDrive ; get the active drive number
|
|
ldx DiskFormat,y ; see what format to read
|
|
lda RequiredMedia,x ; see what media is required
|
|
cpa MediaKind,y ; see if we have correct media
|
|
bne ReadUnknownHdr ; if wrong kind of media
|
|
lda <PhysSectsPerHead ; get number of sectors per head
|
|
sta CurSectsPerHead ; remember PhysSectsPerHead for prefetching
|
|
txa ; get the DiskFormat
|
|
asl a ; compute the table index
|
|
tax ; load the index register
|
|
stx <SectHdrFmtIndex ; save for Read / Write SectorData
|
|
lda <WrongSectsLeft ; see how many sectors we can skip
|
|
cpa #WrongSectsMax/2 ; see if more than half used
|
|
blt @ReadHdr ; if so, don't consider skipping sectors
|
|
lda <ADBActivityMask ; see if ADB deserves priority
|
|
ldy #GiveADBTimeMask ; assume that it doesn't,
|
|
sty <ADBActivityMask ; give ADB priority next time
|
|
and <ADBActivity ; check against the ADB activity history
|
|
beq @ReadHdr ; if not, just read it
|
|
|
|
; ADB should be given some time, so read the header, and return a buffer status that
|
|
; indicates that the buffer has valid data and no I/O is requested for this sector.
|
|
|
|
jsr @ReadHdr ; read the sector header
|
|
bcs @exit ; if error, return the error status
|
|
assert ReadDataValid=$80 ; sign bit is valid bit
|
|
assert IORequested=$40 ; overflow bit is requested bit
|
|
clv ; indicate that no I/O is requested
|
|
lda #ReadDataValid ; indicate that the buffer is valid
|
|
@exit rts ; all done
|
|
|
|
@ReadHdr jmp (ReadHdrDecode,x) ; dispatch to the routine
|
|
|
|
ReadUnknownHdr stz <AdrSearchCountH ; indicate that no bytes were searched
|
|
stz <AdrSearchCountL ; indicate that no bytes were searched
|
|
lda #noAdrMkErr ; unsupported format, return with error
|
|
sec ; set carry to indicate error
|
|
rts ; all done
|
|
|
|
ReadHdrSetup ldy <SeekingHead ; get the desired head number
|
|
lda HeadSelectDecode,y ; get the head select command
|
|
jsr AdrAndSense ; select the disk head
|
|
ReStartRead lda rError ; read and clear any pending errors
|
|
lda #StartAction+WriteMode ; prepare to clear action and write mode
|
|
sta wZeroes ; clear action and write mode (prepare for read)
|
|
lda #ClearFIFO ; prepare to clear the FIFO
|
|
sta wOnes ; toggle it to one
|
|
sta wZeroes ; and back to zero to clear it
|
|
lda rError ; read and clear errors again
|
|
lda #StartAction ; prepare to start reading
|
|
sta wOnes ; start shifting in the read data
|
|
rts
|
|
title 'IOP SWIM Driver - Read Sector Header GCR'
|
|
|
|
ReadGCRHdr jsr ReadHdrSetup ; select the head and start the data transfer
|
|
stz <SectHdrChkSumH ; high byte of checksum is always 0 in GCR
|
|
|
|
|
|
; see if we can read any data, by trying to read 3 bytes
|
|
|
|
; When reading immediatly after a write, the drive may suppress read data for
|
|
; up to 620µsec, so we will wait at least that long for the first byte to be valid.
|
|
; We look for 3 bytes @16.34µsec per byte brings the total wait to 669µsec.
|
|
; The polling loop takes 11 clocks per iteration @1.9584MHz, which is 5.617µsec.
|
|
; So we compute the timeout as 669/5.617 = 119 iterations, and add in another 10%
|
|
; for good measure to get 131. It doesn't hurt for it to be too big, it would just
|
|
; take longer to get noNybErr which should never occur unless there is a hardware error.
|
|
|
|
ldx #131 ; timeout counter
|
|
ldy #3 ; bytes to find counter
|
|
@pollAnyBytes lda rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bmi @gotAnyByte ; if byte is ready
|
|
dex ; dec timeout
|
|
bne @pollAnyBytes ; poll for the byte
|
|
stz <AdrSearchCountL ; update lower byte of the search counter
|
|
stz <AdrSearchCountH ; update upper byte of the search counter
|
|
lda #noNybErr ; if timed out searching for a nibble
|
|
bra @ErrorReturn ; no bytes found, return with error
|
|
|
|
@gotAnyByte lda rData ; get the data byte, to empty the FIFO
|
|
dey ; update count of bytes left to find
|
|
bne @pollAnyBytes ; wait for more bytes
|
|
|
|
; now search for the address mark byte sequence
|
|
|
|
lda #>-AddrSearchMax ; high byte of search byte count
|
|
sta <AdrSearchCountH ; initialize the timeout counter
|
|
lda #<-AddrSearchMax ; low byte of search byte count
|
|
@markSearch ldx #0 ; index into address mark sequence
|
|
@nextMark ldy GCRAddrMark,x ; get expected byte value
|
|
ina ; update lower byte of the timeout counter
|
|
bne @pollAddrMark ; check the address byte
|
|
inc <AdrSearchCountH ; update upper byte of the timeout counter
|
|
bne @pollAddrMark ; if address mark not found on disk
|
|
|
|
@AdrMarkTimeOut lda #<AddrSearchMax+3 ; count of bytes searched before error
|
|
sta <AdrSearchCountL ; update lower byte of the search counter
|
|
lda #>AddrSearchMax+3 ; count of bytes searched before error
|
|
sta <AdrSearchCountH ; update upper byte of the search counter
|
|
lda #noAdrMkErr ; address mark not found within timeout period
|
|
@ErrorReturn ldx rError ; read error reg for logic analyzer
|
|
ldy #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
tax ; set flags based upon error code
|
|
sty wZeroes ; clear action and write mode (read done)
|
|
sec ; set carry to indicate error
|
|
rts ; return with error
|
|
|
|
@pollAddrMark bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollAddrMark ; loop until byte is ready
|
|
cpy rData ; see if correct byte found
|
|
bne @markSearch ; if not, point back to start of search string
|
|
inx ; point to next search byte
|
|
cpx #GCRAddrMarkSize ; see if all bytes searched
|
|
blt @nextMark ; keep searching if not the final byte
|
|
* sec ; carry is left set by CPX above
|
|
adc #<AddrSearchMax-1 ; update count of bytes preceeding header
|
|
sta <AdrSearchCountL ; update lower byte of the search counter
|
|
lda <AdrSearchCountH ; get upper byte of the search counter
|
|
adc #>AddrSearchMax-1 ; update count of bytes preceeding header
|
|
sta <AdrSearchCountH ; update upper byte of the search counter
|
|
|
|
; get the fields of the sector header
|
|
|
|
@pollCylByte bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollCylByte ; loop until byte is ready
|
|
ldx rData ; get the low cylinder number byte
|
|
lda DecodeGCRnibble,x ; denibblize it
|
|
bmi @ChkSumError ; return checksum error if illegal encoding
|
|
asl a ; [0] [C5] [C4] [C3] [C2] [C1] [C0] [0]
|
|
asl a ; [C5] [C4] [C3] [C2] [C1] [C0] [0] [0]
|
|
sta <SectHdrCylinder ; update low byte of cylinder number
|
|
lda DecodeGCRnibble,x ; denibblize it again, initialize the checksum
|
|
|
|
@pollSectByte bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollSectByte ; loop until byte is ready
|
|
ldx rData ; get the sector number byte
|
|
ldy DecodeGCRnibble,x ; denibblize it
|
|
sty <SectHdrSector ; update sector number
|
|
eor DecodeGCRnibble,x ; add it to checksum
|
|
bmi @ChkSumError ; return checksum error if illegal encoding
|
|
cpy <PhysSectsPerHead ; see if it is in range
|
|
blt @pollHeadByte ; keep going if in range
|
|
|
|
jsr @pollHeadByte ; read rest of header
|
|
bcs @TooBigDone ; if any other error, return it instead
|
|
lda #noErr ; header read OK, but sect num too big
|
|
sec ; set carry to indicate error
|
|
@TooBigDone rts ; all done
|
|
|
|
@ChkSumError lda #badCksmErr ; sector header checksum error
|
|
bra @ErrorReturn ; return with checksum error
|
|
|
|
@pollHeadByte bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollHeadByte ; loop until byte is ready
|
|
ldx rData ; get the Head number (and high bits of cyl)
|
|
ldy DecodeGCRnibble,x ; denibblize it
|
|
bmi @ChkSumError ; return checksum error if illegal encoding
|
|
sty <SectHdrHead ; [0] [0] [H] [C10] [C9] [C8] [C7] [C6]
|
|
eor DecodeGCRnibble,x ; add it to checksum
|
|
lsr <SectHdrHead ; [0] [0] [0] [H] [C10] [C9] [C8] [C7]
|
|
ror <SectHdrCylinder ; [C6] [C5] [C4] [C3] [C2] [C1] [C0] [0]
|
|
|
|
@pollFmtByte bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollFmtByte ; loop until byte is ready
|
|
ldx rData ; get the Format Kind
|
|
ldy DecodeGCRnibble,x ; denibblize it
|
|
bmi @ChkSumError ; return checksum error if illegal encoding
|
|
sty <SectHdrFmtKind ; update Format Kind
|
|
eor DecodeGCRnibble,x ; add it to checksum
|
|
lsr <SectHdrHead ; [0] [0] [0] [0] [H] [C10] [C9] [C8]
|
|
ror <SectHdrCylinder ; [C7] [C6] [C5] [C4] [C3] [C2] [C1] [C0]
|
|
|
|
@pollChkSumByte bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollChkSumByte ; loop until byte is ready
|
|
ldx rData ; get the checksum
|
|
ldy DecodeGCRnibble,x ; denibblize it
|
|
sty <SectHdrChkSumL ; update the checksum
|
|
eor DecodeGCRnibble,x ; add it to checksum
|
|
bne @ChkSumError ; if checksum is bad
|
|
|
|
lda <SectHdrHead ; [0] [0] [0] [0] [H] [C10] [C9] [C8]
|
|
cpa #%00001000 ; carry := head
|
|
and #%00000111 ; [0] [0] [0] [0] [0] [C10] [C9] [C8]
|
|
rol a ; [0] [0] [0] [0] [C10] [C9] [C8] [H]
|
|
sta <SectHdrHead ; [0] [0] [0] [0] [C10] [C9] [C8] [H]
|
|
|
|
; we assume that cylinder numbers use at most 8 bits, and that the upper
|
|
; 3 bits will be zero for correct cylinders, so when we compare with the
|
|
; head, we will also be checking the upper 3 bits of cylinder for zero.
|
|
eor <SeekingHead ; see if we found the correct head
|
|
bne @SeekError ; if wrong head or cylinder
|
|
lda <SectHdrCylinder ; get the cylinder number we found
|
|
eor <SeekingCylinder ; see if we found the correct cylinder
|
|
bne @SeekError ; if wrong cylinder
|
|
|
|
@checkSlipMarks
|
|
lda <SectHdrSector ; get the sector we found
|
|
* clc ; setup for addition (cleared by ROL above)
|
|
adc <BaseBufferNum ; compute the buffer number for this sector
|
|
sta <SectBufferNum ; save the buffer number for this sector
|
|
asl a ; data buffers take 2 pages
|
|
* clc ; setup for addition (cleared by ASL above)
|
|
adc #>DataBuffers ; compute the data buffer page number
|
|
sta <DataBufPtr+1 ; setup data buffer page number
|
|
|
|
; Blindly read the first slip mark byte (58 clocks after previous byte read)
|
|
lda rData ; get the first slip mark byte
|
|
cpa GCRAddrSlipMark+0 ; check expected first slip mark byte
|
|
bne @SlipMarkError ; return with error if bad slip mark
|
|
|
|
lda GCRAddrSlipMark+1 ; get expected second slip mark byte
|
|
@pollSlipMark2 ldx rHandshake ; get final handshake register
|
|
assert Dat1Byte=$80
|
|
bpl @pollSlipMark2 ; loop until second slip byte is ready
|
|
cpa rData ; check the second slip mark byte
|
|
bne @SlipMarkError ; return with error if bad slip mark
|
|
stx <SectHdrHandshake ; save the handshake reg
|
|
ldx rError ; get the final error register
|
|
stx <SectHdrError ; save the Error reg
|
|
|
|
ldx <SectBufferNum ; get the buffer number for this sector
|
|
lda TagPtrs,x ; get the tag buffer pointer for this sector
|
|
asl a ; unpack 9th bit into carry
|
|
sta <TagBufPtr ; setup low byte of tag pointer
|
|
lda #0 ; setup to add in tag addr carry
|
|
adc #>TagBuffers ; add in starting page of tag buffers
|
|
sta <TagBufPtr+1 ; setup page number
|
|
* clc ; clear carry to indicate success
|
|
|
|
assert ReadDataValid=$80 ; sign bit is valid bit
|
|
assert IORequested=$40 ; overflow bit is requested bit
|
|
bit BufferStatus,x ; see if the buffer is needed
|
|
rts ; all done
|
|
|
|
@SlipMarkError ldx rError ; read error reg for logic analyzer
|
|
ldy #StartAction+WriteMode ; prepare to clear action and write mode
|
|
sty wZeroes ; clear action and write mode (read done)
|
|
lda #badBtSlpErr ; sector header slip mark error
|
|
sec ; set carry to indicate error
|
|
rts ; return with error
|
|
|
|
@SeekError jsr @checkSlipMarks ; make sure the header is valid first
|
|
bcs @SeekErrorDone ; other errors have priority over seek error
|
|
lda #seekErr ; if valid sector header, report the seek error
|
|
sec ; set carry to indicate error
|
|
@SeekErrorDone rts ; return with error
|
|
title 'IOP SWIM Driver - Read Sector Header MFM'
|
|
|
|
ReadLoDenMFMHdr ldx <CurrentDrive ; get the active drive number
|
|
lda DriveKind,x ; check drive kind
|
|
cpa #DSMFMGCRDriveKind ; see if drive supports MFM formats
|
|
beq ReadMFMHdr ; if correct drive, join common code
|
|
lda #noAdrMkErr ; can't read MFM address marks with this drive
|
|
sec ; set carry to indicate error
|
|
rts ; return with error
|
|
|
|
ReadHiDenMFMHdr
|
|
ReadMFMHdr jsr ReadHdrSetup ; select the head and start the data transfer
|
|
|
|
; search for the address mark byte sequence, with timeout.
|
|
|
|
lda #>-AddrSearchMax ; high byte of search byte count
|
|
sta <AdrSearchCountH ; initialize the timeout counter
|
|
ldy #<-AddrSearchMax ; low byte of search byte count
|
|
ldx #0 ; index into address marks table
|
|
lda #noNybErr ; no data found on disk
|
|
sta <SectHdrError ; default error if no bytes found
|
|
|
|
@pollAnyBytes lda #MarkInFIFO ; test for mark byte also
|
|
bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bmi @gotAnyByte ; if byte is ready
|
|
|
|
bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bmi @gotAnyByte ; if byte is ready
|
|
|
|
bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bmi @gotAnyByte ; if byte is ready
|
|
|
|
nop ; extra 2 clocks to make loop 32 clocks (16µsec)
|
|
txa ; see if any bytes of mark found
|
|
beq @updateTimeout ; if not, just keep on polling
|
|
|
|
@ReStartSearch jsr ReStartRead ; flush the FIFO, and restart search for mark
|
|
ldx #0 ; index into address marks table
|
|
lda #noAdrMkErr ; address mark not found within timeout period
|
|
sta <SectHdrError ; error code if bytes were found
|
|
bra @updateTimeout ; start counting again
|
|
|
|
@gotAnyByte beq @gotDataByte ; if not a mark byte
|
|
lda rMark ; get the mark byte
|
|
eor MFMAddrMark,x ; check against expected value
|
|
bne @ReStartSearch ; if not a match, start over
|
|
inx ; point to next search byte
|
|
cpx #MFMAddrMarkSize ; see if this was the final byte
|
|
beq @ReStartSearch ; final byte should not be a mark byte
|
|
@updateTimeout iny ; update lower byte of the timeout counter
|
|
bne @pollAnyBytes ; check for next byte
|
|
inc <AdrSearchCountH ; update upper byte of the timeout counter
|
|
bne @pollAnyBytes ; check for next byte
|
|
|
|
@AdrMarkTimeOut lda #<AddrSearchMax ; count of bytes searched before error
|
|
sta <AdrSearchCountL ; update lower byte of the search counter
|
|
lda #>AddrSearchMax ; count of bytes searched before error
|
|
sta <AdrSearchCountH ; update upper byte of the search counter
|
|
lda <SectHdrError ; get proper error code
|
|
@ErrorReturn ldx rError ; read error reg for logic analyzer
|
|
ldy #StartAction+WriteMode ; prepare to clear action and write mode
|
|
tax ; set flags based upon error code
|
|
sty wZeroes ; clear action and write mode (read done)
|
|
sec ; set carry to indicate error
|
|
rts ; return with error
|
|
|
|
@gotDataByte lda rData ; get the data byte
|
|
cpa MFMAddrMark,x ; check against expected value
|
|
bne @ReStartSearch ; if not a match, start over
|
|
cpx #MFMAddrMarkSize-1 ; see if this was the final byte
|
|
blt @ReStartSearch ; only final byte should be a data byte
|
|
|
|
tya ; get lower byte of the search counter
|
|
* sec ; carry is left set by CPX above
|
|
adc #<AddrSearchMax-1-4 ; update count of bytes preceeding header
|
|
sta <AdrSearchCountL ; update lower byte of the search counter
|
|
lda <AdrSearchCountH ; get upper byte of the search counter
|
|
adc #>AddrSearchMax-1-4 ; update count of bytes preceeding header
|
|
sta <AdrSearchCountH ; update upper byte of the search counter
|
|
|
|
; get the fields of the sector header
|
|
|
|
ldx #0 ; timeout counter
|
|
lda #MarkInFIFO ; test for mark byte also
|
|
@pollCylByte dex ; decrement timeout counter
|
|
beq @HdrByteTimeOut ; if timed out waiting for a byte
|
|
bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollCylByte ; loop until byte is ready
|
|
bne @ChkSumError ; if mark byte, report as checksum error
|
|
ldy rData ; get the low cylinder number byte
|
|
sty <SectHdrCylinder ; update low byte of cylinder number
|
|
|
|
@pollHeadByte dex ; decrement timeout counter
|
|
beq @HdrByteTimeOut ; if timed out waiting for a byte
|
|
bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollHeadByte ; loop until byte is ready
|
|
bne @ChkSumError ; if mark byte, report as checksum error
|
|
ldy rData ; get the Head number
|
|
sty <SectHdrHead ; update the Head number
|
|
cpy <SeekingHead ; see if we found the correct head
|
|
bne @SeekError ; if wrong head or cylinder
|
|
ldy <SectHdrCylinder ; get the cylinder number we found
|
|
cpy <SeekingCylinder ; see if we found the correct cylinder
|
|
beq @pollSectByte ; if correct cylinder
|
|
|
|
@SeekError jsr @pollSectByte ; make sure the header is valid first
|
|
bcs @SeekErrorDone ; other errors have priority over seek error
|
|
lda #seekErr ; if valid sector header, report the seek error
|
|
sec ; set carry to indicate error
|
|
@SeekErrorDone rts ; return with error
|
|
|
|
@HdrByteTimeOut lda #noNybErr ; timeout while polling for a byte
|
|
bra @ErrorReturn ; return with timeout error
|
|
|
|
@ChkSumError lda #badCksmErr ; sector header checksum error
|
|
bra @ErrorReturn ; return with checksum error
|
|
|
|
@pollSectByte dex ; decrement timeout counter
|
|
beq @HdrByteTimeOut ; if timed out waiting for a byte
|
|
bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollSectByte ; loop until byte is ready
|
|
bne @ChkSumError ; if mark byte, report as checksum error
|
|
ldy rData ; get the sector number
|
|
dey ; adjust to zero based numbering
|
|
sty <SectHdrSector ; update sector number
|
|
cpy <PhysSectsPerHead ; see if it is in range
|
|
blt @pollFmtByte ; keep going if in range
|
|
|
|
jsr @pollFmtByte ; read rest of header
|
|
bcs @TooBigDone ; if any other error, return it instead
|
|
lda #noErr ; header read OK, but sect num too big
|
|
sec ; set carry to indicate error
|
|
@TooBigDone rts ; all done
|
|
|
|
@pollFmtByte dex ; decrement timeout counter
|
|
beq @HdrByteTimeOut ; if timed out waiting for a byte
|
|
bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollFmtByte ; loop until byte is ready
|
|
bne @ChkSumError ; if mark byte, report as checksum error
|
|
ldy rData ; get the (block size) Format Kind
|
|
sty <SectHdrFmtKind ; update Format Kind
|
|
|
|
@pollChkSumH dex ; decrement timeout counter
|
|
beq @HdrByteTimeOut ; if timed out waiting for a byte
|
|
bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollChkSumH ; loop until byte is ready
|
|
bne @ChkSumError ; if mark byte, report as checksum error
|
|
ldy rData ; get the first checksum byte
|
|
sty <SectHdrChkSumH ; update the first checksum byte
|
|
|
|
ldy #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
@pollChkSumL dex ; decrement timeout counter
|
|
beq @HdrByteTimeOut ; if timed out waiting for a byte
|
|
lda rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollChkSumL ; loop until byte is ready
|
|
ldx rData ; get the second checksum byte
|
|
sty wZeroes ; clear action and write mode (read done)
|
|
bit #MarkInFIFO ; test for mark byte
|
|
bne @ChkSumError ; if mark byte, report as checksum error
|
|
stx <SectHdrChkSumL ; update the second checksum byte
|
|
ldx rError ; get the final error register
|
|
stx <SectHdrError ; save the Error reg
|
|
sta <SectHdrHandshake ; save the handshake reg
|
|
and #rErrorValid+CRCNotZero ; check for errors
|
|
bne @ChkSumError ; if bad CRC, return with header checksum error
|
|
|
|
SetupBufferPtrs lda <SectHdrSector ; get the sector we found
|
|
clc ; setup for addition
|
|
adc <BaseBufferNum ; compute the buffer number for this sector
|
|
sta <SectBufferNum ; save the buffer number for this sector
|
|
tax ; setup for indexing by buffer number
|
|
asl a ; data buffers take 2 pages
|
|
* clc ; setup for addition (cleared by ASL above)
|
|
adc #>DataBuffers ; compute the data buffer page number
|
|
sta <DataBufPtr+1 ; setup data buffer page number
|
|
|
|
lda TagPtrs,x ; get the tag buffer pointer for this sector
|
|
asl a ; unpack 9th bit into carry
|
|
sta <TagBufPtr ; setup low byte of tag pointer
|
|
lda #0 ; setup to add in tag addr carry
|
|
adc #>TagBuffers ; add in starting page of tag buffers
|
|
sta <TagBufPtr+1 ; setup page number
|
|
* clc ; clear carry to indicate success
|
|
|
|
assert ReadDataValid=$80 ; sign bit is valid bit
|
|
assert IORequested=$40 ; overflow bit is requested bit
|
|
bit BufferStatus,x ; see if the buffer is needed
|
|
rts ; all done
|
|
endwith
|
|
endwith
|
|
endwith
|
|
endproc
|
|
|
|
seg 'WordConsts'
|
|
ReadHdrDecode proc
|
|
dc.w ReadUnknownHdr ; 0 - uncheckedFormat
|
|
dc.w ReadUnknownHdr ; 1 - unknownFormat
|
|
dc.w ReadUnknownHdr ; 2 - HD20Format
|
|
dc.w ReadGCRHdr ; 3 - GCR400Kformat
|
|
dc.w ReadGCRHdr ; 4 - GCR800Kformat
|
|
dc.w ReadLoDenMFMHdr ; 5 - MFM720Kformat
|
|
dc.w ReadHiDenMFMHdr ; 6 - MFM1440Kformat
|
|
dc.w ReadGCRHdr ; 7 - GCRonHDformat
|
|
dc.w ReadUnknownHdr ; 8 - reserved for future use
|
|
dc.w ReadUnknownHdr ; 9 - reserved for future use
|
|
dc.w ReadUnknownHdr ; 10 - reserved for future use
|
|
endproc
|
|
title 'IOP SWIM Driver - Read Sector Data'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr ReadSectorData
|
|
; Inputs: CurrentDrive - active drive number
|
|
; TagBufPtr - pointer to 12 byte tag buffer
|
|
; DataBufPtr - pointer to 512 byte data buffer
|
|
; Expects to be called after ReadSectorHdr
|
|
; Outputs: A - error status code
|
|
; n,z - based on value returned in A
|
|
; Destroys: X, Y, n, z, c
|
|
; Calls: ReadUnknownData, ReadGCRData, ReadMFMData, RdWrGCRonHDData
|
|
; Called by: SWIMRead, SWIMReadVerify, SWIMWrite, SWIMFormatVerify,
|
|
; SWIMGetRawData, PrefetchIntoCache
|
|
;
|
|
; Function: Reads the sector Tag and Data information for the current
|
|
; sector on the selected head, on the current cylinder.
|
|
;
|
|
; NOTE: The data buffer must be page aligned, and the tag
|
|
; buffer must not span across pages.
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
ReadSectorData proc
|
|
entry ReadDataDecode
|
|
entry ReadUnknownData
|
|
entry ReadGCRData
|
|
entry RdWrGCRonHDData
|
|
entry ReadMFMData
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
ldx <SectHdrFmtIndex ; get index from ReadSectorHdr
|
|
jmp (ReadDataDecode,x) ; dispatch to the routine
|
|
|
|
ReadUnknownData lda #noDtaMkErr ; unsupported format, return with error
|
|
rts ; all done
|
|
title 'IOP SWIM Driver - Read Sector Data - GCR'
|
|
|
|
; we enter with action still set, and read mode, from ReadGCRHdr, but
|
|
; the FIFO has probably overrun, and errors may have been set.
|
|
; The shift register must be left running, in order to properly sync
|
|
; on the GCR sync pattern that preceeds the data mark sequence.
|
|
|
|
RdWrGCRonHDData ldx rError ; read error reg for logic analyzer
|
|
ldy #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
sty wZeroes ; clear action and write mode (read done)
|
|
lda #<gcrOnMfmErr ; indicate improperly formatted disk
|
|
rts ; return with error
|
|
|
|
ReadGCRData lda rData ; empty garbage byte out of the FIFO,
|
|
lda rError ; read and clear errors
|
|
|
|
; now search for the data mark byte sequence
|
|
|
|
assert DataSearchMax<128 ; it fits in 1 byte
|
|
lda #-DataSearchMax ; search byte count
|
|
@markSearch ldx #0 ; index into address marks table
|
|
@nextMark ldy GCRDataMark,x ; get expected byte value
|
|
ina ; update the timeout counter
|
|
beq @DataMarkTimeOut ; error if count expired
|
|
|
|
@pollDataMark bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollDataMark ; loop until byte is ready
|
|
cpy rData ; see if correct byte found
|
|
bne @markSearch ; if not, point back to start of search string
|
|
inx ; point to next search byte
|
|
cpx #GCRDataMarkSize ; see if all bytes searched
|
|
blt @nextMark ; keep searching if not the final byte
|
|
|
|
; get the fields of the data mark
|
|
|
|
lda #TagSize/3 ; 12 tag bytes, read 3 per loop
|
|
sta <LoopCountL ; initialize tag loop counter
|
|
stz <GCRCkSumA ; initialize checksum A
|
|
stz <GCRCkSumB ; initialize checksum B
|
|
lda #0 ; initialize checksum C
|
|
|
|
@pollSectByte bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollSectByte ; loop until byte is ready
|
|
ldx rData ; get the sector number byte
|
|
ldy DecodeGCRnibble,x ; denibblize it
|
|
bmi @ChkSumError ; checksum error if illegal encoding
|
|
@CheckSectNum cpy <SectHdrSector ; check sector number
|
|
beq @ReadTagStart ; if match read tag, else didn't find mark
|
|
|
|
@DataMarkTimeOut
|
|
lda #noDtaMkErr ; data mark not found within timeout period
|
|
bra @ErrorReturn ; return with error status
|
|
title 'IOP SWIM Driver - Read Sector Data - GCR Tags'
|
|
|
|
; read the tag data bytes
|
|
|
|
@ReadTagLoop ldx rData ; get the upper bits nibble
|
|
ldy DecodeGCRnibble,x ; denibblize it
|
|
bmi @ChkSumError ; checksum error if illegal encoding
|
|
|
|
; rotate high bit of CkSumC into carry and low bit of CkSumC
|
|
cpa #$80 ; carry := CheckSumC [7]
|
|
rol a ; [6] [5] [4] [3] [2] [1] [0] [7]
|
|
sta <GCRCkSumC ; update CheckSum C
|
|
|
|
ldx rData ; get ByteA'
|
|
lda DecodeGCRnibble,x ; denibblize it
|
|
if CheckEncodings then
|
|
bmi @ChkSumError ; checksum error if illegal encoding
|
|
else
|
|
* bmi @ChkSumError ; no time to check for illegal encoding
|
|
endif
|
|
eor DecodeGCRHighA,y ; insert upper 2 bits of ByteA'
|
|
eor <GCRCkSumC ; ByteA := ByteA' xor CkSumC
|
|
sta (<TagBufPtr) ; store the first byte
|
|
adc <GCRCkSumA ; CkSumA := CkSumA + ByteA + carry
|
|
sta <GCRCkSumA ; update CheckSum A
|
|
inc <TagBufPtr ; update buffer pointer
|
|
|
|
ldx rData ; get ByteB'
|
|
lda DecodeGCRnibble,x ; denibblize it
|
|
if CheckEncodings then
|
|
bmi @ChkSumError ; checksum error if illegal encoding
|
|
else
|
|
* bmi @ChkSumError ; no time to check for illegal encoding
|
|
endif
|
|
eor DecodeGCRHighB,y ; insert upper 2 bits of ByteB'
|
|
eor <GCRCkSumA ; ByteB := ByteB' xor CkSumA
|
|
sta (<TagBufPtr) ; store the second byte
|
|
adc <GCRCkSumB ; CkSumB := CkSumB + ByteB + carry
|
|
sta <GCRCkSumB ; update CheckSum B
|
|
inc <TagBufPtr ; update buffer pointer
|
|
|
|
ldx rData ; get ByteC'
|
|
lda DecodeGCRnibble,x ; denibblize it
|
|
if CheckEncodings then
|
|
bmi @ChkSumError ; checksum error if illegal encoding
|
|
else
|
|
* bmi @ChkSumError ; no time to check for illegal encoding
|
|
endif
|
|
eor DecodeGCRHighC,y ; insert upper 2 bits of ByteC'
|
|
eor <GCRCkSumB ; ByteC := ByteC' xor CkSumB
|
|
sta (<TagBufPtr) ; store the third byte
|
|
adc <GCRCkSumC ; CkSumC := CkSumC + ByteC + carry
|
|
|
|
dec <LoopCountL ; update loop counter
|
|
beq @ReadDataStart ; if done with tag, read data field
|
|
|
|
inc <TagBufPtr ; update buffer pointer
|
|
|
|
@ReadTagStart bit rHandshake ; see if 2 bytes are available
|
|
assert Dat2Bytes=$40
|
|
bvs @ReadTagLoop ; read the bytes if they are available
|
|
|
|
ldx #BytePollMax ; setup timeout counter
|
|
@PollTagLoop bit rHandshake ; check again
|
|
assert Dat2Bytes=$40
|
|
bvs @ReadTagLoop ; read the bytes if they are available
|
|
dex ; decrement timeout
|
|
bne @PollTagLoop ; poll until full, or timeout
|
|
|
|
@NoNybError lda #noNybErr ; bytes not coming in fast enough
|
|
@ErrorReturn ldx rError ; read error reg for logic analyzer
|
|
ldy #StartAction+WriteMode ; prepare to clear action and write mode
|
|
sty wZeroes ; clear action and write mode (read done)
|
|
tax ; set flags based upon error code
|
|
rts ; return with error
|
|
|
|
@ChkSumError lda #badDCksum ; sector data checksum error
|
|
bra @ErrorReturn ; return with checksum error
|
|
title 'IOP SWIM Driver - Read Sector Data - GCR Data'
|
|
|
|
; read the sector data bytes
|
|
|
|
@ReadDataLoop ldx rData ; get the upper bits nibble
|
|
ldy DecodeGCRnibble,x ; denibblize it
|
|
bmi @ChkSumError ; checksum error if illegal encoding
|
|
|
|
; rotate high bit of CkSumC into carry and low bit of CkSumC
|
|
cpa #$80 ; carry := CheckSumC [7]
|
|
rol a ; [6] [5] [4] [3] [2] [1] [0] [7]
|
|
sta <GCRCkSumC ; update CheckSum C
|
|
|
|
ldx rData ; get ByteA'
|
|
lda DecodeGCRnibble,x ; denibblize it
|
|
if CheckEncodings then
|
|
bmi @ChkSumError ; checksum error if illegal encoding
|
|
else
|
|
* bmi @ChkSumError ; no time to check for illegal encoding
|
|
endif
|
|
eor DecodeGCRHighA,y ; insert upper 2 bits of ByteA'
|
|
eor <GCRCkSumC ; ByteA := ByteA' xor CkSumC
|
|
sta (<DataBufPtr) ; store the byte
|
|
adc <GCRCkSumA ; CkSumA := CkSumA + ByteA + carry
|
|
sta <GCRCkSumA ; update CheckSum A
|
|
|
|
ldx rData ; get ByteB' (empty FIFO before page cross delay)
|
|
|
|
inc <DataBufPtr ; update buffer pointer
|
|
beq @DataCrossPage ; if last byte of first page
|
|
@CrossPageDone
|
|
|
|
lda DecodeGCRnibble,x ; denibblize it
|
|
if CheckEncodings then
|
|
bmi @ChkSumError ; checksum error if illegal encoding
|
|
else
|
|
* bmi @ChkSumError ; no time to check for illegal encoding
|
|
endif
|
|
eor DecodeGCRHighB,y ; insert upper 2 bits of ByteB'
|
|
eor <GCRCkSumA ; ByteB := ByteB' xor CkSumA
|
|
sta (<DataBufPtr) ; store the byte
|
|
adc <GCRCkSumB ; CkSumB := CkSumB + ByteB + carry
|
|
sta <GCRCkSumB ; update CheckSum B
|
|
inc <DataBufPtr ; update buffer pointer
|
|
beq @ReadDataExit ; if last data byte transferred
|
|
|
|
ldx rData ; get ByteC'
|
|
lda DecodeGCRnibble,x ; denibblize it
|
|
if CheckEncodings then
|
|
bmi @ChkSumError ; checksum error if illegal encoding
|
|
else
|
|
* bmi @ChkSumError ; no time to check for illegal encoding
|
|
endif
|
|
eor DecodeGCRHighC,y ; insert upper 2 bits of ByteC'
|
|
eor <GCRCkSumB ; ByteC := ByteC' xor CkSumB
|
|
sta (<DataBufPtr) ; store the byte
|
|
adc <GCRCkSumC ; CkSumC := CkSumC + ByteC + carry
|
|
inc <DataBufPtr ; update buffer pointer
|
|
|
|
@ReadDataStart bit rHandshake ; see if 2 bytes are available
|
|
assert Dat2Bytes=$40
|
|
bvs @ReadDataLoop ; read the bytes if they are available
|
|
|
|
ldx #BytePollMax ; setup timeout counter
|
|
@PollDataLoop bit rHandshake ; check again
|
|
assert Dat2Bytes=$40
|
|
bvs @ReadDataLoop ; read the bytes if they are available
|
|
dex ; decrement timeout
|
|
bne @PollDataLoop ; poll until full, or timeout
|
|
bra @NoNybError ; return error if no bytes found
|
|
|
|
@DataCrossPage inc <DataBufPtr+1 ; update upper byte of buffer pointer
|
|
bra @CrossPageDone ; re-join loop
|
|
title 'IOP SWIM Driver - Read Sector Data - GCR Tail'
|
|
|
|
@ToChkSumError bra @ChkSumError ; jump through here for short displacements
|
|
|
|
@ReadDataExit ldx rData ; get the upper bits of checksum nibbles
|
|
ldy DecodeGCRnibble,x ; denibblize it
|
|
bmi @ToChkSumError ; checksum error if illegal encoding
|
|
inc <DataBufPtr+1 ; update upper byte of data buffer pointer
|
|
inc <TagBufPtr ; update tag buffer pointer
|
|
|
|
@pollCkSumBytes bit rHandshake ; see if 2 bytes are available
|
|
assert Dat2Bytes=$40
|
|
bvc @pollCkSumBytes ; loop until 2 bytes are ready
|
|
|
|
ldx rData ; get checksum byte A
|
|
lda DecodeGCRnibble,x ; denibblize it
|
|
bmi @ToChkSumError ; checksum error if illegal encoding
|
|
eor DecodeGCRHighA,y ; insert upper 2 bits
|
|
eor <GCRCkSumA ; compare it to computed value
|
|
bne @ToChkSumError ; checksum error if doesn't match
|
|
|
|
ldx rData ; get checksum byte B
|
|
lda DecodeGCRnibble,x ; denibblize it
|
|
bmi @ToChkSumError ; checksum error if illegal encoding
|
|
eor DecodeGCRHighB,y ; insert upper 2 bits
|
|
eor <GCRCkSumB ; compare it to computed value
|
|
bne @ToChkSumError ; checksum error if doesn't match
|
|
|
|
ldx rData ; get checksum byte C
|
|
lda DecodeGCRnibble,x ; denibblize it
|
|
bmi @ToChkSumError ; checksum error if illegal encoding
|
|
eor DecodeGCRHighC,y ; insert upper 2 bits
|
|
eor <GCRCkSumC ; compare it to computed value
|
|
bne @ToChkSumError ; checksum error if doesn't match
|
|
|
|
|
|
ldy #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
@pollSlipMarks bit rHandshake ; see if 2 bytes are available
|
|
assert Dat2Bytes=$40
|
|
bvc @pollSlipMarks ; loop until bytes are ready
|
|
|
|
lda rData ; get the first slip mark byte
|
|
eor GCRDataSlipMark+0 ; compare to expected value
|
|
bne @SlipMarkError ; return with error if bad slip mark
|
|
|
|
lda rData ; get the second slip mark byte
|
|
eor GCRDataSlipMark+1 ; compare to expected value
|
|
beq @SlipMarksDone ; return with success if good slip mark
|
|
@SlipMarkError lda #badDBtSlp ; sector data slip mark error
|
|
@SlipMarksDone ldx rError ; read error reg for logic analyzer
|
|
sty wZeroes ; clear action and write mode (read done)
|
|
tax ; set flags based upon error code
|
|
rts ; all done
|
|
title 'IOP SWIM Driver - Read Sector Data - MFM'
|
|
|
|
entry PollDMADone
|
|
|
|
ReadMFMData lda rError ; read and clear any pending errors
|
|
lda #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
sta wZeroes ; clear action and write mode (prepare for read)
|
|
lda #ClearFIFO ; prepare to clear the FIFO
|
|
sta wOnes ; toggle it to one
|
|
sta wZeroes ; and back to zero to clear it
|
|
lda rError ; read and clear errors again
|
|
lda #StartAction ; prepare to start reading
|
|
sta wOnes ; start shifting in the read data
|
|
|
|
; fill in the tag info (while SWIM is looking for "bytes of zeros" sync field)
|
|
|
|
lda <SectHdrCylinder ; get cylinder number from sector header
|
|
ldy #0 ; index to first tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
lda <SectHdrHead ; get head number from sector header
|
|
iny ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
lda <SectHdrSector ; get head number from sector header
|
|
ina ; convert back to one based numbering
|
|
iny ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
lda <SectHdrFmtKind ; get block size from sector header
|
|
ora #$20 ; modified to look double sided
|
|
iny ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
lda <SectHdrChkSumH ; get first checksum byte from sector header
|
|
iny ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
lda <SectHdrChkSumL ; get second checksum byte from sector header
|
|
iny ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
lda <SectHdrError ; get rError value from sector header read
|
|
iny ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
lda <SectHdrHandshake ; get rHandshake value from sector header read
|
|
iny ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
if UseDmaHardware then
|
|
lda #>(BlockSize-1) ; high byte of DMA count
|
|
sta DMA1XferCountH ; setup high byte of transfer count
|
|
lda #<(BlockSize-1) ; low byte of DMA count
|
|
sta DMA1XferCountL ; setup low byte of transfer count
|
|
lda #1 ; first byte of buffer is polled, rest is DMA
|
|
sta DMA1RAMAddressL ; setup low byte of buffer address
|
|
lda <DataBufPtr+1 ; get high byte of buffer address
|
|
sta DMA1RAMAddressH ; setup high byte of buffer address
|
|
endif
|
|
|
|
; now search for the data mark byte sequence
|
|
|
|
assert DataSearchMax<128 ; it fits in 1 byte
|
|
lda #DataSearchMax-1 ; search byte count
|
|
sta <LoopCountL ; initialize the byte search count
|
|
ldx #0 ; index into data marks table
|
|
@GetNextByte ldy #BytePollMax ; approximate loop count for 1 byte time
|
|
lda #MarkInFIFO ; test for mark byte also
|
|
@PollByteLoop bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bmi @GotNextByte ; process the byte if one was found
|
|
dey ; decrement byte timeout counter
|
|
bne @PollByteLoop ; keep polling if count not expired
|
|
dec <LoopCountL ; update the search count
|
|
bpl @GetNextByte ; loop if still more bytes to search
|
|
|
|
@ByteTimeOut lda #noNybErr ; timeout while polling for a byte
|
|
@ErrorReturn ldx rError ; read error reg for logic analyzer
|
|
ldy #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
sty wZeroes ; clear action and write mode (read done)
|
|
tax ; set flags based upon error code
|
|
rts ; return with error
|
|
|
|
@ChkSumError lda #badDCkSum ; sector data checksum error
|
|
bra @ErrorReturn ; return with checksum error
|
|
|
|
|
|
@GotNextByte stz <LoopCountL ; force any new timeouts to cause errors
|
|
beq @GotDataByte ; if not a mark byte
|
|
lda rMark ; get the mark byte
|
|
eor MFMDataMark,x ; check against expected value
|
|
bne @NoDataMark ; if not a match, report error
|
|
inx ; point to next search byte
|
|
cpx #MFMDataMarkSize ; see if this was the final byte
|
|
blt @GetNextByte ; get next byte if not final byte
|
|
; ; final byte should not be a mark byte
|
|
@NoDataMark lda #noDtaMkErr ; data mark not found within timeout period
|
|
bra @ErrorReturn ; return with error status
|
|
|
|
@GotDataByte lda rData ; get the data byte
|
|
eor MFMDataMark,x ; check against expected value
|
|
bne @NoDataMark ; if not a match, report error
|
|
cpx #MFMDataMarkSize-1 ; see if this was the final byte
|
|
blt @NoDataMark ; only final byte should be a data byte
|
|
|
|
|
|
; get the sector data
|
|
|
|
if UseDmaHardware then
|
|
ldx #<(rData*IOA1+DMADIR1+DMAEN1)
|
|
@WaitSectorByte bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @WaitSectorByte ; loop until byte is ready
|
|
lda rData ; get the data byte
|
|
stx DMA1Control ; start the DMA transfer
|
|
sta (<DataBufPtr) ; store the first byte
|
|
|
|
lda <DMARetryStatus ; see if any DMA timeouts on this track
|
|
bne @pollDMA ; if so, don't allow interrupts
|
|
|
|
lda #<DMAxferDelay ; delay to give ADB some time during DMA xfer
|
|
ldx #>DMAxferDelay ; high byte
|
|
jsr StartReadyTimer ; start the timer
|
|
pla ; pop pcL
|
|
plx ; pop pcH
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
jsr WaitTMPBdone ; give ADB some time while DMA xfer in progress
|
|
phx ; push pcH
|
|
pha ; push pcL
|
|
lda #0 ; allow interrupts during poll
|
|
@pollDMA jsr PollDMADone ; wait for transfer to complete
|
|
bne @ErrorReturn ; return retry status if DMA timeout
|
|
else
|
|
ldx #BlockSize/256 ; page counter 2 pages = 512 bytes
|
|
|
|
@GetSectorByte lda #MarkInFIFO ; test for mark byte also
|
|
bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bmi @GotSectorByte ; if byte is ready
|
|
|
|
ldy #BytePollMax ; approximate loop count for 1 byte time
|
|
@SectorByteLoop dey ; decrement byte timeout counter
|
|
beq @ByteTimeOut ; return with timeout error if count expired
|
|
bit rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @SectorByteLoop ; keep polling if byte not ready
|
|
|
|
@GotSectorByte bne @ChkSumError ; if mark byte, report as checksum error
|
|
lda rData ; get the data byte
|
|
sta (<DataBufPtr) ; store the byte
|
|
inc <DataBufPtr ; update buffer pointer
|
|
bne @GetSectorByte ; keep going if not page crossing
|
|
|
|
inc <DataBufPtr+1 ; update buffer pointer page number
|
|
dex ; update page counter
|
|
bne @GetSectorByte ; keep going if not the last page
|
|
endif
|
|
|
|
|
|
; get the sector checksum
|
|
|
|
ldx #BytePollMax*2 ; poll timeout counter for 2 bytes
|
|
@pollChkSumH dex ; decrement timeout counter
|
|
beq @ByteTimeOut ; if timed out waiting for a byte
|
|
lda rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollChkSumH ; loop until byte is ready
|
|
bit #MarkInFIFO ; test for mark byte
|
|
bne @ChkSumError ; if mark byte, report as checksum error
|
|
|
|
lda rData ; get the first checksum byte
|
|
ldy #8 ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
@pollChkSumL dex ; decrement timeout counter
|
|
beq @ByteTimeOut ; if timed out waiting for a byte
|
|
lda rHandshake ; see if byte is available
|
|
assert Dat1Byte=$80
|
|
bpl @pollChkSumL ; loop until byte is ready
|
|
bit #MarkInFIFO ; test for mark byte
|
|
bne @ChkSumError ; if mark byte, report as checksum error
|
|
tax ; save the handshake register value
|
|
|
|
lda rData ; get the second checksum byte
|
|
iny ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
lda rError ; get the final error register
|
|
iny ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
txa ; get the final handshake register
|
|
iny ; point to next tag byte
|
|
sta (<TagBufPtr),y ; save it in the tag bytes
|
|
|
|
ldy #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
and #rErrorValid\
|
|
+CRCNotZero ; check for errors
|
|
beq @ChkSumDone ; if checksum is good
|
|
lda #badDCkSum ; sector data checksum error
|
|
@ChkSumDone sty wZeroes ; clear action and write mode (read done)
|
|
rts ; all done
|
|
endwith
|
|
endwith
|
|
endproc
|
|
|
|
seg 'WordConsts'
|
|
ReadDataDecode proc
|
|
dc.w ReadUnknownData ; 0 - uncheckedFormat
|
|
dc.w ReadUnknownData ; 1 - unknownFormat
|
|
dc.w ReadUnknownData ; 2 - HD20Format
|
|
dc.w ReadGCRData ; 3 - GCR400Kformat
|
|
dc.w ReadGCRData ; 4 - GCR800Kformat
|
|
dc.w ReadMFMData ; 5 - MFM720Kformat
|
|
dc.w ReadMFMData ; 6 - MFM1440Kformat
|
|
dc.w RdWrGCRonHDData ; 7 - GCRonHDformat
|
|
dc.w ReadUnknownData ; 8 - reserved for future use
|
|
dc.w ReadUnknownData ; 9 - reserved for future use
|
|
dc.w ReadUnknownData ; 10 - reserved for future use
|
|
endproc
|
|
title 'IOP SWIM Driver - Write Utilities'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr WriteByte
|
|
; Inputs: A - byte to be written
|
|
; SwimWriteReg - pointer SWIM register to use
|
|
; Outputs: none
|
|
; Destroys: n, v, z
|
|
; Calls: ??
|
|
; Called by: ??
|
|
;
|
|
; Function: Waits for room for a byte to be available in the FIFO, and
|
|
; then writes the byte to the FIFO, using the SWIM register
|
|
; specified by SwimWriteReg.
|
|
;_______________________________________________________________________
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr WriteRepeated
|
|
; Inputs: A - byte to be written
|
|
; X - number of times to be repeated
|
|
; SwimWriteReg - pointer SWIM register to use
|
|
; Outputs: X - zero
|
|
; Destroys: n, v, z
|
|
; Calls: ??
|
|
; Called by: ??
|
|
;
|
|
; Function: Waits for room for a byte to be available in the FIFO, and
|
|
; repeatedly writes the byte to the FIFO, using the SWIM register
|
|
; specified by SwimWriteReg.
|
|
;_______________________________________________________________________
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr WriteTemplate
|
|
; Inputs: A - byte to be written
|
|
; X - number of bytes to write
|
|
; Y - starting offset from SectorTemplates
|
|
; SwimWriteReg - pointer SWIM register to use
|
|
; Outputs: X - zero
|
|
; Y - incremented to point past last byte written
|
|
; Destroys: A, n, v, z
|
|
; Calls: ??
|
|
; Called by: ??
|
|
;
|
|
; Function: Waits for room for a byte to be available in the FIFO, and
|
|
; repeatedly writes the byte string template to the FIFO, using
|
|
; the SWIM register specified by SwimWriteReg.
|
|
;_______________________________________________________________________
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr WriteMFMCRC
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: n, v, z
|
|
; Calls: ??
|
|
; Called by: ??
|
|
;
|
|
; Function: Waits for room for a byte to be available in the FIFO, and
|
|
; then writes to the wCRC register causing 2 CRC bytes to be
|
|
; written in MFM mode.
|
|
;_______________________________________________________________________
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr WriteDataBuffer
|
|
; Inputs: A - byte to be written before buffer
|
|
; X - low byte of byte count
|
|
; Y - high byte of byte count
|
|
; DataBufPtr - starting PAGE address of data buffer
|
|
; SwimWriteReg - pointer SWIM register to use
|
|
; Outputs: A - error status code
|
|
; n,z - based on value returned in A
|
|
; Destroys: n, v, z
|
|
; Calls: ??
|
|
; Called by: ??
|
|
;
|
|
; Function: Writes the contents of the data buffer, whose address and
|
|
; length are passed in as parameters, to the disk.
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
WriteUtilities proc
|
|
entry WriteByte
|
|
entry WriteRepeated
|
|
entry WriteTemplate
|
|
entry WriteMFMCRC
|
|
entry WriteDataExit
|
|
entry WriteDataBuffer
|
|
entry PollDMADone
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
WrRepeatedPoll bit rHandshake ; see if we have room in the FIFO
|
|
assert Dat1Byte=$80
|
|
bpl WrRepeatedPoll ; wait until there is room for one byte
|
|
sta (<SwimWriteReg) ; write the byte
|
|
WriteRepeated dex ; decrement the byte count
|
|
bne WrRepeatedPoll ; loop if not the last byte
|
|
|
|
; otherwise fall into WriteByte for last byte
|
|
WriteByte bit rHandshake ; see if we have room in the FIFO
|
|
assert Dat1Byte=$80
|
|
bpl WriteByte ; wait until there is room for one byte
|
|
sta (<SwimWriteReg) ; write the byte
|
|
rts ; all done
|
|
|
|
|
|
WriteTemplate lda SectorTemplates,y ; get the byte
|
|
iny ; point to next byte of the template
|
|
dex ; decrement the byte count
|
|
beq WriteByte ; exit through WriteByte for last byte
|
|
WrTemplatePoll bit rHandshake ; see if we have room in the FIFO
|
|
assert Dat1Byte=$80
|
|
bpl WrTemplatePoll ; wait until there is room for one byte
|
|
sta (<SwimWriteReg) ; write the byte
|
|
bra WriteTemplate ; loop through all of the bytes
|
|
|
|
|
|
WriteMFMCRC bit rHandshake ; see if room is available in the FIFO
|
|
assert Dat1Byte=$80
|
|
bpl WriteMFMCRC ; wait until there is room
|
|
stz wCRC ; writing any value causes 2 CRC bytes to write
|
|
rts ; all done
|
|
|
|
|
|
WriteDataExit ldx #4 ; 4 bytes to flush shiftreg and 2 FIFOs
|
|
jsr WriteRepeated ; write the gap bytes (only 1 will get to disk)
|
|
|
|
lda rHandshake ; prepare to check rErrorValid bit
|
|
ldy #StartAction+WriteMode ; prepare to clear action and write mode
|
|
sty wZeroes ; clear action and write mode (write done)
|
|
ldx rError ; read and clear the final error register
|
|
and #rErrorValid ; check handshake register for any errors
|
|
beq @Done ; if no errors during write
|
|
lda #wrUnderrun ; report any write error as an underrun error
|
|
@Done rts ; all done
|
|
|
|
|
|
if UseDmaHardware then
|
|
WriteDataBuffer stx DMA1XferCountL ; setup low byte of transfer count
|
|
sty DMA1XferCountH ; setup high byte of transfer count
|
|
stz DMA1RAMAddressL ; setup low byte of buffer address (page aligned)
|
|
ldx <DataBufPtr+1 ; get high byte of buffer address
|
|
stx DMA1RAMAddressH ; setup high byte of buffer address
|
|
ldx #<(wData*IOA1+DMAEN1)
|
|
@pollLoop bit rHandshake ; see if room is available in the FIFO
|
|
assert Dat1Byte=$80
|
|
bpl @pollLoop ; wait until there is room
|
|
sta (<SwimWriteReg) ; write it to the disk
|
|
stx DMA1Control ; start the DMA transfer
|
|
|
|
lda <DMARetryStatus ; see if any DMA timeouts on this track
|
|
bne @pollDMA ; if so, don't allow interrupts
|
|
|
|
lda #<DMAxferDelay ; delay to give ADB some time during DMA xfer
|
|
ldx #>DMAxferDelay ; high byte
|
|
jsr StartReadyTimer ; start the timer
|
|
pla ; pop pcL
|
|
plx ; pop pcH
|
|
ldy <ReadyTMPB ; get the TMPB index
|
|
jsr WaitTMPBdone ; give ADB some time while DMA xfer in progress
|
|
phx ; push pcH
|
|
pha ; push pcL
|
|
lda #0 ; allow interrupts during poll
|
|
@pollDMA bra PollDMADone ; wait for transfer to complete (and return status)
|
|
else
|
|
WriteDataBuffer bit rHandshake ; see if room is available in the FIFO
|
|
assert Dat1Byte=$80
|
|
bpl WriteDataBuffer ; wait until there is room
|
|
sta (<SwimWriteReg) ; write it to the disk
|
|
txa ; test the low byte of the count
|
|
beq @decCountH ; if zero, decrement the high byte
|
|
@nextByte lda (<DataBufPtr) ; get the byte
|
|
inc <DataBufPtr ; update buffer pointer
|
|
bne @pollLoop ; keep going if not page crossing
|
|
inc <DataBufPtr+1 ; update buffer pointer page number
|
|
@pollLoop bit rHandshake ; see if room is available in the FIFO
|
|
assert Dat1Byte=$80
|
|
bpl @pollLoop ; wait until there is room
|
|
sta (<SwimWriteReg) ; write it to the disk
|
|
dex ; decrement low byte of count
|
|
bne @nextByte ; loop through all of the bytes
|
|
@decCountH dey ; decrement high byte of count
|
|
bpl @nextByte ; loop until entire count exhausted
|
|
lda #noErr ; indicate success
|
|
rts ; all done
|
|
endif
|
|
endwith
|
|
endwith
|
|
endproc
|
|
if UseDmaHardware then
|
|
title 'IOP SWIM Driver - Poll DMA Done'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr PollDMADone
|
|
; Inputs: A - if non-zero don't enable interrupts
|
|
; - if zero enable interrupts for all but last page
|
|
; Outputs: A - DMARetryStatus (non-zero if timeout during DMA)
|
|
; Destroys: A, X, Y, n, v, c, z
|
|
; Calls: HandleDMA1Int
|
|
; Called by: ReadMFMData, WriteDataBuffer
|
|
;
|
|
; Function: Waits for a DMA transfer to complete, enabling interrupts
|
|
; if desired. Checks to make sure that bytes to transfer
|
|
; periodicaly, returning an error if DMA appears hung.
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
PollDMADone proc
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
tax ; test ints disable flag
|
|
bne @noInts ; if so, don't allow interrupts
|
|
bra @checkDMA ; see how the DMA is doing (no ints first time)
|
|
|
|
@addrChanged ldx #0 ; reset timeout counter
|
|
ldy DMA1XferCountL ; get the low byte of the DMA address counter
|
|
@intsLoop cli ; let interrupts have a chance
|
|
sei ; lock out interrupts
|
|
@checkDMA lda <DMARetryStatus ; see if DMA timeout while waiting
|
|
bne @done ; if so, return the DMA Retry Status
|
|
lda DMA1XferCountH ; see if more than a page left
|
|
beq @noInts ; if not, shut down interrupts, loop for end of xfer
|
|
cpy DMA1XferCountL ; see if address counter changed
|
|
bne @addrChanged ; if so, reset the timeout
|
|
inx ; update timeout
|
|
bne @intsLoop ; if no timeout, allow ints and keep polling
|
|
bra HandleDMA1Int ; if timed out, kill the xfer and return the error
|
|
|
|
@noInts lda #DMA1INT
|
|
bit InterruptReg ; see if xfer complete
|
|
bne HandleDMA1Int ; if so, it's too soon, no ints next time
|
|
|
|
ldx #$00 ; reset timeout
|
|
@waitNext ldy DMA1XferCountL ; get low byte of count
|
|
@waitLoop bit InterruptReg ; see if xfer complete
|
|
bne @waitDone ; if so, the wait is over
|
|
dex ; update timeout count
|
|
bne @waitLoop ; if no timeout, update keep waiting
|
|
cpy DMA1XferCountL ; see if count changed
|
|
bne @waitNext ; if count change, restart timeout
|
|
bra HandleDMA1Int ; DMA timed out, kill it
|
|
|
|
@waitDone stz DMA1Control ; stop the DMA transfer
|
|
sta InterruptReg ; clear the interrupt
|
|
|
|
lda #noErr ; indicate success
|
|
@done rts ; all done
|
|
|
|
export HandleDMA1Int
|
|
HandleDMA1Int stz DMA1Control ; stop the DMA transfer
|
|
lda #DMA1INT
|
|
sta InterruptReg ; clear the interrupt
|
|
lda #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
sta wZeroes ; clear action and write mode (write done)
|
|
lda rError ; read and clear the final error register
|
|
inc <DMARetryStatus ; indicate that DMA request needs to be retried
|
|
lda <DMARetryStatus ; return the retry count as the status
|
|
cpa #2 ; see if second offense
|
|
blt @done ; if not, return count as error code
|
|
lda #noNybErr ; otherwise indicate timeout while polling for a byte
|
|
@done rts ; return from the interrupt
|
|
endwith
|
|
endwith
|
|
endproc
|
|
endif
|
|
title 'IOP SWIM Driver - Write GCR Data Loop'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jmp WrGCRdataStart
|
|
;
|
|
; Function: This is the internal data transfer loop used by
|
|
; WriteGCRData. In order for this loop to execute in the
|
|
; correct number of cycles, it must reside within page zero,
|
|
; and it contains some self modifing code (no opcodes are
|
|
; modified, just immediate operands, and the low byte of
|
|
; an absolute address operand.) This code is designed to
|
|
; run in lock step with the swim chip, and does not contain
|
|
; any code to check the handshake register, because if we
|
|
; are in sync when we enter, we will stay in sync throughout
|
|
; the loop.
|
|
;_______________________________________________________________________
|
|
|
|
seg 'zcode'
|
|
WrGCRdataLoop proc
|
|
entry WrGCRdataStart ; entry label
|
|
entry WriteGCRtail ; exit label
|
|
entry GCRWrByteC ; modified low byte of an abs address
|
|
entry GCRWrCkSumA ; modified immediate field
|
|
entry GCRWrCkSumB ; modified immediate field
|
|
entry GCRWrCkSumC ; modified immediate field
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
eor (<DataBufPtr) ; ByteC' := ByteC xor CkSumB
|
|
sta <GCRWrByteC+1 ; save ByteC'
|
|
|
|
lda EncodeGCRHighA,x ; get high bits of ByteA'
|
|
ora EncodeGCRHighB,y ; insert high bits of ByteB'
|
|
GCRWrByteC ora EncodeGCRHighC ; insert high bits of ByteC'
|
|
; T=+65 FIFO1 = empty, FIFO2 = empty, ShiftReg = ByteC'
|
|
sta <HighNibble+1 ; save high bits nibble
|
|
HighNibble lda EncodeGCRnibble ; encode the high bits nibble
|
|
sta wData ; write out the high bits nibble
|
|
; T=+76 FIFO1 = empty, FIFO2 = HiBits, ShiftReg = ByteC'
|
|
|
|
lda EncodeGCRnibble,x ; encode the ByteA' nibble
|
|
sta wData ; write out the ByteA' nibble
|
|
; T=+84 FIFO1 = ByteA', FIFO2 = HiBits, ShiftReg = ByteC'
|
|
|
|
lda <GCRWrByteC+1 ; restore ByteC'
|
|
eor <GCRWrCkSumB+1 ; remove CkSumB to get ByteC
|
|
GCRWrCkSumC adc #<GCRWrCkSumC+1 ; CkSumC := CkSumC + ByteC + carry
|
|
cpa #$80 ; carry := CheckSumC [7]
|
|
rol a ; [6] [5] [4] [3] [2] [1] [0] [7]
|
|
; T=+96 FIFO1 = empty, FIFO2 = ByteA', ShiftReg = HiBits
|
|
sta <GCRWrCkSumC+1 ; update checksum C
|
|
inc <DataBufPtr ; point to next data byte
|
|
|
|
; T=+8 FIFO1 = empty, FIFO2 = ByteA', ShiftReg = HiBits
|
|
WrGCRdataStart eor (<DataBufPtr) ; ByteA' := ByteA xor CkSumC
|
|
tax ; save ByteA'
|
|
eor <GCRWrCkSumC+1 ; remove CkSumC to get ByteA
|
|
GCRWrCkSumA adc #GCRWrCkSumA+1 ; CkSumA := CkSumA + ByteA + carry
|
|
sta <GCRWrCkSumA+1 ; update checksum A
|
|
inc DataBufPtr ; point to next data byte
|
|
bne @CrossPageDone ; if not cross page
|
|
inc <DataBufPtr+1 ; update high byte on page crossing
|
|
@CrossPageDone
|
|
;T=+127 FIFO1 = empty, FIFO2 = empty, ShiftReg = ByteA'
|
|
; on the bread board, accesses to the swim chip cause the cycle to be
|
|
; stretched by 0.25 clocks, and since we make 4 accesses in this loop,
|
|
; the total stretch time is 1.0 clocks, which makes this loop exactly
|
|
; 128 clocks. (except for the one iteration which crosses a page boundary,
|
|
; which is 132 clocks)
|
|
|
|
; T=0…7 FIFO1 = empty, FIFO2 = empty, ShiftReg = ByteA'
|
|
lda EncodeGCRnibble,y ; encode the ByteB' nibble
|
|
sta wData ; write out the ByteB' nibble
|
|
; T=+8 FIFO1 = empty, FIFO2 = ByteB', ShiftReg = ByteA'
|
|
|
|
ldy <GCRWrByteC+1 ; get ByteC'
|
|
lda EncodeGCRnibble,y ; encode the ByteC' nibble
|
|
sta wData ; write out the ByteC' nibble
|
|
; T=+19 FIFO1 = ByteC', FIFO2 = ByteB', ShiftReg = ByteA'
|
|
|
|
lda <GCRWrCkSumA+1 ; restore checksum A
|
|
eor (<DataBufPtr) ; ByteB' := ByteB xor CkSumA
|
|
tay ; save ByteB'
|
|
eor <GCRWrCkSumA+1 ; remove CkSumA to get ByteB
|
|
; T=+32 FIFO1 = empty, FIFO2 = ByteC', ShiftReg = ByteB'
|
|
GCRWrCkSumB adc #GCRWrCkSumB+1 ; CkSumB := CkSumB + ByteB + carry
|
|
sta <GCRWrCkSumB+1 ; update checksum B
|
|
inc <DataBufPtr ; point to next data byte
|
|
bne WrGCRdataLoop ; loop until end of page 2 (512 bytes)
|
|
jmp WriteGCRtail ; write out last 2 bytes and checksum
|
|
; T=+51 FIFO1 = empty, FIFO2 = ByteC', ShiftReg = ByteB'
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Write Sector Data'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr WriteSectorData
|
|
; Inputs: CurrentDrive - active drive number
|
|
; TagBufPtr - pointer to 12 byte tag buffer
|
|
; DataBufPtr - pointer to 512 byte data buffer
|
|
; SectHdrSector - sector number that we are about to write
|
|
; Expects to be called after ReadSectorHdr
|
|
; Outputs: A - error status code
|
|
; n,z - based on value returned in A
|
|
; Destroys: X, Y, n, z, c
|
|
; Calls: WriteUnknownData, WriteGCRData, WriteMFMData, RdWrGCRonHDData
|
|
; Called by: SWIMWrite
|
|
;
|
|
; Function: Writes the sector Tag and Data information for the current
|
|
; sector on the selected head, on the current cylinder.
|
|
;
|
|
; NOTE: The data buffer must be page aligned, and the tag
|
|
; buffer must not span across pages.
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
WriteSectorData proc
|
|
entry WriteDataDecode
|
|
entry WriteUnknownData
|
|
entry WriteGCRData
|
|
entry WriteGCRFmtData
|
|
entry WriteGCRtail
|
|
entry WriteMFMData
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
ldx <SectHdrFmtIndex ; get index from ReadSectorHdr
|
|
jmp (WriteDataDecode,x) ; dispatch to the routine
|
|
|
|
WriteUnknownData
|
|
lda #noDtaMkErr ; unsupported format, return with error
|
|
rts ; all done
|
|
title 'IOP SWIM Driver - Write Sector Data - GCR'
|
|
|
|
WriteGCRData
|
|
* lda rData ; empty garbage byte out of the FIFO,
|
|
* lda rError ; read and clear any pending errors
|
|
lda #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
sta wZeroes ; clear action and write mode
|
|
lda #WriteMode ; prepare to enter write mode
|
|
sta wOnes ; change to write mode
|
|
ldy #ClearFIFO ; prepare to clear the FIFO
|
|
sty wOnes ; toggle it to one
|
|
sty wZeroes ; and back to zero to clear it
|
|
; FIFO1 = empty, FIFO2 = empty, ShiftReg = empty
|
|
lda rError ; read and clear errors again
|
|
|
|
ldx #StartAction ; prepare to start writing
|
|
lda #$FF ; byte 1 of 6 byte sync field
|
|
sta wData ; write it to the FIFO
|
|
; FIFO1 = empty, FIFO2 = $FF, ShiftReg = empty
|
|
|
|
WriteGCRFmtData
|
|
lda #$3F ; byte 2 of 6 byte sync field
|
|
sta wData ; write it to the FIFO
|
|
|
|
; FIFO1 = $3F, FIFO2 = $FF, ShiftReg = empty
|
|
lda (<TagBufPtr) ; get byte 1 of 12 byte tag field
|
|
sta <WrTagBytesA+3 ; save ByteA' (byte 1)
|
|
sta <GCRWrCkSumA+1 ; CkSumA := ByteA
|
|
|
|
assert ClearFIFO=1 ; y = 1 from "ldy #ClearFIFO" above
|
|
* ldy #1 ; point to next tag byte
|
|
eor (<TagBufPtr),y ; ByteB' := ByteB xor CkSumA (byte 2 of 12)
|
|
|
|
stx wOnes ; turn on Action to start shifting out the write data
|
|
; T=0 FIFO1 = empty, FIFO2 = $3F, ShiftReg = $FF
|
|
|
|
sta <WrTagBytesB+3 ; save ByteB' (byte 2)
|
|
eor <GCRWrCkSumA+1 ; remove CkSumA to get ByteB
|
|
sta <GCRWrCkSumB+1 ; CkSumB := ByteB
|
|
iny ; point to next tag byte
|
|
|
|
eor (<TagBufPtr),y ; ByteC' := ByteC xor CkSumB (byte 3 of 12)
|
|
sta <WrTagBytesC+3 ; save ByteC' (byte 3)
|
|
eor <GCRWrCkSumB+1 ; remove CkSumB to get ByteC
|
|
cpa #$80 ; carry := CheckSumC [7]
|
|
rol a ; [6] [5] [4] [3] [2] [1] [0] [7]
|
|
sta <GCRWrCkSumC+1 ; initialize checksum C
|
|
iny ; point to next tag byte
|
|
|
|
ldx #$CF ; byte 3 of 6 byte sync field
|
|
; T=32 FIFO1 = empty, FIFO2 = empty, ShiftReg = $3F
|
|
stx wData ; write it to the FIFO
|
|
; T=36 FIFO1 = empty, FIFO2 = $CF, ShiftReg = $3F
|
|
ldx #$F3 ; byte 4 of 6 byte sync field
|
|
@bugPoll bit rHandshake ; wait for 1 byte available in the FIFO
|
|
; T=0…7 FIFO1 = empty, FIFO2 = $CF, ShiftReg = $3F
|
|
assert Dat1Byte=$80
|
|
bpl @bugPoll ; wait until there is room for 1
|
|
stx wData ; write it to the FIFO
|
|
; T=48 FIFO1 = $F3, FIFO2 = $CF, ShiftReg = $3F
|
|
|
|
eor (<TagBufPtr),y ; ByteA' := ByteA xor CkSumC (byte 4 of 12)
|
|
sta <WrTagBytesA+2 ; save ByteA' (byte 4)
|
|
eor <GCRWrCkSumC+1 ; remove CkSumC to get ByteA
|
|
adc <GCRWrCkSumA+1 ; CkSumA := CkSumA + ByteA + carry
|
|
; T=64 FIFO1 = empty, FIFO2 = $F3, ShiftReg = $CF
|
|
sta <GCRWrCkSumA+1 ; update checksum A
|
|
iny ; point to next tag byte
|
|
|
|
eor (<TagBufPtr),y ; ByteB' := ByteB xor CkSumA (byte 5 of 12)
|
|
sta <WrTagBytesB+2 ; save ByteB' (byte 5)
|
|
eor <GCRWrCkSumA+1 ; remove CkSumA to get ByteB
|
|
adc <GCRWrCkSumB+1 ; CkSumB := CkSumB + ByteB + carry
|
|
sta <GCRWrCkSumB+1 ; update checksum B
|
|
iny ; point to next tag byte
|
|
|
|
eor (<TagBufPtr),y ; ByteC' := ByteC xor CkSumB (byte 6 of 12)
|
|
sta <WrTagBytesC+2 ; save ByteC' (byte 6)
|
|
; T=96 FIFO1 = empty, FIFO2 = empty, ShiftReg = $F3
|
|
eor <GCRWrCkSumB+1 ; remove CkSumB to get ByteC
|
|
adc <GCRWrCkSumC+1 ; CkSumC := CkSumC + ByteC + carry
|
|
cpa #$80 ; carry := CheckSumC [7]
|
|
rol a ; [6] [5] [4] [3] [2] [1] [0] [7]
|
|
sta <GCRWrCkSumC+1 ; update checksum C
|
|
iny ; point to next tag byte
|
|
|
|
ldx #$FC ; byte 5 of 6 byte sync field
|
|
stx wData ; write it to the FIFO
|
|
; T=115 FIFO1 = empty, FIFO2 = $FC, ShiftReg = $F3
|
|
ldx #$FF ; byte 6 of 6 byte sync field
|
|
stx wData ; write it to the FIFO
|
|
; T=121 FIFO1 = $FF, FIFO2 = $FC, ShiftReg = $F3
|
|
|
|
eor (<TagBufPtr),y ; ByteA' := ByteA xor CkSumC (byte 7 of 12)
|
|
; T=128 FIFO1 = empty, FIFO2 = $FF, ShiftReg = $FC
|
|
sta <WrTagBytesA+1 ; save ByteA' (byte 7)
|
|
eor <GCRWrCkSumC+1 ; remove CkSumC to get ByteA
|
|
adc <GCRWrCkSumA+1 ; CkSumA := CkSumA + ByteA + carry
|
|
sta <GCRWrCkSumA+1 ; update checksum A
|
|
iny ; point to next tag byte
|
|
|
|
eor (<TagBufPtr),y ; ByteB' := ByteB xor CkSumA (byte 8 of 12)
|
|
sta <WrTagBytesB+1 ; save ByteB' (byte 8)
|
|
eor <GCRWrCkSumA+1 ; remove CkSumA to get ByteB
|
|
adc <GCRWrCkSumB+1 ; CkSumB := CkSumB + ByteB + carry
|
|
sta <GCRWrCkSumB+1 ; update checksum B
|
|
iny ; point to next tag byte
|
|
|
|
; T=160 FIFO1 = empty, FIFO2 = empty, ShiftReg = $FF
|
|
ldx GCRDataMark+0 ; byte 1 of 3 byte data mark sequence
|
|
stx wData ; write it to the FIFO
|
|
; T=167 FIFO1 = empty, FIFO2 = $D5, ShiftReg = $FF
|
|
ldx GCRDataMark+1 ; byte 2 of 3 byte data mark sequence
|
|
stx wData ; write it to the FIFO
|
|
; T=175 FIFO1 = $AA, FIFO2 = $D5, ShiftReg = $FF
|
|
|
|
eor (<TagBufPtr),y ; ByteC' := ByteC xor CkSumB (byte 9 of 12)
|
|
sta <WrTagBytesC+1 ; save ByteC' (byte 9)
|
|
eor <GCRWrCkSumB+1 ; remove CkSumB to get ByteC
|
|
adc <GCRWrCkSumC+1 ; CkSumC := CkSumC + ByteC + carry
|
|
; T=192 FIFO1 = empty, FIFO2 = $AA, ShiftReg = $D5
|
|
cpa #$80 ; carry := CheckSumC [7]
|
|
rol a ; [6] [5] [4] [3] [2] [1] [0] [7]
|
|
sta <GCRWrCkSumC+1 ; update checksum C
|
|
iny ; point to next tag byte
|
|
|
|
eor (<TagBufPtr),y ; ByteA' := ByteA xor CkSumC (byte 10 of 12)
|
|
sta <WrTagBytesA+0 ; save ByteA' (byte 10)
|
|
eor <GCRWrCkSumC+1 ; remove CkSumC to get ByteA
|
|
adc <GCRWrCkSumA+1 ; CkSumA := CkSumA + ByteA + carry
|
|
sta <GCRWrCkSumA+1 ; update checksum A
|
|
iny ; point to next tag byte
|
|
|
|
eor (<TagBufPtr),y ; ByteB' := ByteB xor CkSumA (byte 11 of 12)
|
|
; T=224 FIFO1 = empty, FIFO2 = empty, ShiftReg = $AA
|
|
sta <WrTagBytesB+0 ; save ByteB' (byte 11)
|
|
eor <GCRWrCkSumA+1 ; remove CkSumA to get ByteB
|
|
adc <GCRWrCkSumB+1 ; CkSumB := CkSumB + ByteB + carry
|
|
sta <GCRWrCkSumB+1 ; update checksum B
|
|
iny ; point to next tag byte
|
|
|
|
ldx GCRDataMark+2 ; byte 3 of 3 byte data mark sequence
|
|
stx wData ; write it to the FIFO
|
|
; T=244 FIFO1 = empty, FIFO2 = $AD, ShiftReg = $AA
|
|
|
|
eor (<TagBufPtr),y ; ByteC' := ByteC xor CkSumB (byte 12 of 12)
|
|
sta <WrTagBytesC+0 ; save ByteC' (byte 12)
|
|
|
|
ldy <SectHdrSector ; sector number that we are about to write
|
|
; T=256 FIFO1 = empty, FIFO2 = empty, ShiftReg = $AD
|
|
ldx EncodeGCRnibble,y ; encode the ByteC' nibble
|
|
stx wData ; write out the ByteC' nibble
|
|
; T=263 FIFO1 = empty, FIFO2 = Sector, ShiftReg = $AD
|
|
|
|
eor <GCRWrCkSumB+1 ; remove CkSumB to get ByteC
|
|
adc <GCRWrCkSumC+1 ; CkSumC := CkSumC + ByteC + carry
|
|
cpa #$80 ; carry := CheckSumC [7]
|
|
rol a ; [6] [5] [4] [3] [2] [1] [0] [7]
|
|
sta <GCRWrCkSumC+1 ; update checksum C
|
|
|
|
ldx #4-1 ; get loop counter and index
|
|
@WriteTagsLoop ldy <WrTagBytesA,x ; get ByteA'
|
|
lda EncodeGCRHighA,y ; get high bits of ByteA'
|
|
; T=288 FIFO1 = empty, FIFO2 = empty, ShiftReg = Sector
|
|
; T=+32 FIFO1 = empty, FIFO2 = ByteC', ShiftReg = ByteB'
|
|
ldy <WrTagBytesB,x ; get ByteB'
|
|
ora EncodeGCRHighB,y ; insert high bits of ByteB'
|
|
ldy <WrTagBytesC,x ; get ByteC'
|
|
ora EncodeGCRHighC,y ; insert high bits of ByteC'
|
|
tay ; get high bits nibble
|
|
lda EncodeGCRnibble,y ; encode the high bits nibble
|
|
sta wData ; write out the high bits nibble
|
|
; T=312 FIFO1 = empty, FIFO2 = HiBits, ShiftReg = Sector
|
|
; T=+58 FIFO1 = HiBits, FIFO2 = ByteC', ShiftReg = ByteB'
|
|
|
|
ldy <WrTagBytesA,x ; get ByteA'
|
|
lda EncodeGCRnibble,y ; encode the ByteA' nibble
|
|
; T=320 FIFO1 = empty, FIFO2 = empty, ShiftReg = HiBits
|
|
; T=+66 FIFO1 = empty, FIFO2 = HiBits, ShiftReg = ByteC'
|
|
sta wData ; write out the ByteA' nibble
|
|
; T=324 FIFO1 = empty, FIFO2 = ByteA', ShiftReg = HiBits
|
|
; T=+70 FIFO1 = ByteA', FIFO2 = HiBits, ShiftReg = ByteC'
|
|
|
|
ldy <WrTagBytesB,x ; get ByteB'
|
|
lda <WrTagBytesC,x ; get ByteC'
|
|
sta <GCRWrByteC+1 ; save ByteC'
|
|
dex ; decrement the index / loop count
|
|
bmi @WriteTagsDone ; if no more tags to do
|
|
|
|
@WriteTagsPoll bit rHandshake ; wait for 2 bytes available in the FIFO
|
|
; T=0…7 FIFO1 = empty, FIFO2 = empty, ShiftReg = ByteA'
|
|
assert Dat2Bytes=$40
|
|
bvc @WriteTagsPoll ; wait until there is room for 2
|
|
lda EncodeGCRnibble,y ; encode the ByteB' nibble
|
|
sta wData ; write out the ByteB' nibble
|
|
; T=+10 FIFO1 = empty, FIFO2 = ByteB', ShiftReg = ByteA'
|
|
|
|
ldy <GCRWrByteC+1 ; get ByteC'
|
|
lda EncodeGCRnibble,y ; encode the ByteC' nibble
|
|
sta wData ; write out the ByteC' nibble
|
|
; T=+24 FIFO1 = ByteC', FIFO2 = ByteB', ShiftReg = ByteA'
|
|
|
|
bra @WriteTagsLoop ; loop through all tag bytes
|
|
|
|
@WriteTagsDone bit rHandshake ; wait for 1 bytes available in the FIFO
|
|
; T=0…7 FIFO1 = empty, FIFO2 = ByteA', ShiftReg = HiBits
|
|
assert Dat1Byte=$80
|
|
bpl @WriteTagsDone ; wait until there is room for 1
|
|
lda <GCRWrCkSumC+1 ; setup checksum C
|
|
jmp WrGCRdataStart ; done with tags, start writing the data
|
|
|
|
; T=+51 FIFO1 = empty, FIFO2 = ByteC', ShiftReg = ByteB'
|
|
WriteGCRtail lda EncodeGCRHighA,x ; get high bits of ByteA'
|
|
ora EncodeGCRHighB,y ; insert high bits of ByteB'
|
|
phx ; save ByteA'
|
|
tax ; load index with high bits nibble
|
|
; T=+64 FIFO1 = empty, FIFO2 = empty, ShiftReg = ByteC'
|
|
lda EncodeGCRnibble,x ; encode the high bits nibble
|
|
sta wData ; write out the high bits nibble
|
|
; T=+72 FIFO1 = empty, FIFO2 = HiBits, ShiftReg = ByteC'
|
|
plx ; restore ByteA'
|
|
|
|
lda EncodeGCRnibble,x ; encode the ByteA' nibble
|
|
sta wData ; write out the ByteA' nibble
|
|
; T=+84 FIFO1 = ByteA', FIFO2 = HiBits, ShiftReg = ByteC'
|
|
|
|
lda GCRDataSlipMark+1 ; byte 2 of 2 byte slip mark
|
|
pha ; stack it for later
|
|
|
|
lda GCRDataSlipMark+0 ; byte 1 of 2 byte slip mark
|
|
; T=+95 FIFO1 = empty, FIFO2 = ByteA', ShiftReg = HiBits
|
|
pha ; stack it for later
|
|
|
|
ldx <GCRWrCkSumC+1 ; get checksum byte C
|
|
lda EncodeGCRnibble,x ; encode the byte C nibble
|
|
pha ; stack it for later
|
|
|
|
lda EncodeGCRnibble,y ; encode the ByteB' nibble
|
|
sta wData ; write out the ByteB' nibble
|
|
; T+116 FIFO1 = ByteB', FIFO2 = ByteA', ShiftReg = HiBits
|
|
|
|
ldy <GCRWrCkSumB+1 ; get checksum byte B
|
|
lda EncodeGCRnibble,y ; encode the byte B nibble
|
|
pha ; stack it for later
|
|
|
|
; T+126 FIFO1 = empty, FIFO2 = ByteB', ShiftReg = ByteA'
|
|
lda EncodeGCRHighC,x ; get high bits of byte C
|
|
ora EncodeGCRHighB,y ; insert high bits of byte B
|
|
|
|
ldx <GCRWrCkSumA+1 ; get checksum byte A
|
|
ldy EncodeGCRnibble,x ; encode the byte A nibble
|
|
phy ; stack it for later
|
|
|
|
ora EncodeGCRHighA,x ; insert high bits of byte A
|
|
tay ; get high bits nibble
|
|
lda EncodeGCRnibble,y ; encode the high bits nibble
|
|
sta wData ; write out the high bits nibble
|
|
; T+155 FIFO1 = ChkHi, FIFO2 = ByteB', ShiftReg = ByteA'
|
|
|
|
ldx #5 ; number of stacked bytes
|
|
@TailLoop pla ; get the byte from the stack
|
|
jsr WriteByte ; write it out
|
|
dex ; update byte count
|
|
bne @TailLoop ; loop until all bytes written
|
|
|
|
; Then finally add a few gap bytes so that the SlipMark
|
|
; will be fully written before writing is disabled...
|
|
|
|
lda GCRGapValue ; get the gap value
|
|
bit <WritingForFmt ; see if we are formatting
|
|
bmi @FmtExit ; if so, exit differently
|
|
jmp WriteDataExit ; flush the FIFO to disk
|
|
|
|
@FmtExit jsr WriteByte ; write out 2 GCR gap bytes
|
|
jmp WriteByte ; and return
|
|
title 'IOP SWIM Driver - Write Sector Data - MFM'
|
|
|
|
WriteMFMData
|
|
|
|
; De-select RdData0/1 to de-select index pulse during write to prevent noise/crosstalk
|
|
; on the RdData line from interfering with WrData and causing a bad write. This is a
|
|
; workaround for a noisy external drive cable. Instead we select somthing that will be
|
|
; a constant high.
|
|
|
|
assert (rMFMDriveAdr+HeadSelect)=rMFMModeOnAdr
|
|
lda #rMFMDriveAdr\
|
|
++ph0123en ; select rMFMDriveAdr/rMFMModeOnAdr
|
|
sta wPhase ; change the phase lines, but not HeadSelect.
|
|
|
|
lda rError ; read and clear any pending errors
|
|
lda #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
sta wZeroes ; clear action and write mode
|
|
lda #WriteMode ; prepare to enter write mode
|
|
sta wOnes ; change to write mode
|
|
lda #ClearFIFO ; prepare to clear the FIFO
|
|
sta wOnes ; toggle it to one
|
|
sta wZeroes ; and back to zero to clear it
|
|
lda rError ; read and clear errors again
|
|
|
|
; Save the return PC for the Async call to WriteDataBuffer
|
|
|
|
pla ; pop pcL
|
|
sta <AsyncPcL ; save PcL
|
|
pla ; pop pcH
|
|
sta <AsyncPcH ; save PcH
|
|
|
|
; Delay until we are close to 22 bytes past the end of the address field before
|
|
; starting the write.
|
|
|
|
ldx #MFMreWriteDelay ; get the delay loop counter
|
|
@reWriteWait dex ; decrement the counter
|
|
bne @reWriteWait ; loop until count exhausted
|
|
|
|
; write out the "bytes of zeroes" sync field
|
|
|
|
assert MFMSyncSize>2 ; assume that Sync will more than fill the FIFO
|
|
lda MFMSyncValue ; get the sync value
|
|
sta (<SwimWriteReg) ; fill the first FIFO byte
|
|
sta (<SwimWriteReg) ; fill the second FIFO byte before setting action
|
|
|
|
ldx #StartAction ; prepare to start writing
|
|
stx wOnes ; turn on Action to start shifting out the write data
|
|
|
|
ldx #MFMSyncSize-2 ; number of remaining sync bytes to write
|
|
jsr WriteRepeated ; write out the remaining sync bytes
|
|
|
|
|
|
; write out the data mark byte sequence
|
|
|
|
ldx #MFMDataMarkSize-1 ; number of MARK bytes to write
|
|
ldy #MFMDataMark-SectorTemplates
|
|
assert (wData+1)=wMark
|
|
inc <SwimWriteReg ; point to wMark
|
|
jsr WriteTemplate ; write the data mark mark bytes
|
|
|
|
assert (wMark-1)=wData
|
|
dec <SwimWriteReg ; point back to wData
|
|
lda SectorTemplates,y ; get final byte
|
|
|
|
|
|
; write the sector data
|
|
|
|
* ldx #<BlockSize ; x := zero from WriteTemplate
|
|
ldy #>BlockSize ; page counter 2 pages = 512 bytes
|
|
jsr WriteDataBuffer ; write out the data buffer
|
|
bne @DMAerror ; if timeout during DMA, report the error
|
|
|
|
|
|
; End the sector data with 2 CRC bytes...
|
|
|
|
jsr WriteMFMCRC ; write the 2 CRC bytes
|
|
|
|
; Restore the return PC
|
|
|
|
lda <AsyncPcH ; get PcH
|
|
pha ; restore PcH
|
|
lda <AsyncPcL ; get PcL
|
|
pha ; restore PcL
|
|
|
|
; Then finally add a few gap bytes so that the CRC
|
|
; will be fully written before writing is disabled...
|
|
|
|
lda MFMGapValue ; get the gap value
|
|
jmp WriteDataExit ; flush the FIFO to disk
|
|
|
|
@DMAerror ldx <AsyncPcH ; get PcH
|
|
phx ; restore PcH
|
|
ldx <AsyncPcL ; get PcL
|
|
phx ; restore PcL
|
|
tax ; set conditions to indicate DMA retry
|
|
rts ; return DMA retry count in reg-A
|
|
endwith
|
|
endwith
|
|
endproc
|
|
|
|
seg 'WordConsts'
|
|
WriteDataDecode proc
|
|
dc.w WriteUnknownData ; 0 - uncheckedFormat
|
|
dc.w WriteUnknownData ; 1 - unknownFormat
|
|
dc.w WriteUnknownData ; 2 - HD20Format
|
|
dc.w WriteGCRData ; 3 - GCR400Kformat
|
|
dc.w WriteGCRData ; 4 - GCR800Kformat
|
|
dc.w WriteMFMData ; 5 - MFM720Kformat
|
|
dc.w WriteMFMData ; 6 - MFM1440Kformat
|
|
dc.w RdWrGCRonHDData ; 7 - GCRonHDformat
|
|
dc.w WriteUnknownData ; 8 - reserved for future use
|
|
dc.w WriteUnknownData ; 9 - reserved for future use
|
|
dc.w WriteUnknownData ; 10 - reserved for future use
|
|
endproc
|
|
title 'IOP SWIM Driver - Skip Sector Data'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr SkipSectorData
|
|
; Inputs: none
|
|
; Expects to be called after ReadSectorHdr
|
|
; Outputs: none
|
|
; Destroys: A, X, Y, n, v, z, c
|
|
; Calls: SkipUnknownData, SkipGCRData, SkipMFMData, SleepShort
|
|
; Called by: SWIMRead, SWIMReadVerify, SWIMWrite, SWIMFormatVerify,
|
|
; PrefetchIntoCache
|
|
;
|
|
; Function: Skips the sector Tag and Data information for the current
|
|
; sector whose header has just been read.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
SkipSectorData proc
|
|
entry SkipDataDecode
|
|
entry SkipUnknownData
|
|
entry SkipGCRData
|
|
entry SkipMFMData
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
lda <WrongSectsLeft ; see how many sectors we can skip
|
|
cpa #WrongSectsMax/2 ; see if more than half used
|
|
blt DontSkip ; if so, don't give up the CPU
|
|
ldx <SectHdrFmtIndex ; get index from ReadSectorHdr
|
|
jmp (SkipDataDecode,x) ; dispatch to the routine
|
|
|
|
SkipGCRData lda #GCRSectorDataDelay-TypicalADBDelay-TMPB1ms
|
|
jmp SleepShort ; give ADB some time
|
|
|
|
SkipUnknownData ; unsupported format, use MFM timing
|
|
SkipMFMData lda #MFMSectorDataDelay-TypicalADBDelay-TMPB1ms
|
|
jmp SleepShort ; give ADB some time
|
|
|
|
DontSkip rts ; unsupported format, just return
|
|
endwith
|
|
endwith
|
|
endproc
|
|
|
|
seg 'WordConsts'
|
|
SkipDataDecode proc
|
|
dc.w SkipUnknownData ; 0 - uncheckedFormat
|
|
dc.w SkipUnknownData ; 1 - unknownFormat
|
|
dc.w SkipUnknownData ; 2 - HD20Format
|
|
dc.w SkipGCRData ; 3 - GCR400Kformat
|
|
dc.w SkipGCRData ; 4 - GCR800Kformat
|
|
dc.w SkipMFMData ; 5 - MFM720Kformat
|
|
dc.w SkipMFMData ; 6 - MFM1440Kformat
|
|
dc.w SkipGCRData ; 7 - GCRonHDformat
|
|
dc.w SkipUnknownData ; 8 - reserved for future use
|
|
dc.w SkipUnknownData ; 9 - reserved for future use
|
|
dc.w SkipUnknownData ; 10 - reserved for future use
|
|
endproc
|
|
title 'IOP SWIM Driver - Format Disk Track'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr FormatTrack
|
|
; Inputs:
|
|
; Outputs: A - error status code
|
|
; n,z - based on value returned in A
|
|
; Destroys: X, Y, n, z, c
|
|
; Calls: FmtUnknownTrack, FmtGCRTrack, FmtMFMTrack
|
|
; Called by: SWIMFormat
|
|
;
|
|
; Function: Formats a Cylinder on the current drive.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
FormatTrack proc
|
|
entry FmtTrackDecode
|
|
entry FmtUnknownTrack
|
|
entry FmtGCRTrack
|
|
entry FmtMFMTrack
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
with SectorBuffers
|
|
|
|
ply ; pop pcL
|
|
sty <AsyncPcL ; save PcL
|
|
ply ; pop pcH
|
|
sty <AsyncPcH ; save PcH
|
|
|
|
ldy <CurrentDrive ; get the active drive number
|
|
ldx DiskFormat,y ; see what format to read
|
|
lda RequiredMedia,x ; see what media is required
|
|
cpa MediaKind,y ; see if we have correct media
|
|
bne FmtUnknownTrack ; if wrong kind of media
|
|
|
|
txa ; get the DiskFormat
|
|
asl a ; compute the table index
|
|
tax ; load the index register
|
|
jmp (FmtTrackDecode,x) ; dispatch to the routine
|
|
|
|
FmtUnknownTrack lda #paramErr ; unsupported format, return with error
|
|
FmtTrackDone tax ; save the error code
|
|
lda <AsyncPcH ; restore PcH
|
|
pha ; push pcH
|
|
lda <AsyncPcL ; restore PcL
|
|
pha ; push pcL
|
|
|
|
lda <BaseBufferNum ; get base from last head formatted
|
|
clc ; setup for addition
|
|
adc <PhysSectsPerHead ; point to the first buffer for this head
|
|
sta <BaseBufferNum ; update the base buffer number
|
|
|
|
txa ; restore error code, set flags
|
|
rts ; all done
|
|
|
|
HdrFields record 0,increment
|
|
GCRCkSum
|
|
MFMBlkSize ds.b 1
|
|
GCRFmtKind
|
|
MFMSector ds.b 1
|
|
GCRHeadCyl
|
|
MFMHead ds.b 1
|
|
GCRSector
|
|
MFMCylinder ds.b 1
|
|
GCRCylinder
|
|
MFMLastAddrMark ds.b 1
|
|
endr
|
|
with HdrFields
|
|
|
|
WriteHdrInfo lda <SectHdrInfo+5-1 ; get the first byte to write
|
|
@Poll bit rHandshake ; see if we have room in the FIFO
|
|
assert Dat2Bytes=$40
|
|
bvc @Poll ; wait until there is room for two bytes
|
|
sta (<SwimWriteReg) ; write the byte
|
|
lda <SectHdrInfo+5-2 ; get the second byte to write
|
|
sta (<SwimWriteReg) ; write the byte
|
|
|
|
jsr SetupBufferPtrs ; setup DataBufPtr and TagBufPtr for this sector
|
|
|
|
lda <SectHdrInfo+5-3 ; get the third byte to write
|
|
sta (<SwimWriteReg) ; blindly write the byte
|
|
|
|
lda <SectHdrInfo+5-4 ; get the fourth byte to write
|
|
sta (<SwimWriteReg) ; blindly write the byte
|
|
|
|
lda <SectHdrInfo+5-5 ; get the final byte to write
|
|
jmp WriteByte ; write it out
|
|
|
|
|
|
; Create a buffer filled with the sector format pattern so that DMA can be used to blast them out
|
|
|
|
FillDataBuffer bit <CurDataBCPB ; see if optional data buffer exists
|
|
bpl @dataDone ; if so, don't fill buffer
|
|
ldx #>DataBuffers ; get the starting page number
|
|
stx <DataBufPtr+1 ; initialize high byte of buffer pointer
|
|
ldx #>DataBufSize ; number of pages to initialize
|
|
@fillerLoop sta (<DataBufPtr),y ; initialize the next byte of the buffer
|
|
iny ; update the page offset
|
|
bne @fillerLoop ; loop through the page
|
|
inc <DataBufPtr+1 ; update high byte of buffer pointer
|
|
dex ; update page count
|
|
bne @fillerLoop ; loop through all of the pages
|
|
@dataDone rts ; all done
|
|
|
|
|
|
FillTagBuffer sta TagBufStart,y ; initialize the first page of the buffer
|
|
sta TagBufEnd-$100,y ; initialize the last page of the buffer
|
|
iny ; update the page offset
|
|
bne FillTagBuffer ; loop through the page
|
|
rts ; all done
|
|
|
|
|
|
FmtStartWrite ldy <PhysHead ; get the desired head number
|
|
bne @start ; if not the first one, skip initialization
|
|
stz <BaseBufferNum ; reset buffer number for first head of new cylinder
|
|
ldy <PhysCylinder ; get the desired cylinder number
|
|
bne @start ; if not the first one, skip initialization
|
|
stz <DataBufPtr ; initialize low byte of buffer pointer
|
|
bcc @GCRgap ; if GCR mode, setup GCR gap sync buffer
|
|
|
|
; Create a buffer filled with MFM gap bytes so that DMA can be used to blast them out
|
|
|
|
@MFMgap lda MFMFmtFillValue ; get the data value to write
|
|
jsr FillDataBuffer ; fill the data buffer with the format pattern
|
|
lda MFMGapValue ; get the gap byte value
|
|
jsr FillTagBuffer ; fill the buffer with the gap value
|
|
lda #$FF ; MFM doesn't need tags, so don't fetch them next time
|
|
sta <CurTagBCPB ; see if optional tag buffer exists
|
|
bra @start ; go start the track format
|
|
|
|
; Create a buffer filled with GCR sync groups so that DMA can be used to blast them out
|
|
|
|
@GCRgap lda #0 ; zero's if encoding on the fly, 96 if raw data
|
|
bit <CurTagBCPB ; see if optional tag buffer exists
|
|
bpl @fillGCR ; if so, use zero's as the data buffer pattern
|
|
jsr FillTagBuffer ; initialize the tags to zero's if no buffer exists
|
|
lda GCRFmtFillValue ; get the data value to write
|
|
bit <CurDataBCPB ; see if optional data buffer exists
|
|
bpl @fillGCR ; if so, use encode the data on the fly, using write code
|
|
stz <WritingForFmt ; if no tag or data buffers, use DMA to blast the constant patterh
|
|
@fillGCR jsr FillDataBuffer ; fill the data buffer with the format pattern
|
|
ldx #>GCRGapBuffer ; point to the data buffer
|
|
stx <DataBufPtr+1 ; initialize high byte of buffer pointer
|
|
ldx #GCRInitialSyncCount; number of sync groups to put into buffer
|
|
@syncGroupLoop ldy #GCRGapSyncSize-1 ; loop count and index for each sync group
|
|
@syncCopyLoop lda GCRGapSync,y ; get a byte of sync
|
|
sta (<DataBufPtr),y ; copy it into the buffer
|
|
dey ; update the loop counter / index
|
|
bpl @syncCopyLoop ; copy all of the bytes in the sync group
|
|
lda <DataBufPtr ; get the low byte of the pointer
|
|
clc ; setup carry for addition
|
|
adc #GCRGapSyncSize ; point to next sync group in the buffer
|
|
sta <DataBufPtr ; update the low byte of the pointer
|
|
bcc @updateCount ; if no carry, upper byte is OK
|
|
inc <DataBufPtr+1 ; if carry, update page number
|
|
@updateCount dex ; update the sync group count
|
|
bne @syncGroupLoop ; loop through all of the sync groups
|
|
|
|
@start stz <DataBufPtr ; initialize low byte of buffer pointer
|
|
ldy <PhysHead ; get the desired head number
|
|
lda HeadSelectDecode,y ; get the head select command
|
|
jsr AdrAndSense ; select the disk head
|
|
lda rError ; read and clear any pending errors
|
|
lda #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
sta wZeroes ; clear action and write mode
|
|
lda #WriteMode ; prepare to enter write mode
|
|
sta wOnes ; change to write mode
|
|
lda #ClearFIFO ; prepare to clear the FIFO
|
|
sta wOnes ; toggle it to one
|
|
sta wZeroes ; and back to zero to clear it
|
|
lda rError ; read and clear errors again
|
|
lda #$FF ; garbage bytes to write initially
|
|
sta wData ; write it to the FIFO
|
|
sta wData ; write it to the FIFO
|
|
ldx #StartAction ; prepare to start writing
|
|
stx wOnes ; turn on Action to start shifting out the write data
|
|
jmp WriteByte ; fill the fifo with yet another byte and return
|
|
title 'IOP SWIM Driver - Format Track - GCR'
|
|
|
|
FmtGCRTrack lda <PhysCylinder ; get the current cylinder
|
|
tax ; save a copy
|
|
and #$3F ; mask to six bits
|
|
sta <SectHdrInfo\
|
|
+GCRCylinder ; setup low half of cylinder number
|
|
stz <SectHdrInfo\
|
|
+GCRSector ; start with sector zero
|
|
lda <PhysHead ; get the head number
|
|
sta <SeekingHead ; save head for ReadSectorHdr later
|
|
beq @HeadOK ; if zero use it
|
|
lda #$20 ; otherwise, set high bit of nibble
|
|
@HeadOK ora EncodeGCRHighC,x ; position and insert upper 2 bits of cylinder
|
|
sta <SectHdrInfo\
|
|
+GCRHeadCyl ; setup the side/upper cylinder bits
|
|
lda <FmtByteBlkSize ; get format byte
|
|
and #$20 ; get just the sides bit
|
|
ora <Interleave ; include interleave
|
|
sta <SectHdrInfo\
|
|
+GCRFmtKind ; setup the format kind
|
|
eor <SectHdrInfo\
|
|
+GCRCylinder ; compute checksum
|
|
eor <SectHdrInfo\
|
|
+GCRHeadCyl ; compute checksum
|
|
sta Sect0CkSum ; remember the checksum for sector zero
|
|
|
|
ldy #5-1 ; loop counter / index
|
|
@EncodeHdrLoop ldx <SectHdrInfo,y ; get a byte to encode
|
|
lda EncodeGCRnibble,x ; encode it
|
|
sta SectHdrInfo,y ; replace it
|
|
dey ; decrement counter / index
|
|
bne @EncodeHdrLoop ; encode all remaining bytes
|
|
|
|
clc ; indicate GCR format
|
|
jsr FmtStartWrite ; select the head and start writing
|
|
stz <PhysSector ; start sector index at zero
|
|
lda #>GCRGapBuffer ; point to gap sync data buffer
|
|
sta <DataBufPtr+1 ; initialize high byte of buffer pointer
|
|
ldx #<(GCRInitialSyncCount\
|
|
*GCRGapSyncSize) ; low byte of byte count
|
|
ldy #>(GCRInitialSyncCount\
|
|
*GCRGapSyncSize) ; high byte of byte count
|
|
lda #$FF ; garbage bytes to write initially
|
|
jsr WriteDataBuffer ; write out the data buffer
|
|
bne FmtGCRTrack ; if error due to DMA timeout, try re-formatting the track
|
|
@NextSector lda <SectorGapSize ; add the inter sector sync count
|
|
sta <LoopCountL ; setup Sync loop counter
|
|
@FmtSyncs ldx #GCRGapSyncSize ; number of bytes to write
|
|
ldy #GCRGapSync\
|
|
-SectorTemplates ; offset of gap sync template
|
|
jsr WriteTemplate ; write a gap sync group
|
|
dec <LoopCountL ; update the gap sync loop count
|
|
bne @FmtSyncs ; write all of the sync groups
|
|
|
|
ldy #GCRAddrMark\
|
|
-SectorTemplates ; offset of addr mark template
|
|
ldx #GCRAddrMarkSize ; number of bytes to write
|
|
jsr WriteTemplate ; write the address mark
|
|
|
|
ldx <PhysSector ; get sector index
|
|
lda <SectorMap,x ; get the sector number
|
|
sta <SectHdrSector ; save the sector number for buffer indexing
|
|
tay ; save for later
|
|
eor Sect0CkSum ; compute the new checksum
|
|
tax ; setup to encode
|
|
lda EncodeGCRnibble,x ; encode the checksum
|
|
sta <SectHdrInfo\
|
|
+GCRCkSum ; save computed checksum
|
|
lda EncodeGCRnibble,y ; encode the sector number
|
|
sta <SectHdrInfo\
|
|
+GCRSector ; save encoded sector number
|
|
|
|
jsr WriteHdrInfo ; write out the sector header info
|
|
ldy #GCRAddrSlipMark\
|
|
-SectorTemplates ; offset of addr slip mark template
|
|
ldx #GCRAddrSlipMarkSize+1 ; the end of the sector header, first byte of gap sync
|
|
jsr WriteTemplate ; write the end of the header, partial gap sync
|
|
|
|
bit <WritingForFmt ; see if we have optional data to write
|
|
bpl @FmtDefaultGCR ; if not, just write out the default data
|
|
|
|
* ldx #0 ; still zero from WriteTemplate above
|
|
ldy #1 ; point to next tag byte
|
|
jsr WriteGCRFmtData ; write out the remaining gap, and sector data
|
|
bra @sectorDataDone ; get back in sync with default format code
|
|
|
|
@FmtDefaultGCR ldx #GCRGapSyncSize-1+\ ; remainder of gap sync before the sector data
|
|
GCRDataMarkSize ; the beginning of the sector data
|
|
jsr WriteTemplate ; write the end of the header, gap, begining of data
|
|
|
|
lda <SectHdrInfo\
|
|
+GCRSector ; get the encoded sector number
|
|
|
|
@ByteCount set (TagSize\
|
|
+BlockSize+3) ; tag, data, and 3 byte checksum
|
|
@ByteCount set (@ByteCount*8+5)/6 ; number of bytes after encoding (rounded up)
|
|
|
|
ldx #<@ByteCount ; low byte of byte count
|
|
ldy #>@ByteCount ; high byte of byte count
|
|
jsr WriteDataBuffer ; write out the data buffer
|
|
bne @toFmtGCRTrack ; if error due to DMA timeout, try re-formatting the track
|
|
|
|
ldy #GCRDataSlipMark\
|
|
-SectorTemplates ; offset of addr slip mark template
|
|
ldx #GCRDataSlipMarkSize; the end of the sector data
|
|
jsr WriteTemplate ; write the end of the sector data
|
|
|
|
@sectorDataDone inc <PhysSector ; update the sector index
|
|
lda <PhysSector ; get the updated index
|
|
cpa <PhysSectsPerHead ; see if entire track formatted
|
|
blt @NextSector ; if not, go onto next sector (x=0)
|
|
|
|
lda GCRGapValue ; get the gap value
|
|
jsr WriteDataExit ; flush the fifo and shift register
|
|
bne @ErrExit ; exit if error detected
|
|
|
|
jsr ReadSectorHdr ; try to find sector 1
|
|
bcc @readOK ; if no error, go check the sector number
|
|
tay ; save error code
|
|
bne @DecSync ; if any error, try again with less sync
|
|
@readOK ldy #fmt1Err ; assume "can't find sector 0 after format"
|
|
lda <SectHdrSector ; see if sector zero found
|
|
bne @DecSync ; if not sect zero, try again with less sync
|
|
ldy #fmt2Err ; assume "can't get enough sync"
|
|
|
|
lda <AdrSearchCountL ; get low byte of count
|
|
clc ; setup for addition
|
|
adc #(GCRWriteToRead*5)\
|
|
/(16*6) ; account for dead time
|
|
sta <AdrSearchCountL ; update low byte of count
|
|
lda <AdrSearchCountH ; get high byte of count
|
|
adc #0 ; add in carry out
|
|
ldx #8-1 ; divide loop counter
|
|
asl <AdrSearchCountL ; setup for divide
|
|
@DivideLoop rol a ; shift high byte of numerator
|
|
cpa #5 ; compare to denominator
|
|
bcc @DivideShift ; if N < D, don't subtract, just shift
|
|
sbc #5 ; if N >= D, subtract denominator
|
|
@DivideShift rol <AdrSearchCountL ; shift in quotient bit
|
|
dex ; update loop counter
|
|
bpl @DivideLoop ; loop through all quotient bits
|
|
lda <AdrSearchCountL ; get final quotient
|
|
|
|
sec ; setup for subtraction
|
|
sbc <SectorGapSize ; subtract num of syncs we were using
|
|
cpa #-1 ; see if gap was too short
|
|
bmi @DecSync ; if too short, try again with less sync
|
|
ldy #noErr ; enough sync, format will succeed
|
|
lsr a ; divide excess bytes by 2
|
|
cpa <PhysSectsPerHead ; see if SectorGapSize cound inc by 2
|
|
blt @Exit ; exit if gap sync still reasonable
|
|
inc <SectorGapSize ; use more sync next time
|
|
@Exit tya ; return the error code
|
|
@ErrExit jmp FmtTrackDone ; all done
|
|
|
|
@DecSync dec <SectorGapSize ; use less sync next time
|
|
lda <SectorGapSize ; get sync size
|
|
cpa #GCRMinSyncCount ; see if still reasonable
|
|
blt @Exit ; if too small, return with error
|
|
@toFmtGCRTrack jmp FmtGCRTrack ; otherwise, try again with less sync
|
|
title 'IOP SWIM Driver - Format Track - MFM'
|
|
|
|
FmtMFMTrack lda <FmtByteBlkSize ; get format byte
|
|
and #$1F ; get just the block size
|
|
sta <SectHdrInfo\
|
|
+MFMBlkSize ; setup block size
|
|
lda MFMAddrMark+\
|
|
MFMAddrMarkSize-1 ; get final byte of address mark field
|
|
sta <SectHdrInfo\
|
|
+MFMLastAddrMark ; setup final byte of address mark field
|
|
lda <PhysCylinder ; get the current cylinder
|
|
sta <SectHdrInfo\
|
|
+MFMCylinder ; setup cylinder number
|
|
lda <PhysHead ; get the head number
|
|
sta <SectHdrInfo\
|
|
+MFMHead ; setup head number
|
|
bne @StartFormat ; if not side zero, start immediatly
|
|
|
|
; on side zero, sleep while the disk is rotating. We get here after the seek completes
|
|
; which is about 3 sector times, and we want to start writing about 2 sectors before
|
|
; index goes high, so we want to delay PhysSectsPerHead-5 sector times.
|
|
|
|
lda <PhysSectsPerHead ; total number of sectors
|
|
sec ; prepare carry for subtract
|
|
sbc #3+2 ; subtract off seek and pre-index gap times
|
|
tay ; setup multiply loop counter
|
|
ldx #0 ; initialize high byte of sleep time
|
|
lda #SectorDelay ; delay time for 1 sector
|
|
@RotateMultCLC clc ; initialize carry
|
|
@RotateMult dey ; decrement loop counter
|
|
beq @RotateWait ; loop for all remaining sectors
|
|
adc #SectorDelay ; add delay for next sector
|
|
bcc @RotateMult ; loop through remaining sectors
|
|
inx ; propagate carry into upper byte
|
|
bra @RotateMultCLC ; clear carry, loop through remaining sectors
|
|
@RotateWait jsr SleepLong ; wait for the disk to rotate
|
|
|
|
@StartFormat sec ; indicate MFM format
|
|
jsr FmtStartWrite ; select the head and start writing
|
|
ldx <PhysHead ; see which side we are on
|
|
bne @FindIndex ; if side zero, write 768 bytes before checking index
|
|
|
|
lda #>MFMGapBuffer ; point to gap data buffer
|
|
sta <DataBufPtr+1 ; initialize high byte of buffer pointer
|
|
ldx #$80 ; low byte of byte count
|
|
ldy #$01 ; high byte of byte count (1.5 pages, 384 bytes)
|
|
lda MFMGapValue ; get the gap byte value
|
|
jsr WriteDataBuffer ; write out the data buffer
|
|
bne @StartFormat ; if error due to DMA timeout, try re-formatting the track
|
|
|
|
lda #>MFMGapBuffer ; point to gap data buffer
|
|
sta <DataBufPtr+1 ; initialize high byte of buffer pointer
|
|
ldx #$80 ; low byte of byte count
|
|
ldy #$01 ; high byte of byte count (1.5 pages, 384 bytes)
|
|
lda MFMGapValue ; get the gap byte value
|
|
jsr WriteDataBuffer ; write out the data buffer
|
|
bne @StartFormat ; if error due to DMA timeout, try re-formatting the track
|
|
|
|
@FindIndex stz <LoopCountH ; setup index timeout high byte, low byte in x
|
|
ldy MFMGapValue ; get the gap byte value
|
|
lda #DriveRdData ; setup to test index pulse
|
|
sta <PhysSector ; indicate that low pulse not found yet
|
|
|
|
@FindIndexLoop inx ; update low byte of timeout
|
|
bne @FindIndexWait ; if no overflow, wait for a byte
|
|
inc <LoopCountH ; update index timeout high byte
|
|
bne @FindIndexWait ; if no overflow, wait for a byte
|
|
|
|
@IndexTimeout lda #noIndexErr ; index pulse not found during MFM format
|
|
ldy #StartAction\
|
|
+WriteMode ; prepare to clear action and write mode
|
|
sty wZeroes ; clear action and write mode (write done)
|
|
jmp FmtTrackDone ; return with the error
|
|
|
|
@FindIndexWait bit rHandshake ; see if there is room
|
|
assert Dat1Byte=$80
|
|
bpl @FindIndexWait ; wait for room
|
|
sty wData ; write out a gap byte
|
|
bne @IndexHigh ; index is high, check for low to high transition
|
|
stz <PhysSector ; indicate that index low has been seen
|
|
bra @FindIndexLoop ; go on to next byte
|
|
@IndexHigh bit <PhysSector ; see if index low has been seen
|
|
bne @FindIndexLoop ; if not, go on to next byte
|
|
|
|
; De-select RdData0/1 to de-select index pulse during format to prevent noise/crosstalk
|
|
; on the RdData line from interfering with WrData and causing a bad write. This is a
|
|
; workaround for a noisy external drive cable. Instead we select somthing that will be
|
|
; a constant high.
|
|
|
|
assert (rMFMDriveAdr+HeadSelect)=rMFMModeOnAdr
|
|
lda #rMFMDriveAdr\
|
|
++ph0123en ; select rMFMDriveAdr/rMFMModeOnAdr
|
|
sta wPhase ; change the phase lines, but not HeadSelect.
|
|
|
|
ldx #MFMInitialGapSize ; size of gap after physical index
|
|
lda MFMGapValue ; get the gap byte value
|
|
jsr WriteRepeated ; write out the gap field
|
|
|
|
ldx #MFMSyncSize ; number of sync bytes to write
|
|
lda MFMSyncValue ; get the sync value
|
|
jsr WriteRepeated ; write out the sync field bytes
|
|
|
|
ldx #MFMIndexMarkSize-1 ; number of MARK bytes to write
|
|
ldy #MFMIndexMark-SectorTemplates
|
|
assert (wData+1)=wMark
|
|
inc <SwimWriteReg ; point to wMark
|
|
jsr WriteTemplate ; write the index mark mark bytes
|
|
|
|
assert (wMark-1)=wData
|
|
dec <SwimWriteReg ; point back to wData
|
|
lda SectorTemplates,y ; get final byte
|
|
jsr WriteByte ; write it out
|
|
|
|
ldx #MFMIndexMarkGapSize; size of gap after index mark field
|
|
@NextSector lda MFMGapValue ; get the gap byte value
|
|
jsr WriteRepeated ; write out the gap field
|
|
|
|
ldx #MFMSyncSize ; number of sync bytes to write
|
|
lda MFMSyncValue ; get the sync value
|
|
jsr WriteRepeated ; write out the sync field bytes
|
|
|
|
ldx #MFMAddrMarkSize-1 ; number of MARK bytes to write
|
|
ldy #MFMAddrMark-SectorTemplates
|
|
assert (wData+1)=wMark
|
|
inc <SwimWriteReg ; point to wMark
|
|
jsr WriteTemplate ; write the addr mark mark bytes
|
|
assert (wMark-1)=wData
|
|
dec <SwimWriteReg ; point back to wData
|
|
|
|
ldx <PhysSector ; get sector index
|
|
lda <SectorMap,x ; get the zero based sector number
|
|
sta <SectHdrSector ; save the sector number for buffer indexing
|
|
ina ; MFM sector numbers are one based
|
|
sta <SectHdrInfo\
|
|
+MFMSector ; save header sector number
|
|
jsr WriteHdrInfo ; write out the sector header info
|
|
jsr WriteMFMCRC ; write out the sector header CRC
|
|
|
|
ldx #MFMAddrMarkGapSize ; number of gap bytes to write
|
|
lda MFMGapValue ; get the gap value to write
|
|
jsr WriteRepeated ; write out the gap field
|
|
|
|
|
|
; write out the sector data
|
|
|
|
ldx #MFMSyncSize ; number of sync bytes to write
|
|
lda MFMSyncValue ; get the sync value
|
|
jsr WriteRepeated ; write out the sync field bytes
|
|
|
|
ldx #MFMDataMarkSize-1 ; number of MARK bytes to write
|
|
ldy #MFMDataMark-SectorTemplates
|
|
assert (wData+1)=wMark
|
|
inc <SwimWriteReg ; point to wMark
|
|
jsr WriteTemplate ; write the data mark mark bytes
|
|
|
|
assert (wMark-1)=wData
|
|
dec <SwimWriteReg ; point back to wData
|
|
lda SectorTemplates,y ; get final byte
|
|
|
|
* ldx #<BlockSize ; low byte of byte count
|
|
ldy #>BlockSize ; high byte of byte count
|
|
jsr WriteDataBuffer ; write out the data buffer
|
|
bne @ToStartFormat ; if DMA timeout, re-write the track with ints disabled
|
|
jsr WriteMFMCRC ; write the 2 CRC bytes
|
|
|
|
ldx <SectorGapSize ; number of gap bytes to write
|
|
inc <PhysSector ; update the sector index
|
|
lda <PhysSector ; get the updated index
|
|
cpa <PhysSectsPerHead ; see if entire track formatted
|
|
blt @NextSector ; if not, go onto next sector
|
|
|
|
lda MFMGapValue ; get the gap value to write
|
|
ldy <PhysHead ; see if this is side zero
|
|
beq @TrackDone ; don't write gap after last sector on side zero
|
|
|
|
ldx #4 ; write 4 bytes to flush the FIFO and shift reg
|
|
jsr WriteRepeated ; write out the gap field
|
|
|
|
assert (rRdData0Adr+HeadSelect)=rRdData1Adr
|
|
ldx #rRdData0Adr\
|
|
++ph0123en ; select rRdData0Adr/rRdData1Adr
|
|
stx wPhase ; change the phase lines, but not HeadSelect.
|
|
|
|
ldx #>(-MFMFinalGapSize); get high byte of negated timeout
|
|
sta <LoopCountH ; initialize timeout high byte
|
|
ldx #<(-MFMFinalGapSize); get low byte of negated timeout
|
|
tay ; setup gap value
|
|
lda #DriveRdData ; setup to test index pulse
|
|
@PadTrack bit rHandshake ; see if we have room in the FIFO
|
|
assert Dat1Byte=$80
|
|
bpl @PadTrack ; wait until there is room for one byte
|
|
bne @PadTrackDone ; if index found, stop padding
|
|
sty wData ; write the byte
|
|
inx ; update timeout low byte
|
|
bne @PadTrack ; loop until index or timeout
|
|
inc <LoopCountH ; update timeout high byte
|
|
bne @PadTrack ; loop until index or timeout
|
|
jmp @IndexTimeout ; return timeout error, if index not found
|
|
|
|
@PadTrackDone tya ; setup gap value
|
|
|
|
@TrackDone jsr WriteDataExit ; flush the fifo and shift register
|
|
bne @Exit ; if error, exit now
|
|
|
|
* lda #0 ; initialize loop counter (already zero)
|
|
@HeadSwitchLoop dea ; allow a short delay before head switch
|
|
bne @HeadSwitchLoop ; loop 256 times for 1280 clocks, ≈650µsec
|
|
* lda #noErr ; indicate format succeeded (already zero)
|
|
|
|
@Exit jmp FmtTrackDone ; all done
|
|
@ToStartFormat jmp @StartFormat
|
|
endwith
|
|
endwith
|
|
endwith
|
|
endproc
|
|
|
|
seg 'WordConsts'
|
|
FmtTrackDecode proc
|
|
dc.w FmtUnknownTrack ; 0 - uncheckedFormat
|
|
dc.w FmtUnknownTrack ; 1 - unknownFormat
|
|
dc.w FmtUnknownTrack ; 2 - HD20Format
|
|
dc.w FmtGCRTrack ; 3 - GCR400Kformat
|
|
dc.w FmtGCRTrack ; 4 - GCR800Kformat
|
|
dc.w FmtMFMTrack ; 5 - MFM720Kformat
|
|
dc.w FmtMFMTrack ; 6 - MFM1440Kformat
|
|
dc.w FmtUnknownTrack ; 7 - GCRonHDformat
|
|
dc.w FmtUnknownTrack ; 8 - reserved for future use
|
|
dc.w FmtUnknownTrack ; 9 - reserved for future use
|
|
dc.w FmtUnknownTrack ; 10 - reserved for future use
|
|
endproc
|
|
title 'IOP SWIM Driver - Initialize SWIM chip'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr InitSWIMChip
|
|
; Inputs: none
|
|
; Outputs: A - error status code
|
|
; n,z - based on value returned in A
|
|
; Destroys: A, X, Y, n, z, c
|
|
; Calls: LoadSWIMparams
|
|
; Called by: SWIMInitialize, ReCalibrate
|
|
;
|
|
; Function: Initializes the SWIM chip, and switches it into ISM mode.
|
|
; Returns an error indication if initialization failed.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
InitSWIMChip proc
|
|
|
|
; Initialize the SWIM chip, and force ISM mode
|
|
|
|
ldy #3 ; retry count
|
|
initSWIMloop dey ; decrement retry count
|
|
beq initSWIMerr ; if retry count exceeded
|
|
**??** lda mtrOff ; Disable the interface in case we're in IWM
|
|
lda #startAction\
|
|
+WriteMode ; and turn off ACTION/WRITE in case we're not
|
|
sta wZeroes
|
|
lda #ph0123en+ph0+ph2
|
|
sta wPhase
|
|
cpa rPhase ; see if we can write/read phase lines
|
|
bne inIWMmode ; couldn't, must be IWM mode
|
|
lda #ph0123en+ph1+ph2
|
|
sta wPhase
|
|
cpa rPhase ; see if we can write/read phase lines
|
|
bne inIWMmode ; couldn't, must be IWM mode
|
|
lda #ph0123en+ph0+ph1+ph2
|
|
sta wPhase
|
|
cpa rPhase ; see if we can write/read phase lines
|
|
bne inIWMmode ; couldn't, must be IWM mode
|
|
|
|
inISMmode lda #IWMtimerOverride\
|
|
+IWMtimer2X ; set timer kill and 16mhz timer
|
|
sta wIWMConfig ; for IWM timer
|
|
lda #Select35 ; clear all bits except 3.5 select (ADB out)
|
|
and rSetup ; read and clear the setup bits
|
|
**??** ora #IBMDrive ; write using pulses, not transitions
|
|
sta wSetup ; init the setup register
|
|
eor rSetup ; make sure it was written OK
|
|
beq LoadSWIMparams ; load some parameters, if compared OK
|
|
initSWIMerr lda #initIWMErr ; return error code
|
|
rts ; return
|
|
|
|
inIWMmode lda q6H ; set IWM to sense mode
|
|
lda #$1F ; mask for mode bits
|
|
and q7L ; sense the mode register
|
|
bit q6L ; and back to read mode
|
|
cpa #IWMmode ; see if correct mode is set
|
|
beq IWMmodeSet ; if correct
|
|
ldx #0 ; loop retry count
|
|
initIWMloop dex ; decrement count
|
|
beq initSWIMerr ; if timed out
|
|
lda mtrOff ; change the IWM mode now
|
|
lda q6H ; set IWM to sense state
|
|
lda #$3f ; mask for sense, mode bits
|
|
and q7L ; sense the mode register
|
|
bit #$20 ; test enables
|
|
bne initIWMloop ; if interface not disabled yet
|
|
cpa #IWMmode ; see if low bits match
|
|
beq IWMmodeSet ; if correct
|
|
lda #IWMmode ; try setting it again
|
|
sta q7H ; set to IWM mode
|
|
lda q7L ; set IWM back to sense state
|
|
bra initIWMloop ; try again
|
|
|
|
IWMmodeSet lda q6L ; change back to read mode
|
|
lda q7L ; select the mode register
|
|
lda mtrOff
|
|
lda q6H
|
|
lda #$40+IWMmode ; "magic sequence" bit 6 = 1, 0, 1, 1
|
|
sta q7H ; put the chip in ISM mode bit 6 = 1
|
|
eor #$40 ; clear bit 6
|
|
sta q7H ; put the chip in ISM mode bit 6 = 0
|
|
eor #$40 ; set bit 6
|
|
sta q7H ; put the chip in ISM mode bit 6 = 1
|
|
sta q7H ; put the chip in ISM mode bit 6 = 1
|
|
jmp initSWIMloop ; verify that it is now an ISM
|
|
endproc
|
|
title 'IOP SWIM Driver - Load SWIM parameters'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr LoadSWIMparams
|
|
; Inputs: CurrentDrive - active drive number
|
|
; Outputs: A - error status code
|
|
; n,z - based on value returned in A
|
|
; Destroys: A, X, Y, n, z, c
|
|
; Cycles: 348 - if same param set as last call, first cylinder zone
|
|
; 613 - if new param set, first cylinder zone
|
|
; +15 cycles per additional cylinder zone searched
|
|
; Calls: none
|
|
; Called by: SetUpDrive, Seek, 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
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
LoadSWIMparams proc
|
|
entry SWIMparamIndex
|
|
entry SWIMcylTable
|
|
entry SWIMparams
|
|
with SWIMVarsZ
|
|
with SWIMVars
|
|
|
|
lda #5 ; retry count
|
|
pha ; save retry count
|
|
|
|
ldx <CurrentDrive ; get the drive number
|
|
ldy DiskFormat,x ; get the disk format
|
|
lda SWIMparamIndex,y ; get the parameter table index
|
|
tay ; setup the table index
|
|
CylinderLoop lda SWIMcylTable,y ; get upper bound of this cylinder range
|
|
iny ; point to parameter index
|
|
iny ; point to next cylinder range
|
|
cpa CurrentCylinder,x ; compare to current cylinder
|
|
blt CylinderLoop ; loop until proper range found
|
|
ldx SWIMcylTable-1,y ; get parameter table index
|
|
cpx LastParamSet ; see if same as last time
|
|
stx LastParamSet ; mark current index as most recent
|
|
beq checkParams ; if a match, just compare first
|
|
|
|
ReLoadParams plx ; get retry count
|
|
dex ; count the retry
|
|
bmi LoadParamErr ; retries exhausted, return error
|
|
phx ; save the retry count
|
|
ldx LastParamSet ; get current index
|
|
lda #startAction\
|
|
+WriteMode ; need to write to wZeroes
|
|
sta wZeroes ; to reset parameter ram ptr
|
|
ldy #16 ; loop counter
|
|
loadParamLoop lda SWIMparams,x ; get the parameter value
|
|
sta wParams ; store it in the param ram
|
|
dex ; point to next param
|
|
dey ; decrement the loop count
|
|
bne loadParamLoop ; process next parameter
|
|
|
|
ldx LastParamSet ; get current index
|
|
checkParams lda #startAction\
|
|
+WriteMode ; need to write to wZeroes
|
|
sta wZeroes ; to reset parameter ram ptr
|
|
ldy #16 ; loop counter
|
|
checkParamLoop lda SWIMparams,x ; get the parameter value
|
|
eor rParams ; compare it
|
|
bne ReLoadParams ; if it didn't compare
|
|
dex ; point to next param
|
|
dey ; decrement the loop count
|
|
bne checkParamLoop ; process next parameter
|
|
|
|
lda #$FF-(TransSpaceDis*1+IBMDrive*1+EnableECM*1+GCRMode*1)
|
|
and rSetup ; get bits that don't change
|
|
ora SWIMparams,x ; insert bits that do change
|
|
sta wSetup ; setup the SWIM chip modes
|
|
eor rSetup ; compare it
|
|
bne ReLoadParams ; if it didn't compare
|
|
|
|
plx ; pop retry count
|
|
tax ; return noErr, and update flags
|
|
rts ; all done
|
|
|
|
LoadParamErr lda #initIWMErr ; return error code
|
|
rts ; return
|
|
endwith
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - SWIM parameters'
|
|
|
|
seg 'constants'
|
|
SWIMparamConsts proc ; SWIM chip parameter constants
|
|
|
|
entry SWIMcylTable
|
|
SWIMcylTable
|
|
GCRtable dc.b $ff,GCRparams1-SWIMparams+16
|
|
|
|
MFMtable dc.b $ff,MFMparams1-SWIMparams+16
|
|
dc.b $ff,MFMparams2-SWIMparams+16 ; unused, but just in case we need it
|
|
dc.b $ff,MFMparams1-SWIMparams+16 ; unused, but just in case we need it
|
|
|
|
SPAREtable dc.b $ff,SPAREparams-SWIMparams+16 ; unused, but just in case we need it
|
|
|
|
; table indexed by disk format kind
|
|
|
|
entry SWIMparamIndex
|
|
SWIMparamIndex dc.b GCRtable-SWIMcylTable ; 0 - uncheckedFormat (use GCR)
|
|
dc.b GCRtable-SWIMcylTable ; 1 - unknownFormat (use GCR)
|
|
dc.b GCRtable-SWIMcylTable ; 2 - HD20Format (use GCR)
|
|
dc.b GCRtable-SWIMcylTable ; 3 - GCR400Kformat
|
|
dc.b GCRtable-SWIMcylTable ; 4 - GCR800Kformat
|
|
dc.b MFMtable-SWIMcylTable ; 5 - MFM720Kformat
|
|
dc.b MFMtable-SWIMcylTable ; 6 - MFM1440Kformat
|
|
dc.b GCRtable-SWIMcylTable ; 7 - GCRonHDformat
|
|
dc.b SPAREtable-SWIMcylTable ; 8 - reserved for future use
|
|
dc.b SPAREtable-SWIMcylTable ; 9 - reserved for future use
|
|
dc.b SPAREtable-SWIMcylTable ; 10 - reserved for future use
|
|
|
|
|
|
entry SWIMparams
|
|
SWIMparams
|
|
|
|
; Parameter register values.
|
|
;
|
|
; The timing values Time1, Time0, xSx, xLx and Min have a resolution to
|
|
; the nearest half clock, so their actual register values are doubled so
|
|
; that bits 1-7 become the integer part and bit 0 becomes the ".5" part.
|
|
; xSx and xLx are reduced by 2 clocks, and Min is reduced by 3 clocks to
|
|
; take into account gate delays in the SWIM.
|
|
|
|
; For a 15.6672MHz input clock
|
|
; with 16.33usec byte times (400K, 800K GCR):
|
|
; Cell times 2.0424836, 4.0849672, 6.1274508 microseconds
|
|
; GCR params1 (more accuratly reflect IWM operation)
|
|
; peak shift = 0, pre-comp = 0.
|
|
GCRparams1 dc.b (TransSpaceDis*1+IBMDrive*0+EnableECM*0+GCRMode*1) ; Setup Reg
|
|
dc.b (32-2)*2+0 ; [F] Time1 = 32.0 (2.0424836 uSec)
|
|
dc.b (8*16)+8 ; [E] Early/Norm = 8 / 8
|
|
dc.b (32-2)*2+0 ; [D] Time0 = 32.0 (2.0424836 uSec)
|
|
dc.b (8*16)+8 ; [C] Late/Norm = 8 / 8
|
|
dc.b (32-2)*2+0 ; [B] LLS = 32.0
|
|
dc.b (32-2)*2+0 ; [A] LLL = 32.0
|
|
dc.b (32-2)*2+0 ; [9] LSS = 32.0
|
|
dc.b (32-2)*2+0 ; [8] LSL = 32.0
|
|
dc.b (32-2)*2+0 ; [7] CSLS = 32.0
|
|
dc.b (32-2)*2+0 ; [6] RPT = 32.0
|
|
dc.b (32-2)*2+0 ; [5] SLS = 32.0
|
|
dc.b (32-2)*2+0 ; [4] SLL = 32.0
|
|
dc.b (32-2)*2+0 ; [3] SSS = 32.0
|
|
dc.b (32-2)*2+0 ; [2] SSL = 32.0
|
|
dc.b 64 ; [1] Mult = 64
|
|
dc.b (16-3)*2+0 ; [0] Min = 16.0
|
|
|
|
; For a 15.6672MHz input clock
|
|
; with 16usec byte times (720K, 1440K MFM):
|
|
; Cell times 2.0105698, 2.9998978, 3.9892258 microseconds
|
|
; MFM params1
|
|
MFMparams1 dc.b (TransSpaceDis*0+IBMDrive*1+EnableECM*0+GCRMode*0) ; Setup Reg
|
|
dc.b (31-2)*2+1 ; [F] Time1 = 31.5 (2.0105698 uSec)
|
|
dc.b $57 ; [E] Early/Norm = $57
|
|
dc.b (15-2)*2+1 ; [D] Time0 = 15.5 (0.9893280 uSec)
|
|
dc.b $97 ; [C] Late/Norm = $97
|
|
dc.b (14-2)*2+1 ; [B] LLS = 14.5
|
|
dc.b (14-2)*2+1 ; [A] LLL = 14.5
|
|
dc.b (25-2)*2+1 ; [9] LSS = 25.5
|
|
dc.b (25-2)*2+1 ; [8] LSL = 25.5
|
|
dc.b (15-2)*2+1 ; [7] CSLS = 15.5
|
|
dc.b (15-2)*2+1 ; [6] RPT = 15.5
|
|
dc.b (14-2)*2+0 ; [5] SLS = 14.0
|
|
dc.b (14-2)*2+0 ; [4] SLL = 14.0
|
|
dc.b (25-2)*2+0 ; [3] SSS = 25.0
|
|
dc.b (25-2)*2+0 ; [2] SSL = 25.0
|
|
dc.b 65 ; [1] Mult = 65
|
|
dc.b (15-3)*2+0 ; [0] Min = 15.0
|
|
|
|
; For a 15.6672MHz input clock
|
|
; with 16usec byte times (720K, 1440K MFM):
|
|
; Cell times 2.0105698, 2.9998978, 3.9892258 microseconds
|
|
; MFM params2
|
|
MFMparams2 dc.b (TransSpaceDis*0+IBMDrive*1+EnableECM*0+GCRMode*0) ; Setup Reg
|
|
dc.b (31-2)*2+1 ; [F] Time1 = 31.5 (2.0105698 uSec)
|
|
dc.b $57 ; [E] Early/Norm = $57
|
|
dc.b (15-2)*2+1 ; [D] Time0 = 15.5 (0.9893280 uSec)
|
|
dc.b $97 ; [C] Late/Norm = $97
|
|
dc.b (14-2)*2+1 ; [B] LLS = 14.5
|
|
dc.b (14-2)*2+1 ; [A] LLL = 14.5
|
|
dc.b (25-2)*2+1 ; [9] LSS = 25.5
|
|
dc.b (25-2)*2+1 ; [8] LSL = 25.5
|
|
dc.b (15-2)*2+1 ; [7] CSLS = 15.5
|
|
dc.b (15-2)*2+1 ; [6] RPT = 15.5
|
|
dc.b (14-2)*2+0 ; [5] SLS = 14.0
|
|
dc.b (14-2)*2+0 ; [4] SLL = 14.0
|
|
dc.b (25-2)*2+0 ; [3] SSS = 25.0
|
|
dc.b (25-2)*2+0 ; [2] SSL = 25.0
|
|
dc.b 65 ; [1] Mult = 65
|
|
dc.b (15-3)*2+0 ; [0] Min = 15.0
|
|
|
|
; spare table entry, for possible future use
|
|
; SPARE params
|
|
SPAREparams dc.b (TransSpaceDis*1+IBMDrive*0+EnableECM*0+GCRMode*1) ; Setup Reg
|
|
dc.b (00-2)*2+0 ; [F] Time1 = 00.0 (0.0000000 uSec)
|
|
dc.b (0*16)+0 ; [E] Early/Norm = 0 / 0
|
|
dc.b (00-2)*2+0 ; [D] Time0 = 00.0 (0.0000000 uSec)
|
|
dc.b (0*16)+0 ; [C] Late/Norm = 0 / 0
|
|
dc.b (00-2)*2+0 ; [B] LLS = 00.0
|
|
dc.b (00-2)*2+0 ; [A] LLL = 00.0
|
|
dc.b (00-2)*2+0 ; [9] LSS = 00.0
|
|
dc.b (00-2)*2+0 ; [8] LSL = 00.0
|
|
dc.b (00-2)*2+0 ; [7] CSLS = 00.0
|
|
dc.b (00-2)*2+0 ; [6] RPT = 00.0
|
|
dc.b (00-2)*2+0 ; [5] SLS = 00.0
|
|
dc.b (00-2)*2+0 ; [4] SLL = 00.0
|
|
dc.b (00-2)*2+0 ; [3] SSS = 00.0
|
|
dc.b (00-2)*2+0 ; [2] SSL = 00.0
|
|
dc.b 00 ; [1] Mult = 00
|
|
dc.b (00-3)*2+0 ; [0] Min = 00.0
|
|
|
|
; For a 15.6672MHz input clock
|
|
; with 16.33usec byte times (400K, 800K GCR):
|
|
; Cell times 2.0424836, 4.0849672, 6.1274508 microseconds
|
|
; GCR params2 (computed using formula, with 100ns Peak Shift, 125ns PreComp)
|
|
GCRparams2 dc.b (TransSpaceDis*1+IBMDrive*0+EnableECM*0+GCRMode*1) ; Setup Reg
|
|
dc.b (32-2)*2+0 ; [F] Time1 = 32.0 (2.0424836 uSec)
|
|
dc.b (6*16)+8 ; [E] Early/Norm = 6 / 8
|
|
dc.b (32-2)*2+0 ; [D] Time0 = 32.0 (2.0424836 uSec)
|
|
dc.b (10*16)+8 ; [C] Late/Norm = 10 / 8
|
|
dc.b (30-2)*2+1 ; [B] LLS = 30.5
|
|
dc.b (31-2)*2+0 ; [A] LLL = 31.0
|
|
dc.b (32-2)*2+0 ; [9] LSS = 32.0
|
|
dc.b (33-2)*2+1 ; [8] LSL = 33.5
|
|
dc.b (30-2)*2+0 ; [7] CSLS = 30.0
|
|
dc.b (24-2)*2+0 ; [6] RPT = 24.0
|
|
dc.b (31-2)*2+0 ; [5] SLS = 31.0
|
|
dc.b (30-2)*2+1 ; [4] SLL = 30.5
|
|
dc.b (30-2)*2+1 ; [3] SSS = 30.5
|
|
dc.b (32-2)*2+0 ; [2] SSL = 32.0
|
|
dc.b 64 ; [1] Mult = 64
|
|
dc.b (16-3)*2+0 ; [0] Min = 16.0
|
|
endproc
|
|
title 'IOP SWIM Driver - Address and Sense / Sense'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr AdrAndSense
|
|
; Inputs: A - drive address [0,0, HeadSelect, 0,0, ca2,ca1,ca0]
|
|
; Outputs: z - 1 (BEQ) if DriveRdData line was zero
|
|
; z - 0 (BNE) if DriveRdData line was one
|
|
; Destroys: A, Y, n, v
|
|
; Cycles: 35 / 37
|
|
; Calls: none
|
|
; Called by: PollingTask, SWIMEject, SWIMGetRawData, CheckStaticAttr,
|
|
; CheckDriveMode, TurnOnDrive, WaitDriveReady, ReCalibrate, Seek
|
|
;
|
|
; Function: Sets up the disk register address for the currently enabled
|
|
; disk and reads the DriveRdData line for that register.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr Sense
|
|
; Inputs: none
|
|
; Outputs: z - 1 (BEQ) if DriveRdData line was zero
|
|
; z - 0 (BNE) if DriveRdData line was one
|
|
; Destroys: A, n, v
|
|
; Cycles: 12
|
|
; Calls: none
|
|
; Called by: SWIMGetRawData, Seek
|
|
;
|
|
; Function: Reads the DriveRdData line for the last addressed register.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
AdrAndSense proc
|
|
ldy #ph0123en+ca1+ca0 ; set ca1 and ca0 high
|
|
sty wPhase ; before changing HeadSelect
|
|
ldy #HeadSelect ; prepare to change HeadSelect
|
|
bit #HeadSelect ; test new head select value
|
|
beq ResetHeadSelect ; if new value is zero
|
|
sty wOnes ; otherwise set head select
|
|
bra HeadSelectDone ; join common code
|
|
ResetHeadSelect sty wZeroes ; reset head select
|
|
HeadSelectDone ora #ph0123en ; set the phase line enables
|
|
sta wPhase ; write the phase lines
|
|
entry Sense
|
|
Sense lda #DriveRdData ; prepare to test bit
|
|
and rHandshake ; test the DriveRdData bit
|
|
rts ; all done
|
|
endproc
|
|
title 'IOP SWIM Driver - Address and Strobe / Pulse'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr AdrAndStrb
|
|
; Inputs: A - drive address [0,0, HeadSelect, 0,0, ca2,ca1,ca0]
|
|
; Outputs: none
|
|
; Destroys: A, Y, n, z
|
|
; Cycles: 41 / 43
|
|
; Calls: none
|
|
; Called by: PollingTask, SWIMEject, TurnOnDrive, TurnOffDrive,
|
|
; ReCalibrate, Seek
|
|
;
|
|
; 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.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr Pulse
|
|
; Inputs: none
|
|
; Outputs: none
|
|
; Destroys: A, n, z
|
|
; Cycles: 25
|
|
; Calls: none
|
|
; Called by: Seek
|
|
;
|
|
; Function: pulses the ph3 line, causing the data from ca2 to
|
|
; be written to the last addressed register.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
AdrAndStrb proc
|
|
ldy #ph0123en+ca1+ca0 ; set ca1 and ca0 high
|
|
sty wPhase ; before changing HeadSelect
|
|
ldy #HeadSelect ; prepare to change HeadSelect
|
|
bit #HeadSelect ; test new head select value
|
|
beq ResetHeadSelect ; if new value is zero
|
|
sty wOnes ; otherwise set head select
|
|
bra HeadSelectDone ; join common code
|
|
ResetHeadSelect sty wZeroes ; reset head select
|
|
HeadSelectDone ora #ph0123en ; set the phase line enables
|
|
sta wPhase ; write the phase lines
|
|
doPulse ora #ph0123en+ph3 ; prepare to set ph3 bit
|
|
sta wPhase ; turn ph3 on
|
|
eor #ph3 ; prepare to reset ph3 bit
|
|
sta wPhase ; turn ph3 off
|
|
rts ; all done
|
|
|
|
entry Pulse
|
|
Pulse lda rPhase ; get the old phase lines
|
|
bra doPulse ; finish the pulse
|
|
endproc
|
|
title 'IOP SWIM Driver - Disk Select'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr DiskSelect
|
|
; Inputs: X - Drive Number
|
|
; Outputs: z - 1 (BEQ) if illegal drive number
|
|
; z - 0 (BNE) if legal drive number
|
|
; Destroys: A, Y, n, v, c
|
|
; Cycles: 22 clocks for internal drive
|
|
; 36 or 71 clocks for unknown drive
|
|
; 45 clocks if PassThru doesn't work
|
|
; 54 clocks + 37 clocks per HD20 for external SONY drive
|
|
; 76 clocks + 37 clocks per prior HD20 for HD20 drives
|
|
; Calls: none
|
|
; Called by: SWIMInitialize, PollingTask, SWIMEject, TurnOnDrive
|
|
;
|
|
; Function: Asserts the ENABLE line for the specified drive.
|
|
;
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
DiskSelect proc
|
|
with SWIMVars
|
|
cpx #intDriveNumber ; check for internal drive
|
|
bne chkExternal ; if not internal drive
|
|
lda #Drive2Enabled ; prepare to disable the ext drive
|
|
sta wZeroes ; disable external drive
|
|
lda #Drive1Enabled\
|
|
+MotorOn ; prepare to enable int drive
|
|
sta wOnes ; enable the internal drive
|
|
rts ; all done
|
|
|
|
chkExternal lda #ph0123en\
|
|
++rNoDriveAdr ; address the drive exists reg
|
|
sta wPhase ; write the phase lines
|
|
lda #HeadSelect ; prepare to reset head select
|
|
sta wZeroes ; HeadSelect is 0 for rNoDriveAdr
|
|
lda PassThruBroken ; see if pass-thru connector works
|
|
beq usePassThru ; if you've got it, use it
|
|
|
|
; if PassThru is not working, then external drive port can only have a single HD20
|
|
; connected to it.
|
|
cpx #FirstHD20DriveNumber ; check for 1st HD20
|
|
bne unknownDrive ; if not 1st HD20, give up
|
|
lda #Drive1Enabled ; prepare to disable the int drive
|
|
sta wZeroes ; disable internal drive
|
|
lda #Drive2Enabled\
|
|
+MotorOn ; prepare to enable ext drive
|
|
sta wOnes ; enable the external drive
|
|
rts ; all done
|
|
|
|
usePassThru lda #Drive1Enabled ; prepare to disable the int drive
|
|
sta wZeroes ; disable internal drive
|
|
lda #Drive2Enabled\
|
|
+MotorOn ; prepare to enable ext drive
|
|
sta wOnes ; enable the external drive
|
|
cpx #extDriveNumber ; check for external drive
|
|
bne chkHD20 ; if not external, check for HD20s
|
|
ldy #maxHD20Cnt ; Try all HD20s + 1 ext SONY drive
|
|
bra searchExtDrive ; start looking for it
|
|
|
|
; the following sequence will cause the next drive on the chain to be enabled
|
|
searchLoop lda #ph0123en++rNoDriveAdr++ph3
|
|
sta wPhase ; pulse phase 3 high
|
|
eor #ph3 ; and then
|
|
sta wPhase ; low again
|
|
eor #ph1 ; reset phase 1
|
|
sta wPhase ; pulse phase 1 low
|
|
eor #ph1 ; and then
|
|
sta wPhase ; high again
|
|
searchExtDrive lda #DriveRdData ; prepare to test sense line
|
|
bit rHandShake ; test the sense line (rNoDrive)
|
|
beq loopExit ; exit when SONY drive found
|
|
dey ; decrement the loop counter
|
|
bpl searchLoop ; try next drive on the chain
|
|
loopExit tay ; return z <- false
|
|
rts ; all done
|
|
|
|
chkHD20 lda #MotorOn ; prepare to pulse the drive enable
|
|
sta wZeroes ; disable the drive
|
|
sta wOnes ; enable the drive
|
|
txa ; get drive number
|
|
sec ; set carry for subtract
|
|
sbc #FirstHD20DriveNumber ; make drive number 0 based
|
|
tay ; load search counter
|
|
bge searchExtDrive ; start searching for it
|
|
unknownDrive lda #0 ; return z <- true
|
|
rts ; all done
|
|
endwith
|
|
endproc
|
|
title 'IOP SWIM Driver - Find Drive Kind'
|
|
|
|
;_______________________________________________________________________
|
|
;
|
|
; Routine: jsr FindDriveKind
|
|
; Inputs: none
|
|
; Outputs: A - Disk Drive Kind
|
|
; Destroys: X, Y, n, z, c
|
|
; Cycles: 116
|
|
; Calls: none
|
|
; Called by: SWIMInitialize
|
|
;
|
|
; 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 NoDrive
|
|
;
|
|
; Revised 1 1 1 1 0 1 1 x 1
|
|
; /DrvIn 0 1 1 1 0 0 1 0 1
|
|
; /SingleSide 0 1 1 0 0 1 1 1 1
|
|
; SuperDrive 0 1 0 1 0 0 0 1 1
|
|
;_______________________________________________________________________
|
|
|
|
seg 'code'
|
|
FindDriveKind proc
|
|
entry DriveKindXlate
|
|
ldy #ph0123en\
|
|
+ca2+ca1+ca0 ; prepare to test REVISED status
|
|
sty wPhase ; set the phase lines
|
|
ldx #HeadSelect ; prepare to set HeadSelect
|
|
stx wOnes ; Set HeadSelect
|
|
lda rHandshake ; prepare to test bit
|
|
stx wZeroes ; Clear HeadSelect
|
|
ldx #%00010000 ; init drive kind index / loop counter
|
|
bra start
|
|
|
|
loop sty wPhase ; set the phase lines
|
|
dey ; decrement ca2..ca0 phase values
|
|
lda rHandshake ; prepare to test bit
|
|
start and #DriveRdData ; isolate the DriveRdData bit
|
|
cpa #DriveRdData ; Carry := DriveRdData bit
|
|
txa ; get result
|
|
rol a ; shift next bit into result
|
|
tax ; restore result
|
|
bcc loop ; loop until last bit shifted in
|
|
|
|
lda DriveKindXlate,x ; translate statuses to drive kind
|
|
rts ; all done
|
|
endproc
|
|
|
|
seg 'constants'
|
|
DriveKindXlate proc
|
|
dc.b SSGCRDriveKind ; 0 0 0 0 - 400K GCR drive
|
|
dc.b unknownDriveKind ; 0 0 0 1 - unknown
|
|
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
|
|
endproc
|
|
end
|
|
|