mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-29 20:49:19 +00:00
4458 lines
175 KiB
Plaintext
4458 lines
175 KiB
Plaintext
;
|
|
; File: NewAgeDrvr.a
|
|
;
|
|
; Contains: This is the driver for the NewAge Floppy Disk Controller.
|
|
;
|
|
; Written by: Robert Polic
|
|
;
|
|
; Copyright: © 1992-1993 by Apple Computer, Inc., all rights reserved.
|
|
;
|
|
; This file is used in these builds:
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <SM23> 6/14/93 kc Roll in Ludwig.
|
|
; <LW30> 5/19/93 WS Changed retries to 25, except during disk verification.
|
|
; <LW29> 5/1/93 WS Change fifo setting to F. Enable Precom.
|
|
; <LW28> 4/30/93 WS Remove a debugger and a debugstr.
|
|
; <LW27> 4/22/93 WS Change name of driver to .Sony for compatibility. Removed loops
|
|
; to check for successful PSC writes. Changes approved by CCC mal
|
|
; and chp.
|
|
; <LW26> 4/13/93 WS Changed SetupFDC so that it checks whether it has been call
|
|
; since disk insertion. If it has than do nothing. TrackDump
|
|
; will now dump from sector 0 if it is call in mode 0 with a GCR
|
|
; disk.
|
|
; <LW25> 4/5/93 WS Check for negative drive number in DriveStatus and return error.
|
|
; <LW24> 4/5/93 WS Call to DriveStatus with drive number 1 or 2 will not return
|
|
; with error. The paramblock will be filled out to make it
|
|
; compatible with the SWIM driver.
|
|
; <LW23> 3/31/93 WS Fixed bug where if a disk is inserted close to a disk eject
|
|
; command the floppy drive appears to hang. Updated a error code.
|
|
; <LW22> 3/24/93 WS Changed the floppy icon so that it looks like all the other Mac
|
|
; floppy icon. Modified error check in SenseInterrupt since
|
|
; Newage something return the Equipment Check error bit falsely.
|
|
; Changed the close rountine so that in does nothing but return
|
|
; closErr for compatiblity. Changed all _BlockMove to
|
|
; _BlockMoveData.
|
|
; <LW21> 3/18/93 WS Fix the EjectDisk rountine so system does not hang aftera disk
|
|
; eject. Reduced some of the time out waits. Added a couple more
|
|
; error code to report failures.
|
|
; <LW20> 3/16/93 chp Replace references to the obsolete PSCIntTbl lomem with
|
|
; references to the equivalent field in ExpandMem.
|
|
; <LW19> 3/12/93 WS Add error check for all code that uses the SenseInterruptStatus
|
|
; command to Newage.
|
|
; <LW18> 3/10/93 WS Fix diskcopy bug
|
|
; <LW17> 3/8/93 WS The error code for checking for the correct ioVRefNum was stuff
|
|
; in a register which got blow away later. Fixed. Defer some on
|
|
; the stuff for a disk insertion interrupt.
|
|
; <LW16> 3/1/93 WS Increase some more time out counts.
|
|
; <LW15> 3/1/93 WS Increased timeout count for WriteToFDC and ReadFromFDC.
|
|
; <LW14> 2/23/93 WS Changed DriveStatus so that it returns -1 instead of just a
|
|
; generic negative number in the sides formatted field to indicate
|
|
; 2-sided format. Also the interface field will noe return 0 for
|
|
; 400K disk to indicated the old interface.
|
|
; <LW13> 2/17/93 WS Fixed the calculation of the starting sector so that it works
|
|
; for all modes. Bug # 1066659.
|
|
; <LW12> 2/16/93 WS Fix bug which cause a DMA bus error when doing a write retry.
|
|
; Also added a code to handle a DMA bus error if it occurs.
|
|
; <LW11> 2/10/93 WS Roll out changes to return only one formatlist for 800K GCR
|
|
; format.
|
|
; <LW10> 2/9/93 WS Added code to verify a disk in the diskcopy call to make it
|
|
; compatible with Apple tech. note. For 1.44 Meg MFM disk
|
|
; Formatlist will now return with bit set in the "current disk has
|
|
; this format" field to make it compatible with the old driver and
|
|
; to make Diskcopy 4.2 work. Fixes bug # 1062754. Formatlist
|
|
; also changed to return only one list for 800K GCR disk. Fies
|
|
; bug # 1064306 again.
|
|
; <LW9> 1/20/93 WS Added icon for the Tempest machines. Fixed the Icon for
|
|
; Cyclone. Added Cyclone40 to Icon Table.
|
|
; <LW8> 1/18/93 WS I a generic unformatted disk is inserted into the floppy drive,
|
|
; Newage may hang. Added code for Newage to recover if this
|
|
; ocurs.
|
|
; <LW7> 1/15/93 WS Change the Icon for Cyclone so that it doesn't look like a
|
|
; IICi. Tookout most of the debugger breaks. Changed the format
|
|
; and format/write gor GCR. It should no be faster and a little
|
|
; less buggy.
|
|
; <LW6> 1/7/93 WS Undo some of the changes added in SM16. FormatList now will
|
|
; return 2 entry for 800k GCR format. One for Macintosh style and
|
|
; another for ProDos.
|
|
; <LW5> 1/4/93 WS If format disk is called with param of zero, it will default to
|
|
; format the disk for most storage given the media and drive type.
|
|
; Update and use dCtlposition from the DCtlEntry for offset in
|
|
; read and write operations. Check for invalid cylinder number
|
|
; and return paramErr if invlaid cylinder is requested.
|
|
; <LW4> 12/15/92 WS Added fix to handle error for write protected disk during disk
|
|
; format and disk copy.
|
|
; <LW3> 12/14/92 WS Update ioActCount correctly.
|
|
; <LW2> 12/14/92 WS Removed some code that used to be conditional compiled. Added
|
|
; code to handle calls to the floppy driver with interrupt level
|
|
; mask at 2 or greater. Bypassed bug where an interrupt occurs
|
|
; before the CmdBsy bit in the status register is stable. Added
|
|
; check for disk being manually ejected during format and
|
|
; terminate format it this is true. Optimized MFM format
|
|
; read/write.
|
|
; <SM22> 12/9/92 WS Replace some numbers with equates.
|
|
; <SM21> 12/9/92 WS Roll out most of the changes from the last checkin because it
|
|
; was causing the floppy to hang during accesses.
|
|
; <SM20> 11/30/92 WS Added code to reset Newage in timeout occurs during a DMA
|
|
; transfer. Check that the CPU interrupt mask is less than 2, if
|
|
; not poll the interrupt, other the driver will never know if an
|
|
; interrupt occurs. Misc error check per code review.
|
|
; <SM19> 11/17/92 WS Add code to read ahead to end of track for all track accesses.
|
|
; If this fails then retries will occurs reading only the
|
|
; requested tracks. This does not use the READ A TRACK command.
|
|
; In fact, the code using the READ A TRACK command have been
|
|
; remove since there will be some changes in its inplementation.
|
|
; Add code to make multitrack read work with this scheme. The PSC
|
|
; count register is now check after every read to make sure the
|
|
; requested number of bytes are read. Add code to restore the
|
|
; controller to the proper state if a failure occurs while testing
|
|
; for ED format disk.
|
|
; <SM18> 11/11/92 fau Fixed a bug where the correct result was not being returned
|
|
; after a call to the driver.
|
|
; <SM17> 11/5/92 WS My system crash while checking in <SM16> I double checked
|
|
; everything and am checking it in again. No changes.
|
|
; <SM16> 11/5/92 WS Change error check for invalid ioVrefNum so that it does not
|
|
; crash. Fixed Format List so that it return only one entry for
|
|
; 800K floppies.
|
|
; <SM15> 10/22/92 CSS Fixed a branch short to a regular branch as direct calling of
|
|
; traps expanded code.
|
|
; <SM14> 10/15/92 WS Add code to stripe leading zeroes for the TrackDump routine
|
|
; during GCR mode.
|
|
; <SM13> 10/8/92 PN Fix DriveInfo to return the correct values
|
|
; <SM12> 10/7/92 WS Replaced a lot of hard code number with equates. Added the
|
|
; rountine ReadATrack which is not being use yet. It will be use
|
|
; when NEC fixes the bugs related to the READ A TRACK command.
|
|
; <SM11> 9/24/92 WS Add SenseInterrupt Command in open to remove pending interrupts
|
|
; before installing the handler
|
|
; <SM10> 9/23/92 WS Read only the tracks that are requested.
|
|
; <SM9> 9/15/92 WS Add timeout for wait loops. Disk Copy is now DMA. Track Dump
|
|
; is changed back to poll due to technical difficulties.
|
|
; <SM8> 8/28/92 WS Trackdump now DMAs data to buffer before sorting it out. Change
|
|
; GSL to #$25, so it will sector 36.
|
|
; <SM7> 8/28/92 WS Change GSL in read and write command from #$1B to #$24 to bypass
|
|
; bug in Newage. Add in format code to Rawdump command. Change
|
|
; icons.
|
|
; <SM6> 8/13/92 WS Minor cleanup here and there to remove redundant stuff.
|
|
; <SM5> 8/12/92 WS Because I am a goof, I failed to add all the commands under
|
|
; thsSM4 heading. Eventhough this is the SM5 heading, All these
|
|
; command is for SM4. Pause PSC channel after format command. In
|
|
; TrackDump routine do 32 byte loops instead of 8 byte loops. The
|
|
; clock table for TrackDump have also been updated. In DiskCopy
|
|
; rountine add in command to switch in and out of non-DMA mode.
|
|
; Check for motor on before sending disk copy command. Now looks
|
|
; at the correct location to determind whether command should be
|
|
; in MFM or GCR mode. Recalibrate track before exiting Disk Copy.
|
|
; Change Gap 3 length to 101 bytes. Shorted timeout loop for Read
|
|
; ID command. Clear fMotor flag after ResetFDC call. Fix fCylinder
|
|
; so it is not corrupted after Sense Interrupt Status cammand (use
|
|
; to get bash after calls other that Seek and REcal). Change fifo
|
|
; depth to 8.
|
|
; <SM4> 8/12/92 WS check for can't find IDR after read and write before doing a
|
|
; seek prior to retry.
|
|
; <SM3> 7/28/92 WS Software PSC (FDC channel) before accessing count and address
|
|
; registers to flush internal fifo. Set Newage fifo fepth to 3.
|
|
; Perform seek trak before each retry.
|
|
; <SM2> 7/24/92 RLM changed via2 addr $50f31008 to $50f03a00 and $50f3100c to
|
|
; $50f03c00
|
|
; <P2> 7/2/92 RMP Added raw track dump. Fixed various other bugs.
|
|
; <1> 4/10/92 RMP first checked in
|
|
;
|
|
; To Do:
|
|
;
|
|
Break Equ 0
|
|
noClose Equ 1
|
|
BLANKS ON
|
|
PRINT OFF
|
|
LOAD 'StandardEqu.d'
|
|
INCLUDE 'FSEqu.a'
|
|
INCLUDE 'PSCEqu.a'
|
|
INCLUDE 'NewAgeEqu.a'
|
|
PRINT ON
|
|
|
|
MACHINE MC68020
|
|
MAIN
|
|
;------------------------------------------------------------------------------
|
|
; Data: Driver header.
|
|
;------------------------------------------------------------------------------
|
|
|
|
DRVR Dc.w $6F00 ; drvrFlags (Inside Mac p II-188).
|
|
Dc.w $0014 ; drvrDelay.
|
|
Dc.w $0000 ; drvrEMask.
|
|
Dc.w $0000 ; drvrMenu.
|
|
Dc.w Open - drvr ; Offset to open routine.
|
|
Dc.w Prime - drvr ; Offset to prime routine.
|
|
Dc.w Control - drvr ; Offset to control routine.
|
|
Dc.w Status - drvr ; Offset to status routine.
|
|
Dc.w Close - drvr ; Offset to close routine.
|
|
;** Dc.w '.NewAge' ; Driver name.
|
|
Dc.w '.Sony' ; Driver name. <LW27>
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: Open
|
|
; Inputs: A0 -> Parameter block.
|
|
; A1 -> DCE.
|
|
; Outputs: D0 -> 0 - OK, ~0 - Error.
|
|
; Function: Open driver and allocate needed memory. Init the FDC.
|
|
;------------------------------------------------------------------------------
|
|
|
|
WITH FloppyRecord,ExpandMemRec,PSC_INT_TBL
|
|
OpenRegs REG A0-A3/D3-D5 ; Registers abused.
|
|
|
|
Open Movem.l OpenRegs,-(Sp) ; Save registers.
|
|
Move.l ioRefNum(A0),D3 ; Save refNum in high word.
|
|
Movea.l A1,A3 ; Save DCE.
|
|
Move.l A1,NewAgeVars ; Save it.
|
|
Clr.l dCtlStorage(A3) ; Assume no drives are installed.
|
|
Clr.l dCtlPosition(A3) ; Clear position.
|
|
|
|
Move.l #NewAge32,A0 ; Base address of NewAge.
|
|
Bsr ResetFDC ; Initialize FDC.
|
|
|
|
Moveq #0,D4 ; Bit number to check.
|
|
@InstallLoop Movea.l #NewAge32,A0 ; Base address of NewAge.
|
|
|
|
Move.b #SetEnableOn,D0 ; Enable drive.
|
|
Bsr WriteToFDC ; Send it.
|
|
Move.b D4,D0 ; Drive select.
|
|
Bsr WriteToFDC ; Enable drive.
|
|
Move.b #SenseDrvStatus,D0 ; Sense drive status command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Move.b D4,D0 ; Drive select.
|
|
Bsr WriteToFDC ; Send command.
|
|
Bsr ReadFromFDC ; Read Status3.
|
|
Move.b D0,D5 ; Save it.
|
|
|
|
Move.b #SetEnableOff,D0 ; Disable drive.
|
|
Bsr WriteToFDC ; Send it.
|
|
Move.b D4,D0 ; Drive select.
|
|
Bsr WriteToFDC ; Disable drive.
|
|
Move.b #SenseIntStatus,D0 ; Sense interrupt status command. <SM11>
|
|
Bsr WriteToFDC ; Send command. <SM11>
|
|
Bsr ReadFromFDC ; Get Status0. <SM11>
|
|
|
|
Cmp.b #$FF,D5 ; No drive?
|
|
Beq @Next ; No? Then try next drive. <SM15> CSS
|
|
Moveq #fRecSize,D0 ; Bytes needed for floppy info record.
|
|
_NewPtr ,Sys,Clear ; Allocate local storage.
|
|
Movea.l A0,A2 ; For use later.
|
|
Move.l dCtlStorage(A3),D0 ; Record already installed?
|
|
Bne.s @2 ; Yes? Then link it.
|
|
Move.l A2,dCtlStorage(A3) ; Save in DCE.
|
|
Bra.s @AddDrive ; Add drive queue element.
|
|
@2 Movea.l D0,A0 ; Pointer to first record.
|
|
Move.l A2,fNextRecord(A0) ; Link it.
|
|
|
|
@AddDrive Moveq #20,D0 ; Bytes needed for drive queue elements.
|
|
_NewPtr ,Sys,Clear ; Allocate it.
|
|
Move.w D4,D3 ; Drive bit (refNum in high word).
|
|
Add.w #1,D3 ; Drive number (1 or 2).
|
|
Tst.l (A0)+ ; Skip over flags.
|
|
Move.w #1,qType(A0) ; Set queue type to drive.
|
|
Move.l D3,D0 ; RefNum/DrvNum.
|
|
Swap D0 ; DrvNum/RefNum.
|
|
_AddDrive ; Add drive to queue.
|
|
|
|
Move.w D3,fDriveNum(A2) ; Install drive number.
|
|
Move.b #RetryCount,fRetrySeed(A2) ; Set seed value.
|
|
Move.w #dtQType, DTQE+qType(A2) ; Set Deferred Task queue type
|
|
Move.l A0,fDriveQueue(A2) ; Save drive queue pointer.
|
|
Move.b D4,fDriveSelect(A2) ; Set drive select.
|
|
Move.w #fddMac800,fDriveType(A2) ; Assume 800K.
|
|
Btst #fDriveStatus,D5 ; Drive bit clear?
|
|
Beq.s @Buffer ; Yes? Then must be 800K.
|
|
Move.w #fddSuperDrive,fDriveType(A2) ; Assume SuperDrive.
|
|
Btst #fMode,D5 ; Mode bit set?
|
|
Bne.s @Buffer ; Yes? Then SuperDrive.
|
|
Move.w #fddTyphoon,fDriveType(A2) ; Must be a Typhoon.
|
|
|
|
@Buffer Move.l #BufferSize*BlockSize,D0 ; Bytes needed. <SM9>
|
|
_NewPtr ,Sys,Clear ; Try allocating it.
|
|
Move.l A0,fBuffer(A2) ; Save buffer.
|
|
Movea.l #BufferSize*BlockSize,A1 ; Bytes needed. <SM9>
|
|
_LockMemory ; Lock it.
|
|
|
|
@Next Addq.w #1,D4 ; Next drive bit.
|
|
Cmp.w #2,D4 ; Are we done?
|
|
Beq.s @InstallHandler ; Yes? Then leave.
|
|
Bra @InstallLoop ; Check next drive.
|
|
|
|
@InstallHandler Lea Handler,A1 ; Pointer to interrupt handler.
|
|
Move.l A1,VIA2DT+FloppyEntry ; Install handler.
|
|
Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM1> RLM
|
|
Movea.l ([ExpandMem],emDMADispatchGlobals), A1 ; A1-> PSC Interrupt table ptr
|
|
Lea FDCDMAInt, A2 ; Pointer to interruot handler. <LW12>
|
|
Move.l A2, FDChndlr(A1) ; Install DMA bus error handler. <LW12>
|
|
|
|
@ExitOpen Movem.l (Sp)+,OpenRegs ; Restore registers.
|
|
Moveq #noErr,D0 ; Indicate no error.
|
|
Move.w D0,ioResult(A0) ; Set result value.
|
|
Move.l JIODone,-(Sp) ; Vector to IODone.
|
|
Rts ; And call it.
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: Prime
|
|
; Inputs: A0 -> Parameter block.
|
|
; A1 -> DCE.
|
|
; Outputs: D0 -> 0 - OK, ~0 - Error.
|
|
; Function: Handle read/write requests.
|
|
;------------------------------------------------------------------------------
|
|
|
|
PrimeRegs REG A0-A5/D3-D7 ; Registers abused.
|
|
|
|
Prime Movem.l PrimeRegs,-(Sp) ; Scratch registers.
|
|
Movea.l NewAgeVars,A3 ; DCE pointer.
|
|
Movea.l A0,A4 ; Save parameter block.
|
|
Movea.l ioBuffer(A4),A2 ; Buffer.
|
|
Movea.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record?
|
|
Beq.s @Prepare ; Yes? Then use it.
|
|
Movea.l fNextRecord(A1),A1 ; Next record.
|
|
Beq.s @0 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record? <SM16>
|
|
Beq.s @Prepare ; Yes? Then use it. <SM16>
|
|
@0 Movea.l dCtlStorage(A3),A1 ; Give it a real floppy info. <SM16>
|
|
Move.w #nsDrvErr,fResult(A1); Assume no such drive. <LW17>
|
|
Bra.s @ExitPrime ; Out of here <SM16>
|
|
|
|
@Prepare Move.w #offLinErr,fResult(A1) ; Assume no disk.
|
|
Tst.w fMediaType(A1) ; Disk in?
|
|
Beq.s @ExitPrime ; No? Then leave.
|
|
|
|
Move.b fRetrySeed(A1),fRetries(A1) ; Reset retry count.
|
|
Move.w #paramErr,fResult(A1) ; Assume Parameter Error. <LW5>
|
|
Move.l dCtlPosition(A3),D1 ; Assume I/O from mark. <LW5>
|
|
|
|
@Offset Move.l D1,fPosition(A1) ; New mark.
|
|
Blt.s @ParamErr ; Error if call with negative sector. <LW5>
|
|
Asr.l #8,D1 ; Get starting sector…
|
|
Asr.l #1,D1 ; …by dividing by 512.
|
|
Move.l D1,D5 ; Save start.
|
|
|
|
Clr.l ioActCount(A4) ; Init actual count.
|
|
Move.l ioReqCount(A4),D4 ; Bytes to I/O.
|
|
Asr.l #8,D4 ; Get number of sectors…
|
|
Asr.l #1,D4 ; …by dividing by 512.
|
|
|
|
Cmpi.b #aRdCmd,ioTrap+1(A4); Read call?
|
|
Beq.s @DoRead ; Yes? Then call read routine.
|
|
Bsr DoWrite ; No? Then call write routine.
|
|
Bra.s @ExitPrime ; Leave.
|
|
@DoRead Bsr DoRead ; Call read routine.
|
|
|
|
@ExitPrime Movea.l NewAgeVars,A3 ; DCE pointer.
|
|
Move.l fPosition(A1),dCtlPosition(A3); Update. <LW5>
|
|
@ParamErr Move.l Ticks,fLastAccess(A1) ; Update time.
|
|
Move.w fResult(A1),D1 ; Call result.
|
|
|
|
@Exit Movem.l (Sp)+,PrimeRegs ; Restore registers.
|
|
Move.w D1,ioResult(A0) ; Set result value.
|
|
Move.w ioTrap(A0),D1 ; Trap called.
|
|
Btst #noQueueBit,D1 ; Called immediately?
|
|
Bne.s @2 ; Yes? Then leave via an RTS.
|
|
Move.w ioResult(A0),D0 ; Check for our result and return it in D0
|
|
Ble.s @1 ; Yes? Then go through IODone.
|
|
Btst #asyncTrpBit,D1 ; Called asynchronously?
|
|
Bne.s @2 ; Yes? Then leave via an RTS.
|
|
|
|
@1 Move.l JIODone,-(Sp) ; Vector to IODone.
|
|
@2 Rts ; Return.
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: DoRead
|
|
; Inputs: A1 -> Floppy info record.
|
|
; A2 -> User buffer.
|
|
; A4 -> Parameter block.
|
|
; D4 -> Count in sectors.
|
|
; D5 -> Start (in sectors).
|
|
; Outputs: fResult(A1).
|
|
; Function: Handle read requests.
|
|
; Called by: Prime
|
|
;------------------------------------------------------------------------------
|
|
|
|
|
|
; At this point, we want to read any data we can out of the DMA buffer (fBuffer).
|
|
;
|
|
; | fBuffer |
|
|
; +------------------------------------+
|
|
; |Start (D5) Length (D4) | |
|
|
; +--------------------------------------+
|
|
; .........BBBBBBBBBBBBBBBBBBBBBBBBBBBBBfffffffffff
|
|
;
|
|
; . - Offset into buffer
|
|
; B - Read from buffer
|
|
; f - Read from floppy
|
|
;
|
|
; If the start (D5) is less than the start of the buffer (fStart), all data will
|
|
; be read from the floppy.
|
|
;
|
|
|
|
DoRead Moveq #0,D7 ; Set flag that FDC is not setup.
|
|
Move.w #noErr,fResult(A1) ; In case there are no sectors to read.
|
|
Cmp.w #rdVerify,ioPosMode(A4) ; Read verify?
|
|
Blt.s @CheckBuffer ; No? Then check if data is in buffer.
|
|
Clr.w fCount(A1) ; Clear buffer to force read from media.
|
|
Bra.s @ReadLoop ; Read it.
|
|
|
|
@CheckBuffer Move.w fCount(A1),D0 ; Is buffer empty?
|
|
Beq.s @ReadLoop ; Yes? Then read from media.
|
|
Move.w fStart(A1),D1 ; Starting sector of buffer.
|
|
Cmp.w D1,D5 ; Is data in buffer?
|
|
Blt.s @ReadLoop ; No? Then read from media.
|
|
Add.w D1,D0 ; Ending sector in buffer.
|
|
Sub.w D5,D0 ; Is there an intersection?
|
|
Bmi.s @ReadLoop ; No? Then read from media.
|
|
Sub.w D5,D1 ; Offset (negative).
|
|
Neg.w D1 ; Force positive.
|
|
|
|
Sub.w D0,D4 ; Sectors remaining after read from buffer.
|
|
Bpl.s @CopyFromBuffer ; Copy from buffer.
|
|
Add.w D4,D0 ; Blocks needed.
|
|
Moveq #0,D4 ; Set remaining count to 0.
|
|
|
|
@CopyFromBuffer Move.l A1,A3 ; Save floppy info record.
|
|
Add.w D0,D5 ; Bump up starting block.
|
|
Move.l fBuffer(A3),A0 ; Pointer to buffer.
|
|
Ext.l D1 ; Force long.
|
|
Asl.l #8,D1 ; Calculate offset…
|
|
Asl.l #1,D1 ; By multiplying by 512.
|
|
Add.l D1,A0 ; Offset into buffer.
|
|
Move.l A2,A1 ; User buffer.
|
|
Ext.l D0 ; Force to long.
|
|
Asl.l #8,D0 ; Multiply by 512…
|
|
Asl.w #1,D0 ; …to get number of bytes.
|
|
Move.l D0,ioActCount(A4) ; Update “actual” count.
|
|
Add.l D0,A2 ; Update user buffer pointer.
|
|
_BlockMoveData ; Copy into buffer.
|
|
Move.l A3,A1 ; Floppy info record.
|
|
|
|
; At this point, we have read everything we could from the DMA buffer and the following
|
|
; registers have been updated:
|
|
; ioActCount reflects then number of bytes read from the buffer.
|
|
; Application buffer pointer has been bumped up by number of bytes read.
|
|
; The starting sector (D5) has been bumped up by number of sectors read.
|
|
; The count has been decremented by number of sectors read.
|
|
;
|
|
; We will now calculate our physical offsets into the media to do actual read. This is
|
|
; also the start of the read loop.
|
|
;
|
|
|
|
@ReadLoop Tst.w D4 ; Are there any blocks left to transfer?
|
|
Ble @ExitRead ; No? Then we're done. <SM10>
|
|
Tst.l D7 ; FDC setup?
|
|
Bmi.s @MFM ; Yes? Then check for MFM read.
|
|
Moveq #-1,D7 ; Set flag that FDC is setup.
|
|
Bsr SetUpFDC ; Set it up.
|
|
Tst.w fResult(A1) ; Any errors?
|
|
Bne @ExitRead ; Yes? Out of here.
|
|
Move.w #sectNFerr,fResult(A1) ; Assume blank disk.
|
|
Cmp.w #fBlank,fFormat(A1) ; Blank or missing disk?
|
|
Ble @ExitRead ; Yes? Then leave with error.
|
|
|
|
@MFM Move.w #1,D7 ; Assume side 1.
|
|
Moveq #18,D0 ; Min. sectors/cylinder for non-GCR.
|
|
Move.w fFormat(A1),D1 ; Format code.
|
|
Sub.b #f720K,D1 ; GCR format?
|
|
Blt.s @GCR ; Yes? Then variable sectors/cylinder.
|
|
|
|
Asl.w D1,D0 ; Calc. sectors/cylinder.
|
|
Move.l D5,D3 ; Sector for I/O.
|
|
Divu D0,D3 ; Find starting track.
|
|
Swap D3 ; Sector in low word.
|
|
|
|
Lea SectorsSide,A5 ; Sectors/side table.
|
|
Move.w fFormat(A1),D1 ; Format byte.
|
|
Asl.w #1,D1 ; Word index.
|
|
Move.w 0(A5,D1.w),D1 ; Sectors/side.
|
|
Move.w D1,D6 ; Sector per track.
|
|
Swap D6 ; Save it.
|
|
Move.w D1,D6 ; Sector per track.
|
|
Sub.w D1,D3 ; Subtract number per side from start.
|
|
Bpl.s @Seek ; Positive? Then indeed side 1.
|
|
Add.w D1,D3 ; Negative? Then side 0.
|
|
Clr.w D7 ; Set “head 0” flag.
|
|
Bra.s @Seek ; Seek to track.
|
|
|
|
@GCR Moveq #0,D3 ; Init track register.
|
|
Moveq #4,D0 ; Speedzone loop register.
|
|
Move.l D5,D1 ; Sector number.
|
|
Lea GCR1Side,A5 ; 1-sided GCR lookup table.
|
|
Cmp.w #f400K,fFormat(A1) ; 400K format?
|
|
Beq.s @0 ; Yes? Then use 1-sided table.
|
|
Lea GCR2Side,A5 ; 2-sided GCR lookup table.
|
|
|
|
@0 Sub.w itSectors(A5),D1 ; Number of sectors in speed zone.
|
|
Bmi.s @1 ; Negative? Then we've gone to far.
|
|
Tst.w (A5)+ ; Next zone.
|
|
Add.w #GCRCylndrs,D3 ; Add cylinders.
|
|
Dbra D0,@0 ; Loop.
|
|
|
|
@1 Add.w itSectors(A5),D1 ; Make positive.
|
|
Divu (A5),D1 ; Divide by number of sectors in zone.
|
|
Add.w D1,D3 ; Add in cylinder number.
|
|
Swap D3 ; Cylinder number in high word.
|
|
Swap D1 ; Sector number in low word.
|
|
Move.w D1,D3 ; Install sector number.
|
|
|
|
Moveq #13,D1 ; Start with 13 sectors/side.
|
|
Move.l D3,D2 ; Cylinder:Sector.
|
|
Swap D2 ; Cylinder in bottom word.
|
|
@2 Subq.w #1,D1 ; Bump down sectors/side.
|
|
Sub.w #GCRCylndrs,D2 ; Subtract speed zone.
|
|
Bpl.s @2 ; +? Then must be in another zone.
|
|
Move.w D1,D6 ; Sectors per track.
|
|
Swap D6 ; Save it.
|
|
Move.w D1,D6 ; Sector per track.
|
|
Sub.w D1,D3 ; Subtract number per side from start.
|
|
Bpl.s @Seek ; Positive? Then indeed side 1.
|
|
Add.w D1,D3 ; Negative? Then side 0.
|
|
Clr.w D7 ; Set “head 0” flag.
|
|
|
|
@Seek Move.l #NewAge32,A0 ; Base address.
|
|
Sub.w D3,D6 ; Sectors left on cyclinder (if on side 1).
|
|
Swap D3 ; Cylinder in low word.
|
|
Move.w #paramErr,fResult(A1) ; Assume Parameter error. <LW5>
|
|
Cmp.b #DiskCylndrs,D3 ; Number of cylinders per disk. <LW5>
|
|
Bge @ExitRead ; Access beyond last track? <LW5>
|
|
Cmp.b fCylinder(A1),D3 ; Drive already at this track?
|
|
Beq.s @PrepareBuffer ; Yes? Then skip seek.
|
|
Move.w D3,D2 ; Cylinder number.
|
|
Bsr SeekToTrack ; Seek to it.
|
|
Tst.w fResult(A1) ; Seek error? <LW18>
|
|
Bne @ExitRead ; If so report it. <LW18>
|
|
|
|
; D3.l - Cylinder number.
|
|
; D3.h - Starting sector number (physical) (0 based).
|
|
; D4 - Number of sectors to be read.
|
|
; D5 - Starting sector (logical).
|
|
; D6.l - Remaining sectors from start (D3.h) to end-of-track.
|
|
; D6.h - EOT.
|
|
; D7 - Side (0 or 1).
|
|
;
|
|
; Examples: MFM 720K D3.h=5, D7=0 then D6=4 (9 sectors/side)
|
|
; MFM 1.4M D3.h=2, D7=1 then D6=16 (18 sectors/side)
|
|
; GCR 800K D3.l=16 D3.h=5, D7=0 then D6=6 (11 sectors/track for cylinder 16)
|
|
; GCR 800K D3.l=32 D3.h=5, D7=0 then D6=5 (10 sectors/track for cylinder 32)
|
|
; GCR 400K D3.l=16 D3.h=5, D7=0 then D6=6 (single sided).
|
|
;
|
|
|
|
@PrepareBuffer Move.w D5,fStart(A1) ; Save starting sector.
|
|
IF MTMode THEN
|
|
Tst.w D7 ; Side 1?
|
|
Bne.s @3 ; Yes? Then don't add in side 1 sectors.
|
|
Cmp.w #f400K,fFormat(A1) ; Single-sided?
|
|
Beq.s @3 ; Yes? Then can't add in side 1.
|
|
|
|
Move.l D3,D1 ; Sector:Cylinder. <SM19>
|
|
Swap D1 ; Starting sectors number. <SM19>
|
|
Add.w D4,D1 ; # of sectors to read from start of track <SM19>
|
|
|
|
Move.l D6,D0 ; EOT. <SM19>
|
|
Swap D0 ; Sectors/side. <SM19>
|
|
Sub.w D0,D1 ; <SM19>
|
|
Sub.w D0,D1 ; <SM19>
|
|
Blt.s @3 ; Reading 2 track of data? <SM19>
|
|
Add.w D0,D6 ; Total sectors to read. <SM19>
|
|
@3
|
|
ENDIF
|
|
Sub.w D6,D4 ; Subtract off what we can do in one read.
|
|
Bpl.s @30 ; Positive? Then copy buffer. <SM10>
|
|
Add.w D4,D6 ; Number of sectors to read. <SM10>
|
|
|
|
@30 Add.w D6,D5 ; Next starting sector.
|
|
Move.w D6,fCount(A1) ; Save number of sectors we have read.
|
|
|
|
;;;;;;;;;;;;;;;;Add to optimize read routine <SM12>
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; GCR?
|
|
Ble.s @DoGCR ; Yes? Go do GCR stuff.
|
|
Moveq #0,D0 ; Clear D0 for read.
|
|
@DoFastMFM Bsr MFMTrack ;
|
|
Bra.s @DoCleanUp ;
|
|
@DoGCR Bsr ReadAGCRTrack ;
|
|
@DoCleanUp Cmp.b #0,D0 ; Check for error.
|
|
Bne @20 ; Error? Then go save error and retry.
|
|
Move.l D6,D1 ; Get Sector per track.
|
|
Swap D1 ; In high word.
|
|
Move.l D3,D0 ; Get starting sector.
|
|
Swap D0 ; In high word.
|
|
Sub.w D0,D1 ;
|
|
Move.w D1,fCount(A1) ; New count.
|
|
Bra @CopyBuffer ;
|
|
;;;;;;;;;;;;;;;; End of addittion <SM12>
|
|
|
|
@SendCommand Move.b #ReadData,D0 ; Setup read command.
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; GCR?
|
|
Bgt.s @4 ; No? Then leave FM bit.
|
|
Bclr #fFM,D0 ; Clear FM bit.
|
|
IF MTMode THEN
|
|
Cmp.w #f400K,fFormat(A1) ; Single-sided?
|
|
Bne.s @4 ; No? Then leave multi-track.
|
|
Bclr #fMT,D0 ; Clear multi-track.
|
|
ENDIF
|
|
@4
|
|
IF MTMode THEN
|
|
Move.l D6,D1 ; EOT. <SM19>
|
|
Swap D1 ; EOT. <SM19>
|
|
Cmp.b D1,D6 ; Reading more than one track? <SM19>
|
|
Bgt.s @11 ; No? Then leave multi-track. <SM19>
|
|
Bclr #fMT,D0 ; Clear multi-track. <SM19>
|
|
ENDIF
|
|
@11 Bsr WriteToFDC ; Send command.
|
|
|
|
Move.b D7,D0 ; Head select.
|
|
Lsl.b #fHd,D0 ; Move into position.
|
|
Or.b fDriveSelect(A1),D0 ; Drive select.
|
|
Bsr WriteToFDC ; Write head/drive.
|
|
|
|
Move.w D3,D0 ; Cylinder/Sector.
|
|
Bsr WriteToFDC ; Write cylinder number.
|
|
|
|
Move.b D7,D0 ; Head.
|
|
Bsr WriteToFDC ; Write head number.
|
|
|
|
Move.l D3,D0 ; Sector:Cyclinder.
|
|
Swap D0 ; Sector in low word.
|
|
Moveq #-1,D2 ; Assume GCR.
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; GCR format?
|
|
Ble.s @5 ; Yes? Then sectors are 0 based.
|
|
Addq.b #1,D0 ; Force sector number to be 1 based.
|
|
Moveq #0,D2
|
|
@5 Bsr WriteToFDC ; Write sector number.
|
|
|
|
Move.b fCode(A1),D0 ; Format byte (512 bytes/sector).
|
|
Bsr WriteToFDC ; Write format byte.
|
|
|
|
Move.l D3,D0 ; Sector:Cyclinder. <SM10>
|
|
Swap D0 ; Starting sector. <SM10>
|
|
Add.b D6,D0 ; Number of sectors to read. <SM10>
|
|
|
|
IF MTMode THEN
|
|
Move.l D6,D1 ; Get sector info. <SM19>
|
|
Swap D1 ; Get sector per track. <SM19>
|
|
Cmp.b D1,D0 ; Reading more than one track? <SM19>
|
|
Ble.s @12 ; Yes? EOT. <SM19>
|
|
Sub.w D1,D0 ; Multi-track not used. Number of sector to read.<SM19>
|
|
@12
|
|
ENDIF
|
|
@13 Add.b D2,D0 ; 0 or 1 based flag.
|
|
Bsr WriteToFDC ; Last sector to read.
|
|
|
|
Moveq #GSL,D0 ; Gap 3 length. <SM8>
|
|
Bsr WriteToFDC ; Write gap length.
|
|
|
|
Move.w D6,D2 ; Number of sectors to read.
|
|
|
|
@14 Ext.l D2 ; Force long.
|
|
Asl.w #8,D2 ; Multiply by 512.
|
|
Asl.w #1,D2
|
|
|
|
Move.l #pscControl32,A3 ; PSC control register.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Pause PSC.
|
|
@waitfrozen Move.w (A3), D0 ; <SM3>
|
|
Btst.l #pscFrozen, D0 ; <SM3>
|
|
Beq.s @waitfrozen ; check for pause <SM3>
|
|
Move.w (A3),D0 ; Control setting.
|
|
And.w #$1,D0 ; Get set.
|
|
Asl.w #4,D0 ; Calculate offset to correct set.
|
|
Move.l #pscSet032,A5 ; DMA register set.
|
|
Move.l fBuffer(A1),pscAddress(A5,D0.w) ; Set address.
|
|
Move.l D2,pscCount(A5,D0.w) ; Set count.
|
|
Move.w #(1 << pscDir) | \ ;
|
|
(1 << pscEnable) | \ ;
|
|
(1 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Set direction to read and enable.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(0 << pscSense), \ ;
|
|
(A3) ; Un-pause.
|
|
Move.w #1,fResult(A1) ; Set result to “in progress”.
|
|
Moveq #0,D0 ; DTL (no special sector size).
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Write DTL.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #2,D1 ; DBRA/4 Millisecond.
|
|
@Wait Bsr GetResult ; Check for interrupt. <SM23>
|
|
Cmp.w #0,fResult(A1) ; Result.
|
|
Ble.s @DoneDMA ; Done? Out of here.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait ; Loop
|
|
Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bsr ResetFDC ; If timeout then something horrible happened. <SM20>
|
|
Bsr SetupFDC ; Everything needs to be reset. <SM20>
|
|
Move.w #noNybErr,fResult(A1); Report as timeout error. <SM20>
|
|
@DoneDMA Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Move.w #(1 << pscSWReset) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Reset channel. <SM19>
|
|
Move.w fResult(A1),D0 ; Result.
|
|
|
|
Beq.s @CopyBuffer ; No error? Then transfer to user buffer.
|
|
@20 Move.l fError2(A1),fError3(A1)
|
|
Move.l fError2+4(A1),fError3+4(A1)
|
|
Move.l fError1(A1),fError2(A1)
|
|
Move.l fError1+4(A1),fError2+4(A1)
|
|
Move.l fStatus0(A1),fError1(A1)
|
|
Move.l fStatus0+4(A1),fError1+4(A1)
|
|
Subq.b #1,fRetries(A1) ; Decrement retry counter.
|
|
Bgt @SendCommand ; More retries left? Then try again.
|
|
Bmi.s @Fail ; Did we recalibrate?
|
|
Bsr Track0 ; No? Then try recalibrating.
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne.s @Fail ; Yes? then fail. <LW18>
|
|
Move.w D3,D2 ; Cylinder number.
|
|
Bsr SeekToTrack ; Seek to it.
|
|
Tst.w fResult(A1) ; Error?.
|
|
Bne.s @Fail ; Yes? then fail. <LW18>
|
|
Bra @SendCommand ; And try one more time.
|
|
@Fail Addq.w #1,fHardErrors(A1) ; Bump hard error counter.
|
|
Clr.w fCount(A1) ; Void buffer.
|
|
Bra.s @ExitRead ; And leave.
|
|
|
|
@CopyBuffer Move.l A1,A3 ; Save floppy info record.
|
|
Move.l fBuffer(A1),A0 ; Buffer pointer.
|
|
Move.l A2,A1 ; Destination.
|
|
Move.w D6,D0 ; Number of sectors read. <SM10>
|
|
Ext.l D0 ; Force long.
|
|
Asl.w #8,D0 ; Multiply by 512.
|
|
Asl.w #1,D0
|
|
Add.l D0,A2 ; Bump buffer.
|
|
Add.l D0,ioActCount(A4) ; Bump actual count.
|
|
Cmpa.l A0,A1 ; Let's speed up format verifies.
|
|
Beq.s @9
|
|
Cmp.w #rdVerify,ioPosMode(A4) ; Read verify?
|
|
Blt.s @8 ; No? Then copy into users buffer.
|
|
|
|
Exg A1,A3 ; Swap registers.
|
|
Asr.w #2,D0 ; Long words to compare.
|
|
Move.w #dataVerErr,fResult(A1) ; Assume error.
|
|
@CompareLoop Cmp.l (A0)+,(A3)+ ; Match?
|
|
Bne.s @ExitRead ; Fail? Then leave.
|
|
Subq.l #1,D0 ; Decrement counter.
|
|
Bne.s @CompareLoop ; Compare till done.
|
|
Move.w #noErr,fResult(A1) ; Set no error.
|
|
Bra.s @10 ; Continue.
|
|
|
|
@8 _BlockMoveData ; Transfer into user buffer.
|
|
@9 Move.l A3,A1 ; Floppy info record.
|
|
|
|
@10 Move.b fRetrySeed(A1),D0 ; Initial seed value.
|
|
Cmp.b fRetries(A1),D0 ; Did we have to perform a retry?
|
|
Beq @ReadLoop ;
|
|
Addq.w #1,fSoftErrors(A1) ; Yes? Then bump soft errors.
|
|
Move.b D0,fRetries(A1) ; Reset counter.
|
|
Bra @ReadLoop
|
|
|
|
@ExitRead Move.l ioActCount(A4),D0 ; Bytes read.
|
|
Add.l D0,fPosition(A1) ; Update position.
|
|
Move.l fPosition(A1),ioPosOffset(A4) ; Update position offset.
|
|
Rts ; Return.
|
|
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: DoWrite
|
|
; Inputs: A1 -> Floppy info record.
|
|
; A2 -> User buffer.
|
|
; A4 -> Parameter block.
|
|
; D4 -> Count in sectors.
|
|
; D5 -> Start (in sectors).
|
|
; Outputs: fResult(A1).
|
|
; Function: Handle write requests.
|
|
; Called by: Prime
|
|
;------------------------------------------------------------------------------
|
|
|
|
DoWrite Moveq #0,D7 ; Set flag that FDC is not setup.
|
|
Move.w #noErr,fResult(A1) ; In case there are no sectors to write.
|
|
|
|
@WriteLoop Tst.w D4 ; Are there any blocks left to transfer?
|
|
Ble @ExitWrite ; No? Then we're done.
|
|
Tst.l D7 ; FDC setup?
|
|
Bmi.s @MFM ; Yes? Then check for MFM write.
|
|
Moveq #-1,D7 ; Set flag that FDC is setup.
|
|
Bsr SetUpFDC ; Set it up.
|
|
Tst.w fResult(A1) ; Any errors?
|
|
Bne @ExitWrite ; Yes? Out of here.
|
|
Move.w #sectNFerr,fResult(A1) ; Assume blank disk.
|
|
Cmp.w #fBlank,fFormat(A1) ; Blank or missing disk?
|
|
Ble @ExitWrite ; Yes? Then leave with error.
|
|
|
|
@MFM Move.w #1,D7 ; Assume side 1.
|
|
Moveq #18,D0 ; Min. sectors/cylinder for non-GCR.
|
|
Move.w fFormat(A1),D1 ; Format code.
|
|
Sub.b #f720K,D1 ; GCR format?
|
|
Blt.s @GCR ; Yes? Then variable sectors/cylinder.
|
|
|
|
Asl.w D1,D0 ; Calc. sectors/cylinder.
|
|
Move.l D5,D3 ; Sector for I/O.
|
|
Divu D0,D3 ; Find starting track.
|
|
Swap D3 ; Sector in low word.
|
|
|
|
Lea SectorsSide,A5 ; Sectors/side table.
|
|
Move.w fFormat(A1),D1 ; Format byte.
|
|
Asl.w #1,D1 ; Word index.
|
|
Move.w 0(A5,D1.w),D1 ; Sectors/side.
|
|
Move.w D1,D6 ; Sector per track.
|
|
Swap D6 ; Save it.
|
|
Move.w D1,D6 ; Sector per track.
|
|
Sub.w D1,D3 ; Subtract number per side from start.
|
|
Bpl.s @Seek ; Positive? Then indeed side 1.
|
|
Add.w D1,D3 ; Negative? Then side 0.
|
|
Clr.w D7 ; Set “head 0” flag.
|
|
Bra.s @Seek ; Seek to track.
|
|
|
|
@GCR Moveq #0,D3 ; Init track register.
|
|
Moveq #4,D0 ; Speedzone loop register.
|
|
Move.l D5,D1 ; Sector number.
|
|
Lea GCR1Side,A5 ; 1-sided GCR lookup table.
|
|
Cmp.w #f400K,fFormat(A1) ; 400K format?
|
|
Beq.s @0 ; Yes? Then use 1-sided table.
|
|
Lea GCR2Side,A5 ; 2-sided GCR lookup table.
|
|
|
|
@0 Sub.w itSectors(A5),D1 ; Number of sectors in speed zone.
|
|
Bmi.s @1 ; Negative? Then we've gone to far.
|
|
Tst.w (A5)+ ; Next zone.
|
|
Add.w #GCRCylndrs,D3 ; Add cylinders.
|
|
Dbra D0,@0 ; Loop.
|
|
|
|
@1 Add.w itSectors(A5),D1 ; Make positive.
|
|
Divu (A5),D1 ; Divide by number of sectors in zone.
|
|
Add.w D1,D3 ; Add in cylinder number.
|
|
Swap D3 ; Cylinder number in high word.
|
|
Swap D1 ; Sector number in low word.
|
|
Move.w D1,D3 ; Install sector number.
|
|
|
|
Moveq #13,D1 ; Start with 13 sectors/side.
|
|
Move.l D3,D2 ; Cylinder:Sector.
|
|
Swap D2 ; Cylinder in bottom word.
|
|
@2 Subq.w #1,D1 ; Bump down sectors/side.
|
|
Sub.w #GCRCylndrs,D2 ; Subtract speed zone.
|
|
Bpl.s @2 ; +? Then must be in another zone.
|
|
Move.w D1,D6 ; Sectors per track.
|
|
Swap D6 ; Save it.
|
|
Move.w D1,D6 ; Sector per track.
|
|
Sub.w D1,D3 ; Subtract number per side from start.
|
|
Bpl.s @Seek ; Positive? Then indeed side 1.
|
|
Add.w D1,D3 ; Negative? Then side 0.
|
|
Clr.w D7 ; Set “head 0” flag.
|
|
|
|
@Seek Move.l #NewAge32,A0 ; Base address.
|
|
Sub.w D3,D6 ; Sectors left on cyclinder (if on side 1).
|
|
Swap D3 ; Cylinder in low word.
|
|
Move.w #paramErr,fResult(A1) ; Assume Parameter error. <LW5>
|
|
Cmp.b #DiskCylndrs,D3 ; Number of cylinders per disk. <LW5>
|
|
Bge @ExitWrite ; Access beyond last track? <LW5>
|
|
Cmp.b fCylinder(A1),D3 ; Drive already at this track?
|
|
Beq.s @PrepareBuffer ; Yes? Then skip seek.
|
|
Move.w D3,D2 ; Cylinder number.
|
|
Bsr SeekToTrack ; Seek to it.
|
|
Tst.w fResult(A1) ; Seek error? <LW18>
|
|
Bne @ExitWrite ; If so report it. <LW18>
|
|
|
|
; D3.l - Cylinder number.
|
|
; D3.h - Starting sector number (physical) (0 based).
|
|
; D4 - Number of sectors to be write.
|
|
; D5 - Starting sector (logical).
|
|
; D6 - Remaining sectors from start (D3.h) to end-of-track.
|
|
; D7 - Side (0 or 1).
|
|
;
|
|
; Examples: MFM 720K D3.h=5, D7=0 then D6=4 (9 sectors/side)
|
|
; MFM 1.4M D3.h=2, D7=1 then D6=16 (18 sectors/side)
|
|
; GCR 800K D3.l=16 D3.h=5, D7=0 then D6=6 (11 sectors/track for cylinder 16)
|
|
; GCR 800K D3.l=32 D3.h=5, D7=0 then D6=5 (10 sectors/track for cylinder 32)
|
|
; GCR 400K D3.l=16 D3.h=5, D7=0 then D6=6 (single sided).
|
|
;
|
|
|
|
@PrepareBuffer Move.w D5,fStart(A1) ; Save starting sector.
|
|
Sub.w D6,D4 ; Subtract off what we can do in one write.
|
|
Bpl.s @3 ; Positive? Then copy buffer.
|
|
Add.w D4,D6 ; Number of sectors to write.
|
|
|
|
@3 Add.w D6,D5 ; Next starting sector.
|
|
Move.w D6,fCount(A1) ; Save number of sectors we have read.
|
|
|
|
Move.l A2,A5 ; Assume we'll use application buffer.
|
|
Move.w D6,D0 ; Number of sectors to write.
|
|
Ext.l D0 ; Force long.
|
|
Asl.w #8,D0 ; Multiply by 512.
|
|
Asl.w #1,D0
|
|
Add.l D0,A2 ; Bump application buffer.
|
|
|
|
Move.l A5,A0 ; No? Then copy into DMA buffer.
|
|
Move.l A1,A3 ; Save floppy info record.
|
|
Move.l fBuffer(A1),A1 ; Buffer pointer.
|
|
_BlockMoveData ; Transfer into user buffer.
|
|
; Move.l A1,A5 ; Use DMA buffer.
|
|
Move.l A3,A1 ; Restore floppy info record.
|
|
Move.l #NewAge32,A0 ; Base address.
|
|
;;;;;;;;;;;;;;;;Add to optimize read routine <SM12>
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; GCR?
|
|
Ble.s @SendCommand ; Yes? Go do GCR stuff.
|
|
Moveq #1,D0 ; Set for write.
|
|
Bsr MFMTrack ;
|
|
Cmp.b #0,D0 ; Check for error.
|
|
Bne @21 ; Error? Then go save error and retry.
|
|
Move.w D6,D2 ; Sectors to write. <SM24>
|
|
Ext.l D2 ; Force long. <SM24>
|
|
Asl.w #8,D2 ; Multiply by 512. <SM24>
|
|
Asl.w #1,D2 ; <SM24>
|
|
Bra @UpdateCnt ;
|
|
;;;;;;;;;;;;;;;; End of addittion <SM12>
|
|
@SendCommand Move.b #WriteData,D0 ; Setup write command.
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; GCR?
|
|
Bgt.s @4 ; No? Then leave FM bit.
|
|
Bclr #fFM,D0 ; Clear FM bit.
|
|
@4 Bsr WriteToFDC ; Send command.
|
|
|
|
Move.b D7,D0 ; Head select.
|
|
Lsl.b #fHd,D0 ; Move into position.
|
|
Or.b fDriveSelect(A1),D0 ; Drive select.
|
|
Bsr WriteToFDC ; Write head/drive.
|
|
|
|
Move.w D3,D0 ; Cylinder/Sector.
|
|
Bsr WriteToFDC ; Write cylinder number.
|
|
|
|
Move.b D7,D0 ; Head.
|
|
Bsr WriteToFDC ; Write head number.
|
|
|
|
Move.l D3,D0 ; Sector:Cyclinder.
|
|
Swap D0 ; Sector in low word.
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; GCR format?
|
|
Ble.s @5 ; Yes? Then sectors are 0 based.
|
|
Addq.b #1,D0 ; Force sector number to be 1 based.
|
|
@5 Move.w D0,D2 ; Save starting sector.
|
|
Bsr WriteToFDC ; Write sector number.
|
|
|
|
Move.b fCode(A1),D0 ; Format byte (512 bytes/sector).
|
|
Bsr WriteToFDC ; Write format byte.
|
|
|
|
Move.b D6,D0 ; Sectors to write.
|
|
Add.b D2,D0 ; 0 or 1 based flag.
|
|
Subq.b #1,D0
|
|
Bsr WriteToFDC ; EOT (last sector to read).
|
|
|
|
Moveq #GSL,D0 ; Gap 3 length. <SM8>
|
|
Bsr WriteToFDC ; Write gap length.
|
|
|
|
Move.w D6,D2 ; Sectors to write.
|
|
Ext.l D2 ; Force long.
|
|
Asl.w #8,D2 ; Multiply by 512.
|
|
Asl.w #1,D2
|
|
|
|
Move.l #pscControl32,A3 ; PSC control register.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Pause PSC.
|
|
@waitfrozen Move.w (A3), D0 ; <SM3>
|
|
Btst.l #pscFrozen, D0 ; <SM3>
|
|
Beq.s @waitfrozen ; check for pause <SM3>
|
|
Move.w (A3),D0 ; Control setting.
|
|
And.w #$1,D0 ; Get set.
|
|
Asl.w #4,D0 ; Calculate offset to correct set.
|
|
Movea.l #pscSet032,A5 ; DMA register set.
|
|
Move.l fBuffer(A1),pscAddress(A5,D0.w) ; Set address.
|
|
Move.l D2,pscCount(A5,D0.w) ; Set count. <SM21>
|
|
Move.w #(1 << pscDir) | \ ;
|
|
(0 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Set direction to write.
|
|
Move.w #(1 << pscEnable) | \ ;
|
|
(1 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Enable it.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(0 << pscSense), \ ;
|
|
(A3) ; Un-pause.
|
|
|
|
Move.w #1,fResult(A1) ; Set result to “in progress”.
|
|
Moveq #0,D0 ; DTL (no special sector size).
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Write DTL.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #2,D1 ; DBRA/4 Millisecond.
|
|
@Wait Bsr GetResult ; Check for interrupt. <SM23>
|
|
Cmp.w #0,fResult(A1) ; Result.
|
|
Ble.s @DoneDMA ; Done? Out of here.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait ; Loop
|
|
Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bsr ResetFDC ; If timeout then something horrible happened. <SM20>
|
|
Bsr SetupFDC ; Everything needs to be reset. <SM20>
|
|
Move.w #noNybErr,fResult(A1); Report as timeout error. <SM20>
|
|
@DoneDMA Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Move.w #(1 << pscSWReset) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Reset channel.
|
|
Move.w fResult(A1),D0 ; Result.
|
|
|
|
Beq.s @UpdateCnt ; OK? Then update counter.
|
|
@21 Move.l fError2(A1),fError3(A1)
|
|
Move.l fError2+4(A1),fError3+4(A1)
|
|
Move.l fError1(A1),fError2(A1)
|
|
Move.l fError1+4(A1),fError2+4(A1)
|
|
Move.l fStatus0(A1),fError1(A1)
|
|
Move.l fStatus0+4(A1),fError1+4(A1)
|
|
Subq.b #1,fRetries(A1) ; Decrement retry counter.
|
|
Bgt @SendCommand ; More retries left? Then try again.
|
|
Bmi.s @Fail ; Did we recalibrate?
|
|
Bsr Track0 ; No? Then try recalibrating.
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne.s @Fail ; Yes? then fail. <LW18>
|
|
Move.w D3,D2 ; Cylinder number.
|
|
Bsr SeekToTrack ; Seek to it.
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne.s @Fail ; Yes? then fail. <LW18>
|
|
Bra @SendCommand ; And try one more time.
|
|
@Fail Addq.w #1,fHardErrors(A1) ; Bump hard error counter.
|
|
Clr.w fCount(A1) ; Void buffer.
|
|
Bra.s @ExitWrite ; And leave.
|
|
|
|
@UpdateCnt Add.l D2,ioActCount(A4) ; Update actual count.
|
|
Move.b fRetrySeed(A1),D0 ; Initial seed value.
|
|
Cmp.b fRetries(A1),D0 ; Did we have to perform a retry?
|
|
Beq @WriteLoop ; No? Then just continue.
|
|
Addq.w #1,fSoftErrors(A1) ; Yes? Then bump soft errors.
|
|
Move.b D0,fRetries(A1) ; Reset counter.
|
|
Bra @WriteLoop
|
|
|
|
@ExitWrite Move.l ioActCount(A4),D0 ; Bytes read.
|
|
Add.l D0,fPosition(A1) ; Update position.
|
|
Move.l fPosition(A1),ioPosOffset(A4) ; Update position offset.
|
|
Rts ; Return.
|
|
|
|
GCR1Side Dc.w 12,11,10,9,8 ; Sectors/cylinder.
|
|
Dc.w 192,176,160,144,128 ; Sectors/zone.
|
|
GCR2Side Dc.w 24,22,20,18,16 ; Sectors/cylinder.
|
|
Dc.w 384,352,320,288,256 ; Sectors/zone.
|
|
SectorsSide Dc.w 0,0,0,0,0,0,9,18,36 ; Sectors/side for all formats.
|
|
|
|
; Add ReadATrack Routine <SM12>
|
|
; Modified ReadATrack to MFMTrack <SM23>
|
|
;------------------------------------------------------------------------------
|
|
; Routine: MFMTrack
|
|
; Inputs: A0 -> Newage Address
|
|
; A1 -> Floppy info record.
|
|
; A4 -> Parameter block.
|
|
; D3.l -> Cylinder.
|
|
; D6.l -> # of sector/track.
|
|
; D7 -> Head(side).
|
|
; Outputs: D0 -> 0 = Pass.
|
|
; Function: Read a track.
|
|
; Called by: DoRead
|
|
;------------------------------------------------------------------------------
|
|
TrackRegs REG A3-A5/D2-D6 ; Save these registers.
|
|
|
|
MFMTrack Movem.l TrackRegs,-(Sp) ; Scratch registers.
|
|
Moveq #0,D2 ; Clear register.
|
|
Move.w D0,D2 ; Read/Write flag.
|
|
Swap D2 ; Save it.
|
|
Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #ReadMFMID,D0 ; Read ID Command.
|
|
Bsr WriteToFDC ; Write command.
|
|
Move.b D7,D0 ; Head select.
|
|
Lsl.b #fHd,D0 ; Move into position.
|
|
Or.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts.
|
|
Bsr WriteToFDC ; Write head/drive.
|
|
|
|
Moveq #0,D1 ; Init counter.
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Waitx Bsr GetResult ; Go check for interrupt.
|
|
Cmp.b #-1,fStatus0(A1) ; Wait for status to change.
|
|
Bne.s @3 ; Change? Then check it.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Waitx ; Loop until it changes.
|
|
Move.b #EnableFDCInt,VIA2IER; Enable interrupts.
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bsr ResetFDC ; Time out. Reset FDC.
|
|
Bsr SetUpFDC ; And then restore the world.
|
|
Move.w #noAdrMkErr,fResult(A1) ; Assume blank disk.
|
|
Bra @ExitTrack ; Leave with error.
|
|
|
|
@3 Move.b fSector(A1),D2 ; Current sector number.
|
|
Swap D6 ;
|
|
Cmp.b D2,D6 ; Compare current sector with last sector.
|
|
Bne.s @4 ; Not equal?
|
|
Move.w #0,D2 ; Wrap it around.
|
|
|
|
@4 Moveq #0,D4 ; Clear address value.
|
|
Moveq #0,D5 ; Clear count value.
|
|
Swap D3 ; Get first sector requested.
|
|
; Assume current sector is before the first requested sector and set up accordingly.
|
|
Move.l fBuffer(A1),D4 ; Address for PSC active set.
|
|
|
|
Swap D2 ; Get read/write flag.
|
|
Cmp.b #0,D2 ; Is it read?
|
|
Beq.s @forread ; Yes? set up for read.
|
|
Swap D6 ; Get Number of sectors to write.
|
|
Move.w D6,D5 ; Get Number of sectors to write.
|
|
Move.b D6,D0 ; Get number of sectors to write.
|
|
Add.b D3,D0 ; Last sector to write.
|
|
Swap D6 ; In high word.
|
|
Swap D2 ; In high word.
|
|
Cmp.b D0,D2 ; Is current sector beyond last sector?
|
|
Blt.s @10 ; No? Just continue.
|
|
Move.w #0,D2 ; Reset currnet.
|
|
Bra.s @10 ;
|
|
|
|
@forread Swap D2 ; Get current sector.
|
|
Move.w D6,D5 ; Get Last.
|
|
Sub.w D3,D5 ; Subtract first from last.
|
|
@10 Asl.w #8,D5 ; Multiply by 512.
|
|
Asl.w #1,D5
|
|
|
|
Cmp.b D3,D2 ; Is current before or after first requested sector?
|
|
Ble.s @SetPsc ; Before? Every thing is all set.
|
|
|
|
Moveq #0,D1 ; Clear.
|
|
Move.b D2,D1 ; Get current.
|
|
Sub.b D3,D1 ; The difference between actual start
|
|
Asl.w #8,D1 ; and requested first start.
|
|
Asl.w #1,D1 ; Then multiply by 512 to the bytes.
|
|
|
|
Add.l D1,D4 ; Fix address of buffer.
|
|
Sub.l D1,D5 ; And the count.
|
|
|
|
|
|
@SetPsc Move.l #pscControl32,A3 ; PSC control register.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Pause channel.
|
|
@waitfrozen Move.w (A3), D0 ;
|
|
Btst.l #pscFrozen, D0 ;
|
|
Beq.s @waitfrozen ; Check for pause.
|
|
Move.w (A3),D0 ; Control setting.
|
|
And.w #$1,D0 ; Get set.
|
|
Asl.w #4,D0 ; Calculate offset to correct set.
|
|
Move.l #pscSet032,A5 ; DMA register set.
|
|
|
|
Move.l D4,pscAddress(A5,D0.w) ; Set address.
|
|
Move.l D5,pscCount(A5,D0.w) ; Set count.
|
|
Swap D2 ; Get read/write flag.
|
|
Cmp.b #0,D2 ; Is it read?
|
|
Beq.s @SetRead1 ; Yes? set up for read.
|
|
|
|
Move.w #(1 << pscDir) | \ ;
|
|
(0 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Set direction to write.
|
|
Move.w #(1 << pscEnable) | \ ;
|
|
(1 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Enable it.
|
|
Bra.s @16 ;
|
|
|
|
@SetRead1 Move.w #(1 << pscDir) | \ ;
|
|
(1 << pscEnable) | \ ;
|
|
(1 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Set direction to read and enable.
|
|
|
|
@16 Swap D2
|
|
Cmp.b D3,D2 ; Is current before or after first requested sector?
|
|
Ble.s @UnpausePSC ; Before? Every thing is all set.
|
|
|
|
Bchg #4,D0 ; Switchset.
|
|
Move.l fBuffer(A1),pscAddress(A5,D0.w) ; Set address.
|
|
Move.l D1,pscCount(A5,D0.w) ; Set count.
|
|
Swap D2 ; Get read/write flag.
|
|
Cmp.b #0,D2 ; Is it read?
|
|
Bne.s @17 ; No? Out of here.
|
|
|
|
Move.w #(1 << pscDir) | \ ;
|
|
(1 << pscEnable) | \ ;
|
|
(1 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Set direction to read and enable.
|
|
@17 Swap D2 ;
|
|
@UnpausePSC Move.w #(1 << pscPause) | \ ;
|
|
(0 << pscSense), \ ;
|
|
(A3) ; Un-pause.
|
|
|
|
; Assume current sector is before the first requested sector and set up accordingly.
|
|
Move.b D3,D4 ; First sector to read.
|
|
Add.b #1,D4 ; Make it one base.
|
|
|
|
Swap D2 ; Get read/write flag.
|
|
Cmp.b #0,D2 ; Is it read?
|
|
Beq.s @lastread ; Yes? set up for read.
|
|
Move.l D6,D5 ;
|
|
Swap D5 ; Get number of sector to write.
|
|
Add.b D3,D5 ; Last sector to write.
|
|
Bra.s @11
|
|
|
|
@lastread Move.b D6,D5 ; Last sector to read.
|
|
|
|
@11 Swap D2 ; Get current sector.
|
|
Cmp.b D3,D2 ; Is current before or after first requested sector?
|
|
Ble.s @13 ; Before? Every thing is all set.
|
|
Move.b D2,D4 ; Current sector.
|
|
Add.b #1,D4 ; Start at next sector.
|
|
|
|
@13 Move.l D2,D0 ;
|
|
Swap D0 ; Get read/write flag.
|
|
Cmp.b #0,D0 ; Is it read
|
|
Beq.s @MFMRead ; Yes
|
|
@MFMWrite Move.b #WriteData,D0 ; Setup write command.
|
|
Bra.s @12 ;
|
|
|
|
@MFMRead Move.b #ReadData,D0 ; Setup read command.
|
|
@12 Bsr WriteToFDC ; Send command.
|
|
|
|
Move.b D7,D0 ; Head select.
|
|
Lsl.b #fHd,D0 ; Move into position.
|
|
Or.b fDriveSelect(A1),D0 ; Drive select.
|
|
Bsr WriteToFDC ; Write head/drive.
|
|
|
|
Move.l D3,D0 ; Cylinder.
|
|
Swap D0 ;
|
|
Bsr WriteToFDC ; Write cylinder number.
|
|
|
|
Move.b D7,D0 ; Head.
|
|
Bsr WriteToFDC ; Write head number.
|
|
|
|
Move.b D4,D0 ; Starting sector.
|
|
Bsr WriteToFDC ; Write sector number.
|
|
|
|
Move.b fCode(A1),D0 ; Format byte (512 bytes/sector).
|
|
Bsr WriteToFDC ; Write format byte.
|
|
|
|
Move.b D5,D0 ; Last sector to read.
|
|
Bsr WriteToFDC ; EOT (last sector to read).
|
|
|
|
Moveq #GSL,D0 ; Gap 3 length.
|
|
Bsr WriteToFDC ; Write gap length.
|
|
|
|
Move.w #1,fResult(A1) ; Set result to “in progress”.
|
|
Moveq #0,D0 ; DTL (no special sector size).
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts.
|
|
Bsr WriteToFDC ; Write DTL.
|
|
|
|
Moveq #0,D1 ; Init counter.
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #2,D1 ; DBRA/4 Millisecond.
|
|
@Wait Bsr GetResult ; Check for interrupt.
|
|
Cmp.w #0,fResult(A1) ; Result.
|
|
Ble.s @DoneDMA ; Done? Out of here.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait ; Loop
|
|
Move.b #EnableFDCInt,VIA2IER ; Enable interrupts.
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bsr ResetFDC ; If timeout then something horrible happened.
|
|
Bsr SetupFDC ; Everything needs to be reset.
|
|
Move.w #noNybErr,fResult(A1); Report as timeout error.
|
|
@DoneDMA Move.b #EnableFDCInt,VIA2IER ; Enable interrupts.
|
|
Cmp.w #0,fResult(A1) ; Result.
|
|
Bne.s @ResetPSC ; Exit if error occurs.
|
|
|
|
Cmp.b D3,D2 ; Is current before or after first requested sector?
|
|
Ble.s @ResetPSC ; Before? Every is done. Out of here.
|
|
|
|
Move.b D3,D4 ;
|
|
Add.b #1,D4 ;
|
|
Move.b D2,D5 ;
|
|
Move.w #0,D2 ; The terminating condition.
|
|
Move.l D2,D0 ; Get read/write flag.
|
|
Swap D0 ; In high word.
|
|
Cmp.b #0,D0 ;
|
|
Beq.s @14 ; Read?
|
|
|
|
Move.w (A3),D0 ; Control setting.
|
|
And.w #$1,D0 ; Get set.
|
|
Asl.w #4,D0 ; Calculate offset to correct set.
|
|
Move.w #(1 << pscDir) | \ ;
|
|
(0 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Set direction to write.
|
|
Move.w #(1 << pscEnable) | \ ;
|
|
(1 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Enable it.
|
|
Bra @MFMWrite ;
|
|
@14 Bra @MFMRead ;
|
|
|
|
@ResetPSC Move.w #(1 << pscSWReset) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Pause channel.
|
|
|
|
@ExitTrack Move.w fResult(A1),D0 ; Result.
|
|
Movem.l (Sp)+,TrackRegs ; Restore registers.
|
|
Rts ; Done
|
|
;------------------------------------------------------------------------------
|
|
; Routine: ReadAGCRTrack
|
|
; Inputs: A0 -> Newage Address
|
|
; A1 -> Floppy info record.
|
|
; A4 -> Parameter block.
|
|
; D3.l -> Cylinder.
|
|
; D6.l -> # of sector/track.
|
|
; D7 -> Head(side).
|
|
; Outputs: D0 -> 0 = Pass.
|
|
; Function: Read a track.
|
|
; Called by: DoRead
|
|
;------------------------------------------------------------------------------
|
|
GCRTrackRegs REG A3-A5/D6 ; Save these registers.
|
|
|
|
ReadAGCRTrack Movem.l GCRTrackRegs,-(Sp) ; Scratch registers.
|
|
Move.l #pscControl32,A3 ; PSC control register.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Pause channel.
|
|
@waitfrozen Move.w (A3), D0 ;
|
|
Btst.l #pscFrozen, D0 ;
|
|
Beq.s @waitfrozen ; Check for pause.
|
|
Move.w (A3),D0 ; Control setting.
|
|
And.w #$1,D0 ; Get set.
|
|
Asl.w #4,D0 ; Calculate offset to correct set.
|
|
Move.l #pscSet032,A5 ; DMA register set.
|
|
Move.l fBuffer(A1),pscAddress(A5,D0.w) ; Set address.
|
|
Swap D6 ;
|
|
Move.w D6,D1 ; Sectors per track.
|
|
Swap D3 ; Get starting sector.
|
|
Sub.w D3,D1 ; Number of sector from starting sector to end of track.
|
|
Swap D3 ;
|
|
Ext.l D1 ; Force long.
|
|
Asl.w #8,D1 ; Multiply by 512.
|
|
Asl.w #1,D1
|
|
Move.l D1,pscCount(A5,D0.w); Set count.
|
|
Move.w #(1 << pscDir) | \ ;
|
|
(1 << pscEnable) | \ ;
|
|
(1 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Set direction to read and enable.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(0 << pscSense), \ ;
|
|
(A3) ; Un-pause.
|
|
Move.b #ReadData,D0 ; Setup read command.
|
|
Bclr #fFM,D0 ; Clear FM bit.
|
|
Bsr WriteToFDC ; Send command.
|
|
|
|
Move.b D7,D0 ; Head select.
|
|
Lsl.b #fHd,D0 ; Move into position.
|
|
Or.b fDriveSelect(A1),D0 ; Drive select.
|
|
Bsr WriteToFDC ; Write head/drive.
|
|
|
|
Move.b D3,D0 ; Cylinder.
|
|
Bsr WriteToFDC ; Write cylinder number.
|
|
|
|
Move.b D7,D0 ; Head.
|
|
Bsr WriteToFDC ; Write head number.
|
|
|
|
Move.l D3,D0 ; Sector:Cyclinder.
|
|
Swap D0 ; Sector in low word.
|
|
Bsr WriteToFDC ; Write sector number.
|
|
|
|
Move.b fCode(A1),D0 ; Format byte (512 bytes/sector).
|
|
Bsr WriteToFDC ; Write format byte.
|
|
|
|
Move.b D6,D0 ; EOT.
|
|
Sub.b #1,D0 ; Make it zero base.
|
|
Bsr WriteToFDC ; EOT (last sector to read).
|
|
|
|
Moveq #GSL,D0 ; Gap 3 length.
|
|
Bsr WriteToFDC ; Write gap length.
|
|
|
|
Move.w #1,fResult(A1) ; Set result to “in progress”.
|
|
Moveq #0,D0 ; DTL (no special sector size).
|
|
Move.b #DisableFDCInt,VIA2IER; Disable interrupts.
|
|
Bsr WriteToFDC ; Write DTL.
|
|
|
|
Moveq #0,D1 ; Init counter.
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #2,D1 ; DBRA/4 Millisecond.
|
|
@Wait Bsr GetResult ; Check for interrupt.
|
|
Cmp.w #0,fResult(A1) ; Result.
|
|
Ble.s @DoneDMA ; Done? Out of here.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait ; Loop
|
|
Move.b #EnableFDCInt,VIA2IER ; Enable interrupts.
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bsr ResetFDC ; If timeout then something horrible happened.
|
|
Bsr SetupFDC ; Everything needs to be reset.
|
|
Move.w #noNybErr,fResult(A1); Report as timeout error.
|
|
@DoneDMA Move.b #EnableFDCInt,VIA2IER ; Enable interrupts.
|
|
Move.w #(1 << pscSWReset) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Pause channel.
|
|
|
|
@ExitTrack Move.w fResult(A1),D0 ; Result.
|
|
Movem.l (Sp)+,GCRTrackRegs ; Restore registers.
|
|
Rts ; Done
|
|
;------------------------------------------------------------------------------
|
|
; Routine: Control
|
|
; Inputs: A0 -> Parameter block.
|
|
; A1 -> DCE.
|
|
; Outputs: D0 -> 0 - OK, ~0 - Error.
|
|
; Function: Handle control requests.
|
|
;------------------------------------------------------------------------------
|
|
|
|
ControlRegs REG A0-A5/D3-D7 ; Registers abused.
|
|
|
|
Control
|
|
Movem.l ControlRegs,-(Sp) ; Scratch registers.
|
|
Movea.l A0,A4 ; Save parameter block.
|
|
Movea.l NewAgeVars,A3 ; DCE pointer.
|
|
Moveq #controlErr,D6 ; Assume failure.
|
|
|
|
Move.w csCode(A4),D0 ; CSCode.
|
|
Lea ControlVectors,A0 ; Vector table.
|
|
@Loop Cmp.w (A0),D0 ; Match?
|
|
Beq.s @CallIt ; Yes? Then call it.
|
|
Tst.l (A0)+ ; Next vector.
|
|
Bne.s @Loop ; Does it exist? Then check it.
|
|
Bra.s @ExitControl ; No? Then leave with error.
|
|
|
|
@CallIt Move.w 2(A0),D0 ; Offset.
|
|
Beq.s @ExitControl ; Leave if no routine.
|
|
Jsr ControlVectors(D0) ; Call it.
|
|
|
|
@ExitControl
|
|
Move.w D6,D0 ; Call result.
|
|
Movem.l (Sp)+,ControlRegs ; Restore registers.
|
|
Move.w D0,ioResult(A0) ; Set result value.
|
|
Move.w ioTrap(A0),D1 ; Trap called.
|
|
Btst #noQueueBit,D1 ; Called immediately?
|
|
Bne.s @2 ; Yes? Then leave via an RTS.
|
|
Tst.w D0 ; Completed?
|
|
Ble.s @1 ; Yes? Then go through IODone.
|
|
Btst #asyncTrpBit,D1 ; Called asynchronously?
|
|
Bne.s @2 ; Yes? Then leave via an RTS.
|
|
|
|
@1 Move.l JIODone,-(Sp) ; Vector to IODone.
|
|
@2 Rts ; Return.
|
|
|
|
ControlVectors Dc.w 01,KillIO - ControlVectors
|
|
Dc.w 05,VerifyDisk - ControlVectors
|
|
Dc.w 06,FormatDisk - ControlVectors
|
|
Dc.w 07,EjectDisk - ControlVectors
|
|
Dc.w 08,TagBuffer - ControlVectors
|
|
Dc.w 09,TrackCache - ControlVectors
|
|
Dc.w 21,GetDriveIcon - ControlVectors
|
|
Dc.w 22,GetMediaIcon - ControlVectors
|
|
Dc.w 23,DriveInfo - ControlVectors
|
|
Dc.w 65,CheckMotor - ControlVectors
|
|
|
|
; Apple use only routines…
|
|
Dc.w 18244,TrackDump - ControlVectors
|
|
Dc.w 21315,DiskCopy - ControlVectors
|
|
Dc.w 0, 0
|
|
|
|
;******************************************************************************
|
|
; KillIO (not supported).
|
|
;******************************************************************************
|
|
|
|
KillIO Moveq #-1,D6 ; Result.
|
|
Rts ; And return.
|
|
|
|
;******************************************************************************
|
|
; Verify disk in drive.
|
|
;******************************************************************************
|
|
|
|
VerifyDisk Link A6,#-ioQElSize ; Stack frame for PB.
|
|
Move.l A4,-(Sp) ; Save PB.
|
|
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record?
|
|
Beq.s @Prepare ; Yes? Then use it.
|
|
Move.l fNextRecord(A1),A1 ; Next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record? <SM16>
|
|
Beq.s @Prepare ; Yes? Then use it. <SM16>
|
|
@100 Move.w #nsDrvErr,D6 ; Assume no such drive. <SM16>
|
|
Bra.w @Exit ; Out of here. <SM16>
|
|
|
|
@Prepare Move.w #offLinErr,fResult(A1); Assume no disk.
|
|
Move.w fFormat(A1),D0 ; Format code.
|
|
Beq @Exitverify ; No disk? Leave.
|
|
Move.w #verErr,fResult(A1) ; Assume unformatted disk.
|
|
Cmp.w #fBlank,D0 ; Unknown format?
|
|
Beq.s @ExitVerify ; Yes? Then leave.
|
|
|
|
Lea FormatTable,A2 ; Pointer to format table.
|
|
@FindTable Cmp.w ftFormatCode(A2),D0 ; Correct format table?
|
|
Beq.s @InitRegisters ; Yes? Then prepare for verify.
|
|
Lea ftNextTable(A2),A2 ; Next table.
|
|
Bra.s @FindTable ; Check it.
|
|
|
|
@InitRegisters Moveq #1,D3 ; Head.
|
|
Moveq #0,D2 ; Starting cylinder.
|
|
Moveq #0,D5 ; Starting sector number.
|
|
Moveq #0,D6 ; Init register.
|
|
Move.b ftSectors(A2),D6 ; Number of sectors/track.
|
|
Moveq #(MFMCylndrs -1),D7 ; Tracks/zone for MFM (1 zone).
|
|
Move.l fBuffer(A1),A2 ; Buffer.
|
|
Clr.l fCount(A1) ; Invalidate cache <SM20>
|
|
Lea -ioQElSize(A6),A4 ; Parameter block.
|
|
Move.w fDriveNum(A1),ioDrvNum(A4)
|
|
Cmp.b #10,D6 ; Sector count equal 10?
|
|
Bne.s @DoCylinder ; No? Then MFM.
|
|
Moveq #12,D6 ; Starting sectors/track for GCR.
|
|
Moveq #(GCRCylndrs -1),D7 ; Tracks/zone for GCR (5 zones).
|
|
|
|
@DoCylinder Move.b #VerifyRetry,fRetrySeed(A1) ; Change the retry Count <SM30>
|
|
Move.b #VerifyRetry,fRetries(A1) ; Reset counter. <SM30>
|
|
@CylinderLoop Clr.l ioActCount(A4) ; Init count.
|
|
Move.w D6,D4 ; Sectors/track.
|
|
IF MTMode THEN
|
|
Cmp.w #f400K,fFormat(A1) ; Single sided?
|
|
Beq.s @0
|
|
Asl.w #1,D4 ; Do both sides if multi-track.
|
|
@0
|
|
ENDIF
|
|
Movem.l A2-A5/D2-D7,-(Sp) ; Save registers.
|
|
Bsr DoRead ; Read track (or cylinder).
|
|
Movem.l (Sp)+,A2-A5/D2-D7 ; Restore registers.
|
|
Tst.w fResult(A1) ; Any errors?
|
|
Bne.s @ExitVerify ; Yes? Then leave.
|
|
|
|
Add.w D4,D5 ; Next starting sector.
|
|
IF NOT MTMode THEN
|
|
Cmp.w #f400K,fFormat(A1) ; Single sided?
|
|
Beq.s @NextCylinder ; Yes? Then do next cylinder.
|
|
Subq.w #1,D3 ; Next side.
|
|
Neg.w D3 ; Still positive?
|
|
Beq.s @CylinderLoop ; Yes? Then do it.
|
|
ENDIF
|
|
|
|
@NextCylinder Addq.w #1,D2 ; Next cylinder.
|
|
Dbra D7,@CylinderLoop ; Cylinders left in this zone? Do it.
|
|
Subq.w #1,D6 ; Decrement sectors/cylinder.
|
|
Moveq #(GCRCylndrs -1),D7 ; Reset cylinders/zone.
|
|
Cmp.w #DiskCylndrs,D2 ; Done?
|
|
Blt.s @CylinderLoop ; No? Then do it.
|
|
|
|
@ExitVerify Move.l Ticks,fLastAccess(A1)
|
|
Move.b #RetryCount,fRetrySeed(A1) ; Reset the retry Count <SM30>
|
|
Move.b #RetryCount,fRetries(A1) ; Reset counter. <SM30>
|
|
Move.w fResult(A1),D6 ; Result.
|
|
Move.l #NewAge32,A0 ; Base address of NewAge.
|
|
Bsr Track0 ; Recalibrate.
|
|
@Exit Move.l (Sp)+,A4
|
|
Unlk A6
|
|
Rts ; And leave.
|
|
|
|
;******************************************************************************
|
|
; Format disk in drive.
|
|
;******************************************************************************
|
|
|
|
FormatDisk Move.l #NewAge32,A0 ; Base address of NewAge.
|
|
Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record?
|
|
Beq.s @SetUp ; Yes? Then use it.
|
|
Move.l fNextRecord(A1),A1 ; Next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record? <SM16>
|
|
Beq.s @SetUp ; Yes? Then use it. <SM16>
|
|
@100 Move.w #nsDrvErr,D6 ; Assume no such drive. <SM16>
|
|
Bra.w @Exit ; Out of here. <SM16>
|
|
|
|
@SetUp Lea FormatTable,A2 ; Start of format table.
|
|
Move.w #paramErr,D6 ; Assume parameter error.
|
|
Move.w csParam(A4),D0 ; Format index.
|
|
Blt @ExitFormat ; Offset better be greater than 0. <SM16>
|
|
Bgt.s @5 ; Check for zero. <SM20>
|
|
|
|
Cmp.w #fBlank,fFormat(A1) ; Is it a blank disk, format it. <LW5>
|
|
Beq.s @ValidFormat ; Yes? Then format. <LW5>
|
|
Move.w fFormat(A1),D0 ; Get current format. <LW5>
|
|
Cmp.w #fUnknownGCR,D0 ; Is it a GCR disk? <LW5>
|
|
Blt.s @IsGCR ; Yes? Then reformat as same type. <LW5>
|
|
Cmp.w #f720K,D0 ; Is it a MFM LD disk? <LW5>
|
|
Beq.s @IsLDMFM ; Yes? Then reformat as same type. <LW5>
|
|
Bra.s @ValidFormat ; All other format as 1 <LW5>
|
|
|
|
@IsGCR Sub.w #1,D0 ; GCR format. <LW5>
|
|
bra.s @5 ; Done. <LW5>
|
|
@IsLDMFM Move.w #4,D0 ; Low density MFM disk. <LW5>
|
|
bra.s @5 ; Done. <LW5>
|
|
@ValidFormat Move.w #1,D0 ; Give it a valid format number. <LW5>
|
|
|
|
@5 Cmp.w #4,D0 ; Check that param. is within limit. <SM16>
|
|
Bgt.w @ExitFormat ; Offset better be less than 4. <SM16>
|
|
Lea AppleDrv,A3 ; Assume 800K drive installed.
|
|
Cmp.w #fddMac800,fDriveType(A1) ; 800K drive?
|
|
Beq.s @GetTable ; Yes? Then only GCR.
|
|
Lea LowDensity,A3 ; Assume low density media inserted.
|
|
Cmp.w #mLD,fMediaType(A1) ; Low denisty disk?
|
|
Beq.s @GetTable ; Yes? Then find table.
|
|
Lea HighDensity,A3 ; Assume high density media inserted.
|
|
Cmp.w #mHD,fMediaType(A1) ; High denisty disk?
|
|
Beq.s @GetTable ; Yes? Then find table.
|
|
Lea ExtendedDensity,A3 ; Must be extended density media.
|
|
|
|
@GetTable Subq.w #1,D0 ; Make zero based.
|
|
Asl.w #1,D0 ; Make word index.
|
|
Move.w (A3,D0.w),D0 ; Get offset <SM16>
|
|
Cmp.w #-1,D0 ; Check to see if offset is okay. <SM16>
|
|
Beq.w @ExitFormat ; <SM16>
|
|
Adda.w D0,A2 ; Add offset to table. <SM16>
|
|
Move.b #PerpMode,D0 ; Set up perpendicular mode.
|
|
Bsr WriteToFDC ; Send it.
|
|
Moveq #$0,D0 ; Standard.
|
|
Cmp.w #f2880K,ftFormatCode(A2) ; Extended density?
|
|
Bne.s @0 ; No? Then horizontal mode.
|
|
Moveq #vertical,D0 ; Yes? Set vertical mode.
|
|
@0 Bsr WriteToFDC ; Send it.
|
|
|
|
Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #SetDriveGCR,D0 ; Set drive mode to GCR.
|
|
Moveq #(dr500 << DataRateOffset),D1 ; Data rate.
|
|
Cmp.w #fUnknownGCR,ftFormatCode(A2) ; GCR format?
|
|
Ble.s @1 ; Yes? Then set it.
|
|
Move.b #SetDriveMFM,D0 ; Set drive to MFM.
|
|
Moveq #(dr500 << DataRateOffset) | \ ;
|
|
(ApplePreComp << PreCompOffset), \ ;
|
|
D1 ; Date rate/pre-comp for HD.
|
|
Cmp.w #f1440K,ftFormatCode(A2) ; Double density?
|
|
Ble.s @1 ; Yes? Then set it.
|
|
Moveq #(dr1000 << DataRateOffset) | \ ;
|
|
(ApplePreComp << PreCompOffset), \ ;
|
|
D1 ; Must be extended density.
|
|
@1 Move.b D1,fdcDRR(A0) ; Set data rate/pre-comp.
|
|
Bsr WriteToFDC ; Send mode byte.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Waitx Bsr SenseInterrupt ; Check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Done?
|
|
Bne.s @Donex ; Change? Then Get out.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Waitx ; Loop
|
|
Move.w #noNybErr,fResult(A1) ; Error?
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
|
|
@Donex Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Move.w fResult(A1),D6 ; Error?
|
|
Bne @ExitFormat ; Yes? Out of here.
|
|
Bsr Track0 ; Recalibrate drive.
|
|
Move.w fResult(A1),D6 ; Get error code.
|
|
Bne @ExitFormat ; Error? then fail. <LW18>
|
|
|
|
Moveq #0,D2 ; Starting cylinder number.
|
|
Moveq #0,D7 ; Init sync group flag.
|
|
Moveq #0,D4 ; Init register.
|
|
Move.b ftSectors(A2),D4 ; Number of sectors/track.
|
|
Move.b ftGap3Length(A2),D7 ; Gap3 length or number of sync groups.
|
|
Moveq #(MFMCylndrs -1),D5 ; Tracks/zone for MFM (1 zone).
|
|
Move.b #Format,D6 ; Format command.
|
|
Lea MFMInterleave,A4 ; Interleave table.
|
|
|
|
Cmp.b #10,D4 ; Sector count equal 10?
|
|
Bne.s @CylinderLoop ; No? Then MFM.
|
|
|
|
Moveq #12,D4 ; Starting sectors/track for GCR.
|
|
Moveq #(GCRCylndrs -1),D5 ; Tracks/zone for GCR (5 zones).
|
|
Bclr #fFM,D6 ; Clear MFM flag.
|
|
|
|
Lea Zone1_2,A4 ; 2:1 interleave table.
|
|
Cmp.b #formatGCR4_1,ftFormatByte(A2) ; 4:1 interleave?
|
|
Bne.s @CylinderLoop ; No? Then we have the right table.
|
|
Lea Zone1_4,A4 ; Yes? 4:1 interleave table.
|
|
|
|
@CylinderLoop Moveq #0,D3 ; Starting head.
|
|
Bsr SeekToTrack ; Seek to cylinder.
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @FormatError ; If so report it. <LW18>
|
|
|
|
@SideLoop Move.b D6,D0 ; Format command.
|
|
Bsr WriteToFDC ; Write command.
|
|
|
|
Move.b D3,D0 ; Head select.
|
|
Lsl.b #fHd,D0 ; Move into position.
|
|
Or.b fDriveSelect(A1),D0 ; Drive select.
|
|
Bsr WriteToFDC ; Write head/drive.
|
|
|
|
Move.b ftFormatByte(A2),D0 ; 512 bytes/sector (format code).
|
|
Bsr WriteToFDC ; Write bytes/sector.
|
|
|
|
Move.b D4,D0 ; Sectors/track.
|
|
Bsr WriteToFDC ; Write sectors/track.
|
|
|
|
Move.b D7,D0 ; GCR sync groups or MFM Gap3 length.
|
|
Btst #fFM,D6 ; Is this a MFM format? <LW7>
|
|
Bne.s @WriteSync ; Yes? The just write GAP3 length. <LW7>
|
|
Btst #15,D7 ; Is this the real format? <LW7>
|
|
Bne.s @WriteSync ; Yes? Then write the correct number of Sync Group. <LW7>
|
|
Move.b #$3f,D0 ; Give it a large number of Sync Group for Bogus format. <LW7>
|
|
@WriteSync Bsr WriteToFDC ; Write Gap3 length (or sync groups).
|
|
|
|
Move.l fBuffer(A1),A3 ; Pointer to buffer.
|
|
Move.w D4,D0 ; Sectors/track.
|
|
Subq.w #1,D0 ; For looping.
|
|
Move.l A4,A5 ; Interleave table.
|
|
@BufferLoop Move.b D2,(A3)+ ; Track.
|
|
Move.b D3,(A3)+ ; Side.
|
|
|
|
Btst #fFM,D6 ; Is this a MFM format? <LW7>
|
|
Bne.s @Sector ; Yes? The give the correct Sector number. <LW7>
|
|
Btst #15,D7 ; Is this the real format? <LW7>
|
|
Bne.s @Sector ; Yes? Then give the correct Sector number. <LW7>
|
|
Move.b #$3f,(A3)+ ; Give it a bogus Sector number <LW7>
|
|
Bra.s @FormatByte ; And Continue... <LW7>
|
|
@Sector Move.b (A5)+,(A3)+ ; Sector.
|
|
@FormatByte Move.b ftFormatByte(A2),(A3)+ ; Format byte.
|
|
Dbra D0,@BufferLoop ; Loop till done.
|
|
|
|
Swap D5 ; Scratch in low word.
|
|
Move.w D4,D5 ; Sectors/track.
|
|
Asl.w #2,D5 ; Bytes to transfer.
|
|
|
|
Move.l #pscControl32,A3 ; PSC control register.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Pause channel.
|
|
@waitfrozen Move.w (A3), D0 ; <SM3>
|
|
Btst.l #pscFrozen, D0 ; <SM3>
|
|
Beq.s @waitfrozen ; Check that pause bit is frozen. <SM3>
|
|
Move.w (A3),D0 ; Control setting.
|
|
And.w #$1,D0 ; Get set.
|
|
Asl.w #4,D0 ; Calculate offset to correct set.
|
|
Move.l #pscSet032,A5 ; DMA register set.
|
|
|
|
Move.l fBuffer(A1),pscAddress(A5,D0.w) ; Set address.
|
|
Moveq #0,D1 ; Init register.
|
|
Move.w D5,D1 ; Bytes to transfer.
|
|
Move.l D1,pscCount(A5,D0.w); Set count.
|
|
Move.w #(1 << pscDir) | \ ;
|
|
(0 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Set direction to write.
|
|
Move.w #(1 << pscEnable) | \ ;
|
|
(1 << pscSense), \ ;
|
|
pscStatus(A5,D0.w) ; Enable it.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(0 << pscSense), \ ;
|
|
(A3) ; Un-pause.
|
|
Move.w #1,fResult(A1) ; Set result to “in progress”.
|
|
Move.b #$F6,D0 ; Filler byte.
|
|
Btst #fFM,D6 ; MFM format?
|
|
Bne.s @3 ; Yes? Then fill with $F6.
|
|
Moveq #0,D0 ; GCR? Fill with 0.
|
|
@3 Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Write filler byte.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #2,D1 ; DBRA/4 Millisecond.
|
|
@Wait Bsr GetResult ; Check for interrupt. <SM23>
|
|
Cmp.w #0,fResult(A1) ; Result.
|
|
Ble.s @DoneDMA ; Done? Out of here.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait ; Loop
|
|
Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bsr ResetFDC ; If timeout then something horrible happened. <SM20>
|
|
Move.w #noNybErr,fResult(A1) ; Report as timeout error. <SM20>
|
|
@DoneDMA Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Move.w #(1 << pscSWReset) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Reset channel.
|
|
Move.w fResult(A1),D0 ; Check error. <LW4>
|
|
Bne @FormatError ; <LW4>
|
|
Swap D5 ; Tracks/zone in low word.
|
|
Btst #fFM,D6 ; Formatting MFM?
|
|
Bne.s @NextSide ; Yes? Then do next side.
|
|
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond. <LW7>
|
|
@StupidWait Dbra D1,@StupidWait ; <LW7>
|
|
Btst #15,D7 ; Was this pass real or just erasing the disk? <LW7>
|
|
Bne.s @ClearFlag ; Valid Format? Then clear flag and continue. <LW7>
|
|
Bset #15,D7 ; Set flag. <LW7>
|
|
Beq @SideLoop ; And Do the valid format. <LW7>
|
|
|
|
@ClearFlag Bclr #15,D7 ;
|
|
@NextSide Move.b ftSides(A2),D0 ; Flags/Sides.
|
|
And.b #$F,D0 ; Sides.
|
|
Cmp.b #1,D0 ; Single sided?
|
|
Beq.s @NextCylinder ; Yes? Then do next cylinder.
|
|
|
|
Addq.w #1,D3 ; Next side.
|
|
Cmp.w #1,D3 ; Side 1?
|
|
Beq @SideLoop ; Yes? Then do it.
|
|
|
|
@NextCylinder Addq.w #1,D2 ; Next cylinder.
|
|
Dbra D5,@CylinderLoop ; Cylinders left in this zone? Do it.
|
|
Adda.w #itNextZone,A4 ; Next interleave zone.
|
|
Subq.w #1,D4 ; Decrement sectors/cylinder.
|
|
Moveq #(GCRCylndrs -1),D5 ; Reset cylinders/zone.
|
|
Cmp.w #DiskCylndrs,D2 ; Done?
|
|
Blt @CylinderLoop ; No? Then do it.
|
|
|
|
Bsr Track0 ; Recalibrate.
|
|
Clr.b fCylinder(A1) ; Set track to 0.
|
|
Moveq #noErr,D6 ; Indicate no error.
|
|
Move.l fDriveQueue(A1),A3 ; Pointer to drive queue.
|
|
Move.w ftFormatCode(A2),fFormat(A1) ; Set media format.
|
|
Move.w ftBlocks(A2),dQDrvSz(A3) ; Number of blocks.
|
|
Move.b #$FF,-1(A3) ; Non single-sided. <LW14>
|
|
Move.b #1,-3(A3) ; Set “Disk in place” flag.
|
|
Cmp.w #f400K,fFormat(A1) ; 400K GCR?
|
|
Bne.s @ExitFormat ; No? Then double-sided.
|
|
Clr.b -1(A3) ; Single-sided.
|
|
|
|
@ExitFormat Move.l Ticks,fLastAccess(A1)
|
|
Clr.w fCount(A1) ; Void buffer.
|
|
@Exit Rts ; And leave.
|
|
|
|
@FormatError Move.w fResult(A1),D6 ; <LW4>
|
|
Bra.s @ExitFormat ; <LW4>
|
|
|
|
Zone1_2 Dc.b $0,$6,$1,$7,$2,$8,$3,$9,$4,$A,$5,$B
|
|
Zone2_2 Dc.b $0,$6,$1,$7,$2,$8,$3,$9,$4,$A,$5,$F
|
|
Zone3_2 Dc.b $0,$5,$1,$6,$2,$7,$3,$8,$4,$9,$F,$F
|
|
Zone4_2 Dc.b $0,$5,$1,$6,$2,$7,$3,$8,$4,$F,$F,$F
|
|
Zone5_2 Dc.b $0,$4,$1,$5,$2,$6,$3,$7,$F,$F,$F,$F
|
|
|
|
Zone1_4 Dc.b $0,$3,$6,$9,$1,$4,$7,$A,$2,$5,$8,$B
|
|
Zone2_4 Dc.b $0,$3,$6,$9,$1,$4,$7,$A,$2,$5,$8,$F
|
|
Zone3_4 Dc.b $0,$5,$3,$8,$1,$6,$4,$9,$2,$7,$F,$F
|
|
Zone4_4 Dc.b $0,$7,$5,$3,$1,$8,$6,$4,$2,$F,$F,$F
|
|
Zone5_4 Dc.b $0,$2,$4,$6,$1,$3,$5,$7,$F,$F,$F,$F
|
|
|
|
MFMInterleave Dc.b $01,$02,$03,$04,$05,$06,$07,$08,$09
|
|
Dc.b $0A,$0B,$0C,$0D,$0E,$0F,$10,$11,$12
|
|
Dc.b $13,$14,$15,$16,$17,$18,$19,$1A,$1B
|
|
Dc.b $1C,$1D,$1E,$1F,$20,$21,$22,$23,$24
|
|
|
|
;******************************************************************************
|
|
; Eject disk in drive.
|
|
;******************************************************************************
|
|
|
|
EjectDisk Move.l #NewAge32,A0 ; Base address of NewAge.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Cmp.w fDriveNum(A1),D0 ; Match?
|
|
Beq.s @CheckDrive ; Yes? Then check for drive.
|
|
Move.l fNextRecord(A1),A1 ; Point to next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record? <SM16>
|
|
Beq.s @CheckDrive ; Yes? Then use it. <SM16>
|
|
@100 Move.w #nsDrvErr,D6 ; Assume no such drive. <SM16>
|
|
Bra @ExitEject ; Out of here.
|
|
|
|
@CheckDrive Move.l A3,fDCE(A1) ; Save DCE.
|
|
Move.l fDriveQueue(A1),A3 ; Pointer to drive queue.
|
|
; Add command to enable FDD <SM12>
|
|
Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #SetEnableOn,D0 ; Set enable command.
|
|
Bsr WriteToFDC ; Send it.
|
|
Move.w fDriveSelect(A1),D0 ; Drive number.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Enable the drive.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Waitx Bsr SenseInterrupt ; Check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Done?
|
|
Bne.s @changed ; Change? Then Get out.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Waitx ; Loop
|
|
Move.w #noNybErr,fResult(A1) ; Error?
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
|
|
; End of addtions <SM12>
|
|
@changed Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Move.w fResult(A1),D6 ; Error?
|
|
Bne.s @ExitEject ; Yes? Out of here.
|
|
Cmp.b #1,-3(A3) ; First access?
|
|
Bne.s @Seek ; No? Then don't recalibrate.
|
|
Bsr Track0 ; Recalibrate.
|
|
@Seek Moveq #40,D2 ; Track 40.
|
|
Bsr SeekToTrack ; Park head.
|
|
Moveq #1,D6 ; Set busy.
|
|
|
|
Lea MotorOff,A2 ; Motor off routine.
|
|
Move.l A2,fCompletion(A1) ; Stuff routine pointer.
|
|
Move.b #EjectFloppy,D0 ; Eject command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Bsr WriteToFDC ; Eject it.
|
|
|
|
Clr.l -4(A3) ; Clear all flags.
|
|
Clr.w dQDrvSz(A3) ; Clear size.
|
|
Clr.w fFormat(A1) ; Clear format.
|
|
Clr.w fMediaType(A1) ; Clear media type.
|
|
Move.b #-1,fCylinder(A1) ; Flag unknown track.
|
|
Clr.w fStart(A1) ; Set starting sector in buffer to 0.
|
|
Clr.w fCount(A1) ; Set number of sectors in buffer to 0.
|
|
|
|
@ExitEject Rts ; And leave.
|
|
|
|
MotorOff Tst.b fMotor(A1) ; Motor on?
|
|
Beq.s @Exit ; No? Then leave.
|
|
Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #SetMotorOff,D0 ; Motor off command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Bsr WriteToFDC ; Send drive ID.
|
|
Clr.b fMotor(A1) ; Clear flag.
|
|
|
|
@Exit Move.b #SetEnableOff,D0 ; Disable drive.
|
|
Bsr WriteToFDC ; Send it.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Bsr WriteToFDC ; Disable drive.
|
|
|
|
Moveq #noErr,D0 ; Set done.
|
|
Move.l fDCE(A1),A1 ; Pointer to DCE.
|
|
Move.l dCtlQHead(A1),A0 ; IO queue.
|
|
Move.w ioTrap(A0),D1 ; Trap called.
|
|
Btst #asyncTrpBit,D1 ; Called asynchronously?
|
|
Beq.s @0 ; No? Then return through RTS.
|
|
Move.l JIODone,-(Sp) ; Yes? Return through IODone.
|
|
@0 Rts
|
|
|
|
|
|
|
|
;******************************************************************************
|
|
; Set tag buffer (not supported).
|
|
;******************************************************************************
|
|
|
|
TagBuffer Rts ; Just return.
|
|
|
|
;******************************************************************************
|
|
; Set track cache (not supported).
|
|
;******************************************************************************
|
|
|
|
TrackCache Rts ; Just return.
|
|
|
|
;******************************************************************************
|
|
; Return drive icon and “where” info.
|
|
;******************************************************************************
|
|
; locate equates for offset in icon table…
|
|
itDrive EQU 1
|
|
itIcon EQU 2
|
|
itNextIcon EQU 6
|
|
|
|
GetDriveIcon Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Cmp.w fDriveNum(A1),D0 ; Match?
|
|
Beq.s @GetIcon ; Yes? Then check for drive.
|
|
Move.l fNextRecord(A1),A1 ; Point to next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record? <SM16>
|
|
Beq.s @GetIcon ; Yes? Then use it. <SM16>
|
|
@100 Move.w #nsDrvErr,D6 ; Assume “No such drive” error.
|
|
Bra.s @ExitGetIcon ; No record? Then leave.
|
|
|
|
@GetIcon Lea IconTable,A2 ; Start of icon table.
|
|
Move.l A2,A0 ; Save pointer.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select (0 or 1).
|
|
Move.b $CB3,D1 ; (BoxFlag) Box we're running on.
|
|
|
|
Move.w #controlErr,D6 ; Assume we can't find icon.
|
|
@IconLoop Tst.b (A2) ; End of table?
|
|
Beq.s @ExitGetIcon ; Yes? Then leave.
|
|
Cmp.b (A2),D1 ; Boxflag match?
|
|
Bne.s @Next ; No? Then check next entry.
|
|
Cmp.b itDrive(A2),D0 ; Right drive?
|
|
Bne.s @Next ; No? Then check next entry.
|
|
|
|
Adda.l itIcon(A2),A0 ; Pointer to icon.
|
|
Move.l A0,csParam(A4) ; Return pointer.
|
|
Moveq #noErr,D6 ; Indicate “no error”.
|
|
Bra.s @ExitGetIcon ; And leave.
|
|
@Next Adda.w #itNextIcon,A2 ; Next table.
|
|
Bra.s @IconLoop ; And check it.
|
|
|
|
@ExitGetIcon Rts ; Leave.
|
|
|
|
IconTable Dc.b 37,0 ; Cyclone33
|
|
Dc.l FrigidaireInternal - IconTable
|
|
Dc.b 72,0 ; Cyclone40 <LW9>
|
|
Dc.l FrigidaireInternal - IconTable
|
|
Dc.b 54,0 ; Tempest25 <LW9>
|
|
Dc.l QFCInternal - IconTable
|
|
Dc.b 73,0 ; Tempest33 <LW9>
|
|
Dc.l QFCInternal - IconTable
|
|
|
|
Dc.b 37,1 ; Non existing cyclone33 external
|
|
Dc.l FrigidaireExternal - IconTable
|
|
Dc.b 72,1 ; Non existing cyclone40 external <LW9>
|
|
Dc.l FrigidaireExternal - IconTable
|
|
Dc.b 54,1 ; Non existing Tempest25 external <LW9>
|
|
Dc.l QFCExternal - IconTable
|
|
Dc.b 73,1 ; Non existing Tempest33 external <LW9>
|
|
Dc.l QFCExternal - IconTable
|
|
Dc.b 0,0 ; End of table
|
|
|
|
;******************************************************************************
|
|
; Return media icon and “where” info.
|
|
;******************************************************************************
|
|
|
|
GetMediaIcon Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Cmp.w fDriveNum(A1),D0 ; Match?
|
|
Beq.s @GetIcon ; Yes? Then check for drive.
|
|
Move.l fNextRecord(A1),A1 ; Point to next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Match? <SM16>
|
|
Beq.s @GetIcon ; Yes? Then check for drive. <SM16>
|
|
@100 Move.w #nsDrvErr,D6 ; Assume “No such drive” error.
|
|
Bra.s @Exit ; No record? Then leave.
|
|
|
|
@GetIcon Pea MediaIcon ; Pointer to icon data.
|
|
Move.l (Sp)+,csParam(A4)
|
|
Moveq #noErr,D6 ; Indicate no error.
|
|
@Exit Rts ; And leave.
|
|
|
|
;******************************************************************************
|
|
; Return drive info.
|
|
;******************************************************************************
|
|
|
|
DriveInfo Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Cmp.w fDriveNum(A1),D0 ; Match?
|
|
Beq.s @BuildRecord ; Yes? Then check for drive.
|
|
Move.l fNextRecord(A1),A1 ; Point to next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Match? <SM16>
|
|
Beq.s @BuildRecord ; Yes? Then check for drive. <SM16>
|
|
@100 Move.w #nsDrvErr,D6 ; Assume “No such drive” error.
|
|
Bra.s @ExitDriveInfo ; No record? Then leave.
|
|
|
|
@BuildRecord Moveq #noErr,D6 ; No error.
|
|
Moveq #0,D2 ; Init record.
|
|
Move.w fDriveType(A1),D2 ; Drive type.
|
|
Tst.b fDriveSelect(A1) ; First drive found?
|
|
Beq.s @ExitDriveInfo ; Yes? Then “primary” drive.
|
|
Bset #11,D2 ; “Secondary” drive.
|
|
Bset #8,D2 ; External drive.
|
|
|
|
@ExitDriveInfo Move.l D2,csParam(A4) ; Put it in the right place
|
|
Rts ; And leave.
|
|
|
|
;******************************************************************************
|
|
; Check idle motor.
|
|
;******************************************************************************
|
|
|
|
CheckMotor Move.l #NewAge32,A0 ; Base address of NewAge.
|
|
Move.l dCtlStorage(A3),D0 ; Pointer to floppy info.
|
|
@Loop Beq.s @ExitMotor ; No record? Then leave.
|
|
Move.l D0,A1 ; Pointer to record.
|
|
Tst.b fMotor(A1) ; Motor on?
|
|
Beq.s @Next ; No? Then check next drive.
|
|
Move.l fLastAccess(A1),D0 ; Last time accessed.
|
|
Add.l #WaitTime,D0 ; Add timeout.
|
|
Cmp.l Ticks,D0 ; Time up?
|
|
Bgt.s @Next ; No? Then let drive spin.
|
|
|
|
Move.b #SetMotorOff,D0 ; Motor off command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Bsr WriteToFDC ; Send drive ID.
|
|
Clr.b fMotor(A1) ; Clear flag.
|
|
|
|
@Next Move.l fNextRecord(A1),D0 ; Next record.
|
|
Bra.s @Loop ; Loop till we're done.
|
|
|
|
@ExitMotor Moveq #noErr,D6 ; Indicate no error.
|
|
Rts ; And leave.
|
|
|
|
;******************************************************************************
|
|
; Diagnostic raw track dump.
|
|
; For technical reasons (since clock and data streams have to be
|
|
; seperated in real time) RawDump is performed only in non-DMA mode.
|
|
;******************************************************************************
|
|
|
|
TrackDump Move.l #NewAge32,A0 ; Base address of NewAge.
|
|
Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record?
|
|
Beq.s @SetUp ; Yes? Then use it.
|
|
Move.l fNextRecord(A1),A1 ; Next record.
|
|
Beq.s @99 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Drive numbers match? <SM16>
|
|
Beq.s @SetUp ; Yes? Then use it. <SM16>
|
|
@99 Move.w #nsDrvErr,D6 ; Let's make sure drive exists. <SM16>
|
|
Bra @ExitTrackDump ; No? Then let's bail. <SM16>
|
|
|
|
@SetUp Move.w #offLinErr,D6 ; Check to see if we have a media.
|
|
Tst.w fMediaType(A1) ; Disk in place?
|
|
Beq @ExitTrackDump ; No? What do they expect us to do?
|
|
|
|
Move.w #noErr,D6 ; Assume no error.
|
|
Clr.l csParam+12(A4) ; Assume we can't read anything.
|
|
Move.l csParam+8(A4),D7 ; Count 0?
|
|
Beq @ExitTrackDump ; Yes? Then leave without doing it.
|
|
Tst.l csParam+0(A4) ; Clock buffer nil?
|
|
Sne D5 ; Set flag.
|
|
Lsl.w #1,D5 ; Move flag to second byte.
|
|
Tst.l csParam+4(A4) ; Data buffer nil?
|
|
Sne D5 ; Set flag.
|
|
Lsr.w #7,D5 ; Bit 0 - data flag; bit 1 - clock flag.
|
|
Tst.b D5 ; Any buffers?
|
|
Beq @ExitTrackDump ; No? Then can't do anything.
|
|
|
|
@RangeCheck Move.w #paramErr,D6 ; Assume parameter error.
|
|
Move.w csParam+18(A4),D2 ; Track number.
|
|
Bmi @ExitTrackDump ; Less than 0? NOT!
|
|
Cmp.w #(DiskCylndrs -1),D2 ; Greater than 79?
|
|
Bgt @ExitTrackDump ; NOT!
|
|
|
|
Move.b csParam+20(A4),D3 ; Head.
|
|
Bmi @ExitTrackDump ; Less than 0? NOT!
|
|
Cmp.b #1,D3 ; Greater than 1?
|
|
Bgt @ExitTrackDump ; NOT!
|
|
|
|
Move.w csParam+16(A4),D4 ; Search mode.
|
|
Bmi @ExitTrackDump ; Less than 0? (This is getting boring!)
|
|
Cmp.w #3,D4 ; Greater …
|
|
Bgt @ExitTrackDump ; Yea, yea.
|
|
Blt.s @CheckMedia ; Type 3 only works for MFM.
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; So let's make sure media is MFM.
|
|
Ble @ExitTrackDump ; GCR? No way man.
|
|
|
|
@CheckMedia Move.b #PerpMode,D0 ; Set up perpendicular mode.
|
|
Bsr WriteToFDC ; Send it.
|
|
Moveq #$0,D0 ; Standard.
|
|
Cmp.w #f2880K,fFormat(A1) ; Extended density?
|
|
Bne.s @0 ; No? Then horizontal mode.
|
|
Moveq #vertical,D0 ; Yes? Set vertical mode.
|
|
@0 Bsr WriteToFDC ; Send it.
|
|
|
|
Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #SetDriveGCR,D0 ; Set drive mode to GCR.
|
|
Moveq #(dr500 << DataRateOffset),D1 ; Data rate.
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; GCR format?
|
|
Ble.s @1 ; Yes? Then set it.
|
|
Move.b #SetDriveMFM,D0 ; Set drive to MFM.
|
|
Moveq #(dr500 << DataRateOffset) | \ ;
|
|
(ApplePreComp << PreCompOffset), \ ;
|
|
D1 ; Date rate/pre-comp for HD.
|
|
Cmp.w #f1440K,fFormat(A1) ; Double density?
|
|
Ble.s @1 ; Yes? Then set it.
|
|
Moveq #(dr1000 << DataRateOffset) | \ ;
|
|
(ApplePreComp << PreCompOffset), \ ;
|
|
D1 ; Must be extended density.
|
|
@1 Move.b D1,fdcDRR(A0) ; Set data rate/pre-comp.
|
|
Bsr WriteToFDC ; Send mode byte.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Waitx Bsr SenseInterrupt ; Check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Done?
|
|
Bne.s @Donex ; Change? Then Get out.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Waitx ; Loop
|
|
Move.w #noNybErr,fResult(A1) ; Error?
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
|
|
@Donex Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Move.w fResult(A1),D6 ; Error?
|
|
Bne @ExitTrackDump ; Yes? Out of here.
|
|
Move.b #Specify,D0 ; Specify command.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
Moveq #0,D0 ; Step rate/head unload.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
Moveq #1,D0 ; Head load/Non-DMA.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
|
|
Bsr SeekToTrack ; Seek to track.
|
|
|
|
Move.l csParam(A4),A2 ; Clock buffer.
|
|
Move.l csParam+4(A4),A3 ; Data buffer.
|
|
|
|
Move.w #RawDump,D0 ; Command.
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; MFM?
|
|
Bgt.s @SendCommand ; Yes? Then send command.
|
|
Bclr #fFM,D0 ; Set it to GCR.
|
|
Bclr #1,D5 ; Don't save clocks.
|
|
@SendCommand Bsr WriteToFDC ; Send it.
|
|
|
|
Move.b csParam+21(A4),D2 ; Starting sector.
|
|
Moveq #0,D0 ; Assume dump from index.
|
|
Cmp.w #3,D4 ; Search from index?
|
|
Beq.s @SetHead ; Yes? Then set head bit.
|
|
|
|
Moveq #$10,D0 ; Assume dump from data mark.
|
|
Cmp.w #2,D4 ; Dump from data mark?
|
|
Beq.s @SetHead ; Yes? Then set head bit.
|
|
|
|
Moveq #$8,D0 ; Assume dump from address mark.
|
|
Cmp.w #1,D4 ; Dump from address?
|
|
Beq.s @SetHead ; Yes? Then set head bit.
|
|
|
|
Moveq #1,D2 ; Must be dump immediate. Since
|
|
; NewAge doesn't support this, we'll
|
|
; dump from Address 1. Hope this is OK.
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; MFM? <LW26>
|
|
Bgt.s @SetHead ; Yes? Then send command. <LW26>
|
|
Moveq #0,D2 ; For GCR disk start at Address 0. <LW26>
|
|
|
|
@SetHead Move.b D3,D1 ; Head
|
|
Rol.b #2,D1 ; Position it.
|
|
Or.b D1,D0 ; Set it.
|
|
Move.w fDriveSelect(A1),D1 ; Drive select.
|
|
Or.b D1,D0 ; Set it.
|
|
Bsr WriteToFDC ; Send mode, head and drive.
|
|
|
|
Move.w csParam+18(A4),D0 ; Starting cylinder.
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
Move.b D3,D0 ; Head.
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
Move.b D2,D0 ; Starting sector.
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
Move.b fCode(A1),D0 ; Format byte (512 bytes/sector). <SM7>
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
Move SR,D0 ; Get and save the SR.
|
|
Btst #13,D0 ; Are we in Supervisor mode?
|
|
Bne.s @3 ; Yes? Then don't call _EnterSupervisorMode.
|
|
_EnterSupervisorMode ; Enter supervisor mode.
|
|
@3 Ori.w #$0700,SR ; Disable interrupts till done with I/O.
|
|
Move.w D0,-(Sp) ; Save status register.
|
|
|
|
Move.w D7,D0 ; Bytes to dump.
|
|
Lsr.w #8,D0 ; MSB.
|
|
Bsr WriteToFDC ; Send it.
|
|
Move.w D7,D0 ; Bytes to dump.
|
|
Bsr WriteToFDC ; Send LSB.
|
|
|
|
Lea Clock0,A5 ; Clock 0 table.
|
|
Clr.l D2 ; Bytes transferred.
|
|
Clr.l D7 ; Previous byte.
|
|
Moveq #31,D4 ; Clock register. <SM4>
|
|
|
|
Move.w #1,D6 ; Address increment if saving data.
|
|
Btst #0,D5 ; Saving data?
|
|
Bne.s @HaveDataBuf ; Yes? All set.
|
|
Move.w #0,D6 ; Don't increment address if not saving data.
|
|
Clr.w fCount(A1) ; Clear track cache.
|
|
Move.l fBuffer(A1),A3 ; Steal storage space.
|
|
|
|
@HaveDataBuf Btst #1,D5 ; Saving clocks?
|
|
Bne.s @HaveClockBuf ; Yes? Go set Address increment.
|
|
Move.w #0,D5 ; Don't increment address if not saving data.
|
|
Clr.w fCount(A1) ; Clear track cache.
|
|
Move.l fBuffer(A1),A2 ; Steal storage space.
|
|
Bra.s @Loop ; Go
|
|
|
|
@HaveClockBuf Move.w #4,D5 ; Address increment if clocks are save.
|
|
|
|
@Loop Asl.w #8,D7 ; get LSB bit of previous byte in position <SM4>
|
|
Move.l #$FFFF,D0 ; Timeout.
|
|
@ReadLoop Move.b fdcMSR(A0),D1 ; Main status.
|
|
Bpl.s @100 ; Not ready? Check timeout.
|
|
Btst #fExecute,D1 ; Still executing?
|
|
Beq.s @Status ; No? Then bail out.
|
|
Move.b fdcDATA(A0),D7 ; Yes? Read byte.
|
|
|
|
Move.b D7,(A3) ; Save byte.
|
|
Adda.w D6,A3 ; Increment Address.
|
|
Addq.l #1,D2 ; Bump counter.
|
|
Asl.l #1,D3 ; Shift clock bits. <SM4>
|
|
Andi.w #$1FF,D7 ; Get index of clock table ready <SM4>
|
|
|
|
Move.l #$FFFF,D0 ; Timeout.
|
|
@ReadLoop2 Move.b fdcMSR(A0),D1 ; Main status.
|
|
Bpl.s @200 ; Not ready? Check timeout.
|
|
Btst #fExecute,D1 ; Still executing?
|
|
Beq.s @Status ; No? Then bail out.
|
|
Move.b fdcDATA(A0),D0 ; Yes? Read byte.
|
|
|
|
Cmp.b (A5,D7.w),D0 ; Match? <SM4>
|
|
Beq.s @Next ; Yes? Then leave as 0.
|
|
Bset #0,D3 ; Set bad clock bit.
|
|
@Next Dbra D4,@Loop ; Next byte.
|
|
Moveq #31,D4 ; Clock register. <SM4>
|
|
Move.l D3,(A2) ; Save clock bits. <SM4>
|
|
Adda.w D5,A2 ; Increment Address.
|
|
Bra.s @Loop ; Next 32 bytes. <SM4>
|
|
|
|
@100 Subq.l #1,D0 ; Decrement counter.
|
|
Bne.s @ReadLoop ; Loop.
|
|
Bra.s @Status
|
|
|
|
@200 Subq.l #1,D0 ; Decrement counter.
|
|
Bne.s @ReadLoop2 ; Loop.
|
|
|
|
@Status Bsr ReadFromFDC
|
|
Move.b D0,fStatus0(A1)
|
|
Bsr ReadFromFDC
|
|
Move.b D0,fStatus1(A1)
|
|
Bsr ReadFromFDC
|
|
Move.b D0,fStatus2(A1)
|
|
Bsr ReadFromFDC
|
|
Move.b D0,fCylinder(A1)
|
|
Bsr ReadFromFDC
|
|
Move.b D0,fHead(A1)
|
|
Bsr ReadFromFDC
|
|
Move.b D0,fSector(A1)
|
|
Bsr ReadFromFDC
|
|
Move.b D0,fData(A1)
|
|
|
|
Move.w (Sp)+,SR ; Restore status register.
|
|
|
|
|
|
Rol.l D4,D3 ; Position clocks. <SM4>
|
|
@ClockEdge Cmp.w #31,D4 ; What is the count?
|
|
Bge.s @6 ; Full Count? Nothing to save.
|
|
Move.b D3,(A2)+ ; Save last clock bits.
|
|
Add.w #8, D4 ;
|
|
Bra.s @ClockEdge
|
|
|
|
|
|
@6 Bsr CheckError ; Get error code.
|
|
Move.w D1,D6 ; Error code.
|
|
Bne.s @DoneTrackDump ;
|
|
|
|
; Add code to get rid of zeros in the selfsync byte in GCR Mode <SM14>
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; GCR?
|
|
Bgt.s @DoneTrackDump ; No? Great! All set.
|
|
|
|
Move.l csParam+4(A4),A3 ; Data buffer.
|
|
Beq.s @DoneTrackDump ; Nil? Great! Don't have to do anything.
|
|
Move.l A3,A2 ; Data buffer. <SM16>
|
|
|
|
Moveq #0,D0 ; Clear one register.
|
|
Moveq #0,D5 ; Clear another register.
|
|
Moveq #0,D1 ; Clear count register.
|
|
Move.l D2,D3 ; Need the number of bytes for count.
|
|
@9 Move.b (A3)+,D0 ; Get a byte of data.
|
|
Swap D0 ; Clear space for more bytes <SM20>
|
|
Move.w (A3),D0 ; Need three bytes. <SM20>
|
|
Lsl.l D1,D0 ; Shift out bytes that have been used
|
|
Btst #23,D0 ; Test MSB.
|
|
Bne.s @10 ; MSB = 1? All set.
|
|
|
|
And.l #$FFFFFF,D0 ; Make sure the upper bits are clear.
|
|
Cmp.l #$010000,D0 ; Is it a byte of zeroes.
|
|
Bge.s @11 ; Yes? go handle a byte of zeros.
|
|
Sub.l #1,D2 ; Decrement count. <SM20>
|
|
Sub.l #1,D3 ; Decrement count. <SM20>
|
|
Bne.s @9 ; Done yet? <SM20>
|
|
Bra.s @DoneTrackDump ; Out of here. <SM20>
|
|
|
|
@11 Add.w #1,D1 ; MSB = 0? Add to counter.
|
|
Lsl.l #1,D0 ; Shift 1 bit.
|
|
Btst #23,D0 ; Test MSB.
|
|
Beq.s @11 ; Still 0? Do it again.
|
|
|
|
@13 Cmp.w #8,D1 ; Have we shift 8 bits yet?
|
|
Blt.s @10 ; No? No need to do anything yet.
|
|
Sub.w #8,D1 ; Swap 8 in counter.
|
|
Adda.l #1,A3 ; For a one in the address.
|
|
Sub.l #1,D2 ; Update the real counter.
|
|
Bra.s @13
|
|
|
|
@10 Swap D0 ; Byte is now good.
|
|
Move.b D0,(A2)+ ; Restore good data.
|
|
Sub.l #1,D3 ; Decrement count.
|
|
Bgt.s @9 ;
|
|
|
|
; End of addition <SM14>
|
|
@DoneTrackDump
|
|
Move.l D2,csParam+12(A4) ; Save count.
|
|
|
|
Move.b #Specify,D0 ; Specify command.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
Moveq #0,D0 ; Step rate/head unload.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
Moveq #0,D0 ; Head load/DMA.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
|
|
@ExitTrackDump Rts ; Return.
|
|
|
|
Clock0 Dc.b $FF,$7F,$3F,$3F,$9F,$1F,$1F,$1F,$CF,$4F,$0F,$0F,$8F,$0F,$0F,$0F
|
|
Dc.b $E7,$67,$27,$27,$87,$07,$07,$07,$C7,$47,$07,$07,$87,$07,$07,$07
|
|
Dc.b $F3,$73,$33,$33,$93,$13,$13,$13,$C3,$43,$03,$03,$83,$03,$03,$03
|
|
Dc.b $E3,$63,$23,$23,$83,$03,$03,$03,$C3,$43,$03,$03,$83,$03,$03,$03
|
|
Dc.b $F9,$79,$39,$39,$99,$19,$19,$19,$C9,$49,$09,$09,$89,$09,$09,$09
|
|
Dc.b $E1,$61,$21,$21,$81,$01,$01,$01,$C1,$41,$01,$01,$81,$01,$01,$01
|
|
Dc.b $F1,$71,$31,$31,$91,$11,$11,$11,$C1,$41,$01,$01,$81,$01,$01,$01
|
|
Dc.b $E1,$61,$21,$21,$81,$01,$01,$01,$C1,$41,$01,$01,$81,$01,$01,$01
|
|
Dc.b $FC,$7C,$3C,$3C,$9C,$1C,$1C,$1C,$CC,$4C,$0C,$0C,$8C,$0C,$0C,$0C
|
|
Dc.b $E4,$64,$24,$24,$84,$04,$04,$04,$C4,$44,$04,$04,$84,$04,$04,$04
|
|
Dc.b $F0,$70,$30,$30,$90,$10,$10,$10,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $E0,$60,$20,$20,$80,$00,$00,$00,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $F8,$78,$38,$38,$98,$18,$18,$18,$C8,$48,$08,$08,$88,$08,$08,$08
|
|
Dc.b $E0,$60,$20,$20,$80,$00,$00,$00,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $F0,$70,$30,$30,$90,$10,$10,$10,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $E0,$60,$20,$20,$80,$00,$00,$00,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
|
|
Clock1 Dc.b $FE,$7E,$3E,$3E,$9E,$1E,$1E,$1E,$CE,$4E,$0E,$0E,$8E,$0E,$0E,$0E
|
|
Dc.b $E6,$66,$26,$26,$86,$06,$06,$06,$C6,$46,$06,$06,$86,$06,$06,$06
|
|
Dc.b $F2,$72,$32,$32,$92,$12,$12,$12,$C2,$42,$02,$02,$82,$02,$02,$02
|
|
Dc.b $E2,$62,$22,$22,$82,$02,$02,$02,$C2,$42,$02,$02,$82,$02,$02,$02
|
|
Dc.b $F8,$78,$38,$38,$98,$18,$18,$18,$C8,$48,$08,$08,$88,$08,$08,$08
|
|
Dc.b $E0,$60,$20,$20,$80,$00,$00,$00,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $F0,$70,$30,$30,$90,$10,$10,$10,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $E0,$60,$20,$20,$80,$00,$00,$00,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $FC,$7C,$3C,$3C,$9C,$1C,$1C,$1C,$CC,$4C,$0C,$0C,$8C,$0C,$0C,$0C
|
|
Dc.b $E4,$64,$24,$24,$84,$04,$04,$04,$C4,$44,$04,$04,$84,$04,$04,$04
|
|
Dc.b $F0,$70,$30,$30,$90,$10,$10,$10,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $E0,$60,$20,$20,$80,$00,$00,$00,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $F8,$78,$38,$38,$98,$18,$18,$18,$C8,$48,$08,$08,$88,$08,$08,$08
|
|
Dc.b $E0,$60,$20,$20,$80,$00,$00,$00,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $F0,$70,$30,$30,$90,$10,$10,$10,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
Dc.b $E0,$60,$20,$20,$80,$00,$00,$00,$C0,$40,$00,$00,$80,$00,$00,$00
|
|
|
|
;******************************************************************************
|
|
; DiskCopy (Format and write in one pass).
|
|
;******************************************************************************
|
|
; local equates…
|
|
|
|
DiskCopy Link A6,#-ioQElSize ; Local storage. <LW10>
|
|
Move.l #NewAge32,A0 ; Base address of NewAge.
|
|
Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record?
|
|
Beq.s @SetUp ; Yes? Then use it.
|
|
Move.l fNextRecord(A1),A1 ; Next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record? <SM16>
|
|
Beq.s @SetUp ; Yes? Then use it.
|
|
@100 Move.w #nsDrvErr,D6 ; Assume no such drive.
|
|
Bra.w @InvalidDriveExit ; Out of here
|
|
|
|
@SetUp Move.w #paramErr,D6 ; Assume parameter error.
|
|
Move.l csParam+2(A4),A5 ; Pointer to data.
|
|
Move.b csParam+10(A4),D3 ; Format byte.
|
|
Move.b csParam+11(A4),D7 ; Do Verify.
|
|
Swap D7 ; Need lower half of D7
|
|
Move.w csParam(A4),D0 ; Format code.
|
|
Cmp.w #3,D0 ; 720K MFM format? <SM4>
|
|
Beq.s @CheckLDMFM ; Check for LD media + correct drive. <SM4>
|
|
Cmp.w #2,D0 ; GCR format?
|
|
Beq.s @CheckGCR ; Check for LD media.
|
|
Cmp.w #1,D0 ; Better be 1.
|
|
Bne @ExitFormatCopy ; No? Then we're out-a-here.
|
|
|
|
Lea FormatTable,A2 ; Assume LD media (then 400K GCR). <SM4>
|
|
Cmp.w #mLD,fMediaType(A1) ; LD?
|
|
Beq.s @InitRegisters ; Yes? Then prepare to format.
|
|
|
|
Lea SuperDrvTable,A2 ; Assume HD media (then 1.4 MFM).
|
|
Cmp.w #mHD,fMediaType(A1) ; HD?
|
|
Beq.s @InitRegisters ; Yes? Then prepare to format.
|
|
|
|
Lea TyphoonTable,A2 ; Must be ED media (then 2.8 MFM).
|
|
Bra.s @InitRegisters ; Prepare to format.
|
|
|
|
@CheckLDMFM Cmp.w #fddMac800,fDriveType(A1) ; 800K drive installed?
|
|
Beq @ExitFormatCopy ; Yes? Then can't support MFM.
|
|
Cmp.w #mLD,fMediaType(A1) ; Low density disk inserted?
|
|
Bne @ExitFormatCopy ; No? Then param error.
|
|
Lea SuperDrvTable+12,A2 ; Yes? Then point to format description.
|
|
Bra.s @InitRegisters ; Prepare for format. <SM4>
|
|
|
|
@CheckGCR Cmp.w #mLD,fMediaType(A1) ; Low density disk inserted? <SM4>
|
|
Bne @ExitFormatCopy ; No? Then param error. <SM4>
|
|
Lea FormatTable+12,A2 ; Yes? Then point to format description.
|
|
|
|
@InitRegisters Move.b #PerpMode,D0 ; Set up perpendicular mode.
|
|
Bsr WriteToFDC ; Send it.
|
|
Moveq #$0,D0 ; Standard.
|
|
Cmp.w #f2880K,ftFormatCode(A2) ; Extended density?
|
|
Bne.s @0 ; No? Then horizontal mode.
|
|
Moveq #vertical,D0 ; Yes? Set vertical mode.
|
|
@0 Bsr WriteToFDC ; Send it.
|
|
|
|
Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #SetDriveGCR,D0 ; Set drive mode to GCR.
|
|
Moveq #(dr500 << DataRateOffset),D1 ; Data rate.
|
|
Cmp.w #fUnknownGCR,ftFormatCode(A2) ; GCR format?
|
|
Ble.s @1 ; Yes? Then set it.
|
|
Move.b #SetDriveMFM,D0 ; Set drive to MFM.
|
|
Moveq #(dr500 << DataRateOffset) | \ ;
|
|
(ApplePreComp << PreCompOffset), \ ;
|
|
D1 ; Date rate/pre-comp for HD.
|
|
Cmp.w #f1440K,ftFormatCode(A2) ; Double density?
|
|
Ble.s @1 ; Yes? Then set it.
|
|
Moveq #(dr1000 << DataRateOffset) | \ ;
|
|
(ApplePreComp << PreCompOffset), \ ;
|
|
D1 ; Must be extended density.
|
|
@1 Move.b D1,fdcDRR(A0) ; Set data rate/pre-comp.
|
|
Bsr WriteToFDC ; Send mode byte.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Waitx Bsr SenseInterrupt ; Check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Done?
|
|
Bne.s @Donex ; Change? Then Get out.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Waitx ; Loop
|
|
Move.w #noNybErr,fResult(A1) ; Error?
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
|
|
@Donex Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Move.w fResult(A1),D6 ; Error?
|
|
Bne @ExitFormatCopy ; Yes? Out of here.
|
|
Bsr Track0 ; Recalibrate.
|
|
Move.w fResult(A1),D6 ; Error?
|
|
Bne @ExitFormatCopy ; Yes? Out of here.
|
|
|
|
Moveq #0,D2 ; Starting cylinder number.
|
|
Move.w #0,D7 ; Init sync group flag.
|
|
Move.b ftSectors(A2),-3(A6); Number of sectors/track.
|
|
Move.b ftFormatByte(A2),-2(A6) ; Format code.
|
|
Move.b ftGap3Length(A2),D7 ; Gap3 length or number of sync groups.
|
|
Moveq #(MFMCylndrs -1),D5 ; Tracks/zone for MFM (1 zone).
|
|
Move.b #FormatWrite,-1(A6) ; FormatTrack command.
|
|
Lea MFMInterleave,A4 ; Interleave table.
|
|
|
|
Cmp.b #10,-3(A6) ; Sector count equal 10?
|
|
Bne.s @CylinderLoop ; No? Then MFM.
|
|
|
|
Move.b #12,-3(A6) ; Starting sectors/track for GCR.
|
|
Moveq #(GCRCylndrs -1),D5 ; Tracks/zone for GCR (5 zones).
|
|
Bclr.b #fFM,-1(A6) ; Clear MFM flag.
|
|
Move.b D3,-2(A6) ; Format code.
|
|
|
|
Lea Zone1_2,A4 ; 2:1 interleave table.
|
|
Cmp.b #formatGCR4_1,D3 ; 4:1 interleave?
|
|
Bne.s @CylinderLoop ; No? Then we have the right table.
|
|
Lea Zone1_4,A4 ; Yes? 4:1 interleave table.
|
|
|
|
@CylinderLoop Moveq #0,D3 ; Starting head.
|
|
Bsr SeekToTrack ; Seek to cylinder.
|
|
Move.w fResult(A1),D6 ; Seek error? <LW18>
|
|
Bne @ExitFormatCopy ; If so report it. <LW18>
|
|
|
|
@SideLoop Move.b -1(A6),D0 ; FormatWrite command. <SM4>
|
|
Bsr WriteToFDC ; Write command.
|
|
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Tst.w D3 ; Check side.
|
|
Beq.s @4 ; Side 0? Then all set.
|
|
Bset #fHd,D0 ; Set “side 1” flag.
|
|
@4 Bsr WriteToFDC ; Write head/drive.
|
|
|
|
Move.b -2(A6),D0 ; 512 bytes/sector (format code).
|
|
Bsr WriteToFDC ; Write bytes/sector.
|
|
|
|
Move.b -3(A6),D0 ; Sectors/track.
|
|
Bsr WriteToFDC ; Write sectors/track.
|
|
|
|
Move.l fBuffer(A1),A3 ; Pointer to buffer.
|
|
Moveq #0,D0 ; Clear sector loop counter.
|
|
Move.b -3(A6),D0 ; Sectors/track.
|
|
Subq.w #1,D0 ; For looping.
|
|
Move.l A4,D6 ; Keep copy of interleave table.
|
|
|
|
@10 Move.b D2,(A3)+ ; Track.
|
|
Move.b D3,(A3)+ ; Side.
|
|
|
|
Btst #fFM,-1(A6) ; Is this a MFM format? <LW7>
|
|
Bne.s @Sector ; Yes? The give the correct Sector number. <LW7>
|
|
Btst #15,D7 ; Is this the real format? <LW7>
|
|
Bne.s @Sector ; Yes? Then give the correct Sector number. <LW7>
|
|
Move.b #$3f,(A3)+ ; Give it a bogus Sector number <LW7>
|
|
Bra.s @FormatByte ; And Continue... <LW7>
|
|
@Sector Move.b (A4),(A3)+ ; Sector.
|
|
@FormatByte Move.b -2(A6),(A3)+ ; Format byte.
|
|
|
|
Move.w #127,D4 ; 512 bytes/sector but doing long transfer.
|
|
Moveq #0,D1 ; Init offset register.
|
|
Move.b (A4)+,D1 ; Sector number.
|
|
Btst #fFM,-1(A6) ; Formatting MFM?
|
|
Beq.s @14 ; no? then no need to do sub.
|
|
Sub.b #1,D1 ; mfminterleave is 1 base.
|
|
@14 Asl.w #8,D1 ; * 256.
|
|
Asl.w #1,D1 ; * 2 (* 512).
|
|
@11 Move.l (A5,D1.w),(A3)+ ; long to send.
|
|
Addq.w #4,D1 ; Next long.
|
|
Dbra D4,@11 ; Loop for one sector of data
|
|
Dbra D0,@10 ; Loop for one track.
|
|
|
|
Moveq #0,D0 ; Clear sector loop counter.
|
|
Move.b -3(A6),D0 ; Sectors/track.
|
|
Asl.l #2,D0 ; 4 header param/ sector.
|
|
Move.l D0,D1 ; Save it.
|
|
Asl.l #7,D0 ; 512 data byte/sector.
|
|
Add.l D0,D1 ; Bytes to transfer.
|
|
Move.l #pscControl32,A3 ; PSC control register.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Pause PSC.
|
|
@waitfrozen Move.w (A3), D0 ;
|
|
Btst.l #pscFrozen, D0 ; Test forzen bit.
|
|
Beq.s @waitfrozen ; Check that pause bit is frozen.
|
|
Move.w (A3),D0 ; Control setting.
|
|
And.w #$1,D0 ; Get set.
|
|
Asl.w #4,D0 ; Calculate offset to correct set.
|
|
Move.l #pscSet032,A4 ; DMA register set.
|
|
Move.l D1,pscCount(A4,D0.w) ; Set count. <SM21>
|
|
Move.l fBuffer(A1),pscAddress(A4,D0.w) ; Set address.
|
|
Move.w #(1 << pscDir) | \ ;
|
|
(0 << pscSense), \ ;
|
|
pscStatus(A4,D0.w) ; Set direction to write.
|
|
Move.w #(1 << pscEnable) | \ ;
|
|
(1 << pscSense), \ ;
|
|
pscStatus(A4,D0.w) ; Enable it.
|
|
Move.w #(1 << pscPause) | \ ;
|
|
(0 << pscSense), \ ;
|
|
(A3) ; Un-pause.
|
|
|
|
Move.l D6,A4 ; Restore interleave table.
|
|
Move.w #1,fResult(A1) ; Set result to “in progress”.
|
|
Move.b D7,D0 ; GCR sync groups or MFM Gap3 length.
|
|
Btst #fFM,-1(A6) ; Is this a MFM format? <LW7>
|
|
Bne.s @WriteSync ; Yes? The just write GAP3 length. <LW7>
|
|
Btst #15,D7 ; Is this the real format? <LW7>
|
|
Bne.s @WriteSync ; Yes? Then write the correct number of Sync Group. <LW7>
|
|
Move.b #$3f,D0 ; Give it a large number of Sync Group for Bogus format. <LW7>
|
|
@WriteSync Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Write Gap3 length (or sync groups).
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #2,D1 ; DBRA/4 Millisecond.
|
|
@Wait Bsr GetResult ; Check for interrupt. <SM23>
|
|
Cmp.w #0,fResult(A1) ; Result.
|
|
Ble.s @DoneDMA ; Done? Out of here.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait ; Loop
|
|
Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bsr ResetFDC ; If timeout then something horrible happened. <SM20>
|
|
Move.w #noNybErr,fResult(A1) ; Report as timeout error. <SM20>
|
|
@DoneDMA Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Move.w #(1 << pscSWReset) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A3) ; Reset channel.
|
|
Move.w fResult(A1),D6 ; Check error. <LW4>
|
|
Bne @ExitFormatCopy ; <LW4>
|
|
Btst #fFM,-1(A6) ; Formatting MFM? <SM4>
|
|
Bne.s @NextSide ; Yes? Then do next side.
|
|
|
|
Btst #15,D7 ; Was this pass real or just erasing the disk? <LW7>
|
|
Bne.s @ClearFlag ; Valid Format? Then clear flag and continue. <LW7>
|
|
Bset #15,D7 ; Set flag. <LW7>
|
|
Beq @SideLoop ; And Do the valid format. <LW7>
|
|
|
|
@ClearFlag Bclr #15,D7 ; <LW7>
|
|
@NextSide Moveq #0,D0 ; Init register.
|
|
Move.b -3(A6),D0 ; Sectors/track.
|
|
Asl.w #8,D0 ; * 256.
|
|
Asl.w #1,D0 ; * 2 (* 512).
|
|
Adda.w D0,A5 ; Bump data pointer.
|
|
|
|
Move.b ftSides(A2),D0 ; Flags/Sides.
|
|
And.b #$F,D0 ; Sides.
|
|
Cmp.b #1,D0 ; Single sided?
|
|
Beq.s @NextCylinder ; Yes? Then do next cylinder.
|
|
|
|
Addq.w #1,D3 ; Next side.
|
|
Cmp.w #1,D3 ; Side 1?
|
|
Beq @SideLoop ; Yes? Then do it.
|
|
|
|
@NextCylinder Addq.w #1,D2 ; Next cylinder.
|
|
Dbra D5,@CylinderLoop ; Cylinders left in this zone? Do it.
|
|
Adda.w #itNextZone,A4 ; Next interleave zone.
|
|
Subq.b #1,-3(A6) ; Decrement sectors/cylinder.
|
|
Moveq #15,D5 ; Reset cylinders/zone.
|
|
Cmp.w #DiskCylndrs,D2 ; Done?
|
|
Blt @CylinderLoop ; No? Then do it.
|
|
|
|
Bsr Track0 ; Recalibrate. <SM4> ws
|
|
Clr.b fCylinder(A1) ; Set track to 0. <SM4> ws
|
|
Moveq #noErr,D6 ; Indicate no error.
|
|
Move.l fDriveQueue(A1),A3 ; Pointer to drive queue.
|
|
Move.w ftFormatCode(A2),fFormat(A1) ; Set media format.
|
|
Move.w ftBlocks(A2),dQDrvSz(A3) ; Number of blocks.
|
|
Move.b #$FF,-1(A3) ; Non single-sided. <LW14>
|
|
Move.b #1,-3(A3) ; Set “Disk in place” flag.
|
|
Cmp.w #f400K,fFormat(A1) ; 400K GCR?
|
|
Bne.s @VerifyDisk ; No? Then double-sided.
|
|
Clr.b -1(A3) ; Single-sided.
|
|
|
|
Swap D7 ; Get do verify flag.
|
|
Tst.w D7 ;
|
|
Beq.s @ExitFormatCopy ; Don't do verify.
|
|
|
|
@VerifyDisk Move.l NewAgeVars,A3 ; DCE pointer.
|
|
Moveq #1,D3 ; Head.
|
|
Moveq #0,D2 ; Starting cylinder.
|
|
Moveq #0,D5 ; Starting sector number.
|
|
Moveq #0,D6 ; Init register.
|
|
Move.b ftSectors(A2),D6 ; Number of sectors/track.
|
|
Moveq #(MFMCylndrs -1),D7 ; Tracks/zone for MFM (1 zone).
|
|
Move.l fBuffer(A1),A2 ; Buffer.
|
|
Clr.l fCount(A1) ; Invalidate cache <SM20>
|
|
Lea -ioQElSize(A6),A4 ; Parameter block.
|
|
Move.w fDriveNum(A1),ioDrvNum(A4)
|
|
Cmp.b #10,D6 ; Sector count equal 10?
|
|
Bne.s @DoVerify ; No? Then MFM. <LW10>
|
|
Moveq #12,D6 ; Starting sectors/track for GCR.
|
|
Moveq #(GCRCylndrs -1),D7 ; Tracks/zone for GCR (5 zones).
|
|
|
|
@DoVerify Move.b #VerifyRetry,fRetrySeed(A1) ; Change the retry Count <SM30>
|
|
Move.b #VerifyRetry,fRetries(A1) ; Reset counter. <SM30>
|
|
@VerifyLoop Clr.l ioActCount(A4) ; Init count. <LW10>
|
|
Move.w D6,D4 ; Sectors/track. <LW10>
|
|
IF MTMode THEN
|
|
Cmp.w #f400K,fFormat(A1) ; Single sided? <LW10>
|
|
Beq.s @0 ; <LW10>
|
|
Asl.w #1,D4 ; Do both sides if multi-track. <LW10>
|
|
@0
|
|
ENDIF
|
|
Movem.l A2-A5/D2-D7,-(Sp) ; Save registers. <LW10>
|
|
Bsr DoRead ; Read track (or cylinder). <LW10>
|
|
Movem.l (Sp)+,A2-A5/D2-D7 ; Restore registers. <LW10>
|
|
Tst.w fResult(A1) ; Any errors? <LW10>
|
|
Bne.s @CopyError ; Yes? Then leave. <LW10>
|
|
Add.w D4,D5 ; Next starting sector. <LW10>
|
|
|
|
IF NOT MTMode THEN
|
|
Cmp.w #f400K,fFormat(A1) ; Single sided? <LW10>
|
|
Beq.s @ReadNextCylinder ; Yes? Then do next cylinder. <LW10>
|
|
Subq.w #1,D3 ; Next side. <LW10>
|
|
Neg.w D3 ; Still positive? <LW10>
|
|
Beq.s @VerifyLoop ; Yes? Then do it. <LW10>
|
|
ENDIF
|
|
|
|
@ReadNextCylinder
|
|
Addq.w #1,D2 ; Next cylinder. <LW10>
|
|
Dbra D7,@VerifyLoop ; Cylinders left in this zone? Do it. <LW10>
|
|
Subq.w #1,D6 ; Decrement sectors/cylinder. <LW10>
|
|
Moveq #(GCRCylndrs -1),D7 ; Reset cylinders/zone. <LW10>
|
|
Cmp.w #DiskCylndrs,D2 ; Done? <LW10>
|
|
Blt.s @VerifyLoop ; No? Then do it. <LW10>
|
|
Move.w fResult(A1),D6 ; <LW10>
|
|
|
|
@ExitFormatCopy Move.l Ticks,fLastAccess(A1)
|
|
Move.b #RetryCount,fRetrySeed(A1) ; Reset the retry Count <SM30>
|
|
Move.b #RetryCount,fRetries(A1) ; Reset counter. <SM30>
|
|
Clr.w fCount(A1) ; Void buffer.
|
|
|
|
@InvalidDriveExit
|
|
Unlk A6 ; Kill local storage.
|
|
Rts ; And leave.
|
|
|
|
@CopyError Move.w fResult(A1),D6 ; <LW4>
|
|
Bra.s @ExitFormatCopy ; <LW4>
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: Status
|
|
; Inputs: A0 -> Parameter block.
|
|
; A1 -> DCE.
|
|
; Outputs: D0 -> 0 - OK, ~0 - Error.
|
|
; Function: Return info for status requests.
|
|
;------------------------------------------------------------------------------
|
|
|
|
StatusRegs REG A0-A4/D3-D7 ; Registers abused.
|
|
|
|
Status
|
|
Movem.l StatusRegs,-(Sp) ; Scratch registers.
|
|
Movea.l A0,A4 ; Save parameter block.
|
|
Movea.l NewAgeVars,A3 ; DCE pointer.
|
|
Moveq #statusErr,D6 ; Assume failure.
|
|
|
|
Move.w csCode(A4),D0 ; CSCode.
|
|
Lea StatusVectors,A0 ; Vector table.
|
|
@Loop Cmp.w (A0),D0 ; Match?
|
|
Beq.s @CallIt ; Yes? Then call it.
|
|
Tst.l (A0)+ ; Next vector.
|
|
Bne.s @Loop ; Does it exist? Then check it.
|
|
Bra.s @ExitStatus ; No? Then leave with error.
|
|
|
|
@CallIt Move.w 2(A0),D0 ; Offset.
|
|
Beq.s @ExitStatus ; Leave if no routine.
|
|
Jsr StatusVectors(D0) ; Call it.
|
|
|
|
@ExitStatus Move.w D6,D0 ; Call result.
|
|
Movem.l (Sp)+,StatusRegs ; Restore registers.
|
|
Move.w D0,ioResult(A0) ; Set result value.
|
|
Move.w ioTrap(A0),D1 ; Trap called.
|
|
Btst #noQueueBit,D1 ; Called immediately?
|
|
Bne.s @2 ; Yes? Then leave via an RTS.
|
|
Tst.w D0 ; Completed?
|
|
Ble.s @1 ; Yes? Then go through IODone.
|
|
Btst #asyncTrpBit,D1 ; Called asynchronously?
|
|
Bne.s @2 ; Yes? Then leave via an RTS.
|
|
|
|
@1 Move.l JIODone,-(Sp) ; Vector to IODone.
|
|
@2 Rts ; Return.
|
|
|
|
StatusVectors Dc.w 06,FormatList - StatusVectors
|
|
Dc.w 08,DriveStatus - StatusVectors
|
|
|
|
; Apple use only routines…
|
|
Dc.w 10,MFMStatus - StatusVectors
|
|
Dc.w 17494,Duplicator - StatusVectors
|
|
Dc.w 21072,FloppyInfo - StatusVectors
|
|
Dc.w 21315,FormatByte - StatusVectors
|
|
Dc.w 0, 0
|
|
|
|
;******************************************************************************
|
|
; Return format list.
|
|
;******************************************************************************
|
|
|
|
FormatList Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to record.
|
|
Cmp.w fDriveNum(A1),D0 ; Match?
|
|
Beq.s @BuildTable ; Yes? Then check for drive.
|
|
Move.l fNextRecord(A1),A1 ; Point to next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Match? <SM16>
|
|
Beq.s @BuildTable ; Yes? Then check for drive. <SM16>
|
|
@100 Move.w #nsDrvErr,D6 ; Assume “No such drive” error.
|
|
Bra.s @ExitFormatList ; No record? Then leave.
|
|
|
|
@BuildTable Move.w #offLinErr,D6 ; Assume no disk.
|
|
Tst.w fFormat(A1) ; Disk in?
|
|
Beq.s @ExitFormatList ; No? Then leave with error.
|
|
|
|
Move.w #paramErr,D6 ; Assume parameter error.
|
|
Move.w csParam(A4),D0 ; Number of formats to return.
|
|
Subq.w #1,D0 ; For looping.
|
|
Bmi.s @ExitFormatList ; -? Then param error.
|
|
|
|
Moveq #noErr,D6 ; No error.
|
|
Move.l csParam+2(A4),A0 ; Table.
|
|
Clr.w csParam(A4) ; Init number of entries returned.
|
|
Lea FormatTable,A2 ; Pointer to format table.
|
|
Lea AppleDrv,A3 ; Assume AppleDrive installed.
|
|
Cmp.w #fddMac800,fDriveType(A1) ; 800K drive installed?
|
|
Beq.s @FormatLoop ; Yes? Then we know end of table.
|
|
Lea LowDensity,A3 ; Assume low denisty media inserted.
|
|
Cmp.w #mLD,fMediaType(A1) ; Low denisty disk?
|
|
Beq.s @FormatLoop ; Yes? Then all GCR & 720K supported.
|
|
Lea HighDensity,A3 ; Assume high denisty media inserted.
|
|
Cmp.w #mHD,fMediaType(A1) ; High denisty disk?
|
|
Beq.s @FormatLoop ; Yes? Then only HD format allowed.
|
|
Lea ExtendedDensity,A3 ; Must be extended density media.
|
|
|
|
@FormatLoop Move.w (A3)+,D3 ; Offset to format.
|
|
Bmi.s @ExitFormatList ; -? Then end of list.
|
|
Move.l (A2,D3.w),(A0)+ ; Number of blocks.
|
|
Move.l ftFlags(A2,D3.w),D2 ; Flags.
|
|
Move.l ftFormatByte(A2,D3.w),D1 ; Format flag.
|
|
Cmp.w fFormat(A1),D1 ; Match?
|
|
Bne.s @0 ; No? The disk doesn't have this format.
|
|
Bset #30,D2 ; Yes? Then disk has this format.
|
|
@0 Move.l D2,(A0)+ ; Install flags.
|
|
Addq.w #1,csParam(A4) ; Bump count.
|
|
@1 Dbra D0,@FormatLoop ; Loop. <SM16>
|
|
|
|
@ExitFormatList Rts ; Leave.
|
|
|
|
AppleDrv Dc.w 0,12,24,-1,-1 ; Apple drive can support all GCR.
|
|
LowDensity Dc.w 0,12,24,48,-1 ; SuperDrive/Typhoon LD: GCR, 720K MFM.
|
|
HighDensity Dc.w 36,-1,-1,-1,-1 ; SuperDrive/Typhoon HD: 1.4 MFM.
|
|
ExtendedDensity Dc.w 60,-1,-1,-1,-1 ; Typhoon ExtendedDensity: 2.8 MFM.
|
|
|
|
FormatTable Dc.l 0800,$810A0050 ; 400K GCR 2:1 Interleave.
|
|
Dc.b $12,$8 ; Format byte, sync groups.
|
|
Dc.w f400K ; Format code.
|
|
|
|
Dc.l 1600,$820A0050 ; 800K GCR 2:1 Interleave.
|
|
Dc.b $22,$8 ; Format byte, sync groups.
|
|
Dc.w f800K ; Format code.
|
|
|
|
Dc.l 1600,$A20A0050 ; ProDOS GCR 4:1 Interleave.
|
|
Dc.b $24,$8 ; Format byte, sync groups.
|
|
Dc.w fProDOS ; Format code.
|
|
|
|
SuperDrvTable Dc.l 2880,$d2120050 ; 1.4 MFM. <LW10>
|
|
Dc.b $02,$65 ; Format byte, Gap3 length.
|
|
Dc.w f1440K ; Format code.
|
|
|
|
Dc.l 1440,$82090050 ; 720K MFM.
|
|
Dc.b $02,$50 ; Format byte, Gap3 length.
|
|
Dc.w f720K ; Format code.
|
|
|
|
TyphoonTable Dc.l 5760,$d2240050 ; 2.8 MFM. <LW10>
|
|
Dc.b $02,$54 ; Format byte, Gap3 length.
|
|
Dc.w f2880K ; Format code.
|
|
EndOfTable Dc.l 0,0,0 ; End of table.
|
|
|
|
|
|
|
|
;******************************************************************************
|
|
; Return drive status.
|
|
;******************************************************************************
|
|
|
|
DriveStatus Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Cmp.w fDriveNum(A1),D0 ; Match?
|
|
Beq.s @GetRecord ; Yes? Then check for drive.
|
|
Move.l fNextRecord(A1),A1 ; Point to next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Match? <SM16>
|
|
Beq.s @GetRecord ; Yes? Then check for drive. <SM16>
|
|
|
|
@100 Move.w #nsDrvErr,D6 ; Assume “No such drive” error.
|
|
Cmp.w #2,D0 ; Must accept drive number 1 and 2.
|
|
Bgt.s @ExitDrvStatus ; Unknown drive number? Then leave.
|
|
Cmp.w #0,D0 ; Must accept drive number 1 and 2.
|
|
Ble.s @ExitDrvStatus ; Unknown drive number? Then leave.
|
|
Moveq #noErr,D6 ; Indicate no error.
|
|
Lea csParam(A4),A0 ; Record.
|
|
|
|
; This is the data return for an uninstalled drive.
|
|
Clr.l (A0)+ ; Track at zero. Not write protect. No disk.
|
|
Move.w #$FFFF,(A0)+ ; No drive installed. Two side drive not installed.
|
|
Clr.l (A0)+ ; No queue link.
|
|
Clr.w (A0)+ ; Dummy queue?
|
|
Move.w D0,(A0)+ ; Drive number.
|
|
Move.w #$FFFB,(A0)+ ; Floppy drive ref number.
|
|
Clr.w (A0)+ ; File System ID.
|
|
Move.w #$FFFF,(A0)+ ; 2 sided format. New interface.
|
|
Clr.w (A0)+
|
|
|
|
Bra.s @ExitDrvStatus ; Done
|
|
|
|
@GetRecord Move.l fDriveQueue(A1),A3 ; Drive queue.
|
|
|
|
Lea csParam(A4),A0 ; Record.
|
|
Clr.b (A0)+ ; High byte of track.
|
|
Move.b fCylinder(A1),(A0)+ ; Track.
|
|
Move.b -4(A3),(A0)+ ; Write protect.
|
|
Move.b -3(A3),(A0)+ ; Disk in place.
|
|
Move.b #1,(A0)+ ; Installed.
|
|
Move.b #$FF,(A0)+ ; 2-Sided drive.
|
|
Move.l qLink(A3),(A0)+
|
|
Move.w qType(A3),(A0)+
|
|
Move.w dQDrive(A3),(A0)+
|
|
Move.w dQRefNum(A3),(A0)+
|
|
Move.w dQFSID(A3),(A0)+
|
|
Move.b -1(A3),(A0)+ ; Sides.
|
|
Move.b #$FF,(A0)+ ; Assume new interface.
|
|
Cmp.w #f400K,fFormat(A1) ; 400K GCR? <LW14>
|
|
Bne.s @1 ; No? Then double-sided. <LW14>
|
|
Move.b #0,-1(A0) ; Old interface. <LW14>
|
|
@1 Move.w fSoftErrors(A1),(A0)+ ; Soft errors.
|
|
|
|
Moveq #noErr,D6 ; Indicate no error.
|
|
@ExitDrvStatus Rts ; And leave.
|
|
|
|
|
|
;******************************************************************************
|
|
; Return MFM status.
|
|
;******************************************************************************
|
|
|
|
MFMStatus Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Cmp.w fDriveNum(A1),D0 ; Match?
|
|
Beq.s @SetStatus ; Yes? Then check for drive.
|
|
Move.l fNextRecord(A1),A1 ; Point to next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Match? <SM16>
|
|
Beq.s @SetStatus ; Yes? Then check for drive. <SM16>
|
|
@100 Move.w #nsDrvErr,D6 ; Assume “No such drive” error.
|
|
Bra.s @ExitMFMStatus ; No record? Then leave.
|
|
|
|
@SetStatus Clr.b csParam(A4) ; Assume 800K floppy.
|
|
Cmp.w #fddMac800,fDriveType(A1) ; Check drive type.
|
|
Ble.s @0 ; 800K or less?
|
|
Move.b #-1,csParam(A4) ; Assume SuperDrive.
|
|
Cmp.w #fddSuperDrive,fDriveType(A1)
|
|
Beq.s @0 ; SuperDrive?
|
|
Move.b #-2,csParam(A4) ; Must be a Typhoon.
|
|
|
|
@0 Clr.b csParam+1(A4) ; Assume GCR format.
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; Last GCR format.
|
|
Ble.s @1 ; GCR?
|
|
Move.b #-1,csParam+1(A4) ; Must be MFM.
|
|
|
|
@1 Move.w fFormat(A1),D0 ; Drive format.
|
|
Sub.w #f720K,D0 ; Make 0 based.
|
|
Neg.w D0 ; Negate it.
|
|
Move.b D0,csParam+2(A4) ; Disk format.
|
|
Move.b #-3,csParam+3(A4) ; NewAge.
|
|
|
|
Moveq #noErr,D6 ; Indicate no error.
|
|
@ExitMFMStatus Rts ; And leave.
|
|
|
|
;******************************************************************************
|
|
; Return duplicator version.
|
|
;******************************************************************************
|
|
|
|
Duplicator Move.w #$0410,csParam(A4) ; Return min. duplicator version.
|
|
Moveq #noErr,D6 ; Indicate no error.
|
|
Rts ; And leave.
|
|
|
|
;******************************************************************************
|
|
; Return floppy info record.
|
|
;******************************************************************************
|
|
|
|
FloppyInfo Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Cmp.w fDriveNum(A1),D0 ; Match?
|
|
Beq.s @SetPointer ; Yes? Then check for drive.
|
|
Move.l fNextRecord(A1),A1 ; Point to next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Match? <SM16>
|
|
Beq.s @SetPointer ; Yes? Then check for drive. <SM16>
|
|
@100 Move.w #nsDrvErr,D6 ; Assume “No such drive” error.
|
|
Bra.s @ExitFloppyInfo ; No record? Then leave.
|
|
|
|
@SetPointer Move.l A1,csParam(A4) ; Return pointer to floppy info record.
|
|
Moveq #noErr,D6 ; Set no error.
|
|
|
|
@ExitFloppyInfo Rts ; Leave.
|
|
|
|
|
|
;******************************************************************************
|
|
; Return last format byte read.
|
|
;******************************************************************************
|
|
|
|
FormatByte Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Move.l dCtlStorage(A3),A1 ; Pointer to floppy info.
|
|
Cmp.w fDriveNum(A1),D0 ; Match?
|
|
Beq.s @GetByte ; Yes? Then check for drive.
|
|
Move.l fNextRecord(A1),A1 ; Point to next record.
|
|
Beq.s @100 ; <LW17>
|
|
Cmp.w fDriveNum(A1),D0 ; Match? <SM16>
|
|
Beq.s @GetByte ; Yes? Then check for drive. <SM16>
|
|
@100 Move.w #nsDrvErr,D6 ; Assume “No such drive” error.
|
|
Bra.s @ExitFormatByte ; No record? Then leave.
|
|
|
|
@GetByte Move.b fData(A1),csParam(A4) ; Set format code.
|
|
Moveq #noErr,D6 ; Set no error.
|
|
|
|
@ExitFormatByte Rts ; Leave.
|
|
|
|
IF noClose Then
|
|
;------------------------------------------------------------------------------
|
|
; Routine: Close
|
|
; Inputs: A0 -> Parameter block.
|
|
; A1 -> DCE.
|
|
; Outputs: D0 -> 0 - OK.
|
|
; Function: The Floppy Driver should not be close. Return error code.
|
|
;------------------------------------------------------------------------------
|
|
Close Moveq #closErr,D0 ; Indicate error.
|
|
Move.l JIODone,-(Sp) ; Vector to IODone.
|
|
Rts ; And call it.
|
|
|
|
ELSE
|
|
;------------------------------------------------------------------------------
|
|
; Routine: Close
|
|
; Inputs: A0 -> Parameter block.
|
|
; A1 -> DCE.
|
|
; Outputs: D0 -> 0 - OK.
|
|
; Function: Close the driver releasing any memory used.
|
|
;------------------------------------------------------------------------------
|
|
|
|
CloseRegs REG A0-A3/D3-D4 ; Registers abused.
|
|
|
|
Close Movem.l CloseRegs,-(Sp) ; Scratch registers.
|
|
Movea.l NewAgeVars,A1 ; DCE.
|
|
Clr.l VIA2DT+(5*4) ; Clear handler.
|
|
Move.b #DisableFDCInt,VIA2IER; Disable interrups. <SM1> RLM
|
|
Movea.l dCtlStorage(A1),A2 ; Floppy info record.
|
|
Move.w dCtlRefNum(A1),D4 ; Driver RefNum.
|
|
@Loop Move.l fNextRecord(A2),D3 ; Pointer to next record.
|
|
Move.l fBuffer(A2),D0 ; Buffer?
|
|
Beq.s @0 ; No? Then kill record.
|
|
Move.l D0,A0 ; Buffer pointer.
|
|
_DisposPtr ; Free it.
|
|
@0 Move.l A2,A0 ; Pointer to record.
|
|
_DisposPtr
|
|
|
|
Move.l D3,A2 ; Assume there is another record.
|
|
Tst.l D3 ; Is there another record?
|
|
Bne.s @Loop ; Yes? Then kill it.
|
|
|
|
@1 Lea DrvQHdr+2,A0 ; Pointer to first drive.
|
|
@DrvLoop Move.l A0,A1 ; Save current drive.
|
|
Move.l (A0),D0 ; Is there a drive?
|
|
Beq.s @ExitClose ; No? Then we're done.
|
|
Move.l D0,A0 ; Pointer to drive queue.
|
|
Cmp.w dQRefNum(A0),D4 ; Is this our drive?
|
|
Bne.s @DrvLoop ; No? Then check next drive.
|
|
|
|
Lea DrvQHdr,A1 ; Drive queue header.
|
|
_Dequeue ; Remove it from the queue.
|
|
Tst.l -(A0) ; Point to fron of flags.
|
|
_DisposPtr ; Kill queue record.
|
|
Bra.s @1 ; Loop till done.
|
|
|
|
@ExitClose Movem.l (Sp)+,CloseRegs ; Restore registers.
|
|
Moveq #noErr,D0 ; Indicate no error.
|
|
Move.l JIODone,-(Sp) ; Vector to IODone.
|
|
Rts ; And call it.
|
|
ENDIF
|
|
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: SetUpFDC
|
|
; Inputs: A4 -> Parameter block.
|
|
; Outputs: A0 -> FDC base address.
|
|
; A1 -> Floppy info record.
|
|
; Function: Set the FDC to the correct mode of opertion.
|
|
; Called By: Prime, Verify.
|
|
;------------------------------------------------------------------------------
|
|
|
|
SetUpFDC Move.l A2,-(Sp) ; Save A2.
|
|
Move.l #NewAge32,A0 ; Base address of NewAge.
|
|
Move.l NewAgeVars,A1 ; Pointer to DCE.
|
|
Move.l dCtlStorage(A1),A1 ; Floppy info records.
|
|
Move.w ioDrvNum(A4),D0 ; Drive number.
|
|
Cmp.w fDriveNum(A1),D0 ; Correct record?
|
|
Beq.s @SetUp ; Yes? Then use it.
|
|
Move.l fNextRecord(A1),A1 ; Next record.
|
|
|
|
@SetUp Move.l fDriveQueue(A1),A2 ; Drive queue. <LW26>
|
|
Cmp.b #1,-3(A2) ; First access to this floppy? <LW26>
|
|
Beq @SetUp2 ; Yes? Then need to do everything. <LW26>
|
|
Tst.b fMotor(A1) ; Motor on? <LW26>
|
|
Bne @ExitSetUp ; Yes? Then done. <LW26>
|
|
Bsr EnableMotor ; Turn motor on. <LW26>
|
|
Bra @ExitSetUp ; Done. <LW26>
|
|
|
|
|
|
@SetUp2 Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #SetEnableOn,D0 ; Set enable command.
|
|
Bsr WriteToFDC ; Send it.
|
|
Move.w fDriveSelect(A1),D0 ; Drive number.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Enable the drive.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Wait Bsr SenseInterrupt ; Go check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Wait for status to change.
|
|
Bne.s @Changed ; Change? Then Get out.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait ; Loop
|
|
Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Bsr ResetFDC ; Try resetting FDC.
|
|
Move.w #noNybErr,fResult(A1) ; Error?
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
|
|
@Changed Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @ExitSetUp ; Yes? Out of here.
|
|
; Move.l fDriveQueue(A1),A2 ; Drive queue.
|
|
; Cmp.b #1,-3(A2) ; First access to this floppy?
|
|
; Bne @SetMode ; No? Then we already know what it is.
|
|
Move.b #2,-3(A2) ; Yes? Then set flag.
|
|
Bsr Track0 ; And recalibrate.
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @ExitSetUp ; Yes? Out of here.
|
|
|
|
Move.b #SenseDrvStatus,D0 ; Sense drive status command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Move.b fDriveSelect(A1),D0 ; Drive ID.
|
|
Bsr WriteToFDC ; Send command.
|
|
Bsr ReadFromFDC ; Read Status3.
|
|
Move.b D0,fStatus3(A1) ; Save it.
|
|
|
|
Move.b #1,fRetries(A1) ; Set retry count.
|
|
Move.w #mLD,fMediaType(A1) ; Assume low density.
|
|
Btst #7,D0 ; Low density bit set?
|
|
Bne.s @StartMotor ; Yes? Then start motor.
|
|
Move.w #mHD,fMediaType(A1) ; Assume high density.
|
|
Btst #1,D0 ; Sel media bit set?
|
|
Bne.s @StartMotor ; Yes? Then high density.
|
|
Move.w #mED,fMediaType(A1) ; Must be extended density.
|
|
|
|
@StartMotor Bsr EnableMotor ; Turn motor on.
|
|
Tst.w fResult(A1) ; Get error code.
|
|
Bne @ExitSetUp ; Error? Out of here.
|
|
Move.b #PerpMode,D0 ; Set up perpendicular mode.
|
|
Bsr WriteToFDC ; Send it.
|
|
Moveq #$0,D0 ; Standard.
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
@GCRRetry Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #SetDriveGCR,D0 ; Set drive mode to GCR.
|
|
Bsr WriteToFDC ; Send it.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Wait0 Bsr SenseInterrupt ; Go check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Wait for status to change.
|
|
Bne.s @Changed0 ; Change? Then Get out.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait0 ; Loop
|
|
Bra.s @0 ; Error. Go reset.
|
|
|
|
@Changed0 Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @ExitSetUp ; Yes? Out of here.
|
|
Move.b #(dr500 << DataRateOffset),fdcDRR(A0) ; Set data rate.
|
|
Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #ReadGCRID,D0 ; Command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Write head/drive.
|
|
Bne.s @0 ; Fail? Reset.
|
|
|
|
Moveq #0,D1 ; Init counter.
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond. <SM4> ws
|
|
@Wait1 Bsr GetResult ; Go check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Wait for status to change.
|
|
Bne @CheckResult ; Change? Then check it.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait1 ; Loop until it changes.
|
|
|
|
@0 Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bsr ResetFDC ; Try resetting FDC.
|
|
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <LW8>
|
|
Move.b #-1,fStatus0(A1) ; Set flag. <LW8>
|
|
Moveq #0,D1 ; Init counter. <LW8>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond. <LW8>
|
|
Asl.l #6,D1 ; DBRA/64 Millisecond. <LW8>
|
|
@WaitGCRInt Bsr SenseInterrupt ; Check for interrupt. <LW8>
|
|
Cmp.b #-1,fStatus0(A1) ; Done? <LW8>
|
|
Bne.s @GCRResetInt ; Change? Then Get out. <LW8>
|
|
Subq.l #1,D1 ; Decrement counter. <LW8>
|
|
Bne.s @WaitGCRInt ; Loop <LW8>
|
|
Move.w #noNybErr,fResult(A1) ; Set flag
|
|
|
|
@GCRResetInt Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <LW8>
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @ExitSetUp ; Yes? Out of here.
|
|
Move.b #-1,fStatus0(A1) ; Set flag. <LW8>
|
|
Move.b #SetEnableOn,D0 ; Set enable command. <LW8>
|
|
Bsr WriteToFDC ; Send it. <LW8>
|
|
Move.w fDriveSelect(A1),D0 ; Drive number. <LW8>
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <LW8>
|
|
Bsr WriteToFDC ; Enable the drive. <LW8>
|
|
|
|
Moveq #0,D1 ; Init counter. <LW8>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond. <LW8>
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond. <LW8>
|
|
@WaitGCREnable Bsr SenseInterrupt ; Go check for interrupt. <LW8>
|
|
Cmp.b #-1,fStatus0(A1) ; Wait for status to change. <LW8>
|
|
Bne.s @GCREnable ; Change? Then Get out. <LW8>
|
|
Subq.l #1,D1 ; Decrement counter. <LW8>
|
|
Bne.s @WaitGCREnable ; Loop <LW8>
|
|
Move.w #noNybErr,fResult(A1) ; Set flag
|
|
|
|
@GCREnable Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <LW8>
|
|
Clr.b fMotor(A1) ; Clear flag. <LW8>
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @ExitSetUp ; Yes? Out of here.
|
|
|
|
Move.b #PerpMode,D0 ; Set up perpendicular mode.
|
|
Bsr WriteToFDC ; Send it.
|
|
Moveq #$0,D0 ; Standard.
|
|
Bsr WriteToFDC ; Send it.
|
|
Subq.b #1,fRetries(A1) ; Retry?
|
|
Bpl @GCRRetry ; Yes? Then do it.
|
|
Bra.s @MFM ; No? Then try MFM.
|
|
|
|
@CheckResult Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Tst.w fResult(A1) ; Check error code.
|
|
Bne.s @MFM ; Error? Then check for MFM disk.
|
|
|
|
Move.w #f400K,fFormat(A1) ; Assume 400K GCR.
|
|
Move.b fData(A1),D0 ; Data code.
|
|
Cmp.b #$02,D0 ; 400K format (SuperDrive bug)?
|
|
Beq @SetQueue ; Yes? Then setup drive queue.
|
|
Cmp.b #formatGCR400K,D0 ; 400K format (The real one)?
|
|
Beq @SetQueue ; Yes? Then setup drive queue.
|
|
Move.w #f800K,fFormat(A1) ; Assume 800K GCR (Mac).
|
|
Cmp.b #formatGCR2_1,D0 ; 800K format?
|
|
Beq @SetQueue ; Yes? Then setup drive queue.
|
|
Move.w #fProDOS,fFormat(A1) ; Assume ProDOS format.
|
|
Cmp.b #formatGCR4_1,D0 ; Apple II format?
|
|
Beq @SetQueue ; Yes? Then setup drive queue.
|
|
Move.w #fUnknownGCR,fFormat(A1) ; Set “unknown” flag.
|
|
Beq @SetQueue ; Yes? Then setup drive queue.
|
|
|
|
@MFM Cmp.w #fddSuperDrive,fDriveType(A1) ; At least a SuperDrive?
|
|
Blt @Blank ; No? Then can't check MFM.
|
|
Clr.b fLastSector(A1) ; Set last sector to zero.
|
|
Move.b #1,fRetries(A1) ; Set retry count.
|
|
|
|
@MFMRetry Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #SetDriveMFM,D0 ; Set drive mode to MFM.
|
|
Bsr WriteToFDC ; Send it.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Wait2 Bsr SenseInterrupt ; Go check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Wait for status to change.
|
|
Bne.s @Changed2 ; Change? Then Get out.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait2 ; Loop
|
|
Bra.s @1 ;
|
|
|
|
@Changed2 Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @ExitSetUp ; Yes? Out of here.
|
|
Move.b #(ApplePreComp << PreCompOffset)| \ ;
|
|
(dr500 << DataRateOffset), \ ;
|
|
fdcDRR(A0) ; Set MFM pre-comp.
|
|
@MFMLoop Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #ReadMFMID,D0 ; Command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Write head/drive.
|
|
Bne.s @1 ; Error? Then retry.
|
|
|
|
Moveq #0,D1 ; Init counter.
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond. <SM4> ws
|
|
@Wait3 Bsr GetResult ; Go check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Wait for status to change.
|
|
Bne @CheckMFM ; Change? Then check it.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait3 ; Loop until it changes.
|
|
|
|
@1 Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bsr ResetFDC ; Try resetting FDC.
|
|
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <LW8>
|
|
Move.b #-1,fStatus0(A1) ; Set flag. <LW8>
|
|
Moveq #0,D1 ; Init counter. <LW8>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond. <LW8>
|
|
Asl.l #6,D1 ; DBRA/64 Millisecond. <LW8>
|
|
@WaitMFMInt Bsr SenseInterrupt ; Check for interrupt. <LW8>
|
|
Cmp.b #-1,fStatus0(A1) ; Done? <LW8>
|
|
Bne.s @MFMResetInt ; Change? Then Get out. <LW8>
|
|
Subq.l #1,D1 ; Decrement counter. <LW8>
|
|
Bne.s @WaitMFMInt ; Loop <LW8>
|
|
Move.w #noNybErr,fResult(A1) ; Set flag
|
|
|
|
@MFMResetInt Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <LW8>
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @ExitSetUp ; Yes? Out of here.
|
|
Move.b #-1,fStatus0(A1) ; Set flag. <LW8>
|
|
Move.b #SetEnableOn,D0 ; Set enable command. <LW8>
|
|
Bsr WriteToFDC ; Send it. <LW8>
|
|
Move.w fDriveSelect(A1),D0 ; Drive number. <LW8>
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <LW8>
|
|
Bsr WriteToFDC ; Enable the drive. <LW8>
|
|
|
|
Moveq #0,D1 ; Init counter. <LW8>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond. <LW8>
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond. <LW8>
|
|
@WaitMFMEnable Bsr SenseInterrupt ; Go check for interrupt. <LW8>
|
|
Cmp.b #-1,fStatus0(A1) ; Wait for status to change. <LW8>
|
|
Bne.s @MFMEnable ; Change? Then Get out. <LW8>
|
|
Subq.l #1,D1 ; Decrement counter. <LW8>
|
|
Bne.s @WaitMFMEnable ; Loop <LW8>
|
|
Move.w #noNybErr,fResult(A1) ; Set flag
|
|
|
|
@MFMEnable Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <LW8>
|
|
Clr.b fMotor(A1) ; Clear flag. <LW8>
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @ExitSetUp ; Yes? Out of here.
|
|
Move.b #PerpMode,D0 ; Set up perpendicular mode.
|
|
Bsr WriteToFDC ; Send it.
|
|
Moveq #$0,D0 ; Standard.
|
|
Bsr WriteToFDC ; Send it.
|
|
Subq.b #1,fRetries(A1) ; Decrement retry count.
|
|
Bpl @MFMRetry ; Retry.
|
|
Bra.s @ED ; Try ED.
|
|
|
|
@CheckMFM Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Tst.w fResult(A1) ; Check error code.
|
|
Bne.s @ED ; Error? Then try extended density.
|
|
|
|
Move.b fSector(A1),D0 ; Sector number read.
|
|
Cmp.b fLastSector(A1),D0 ; New sector less than old?
|
|
Bls.s @CheckFormat ; Yes? Then determine format.
|
|
Move.b D0,fLastSector(A1) ; No? Then save new high sector number.
|
|
Bra @MFMLoop ; And do another read.
|
|
|
|
@CheckFormat Move.b fLastSector(A1),D0 ; High sector number.
|
|
Move.w #f720K,fFormat(A1) ; Assume 720K.
|
|
Cmp.b #9,D0 ; 9 sectors or less?
|
|
Beq @SetQueue ; Yes? Then setup drive queue.
|
|
Move.w #f1440K,fFormat(A1) ; No? Then must be 1.4Meg format.
|
|
Bra @SetQueue ; Setup drive queue.
|
|
|
|
@ED Cmp.w #fddTyphoon,fDriveType(A1) ; At least a Typhoon?
|
|
Blt @Blank ; No? Then can't check ED.
|
|
Move.b #1,fRetries(A1) ; Set retry count.
|
|
|
|
@EDRetry Move.b #PerpMode,D0 ; Set up perpendicular mode.
|
|
Bsr WriteToFDC ; Send it.
|
|
Moveq #vertical,D0 ; Perpendicular (1Mbps).
|
|
Bsr WriteToFDC ; Send it.
|
|
Move.b #(ApplePreComp << PreCompOffset)| \ ;
|
|
(dr1000 << DataRateOffset), \ ;
|
|
fdcDRR(A0) ; Set pre-comp and data rate.
|
|
Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #ReadMFMID,D0 ; Command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Write head/drive.
|
|
Bne.s @2 ; Error? Then retry.
|
|
|
|
Moveq #0,D1 ; Init counter.
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond. <SM4> ws
|
|
@Wait4 Bsr GetResult ; Go check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Wait for status to change.
|
|
Bne @CheckED ; Change? Then check it.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait4 ; Loop until it changes.
|
|
|
|
@2 Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bsr ResetFDC ; Try resetting FDC.
|
|
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <LW8>
|
|
Move.b #-1,fStatus0(A1) ; Set flag. <LW8>
|
|
Moveq #0,D1 ; Init counter. <LW8>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond. <LW8>
|
|
Asl.l #6,D1 ; DBRA/64 Millisecond. <LW8>
|
|
@WaitEDInt Bsr SenseInterrupt ; Check for interrupt. <LW8>
|
|
Cmp.b #-1,fStatus0(A1) ; Done? <LW8>
|
|
Bne.s @EDResetInt ; Change? Then Get out. <LW8>
|
|
Subq.l #1,D1 ; Decrement counter. <LW8>
|
|
Bne.s @WaitEDInt ; Loop <LW8>
|
|
Move.w #noNybErr,fResult(A1) ; Set flag
|
|
|
|
@EDResetInt Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <LW8>
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @ExitSetUp ; Yes? Out of here.
|
|
Move.b #-1,fStatus0(A1) ; Set flag. <LW8>
|
|
Move.b #SetEnableOn,D0 ; Set enable command. <LW8>
|
|
Bsr WriteToFDC ; Send it. <LW8>
|
|
Move.w fDriveSelect(A1),D0 ; Drive number. <LW8>
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <LW8>
|
|
Bsr WriteToFDC ; Enable the drive. <LW8>
|
|
|
|
Moveq #0,D1 ; Init counter. <LW8>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond. <LW8>
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond. <LW8>
|
|
@WaitEDEnable Bsr SenseInterrupt ; Go check for interrupt. <LW8>
|
|
Cmp.b #-1,fStatus0(A1) ; Wait for status to change. <LW8>
|
|
Bne.s @EDEnable ; Change? Then Get out. <LW8>
|
|
Subq.l #1,D1 ; Decrement counter. <LW8>
|
|
Bne.s @WaitEDEnable ; Loop <LW8>
|
|
Move.w #noNybErr,fResult(A1) ; Set flag
|
|
|
|
@EDEnable Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <LW8>
|
|
Clr.b fMotor(A1) ; Clear flag. <LW8>
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne @ExitSetUp ; Yes? Out of here.
|
|
Subq.b #1,fRetries(A1) ; Decrement retry count. <SM19>
|
|
Bpl @EDRetry ; Retry. <SM19>
|
|
Bra.s @Blank ; <SM19>
|
|
|
|
@CheckED Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Tst.w fResult(A1) ; Check error code.
|
|
Bne.s @Blank ; Error? Must be blank.
|
|
|
|
Move.w #f2880K,fFormat(A1) ; Assume 2.8.
|
|
Move.b #$24,fLastSector(A1); Set number of sectors.
|
|
Bra.s @SetQueue ; Yes? Then setup drive queue.
|
|
|
|
@Blank Move.w #fBlank,fFormat(A1) ; Error? Indicate blank disk.
|
|
Clr.w fResult(A1) ;
|
|
|
|
@SetQueue Move.b fData(A1),fCode(A1) ; Save format code.
|
|
Move.b fRetrySeed(A1),fRetries(A1) ; Reset retries.
|
|
Lea Blocks,A2 ; Block table.
|
|
Move.w fFormat(A1),D1 ; Format code.
|
|
Asl.w #1,D1 ; Make index.
|
|
Move.w (A2,D1.w),D1 ; Number of blocks.
|
|
|
|
Move.l fDriveQueue(A1),A2 ; Pointer to drive queue.
|
|
Move.w D1,dQDrvSz(A2) ; Install number of blocks.
|
|
Move.b #$FF,-1(A2) ; Non single-sided. <LW14>
|
|
Cmp.w #f400K,fFormat(A1) ; 400K GCR?
|
|
Bne.s @3 ; No? Then double-sided.
|
|
Clr.b -1(A2) ; Single-sided.
|
|
@3 Btst.b #fWriteStatus,fStatus3(A1) ; Write protected?
|
|
Bne @ExitSetup ; No? Then leave.
|
|
Move.b #$80,-4(A2) ; Set flag.
|
|
Bra @ExitSetUp ; Leave.
|
|
|
|
@SetMode Tst.b fMotor(A1) ; Motor on?
|
|
Bne.s @4 ; Yes? Then set up FDC.
|
|
Bsr EnableMotor ; Turn motor on.
|
|
Tst.w fResult(A1) ; Get error code.
|
|
Bne.s @ExitSetUp ; Error? Out of here.
|
|
|
|
@4 Move.b #PerpMode,D0 ; Set up perpendicular mode.
|
|
Bsr WriteToFDC ; Send it.
|
|
Moveq #$0,D0 ; Standard.
|
|
Cmp.w #f2880K,fFormat(A1) ; Extended density?
|
|
Bne.s @5 ; No? Then horizontal mode.
|
|
Moveq #vertical,D0 ; Yes? Set vertical mode.
|
|
@5 Bsr WriteToFDC ; Send it.
|
|
|
|
Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #SetDriveGCR,D0 ; Set drive mode to GCR.
|
|
Moveq #(dr500 << DataRateOffset),D1 ; Data rate.
|
|
Cmp.w #fUnknownGCR,fFormat(A1) ; GCR format?
|
|
Ble.s @6 ; Yes? Then set it.
|
|
Move.b #SetDriveMFM,D0 ; Set drive to MFM.
|
|
Moveq #(dr500 << DataRateOffset) | \ ;
|
|
(ApplePreComp << PreCompOffset), \ ;
|
|
D1 ; Date rate/pre-comp for HD.
|
|
Cmp.w #f1440K,fFormat(A1) ; Double density?
|
|
Ble.s @6 ; Yes? Then set it.
|
|
Moveq #(dr1000 << DataRateOffset) | \ ;
|
|
(ApplePreComp << PreCompOffset), \ ;
|
|
D1 ; Must be extended density.
|
|
@6 Move.b D1,fdcDRR(A0) ; Set data rate/pre-comp.
|
|
Bsr WriteToFDC ; Send mode byte.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Send it.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Waitx Bsr SenseInterrupt ; Check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Done?
|
|
Bne.s @ExitSetUp ; Change? Then Get out.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Waitx ; Loop
|
|
Move.w #noNybErr,fResult(A1) ; Set flag
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
@ExitSetUp Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Move.l (Sp)+,A2 ; Restore A2.
|
|
Rts ; And leave.
|
|
|
|
Blocks Dc.w 0,0,800,1600,1600,0,1440,2880,5760
|
|
|
|
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: CheckError
|
|
; Inputs: A1 -> Drive record.
|
|
; Outputs: D1 -> 0 - OK, ~0 - Error.
|
|
; Function: Look at status returned by FDC for error message.
|
|
; Called By: Prime, Verify.
|
|
;------------------------------------------------------------------------------
|
|
|
|
CheckError Move.b fStatus0(A1),D0 ; Status 0.
|
|
Move.w #tk0BadErr,D1 ; Assume drive error.
|
|
Btst #fFDDErr,D0 ; Equipment check?
|
|
Bne.s ExitError ; Yes? The leave.
|
|
|
|
Move.w #offLinErr,D1 ; Assume disk is ejected.
|
|
Btst #fReady,D0 ; Not ready?
|
|
Bne.s ExitError ; Yes? The leave.
|
|
|
|
Move.b fStatus2(A1),D0 ; Status 2.
|
|
Move.w #noDtaMkErr,D1 ; Assume deleted data mark.
|
|
Btst #fCntrlMrk,D0 ; Deleted data mark?
|
|
Bne.s ExitError ; Yes? Then leave.
|
|
|
|
Move.w #badDCksum,D1 ; Assume data checksum error.
|
|
Btst #fDataField,D0 ; Data checksum error?
|
|
Bne.s ExitError ; Yes? Then leave.
|
|
|
|
Move.w #seekErr,D1 ; Assume wrong cylinder.
|
|
Btst #fNoCylndr,D0 ; Wrong cylinder error?
|
|
Bne.s ExitError ; Yes? Then leave.
|
|
|
|
Move.w #seekErr,D1 ; Assume no data mark.
|
|
Btst #fBadCylndr,D0 ; Bad track?
|
|
Bne.s ExitError ; Yes? Then leave.
|
|
|
|
Move.w #noDtaMkErr,D1 ; Assume no data mark.
|
|
Btst #fNoDataAddrMrk,D0 ; No data mark error?
|
|
Bne.s ExitError ; Yes? Then leave.
|
|
|
|
Move.b fStatus1(A1),D0 ; Status 1.
|
|
Move.w #badCksmErr,D1 ; Assume address checksum error.
|
|
Btst #fDataErr,D0 ; Address checksum error?
|
|
Bne.s ExitError ; Yes? Then leave.
|
|
|
|
Move.w #wrUnderrun,D1 ; Assume underrun/overrun error.
|
|
Btst #fOvrRun,D0 ; Underrun/overrun error?
|
|
Bne.s ExitError ; Yes? Then leave.
|
|
|
|
Move.w #sectNFErr,D1 ; Assume sector not found.
|
|
Btst #fNoData,D0 ; Sector not found error?
|
|
Bne.s ExitError ; Yes? Then leave.
|
|
|
|
Move.w #wPrErr,D1 ; Assume write protected.
|
|
Btst #fWrtPrtct,D0 ; Write protected?
|
|
Bne.s ExitError ; Yes? Then leave.
|
|
|
|
Move.w #noAdrMkErr,D1 ; Assume no address mark.
|
|
Btst #fNoAddrMrk,D0 ; No address mark?
|
|
Bne.s ExitError ; Yes? Then leave.
|
|
|
|
Move.w #noErr,D1 ; Set no error.
|
|
ExitError Move.w D1,fResult(A1) ; Save result.
|
|
Rts ; Leave.
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: SeekToTrack
|
|
; Inputs: A0 -> FDC address.
|
|
; A1 -> Drive record.
|
|
; D2 -> Cylinder number.
|
|
; Outputs: D0 -> 0 - OK, ~0 - Error.
|
|
; Function: Seek to a given cylinder.
|
|
; Called By: Prime, Format, Verify.
|
|
;------------------------------------------------------------------------------
|
|
|
|
SeekToTrack Tst.b fMotor(A1) ; Drive turned on?
|
|
Bne.s @DoSeek ; Yes? Then do seek.
|
|
Bsr EnableMotor ; Turn motor on.
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne.s @Exit ; Yes? Out of here.
|
|
|
|
@DoSeek Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #Seek,D0 ; Seek command.
|
|
Bsr WriteToFDC ; Send byte.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Bsr WriteToFDC ; Send byte.
|
|
Move.b D2,D0 ; Cylinder number.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Send byte.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Wait Bsr SenseInterrupt ; Check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Change yet?
|
|
Bne.s @SecondWait ; Yes? Go Check second conditon.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait ; Loop
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
|
|
@SecondWait Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
Move.b fdcMSR(A0),D0 ; Status?
|
|
And.b #(1 << fDrv0Bsy)| \ ;
|
|
(1 << fDrv1Bsy), \ ;
|
|
D0 ; Still seeking?
|
|
Beq.s @Exit ; Yes? Then loop.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @SecondWait ; Loop
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Move.w #cantStepErr,fResult(A1) ;
|
|
@Exit Rts ; And leave.
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: Track0
|
|
; Inputs: A0 -> FDC address.
|
|
; A1 -> Drive record.
|
|
; Outputs: D0 -> 0 - OK, ~0 - Error.
|
|
; Function: Recalibrate to cylinder 0.
|
|
; Called By: SetUpFDC.
|
|
;------------------------------------------------------------------------------
|
|
|
|
Track0 Tst.b fMotor(A1) ; Drive turned on?
|
|
Bne.s @DoRecalibrate ; Yes? Then do recalibrate.
|
|
Bsr EnableMotor ; Turn motor on.
|
|
Tst.w fResult(A1) ; Error?
|
|
Bne.s @Exit ; Yes? Out of here.
|
|
|
|
@DoRecalibrate Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #Recalibrate,D0 ; Recalibrate command.
|
|
Bsr WriteToFDC ; Send byte.
|
|
Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Send byte.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Wait Bsr SenseInterrupt ; Check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Done?
|
|
Bne.s @Done ; Change? Then Get out.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait ; Loop
|
|
Move.b #1,D0 ; Report error.
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
@Done Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
@Exit Rts ; And leave.
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: EnableMotor
|
|
; Inputs: A0 -> FDC address.
|
|
; A1 -> Drive record.
|
|
; Outputs: None.
|
|
; Function: Turn motor on to drive.
|
|
; Called By: SetUpFDC, SeekToTrack, Track0.
|
|
;------------------------------------------------------------------------------
|
|
|
|
EnableMotor Move.b #-1,fStatus0(A1) ; Set flag.
|
|
Move.b #SetMotorOn,D0 ; Motor on command.
|
|
Bsr WriteToFDC ; Send command.
|
|
@0 Move.b fDriveSelect(A1),D0 ; Drive select.
|
|
Move.b #DisableFDCInt,VIA2IER ; Disable interrupts. <SM23>
|
|
Bsr WriteToFDC ; Send drive ID.
|
|
Move.b #1,fMotor(A1) ; Set flag.
|
|
|
|
Moveq #0,D1 ; Init counter. <SM9>
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@Wait Bsr SenseInterrupt ; Check for interrupt. <SM23>
|
|
Cmp.b #-1,fStatus0(A1) ; Done?
|
|
Bne.s @Done ; Change? Then Get out.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @Wait ; Loop
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
@Done Move.b #EnableFDCInt,VIA2IER ; Enable interrupts. <SM23>
|
|
@Exit Rts ; And leave.
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: WriteToFDC
|
|
; Inputs: A0 -> FDC address.
|
|
; D0 -> Byte to write.
|
|
; Outputs: D1 -> 0 - OK, ~0 - Error.
|
|
; Function: Write byte to FDC.
|
|
;------------------------------------------------------------------------------
|
|
|
|
WriteToFDC Moveq #0,D1 ; Init counter.
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
Asl.l #1,D1 ; DBRA/512 Millisecond. <LW15>
|
|
Swap D0 ; Data in high byte.
|
|
@WriteLoop Move.b fdcMSR(A0),D0 ; Main status.
|
|
Bpl.s @1 ; Not ready? Loop.
|
|
Btst #fDIO,D0 ; Correct direction?
|
|
Bne.s @1 ; No? Then loop.
|
|
Swap D0 ; Data in low word.
|
|
Move.b D0,fdcDATA(A0) ; Write byte.
|
|
@WaitLoop Tst.b fdcMSR(A0) ; Status.
|
|
Bpl.s @0 ; Wait for FDC to take it.
|
|
Moveq #0,D1 ; Set noErr.
|
|
@ExitWrite Rts ; And return. <SM6>
|
|
@0 Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @WaitLoop ;
|
|
Bra.s @2 ;
|
|
|
|
@1 Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @WriteLoop ; Loop.
|
|
@2 Moveq #1,D1 ; Set flag.
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bra.s @ExitWrite ; And leave. <SM6>
|
|
;------------------------------------------------------------------------------
|
|
; Routine: ReadFromFDC
|
|
; Inputs: A0 -> FDC address.
|
|
; Outputs: D0 -> Byte read.
|
|
; D1 -> 0 - OK, ~0 - Error.
|
|
; Function: Read byte from FDC.
|
|
;------------------------------------------------------------------------------
|
|
|
|
ReadFromFDC Moveq #0,D0 ; Init counter.
|
|
Move.w TimeDBRA,D0 ; DBRA/Millisecond.
|
|
Asl.l #8,D0 ; DBRA/256 Millisecond.
|
|
Asl.l #1,D0 ; DBRA/512 Millisecond. <LW15>
|
|
@ReadLoop Move.b fdcMSR(A0),D1 ; Main status.
|
|
Bpl.s @0 ; Not ready? Check timeout.
|
|
Btst #fCmdBsy,D1 ; Command still busy?
|
|
Beq.s @1 ; No? Then bail out.
|
|
Btst #fDIO,D1 ; Correct direction?
|
|
Beq.s @0 ; No? Check timeout.
|
|
Move.b fdcDATA(A0),D0 ; Yes? Read byte.
|
|
Moveq #0,D1 ; Set noErr.
|
|
@ExitRead Rts ; And return. <SM6>
|
|
@0 Subq.l #1,D0 ; Decrement counter.
|
|
Bne.s @ReadLoop ; Loop.
|
|
@1 Moveq #1,D1 ; Set flag.
|
|
If Break Then
|
|
_debugger
|
|
EndIf
|
|
Bra.s @ExitRead ; And leave. <SM6>
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: FDCDMAInt
|
|
; Inputs: None.
|
|
; Outputs: None.
|
|
; Function: FDC DMA Interrupt handler.
|
|
; Since the floppy does not use the DMA interrupt this is here to
|
|
; handle the case of a DMA bus error. Added in <LW12>
|
|
;------------------------------------------------------------------------------
|
|
FDCDMAInt Move.l A0,-(Sp) ; Save registers.
|
|
|
|
Movea.l #pscControl32,A0 ; PSC control register.
|
|
Move.w #(1 << pscSWReset) | \ ;
|
|
(1 << pscSense), \ ;
|
|
(A0) ; Reset channel.
|
|
|
|
; Compile out debug string <LW28>
|
|
If Break Then
|
|
pea @BusErrStr ;
|
|
_DebugStr ; Call Macsbug.
|
|
EndIf
|
|
|
|
@ExitFDCDMA Move.l (Sp)+,A0 ; Restore registers.
|
|
Rts
|
|
|
|
; Compile out string <LW28>
|
|
If Break Then
|
|
@BusErrStr dc.b 'A DMA bus error have occur'
|
|
EndIf
|
|
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: Handler
|
|
; Inputs: None.
|
|
; Outputs: None.
|
|
; Function: FDC Interrupt handler.
|
|
;------------------------------------------------------------------------------
|
|
|
|
HandlerRegs REG A0-A2/D0-D2
|
|
|
|
Handler Movem.l HandlerRegs,-(Sp) ; Save registers.
|
|
Btst.b #FDCInt,VIA2IFR ; FDC causing interrupt? <SM1> RLM
|
|
Beq.s @ExitHandler ; No? Leave.
|
|
|
|
Movea.l NewAgeVars,A1 ; Pointer to DCE.
|
|
Movea.l dCtlStorage(A1),A1 ; Floppy info records.
|
|
|
|
Movea.l #NewAge32,A0 ; Base address of NewAge.
|
|
Move.b #SenseIntStatus,D0 ; Sense interrupt status command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Bsr ReadFromFDC ; Get Status0.
|
|
Move.b D0,D2 ; Save it.
|
|
|
|
And.w #(1 << fDrive),D0 ; Drive number.
|
|
Move.l A1,D1 ; Does drive record exist?
|
|
Beq.s @ExitHandler ; No? Then leave.
|
|
Cmp.w fDriveSelect(A1),D0 ; Are we looking at the right record?
|
|
Beq.s @Check ; Yes? Then get rest of status.
|
|
Move.l fNextRecord(A1),D1 ; Does drive record exist?
|
|
Beq.s @ExitHandler ; No? Then leave.
|
|
Move.l D1,A1 ; Pointer to next drive record.
|
|
|
|
@Check Move.b D2,fStatus0(A1) ; Save status 0.
|
|
Move.b D2,D0 ; Status 0.
|
|
Lsr.b #IntStatOffset,D0 ; Int code in bottom 2 bits.
|
|
Beq.s @CheckComplete ; Normal? Then check for a completion.
|
|
Cmp.b #AbnrmlTrmnt,D0 ; Abnormal?
|
|
Beq.s @CheckComplete ; Then check for a completion.
|
|
Cmp.b #CssttChng,D0 ; Cassette change?
|
|
Beq.s @Cassette ; Yes? Then check for insert or eject.
|
|
Bsr ResetFDC ; Illegal command? Reset FDC.
|
|
Clr.b fMotor(A1) ; Clear flag. <SM4> ws
|
|
Bra.s @ExitHandler ; And leave.
|
|
|
|
@Cassette Move.w #fNone,fFormat(A1) ; Assume disk is being ejected.
|
|
Btst #fDiskStatus,D2 ; Floppy in?
|
|
Bne.s @ExitHandler ; No? Then leave.
|
|
|
|
Tst.b DTInstalled(A1) ;
|
|
Bne.s @ExitHandler ;
|
|
Lea DTQE(A1), A0 ; A0->deferred task queue element.
|
|
Lea DeferPost,A2 ; Get DT address.
|
|
Move.l A2, dtAddr(A0) ; Set address of DT.
|
|
Move.l A1, dtParm(A0) ; Set variable pointer to Newage vars.
|
|
Addq.b #1,DTInstalled(A1) ;
|
|
|
|
MoveA.l JDTInstall, A1 ;
|
|
Jsr (A1) ; install deferred task
|
|
|
|
@ExitHandler Movem.l (Sp)+,HandlerRegs ; Restore registers.
|
|
Rts
|
|
|
|
@CheckComplete Move.l fCompletion(A1),D0 ; Completion routine.
|
|
Beq.s @ExitHandler ; None? Then leave.
|
|
Clr.l fCompletion(A1) ; Clear pointer.
|
|
Move.l D0,A2 ; Pointer to routine.
|
|
Jsr (A2) ; Call it.
|
|
Bra.s @ExitHandler ; And leave.
|
|
|
|
DeferPost Movem.l D0/A0, -(SP) ;
|
|
Move.w fDriveNum(A1),D0 ; Drive number.
|
|
Move.l fDriveQueue(A1),A0 ; Drive queue.
|
|
Move.b #1,-3(A0) ; Set disk in flag.
|
|
Move.w #mUnknown,fMediaType(A1)
|
|
Ext.l D0 ; Drive number.
|
|
Lea diskInsertEvt,A0 ; Event number.
|
|
_PostEvent ; Post it.
|
|
Clr.b DTInstalled(A1) ;
|
|
Movem.l (Sp)+,D0/A0 ; Restore registers.
|
|
Rts
|
|
|
|
; Added SenseInterrupt because when Newage interrupts After a command, it does not always have
|
|
; the Command Busy bit clear, as it should. This routine will wait until the bit is cleared
|
|
; before doing anything else. <SM23>
|
|
;------------------------------------------------------------------------------
|
|
; Routine: SenseInterrupt
|
|
; Inputs: None.
|
|
; Outputs: None.
|
|
; Function: FDC Interrupt handler.
|
|
;------------------------------------------------------------------------------
|
|
SenseRegs REG A0-A2/D0-D3
|
|
|
|
SenseInterrupt Movem.l SenseRegs,-(Sp) ; Save registers.
|
|
|
|
@CheckFDC Btst.b #FDCInt,VIA2IFR ; FDC causing interrupt?
|
|
Beq.s @ExitSense ; No? Leave.
|
|
|
|
Movea.l NewAgeVars,A1 ; Pointer to DCE.
|
|
Movea.l dCtlStorage(A1),A1 ; Floppy info records.
|
|
Movea.l #NewAge32,A0 ; Base address of NewAge.
|
|
@Ack Move.b fdcMSR(A0),D0 ; Main status register.
|
|
Btst #fCmdBsy,D0 ; Busy bit set yet?
|
|
Bne.s @Ack ; No? then wait until it is!
|
|
|
|
@SenseInt Move.b #SenseIntStatus,D0 ; Sense interrupt status command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Bsr ReadFromFDC ; Get Status0.
|
|
Move.b D0,D2 ; Save it.
|
|
Btst #fSeek,D2 ; Was it a seek of recal command?
|
|
Beq.s @nocylinder ; Only one result byte if it is not.
|
|
Bsr ReadFromFDC ; Get PCN (present cylinder number).
|
|
Move.b D0,D1 ; Save PCN.
|
|
|
|
@nocylinder Move.b D2,D0 ; Status 0.
|
|
And.w #(1 << fDrive),D0 ; Drive number.
|
|
Move.l A1,D3 ; Does drive record exist?
|
|
Beq.s @ExitSense ; No? Then leave.
|
|
Cmp.w fDriveSelect(A1),D0 ; Are we looking at the right record?
|
|
Beq.s @Check ; Yes? Then get rest of status.
|
|
Move.l fNextRecord(A1),D3 ; Does drive record exist?
|
|
Beq.s @ExitSense ; No? Then leave.
|
|
Move.l D3,A1 ; Pointer to next drive record.
|
|
|
|
@Check Move.b D2,fStatus0(A1) ; Save status 0.
|
|
Btst #fSeek,D2 ; Was it a seek of recal command?
|
|
Beq.s @ChkErr ; Only one result byte if it is not.
|
|
Move.b D1,fCylinder(A1) ; Save cylinder number.
|
|
|
|
@ChkErr
|
|
; Move.w #tk0BadErr,D0 ; Assume drive error.
|
|
; Btst #fFDDErr,D2 ; Equipment check?
|
|
; Bne.s @TestTrmnt ; Yes? The leave.
|
|
Move.w #offLinErr,D0 ; Assume disk is ejected.
|
|
Btst #fReady,D2 ; Not ready?
|
|
Bne.s @TestTrmnt ; Yes? The leave.
|
|
Clr.w D0 ; Set no error.
|
|
|
|
@TestTrmnt Move.w D0,fResult(A1) ; Save result.
|
|
Move.b D2,D0 ; Status 0.
|
|
Lsr.b #IntStatOffset,D0 ; Int code in bottom 2 bits.
|
|
Beq.s @CheckComplete ; Normal? Then check for a completion.
|
|
Cmp.b #AbnrmlTrmnt,D0 ; Abnormal?
|
|
Beq.s @CheckComplete ; Then check for a completion.
|
|
|
|
@ExitSense Movem.l (Sp)+,SenseRegs ; Restore registers.
|
|
Rts
|
|
|
|
@CheckComplete Move.l fCompletion(A1),D0 ; Completion routine.
|
|
Beq.s @ExitSense ; None? Then leave.
|
|
Clr.l fCompletion(A1) ; Clear pointer.
|
|
Move.l D0,A2 ; Pointer to routine.
|
|
Jsr (A2) ; Call it.
|
|
Bra.s @ExitSense ; And leave.
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: GetResult
|
|
; Inputs: None.
|
|
; Outputs: None.
|
|
; Function: FDC Interrupt handler.
|
|
;------------------------------------------------------------------------------
|
|
ResultRegs REG A0-A2/D0-D3
|
|
|
|
GetResult Movem.l ResultRegs,-(Sp) ; Save registers.
|
|
|
|
@CheckFDC Btst.b #FDCInt,VIA2IFR ; FDC causing interrupt?
|
|
Beq @ExitResult ; No? Leave.
|
|
|
|
Movea.l NewAgeVars,A1 ; Pointer to DCE.
|
|
Movea.l dCtlStorage(A1),A1 ; Floppy info records.
|
|
Movea.l #NewAge32,A0 ; Base address of NewAge.
|
|
@Ack Move.b fdcMSR(A0),D0 ; Main status register.
|
|
Btst #fCmdBsy,D0 ; Busy bit Reset yet?
|
|
Beq.s @Ack ; No? then wait until it is!
|
|
|
|
Bsr ReadFromFDC ; Get status register 0.
|
|
Move.b D0,D1 ; Copy status.
|
|
And.w #1,D1 ; Drive number.
|
|
Move.l A1,D3 ; Does drive record exist?
|
|
Beq.s @ExitResult ; No? Then leave.
|
|
Cmp.w fDriveSelect(A1),D1 ; Are we looking at the right record?
|
|
Beq.s @0 ; Yes? Then get rest of status.
|
|
Move.l fNextRecord(A1),D3 ; Does drive record exist?
|
|
Beq.s @ExitResult ; No? Then leave.
|
|
Move.l D3,A1 ; Pointer to next drive record.
|
|
|
|
@0 Move.b D0,fStatus0(A1) ; Save it.
|
|
Move.b fdcMSR(A0),D0 ; Check for end of result.
|
|
Btst #fCmdBsy,D0 ; Done?
|
|
Beq.s @ExitResult ; Yes? Then leave.
|
|
|
|
Bsr ReadFromFDC ; Get status register 1.
|
|
Move.b D0,fStatus1(A1) ; Save it.
|
|
|
|
Bsr ReadFromFDC ; Get status register 2.
|
|
Move.b D0,fStatus2(A1) ; Save it.
|
|
|
|
Bsr ReadFromFDC ; Get cylinder number.
|
|
Move.b D0,fCylinder(A1) ; Save it.
|
|
|
|
Bsr ReadFromFDC ; Get head number.
|
|
Move.b D0,fHead(A1) ; Save it.
|
|
|
|
Bsr ReadFromFDC ; Get sector number.
|
|
Move.b D0,fSector(A1) ; Save it.
|
|
|
|
Bsr ReadFromFDC ; Get format code.
|
|
Move.b D0,fData(A1) ; Save it.
|
|
|
|
Moveq #0,D1 ; Init counter.
|
|
Move.w TimeDBRA,D1 ; DBRA/Millisecond.
|
|
Asl.l #8,D1 ; DBRA/256 Millisecond.
|
|
@1 Move.b fdcMSR(A0),D0 ; Main status.
|
|
Btst #fCmdBsy,D0 ; Still busy?
|
|
Beq.s @2 ; No? Then leave.
|
|
Subq.l #1,D1 ; Decrement counter.
|
|
Bne.s @1 ; Loop till timeout.
|
|
|
|
; Compile out debugger <LW28>
|
|
If Break Then
|
|
_Debugger ; Die for now.
|
|
EndIf
|
|
|
|
@2 Bsr CheckError ; Check I/O result.
|
|
|
|
@CheckComplete Move.l fCompletion(A1),D0 ; Completion routine.
|
|
Beq.s @ExitResult ; None? Then leave.
|
|
Clr.l fCompletion(A1) ; Clear pointer.
|
|
Move.l D0,A2 ; Pointer to routine.
|
|
Jsr (A2) ; Call it.
|
|
|
|
@ExitResult Movem.l (Sp)+,ResultRegs ; Restore registers.
|
|
Rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Routine: Initialize FDC
|
|
; Inputs: A0 -> FDC address.
|
|
; Outputs: None.
|
|
; Function: Reset FDC during open or after serious error.
|
|
; Called by: Open, Handler.
|
|
;------------------------------------------------------------------------------
|
|
|
|
ResetFDC Move.b #(1 << fReset) | \ ;
|
|
(NoPreComp << PreCompOffset) | \ ;
|
|
(dr500 << DataRateOffset), \ ;
|
|
fdcDRR(A0) ; Reset the FDC.
|
|
Move.b #SelectDriveType,D0 ; Select drive type command.
|
|
Bsr WriteToFDC ; Send command.
|
|
Move.b #drvApple,D0 ; Apple drive type.
|
|
Bsr WriteToFDC ; Select it.
|
|
|
|
Move.b #Configure,D0 ; Configure command.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
Clr.b D0 ; ???
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
Move.b #$F,D0 ; Set FIFO to 0 bytes. <SM9>
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
Move.b #$0,D0 ; Start precomp at this track.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
|
|
Move.b #Specify,D0 ; Specify command.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
Moveq #0,D0 ; Step rate/head unload.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
Moveq #0,D0 ; Head load/DMA.
|
|
Bsr WriteToFDC ; Send byte to FDC.
|
|
Rts ; Return.
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Data: Icon data and “where” string.
|
|
;------------------------------------------------------------------------------
|
|
|
|
STRING PASCAL
|
|
FrigidaireInternal
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000010000111111111111100100000
|
|
Dc.l %00000011000100000000000100100000
|
|
Dc.l %00111111100100111111100100100000
|
|
Dc.l %00000011000100000000000100100000
|
|
Dc.l %00000010000111111111111100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000111111111111100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000001100000000000110110000
|
|
Dc.l %00000000001100000000000100110000
|
|
Dc.l %00000000001100000110000110110000
|
|
Dc.l %00000000001100000110000100110000
|
|
Dc.l %00000000001100000000000110110000
|
|
Dc.l %00000000001100000000000100110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111100000000111110000
|
|
Dc.l %00000000000000000000000000000000
|
|
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000110000111111111111111100000
|
|
Dc.l %00000111000111111111111111100000
|
|
Dc.l %01111111100111111111111111100000
|
|
Dc.l %01111111110111111111111111100000
|
|
Dc.l %01111111100111111111111111100000
|
|
Dc.l %00000111000111111111111111100000
|
|
Dc.l %00000110000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111100000000111110000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l 'Internal'
|
|
|
|
FrigidaireExternal
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000010000111111111111100100000
|
|
Dc.l %00000011000100000000000100100000
|
|
Dc.l %00111111100100111111100100100000
|
|
Dc.l %00000011000100000000000100100000
|
|
Dc.l %00000010000111111111111100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000111111111111100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000000100000000000100100000
|
|
Dc.l %00000000001100000000000110110000
|
|
Dc.l %00000000001100000000000100110000
|
|
Dc.l %00000000001100000110000110110000
|
|
Dc.l %00000000001100000110000100110000
|
|
Dc.l %00000000001100000000000110110000
|
|
Dc.l %00000000001100000000000100110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111100000000111110000
|
|
Dc.l %00000000000000000000000000000000
|
|
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000110000111111111111111100000
|
|
Dc.l %00000111000111111111111111100000
|
|
Dc.l %01111111100111111111111111100000
|
|
Dc.l %01111111110111111111111111100000
|
|
Dc.l %01111111100111111111111111100000
|
|
Dc.l %00000111000111111111111111100000
|
|
Dc.l %00000110000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000000111111111111111100000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111111111111111110000
|
|
Dc.l %00000000001111100000000111110000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l 'External'
|
|
|
|
QFCInternal
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000001000000
|
|
Dc.l %00000000000000000000000001000000
|
|
Dc.l %00000000000000000000000001000000
|
|
Dc.l %00000000000000000000000001000000
|
|
Dc.l %00000000000000000000000111110000
|
|
Dc.l %00000000000000000000000011100000
|
|
Dc.l %00000000000000000000000001000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01000000001000000000100000000010
|
|
Dc.l %01011000001000000000101111111010
|
|
Dc.l %01011000001000000000100000000010
|
|
Dc.l %01000000001000000000100000000010
|
|
Dc.l %01001000001000000000100000011010
|
|
Dc.l %01111111111111111111111111100110
|
|
Dc.l %00111111111111111111111111111100
|
|
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l 'Internal'
|
|
|
|
QFCExternal
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000001000000
|
|
Dc.l %00000000000000000000000001000000
|
|
Dc.l %00000000000000000000000001000000
|
|
Dc.l %00000000000000000000000001000000
|
|
Dc.l %00000000000000000000000111110000
|
|
Dc.l %00000000000000000000000011100000
|
|
Dc.l %00000000000000000000000001000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01000000001000000000100000000010
|
|
Dc.l %01011000001000000000101111111010
|
|
Dc.l %01011000001000000000100000000010
|
|
Dc.l %01000000001000000000100000000010
|
|
Dc.l %01001000001000000000100000011010
|
|
Dc.l %01111111111111111111111111100110
|
|
Dc.l %00111111111111111111111111111100
|
|
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %00000000000000000000000000000000
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l %01111111111111111111111111111110
|
|
Dc.l 'External'
|
|
|
|
MediaIcon Dc.l %01111111111111111111111111111000
|
|
Dc.l %10000001000000000000000100000100
|
|
Dc.l %10000001000000000111000100000010
|
|
Dc.l %10000001000000001000100100000001
|
|
Dc.l %10000001000000001000100100000001
|
|
Dc.l %10000001000000001000100100000001
|
|
Dc.l %10000001000000001000100100000001
|
|
Dc.l %10000001000000001000100100000001
|
|
Dc.l %10000001000000000111000100000001
|
|
Dc.l %10000001000000000000000100000001
|
|
Dc.l %10000000111111111111111000000001
|
|
Dc.l %10000000000000000000000000000001
|
|
Dc.l %10000000000000000000000000000001
|
|
Dc.l %10000000000000000000000000000001
|
|
Dc.l %10000000000000000000000000000001
|
|
Dc.l %10000000000000000000000000000001
|
|
Dc.l %10000000000000000000000000000001
|
|
Dc.l %10000000000000000000000000000001
|
|
Dc.l %10000111111111111111111111100001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %10001000000000000000000000010001
|
|
Dc.l %01111111111111111111111111111110
|
|
|
|
Dc.l %01111111111111111111111111111000
|
|
Dc.l %11111111111111111111111111111100
|
|
Dc.l %11111111111111111111111111111110
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %11111111111111111111111111111111
|
|
Dc.l %01111111111111111111111111111110
|
|
|
|
Dc.l 'Internal'
|
|
|
|
|
|
|
|
|
|
|
|
END
|