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