1522 lines
35 KiB
ArmAsm
1522 lines
35 KiB
ArmAsm
/* $NetBSD: iwm.s,v 1.2 1999/03/27 05:45:19 scottr Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1996-99 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 We run at spl4 to give the NMI switch a chance. All currently
|
|
* supported machines have no interrupt sources > 4 (SSC) -- the
|
|
* Q700 interrupt levels can be shifted around in A/UX mode,
|
|
* but we're not there, yet.
|
|
*
|
|
* 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 <mac68k/obio/iwmreg.h>
|
|
|
|
#define USE_DELAY 0 /* "1" bombs for unknown reasons */
|
|
|
|
|
|
/*
|
|
* References to global name space
|
|
*/
|
|
.extern _TimeDBRA | in mac68k/macrom.c
|
|
.extern _VIA1Base | in mac68k/machdep.c
|
|
.extern _IWMBase | in iwm_fd.c
|
|
|
|
|
|
.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 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 #iwmMode,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 #iwmMode,d0
|
|
beq initDone
|
|
moveb #iwmMode,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!
|
|
*
|
|
* XXX make "sizeof cylCache_t" a symbolic constant
|
|
*
|
|
* Parameters: fp+08 l Address of sector data buffer (512 bytes)
|
|
* fp+12 l Address of sector header struct (I/O)
|
|
* fp+16 l Address of cache buffer ptr array
|
|
* Returns: d0 result code
|
|
* Local: fp-2 w CPU status register
|
|
* fp-3 b side,
|
|
* fp-4 b track,
|
|
* fp-5 b sector wanted
|
|
* fp-6 b retry count
|
|
* fp-7 b sector read
|
|
*/
|
|
ENTRY(iwmReadSector)
|
|
link a6,#-8
|
|
moveml d1-d7/a0-a5,sp@-
|
|
|
|
movel _Via1Base,a1
|
|
movel a6@(o_hdr),a4 | Addr of sector header struct
|
|
|
|
moveb a4@+,a6@(-3) | Save side bit,
|
|
moveb a4@+,a6@(-4) | track#,
|
|
moveb a4@,a6@(-5) | sector#
|
|
moveb #2*maxGCRSectors,a6@(-6) | Max. retry count
|
|
|
|
movew sr,a6@(-2) | Save CPU status register
|
|
oriw #0x0600,sr | Block all interrupts
|
|
|
|
rsNextSect:
|
|
movel a6@(o_hdr),a4 | Addr of sector header struct
|
|
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@(o_hdr),a4 | Sector header struct
|
|
|
|
moveb a4@(o_side),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 track# we want
|
|
cmpb a4@(o_track),d1 | Compare to the header we've read
|
|
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 as set up by readSectHdr
|
|
* a2 points to 'diskTo' translation table
|
|
* a4 points to tags buffer
|
|
*/
|
|
rsGetSect:
|
|
moveb a4@(2),a6@(-7) | save sector number
|
|
lea a4@(3),a4 | Beginning of tag buffer
|
|
moveq #50,d3 | Max. retries for sector lookup
|
|
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.
|
|
* Compare sector # to what we wanted: If it matches, read directly
|
|
* to buffer, else read to track cache.
|
|
*/
|
|
movew #0x01FE,d4 | Loop counter
|
|
moveq #0,d1 | Clear d1.L
|
|
moveb a6@(-7),d1 | Get sector# we have read
|
|
cmpb a6@(-5),d1 | Compare to the sector# we want
|
|
bne rsToCache
|
|
movel a6@(o_buf),a4 | Sector data buffer
|
|
bra rsData
|
|
rsToCache:
|
|
movel a6@(o_rslots),a4 | Base address of slot array
|
|
lslw #3,d1 | sizeof cylCacheSlot_t is 8 bytes
|
|
movel #-1,a4@(o_valid,d1)
|
|
movel a4@(o_secbuf,d1),a4 | and get its buffer ptr
|
|
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.
|
|
|
|
/*
|
|
* See if we got the sector we wanted. If not, and no error
|
|
* occurred, mark buffer valid. Else ignore the sector.
|
|
* Then, read on.
|
|
*/
|
|
rsDone:
|
|
movel a6@(o_hdr),a4 | Addr of sector header struct
|
|
moveb a4@(o_sector),d1 | Get # of sector we have just read
|
|
cmpb a6@(-5),d1 | Compare to the sector we want
|
|
beq rsAllDone
|
|
|
|
tstb d0 | Any error? Simply ignore data
|
|
beq rsBufValid
|
|
lslw #3,d1 | sizeof cylCacheSlot_t is 8 bytes
|
|
movel a6@(o_rslots),a4
|
|
clrl a4@(o_valid,d1) | Mark buffer content "invalid"
|
|
|
|
rsBufValid:
|
|
subqb #1,a6@(-6) | max. retries
|
|
bne rsNextSect
|
|
| Sector not found, but
|
|
tstb d0 | don't set error code if we
|
|
bne rsAllDone | already have one.
|
|
moveq #sectNFErr,d0
|
|
rsAllDone:
|
|
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!
|
|
*
|
|
* XXX Use registers more efficiently
|
|
*
|
|
* Parameters: fp+8 l Address of sector header struct (I/O)
|
|
* fp+12 l Address of cache buffer ptr array
|
|
* Returns: d0 result code
|
|
*
|
|
* Local: fp-2 w CPU status register
|
|
* fp-3 b side,
|
|
* fp-4 b track,
|
|
* fp-5 b sector wanted
|
|
* fp-6 b retry count
|
|
* fp-10 b current slot
|
|
*/
|
|
ENTRY(iwmWriteSector)
|
|
link a6,#-10
|
|
moveml d1-d7/a0-a5,sp@-
|
|
|
|
movel _Via1Base,a1
|
|
movel a6@(o_hdr),a4 | Addr of sector header struct
|
|
|
|
moveb a4@+,a6@(-3) | Save side bit,
|
|
moveb a4@+,a6@(-4) | track#,
|
|
moveb a4@,a6@(-5) | sector#
|
|
moveb #maxGCRSectors,a6@(-6) | Max. retry count
|
|
|
|
movew sr,a6@(-2) | Save CPU status register
|
|
oriw #0x0600,sr | Block all interrupts
|
|
|
|
wsNextSect:
|
|
movel a6@(o_hdr),a4
|
|
bsr readSectHdr | Get next available sector header
|
|
bne wsAllDone | Return if error
|
|
|
|
/*
|
|
* Is this the right track & side? If not, return with error
|
|
*/
|
|
movel a6@(o_hdr),a4 | Sector header struct
|
|
|
|
moveb a4@(o_side),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@(o_track),d1 | Compare to the read header
|
|
beq wsCompSect
|
|
|
|
wsSeekErr:
|
|
moveq #seekErr,d0 | Wrong track or side
|
|
bra wsAllDone
|
|
|
|
/*
|
|
* Look up the current sector number in the cache.
|
|
* If the buffer is dirty ("valid"), write it to disk. If not,
|
|
* loop over all the slots and return if all of them are clean.
|
|
*
|
|
* Alternatively, we could decrement a "dirty sectors" counter here.
|
|
*/
|
|
wsCompSect:
|
|
moveq #0,d1 | Clear register
|
|
moveb a4@(o_sector),d1 | get the # of header read
|
|
lslw #3,d1 | sizeof cylCacheSlot_t is 8 bytes
|
|
movel a6@(o_wslots),a4
|
|
tstl a4@(o_valid,d1) | Sector dirty?
|
|
bne wsBufDirty
|
|
|
|
moveq #maxGCRSectors-1,d2 | Any dirty sectors left?
|
|
wsChkDty:
|
|
movew d2,d1
|
|
lslw #3,d1 | sizeof cylCacheSlot_t is 8 bytes
|
|
tstl a4@(o_valid,d1)
|
|
bne wsNextSect | Sector dirty?
|
|
dbra d2,wsChkDty
|
|
|
|
bra wsAllDone | We are through with this track.
|
|
|
|
|
|
/*
|
|
* 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 IWM base address (later: data register)
|
|
* a1 Via1Base
|
|
* a2 IWM handshake register
|
|
* a3 data (tags buffer, data buffer)
|
|
* a4 Sync pattern, 'toDisk' translation table
|
|
*/
|
|
wsBufDirty:
|
|
movel _IWMBase,a0
|
|
lea a4@(0,d1),a3
|
|
movel a3,a6@(-10) | Save ptr to current slot
|
|
tstb a0@(q6H) | Enable writing to disk
|
|
movel a6@(o_hdr),a4 | Sector header struct
|
|
lea a4@(o_Tags),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@(-10),a3 | Get ptr to current slot
|
|
movel a3@(o_secbuf),a3 | Get 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:
|
|
tstb d0 | Any error? Simply retry
|
|
bne wsBufInvalid
|
|
|
|
movel a6@(-10),a4 | Else, get ptr to current slot
|
|
clrl a4@(o_valid) | Mark current buffer "clean"
|
|
bra wsNextSect
|
|
|
|
wsBufInvalid:
|
|
subqb #1,a6@(-6) | retries
|
|
bne wsNextSect
|
|
| Sector not found, but
|
|
tstb d0 | don't set error code if we
|
|
bne wsAllDone | already have one.
|
|
moveq #sectNFErr,d0
|
|
|
|
wsAllDone:
|
|
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: a4 sectorHdr_t address
|
|
* Returns: d0 result code
|
|
* Uses: d0-d4, a0, a2-a4
|
|
*/
|
|
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
|
|
movel _IWMBase,a0 | IWM base address
|
|
|
|
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 | No char at IWM, repeat read
|
|
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
|