mac-rom/Drivers/IOP/SWIMDriver.aii

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