9da0e1f5c5
controllers. Supports GCR-encoded disks only (400K and 800K); neither of the 1.44M formats will work until someone figures out how to drive the SWIM and its descendants. This code was written by Hauke Fath, and had only minor touchup (mostly KNF) by me.
1440 lines
32 KiB
ArmAsm
1440 lines
32 KiB
ArmAsm
/* $Id: iwm.s,v 1.1 1999/02/18 07:38:26 scottr Exp $ */
|
||
|
||
/*
|
||
* Copyright (c) 1996-98 Hauke Fath. All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
* 3. The name of the author may not be used to endorse or promote products
|
||
* derived from this software without specific prior written permission.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
|
||
/*
|
||
* iwm.s -- low level routines for Sony floppy disk access.
|
||
* The present implementation supports the 800K GCR format on non-DMA
|
||
* machines.
|
||
*
|
||
* The IWM and SWIM chips run in polled mode; they are not capable of
|
||
* interrupting the CPU. That's why interrupts need only be blocked
|
||
* when there is simply no time for interrupt routine processing,
|
||
* i.e. during data transfers.
|
||
*
|
||
* o The local routines do not block any interrupts.
|
||
*
|
||
* o The iwmXXX() routines that set/get IWM or drive settings are not
|
||
* time critical and do not block interrupts.
|
||
*
|
||
* o The iwmXXX() routines that are called to perform data transfers
|
||
* block all interrupts because otherwise the current sector data
|
||
* would be lost.
|
||
* The old status register content is stored on the stack.
|
||
*
|
||
* o As a special case iwmReadSectHdr() must run with interrupts disabled
|
||
* (it transfers data). Depending on the needs of the caller, it
|
||
* may be necessary to block interrupts after completion of the routine
|
||
* so interrupt handling is left to the caller.
|
||
*
|
||
* If we wanted to deal with incoming serial data / serial interrupts,
|
||
* we would have to either call zshard(0) {mac68k/dev/zs.c} or
|
||
* zsc_intr_hard(0) {sys/dev/ic/z8530sc.c}. Or we would have to roll our
|
||
* own as both of the listed function calls look rather expensive compared
|
||
* to a 'tst.b REGADDR ; bne NN'.
|
||
*/
|
||
|
||
#include <m68k/asm.h>
|
||
|
||
#include "iwm_regs.s"
|
||
|
||
#define USE_DELAY 0 /* "1" bombs for unknown reasons */
|
||
|
||
|
||
/*
|
||
* References to global name space
|
||
*/
|
||
.extern _TimeDBRA | in mac68k/macrom.c
|
||
.extern _IWMBase | in mac68k/machdep.c
|
||
.extern _VIA1Base |
|
||
|
||
|
||
.data
|
||
|
||
diskTo:
|
||
/*
|
||
* Translation table from 'disk bytes' to 6 bit 'nibbles',
|
||
* taken from the .Sony driver.
|
||
* This could be made a loadable table (via ioctls) to read
|
||
* e.g. ProDOS disks (there is a hook for such a table in .Sony).
|
||
*/
|
||
.byte /* 90 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01
|
||
.byte /* 98 */ 0xFF, 0xFF, 0x02, 0x03, 0xFF, 0x04, 0x05, 0x06
|
||
.byte /* A0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x08
|
||
.byte /* A8 */ 0xFF, 0xFF, 0xFF, 0x09, 0x0A, 0x0B, 0x0C, 0x0D
|
||
.byte /* B0 */ 0xFF, 0xFF, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
|
||
.byte /* B8 */ 0xFF, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A
|
||
.byte /* C0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||
.byte /* C8 */ 0xFF, 0xFF, 0xFF, 0x1B, 0xFF, 0x1C, 0x1D, 0x1E
|
||
.byte /* D0 */ 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x20, 0x21
|
||
.byte /* D8 */ 0xFF, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28
|
||
.byte /* E0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x29, 0x2A, 0x2B
|
||
.byte /* E8 */ 0xFF, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32
|
||
.byte /* F0 */ 0xFF, 0xFF, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38
|
||
.byte /* F8 */ 0xFF, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
|
||
|
||
hdrLeadIn:
|
||
.byte 0xD5, 0xAA, 0x96
|
||
|
||
hdrLeadOut:
|
||
.byte 0xDE, 0xAA, 0xFF
|
||
|
||
dataLeadIn:
|
||
.byte 0xD5, 0xAA, 0xAD
|
||
|
||
dataLeadOut:
|
||
.byte 0xDE, 0xAA, 0xFF, 0xFF
|
||
|
||
|
||
toDisk:
|
||
/*
|
||
* Translation table from 6-bit nibbles [0x00..0x3f] to 'disk bytes'
|
||
*/
|
||
.byte /* 00 */ 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6
|
||
.byte /* 08 */ 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3
|
||
.byte /* 10 */ 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC
|
||
.byte /* 18 */ 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3
|
||
.byte /* 20 */ 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE
|
||
.byte /* 28 */ 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC
|
||
.byte /* 30 */ 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xf5, 0xF6
|
||
.byte /* 38 */ 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
|
||
|
||
syncPattern:
|
||
/*
|
||
* This sync pattern creates 4 sync chars with 10 bits each that look
|
||
* like 0011111111b (i.e. 0x0FF). As the IWM ignores leading zero
|
||
* bits, it locks on 0xFF after the third sync byte.
|
||
* For convenience, the bytes of the sector data lead-in
|
||
* (D5 AA AD) follow.
|
||
*/
|
||
.byte 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF
|
||
.byte 0xD5, 0xAA, 0xAD
|
||
|
||
|
||
|
||
.text
|
||
|
||
/*
|
||
* Register conventions:
|
||
* a0 IWM base address
|
||
* a1 VIA1 base address
|
||
*
|
||
* d0 return value (0 == no error)
|
||
*
|
||
* Upper bits in data registers that are not cleared give nasty
|
||
* (pseudo-) random errors when building an address. Make sure those
|
||
* registers are cleaned with a moveq before use!
|
||
*/
|
||
|
||
|
||
|
||
/**
|
||
** Export wrappers
|
||
**/
|
||
|
||
/*
|
||
* iwmQueryDrvFlags -- export wrapper for driveStat
|
||
*
|
||
* Parameters: stack l drive selector
|
||
* stack l register selector
|
||
* Returns: d0 flag
|
||
*/
|
||
ENTRY(iwmQueryDrvFlag)
|
||
link a6,#0
|
||
moveml d1/a0-a1,sp@-
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
movel a6@(8),d0 | Get drive #
|
||
beq quDrv00
|
||
cmpl #1,d0
|
||
beq quDrv01
|
||
|
||
bra quDone | Invalid drive #
|
||
|
||
quDrv00:
|
||
tstb a0@(intDrive) | SELECT; choose drive #0
|
||
bra queryDrv
|
||
|
||
quDrv01:
|
||
tstb a0@(extDrive) | SELECT; choose drive #1
|
||
|
||
queryDrv:
|
||
movel a6@(12),d0 | Get register #
|
||
bsr driveStat
|
||
|
||
quDone:
|
||
moveml sp@+,d1/a0-a1
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
/*
|
||
* iwmReadSectHdr -- read and decode the next available sector header.
|
||
*
|
||
* Parameters: stack l Address of sector header struct (I/O)
|
||
* b side (0, 1)
|
||
* b track (0..79)
|
||
* b sector (0..11)
|
||
* Returns: d0 result code
|
||
*/
|
||
ENTRY(iwmReadSectHdr)
|
||
link a6,#0
|
||
moveml d1-d5/a0-a4,sp@-
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
movel a6@(0x08),a4 | Get param block address
|
||
|
||
bsr readSectHdr
|
||
|
||
moveml sp@+,d1-d5/a0-a4
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
|
||
/**
|
||
** Exported functions
|
||
**/
|
||
|
||
/*
|
||
* iwmInit -- Initialize IWM chip.
|
||
*
|
||
* Parameters: -
|
||
* Returns: d0 result code
|
||
*/
|
||
ENTRY(iwmInit)
|
||
link a6,#0
|
||
moveml d2/a0,sp@-
|
||
movel _IWMBase,a0
|
||
|
||
/*
|
||
* Reset IWM to known state (clear disk I/O latches)
|
||
*/
|
||
tstb a0@(ph0L) | CA0
|
||
tstb a0@(ph1L) | CA1
|
||
tstb a0@(ph2L) | CA2
|
||
tstb a0@(ph3L) | LSTRB
|
||
|
||
tstb a0@(mtrOff) | ENABLE; make sure drive is off
|
||
tstb a0@(intDrive) | SELECT; choose drive 1
|
||
moveq #0x1F,d0 | XXX was 0x17 -- WHY!?
|
||
|
||
/*
|
||
* First do it quick...
|
||
*/
|
||
tstb a0@(q6H)
|
||
andb a0@(q7L),d0 | status register
|
||
tstb a0@(q6L)
|
||
cmpib #0x17,d0 | all is well??
|
||
beq initDone
|
||
|
||
/*
|
||
* If this doesn't succeed (e.g. drive still running),
|
||
* we do it thoroughly.
|
||
*/
|
||
movel #0x00080000,d2 | ca. 500,000 retries = 1.5 sec
|
||
initLp:
|
||
moveq #initIWMErr,d0 | Initialization error
|
||
subql #1,d2
|
||
bmi initErr
|
||
tstb a0@(mtrOff) | disable drive
|
||
tstb a0@(q6H)
|
||
moveq #0x3F,d0
|
||
andb a0@(q7L),d0
|
||
bclr #5,d0 | Reset bit 5 and set Z flag
|
||
| according to previous state
|
||
bne initLp | Loop if drive still on
|
||
cmpib #0x17,d0
|
||
beq initDone
|
||
moveb #0x17,a0@(q7H) | Init IWM
|
||
tstb a0@(q7L)
|
||
bra initLp
|
||
|
||
initDone:
|
||
tstb a0@(q6L) | Prepare IWM for data
|
||
moveq #0,d0 | noErr
|
||
|
||
initErr:
|
||
moveml sp@+,d2/a0
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
/*
|
||
* iwmCheckDrive -- Check if given drive is available and return bit vector
|
||
* with capabilities (SS/DS, disk inserted, ...)
|
||
*
|
||
* Parameters: stack l Drive number (0,1)
|
||
* Returns: d0 Bit 0 - 0 = Drive is single sided
|
||
* 1 - 0 = Disk inserted
|
||
* 2 - 0 = Motor is running
|
||
* 3 - 0 = Disk is write protected
|
||
* 4 - 0 = Disk is DD
|
||
* 31 - (-1) No drive / invalid drive #
|
||
*/
|
||
ENTRY(iwmCheckDrive)
|
||
link a6,#0
|
||
moveml d1/a0-a1,sp@-
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
moveq #-1,d1 | no drive
|
||
|
||
movel a6@(0x08),d0 | check drive #
|
||
beq chkDrv00
|
||
cmpl #1,d0
|
||
beq chkDrv01
|
||
|
||
bra chkDone | invalid drive #
|
||
|
||
chkDrv00:
|
||
tstb a0@(intDrive) | SELECT; choose drive #0
|
||
bra chkDrive
|
||
|
||
chkDrv01:
|
||
tstb a0@(extDrive) | SELECT; choose drive #1
|
||
|
||
chkDrive:
|
||
moveq #-2,d1 | error code
|
||
moveq #drvInstalled,d0 | Drive installed?
|
||
bsr driveStat
|
||
bmi chkDone | no drive
|
||
|
||
moveq #0,d1 | Drive found
|
||
tstb a0@(mtrOn) | ENABLE; activate drive
|
||
moveq #singleSided,d0 | Drive is single-sided?
|
||
bsr driveStat
|
||
bpl chkHasDisk
|
||
/*
|
||
* Drive is double-sided -- this is not really a surprise as the
|
||
* old ss 400k drive needs disk speed control from the Macintosh
|
||
* and we're not doing that here. Anyway - just in case...
|
||
* I am not sure m680x0 Macintoshes (x>0) support 400K drives at all
|
||
* due to their radically different sound support.
|
||
*/
|
||
bset #0,d1 | 1 = no.
|
||
chkHasDisk:
|
||
moveq #diskInserted,d0 | Disk inserted?
|
||
bsr driveStat
|
||
bpl chkMotorOn
|
||
bset #1,d1 | 1 = No.
|
||
bra chkDone
|
||
chkMotorOn:
|
||
moveq #drvMotorState,d0 | Motor is running?
|
||
bsr driveStat
|
||
bpl chkWrtProt
|
||
bset #2,d1 | 1 = No.
|
||
chkWrtProt:
|
||
moveq #writeProtected,d0 | Disk is write protected?
|
||
bsr driveStat
|
||
bpl chkDD_HD
|
||
bset #3,d1 | 1 = No.
|
||
chkDD_HD:
|
||
moveq #diskIsHD,d0 | Disk is HD? (was "drive installed")
|
||
bsr driveStat
|
||
bpl chkDone
|
||
bset #4,d1 | 1 = No.
|
||
chkDone:
|
||
movel d1,d0
|
||
moveml sp@+,d1/a0-a1
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
/*
|
||
* iwmDiskEject -- post EJECT command and toggle LSTRB line to give a
|
||
* strobe signal.
|
||
* IM III says pulse length = 500 ms, but we seem to get away with
|
||
* less delay; after all, we spin lock the CPU with it.
|
||
*
|
||
* Parameters: stack l drive number (0,1)
|
||
* a0 IWMBase
|
||
* a1 VIABase
|
||
* Returns: d0 result code
|
||
*/
|
||
ENTRY(iwmDiskEject)
|
||
link a6,#0
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
movel a6@(0x08),d0 | Get drive #
|
||
beq ejDrv00
|
||
cmpw #1,d0
|
||
beq ejDrv01
|
||
|
||
bra ejDone | Invalid drive #
|
||
|
||
ejDrv00:
|
||
tstb a0@(intDrive) | SELECT; choose drive #0
|
||
bra ejDisk
|
||
|
||
ejDrv01:
|
||
tstb a0@(extDrive) | SELECT; choose drive #1
|
||
ejDisk:
|
||
tstb a0@(mtrOn) | ENABLE; activate drive
|
||
|
||
moveq #motorOffCmd,d0 | Motor off
|
||
bsr driveCmd
|
||
|
||
moveq #diskInserted,d0 | Disk inserted?
|
||
bsr driveStat
|
||
bmi ejDone
|
||
|
||
moveq #ejectDiskCmd,d0 | Eject it
|
||
bsr selDriveReg
|
||
|
||
tstb a0@(ph3H) | LSTRB high
|
||
#if USE_DELAY
|
||
movel #1000,sp@- | delay 1 ms
|
||
jsr _C_LABEL(delay)
|
||
addqw #4,sp | clean up stack
|
||
#else
|
||
movew #1,d0
|
||
bsr iwmDelay
|
||
#endif
|
||
tstb a0@(ph3L) | LSTRB low
|
||
moveq #0,d0 | All's well...
|
||
ejDone:
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
/*
|
||
* iwmSelectDrive -- select internal (0) / external (1) drive.
|
||
*
|
||
* Parameters: stack l drive ID (0/1)
|
||
* Returns: d0 drive #
|
||
*/
|
||
ENTRY(iwmSelectDrive)
|
||
link a6,#0
|
||
moveml a0-a1,sp@-
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
movel a6@(8),d0 | Get drive #
|
||
bne extDrv
|
||
tstb a0@(intDrive)
|
||
bra sdDone
|
||
extDrv:
|
||
tstb a0@(extDrive)
|
||
sdDone:
|
||
moveml sp@+,a0-a1
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
/*
|
||
* iwmMotor -- switch drive motor on/off
|
||
*
|
||
* Parameters: stack l drive ID (0/1)
|
||
* stack l on(1)/off(0)
|
||
* Returns: d0 motor cmd
|
||
*/
|
||
ENTRY(iwmMotor)
|
||
link a6,#0
|
||
moveml a0-a1,sp@-
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
movel a6@(8),d0 | Get drive #
|
||
bne mtDrv1
|
||
tstb a0@(intDrive)
|
||
bra mtSwitch
|
||
mtDrv1:
|
||
tstb a0@(extDrive)
|
||
mtSwitch:
|
||
movel #motorOnCmd,d0 | Motor ON
|
||
tstl a6@(12)
|
||
bne mtON
|
||
movel #motorOffCmd,d0
|
||
mtON:
|
||
bsr driveCmd
|
||
|
||
moveml sp@+,a0-a1
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
/*
|
||
* iwmSelectSide -- select side 0 (lower head) / side 1 (upper head).
|
||
*
|
||
* This MUST be called immediately before an actual read/write access.
|
||
*
|
||
* Parameters: stack l side bit (0/1)
|
||
* Returns: -
|
||
*/
|
||
ENTRY(iwmSelectSide)
|
||
link a6,#0
|
||
moveml d1/a0-a1,sp@-
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
moveq #0x0B,d0 | Drive ready for reading?
|
||
bsr selDriveReg | (undocumented)
|
||
ss01:
|
||
bsr dstatus
|
||
bmi ss01
|
||
|
||
moveq #rdDataFrom0,d0 | Lower head
|
||
movel a6@(0x08),d1 | Get side #
|
||
beq ssSide0
|
||
moveq #rdDataFrom1,d0 | Upper head
|
||
ssSide0:
|
||
bsr driveStat
|
||
|
||
moveml sp@+,d1/a0-a1
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
/*
|
||
* iwmTrack00 -- move head to track 00 for drive calibration.
|
||
*
|
||
* XXX Drive makes funny noises during resore. Tune delay/retry count?
|
||
*
|
||
* Parameters: -
|
||
* Returns: d0 result code
|
||
*/
|
||
ENTRY(iwmTrack00)
|
||
link a6,#0
|
||
moveml d1-d4/a0-a1,sp@-
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
moveq #motorOnCmd,d0 | Switch drive motor on
|
||
bsr driveCmd
|
||
|
||
moveq #stepOutCmd,d0 | Step out
|
||
bsr driveCmd
|
||
|
||
movew #100,d2 | Max. tries
|
||
t0Retry:
|
||
moveq #atTrack00,d0 | Already at track 0?
|
||
bsr driveStat
|
||
bpl isTrack00 | Track 0 => Bit 7 = 0
|
||
|
||
moveq #doStepCmd,d0 | otherwise step
|
||
bsr driveCmd
|
||
movew #80,d4 | Retries
|
||
t0Still:
|
||
moveq #stillStepping,d0 | Drive is still stepping?
|
||
bsr driveStat
|
||
dbmi d4,t0Still
|
||
|
||
cmpiw #-1,d4
|
||
bne t002
|
||
|
||
moveq #cantStepErr,d0 | Not ready after many retries
|
||
bra t0Done
|
||
t002:
|
||
|
||
#if USE_DELAY
|
||
movel #15000,sp@-
|
||
jsr _C_LABEL(delay) | in mac68k/clock.c
|
||
addqw #4,sp
|
||
#else
|
||
movew #15,d0
|
||
bsr iwmDelay
|
||
#endif
|
||
|
||
dbra d2,t0Retry
|
||
|
||
moveq #tk0BadErr,d0 | Can't find track 00!!
|
||
bra t0Done
|
||
|
||
isTrack00:
|
||
moveq #0,d0
|
||
t0Done:
|
||
moveml sp@+,d1-d4/a0-a1
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
/*
|
||
* iwmSeek -- do specified # of steps (positive - in, negative - out).
|
||
*
|
||
* Parameters: stack l # of steps
|
||
* returns: d0 result code
|
||
*/
|
||
ENTRY(iwmSeek)
|
||
link a6,#0
|
||
moveml d1-d4/a0-a1,sp@-
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
moveq #motorOnCmd,d0 | Switch drive motor on
|
||
bsr driveCmd
|
||
|
||
moveq #stepInCmd,d0 | Set step IN
|
||
movel a6@(8),d2 | Get # of steps from stack
|
||
beq stDone | 0 steps? Nothing to do.
|
||
bpl stepOut
|
||
|
||
moveq #stepOutCmd,d0 | Set step OUT
|
||
negl d2 | Make # of steps positive
|
||
stepOut:
|
||
subql #1,d2 | Loop exits for -1
|
||
bsr driveCmd | Set direction
|
||
stLoop:
|
||
moveq #doStepCmd,d0
|
||
bsr driveCmd | Step one!
|
||
movew #80,d4 | Retries
|
||
st01:
|
||
moveq #stillStepping, d0 | Drive is still stepping?
|
||
bsr driveStat
|
||
dbmi d4,st01
|
||
|
||
cmpiw #-1,d4
|
||
bne st02
|
||
|
||
moveq #cantStepErr,d2 | Not ready after many retries
|
||
bra stDone
|
||
st02:
|
||
|
||
#if USE_DELAY
|
||
movel #30,sp@-
|
||
jsr _C_LABEL(delay) | in mac68k/clock.c
|
||
addqw #4,sp
|
||
#else
|
||
movew _TimeDBRA,d4 | dbra loops per ms
|
||
lsrw #5,d4 | DIV 32
|
||
st03: dbra d4,st03 | makes ca. 30 us
|
||
#endif
|
||
|
||
dbra d2,stLoop
|
||
|
||
moveq #0,d2 | All is well
|
||
stDone:
|
||
movel d2,d0
|
||
moveml sp@+,d1-d4/a0-a1
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
/*
|
||
* iwmReadSector -- read and decode the next available sector.
|
||
*
|
||
* TODO: Poll SCC as long as interrupts are disabled (see top comment)
|
||
* Add a branch for Verify (compare to buffer)
|
||
* Understand and document the checksum algorithm!
|
||
*
|
||
* Parameters: fp+08 l Address of sector data buffer (512 bytes)
|
||
* fp+12 l Address of sector header struct (I/O)
|
||
* Returns: d0 result code
|
||
* Local: fp-2 w CPU status register
|
||
* fp-3 b side,
|
||
* fp-4 b track,
|
||
* fp-5 b sector wanted
|
||
*/
|
||
ENTRY(iwmReadSector)
|
||
link a6,#-6
|
||
moveml d1-d7/a0-a5,sp@-
|
||
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
movel a6@(12),a4 | Addr of sector header struct
|
||
|
||
moveb a4@(0),a6@(-3) | Save side bit,
|
||
moveb a4@(1),a6@(-4) | track#,
|
||
/* moveb a4@(2),a6@(-5) | sector# */
|
||
|
||
movew sr,a6@(-2) | Save CPU status register
|
||
oriw #0x0700,sr | Block all interrupts
|
||
|
||
bsr readSectHdr | Get next available SECTOR header
|
||
bne rsDone | Return if error
|
||
|
||
/*
|
||
* Is this the right track & side? If not, return with error
|
||
*/
|
||
movel a6@(12),a4 | Sector header struct
|
||
|
||
moveb a4@(0),d1 | Get actual side
|
||
lsrb #3,d1 | "Normalize" side bit (to bit 0)
|
||
andb #1,d1
|
||
moveb a6@(-3),d2 | Get wanted side
|
||
eorb d1,d2 | Compare side bits
|
||
bne rsSeekErr | Should be equal!
|
||
|
||
moveb a6@(-4),d1 | Get wanted track#
|
||
cmpb a4@(1),d1 | Compare to the read header
|
||
beq rsGetSect
|
||
|
||
rsSeekErr:
|
||
moveq #seekErr,d0 | Wrong track or side found
|
||
bra rsDone
|
||
|
||
/*
|
||
* Check for sector data lead-in 'D5 AA AD'
|
||
* Registers:
|
||
* a0 points to data register of IWM
|
||
* a2 points to 'diskTo'<EFBFBD>translation table
|
||
* a4 points to tags buffer
|
||
*/
|
||
rsGetSect:
|
||
lea a4@(3),a4 | Beginning of tag buffer
|
||
moveq #50,d3 | Max. retries to seek
|
||
rsLeadIn:
|
||
lea dataLeadIn,a3 | Sector data lead-in
|
||
moveq #0x03,d4 | is 3 bytes long
|
||
rsLI1:
|
||
moveb a0@,d2 | Get next byte
|
||
bpl rsLI1
|
||
dbra d3,rsLI2
|
||
moveq #noDtaMkErr,d0 | Can't find a data mark
|
||
bra rsDone
|
||
|
||
rsLI2:
|
||
cmpb a3@+,d2
|
||
bne rsLeadIn | If ne restart scan
|
||
subqw #1,d4
|
||
bne rsLI1
|
||
/*
|
||
* We have found the lead-in. Now get the 12 tag bytes.
|
||
* (We leave a3 pointing to 'dataLeadOut' for later.)
|
||
*/
|
||
rsTagNyb0:
|
||
moveb a0@,d3 | Get a char,
|
||
bpl rsTagNyb0
|
||
moveb a2@(0,d3),a4@+ | remap and store it
|
||
|
||
moveq #0,d5 | Clear checksum registers
|
||
moveq #0,d6
|
||
moveq #0,d7
|
||
moveq #10,d4 | Loop counter
|
||
moveq #0,d3 | Data scratch reg
|
||
|
||
rsTags:
|
||
rsTagNyb1:
|
||
moveb a0@,d3 | Get 2 bit nibbles
|
||
bpl rsTagNyb1
|
||
moveb a2@(0,d3),d1 | Remap disk byte
|
||
rolb #2,d1
|
||
moveb d1,d2
|
||
andib #0xC0,d2 | Get top 2 bits for first byte
|
||
rsTagNyb2:
|
||
moveb a0@,d3 | Get first 6 bit nibble
|
||
bpl rsTagNyb2
|
||
orb a2@(0,d3),d2 | Remap it and complete first byte
|
||
|
||
moveb d7,d3 | The X flag bit (a copy of the carry
|
||
addb d7,d3 | flag) is added with the next addx
|
||
|
||
rolb #1,d7
|
||
eorb d7,d2
|
||
moveb d2,a4@+ | Store tag byte
|
||
addxb d2,d5 | See above
|
||
|
||
rolb #2,d1
|
||
moveb d1,d2
|
||
andib #0xC0,d2 | Get top 2 bits for second byte
|
||
rsTagNyb3:
|
||
moveb a0@,d3 | Get second 6 bit nibble
|
||
bpl rsTagNyb3
|
||
orb a2@(0,d3),d2 | remap it and complete byte
|
||
eorb d5,d2
|
||
moveb d2,a4@+ | Store tag byte
|
||
addxb d2,d6
|
||
|
||
rolb #2,d1
|
||
andib #0xC0,d1 | Get top 2 bits for third byte
|
||
rsTagNyb4:
|
||
moveb a0@,d3 | Get third 6 bit nibble
|
||
bpl rsTagNyb4
|
||
orb a2@(0,d3),d1 | remap it and complete byte
|
||
eorb d6,d1
|
||
moveb d1,a4@+ | Store tag byte
|
||
addxb d1,d7
|
||
|
||
subqw #3,d4 | Update byte counter (four 6&2 encoded
|
||
bpl rsTags | disk bytes make three data bytes).
|
||
/*
|
||
* Jetzt sind wir hier...
|
||
* ...und Thomas D. hat noch was zu sagen...
|
||
*
|
||
* We begin to read in the actual sector data.
|
||
*/
|
||
movel a6@(8),a4 | Sector data buffer
|
||
movew #0x01FE,d4 | Loop counter
|
||
|
||
rsData:
|
||
rsDatNyb1:
|
||
moveb a0@,d3 | Get 2 bit nibbles
|
||
bpl rsDatNyb1
|
||
moveb a2@(0,d3),d1 | Remap disk byte
|
||
rolb #2,d1
|
||
moveb d1,d2
|
||
andib #0xC0,d2 | Get top 2 bits for first byte
|
||
rsDatNyb2:
|
||
moveb a0@,d3 | Get first 6 bit nibble
|
||
bpl rsDatNyb2
|
||
orb a2@(0,d3),d2 | Remap it and complete first byte
|
||
|
||
moveb d7,d3 | The X flag bit (a copy of the carry
|
||
addb d7,d3 | flag) is added with the next addx
|
||
|
||
rolb #1,d7
|
||
eorb d7,d2
|
||
moveb d2,a4@+ | Store data byte
|
||
addxb d2,d5 | See above
|
||
|
||
rolb #2,d1
|
||
moveb d1,d2
|
||
andib #0xC0,d2 | Get top 2 bits for second byte
|
||
rsDatNyb3:
|
||
moveb a0@,d3 | Get second 6 bit nibble
|
||
bpl rsDatNyb3
|
||
orb a2@(0,d3),d2 | Remap it and complete byte
|
||
eorb d5,d2
|
||
moveb d2,a4@+ | Store data byte
|
||
addxb d2,d6
|
||
tstw d4
|
||
beq rsCkSum | Data read, continue with checksums
|
||
|
||
rolb #2,d1
|
||
andib #0xC0,d1 | Get top 2 bits for third byte
|
||
rsDatNyb4:
|
||
moveb a0@,d3 | Get third 6 bit nibble
|
||
bpl rsDatNyb4
|
||
orb a2@(0,d3),d1 | Remap it and complete byte
|
||
eorb d6,d1
|
||
moveb d1,a4@+ | Store data byte
|
||
addxb d1,d7
|
||
subqw #3,d4 | Update byte counter
|
||
bra rsData
|
||
|
||
/*
|
||
* Next read checksum bytes
|
||
* While reading the sector data, three separate checksums are
|
||
* maintained in D5/D6/D7 for the 1st/2nd/3rd data byte of each group.
|
||
*/
|
||
rsCkSum:
|
||
rsCkS1:
|
||
moveb a0@,d3 | Get 2 bit nibbles
|
||
bpl rsCkS1
|
||
moveb a2@(0,d3),d1 | Remap disk byte
|
||
bmi rsBadCkSum | Fault! (Bad read)
|
||
rolb #2,d1
|
||
moveb d1,d2
|
||
andib #0xC0,d2 | Get top 2 bits for first byte
|
||
rsCkS2:
|
||
moveb a0@,d3 | Get first 6 bit nibble
|
||
bpl rsCkS2
|
||
moveb a2@(0,d3),d3 | and remap it
|
||
bmi rsBadCkSum | Fault! ( > 0x3f is bad read)
|
||
orb d3,d2 | Merge 6&2
|
||
cmpb d2,d5 | Compare first checksum to D5
|
||
bne rsBadCkSum | Fault! (Checksum)
|
||
|
||
rolb #2,d1
|
||
moveb d1,d2
|
||
andib #0xC0,d2 | Get top 2 bits for second byte
|
||
rsCkS3:
|
||
moveb a0@,d3 | Get second 6 bit nibble
|
||
bpl rsCkS3
|
||
moveb a2@(0,d3),d3 | and remap it
|
||
bmi rsBadCkSum | Fault! (Bad read)
|
||
orb d3,d2 | Merge 6&2
|
||
cmpb d2,d6 | Compare second checksum to D6
|
||
bne rsBadCkSum | Fault! (Checksum)
|
||
|
||
rolb #2,d1
|
||
andib #0xC0,d1 | Get top 2 bits for second byte
|
||
rsCkS4:
|
||
moveb a0@,d3 | Get third 6 bit nibble
|
||
bpl rsCkS4
|
||
moveb a2@(0,d3),d3 | and remap it
|
||
bmi rsBadCkSum | Fault! (Bad read)
|
||
orb d3,d1 | Merge 6&2
|
||
cmpb d1,d7 | Compare third checksum to D7
|
||
beq rsLdOut | Fault! (Checksum)
|
||
|
||
rsBadCkSum:
|
||
moveq #badDCkSum,d0 | Bad data mark checksum
|
||
bra rsDone
|
||
|
||
rsBadDBtSlp:
|
||
moveq #badDBtSlp,d0 | One of the data mark bit slip
|
||
bra rsDone | nibbles was incorrect
|
||
|
||
|
||
/*
|
||
* We have gotten the checksums allright, now look for the
|
||
* sector data lead-out 'DE AA'
|
||
* (We have a3 still pointing to 'dataLeadOut'; this part of the
|
||
* table is used for writing to disk, too.)
|
||
*/
|
||
rsLdOut:
|
||
moveq #1,d4 | Is two bytes long {1,0}
|
||
rsLdOut1:
|
||
moveb a0@,d3 | Get token
|
||
bpl rsLdOut1
|
||
cmpb a3@+,d3
|
||
bne rsBadDBtSlp | Fault!
|
||
dbra d4,rsLdOut1
|
||
moveq #0,d0 | OK.
|
||
rsDone:
|
||
movew a6@(-2),sr | Restore interrupt mask
|
||
moveml sp@+,d1-d7/a0-a5
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
|
||
|
||
/*
|
||
* iwmWriteSector -- encode and write data to the specified sector.
|
||
*
|
||
* TODO: Poll SCC as long as interrupts are disabled (see top comment)
|
||
* Understand and document the checksum algorithm!
|
||
*
|
||
* Parameters: fp+8 l Address of sector data buffer (512 bytes)
|
||
* fp+12 l Address of sector header struct (I/O)
|
||
* Returns: d0 result code
|
||
*
|
||
* Local: fp-2 w CPU status register
|
||
* fp-3 b side,
|
||
* fp-4 b track,
|
||
* fp-5 b sector wanted
|
||
*/
|
||
ENTRY(iwmWriteSector)
|
||
link a6,#-6
|
||
moveml d1-d7/a0-a5,sp@-
|
||
|
||
movel _IWMBase,a0
|
||
movel _Via1Base,a1
|
||
|
||
movel a6@(12),a4 | Addr of sector header struct
|
||
|
||
moveb a4@(0),a6@(-3) | Save side bit,
|
||
moveb a4@(1),a6@(-4) | track#,
|
||
moveb a4@(2),a6@(-5) | sector#
|
||
|
||
movew sr,a6@(-2) | Save CPU status register
|
||
oriw #0x0700,sr | Block all interrupts
|
||
|
||
bsr readSectHdr | Get next available sector header
|
||
bne wsDone | Return if error
|
||
|
||
/*
|
||
* Is this the right track & side? If not, return with error
|
||
*/
|
||
movel a6@(12),a4 | Sector header struct
|
||
|
||
moveb a4@(0),d1 | Get side#
|
||
lsrb #3,d1 | "Normalize" side bit...
|
||
andb #1,d1
|
||
moveb a6@(-3),d2 | Get wanted side
|
||
eorb d1,d2 | Compare side bits
|
||
bne wsSeekErr
|
||
|
||
moveb a6@(-4),d1 | Get wanted track#
|
||
cmpb a4@(1),d1 | Compare to the read header
|
||
beq wsCompSect
|
||
|
||
wsSeekErr:
|
||
moveq #seekErr,d0 | Wrong track or side
|
||
bra wsDone
|
||
|
||
|
||
/*
|
||
* Are we at the right sector? If not, we return with zero flag
|
||
* cleared, but d0 = 0.
|
||
*/
|
||
wsCompSect:
|
||
moveq #0,d1 | Clear register
|
||
|
||
moveb a6@(-5),d1 | Get wanted sector#
|
||
cmpb a4@(2),d1 | Compare to the read header
|
||
bne wsDone
|
||
|
||
|
||
/*
|
||
* Write sync pattern and sector data lead-in 'D5 AA'. The
|
||
* missing 'AD' is made up by piping 0x0B through the nibble
|
||
* table (toDisk).
|
||
*
|
||
* To set up IWM for writing:
|
||
*
|
||
* access q6H & write first byte to q7H.
|
||
* Then check bit 7 of q6L (status reg) for 'IWM ready'
|
||
* and write subsequent bytes to q6H.
|
||
*
|
||
* Registers:
|
||
* a0 Data register of IWM
|
||
* a1 Via1Base
|
||
* a2 IWM handshake register
|
||
* a3 data (tags buffer, data buffer)
|
||
* a4 Sync pattern, 'toDisk'<EFBFBD>translation table
|
||
*/
|
||
movel _IWMBase,a0
|
||
tstb a0@(q6H) | Enable writing to disk
|
||
lea a4@(3),a3 | Point a3 to tags buffer
|
||
lea syncPattern,a4
|
||
|
||
moveb a4@+,a0@(q7H) | Write first sync byte
|
||
lea a0@(q6L),a2 | Point a2 to handshake register
|
||
lea a0@(q6H),a0 | Point a0 to IWM data register
|
||
|
||
moveq #6,d0 | Loop counter for sync bytes
|
||
moveq #0,d2
|
||
moveq #0,d3
|
||
movel #0x02010009,d4 | Loop counters for tag/sector data
|
||
|
||
/*
|
||
* Write 5 sync bytes and first byte of sector data lead-in
|
||
*/
|
||
wsLeadIn:
|
||
moveb a4@+,d1 | Get next sync byte
|
||
wsLI1:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsLI1
|
||
moveb d1,a0@ | Write it to disk
|
||
subqw #1,d0
|
||
bne wsLeadIn
|
||
|
||
moveb a4@+,d1 | Write 2nd byte of sector lead-in
|
||
lea toDisk,a4 | Point a4 to nibble translation table
|
||
wsLI2:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsLI2
|
||
moveb d1,a0@ | Write it to disk
|
||
|
||
moveq #0,d5 | Clear checksum registers
|
||
moveq #0,d6
|
||
moveq #0,d7
|
||
|
||
moveq #0x0B,d1 | 3rd byte of sector data lead-in
|
||
| (Gets translated to 0xAD)
|
||
moveb a3@+,d2 | Get 1st byte from tags buffer
|
||
bra wsDataEntry
|
||
|
||
/*
|
||
* The following loop reads the content of the tags buffer (12 bytes)
|
||
* and the data buffer (512 bytes).
|
||
* Each pass reads out three bytes and
|
||
* a) splits them 6&2 into three 6 bit nibbles and a fourth byte
|
||
* consisting of the three 2 bit nibbles
|
||
* b) encodes the nibbles with a table to disk bytes (bit 7 set, no
|
||
* more than two consecutive zero bits) and writes them to disk as
|
||
*
|
||
* 00mmnnoo fragment 2 bit nibbles
|
||
* 00mmmmmm 6 bit nibble -- first byte
|
||
* 00nnnnnn 6 bit nibble -- second byte
|
||
* 00oooooo 6 bit nibble -- third byte
|
||
*
|
||
* c) adds up three 8 bit checksums, one for each of the bytes written.
|
||
*/
|
||
wsSD1:
|
||
movel a6@(8),a3 | Start of sector data buffer
|
||
|
||
wsData:
|
||
addxb d2,d7
|
||
eorb d6,d2
|
||
moveb d2,d3
|
||
lsrw #6,d3 | Put 2 bit nibbles into place
|
||
wsRDY01:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY01
|
||
moveb a4@(0,d3),a0@ | Translate nibble and write
|
||
subqw #3,d4 | Update counter
|
||
moveb d7,d3
|
||
addb d7,d3 | Set X flag (??)
|
||
rolb #1,d7
|
||
andib #0x3F,d0
|
||
wsRDY02:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY02
|
||
moveb a4@(0,d0),a0@ | Translate nibble and write
|
||
|
||
/*
|
||
* We enter with the last byte of the sector data lead-in
|
||
* between our teeth (D1, that is).
|
||
*/
|
||
wsDataEntry:
|
||
moveb a3@+,d0 | Get first byte
|
||
addxb d0,d5
|
||
eorb d7,d0
|
||
moveb d0,d3 | Keep top two bits
|
||
rolw #2,d3 | by shifting them to MSByte
|
||
andib #0x3F,d1
|
||
wsRDY03:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY03
|
||
moveb a4@(0,d1),a0@ | Translate nibble and write
|
||
|
||
moveb a3@+,d1 | Get second byte
|
||
addxb d1,d6
|
||
eorb d5,d1
|
||
moveb d1,d3 | Keep top two bits
|
||
rolw #2,d3 | by shifting them to MSByte
|
||
andib #0x3F,d2
|
||
wsRDY04:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY04
|
||
moveb a4@(0,d2),a0@ | Translate nibble and write
|
||
/*
|
||
* XXX We have a classic off-by-one error here: the last access
|
||
* reaches beyond the data buffer which bombs with memory
|
||
* protection. The value read isn't used anyway...
|
||
* Hopefully there is enough time for an additional check
|
||
* (exit the last loop cycle before the buffer access).
|
||
*/
|
||
tstl d4 | Last loop cycle?
|
||
beq wsSDDone | Then get out while we can.
|
||
|
||
moveb a3@+,d2 | Get third byte
|
||
tstw d4 | First write tag buffer,...
|
||
bne wsData
|
||
|
||
swap d4 | ...then write data buffer
|
||
bne wsSD1
|
||
|
||
/*
|
||
* Write nibbles for last 2 bytes, then
|
||
* split checksum bytes in 6&2 and write them to disk
|
||
*/
|
||
wsSDDone:
|
||
clrb d3 | No 513th byte
|
||
lsrw #6,d3 | Set up 2 bit nibbles
|
||
wsRDY05:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY05
|
||
moveb a4@(0,d3),a0@ | Write fragments
|
||
moveb d5,d3
|
||
rolw #2,d3
|
||
moveb d6,d3
|
||
rolw #2,d3
|
||
andib #0x3F,d0
|
||
wsRDY06:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY06
|
||
moveb a4@(0,d0),a0@ | Write 511th byte
|
||
andib #0x3F,d1
|
||
wsRDY07:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY07
|
||
moveb a4@(0,d1),a0@ | write 512th byte
|
||
moveb d7,d3
|
||
lsrw #6,d3 | Get fragments ready
|
||
wsRDY08:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY08
|
||
moveb a4@(0,d3),a0@ | Write fragments
|
||
andib #0x3F,d5
|
||
wsRDY09:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY09
|
||
moveb a4@(0,d5),a0@ | Write first checksum byte
|
||
andib #0x3F,D6
|
||
wsRDY10:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY10
|
||
moveb a4@(0,d6),a0@ | Write second checksum byte
|
||
andib #0x3F,d7
|
||
wsRDY11:
|
||
tstb a2@ | IWM ready?
|
||
bpl wsRDY11
|
||
moveb a4@(0,d7),a0@ | Write third checksum byte
|
||
|
||
/*
|
||
* Write sector data lead-out
|
||
*/
|
||
lea dataLeadOut,a4 | Sector data lead-out
|
||
moveq #3,d2 | Four bytes long {3,2,1,0}
|
||
wsLeadOut:
|
||
moveb a2@,d1 | IWM ready?
|
||
bpl wsLeadOut
|
||
moveb a4@+,a0@ | Write lead-out
|
||
dbf d2,wsLeadOut
|
||
|
||
moveq #0,d0
|
||
btst #6,d1 | Check IWM underrun bit
|
||
bne wsNoErr
|
||
|
||
moveq #wrUnderRun,d0 | Could not write
|
||
| fast enough to keep up with IWM
|
||
wsNoErr:
|
||
tstb a0@(0x0200) | q7L -- Write OFF
|
||
|
||
wsDone:
|
||
movew a6@(-2),sr | Restore interrupt mask
|
||
moveml sp@+,d1-d7/a0-a5
|
||
unlk a6
|
||
rts
|
||
|
||
|
||
|
||
/**
|
||
** Local functions
|
||
**/
|
||
|
||
/*
|
||
* iwmDelay
|
||
*
|
||
* In-kernel calls to delay() in mac68k/clock.c bomb
|
||
*
|
||
* Parameters: d0 delay in milliseconds
|
||
* Trashes: d0, d1
|
||
* Returns: -
|
||
*/
|
||
iwmDelay:
|
||
/* TimeDBRA is ~8K for 040/33 machines, so we need nested loops */
|
||
id00: movew _TimeDBRA,d1 | dbra loops per ms
|
||
id01: dbra d1,id01 |
|
||
dbra d0,id00
|
||
rts
|
||
|
||
|
||
/*
|
||
* selDriveReg -- Select drive status/control register
|
||
*
|
||
* Parameters: d0 register #
|
||
* (bit 0 - CA2, bit 1 - SEL, bit 2 - CA0, bit 3 - CA1)
|
||
* a0 IWM base address
|
||
* a1 VIA base address
|
||
* Returns: d0 register # (unchanged)
|
||
*/
|
||
selDriveReg:
|
||
tstb a0@(ph0H) | default CA0 to 1 (says IM III)
|
||
tstb a0@(ph1H) | default CA1 to 1
|
||
|
||
btst #0,d0 | bit 0 set => CA2 on
|
||
beq se00
|
||
tstb a0@(ph2H)
|
||
bra se01
|
||
se00:
|
||
tstb a0@(ph2L)
|
||
|
||
se01:
|
||
btst #1,d0 | bit 1 set => SEL on (VIA 1)
|
||
beq se02
|
||
bset #vHeadSel,a1@(vBufA)
|
||
bra se03
|
||
se02:
|
||
bclr #vHeadSel,a1@(vBufA)
|
||
|
||
se03:
|
||
btst #2,d0 | bit 2 set => CA0 on
|
||
bne se04
|
||
tstb a0@(ph0L)
|
||
|
||
se04:
|
||
btst #3,d0 | bit 3 set => CA1 on
|
||
bne se05
|
||
tstb a0@(ph1L)
|
||
se05:
|
||
rts
|
||
|
||
|
||
|
||
/*
|
||
* dstatus -- check drive status (bit 7 - N flag) wrt. a previously
|
||
* set status tag.
|
||
*
|
||
* Parameters: d0 register selector
|
||
* a0 IWM base address
|
||
* Returns: d0 status
|
||
*/
|
||
dstatus:
|
||
tstb a0@(q6H)
|
||
moveb a0@(q7L),d0
|
||
tstb a0@(q6L) | leave in "read data reg"
|
||
tstb d0 | state for safety
|
||
|
||
rts
|
||
|
||
|
||
/*
|
||
* driveStat -- query drive status.
|
||
*
|
||
* Parameters: a0 IWMBase
|
||
* a1 VIABase
|
||
* d0 register selector
|
||
* Returns: d0 status (Bit 7)
|
||
*/
|
||
driveStat:
|
||
tstb a0@(mtrOn) | ENABLE; turn drive on
|
||
bsr selDriveReg
|
||
bsr dstatus
|
||
|
||
rts
|
||
|
||
|
||
/*
|
||
* dtrigger -- toggle LSTRB line to give drive a strobe signal
|
||
* IM III says pulse length = 1 us < t < 1 ms
|
||
*
|
||
* Parameters: a0 IWMBase
|
||
* a1 VIABase
|
||
* Returns: -
|
||
*/
|
||
dtrigger:
|
||
tstb a0@(ph3H) | LSTRB high
|
||
moveb a1@(vBufA),a1@(vBufA) | intelligent nop seen in q700 ROM
|
||
tstb a0@(ph3L) | LSTRB low
|
||
|
||
rts
|
||
|
||
|
||
/*
|
||
* driveCmd -- send command to drive.
|
||
*
|
||
* Parameters: a0 IWMBase
|
||
* a1 VIABase
|
||
* d0 Command token
|
||
* Returns: -
|
||
*/
|
||
driveCmd:
|
||
bsr selDriveReg
|
||
bsr dtrigger
|
||
|
||
rts
|
||
|
||
|
||
/*
|
||
* readSectHdr -- read and decode the next available sector header.
|
||
*
|
||
* TODO: Poll SCC as long as interrupts are disabled.
|
||
*
|
||
* Parameters: a0 IWMBase
|
||
* a1 VIABase
|
||
* a4 sectorHdr_t address
|
||
* Returns: d0 result code
|
||
*/
|
||
readSectHdr:
|
||
moveq #3,d4 | Read 3 chars from IWM for sync
|
||
movew #600,d3 | Retries to sync to disk
|
||
moveq #0,d2 | Clear scratch regs
|
||
moveq #0,d1
|
||
moveq #0,d0
|
||
|
||
tstb a0@(q7L)
|
||
lea a0@(q6L),a0 | IWM data register
|
||
shReadSy:
|
||
moveb a0@,d2 | Read char
|
||
dbra d3,shSeekSync
|
||
|
||
moveq #noNybErr,d0 | Disk is blank?
|
||
bra shDone
|
||
|
||
shSeekSync:
|
||
bpl shReadSy | No char at IWM, repeat read
|
||
subqw #1,d4
|
||
bne shReadSy
|
||
/*
|
||
* When we get here, the IWM should be in sync with the data
|
||
* stream from disk.
|
||
* Next look for sector header lead-in 'D5 AA 96'
|
||
*/
|
||
movew #1500,d3 | Retries to seek header
|
||
shLeadIn:
|
||
lea hdrLeadIn,a3 | Sector header lead-in bytes
|
||
moveq #0x03,d4 | is 3 bytes long
|
||
shLI1:
|
||
moveb a0@,d2 | Get next byte
|
||
bpl shLI1
|
||
dbra d3,shLI2
|
||
moveq #noAdrMkErr,d0 | Can't find an address mark
|
||
bra shDone
|
||
|
||
shLI2:
|
||
cmpb a3@+,d2
|
||
bne shLeadIn | If ne restart scan
|
||
subqw #1,d4
|
||
bne shLI1
|
||
/*
|
||
* We have found the lead-in. Now get the header information.
|
||
* Reg d4 holds the checksum.
|
||
*/
|
||
lea diskTo-0x90,a2 | Translate disk bytes -> 6&2
|
||
shHdr1:
|
||
moveb a0@,d0 | Get 1st char
|
||
bpl shHdr1
|
||
moveb a2@(0,d0),d1 | and remap it
|
||
moveb d1,d4
|
||
rorw #6,d1 | separate 2:6, drop hi bits
|
||
shHdr2:
|
||
moveb a0@,d0 | Get 2nd char
|
||
bpl shHdr2
|
||
moveb a2@(0,d0),d2 | and remap it
|
||
eorb d2,d4
|
||
shHdr3:
|
||
moveb a0@,d0 | Get 3rd char
|
||
bpl shHdr3
|
||
moveb a2@(0,d0),d1 | and remap it
|
||
eorb d1,d4
|
||
rolw #6,d1 |
|
||
shHdr4:
|
||
moveb a0@,d0 | Get 4th char
|
||
bpl shHdr4
|
||
moveb a2@(0,d0),d3 | and remap it
|
||
eorb d3,d4
|
||
shHdr5:
|
||
moveb a0@,d0 | Get checksum byte
|
||
bpl shHdr5
|
||
moveb a2@(0,d0),d5 | and remap it
|
||
eorb d5,d4
|
||
bne shCsErr | Checksum ok?
|
||
/*
|
||
* We now have in
|
||
* d1/lsb track number
|
||
* d1/msb bit 3 is side bit
|
||
* d2 sector number
|
||
* d3 ???
|
||
* d5 checksum (=0)
|
||
*
|
||
* Next check for lead-out.
|
||
*/
|
||
moveq #1,d4 | is 2 bytes long
|
||
shHdr6:
|
||
moveb a0@,d0 | Get token
|
||
bpl shHdr6
|
||
cmpb a3@+,d0 | Check
|
||
bne shLOErr | Fault!
|
||
dbra d4,shHdr6
|
||
movew d1,d0 | Isolate side bit
|
||
lsrw #8,d0
|
||
moveb d0,a4@+ | and store it
|
||
moveb d1,a4@+ | Store track number
|
||
moveb d2,a4@+ | and sector number
|
||
moveq #0,d0 | All is well
|
||
bra shDone
|
||
|
||
shCsErr:
|
||
moveq #badCkSmErr,d0 | Bad sector header checksum
|
||
bra shDone
|
||
shLOErr:
|
||
moveq #badBtSlpErr,d0 | Bad address mark (no lead-out)
|
||
|
||
shDone:
|
||
tstl d0 | Set flags
|
||
rts
|