; ; 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 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 wData ; get high byte of SWIM wData reg stx 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 PollRate ; get high byte of poll rate sta TMPBtimeH,y ; store it into the TMPB 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 ; 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 ; 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 #TagBuffers ; add in starting page of tag buffers sta TagBuffers ; add in starting page of tag buffers sta = 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 prev sty cur ldx 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 prev sty cur lda DataBuffers ; add in starting page of data buffers sta BCPBiopAddrH,y ; remember the data page lda ReqDriveStatus ; get page number of status message buffer ldx ReqDriveStatus ; get page number of status message buffer ldx (DataBuffers+(RawDataBufferNum*BlockSize)+RawByteCountMax+$FF) jsr SetupRawBCPB ; copy the ReqRawDataAddress sty (DataBuffers+(RawClockBufferNum*BlockSize)+RawByteCountMax/8+$FF) jsr SetupRawBCPB ; copy the ReqRawClockAddress sty (DataBuffers+\ (RawTempBufferNum*BlockSize)) ; page number of temp buffer sta (DataBuffers+\ (RawClockBufferNum*BlockSize)+\ RawByteCountMax/8+$FF) ; see if end has been reached bne WaitNextByte ; keep looping until end of buffer ldy 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 #ChuckingDelay ; get high byte of Disk Chucking wait time lda #MotorOnDelay ; get high byte of Disk Motor On wait time lda #MotorOnSettle ; get high byte of settle time sta SettleTimeH ; set high byte of Motor speed settle time 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 # 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 MotorOnDelay ; get high byte of Disk Motor On wait time lda #MotorOnSettle ; get high byte of settle time sta SettleTimeH ; set high byte of Motor speed settle time lda # 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 SeekDelay ; get high byte of Seek initial wait time lda #= D, subtract denominator @CylinderShift rol = PhysSectsPerHead, loop sty 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 -AddrSearchMax ; high byte of search byte count sta AddrSearchMax+3 ; count of bytes searched before error sta AddrSearchMax-1 ; update count of bytes preceeding header sta DataBuffers ; compute the data buffer page number sta TagBuffers ; add in starting page of tag buffers sta -AddrSearchMax ; high byte of search byte count sta AddrSearchMax ; count of bytes searched before error sta AddrSearchMax-1-4 ; update count of bytes preceeding header sta DataBuffers ; compute the data buffer page number sta TagBuffers ; add in starting page of tag buffers sta (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 DMAxferDelay ; high byte jsr StartReadyTimer ; start the timer pla ; pop pcL plx ; pop pcH ldy DMAxferDelay ; high byte jsr StartReadyTimer ; start the timer pla ; pop pcL plx ; pop pcH ldy 2 ; assume that Sync will more than fill the FIFO lda MFMSyncValue ; get the sync value sta (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 DataBuffers ; get the starting page number stx DataBufSize ; number of pages to initialize @fillerLoop sta (GCRGapBuffer ; point to the data buffer stx GCRGapBuffer ; point to gap sync data buffer sta (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 @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 = D, subtract denominator @DivideShift rol MFMGapBuffer ; point to gap data buffer sta MFMGapBuffer ; point to gap data buffer sta 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 (-MFMFinalGapSize); get high byte of negated timeout sta