mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-12-28 16:31:01 +00:00
4325cdcc78
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included. The Tools directory, containing mostly junk, is also excluded.
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
|
|
|