supermario/base/SuperMarioProj.1994-02-09/Drivers/Sony/SonyMFM.a

2355 lines
86 KiB
Plaintext
Raw Normal View History

2019-06-29 15:17:50 +00:00
;
; File: SonyMFM.a
;
; Contains: SWIM/SuperDrive MFM Floppy Disk Support Routines
;
; Written by: Steve Christensen 16-May-86
;
; Copyright: © 1986-1990, 1992 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <SM10> 1/10/93 RC Added nops for Smurf
; <SM9> 12/14/92 RC Restore Pre-PDM D2 with Horror Roll in
; <SM7> 12/9/92 RC Fixed a build bug which was introduced by the Horror Roll in.
; The bug was in SetChipMode. If nonSerializedIO is TRUE, then
; the nop's will cause one of the BEQ.S to needs to be a BEQ.
; <SM6> 12/8/92 rab Removed a forROM conditional from jtISMRdData that was causing
; an 'Uncompleted conditional directive' warning.
; <SM5> 12/7/92 rab Roll in Horror changes. Comments follow…
; <H11> 9/22/92 DHN Fixed problem in CheckCRC with SWIM2 ASIC where rHandShake bit 8
; (data avail) goes high before its other bits are valid. We read
; rHandShake again to get the valid bits. There are other related
; fixes.
; <H10> 7/21/92 NJV Removed hasSonora1 conditionalized code (no longer needed)
; <H9> 7/15/92 CMP Patched read data mark code to allow for a mark byte in the
; middle of the sync field due to a late write splice. We now
; allow for one bogus mark byte before giving up and returning
; noDMark error.
; <H8> 5/12/92 CMP Patched RdAddrMark. When _PollSCC was called it was messing up
; timing, causing address marks to be missed occaisionally. The
; SCC polling code was put inline to fix this.
; <H6> 3/6/92 CMP Increased mByteTOCnt to 60 to accomodate 33Mhz. CPUs.
; <H4> 11/14/91 SWC Overpatched the SWIM2 code. Patched mAdrDisk to handle HDSEL
; selection using the HDSEL pin on SWIM/SWIM2 in addition to the
; VIA bit so we'll work with DB-Lite's connector bars.
; <SM4> 10/18/92 CCH Added nop's for systems with non-serial writes to IO space.
; <5> 7/14/92 CSS Fixed the comment below so an exact version of this
; file could be copied into SuperMario.
; <4> 4/27/92 JSM Get rid of conditionals: dont use onMacXX style conditionals,
; has3rdFloppyand supportsDCD are always false, forROM is always
; true. Only conditional left is CPU check.
; <3> 7/30/90 BG Removed CPU = 040 conditional in preparation for moving from
; Mac040 to Mac32 build.
; <2> 1/4/90 BG Define preliminary 040-based *reWriteAdj/Mul/Div* values. Also
; added Marks to the file for all routines.
;
; <2.5> 5/26/89 GGD Changed conditional for write gap timing for 020/030 machines.
; <2.4> 5/23/89 GGD No changes to this file, entire Sony Driver is checked out and
; in as a group.
; <2.3> 4/29/89 GGD No changes to this file, entire Sony Driver is checked out and
; in as a group.
; <2.2> 4/10/89 gmr No changes to this file, entire Sony Driver is checked out and
; in as a group.
; <2.1> 2/21/89 GGD Added VIA accesses during mPulse to waste some time to ensure
; adequate pulse width of LSTRB. Added VIA accesses in mDiskSel to
; kill some time.
; <2.0> 12/15/88 GGD Re-wrote write splice timing loop to use TimeVIADB for better
; accuracy. Changed some machine based conditionals to feature
; based.
; <1.2> 11/21/88 CCH Changed ForRam equates to ForRom.
; <1.1> 11/11/88 CCH Fixed Header.
; <1.0> 11/9/88 CCH Adding to EASE.
;
; <1.9> 9/29/88 GGD Changed the gap between sectors for 1440K disks (gap3Sz1440K)
; from 108 to 101 bytes to conform to Sony's latest request to
; improve speed variation margins, and to be compatible with the
; way that IBM is now doing it. Also changed to write splice
; timing computation to use the constant gap2Size instead of the
; hard coded value of 22 (doesn't change the object code).
; <1.8> 9/19/88 GGD SWC/Put the index mark code back into the format routine.
; <1.8> 9/12/88 SWC Put the index mark code back into the format routine. Increased
; the number of read tries in mVerTrack.
; <1.7> 8/16/88 GGD Adjusted write splice timing constants forROM on HcMac (2 wait
; states now) Saved reg D2 in SetChipMode when calling SetIWMMode
; to prevent potential infinate loop, if hardware fails.
; <1.7> 8/11/88 GGD Adjusted write splice timing constants forROM on HcMac (2 wait
; states now) Saved reg D2 in SetChipMode when calling SetIWMMode
; to prevent potential infinate loop, if hardware fails.
; <1.6> 7/15/88 GGD Added extra delays in SetParams to fix violation of SWIM timing
; restriction.
; <1.6> 7/14/88 GGD Added extra delays in SetParams to fix violation of SWIM timing
; restriction.
; <1.5> 6/15/88 GGD No changes to this file, entire Sony Driver is checked out and
; in as a group.
; <1.4> 5/25/88 GGD SWC/Fixed to previous fix in format code, write an extra gap
; btye before turning on Index.
; <1.4> 5/25/88 SWC Fixed to previous fix in format code, write an extra gap btye
; before turning on Index.
; <1.3> 5/24/88 GGD SWC/Turned the index signal off and on in mWrData and mDoFormat
; by selecting mfmDrvAdr/drvModeAdr instead of RdDtaAdr/RdDta1Adr
; so that the index pulse noise problem in the drive will not
; manifest itself anymore.
; <1.3> 5/18/88 SWC Turned the index signal off and on in mWrData and mDoFormat by
; selecting mfmDrvAdr/drvModeAdr instead of RdDtaAdr/RdDta1Adr so
; that the index pulse noise problem in the drive will not
; manifest itself anymore.
; <1.2> 5/3/88 GGD Just checked out and back in to get file version in sync with
; other files in the Sony Driver.
; <1.1> 5/3/88 GGD mWrData: poll the SCC in the initial wait loop. Tuned write
; splice and byte timeout timing constants. Added onNuMacDCD
; support. Made the address mark checking more flexible in
; mRdAddr.
;
; <1.2> 5/2/88 SWC Made the address mark checking more flexible in mRdAddr.
; <1.2> 4/29/88 GGD Added onNuMacDCD support.
; <1.2> 4/28/88 GGD Tuned write splice and byte timeout timing constants.
; <1.2> 4/26/88 SWC mWrData: poll the SCC in the initial wait loop.
; <1.0> 4/18/88 GGD MFM Support <C437> by Steve Christensen
; <1.1> 3/29/88 GGD Removed GCRonMFMErr, since it is now in SysErr.a
; 3/10/88 SWC ChkDiskType: set DiskInPlace(A1,D1) to 3 for 1440K GCR.
; 3/1/88 SWC Updated to latest SWIM params recommended by Sony (their rev
; 4.0).
; 2/23/88 SWC Added GCRonMFMErr equate for 1440K MFM disks formatted as GCR.
; ChkDiskType: return an error if a 1440K MFM disk is formatted as
; GCR.
; 2/22/88 SWC Modified the format code to save a revolution per cylinder
; (saves 16 sec). mVerTrack: changed DataVerErr to VerErr to be
; consistent with GCR verify. mVerTrack: check that the track and
; side numbers are OK as well.
; 2/10/88 SWC ChkDiskType: do a Recal if 2meg to keep chip mode in sync with
; mfmDisk(A1,D1).
; <C437> 2/5/88 SWC Fixed the gap lengths to conform with <ugh> IBM standard.
; <C907> 2/4/88 GGD Port to HcMac (Laguna).
; 2/1/88 SWC Oops! Steve copied the SE internal drive switch bug from
; DiskSelect into mDiskSelect, but now it's fixed.
; 1/25/88 SWC Compacted the read-mark-byte loops in mRdAddr and mRdData. New
; parameters! (more to come, no doubt).
; 1/20/88 SWC CRC error -OR- error register bit set -> flag as CRC error.
; 1/11/88 SWC Increased SE timeout counter value (we were timing out when the
; code was put into ROM). Always set error correction OFF (noisy
; environments were messing up its performance).
; 12/22/87 SWC Fixed SetChipMode so we always exit in a non-write state if the
; IWM register set is selected.
; 12/8/87 SWC Move parameter RAM setup to SetParams so we can use various
; parameter sets for different portions of the disk. Always set
; error correction ON for address fields (mRdAddr) and OFF for
; data fields (mRdData).
; 12/4/87 SWC Added a second access of the error register to make sure the
; chip wasn't staying in a weird state for R/W operations. Tied
; the re-write delay into TimeDBRA so we're relatively independent
; of processor speed.
; 11/23/87 SWC Clear SideTrack(A1) in GetASector (ChkDiskType) so that we will
; read off of side 0 (in case it's a 400K disk).
; 11/5/87 SWC Turned off error correction as the default setting.
; 9/30/87 SWC Fixed the sector re-write gap time in mWrData.
; 9/3/87 SWC Changed mDoFormat slightly to handle greater drive speed
; variation.
; 8/18/87 SWC Changed the IWM->ISM switch to the new "magic sequence".
; 7/9/87 SWC Added delays (SCC checks) to mDoFormat to increase the time
; between successive chip accesses when running on a Mac II.
; 4/16/87 SWC Converted ICBM routines to work with SWIM. Added mAdrDisk,
; mSense, mPulse, mDiskSelect to handle disk command/status when
; the MFM register set is selected.
; <C437> 2/25/87 SWC Added these routines to Sony driver to support MFM disks.
title 'File: SonyMFM.a'
;______________________________________________________________________________
;
; SWIM/SuperDrive MFM Floppy Disk Support Routines
;
; This file contains all routines necessary to support MFM disks using the
; SWIM disk controller and Sony MFM/GCR drives. The rest of the Sony driver
; has been modified where necessary so that it knows about MFM. In general,
; the MFM routines that have GCR counterparts look the same to the outside
; world.
;
; Routines:
;
; Check4SWIM -- Checks for the presence of a SWIM, and if one is found,
; initializes it and the appropriate Sony vars.
; SetChipMode -- selects the appropriate register set (IWM/SWIM) for the
; current disk.
; SetParams -- stuffs a set of 16 values into the SWIM's parameter RAM
; based on the current track.
; ChkDiskType -- finds out whether an MFM or GCR disk is inserted.
; mAdrDisk -- selects a drive address without reading or writing.
; mSense -- reads the state of the currently-selected drive address.
; mPulse -- strobes the LSTRB (phase 3) line to execute a drive command.
; mDiskSelect -- finishes disk selection if the SWIM register set is selected.
; mGetTrack -- given the block number, calculate the track, head, sector.
; mRdAddr -- Returns information about the next address mark to spin by.
; mRdData -- Reads the contents of a sector into the user's buffer.
; mWrData -- Writes the contents of the user's buffer into a sector.
; mFmtTrack -- Formats both sides of the current track
; mDoFormat -- Formats one side of the current track.
; mVerTrack -- Verifies both sides of the current track.
;______________________________________________________________________________
BLANKS ON
STRING ASIS
;______________________________________________________________________________
;
; MFM Read/Write/Format constants used only in this file
;______________________________________________________________________________
;Field sizes:
gap4aSize EQU 80 ;Gap between index and index mark <1.8>
gap1Size EQU 50 ;Gap between index mark and sector 1 <1.8>
gap2Size EQU 22 ;Gap between address and data fields
gap3Sz720K EQU 80 ;Gap between adjacent sectors (720K)
gap3Sz1440K EQU 101 ;Gap between adjacent sectors (1440K) <1.9>
gap4bSize EQU 1000 ;Gap from end of last sector to index (max) <1.8>
syncSize EQU 12 ;Sync field size
markSize EQU 4 ;Size of index/address/data marks
gapByte EQU $4E ;Inter/intra sector gap byte
theSync EQU $00 ;Sync byte preceeding each mark
fmtFillByte EQU $F6-256 ;Format byte to fill sectors with
noIndexErr EQU Fmt2Err ;Use this one to show index timeout
dataTOErr EQU noNybErr ;Timed out while waiting to read a byte
mByteTOCnt EQU 60 ; byte timeout count (for reading) <H6><SM5>
mSctrTOCnt EQU 20000 ;"Look for Address Mark" timeout count
reWriteAdj EQU (71+60)/2 ; uSecs already used from 22 byte write gap time <2.0>
reWriteMul EQU 20 ; number of clocks in ROM TimeVIADB loop <2.0>
reWriteDiv EQU 20 ; number of clocks in RAM TimeVIADB loop <2.0>
;______________________________________________________________________________
;
; The format of an MFM track is as follows:
;
; Index Pulse->
; [ 80] gap bytes $4E
; [ 12] sync field $00
; [ 4] index mark $C2,$C2,$C2,$FC
; [ 50] gap $4E
; LOOP:
; [ 12] sync field $00 <-------------------+
; [ 4] address mark $A1,$A1,$A1,$FE <-------+ |
; [ 1] cylinder number $00 to $4F | |
; [ 1] side number $00 to $01 address field |
; [ 1] sector number $01 to $09/$12 | |
; [ 1] format byte $02=512 bytes/sector | |
; [ 2] CRC $0000 to $FFFF <-------+ 1 sector
; [ 22] inter-sector gap $4E |
; [ 12] sync field $00 |
; [ 4] data mark $A1,$A1,$A1,$FB <-------+ |
; [512] sector data $00 to $FF data field |
; [ 2] CRC $0000 to $FFFF <-------+ |
; [ 80] gap (101 for 1440K) $4E <-------------------+
; REPEAT FOR 8 or 17 MORE SECTORS
; [xxx] gap to index $4E
; Index Pulse->
;______________________________________________________________________________
;______________________________________________________________________________
;
; Routine: Check4SWIM
;
; Inputs: none
;
; Outputs: A1 -- pointer to SonyVars
;
; A0,D0 are trashed
;
; Function: Checks for the presence of a SWIM, and if one is found, sets isSWIM(A1).
;______________________________________________________________________________
Check4SWIM MOVEA.L SonyVars,A1
MOVE SR,-(SP) ;Save SR
ORI #HiIntMask,SR ;and disable interrupts
MOVEA.L IWM,A0
TST.B IntDrive(A0) ;Be sure the internal drive is selected
TST.B Q7L(A0) ;Select the mode register
TST.B MtrOff(A0)
TST.B Q6H(A0)
MOVEQ #$40++IWMMode,D0
MOVE.B D0,Q7H(A0) ;Try to put the chip into MFM mode
MOVE.B #IWMMode,Q7H(A0) ;The "magic sequence" is to set bit 6
MOVE.B D0,Q7H(A0) ; to "1", "0", "1", "1" over four
MOVE.B D0,Q7H(A0) ; consecutive accesses
MOVEQ #$F5-256,D0 ;If we can write to the phase register
MOVE.B D0,wPhase(A0) ;and read back the same value, this
if NonSerializedIO then
nop ; force write to complete force write to complete <SM4>
endif
CMP.B rPhase(A0),D0 ;must be an SWIM
BNE.S @1 ;-> mismatch: IWM
MOVEQ #$F6-256,D0
MOVE.B D0,wPhase(A0)
if NonSerializedIO then
nop ; force write to complete
endif
CMP.B rPhase(A0),D0
BNE.S @1 ;-> mismatch: IWM
MOVEQ #$F7-256,D0
MOVE.B D0,wPhase(A0)
if NonSerializedIO then
nop ; force write to complete
endif
CMP.B rPhase(A0),D0
BNE.S @1 ;-> mismatch: IWM
ST isSWIM(A1) ;A SWIM is installed
MOVE.B #$F8,wZeroes(A0) ;Switch back to IWM register set
if NonSerializedIO then
nop ; force write to complete
endif
@1 TST.B Q7L(A0) ;Back to read state with the drive enabled
TST.B Q6L(A0)
TST.B MtrOn(A0)
TST.B isSWIM(A1) ; do we have at least a SWIM <SM5>
BPL.S @2 ; -> no, done <SM5>
MOVEQ #$F5-256,D0 ; if we can write to the phase register <SM5>
MOVE.B D0,wPhase(A0) ; and read back the same value, this <SM5>
if NonSerializedIO then
nop ; force write to complete <SM5>
endif
CMP.B rPhase(A0),D0 ; must be an SWIM2 <SM5>
BNE.S @2 ; -> mismatch: SWIM <SM5>
MOVEQ #$F6-256,D0 ; <SM5>
MOVE.B D0,wPhase(A0) ; <SM5>
if NonSerializedIO then
nop ; force write to complete <SM5>
endif
CMP.B rPhase(A0),D0 ; <SM5>
BNE.S @2 ; -> mismatch: SWIM <SM5>
MOVEQ #$F7-256,D0 ; <SM5>
MOVE.B D0,wPhase(A0) ; <SM5>
if NonSerializedIO then
nop ; force write to complete <SM5>
endif
CMP.B rPhase(A0),D0 ; <SM5>
BNE.S @2 ; -> mismatch: SWIM <SM5>
SUBQ.B #1,isSWIM(A1) ; a SWIM2 is installed <SM5>
ST mfmMode(A1) ; make sure we always use the ISM register set <SM5>
LEA jtISMRdAddr,A0 ; use special SWIM2 routines for GCR reading <SM5>
MOVE.L A0,JRdAddr ; <SM5>
LEA jtISMRdData,A0 ; <SM5>
MOVE.L A0,JRdData ; <SM5>
BSR CheckFor32Mhz ; check for high speed clock to SWIM2 <H9><SM5>
@2 MOVE (SP)+,SR ;Enable interrupts
RTS
;______________________________________________________________________________
;
; Routine: SetChipMode
;
; Inputs: none
;
; Outputs: D0 -- result code
;
; Function: Selects the appropriate register set for the current disk.
;______________________________________________________________________________
;
; If we can write to the SWIM phase register and read back the same values,
; we must be in SWIM mode. The three phase combinations used are safe for
; use even if an HD-20 is selected since they are the ID states...
SetChipMode BSR GetDrv1 ;Setup A1,D1
TST.B isSWIM(A1) ;Is a SWIM connected?
BPL SetIWMMode ;-> if not, just init IWM style
MOVE SR,-(SP) ; save SR <SM5>
ORI #HiIntMask,SR ; and disable interrupts <SM5>
MOVEA.L IWM,A0 ; <SM5>
CMP.B #-2,isSWIM(A1) ; see if this is a SWIM2 <SM5>
BEQ @2 ; <SM5>
MOVEQ #2-1,D2 ;Retry count for setting MFM mode
@0 MOVEA.L IWM,A0
MOVE.B #$38,wZeroes(A0) ;Turn off ACTION in case we're in ISM mode
if NonSerializedIO then
nop ; force write to complete <SM5>
endif
MOVEQ #$F5-256,D0
MOVE.B D0,wPhase(A0)
if NonSerializedIO then
nop ; force write to complete
endif
CMP.B rPhase(A0),D0
BNE.S @1 ;-> mismatch: IWM register set
MOVEQ #$F6-256,D0
MOVE.B D0,wPhase(A0)
if NonSerializedIO then
nop ; force write to complete
endif
CMP.B rPhase(A0),D0
BNE.S @1 ;-> mismatch: IWM register set
MOVEQ #$F7-256,D0
MOVE.B D0,wPhase(A0)
if NonSerializedIO then
nop ; force write to complete
endif
CMP.B rPhase(A0),D0
BNE.S @1 ;-> mismatch: IWM register set
; If we get to this point, the SWIM register set is currently selected...
TST.B mfmDisk(A1,D1) ;Do we want to be in MFM mode?
BMI.S @2 ;-> yes, go finish the initialization
MOVE.B #$F8,wZeroes(A0) ;else switch back to the IWM register set
if NonSerializedIO then
nop ; force write to complete
endif
TST.B Q7L(A0) ;and get out of write mode
; ...and here the IWM register set is selected...
@1 CLR.B mfmMode(A1) ;We're in IWM mode
MOVE.L D2,-(SP) ; preserve D2 (SetIWMMode uses it) <1.7>
BSR SetIWMMode ; so go do a normal initialization
MOVE.L (SP)+,D2 ; restore D2 <1.7>
TST.W D0 ; re-check the error code <1.7>
BNE.S @6 ;-> couldn't initialize the chip
TST.B mfmDisk(A1,D1) ;Is IWM mode where we want to be?
BPL.S @6 ;Yes, all done
TST.B Q7L(A0) ;Select the mode register
TST.B MtrOff(A0)
TST.B Q6H(A0)
MOVEQ #$40++IWMMode,D0
MOVE.B D0,Q7H(A0) ;and try to put the chip into MFM mode
MOVE.B #IWMMode,Q7H(A0) ;The "magic sequence" is to set bit 6
MOVE.B D0,Q7H(A0) ; to "1", "0", "1", "1" over four
MOVE.B D0,Q7H(A0) ; consecutive accesses
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D2,@0 ;Go verify we're in MFM mode
BRA.S @4 ;Couldn't do it after 2 tries, so return an error
; We're in SWIM mode, so make sure the registers are set up correctly...
@2 MOVE.B #$38,wZeroes(A0) ;Make sure ACTION's cleared
if NonSerializedIO then
nop ; force write to complete <SM5>
endif
MOVE.B #$C0,wIWMConfig(A0) ;Set timer kill + 16MHz timer
if NonSerializedIO then
nop ; force write to complete <SM5>
endif
MOVEQ #$20,D0 ;Init the Setup register to disable the ECM
TST.B mfmDisk(A1,D1) ; is this an MFM disk? <SM5>
BMI.S @MFM ; -> yes <SM5>
MOVEQ #$44,D0 ; GCR setup <SM5>
@MFM ; <SM5>
CMP.B #-2,isSWIM(A1) ; SWIM2? <H9><SM5>
BNE.S @7 ; -> no, setup is fine the way it is <H9><SM5>
TST.B Clock32Mhz(A1) ; do we have a 32Mhz Clock? <H9><SM5>
BEQ.S @7 ; -> no <H9><SM5>
BSET #3,D0 ; yes, set the half speed bit <H9><SM5>
@7 ; <H9><SM5>
MOVE.B D0,wSetup(A0) ; write the setup register <SM5>
if NonSerializedIO then
nop ; force write to complete
endif
CMP.B rSetup(A0),D0 ;Was it written OK?
BNE.S @3 ;-> no, exit with an error
BSR.S SetParams ;Initialize the parameter RAM
BEQ.S @5
@3 MOVE.B #$F8,wZeroes(A0) ;Couldn't init, so shut everything down
if NonSerializedIO then
nop ; force write to complete
endif
@4 TST.B Q7L(A0) ;and return to IWM in a non-write state
MOVEQ #InitIWMErr,D0 ;Return an error
@5 SEQ mfmMode(A1) ;Update the current mode
@6 MOVE (SP)+,SR ;Enable interrupts
TST.W D0 ;Set the CCR
RTS
;______________________________________________________________________________
;
; Routine: SetParams
;
; Inputs: A0 -- pointer to SWIM's base address
; A1 -- pointer to SonyVars
; D1 -- offset to disk's local vars
; track(A1,D1) contains the current track number
;
; Outputs: D0 -- result code (either noErr or InitIWMErr)
;
; Function: Stuffs in the appropriate set of parameters into the SWIM's
; parameter RAM based on which disk zone the current track is in.
; This is called by either SetChipMode or Seek.
;______________________________________________________________________________
SetParams ; <SM5> begin
LEA mParams,A2 ; assume it's MFM mode
TST.B mfmDisk(A1,D1) ; is it?
BMI.S @MFM
LEA gParams,A2 ; no, use the GCR parameter set
@MFM
CMP.B #-2,isSWIM(A1) ; see if this is a SWIM2
BNE.S NotSWIM2 ; -> it's not
MOVE.L D3,-(SP) ; save D3 <H2><H4>
MOVEA.L VIA,A1 ; get VIA1 base address
ADDA.W #VBufD,A1 ; 6522 disk-reg has head sel, wait/req
MOVEQ #5-1,D2 ;give a few tries to set up the params
@1 MOVE.B #$38,wZeroes(A0) ;reset the parameter register counter
SWAP D2 ;(save the loop counter)
MOVEQ #4-1,D0
@2 MOVE.B (A1),(A1) ; access VIA twice to kill some time
MOVE.B rParams(A0),D2 ; read first parm value
MOVE.B rParams(A0),D3 ; clear next value out, it is not used
MOVE.B 0(A2,D0),D3 ; get the value it should be
SUB.B #1,D0 ; decrement by two
LSR.B #4,D2 ; compare high nibbles
LSR.B #4,D3 ; compare high nibbles
CMP.B D2,D3
DBNE D0,@2
BNE.S @3 ;-> no, go stuff the parameters into the chip
MOVEA.L SonyVars,A1 ; restore pointer to SonyVars
MOVE.L (SP)+,D3 ; restore D3
MOVEQ #0,D0 ; parameters all set up, return with no error
RTS
@3 MOVE.B #$38,wZeroes(A0) ;reset the parameter register counter
MOVEQ #4-1,D0
@4 MOVE.B (A1),(A1) ; access VIA twice to kill some time
MOVE.B 0(A2,D0),wParams(A0); and initialize the parameter RAM
DBRA D0,@4
SWAP D2 ;(restore the loop counter)
DBRA D2,@1 ;try again?
MOVEA.L SonyVars,A1 ; restore pointer to SonyVars
MOVE.L (SP)+,D3 ; restore D3
MOVEQ #InitIWMErr,D0 ; couldn't initialize RAM so return an error
RTS
NotSWIM2 ; <SM5> end
MOVEA.L VIA,A1 ; get VIA1 base address <2.1>
ADDA.W #VBufD,A1 ; 6522 disk-reg has head sel, wait/req <2.1>
MOVEQ #5-1,D2 ;give a few tries to set up the params
@1 MOVE.B #$38,wZeroes(A0) ;reset the parameter register counter <1.6>
if NonSerializedIO then
nop ; force write to complete
endif
SWAP D2 ;(save the loop counter) <1.6>
MOVEQ #16-1,D0
@2 MOVE.B (A1),(A1) ; access VIA twice to kill some time <2.1>
if NonSerializedIO then
nop ; force write to complete
endif
MOVE.B rParams(A0),D2 ;check if the parameter values are already set up
CMP.B 0(A2,D0),D2
DBNE D0,@2
BNE.S @3 ;-> no, go stuff the parameters into the chip
MOVEA.L SonyVars,A1 ; restore pointer to SonyVars <2.1>
MOVEQ #0,D0 ;the parameters are all set up so return with no error
RTS
@3 MOVE.B #$38,wZeroes(A0) ;reset the parameter register counter
MOVEQ #16-1,D0
@4 MOVE.B (A1),(A1) ; access VIA twice to kill some time <2.1>
MOVE.B 0(A2,D0),wParams(A0); and initialize the parameter RAM
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,@4
SWAP D2 ;(restore the loop counter)
DBRA D2,@1 ;try again?
MOVEA.L SonyVars,A1 ; restore pointer to SonyVars <2.1>
MOVEQ #InitIWMErr,D0 ;couldn't initialize the parameter RAM so return an error
RTS
; This table contains the last track number+1 and table offset for each of the
; parameter zones on an MFM disk. Different parameter sets can be used to
; optimize the error correction performance to handle variations in peak shift,
; drive speed, etc.
mParamZones DC.W 80,mParams-mParamZones ;change back after getting the 2nd parm set right
DC.W 80,mParams+16-mParamZones
DC.W 80,mParams-mParamZones
DC.W 80,mParams-mParamZones
; Parameter register values in reverse order.
;
; The timing values Time1, Time0, xSx, xLx and Min have a resolution to
; the nearest half clock, so their actual register values are doubled so
; that bits 1-7 become the integer part and bit 0 becomes the ".5" part.
; xSx and xLx are reduced by 2 clocks, and Min is reduced by 3 clocks to
; take into account gate delays in the SWIM.
; For a 15.6672MHz input clock (Mac SE, Mac II, etc):
mParams DC.B (31-2)*2+1 ;[F] Time1 = 31.5
DC.B $57 ;[E] Early/Norm = $57
DC.B (15-2)*2+1 ;[D] Time0 = 15.5
DC.B $97 ;[C] Late/Norm = $97
DC.B (14-2)*2+1 ;[B] LLS = 14.5
DC.B (14-2)*2+1 ;[A] LLL = 14.5
DC.B (25-2)*2+1 ;[9] LSS = 25.5
DC.B (25-2)*2+1 ;[8] LSL = 25.5
DC.B (15-2)*2+1 ;[7] CSLS = 15.5
DC.B (15-2)*2+1 ;[6] RPT = 15.5
DC.B (14-2)*2+0 ;[5] SLS = 14.0
DC.B (14-2)*2+0 ;[4] SLL = 14.0
DC.B (25-2)*2+0 ;[3] SSS = 25.0
DC.B (25-2)*2+0 ;[2] SSL = 25.0
DC.B 65 ;[1] Mult = 65
DC.B (15-3)*2 ;[0] Min = 15.0
gParams DC.B (32-2)*2+0 ;[F] Time1 = 32.0 <SM5> begin
DC.B (8*16)+8 ;[E] Early/Norm = 8 / 8
DC.B (32-2)*2+0 ;[D] Time0 = 32.0
DC.B (8*16)+8 ;[C] Late/Norm = 8 / 8
DC.B (32-2)*2+0 ;[B] LLS = 32.0
DC.B (32-2)*2+0 ;[A] LLL = 32.0
DC.B (32-2)*2+0 ;[9] LSS = 32.0
DC.B (32-2)*2+0 ;[8] LSL = 32.0
DC.B (32-2)*2+0 ;[7] CSLS = 32.0
DC.B (32-2)*2+0 ;[6] RPT = 32.0
DC.B (32-2)*2+0 ;[5] SLS = 32.0
DC.B (32-2)*2+0 ;[4] SLL = 32.0
DC.B (32-2)*2+0 ;[3] SSS = 32.0
DC.B (32-2)*2+0 ;[2] SSL = 32.0
DC.B 64 ;[1] Mult = 64
DC.B (16-3)*2+0 ;[0] Min = 16.0 <SM5> end
; Alternate parameter sets which may be used in the future or removed entirely.
; For now they simply occupy 32 (2*16) bytes of space so we don't have to worry
; about shuffling pieces of the driver about ROM when parameter changes come along.
DC.B $53,$4F,$2E,$2E,$2E,$57,$48,$41,$54,$20,$41,$52,$45,$20,$59,$4F
DC.B $55,$20,$53,$54,$41,$52,$49,$4E,$47,$20,$41,$54,$3F,$20,$20,$20
;______________________________________________________________________________
;
; Routine: ChkDiskType
;
; Inputs: A0 -- pointer to IWM/SWIM base address
; A1 -- pointer to SonyVars
; D1 -- offset to disk's local vars
; mfmDisk(A1,D1)=0
;
; Outputs: mfmDisk(A1,D1) is set depending on the disk format type.
; If it's an MFM disk, twoMegFmt(A1,D1) is set accordingly.
;
; Function: Checks whether an MFM or GCR disk has just been inserted.
; On entry, the drive is powered up for GCR and DiskInPlace(A1,D1)=2.
; If a 1440K disk is formatted as a GCR 400K or 800K disk, it will
; set DiskInPlace(A1,D1) to 3 to indicate the fact (but also to mark
; it as clamped for anyone that cares), and return with a special
; error for the format package to figure out.
;
; Note that ChkDiskType can't be called as a subroutine since it
; must be on the same stack level as DiskPrime because WakeUp gets
; called (from Recal).
;______________________________________________________________________________
ChkDiskType MOVE.B isSWIM(A1),D0 ;Are we using a SWIM
AND.B mfmDrive(A1,D1),D0 ;and a SuperDrive?
BPL.S @2 ;-> no, just return
MOVEQ #TwoMegAdr,D0 ;Check for double-density disk
BSR AdrAndSense
SPL twoMegFmt(A1,D1) ;$FF if so
BSR.S GetASector ;Try to find a GCR sector
TST.W D0
BMI.S @0 ;-> it's not GCR, but it might be MFM
MOVE.B twoMegFmt(A1,D1),mfmDisk(A1,D1) ;is this a 1440K disk?
BPL.S @2
ADDQ.B #1,DiskInPlace(A1,D1) ;make it 3 to signal weird format, but clamped
MOVE.W #GCRonMFMErr,D0 ;yes, we won't allow it to be used in GCR mode
BRA DskRWOff
@0 ST mfmDisk(A1,D1) ;Now try for an MFM disk
BSR Recal ;Select the SWIM regs + power up drive in MFM mode
BNE.S @1 ;-> error
TST.B twoMegFmt(A1,D1) ;is this a 1440K disk?
BMI.S @2 ;-> yes, it's gotta be MFM
BSR.S GetASector ;Try to find an MFM sector
SUBI.B #$22,D3 ;We only support type 2 (512 byte blocks)
OR.W D3,D0 ;No error too?
BEQ.S @2 ;-> yes, all done
@1 MOVE.B twoMegFmt(A1,D1),mfmDisk(A1,D1) ;MFM if no error and right format type
; -OR- it's a double-density disk
@2 BRA CkWrProt ;Back to Prime where we left off...
GetASector MOVEQ #5,D1
@1 MOVE.W D1,-(SP) ;Save the retry counter
MOVE SR,-(SP) ;Save the interrupt level
CLR.W SideTrack(A1) ;Make sure we read off of side 0 (just in case it's 400K)
BSR RdAddrSetup ;Try to find a sector
BSR EmptyPD ;Get rid of any SCC data (sets up A1,D1)
MOVE (SP)+,SR ;Restore the interrupt state
MOVE.W (SP)+,D1 ;and the retry counter
TST.W D0 ;Was the read successful?
DBPL D1,@1 ;-> loop if not
BRA GetDrv1 ;Setup A1,D1
;______________________________________________________________________________
;
; Routine: mAdrDisk
;
; Inputs: D0 -- drive address ([ca1][ca0][sel][ca2])
; D1 -- word offset of current drive's specific vars
; A0 -- IWM base ptr
; A1 -- pointer to driver variables
; A2 -- VIA base ptr
;
; Outputs: same
;
; Function: Selects the input drive address by manipulating the phase lines
; in the SWIM and the HDSEL line in the VIA.
;______________________________________________________________________________
mAdrDisk MOVE.B #$F3,wPhase(A0) ;CA0,CA1 must be 1 when changing SEL to zero
if NonSerializedIO then
nop ; force write to complete
endif
BTST #1,D0 ;Is SEL high or low? <SM5>
BEQ.S @1 ; <SM5>
MOVE.B #$20,wOnes(A0) ;high <SM5>
BRA.S @2 ; <SM5>
@1 MOVE.B #$20,wZeroes(A0) ;low <SM5>
@2 BTST #1,D0 ;Is SEL high or low? <SM5>
BEQ.S @3
BSET #VHeadSel,VBufD(A2) ;high
BRA.S @4
@3 BCLR #VHeadSel,VBufD(A2) ;low
@4 MOVE.B mPhaseTbl(D0),wPhase(A0) ;Set the rest of the lines
if NonSerializedIO then
nop ; force write to complete
endif
RTS
mPhaseTbl DC.B $F0,$F4,$F0,$F4,$F1,$F5,$F1,$F5,$F2,$F6,$F2,$F6,$F3,$F7,$F3,$F7,$53,$57,$43,$21
;______________________________________________________________________________
;
; Routine: mSense
;
; Inputs: D1 -- word offset of current drive's specific vars
; A0 -- IWM base ptr
; A1 -- pointer to driver variables
; A2 -- VIA base ptr
;
; Outputs: D0 -- drive status (in bit 7)
;
; Function: Reads the state of the currently-selected drive address.
;______________________________________________________________________________
mSense BTST #3,rHandshake(A0) ;Read the sense bit
SNE D0 ;and move it to bit 7
TST.B D0 ;Set the CCR
RTS
;______________________________________________________________________________
;
; Routine: mPulse
;
; Inputs: D1 -- word offset of current drive's specific vars
; A0 -- IWM base ptr
; A1 -- pointer to driver variables
; A2 -- VIA base ptr
;
; Outputs: same
;
; Function: Strobes the LSTRB (phase 3) line to execute a drive command
; corresponding to the currently-selected drive address.
;______________________________________________________________________________
mPulse MOVE SR,-(SP) ;Disable interrupts
ORI #HiIntMask,SR
MOVE.L D0,-(SP) ;Save D0 just in case
MOVEQ #%11111000-256,D0 ;Mask with phase 3 turned on
OR.B rPhase(A0),D0 ; and add in the states of phases 0-2
MOVE.B D0,wPhase(A0) ;Toggle phase 3 high...
if NonSerializedIO then
nop ; force write to complete
endif
MOVE.B VBufD(A2),VBufD(A2) ; access VIA to kill some time <2.1>
SUBQ.B #8,D0
MOVE.B D0,wPhase(A0) ;...then low
if NonSerializedIO then
nop ; force write to complete
endif
MOVE.L (SP)+,D0 ;Restore D0
MOVE (SP)+, SR ;Enable interrupts
RTS
;______________________________________________________________________________
;
; Routine: mDiskSelect
;
; Inputs: A0 -- pointer to SWIM base address
; A1 -- pointer to SonyVars
; A2 -- pointer to VIA base address
; D1 -- offset to current drive's variables
;
; Outputs: same as input
;
; Function: Finishes the interface selection if we're in MFM mode (SWIM regs).
;______________________________________________________________________________
mDiskSelect
CMP.W #drive1,D1 ;If Drive=1, it's internal
BNE.S @1 ;-> it's not
MOVE.B #$04,wZeroes(A0) ;Disable the external drive and
MOVE.B #$82,wOnes(A0) ;enable the internal one
if NonSerializedIO then
nop ; force write to complete
endif
RTS
@1 MOVE.B #$F7,wPhase(A0) ;Go to state 7 in case we're enabled
if NonSerializedIO then
nop ; force write to complete
endif
BCLR #vHeadSel,VBufD(A2) ; set HdSel low (/Exists addr) <SM5>
MOVE.B #$20,wZeroes(A0) ; <SM5>
MOVE.B #$02,wZeroes(A0) ;Disable the internal drive and
MOVE.B #$84,wOnes(A0) ;enable the external one
if NonSerializedIO then
nop ; force write to complete
endif
RTS
;______________________________________________________________________________
;
; Routine: mGetTrack
;
; Inputs: A0 -- pointer to caller's parameter block
; A1 -- pointer to SonyVars
; A2 -- pointer to driver's DCE
; D0 -- absolute disk block number
; D1 -- offset to drive's local vars
; D7 -- 9 (2^9=512)
;
; Outputs: D0 -- first sector we want on this track - 1
; D2 -- -1
; D3 -- number of sectors/track + 1
; D5 -- track we're on
; D6 -- zero if side zero
;
; Function: Converts a block number into track, head, sector.
;______________________________________________________________________________
mGetTrack MOVEQ #90,D2 ;Assume 720K disk: 1440 blocks/16=90
LSL.W #4,D2 ;(multiply by 16 to get total blocks/disk)
MOVEQ #9,D3 ;Number of blocks/track (one side)
TST.B twoMegFmt(A1,D1) ;Is this a double-density disk?
BPL.S @1
ADD.W D2,D2 ;Yes, 1440K disk: twice as many blocks
MOVEQ #18,D3 ; and sectors
@1 CMP.L D2,D0 ;Block number past end-of-disk?
BGE DskParmErr ;-> yes, return an error
DIVU D3,D0 ;D0.L=[first sector][2*track+side]
LSR.W #1,D0 ;Extract the side number:
SCS D6 ;$00=side 0, $FF=side 1
MOVE.W D0,D5 ;D5=desired track
SWAP D0 ;D0.W=first sector we want - 1
MOVEQ #-1,D2
ADDQ.W #1,D3
BRA gtResume ;Pick up where we left off (SonyRWT)
;_______________________________________________________________________________
;
; Routine: mRdAddr
;
; Inputs: A2 -- pointer to mAddrMarks table
; A5 -- ptr to 6522 A-reg (has head sel, wait/req)
; A6 -- ptr to SCC A-channel data register
;
; Outputs: A3 -- pointer to rHandshake
; A4 -- pointer to rMark
; D0 -- result code
; D1 -- <side><track> (low-order 12 bits)
; D2 -- <sector>
; D3 -- <block size code>+$20 (this should be $22 for 720K and 1440K disks)
;
; A0,D4,D5 are trashed
;
; Function: This routine reads the next address mark that spins by. On exit,
; the attributes of the mark are passed back as shown above.
;
; The disk should be up to speed with the correct head selected, the
; SWIM registers should be selected, and interrupts should be disabled.
;_______________________________________________________________________________
mAddrMarks DC.B $A1,$A1,$A1,$FE
mRdAddr LEA mAddrMarks,A2 ;Point to our address mark table
MOVE.L jMRdAddr,-(SP)
RTS
jtMRdAddr MOVE.L (SP)+,DskRtnAdr ;Save return address
LEA TagData+2,A0
CLR.L (A0)+ ;Zero out the TagData
CLR.L (A0)+
CLR.L (A0)+
MOVEA.L IWM,A4
LEA rHandshake(A4),A3 ;Point to handshake and
LEA rMark(A4),A4 ;mark registers for speed
; Try to find the next address mark. We give a certain amount of time to try to
; find an address mark before timing out. This means that many addresss marks
; (both real and imagined) may be seen since depending on how we sync up with
; the data spinning by, normal bytes can take on a sinister "mark" alias...
MOVEQ #NoAdrMkErr,D0 ;assume we can't find an address mark
MOVE.W #mSctrTOCnt,D2 ;timeout counter (needs to be tuned)
RdAddrMark TST.B rError-rMark(A4) ;clear the error register
MOVE.B #$18,wZeroes-rMark(A4) ;clear the write and action bits <SM5>
MOVE.B #$01,wOnes-rMark(A4) ;toggle the clFIFO bit to clear out <SM5>
MOVE.B #$01,wZeroes-rMark(A4) ; any data in the FIFO <SM5>
if NonSerializedIO then
nop ; force write to complete
endif
BTST #5,rHandshake-rMark(A4) ; check to see if there are any errors <SM5>
BEQ.S @0 ; -> no, continue <SM5>
TST.B rError-rMark(A4) ; yes, clear the error register <SM5>
@0 MOVE.B #$08,wOnes-rMark(A4) ;turn on the action bit: GO! <SM5>
if NonSerializedIO then
nop ; force write to complete
endif
MOVEA.L A2,A0 ;point to our mark table <SM5>
MOVEQ #markSize-1,D1
@1
TST.B (A5) ; throttle execution speed with a VIA access<SM5>
BTST #RxCA,aCtl-aData(A6) ; SCC data available? <SM5>
BEQ.S @2
MOVE.B (A6),-(SP) ; yes, push it on the stack <SM5>
@2 TST.B (A3) ;wait for an address mark byte <SM5>
DBMI D2,@1
BPL ReadExit ;-> timed out (return "No Address Mark") <SM5>
MOVE.B (A4),D5 ;get a byte <SM5>
CMP.B (A0)+,D5 ;is it a correct mark byte? <SM5>
DBNE D1,@1
BEQ.S RdAddrMarkDone ;-> yes, branch back into mainline code <H18><SM5>
MOVE.B #$08,wZeroes-rMark(A4) ;-> no, clear the write and action bits <H18><SM5>
BRA.S RdAddrMark ; and start over <H18><SM5>
RdAddrMarkDone ; <SM5>
; We found an address mark so retrieve the information from the ID field...
MOVEQ #mByteTOCnt,D0
RdAddrField TST.B (A3) ;Wait for a byte
DBMI D0,RdAddrField
BPL.S RdAddrTO ;-> timed out
MOVEQ #0,D1
MOVE.B (A4),D1 ;Read the cylinder
LEA TagData+2,A0 ;We'll return sector info in the tags
MOVE.B D1,(A0)+ ; like the cylinder number
MOVEQ #mByteTOCnt,D0
@1 TST.B (A3) ;Wait for a byte
DBMI D0,@1
BPL.S RdAddrTO ;-> timed out
MOVE.B (A4),(A0)+ ;Which side is it?
BEQ.S @2
BSET #11,D1 ;Side 1: set bit 11
@2 MOVEQ #mByteTOCnt,D0
MOVEQ #0,D2
@3 TST.B (A3) ;Wait for a byte
DBMI D0,@3
BPL.S RdAddrTO ;-> timed out
MOVE.B (A4),D2 ;Read the sector
MOVE.B D2,(A0)+
_PollSCC ; poll the SCC modem port <H4><SM5>
@4 MOVEQ #mByteTOCnt,D0
MOVEQ #$20,D3 ;(this is so we look double-sided)
@5 TST.B (A3) ;Wait for a byte
DBMI D0,@5
BPL.S RdAddrTO ;-> timed out
OR.B (A4),D3 ;Read the block size
MOVE.B D3,(A0)+
MOVEQ #badCkSmErr,D0 ;Assume CRC error
BRA CheckCRC ;Now go check the CRC
RdAddrTO BRA ReadTOErr
;_______________________________________________________________________________
;
; Routine: mRdData
;
; Inputs: A0 -- pointer to data buffer
; A1 -- pointer to mDataMarks table
; A3 -- pointer to rHandshake
; A4 -- pointer to rMark
; A5 -- pointer to 6522 A-reg (has head sel, wait/req)
; A6 -- pointer to SCC channel A data register
;
; Outputs: D0 -- result code
;
; A0-A2,D1-D2,D4-D5 are trashed
; (don't play with D3 since mVerTrack uses it)
;
; Function: This routine reads the next data mark that spins by, then reads
; the 512-byte data block into the buffer pointed to by A0.
;
; The disk should be up to speed with the correct head selected,
; and interrupts should be disabled. This routine should be
; called immediately after RdAddr for disk reads.
;_______________________________________________________________________________
mDataMarks DC.B $A1,$A1,$A1,$FB
mRdData LEA mDataMarks,A1
MOVE.L jMRdData,-(SP)
RTS
jtMRdData MOVE.L (SP)+,DskRtnAdr ;Save return address
MOVE.W #2-1,D4 ; loop count for invalid mark bytes <H9><SM5>
; Try to find a valid data mark (the next mark must be a data mark or something's wrong)
RdDataMarkStart ; <H9><SM5>
TST.B rError-rMark(A4) ;Clear the error register
MOVE.B #$18,wZeroes-rMark(A4) ;Clear the write and action bits
MOVE.B #$01,wOnes-rMark(A4) ;Toggle the clFIFO bit to clear out
MOVE.B #$01,wZeroes-rMark(A4) ;any data in the FIFO
TST.B rError-rMark(A4) ;Clear the error register again for grins
MOVE.B #$08,wOnes-rMark(A4) ;Turn on the action bit: GO!
if NonSerializedIO then
nop ; force write to complete
endif
MOVEQ #noDtaMkErr,D0 ;Assume error
MOVEQ #markSize-1,D1 ; <SM5>
MOVEQ #-1,D2 ; timeout counter (needs to be tuned) <SM5>
MOVE.L A1,D5 ; <SM5>
MOVEA.L SonyVars,A1 ; <SM5>
CMPI.B #-2,isSWIM(A1) ; are we using a SWIM2? <SM5>
MOVEA.L D5,A1 ; <SM5>
BNE.S RdDataMark ; -> nope <SM5>
@Wait4Mark _PollSCC ; poll the SCC modem port <H5><SM5>
MOVE.B (A3),D5 ; wait for a data mark <SM5>
DBMI D2,@Wait4Mark ; <SM5>
BPL ReadTOErr ; -> timed out <SM5>
BTST #0,D5 ; is it a mark byte? <SM5>
BNE GotDataMark ; -> yes, go for it <SM5>
MOVE.B (A4),D5 ; read a garbage byte <SM5>
BRA.S @Wait4Mark ; -> keep trying <SM5>
RdDataMark _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A3) ;Wait for a data mark
DBMI D2,RdDataMark
BPL.S jReadTOErr ;-> timed out
GotDataMark MOVEQ #mByteTOCnt,D2 ;reset the timeout counter for the next byte<SM5>
MOVE.B (A4),D5 ; and read in the current byte
CMP.B (A1)+,D5 ;is it a correct data mark?
DBNE D1,RdDataMark
BEQ.S @1 ;-> yes, continue reading sector data <SM5>
MOVE.B #$08,wZeroes-rMark(A4) ;-> no, clear the write and action bits <SM5>
if NonSerializedIO then
nop ; force write to complete <SM5>
endif
LEA mDataMarks,A1 ; reset pointer to data mark field <SM5>
DBRA D4,RdDataMarkStart ; start again, we will try twice <SM5>
BRA ReadExit ; After second try, exit with noDataMkErr<SM5>
@1
MOVE.W #512-1,D4 ; Initialize number of bytes to read <SM5>
TST.B DskVerify ; Are we comparing? <SM5>
BNE.S VfyDataBlk ;-> yes
; We found a data mark so retrieve the data block...
RdDataBlk TST.B (A3) ;do a quick check for a byte
BMI.S @2 ;-> it's already ready
MOVEQ #mByteTOCnt,D1 ;timeout counter
_PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A3) ;wait for a data byte
DBMI D1,@1
BPL.S jReadTOErr ;-> timeout
@2 MOVE.B (A4),(A0)+ ;read the data byte and save it
DBRA D4,RdDataBlk
BRA.S CheckDCRC
jReadTOErr BRA.S ReadTOErr
; We found a data mark so compare the data being read with what we just wrote...
VfyDataBlk TST.B (A3) ;do a quick check for a byte
BMI.S @2 ;-> it's already ready
MOVEQ #mByteTOCnt,D1 ;timeout counter
_PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A3) ;wait for a data byte
DBMI D1,@1
BPL.S jReadTOErr ;-> timeout
@2 MOVE.B (A4),D2 ;read a data byte
CMP.B (A0)+,D2 ;does it match what it's expected to be?
DBNE D4,VfyDataBlk ;keep looping if so (until done, that is)
BNE.S VerifyErr ;-> nope, report an error
; Finally, check the 16-bit CRC to make sure the data's all correct. We check
; for CRC error after reading the first CRC byte but before reading the second.
; From here to the RTS, D1-D3 must be preserved 'cause this code is shared by mRdAddr.
CheckDCRC MOVEQ #badDCkSum,D0 ;Assume error
LEA TagData+10,A0 ;Include the CRC in the TagData
CheckCRC MOVEQ #mByteTOCnt,D4 ;Timeout counter
@1 TST.B (A3) ;Wait for the first CRC byte
DBMI D4,@1
BPL.S ReadTOErr ;-> timeout
MOVE.B (A4),(A0)+ ;Save it in case we want it later
MOVEQ #mByteTOCnt,D4 ;Timeout counter
@2 TST.B (A3) ;Wait for the second CRC byte <H11><SM5>
DBMI D4,@2
BPL.S ReadTOErr ;-> timeout
MOVE.B (A3),D5 ;Get the handshake register with valid bits <SM5>
MOVE.B (A4),(A0)+ ;Save the CRC byte in case we want it later
MOVE.B rError-rMark(A4),(A0)+ ;Stuff the error register into tags
MOVE.B D5,(A0) ;followed by the handshake register
ANDI.B #%00100010,D5 ;CRC error or something in the error register?
BNE.S ReadExit
MOVEQ #0,D0 ;No, the data's OK
; Now turn off the hardware...
ReadExit MOVE.B #$18,wZeroes-rMark(A4) ;Clear the write and action bits
if NonSerializedIO then
nop ; force write to complete
endif
MOVE.L DskRtnAdr,-(SP)
TST.W D0
RTS
ReadTOErr MOVEQ #dataTOErr,D0 ;Timed out waiting for a byte
BRA.S ReadExit
VerifyErr MOVEQ #dataVerErr,D0 ;No, report the error
BRA.S ReadExit
;_______________________________________________________________________________
;
; Routine: mWrData
;
; Inputs: A0 -- pointer to data buffer
; A2 -- pointer to data mark table
; A5 -- pointer to 6522 A-reg (has head sel, wait/req)
; A6 -- pointer to SCC channel A data register
;
; Outputs: D0 -- result code
;
; A0,A1,D1,D2 are trashed
;
; Function: This routine writes 12 sync bytes, 4 data mark bytes, the data buffer,
; and 2 CRC bytes. There is no other overhead so it may be used for
; one-to-one writing.
;
; Since timing is tight coming into this routine, the disk write-protect
; sense line should be checked by the read/write track logic.
;_______________________________________________________________________________
mWrData LEA mDataMarks,A2
MOVE.L jMWrData,-(SP)
RTS
jtMWrData MOVE.L (SP)+,DskRtnAdr ;Save return address
MOVEA.L IWM,A4
LEA wData(A4),A3 ;Point to write data and
LEA rHandshake(A4),A4 ; handshake registers for speed
MOVE.B #$F5,wPhase-wData(A3) ;select another drive address (in this case x101) <1.3/19may88>
; to avoid the drive's index crosstalk problem <1.3/19may88>
TST.B rError-wData(A3) ;Clear the error register
MOVE.B #$18,wZeroes-wData(A3) ;Clear the write and action bits
MOVE.B #$10,wOnes-wData(A3) ;Set the write bit
MOVE.B #$01,wOnes-wData(A3) ;Toggle the clFIFO bit to clear out
MOVE.B #$01,wZeroes-wData(A3) ; any data in the FIFO
if NonSerializedIO then
nop ; force write to complete
endif
TST.B rError-wData(A3) ;Clear the error register again for grins
; Delay a bit then write the sync bytes before the data mark to position
; the sector data in the correct location relative to the address header.
; We'll poll the SCC while we wait to keep the CompuServe types happy that
; we're not losing precious bytes of gossip while we're saving stuff to disk...
MOVE.W #((gap2Size*16-reWriteAdj)<<16)\ ; <2.0>
/1000,D0 ; write splice timing constant <2.0>
;Calculate how much time we have to wait before
MULU TimeVIADB,D0 ; beginning to re-write the sector data. It needs to be <2.0>
SWAP D0 ; positioned close to 22 bytes from the end of the address field.
@1 BTST.B #7,(A5) ;SCC data available? (TimeSCCDB assumes BTST in loop)
DBEQ D0,@1 ;keep looping if not (until timing out, that is)
BNE.S @2 ;-> all done waiting
MOVE.B (A6),-(SP) ;data byte available so push it on the stack
SUBQ.W #2,D0 ;adjust for the missed DBEQ + the MOVE.B
BPL.S @1 ;-> wait some more
; OK, now we should be in the right position to start writing the sync bytes...
@2 MOVEQ #syncSize-2-1,D0
MOVEQ #theSync,D1
MOVE.B D1,(A3) ;Write the first two sync bytes
MOVE.B D1,(A3) ; to fill up the FIFO
MOVE.B #$08,wOnes-wData(A3);Turn on the ACTION bit and go!
if NonSerializedIO then
nop ; force write to complete
endif
WriteSync _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4) ;Wait until there's an opening in the FIFO
BPL.S @1
MOVE.B D1,(A3) ;Write out a sync byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,WriteSync
; Write the data marks...
MOVEQ #markSize-1-1,D0
WrDataMark _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4)
BPL.S @1
MOVE.B (A2)+,wMark-wData(A3) ;Write out a mark byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,WrDataMark
@2 TST.B (A4)
BPL.S @2
MOVE.B (A2),(A3) ;Write out the last mark byte
if NonSerializedIO then
nop ; force write to complete
endif
; Write the sector data...
MOVE.W #512-1,D0
WrDataBlk _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4)
BPL.S @1
MOVE.B (A0)+,(A3) ;Write out a data byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,WrDataBlk
; End the sector data with 2 CRC bytes...
WriteCRC TST.B (A4)
BPL.S WriteCRC
MOVE.B D0,wCRC-wData(A3)
if NonSerializedIO then
nop ; force write to complete
endif
; Then finally add a few gap bytes so that the CRC
; will be fully written before writing is disabled...
;
; FIFO1: CRC | gap1 | gap2 | gap2 | gap3
; FIFO2: data512 | CRC | gap1 | gap1 | gap2
; ShiftReg: data511 | data512 | CRC1 | CRC2 | gap1
MOVEQ #4-1,D0
WrtGap3 _PollSCC ; poll the SCC modem port <H4><SM5>
@1 MOVE.B (A4),D2
BPL.S @1
MOVE.B #gapByte,(A3) ;Write out a gap byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,WrtGap3
MOVEQ #0,D0 ;Assume no underrun
BTST #5,D2 ;Any errors?
BEQ.S WriteExit
MOVEQ #WrUnderRun,D0 ;Yes, underrun error
WriteExit MOVE.B #$18,wZeroes-wData(A3) ;Clear the write and action bits
if NonSerializedIO then
nop ; force write to complete
endif
MOVE.L DskRtnAdr,-(SP)
TST.W D0 ;Set CCR
RTS
;______________________________________________________________________________
;
; Routine: mFmtTrack
;
; Inputs: SideTrack contains the current track and side
;
; Outputs: D0 -- result code
;
; Function: Formats the current track by writing the address header and data
; block for all sectors on both sides.
;______________________________________________________________________________
mFmtTrack BSR GetDrv1
MOVE.W SideTrack(A1),D4 ;Current side/track
MOVEQ #0,D5 ;Assume side 0
BCLR #11,D4 ;Is it?
BEQ.S @1
MOVEQ #1,D5 ;No, it's side 1
MOVEQ #0,D2
MOVE.W TimeDBRA,D2 ;wait 750usec before switching heads or the drive
MOVE.L D2,D0 ; will ignore us, and then where will we be?
ADD.L D2,D0
ADD.L D2,D0
LSR.L #2,D0
@0 DBRA D0,@0 ;just sit here for a bit...
@1 MOVEQ #9-1,D2 ;Assume 1 meg format
MOVE.B twoMegFmt(A1,D1),D0 ;Is this a double-density disk?
BPL.S @2
MOVEQ #18-1,D2 ;Yes, there are twice as many sectors
BSET #31,D2 ;Mark as double-density
@2 BSR.S mDoFormat ;Format the track (disables interrupts)
BSR toEmptyPD ;Get rid of poll data (saves D0 in DskErr)
ANDI #$F8FF,SR ;Open up interrupts
MOVE.W DskErr,D0 ;Was there an error?
BNE.S @3 ;-> yes, return immediately
BSR GetDrv1 ;Point to drive vars
BSET #3,SideTrack(A1) ;Is this the second side?
BEQ.S mFmtTrack ;-> no, go format it now
MOVEQ #0,D0 ;Format was successful
@3 RTS
;_______________________________________________________________________________
;
; Routine: mDoFormat
;
; Inputs: D2 -- sectors/track - 1 (8 or 17); if 17, bit 31 will be set=1
; D4 -- track
; D5 -- side
;
; Outputs: D0 -- result code
;
; Uses: A3 -- pointer to Write Data register
; A4 -- pointer to Handshake register
; A5 -- pointer to 6522 A-reg (has wait/req)
; A6 -- pointer to SCC A-channel data register
; D0 -- loop counter
; D1 -- byte to write
; D3 -- loop counter
; D6 -- sector
;
; Function: Formats the current track; the disk is assumed to be up to
; speed and correctly positioned, and interrupts disabled.
;
; write some gap bytes while waiting for the rising edge of index
;
; Gap bytes ($4E) [ 80]
; Sync bytes ($00) [ 12]
; C2 C2 C2 FC (index mark) [ 4]
; Gap bytes ($4E) [ 50]
;
; then starting with sector 1:
;
; loop: Sync bytes ($00) [ 12]
; A1 A1 A1 FE cyl side sect size CRC [ 10]
; Gap bytes ($4E) [ 22]
; Sync bytes ($00) [ 12]
; A1 A1 A1 FB data bytes CRC [518]
; Gap bytes ($4E) [ 80 for 720K, 101 for 1440K]
;
; loop for all sectors (9 for 1MB, 18 for 2MB)
;
; write gap bytes until the rising edge of index is sensed
;_______________________________________________________________________________
mDoFormat MOVE.L (SP)+,DskRtnAdr ;Save return address
MOVEQ #RdDtaAdr,D0 ;PAL address for side 0
TST.W D5 ;Side 0?
BEQ.S @1 ;-> yes
MOVEQ #RdDta1Adr,D0 ;PAL address for side 1
@1 BSR AdrDisk ;Select head
MOVEQ #4,D0
BSR SetUpPoll ;Set up A5, A6, PollStack
MOVEA.L IWM,A4
LEA wData(A4),A3 ;Point to the write data and
LEA rHandshake(A4),A4 ;handshake registers for speed
MOVE.B #$18,wZeroes-wData(A3) ;Clear the write and action bits
MOVE.B #$10,wOnes-wData(A3) ;Set the write bit
MOVE.B #$01,wOnes-wData(A3) ;Toggle the clFIFO bit to clear out
MOVE.B #$01,wZeroes-wData(A3) ; any data in the FIFO <SM5>
TST.B rError-wData(A3) ; clear the error register again <SM5>
; Write a bunch of gap bytes while waiting for the start of index. We track index
; by waiting for it to go low (if it starts high), then go high (start of index).
MOVEQ #gapByte,D1
MOVE.B D1,(A3) ;Write the first two gap bytes
MOVE.B D1,(A3) ; to fill up the FIFO
MOVE.B #$08,wOnes-wData(A3);Turn on the action bit: GO!
if NonSerializedIO then
nop ; force write to complete
endif
MOVE.W #2*6750,D3 ;Assumes 300 rpm disk (with variation), 16uSec/byte
WtIndexLo _PollSCC ; poll the SCC modem port <H4><SM5>
@1 MOVE.B (A4),D0 ;Wait until there's an opening in the FIFO
BPL.S @1
MOVE.B D1,(A3) ;Write a gap byte
if NonSerializedIO then
nop ; force write to complete
endif
BTST #3,D0 ;Is index pulse low?
BEQ.S WtIndexHi ;-> yes, now go wait for it to go high
DBRA D3,WtIndexLo
BRA NoFmtIndex ;-> timed out waiting for index
WtIndexHi _PollSCC ; poll the SCC modem port <H4><SM5>
@1 MOVE.B (A4),D0 ;Wait until there's an opening in the FIFO
BPL.S @1
MOVE.B D1,(A3) ;Write a gap byte
if NonSerializedIO then
nop ; force write to complete
endif
BTST #3,D0 ;Is index pulse high?
BNE.S FmtStart ;-> yes, start the actual format
DBRA D3,WtIndexHi
BRA NoFmtIndex ;-> timed out waiting for index
; When we get here, we're just after the rising edge of the index pulse.
; First thing we write on the track is a starting gap and sync bytes...
FmtStart MOVE.B #$F5,wPhase-wData(A3) ;turn off the index signal while we format <1.3/19may88>
if NonSerializedIO then
nop ; force write to complete
endif
MOVEQ #gap4aSize-1-1,D0 ; <1.8>
@1 _PollSCC ; poll the SCC modem port <H4><SM5>
@2 TST.B (A4) ;Wait until there's an opening in the FIFO <1.8>
BPL.S @2 ; <1.8>
MOVE.B D1,(A3) ;Write out a gap byte <1.8>
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,@1 ; <1.8>
MOVEQ #syncSize-1,D0 ; <1.8>
MOVEQ #theSync,D1 ; <1.8>
@3 _PollSCC ; poll the SCC modem port <H4><SM5>
@4 TST.B (A4) ;Wait until there's an opening in the FIFO <1.8>
BPL.S @4 ; <1.8>
MOVE.B D1,(A3) ;Write out a sync byte <1.8>
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,@3 ; <1.8>
; Now write the index mark ($C2 $C2 $C2 $FC)...
MOVEQ #markSize-1-1,D0 ; <1.8>
WrIndexMark _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4) ;Wait until there's an opening in the FIFO <1.8>
BPL.S @1 ; <1.8>
MOVE.B #$C2,wMark-wData(A3);Write out an index mark byte <1.8>
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,WrIndexMark ; <1.8>
@2 TST.B (A4) ;Wait until there's an opening in the FIFO <1.8>
BPL.S @2 ; <1.8>
MOVE.B #$FC,(A3) ;Write out the last mark byte <1.8>
if NonSerializedIO then
nop ; force write to complete
endif
; Write another gap...
MOVEQ #gap1Size-1,D0 ; <1.8>
FmtGap1 _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4) ;Wait until there's an opening in the FIFO <1.8>
BPL.S @1 ; <1.8>
MOVE.B #gapByte,(A3) ;Write out a gap byte <1.8>
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,FmtGap1 ; <1.8>
MOVEQ #1,D6 ;Starting sector number
BRA.S FmtAddrSync ;Start a sector with the address mark sync
; Tack yet another gap onto the end of the sector...
FmtNextSect MOVEQ #gap3Sz720K-1,D0
TST.L D2 ;Is it the 1440K format?
BPL.S FmtGap3
MOVEQ #gap3Sz1440K-1,D0 ;Yes, there's a larger inter-sector gap
FmtGap3 _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4)
BPL.S @1
MOVE.B #gapByte,(A3) ;Write out a gap byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,FmtGap3
; For each sector -- write sync bytes...
FmtAddrSync MOVEQ #syncSize-1,D0
MOVEQ #theSync,D1
@1 _PollSCC ; poll the SCC modem port <H4><SM5>
@2 TST.B (A4) ;Wait until there's an opening in the FIFO
BPL.S @2
MOVE.B D1,(A3) ;Write out a sync byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,@1
; Write the address mark and field...
MOVEQ #markSize-1-1,D0
FmtAddrMark _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4)
BPL.S @1
MOVE.B #$A1,wMark-wData(A3);Write out a mark byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,FmtAddrMark
@2 TST.B (A4)
BPL.S @2
MOVE.B #$FE,(A3) ;Write out the last mark byte
if NonSerializedIO then
nop ; force write to complete
endif
_PollSCC ; poll the SCC modem port <H4><SM5>
@3 TST.B (A4)
BPL.S @3
MOVE.B D4,(A3) ;Write the track number
if NonSerializedIO then
nop ; force write to complete
endif
_PollSCC ; poll the SCC modem port <H4><SM5>
@4 TST.B (A4)
BPL.S @4
MOVE.B D5,(A3) ;Write the side number
if NonSerializedIO then
nop ; force write to complete
endif
_PollSCC ; poll the SCC modem port <H4><SM5>
@5 TST.B (A4)
BPL.S @5
MOVE.B D6,(A3) ;Write the sector number
if NonSerializedIO then
nop ; force write to complete
endif
_PollSCC ; poll the SCC modem port <H4><SM5>
@6 TST.B (A4)
BPL.S @6
MOVE.B #$02,(A3) ;Write the blocksize ($02=512 bytes/block)
if NonSerializedIO then
nop ; force write to complete
endif
; End the address field with 2 CRC bytes...
FmtAddrCRC _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4)
BPL.S @1
MOVE.B D0,wCRC-wData(A3) ;"Write" CRC bytes
if NonSerializedIO then
nop ; force write to complete
endif
; Write the intra-sector gap and sync bytes...
MOVEQ #gap2Size-1,D0
FmtGap2 _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4)
BPL.S @1
MOVE.B #gapByte,(A3) ;Write out a gap byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,FmtGap2
MOVEQ #syncSize-1,D0
MOVEQ #theSync,D1
@2 _PollSCC ; poll the SCC modem port <H4><SM5>
@3 TST.B (A4)
BPL.S @3
MOVE.B D1,(A3) ;Write out a sync byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,@2
; Write the data marks...
MOVEQ #markSize-1-1,D0
FmtDataMark _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4)
BPL.S @1
MOVE.B #$A1,wMark-wData(A3);Write out a mark byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,FmtDataMark
@2 TST.B (A4)
BPL.S @2
MOVE.B #$FB,(A3) ;Write out the last mark byte
if NonSerializedIO then
nop ; force write to complete
endif
; Write the format byte for the sector data...
MOVE.W #512-1,D0
FmtDataBlk _PollSCC ; poll the SCC modem port <H4><SM5>
@1 TST.B (A4)
BPL.S @1
MOVE.B #fmtFillByte,(A3) ;Write out a format byte
if NonSerializedIO then
nop ; force write to complete
endif
DBRA D0,FmtDataBlk
; End the sector data with 2 CRC bytes...
FmtDataCRC TST.B (A4)
BPL.S FmtDataCRC
MOVE.B D0,wCRC-wData(A3) ;"Write" CRC bytes
if NonSerializedIO then
nop ; force write to complete
endif
ADDQ.W #1,D6 ;Next sector
DBRA D2,FmtNextSect
; Finally, finish off the track with a gap to index to clean things up.
; Five gap bytes will be written before checking for index to make sure
; that the data CRC for the last sector is completely written in case
; it ends up VERY close to the end of the disk (motor speed variation).
; If this is side 0, we'll end the format before the index pulse so that
; we can switch heads in time (about 1ms). This reduces the number of
; revolutions from 4 to 3 per cylinder which saves 16 seconds overall.
MOVEQ #5-1,D0 ;We need to write 5 gap bytes so the CRC is shifted out <1.4/25may88>
MOVEQ #gapByte,D1
MOVE.W #gap4bSize,D3
TST.W D5 ;is this side 0?
BNE.S FmtLastGap
MOVEQ #5-1,D3 ;yes, use the Reader's Digest gap size... <1.4/25may88>
BRA.S FmtLastGap ; <1.3/19may88>
TurnIndexOn MOVE.B #$F4,wPhase-wData(A3) ;turn the index signal back on here <1.3/19may88>
; so there's no glitch in the data <1.3/19may88>
if NonSerializedIO then
nop ; force write to complete
endif
FmtLastGap _PollSCC ; poll the SCC modem port <H4><SM5>
@1 MOVE.B (A4),D2 ;Wait until there's an opening in the FIFO
BPL.S @1
MOVE.B D1,(A3) ;Write a gap byte
if NonSerializedIO then
nop ; force write to complete
endif
SUBQ.W #1,D0 ;Have we written enough gap bytes yet?
BGT.S @2 ;-> nope <1.3/19may88>
BEQ.S TurnIndexOn ;-> turn index back on before we check it <1.3/19may88>
BTST #3,D2 ;Is index pulse high?
BNE.S FmtDone ;-> yes, all done
@2 DBRA D3,FmtLastGap
TST.W D5 ;is this side 0?
BEQ.S FmtDone ;-> yes, we don't care if we don't see index
NoFmtIndex MOVEQ #noIndexErr,D0 ;Timed out waiting for index
BRA.S DoFmtExit ;-> timed out
FmtDone MOVEQ #0,D0 ;Assume no underrun
BTST #5,D2 ;Any errors?
BEQ.S DoFmtExit
MOVEQ #WrUnderRun,D0 ;Yes, underrun error
DoFmtExit MOVE.B #$18,wZeroes-wData(A3) ;Clear the write and action bits
if NonSerializedIO then
nop ; force write to complete
endif
MOVE.L DskRtnAdr,-(SP)
TST.W D0 ;Set CCR
RTS
;_______________________________________________________________________________
;
; Routine: mVerTrack
;
; Inputs: A1 -- pointer to SonyVars
; D1 -- offset to drive's vars
; D6 -- current track number
;
; Outputs: D0 -- result code
;
; Function: Verifies that all sectors (both sides) have been written correctly.
;_______________________________________________________________________________
mVerTrack MOVEM.L D2-D7,-(SP)
ORI #HiIntMask,SR ;No interrupts
MOVE.W D6,SideTrack(A1) ;Start on side 0
@1 MOVEQ #4*9,D7 ;Assume 1 meg format (allow several revs) <1.8>
MOVE.L #$000003FE,D6 ;Sector map (1-9)
TST.B twoMegFmt(A1,D1) ;Is this a double-density disk?
BPL.S @2
MOVEQ #4*18,D7 ;Yes, twice as many sectors <1.8>
MOVE.L #$0007FFFE,D6 ; and a bigger sector map (1-18)
@2 BSR RdAddrSetup ;Get the next address mark
BNE.S @3 ;-> error
BTST D2,D6 ;Have we already read this sector?
BEQ.S @3 ;-> yes, skip
CMP.W SideTrack(A1),D1 ;Do the track and side also match our expectations?
BNE.S @3 ;-> no, something's a tad wrong, me thinks
MOVE.W D2,D3 ;Save the sector number
MOVEA.L DiskBuffer(A1),A0 ;Get someplace to put the data
CLR.B DskVerify ;Never verify even tho' we are (verifying)
BSR mRdData ;Read in the sector
BNE.S @3 ;-> error
BCLR D3,D6 ;Mark the sector as read
@3 BSR toEmptyPD ;Get rid of poll data (saves D0 in DskErr)
TST.L D6 ;Have all the sectors been read?
DBEQ D7,@2 ;Keep looping if not
BEQ.S @4
MOVEQ #VerErr,D0 ;Return an error 'cause we couldn't
BRA.S @5 ; verify every sector for some reason
@4 BSET #3,SideTrack(A1) ;Is this the second side?
BEQ.S @1 ;-> no, go verify it
@5 ANDI #$F8FF,SR ;Turn interrupts back on
MOVEM.L (SP)+,D2-D7
TST.W D0
RTS
;_______________________________________________________________________ <SM5> begin
;
; Routine: jtISMRdAddr
; Arguments:
; A5.L (input) -- ptr to 6522 A-reg (has head sel, wait/req)
; A6.L (input) -- ptr to SCC A-channel data register
; A2.L (input) -- pointer to AdrMks table
; D0.W (output) --
; 0-1499 = no error, 1500 - nybbles before address mark
; -66 = no transitions found
; -67 = no address mark found (no header read)
; -68 = bad data nybble (header read aborted)
; -69 = bad checksum (header read but is inconsistent)
; -70 = bad bit slip marks (header read but may be bad)
; D1.W (output) -- <side><track> (low-order 12 bits)
; D2.W (output) -- <sector>
; D3.W (output) -- <volume>
; A3.L (output) -- ptr to <DNib-$96> (denibblizing table)
; A4.L (output) -- DiskQ6L pointer
; D4,D5,A0,A2 are trashed
; D6-D7 and A1,A5-A6 are preserved
; Function: This routine reads the next address mark that spins by. On exit,
; the attributes of the mark are passed back as:
;
; D1 = <side> <track>
; D2 = <sector>
; D3 = <volume>
;
; The disk should be up to speed with the correct head
; selected, and interrupts should be disabled.
;
; 'Nibble must find' count may have to be tuned.
;_______________________________________________________________________
jtISMRdAddr
; When reading immediatly after a write, the drive may suppress read data for
; up to 620µsec, so we will wait at least that long for the first byte to be valid.
; We look for 3 bytes @16µsec per byte brings the total wait to 668µsec.
; There is an access to the VIA in the loop, which must synchronize with the 783khz
; clock (1.2765µsec per access), so we compute the timeout for the fastest processor
; which would be 668/1.2765 = 523 iterations, and add in another 10% for good measure
; to get 575. It doesn't hurt for it to be too big on slower processors, it would just
; take longer to get noNybErr which should never occur unless there is a hardware error.
MOVEQ #3,D3 ; try for 3 bytes
MOVE.W #575,D2 ; setup loop timeout count
MOVEQ #0,D5 ; D5 is used for offsets, so clear it
movea.l IWM,A4 ;
LEA rData(A4),A4 ;Point to rData reg
TST.B rError-rData(A4) ;clear the error register
MOVE.B #$18,wZeroes-rData(A4) ;clear the write and action bits
MOVE.B #$01,wOnes-rData(A4) ;toggle the clFIFO bit to clear out
MOVE.B #$01,wZeroes-rData(A4) ; any data in the FIFO
TST.B rError-rData(A4) ;clear the error register again
MOVE.B #$08,wOnes-rData(A4) ;turn on the action bit: GO!
MOVE.L (SP)+,DskRtnAdr ; return address
@1 _PollSCC ; poll the SCC modem port <H5>
@2 TST.B rHandshake-rData(A4); test for a nibble
DBRA D2,@3 ; if no timeout yet, see if it was valid
MOVEQ #NoNybErr,D0 ; set CCR
BRA ISMRdAddrXit
@3 BPL.S @1 ; try again if no nibble
MOVE.B (A4),D5 ; read a nibble
SUBQ.W #1,D3 ; leave D3=0
BNE.S @1 ; if we can find 3, should be ok
MOVE.W #MustFindCt,D0 ; D0 holds nibble must find count
TST.B NewIntf(A1,D1) ; new interface drive?
BEQ.S @4
MOVE.W #MustFindCt-32,D0
@4 LEA (DNib-$96),A3 ; A3 points to denibblizing table
ISMRdAddr1 MOVE.L A2,A0 ; point to our mark table
MOVEQ #3,D1 ;
@1 TST.B rHandshake-rData(A4);look for nibble
BPL.S @1 ;
MOVE.B (A4),D5 ;read the nibble
_PollSCC ; poll the SCC modem port <H5>
@2 DBRA D0,@3 ;
MOVEQ #NoAdrMkErr,D0 ; after 1500 tries report error
BRA.W ISMRdAddrXit
@3 CMP.B (A0)+,D5 ;
BNE.S ISMRdAddr1 ;
SUBQ.W #1,D1 ;
BNE.S @1 ; leave D1=0 (returns <side><track>)
; We found an address mark header so retrieve the information from the mark.
_PollSCC ; poll the SCC modem port <H5>
ISMRdAddrMk TST.B rHandshake-rData(A4); test for track number byte
BPL.S ISMRdAddrMk ;
MOVE.B (A4),D5 ; read it
MOVE.B 0(A3,D5.W),D1 ; denibblize it
MOVE.B D1,D4 ; init the checksum
ROR.W #6,D1 ; make room for 6 bits of side . . .
_PollSCC ; poll the SCC modem port <H5>
@2 TST.B rHandshake-rData(A4); test for sector number byte
BPL.S @2 ;
MOVE.B (A4),D5 ; read it
MOVEQ #0,D2 ; zero extend the sector number
MOVE.B 0(A3,D5.W),D2 ; denibblize it
EOR.B D2,D4 ; calculate the checksum
_PollSCC ; poll the SCC modem port <H5>
@3 TST.B rHandshake-rData(A4); test for side number byte
BPL.S @3 ;
MOVE.B (A4),D5 ; read it
MOVE.B 0(A3,D5.W),D1 ; denibblize it
EOR.B D1,D4 ; calculate the checksum
ROL.W #6,D1 ; D1,bits 0-11 = <side><track>
_PollSCC ; poll the SCC modem port <H5>
@4 TST.B rHandshake-rData(A4); test for volume number byte
BPL.S @4 ;
MOVE.B (A4),D5 ; read it
MOVE.B 0(A3,D5.W),D3 ; denibblize it
EOR.B D3,D4 ; calculate the checksum
_PollSCC ; poll the SCC modem port <H5>
@5 TST.B rHandshake-rData(A4); test for checksum nibble
BPL.S @5 ;
MOVE.B (A4),D5 ; read it
MOVE.B 0(A3,D5.W),D5 ; denibblize it
EOR.B D5,D4
BNE.S ISMBadCkSum ; report bad checksum
MOVEQ #1,D4 ; 2 bit slip marks
ISMRdAddrEnd
_PollSCC ; poll the SCC modem port <H5>
@1 TST.B rHandshake-rData(A4); test for first trail nibble
BPL.S @1 ;
MOVE.B (A4),D5 ; read it
CMP.B (A0)+,D5 ; trail mark ok?
BNE.S ISMNoSlip ; br if not
DBRA D4,ISMRdAddrEnd
ISMRdAddrXit
MOVE.B #$18,wZeroes-rData(A4) ;Clear the write and action bits
BRA DskRtn ; return with D0 positive, BPL for ok
ISMBadCkSum MOVEQ #BadCkSmErr,D0
BRA.S ISMRdAddrXit
ISMNoSlip MOVEQ #BadBtSlpErr,D0
BRA.S ISMRdAddrXit
eject ;
;_______________________________________________________________________
;
; Routine: jtISMRdData
; Arguments:
; A0.L (input) -- ptr to 512-byte data buffer
; A3.L (input) -- ptr to <DNib-$96> (denibblizing table)
; A4.L (input) -- rData reg pointer
; A5.L (input) -- ptr to 6522 A-reg (has head sel, wait/req)
; A6.L (input) -- ptr to SCC channel A data register
;
; D0.W (output) --
; 0 = no error
; -71 = no data mark found
; -72 = bad checksum
; -73 = bad bit slip marks
; A3-A6 are preserved
; all other registers are trashed
; Function: This routine reads the next data mark that spins by. The 524
; bytes of data are split as follows: the first 12 bytes are put
; into TagData, the remaining 512 bytes into the buffer at (A0).
; This routine denibblizes and computes the checksum on the fly,
; so it may be used for one-to-one reading.
;
; The disk should be up to speed with the correct head
; selected, and interrupts should be disabled. This routine
; should be called immediately after RdAddr for disk reads.
;
; To do:
;
; Currently (on the SCC version Macintosh), there is no time to check each
; nibble when denibblizing data nibbles for $FF mappings; this should be added
; when this routine is put in ROM or run on a faster processor.
;
; An optimization could be added for missed address marks: if the calling
; routine is after a particular sector number and already knows which track
; it is on, this routine could be modified to check for that sector in the
; byte just before the encoded data, and return immediately on no match . . .
;_______________________________________________________________________
jtISMRdData MOVE.L (SP)+,DskRtnAdr ; save return address here
MOVEQ #48,D2 ; D2 holds nibble must find count
MOVEQ #0,D3 ; clear D3 for use as an index
MOVEQ #-64,D0 ; $C0 used to mask off bits 0-5
MOVE.L #$1FE000A,D4 ; byte read counts for 2 buffers
MOVEQ #0,D5 ; init CkSumA
MOVEQ #0,D6 ; init CkSumB
MOVEQ #0,D7 ; init CkSumC
; try to find a valid header until we've timed out
; A4 points to rData in the case of SWIM2
; TST.B rError-rData(A4) ;Clear error register
; MOVE.B #$18,wZeroes-rData(A4) ;Clear write and action
; MOVE.B #$01,wOnes-rData(A4) ;Toggle clFIFO bit to clear out
; MOVE.B #$01,wZeroes-rData(A4) ;any data in the FIFO
TST.B rError-rData(A4) ;Clear the error register again
MOVE.B #$08,wOnes-rData(A4) ;Turn on the action bit: GO!
ISMRdData1 MOVE.L A1,A2 ; address of address marks
MOVEQ #3,D1
@1 TST.B rHandshake-rData(A4); test for nibble
BPL.S @1
MOVE.B (A4),D3 ; read it
_PollSCC ; poll the SCC modem port <H5>
@2 DBRA D2,@3
MOVEQ #NoDtaMkErr,D0 ; after 48 tries report error
BRA.S ISMRdAddrXit
@3 CMP.B (A2)+,D3
BNE.S ISMRdData1
SUBQ.W #1,D1
BNE.S @1 ; leave D1=0
; We found a data mark header so retrieve the information from the mark.
@4 TST.B rHandshake-rData(A4); test for encoded sector number
BPL.S @4 ;
MOVE.B (A4),D3 ; read it
LEA TagData+1,A1 ; A1 points to buffer for extra bytes
MOVE.B 0(A3,D3.W),(A1)+ ;
;_______________________________________________________________________
;
; We found the header so the actual info is about to spin by. 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.
;
; D7 = CkSumC A7 = stack (where poll data is pushed)
; D6 = CkSumB A6 = ptr to SCC chan A data reg
; D5 = CkSumA A5 = ptr to 6522 A-reg
; D4 = loop counters A4 = ptr to rData
; D3 = buffer A3 = ptr to denibblizing table
; D2 = buffer A2 = ptr to trail mark table
; D1 = buffer for odd bits A1 = ptr to data buffer
; D0 = $C0 mask A0 = ptr to user buffer
;_______________________________________________________________________
ISMRdData2 _PollSCC ; poll the SCC modem port <H5>
@0 TST.B rHandshake-rData(A4); test for a nibble
BPL.S ISMRdData2 ;
MOVE.B (A4),D3 ; read it
MOVE.B 0(A3,D3.W),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]
@1 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @1 ;
MOVE.B (A4),D3 ; read it
OR.B 0(A3,D3.W),D2 ; D2 = ByteA' (no time for bad nibs)
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]
@2 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @2 ;
MOVE.B (A4),D3 ; read it
OR.B 0(A3,D3.W),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]
_PollSCC ; poll the SCC modem port <H5>
@3 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @3 ;
MOVE.B (A4),D3 ; read it
OR.B 0(A3,D3.W),D1 ; D1 = ByteC' (no time for bad nibs)
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 ISMRdData2 ; loop (careful->subq changes ex bit)
SWAP D4 ; first time thru switch counters, buffers
TST.B DskVerify ; doing a read verify?
BEQ.S ISMRdData3A ; br if not
BRA.S ISMRdVer1 ; br if so (only verify, don't read)
ISMRdData3 _PollSCC ; poll the SCC modem port <H5>
ISMRdData3A TST.B rHandshake-rData(A4); test for a nibble
BPL.S ISMRdData3 ;
MOVE.B (A4),D3 ; read it
MOVE.B 0(A3,D3.W),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]
@1 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @1 ;
MOVE.B (A4),D3 ; read it
OR.B 0(A3,D3.W),D2 ; D2 = ByteA' (no time for bad nibs)
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]
@2 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @2 ;
MOVE.B (A4),D3 ; read it
OR.B 0(A3,D3.W),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.W ISMRdCkSum ; 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]
_PollSCC ; poll the SCC modem port <H5>
@3 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @3 ;
MOVE.B (A4),D3 ; read it
OR.B 0(A3,D3.W),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 ISMRdData3 ; loop (careful->subq changes ex bit)
ISMRVerify _PollSCC ; poll the SCC modem port <H5>
ISMRdVer1 TST.B rHandshake-rData(A4); test for a nibble
BPL.S ISMRVerify ;
MOVE.B (A4),D3 ; read it
MOVE.B 0(A3,D3.W),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]
@1 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @1 ;
MOVE.B (A4),D3 ; read it
OR.B 0(A3,D3.W),D2 ; D2 = ByteA' (no time for bad nibs)
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'
CMP.B (A0)+,D2 ; check first byte
BNE.S @4 ; (to RdVerErr) exit on errors
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]
@2 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @2 ;
MOVE.B (A4),D3 ; read it
OR.B 0(A3,D3.W),D2 ; D2 = ByteB' (no time for bad nibs)
EOR.B D5,D2 ; D2 = ByteB
CMP.B (A0)+,D2 ; compare second byte
BNE.W ISMRdVerErr ; exit on errors
ADDX.B D2,D6 ; CkSumB',ex <- CkSumB + ByteB + ex
TST.W D4 ;
BEQ.S ISMRdCkSum ; 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]
_PollSCC ; poll the SCC modem port <H5>
@3 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @3 ;
MOVE.B (A4),D3 ; read it
OR.B 0(A3,D3.W),D1 ; D1 = ByteC' (no time for bad nibs)
EOR.B D6,D1 ; D1 = ByteC
CMP.B (A0)+,D1 ; compare third byte
@4 BNE.S ISMRdVerErr ; exit on errors
ADDX.B D1,D7 ; CkSumC'' <- CkSumC' + ByteC + ex
SUBQ.W #3,D4 ; decrement counter by 3 bytes
BRA.S ISMRVerify ; loop (careful->subq changes ex bit)
; now read the four checksum nibbles and compare . . .
ISMRdCkSum _PollSCC ; poll the SCC modem port <H5>
@0 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @0 ;
MOVE.B (A4),D3 ; read it
MOVE.B 0(A3,D3.W),D1 ; D1 = [00][00][A7][A6][B7][B6][C7][C6]
BMI.S ISMDCkSumBad ; branch if bad nibble
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]
@1 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @1 ;
MOVE.B (A4),D3 ; read it
MOVE.B 0(A3,D3.W),D3 ; D3 = [0][0][A5][A4][A3][A2][A1][A0]
BMI.S ISMDCkSumBad ; br if bad nibble
OR.B D3,D2 ; D2 = CkSumA
CMP.B D2,D5 ; check against calculated value
BNE.S ISMDCkSumBad ; 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]
@2 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @2 ;
MOVE.B (A4),D3 ; read it
MOVE.B 0(A3,D3.W),D3 ; D3 = [0][0][B5][B4][B3][B2][B1][B0]
BMI.S ISMDCkSumBad ; branch if bad nibble
OR.B D3,D2 ; D2 = CkSumB
CMP.B D2,D6 ; check against calculated value
BNE.S ISMDCkSumBad ; 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]
_PollSCC ; poll the SCC modem port <H5>
@3 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @3 ;
MOVE.B (A4),D3 ; read it
MOVE.B 0(A3,D3.W),D3 ; D3 = [00][00][C5][C4][C3][C2][C1][C0]
BMI.S ISMDCkSumBad ; branch if bad nibble
OR.B D3,D1 ; D1 = CkSumC
CMP.B D1,D7 ; check against calculated value
BEQ.S ISMRdSlip ;
ISMDCkSumBad
MOVEQ #BadDCkSum,D0 ; bad checksum
BRA.S ISMRdDataXit
ISMRdVerErr MOVEQ #DataVerErr,D0
BRA.S ISMRdDataXit
; finally we must read the bit slip marks before giving our stamp of approval
ISMRdSlip MOVEQ #1,D4 ; 2 bit slip marks
@1 TST.B rHandshake-rData(A4); test for a nibble
BPL.S @1 ;
MOVE.B (A4),D3 ; read it
_PollSCC ; poll the SCC modem port <H5>
@2 CMP.B (A2)+,D3 ; trail mark ok?
BNE.S ISMNoDSlip ; br if not
DBRA D4,@1
MOVEQ #0,D0 ; report success!!!
ISMRdDataXit
MOVE.B #$18,wZeroes-rData(A4) ;Clear the write and action bits
BRA DskRtn ; return with D0 positive, BPL for ok
ISMNoDSlip MOVEQ #BadDBtSlp,D0 ; bad slip nibbles is error code 3 . . .
BRA.S ISMRdDataXit
;——————————————————————————————————————————————————————————————————————————————— <H9>
;
; Routine: CheckFor32Mhz
;
; Inputs: A1 - pointer to Sony driver's variables
;
; Outputs: none
;
; Function: Checks for clock speed of 32Mhz to the SWIM2. Sets global Clock32Mhz
; to TRUE if present, FALSE otherwise. This routine is called only for
; SWIM2.
; With a 16Mhz clock, each byte will take 16µs to shift out. We write
; out 20 bytes which will take 320µs at 16Mhz and 160µs at 32Mhz. To
; allow for any skewing and overhead we compare against 240µs.
;———————————————————————————————————————————————————————————————————————————————
CheckFor32Mhz ;
MOVEM.L D0-D3/A2-A4,-(SP) ; save regs <H28>
MOVEA.L IWM,A4 ; get SWIM2 base address
MOVEA.L VIA,A2 ; get VIA base address
LEA wData(A4),A3 ; Point to write data and
LEA rHandshake(A4),A4 ; handshake registers for speed
MOVE.B #$20,wSetup-wData(A3) ; put us into MFM mode with fast clock
MOVE.B #$86,wZeroes-wData(A3) ; Ensure that no drives are enabled
MOVE.B #$18,wZeroes-wData(A3) ;Clear the write and action bits
MOVE.B #$10,wOnes-wData(A3) ;Set the write bit
MOVE.B #$01,wOnes-wData(A3) ;Toggle the clFIFO bit to clear out
MOVE.B #$01,wZeroes-wData(A3) ; any data in the FIFO
TST.B rError-wData(A3) ;Clear the error register
MOVEQ #20,D0 ; set byte count
MOVE.B D0,(A3) ; Write the first two bytes
MOVE.B D0,(A3) ; to fill up the FIFO
MOVE.B vT2CH(A2),D1 ; get timer high byte <H28>
MOVE.B vT2C(A2),D3 ; and timer low byte <H28>
CMP.B vT2CH(A2),D1 ; did high byte change? <H28>
BEQ.S @0 ; -> no, we're fine <H28>
MOVEQ #0,D3 ; yes, set low byte to 0 (which will decr both) <H28>
@0 LSL.W #8,D1 ; shift it over <H28>
MOVE.B D3,D1 ; get the lower half <H28>
MOVE.B #$08,wOnes-wData(A3); Turn on the ACTION bit and go!
@1 TST.B (A4) ; Wait until there's an opening in the FIFO
BPL.S @1
MOVE.B D0,(A3) ; Write a byte
DBRA D0,@1 ; do this until byte count is exhausted
@done MOVE.B #$18,wZeroes-wData(A3) ;Clear the write and action bits
MOVE.B vT2CH(A2),D2 ; get timer high byte <H28>
MOVE.B vT2C(A2),D3 ; and timer low byte <H28>
CMP.B vT2CH(A2),D2 ; did high byte change? <H28>
BEQ.S @2 ; -> no, we're fine <H28>
MOVEQ #0,D3 ; yes, set low byte to 0 (which will decr both) <H28>
@2 LSL.W #8,D2 ; shift it over <H28>
MOVE.B D3,D2 ; get the lower half <H28>
MOVE.B vT2CH(A2),vT2CH(A2) ; re-enable timer 2 interrupts <H31>
CLR.B Clock32MHz(A1) ; assume it's -really- slow <H24>
SUB.W D2,D1 ; get total elapsed ticks
BMI.S @ReallySlow ; -> >32K delta, so it's really slow <H24>
CMP.W #nTicks*240/1000,D1 ; how many VIA ticks elapsed? (240µs is the midpoint)
SLT Clock32Mhz(A1) ; less than 240µs worth, must be 32Mhz clock
@ReallySlow MOVEM.L (SP)+,D0-D3/A2-A4 ; restore registers <H28>
RTS ; return <SM5> end