Home | History | Annotate | Line # | Download | only in obio
iwm.s revision 1.1.2.1
      1 /*	$NetBSD: iwm.s,v 1.1.2.1 1999/05/16 22:38:13 scottr Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1996-99 Hauke Fath.  All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 /*
     30  * iwm.s -- low level routines for Sony floppy disk access.
     31  * The present implementation supports the 800K GCR format on non-DMA
     32  * machines.
     33  *
     34  * The IWM and SWIM chips run in polled mode; they are not capable of
     35  * interrupting the CPU. That's why interrupts need only be blocked
     36  * when there is simply no time for interrupt routine processing,
     37  * i.e. during data transfers.
     38  *
     39  * o  The local routines do not block any interrupts.
     40  *
     41  * o  The iwmXXX() routines that set/get IWM or drive settings are not
     42  *    time critical and do not block interrupts.
     43  *
     44  * o  The iwmXXX() routines that are called to perform data transfers
     45  *    block all interrupts because otherwise the current sector data
     46  *    would be lost.
     47  *    The old status register content is stored on the stack.
     48  *
     49  * o  We run at spl4 to give the NMI switch a chance. All currently
     50  *    supported machines have no interrupt sources > 4 (SSC) -- the
     51  *    Q700 interrupt levels can be shifted around in A/UX mode,
     52  *    but we're not there, yet.
     53  *
     54  * o  As a special case iwmReadSectHdr() must run with interrupts disabled
     55  *    (it transfers data). Depending on the needs of the caller, it
     56  *    may be necessary to block interrupts after completion of the routine
     57  *    so interrupt handling is left to the caller.
     58  *
     59  * If we wanted to deal with incoming serial data / serial interrupts,
     60  * we would have to either call zshard(0) {mac68k/dev/zs.c} or
     61  * zsc_intr_hard(0) {sys/dev/ic/z8530sc.c}. Or we would have to roll our
     62  * own as both of the listed function calls look rather expensive compared
     63  * to a 'tst.b REGADDR ; bne NN'.
     64  */
     65 
     66 #include <m68k/asm.h>
     67 
     68 #include <mac68k/obio/iwmreg.h>
     69 
     70 #define USE_DELAY	0	/* "1" bombs for unknown reasons */
     71 
     72 
     73 /*
     74  * References to global name space
     75  */
     76 	.extern	_TimeDBRA		| in mac68k/macrom.c
     77 	.extern _VIA1Base		| in mac68k/machdep.c
     78 	.extern	_IWMBase		| in iwm_fd.c
     79 
     80 
     81 	.data
     82 
     83 diskTo:
     84 	/*
     85 	 * Translation table from 'disk bytes' to 6 bit 'nibbles',
     86 	 * taken from the .Sony driver.
     87 	 * This could be made a loadable table (via ioctls) to read
     88 	 * e.g. ProDOS disks (there is a hook for such a table in .Sony).
     89 	 */
     90 	.byte	/* 90 */  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01
     91 	.byte	/* 98 */  0xFF, 0xFF, 0x02, 0x03, 0xFF, 0x04, 0x05, 0x06
     92 	.byte	/* A0 */  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x08
     93 	.byte	/* A8 */  0xFF, 0xFF, 0xFF, 0x09, 0x0A, 0x0B, 0x0C, 0x0D
     94 	.byte	/* B0 */  0xFF, 0xFF, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
     95 	.byte	/* B8 */  0xFF, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A
     96 	.byte	/* C0 */  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
     97 	.byte	/* C8 */  0xFF, 0xFF, 0xFF, 0x1B, 0xFF, 0x1C, 0x1D, 0x1E
     98 	.byte	/* D0 */  0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x20, 0x21
     99 	.byte	/* D8 */  0xFF, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28
    100 	.byte	/* E0 */  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x29, 0x2A, 0x2B
    101 	.byte	/* E8 */  0xFF, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32
    102 	.byte	/* F0 */  0xFF, 0xFF, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38
    103 	.byte	/* F8 */  0xFF, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
    104 
    105 hdrLeadIn:
    106 	.byte	0xD5, 0xAA, 0x96
    107 
    108 hdrLeadOut:
    109 	.byte	0xDE, 0xAA, 0xFF
    110 
    111 dataLeadIn:
    112 	.byte	0xD5, 0xAA, 0xAD
    113 
    114 dataLeadOut:
    115 	.byte	0xDE, 0xAA, 0xFF, 0xFF
    116 
    117 
    118 toDisk:
    119 	/*
    120 	 * Translation table from 6-bit nibbles [0x00..0x3f] to 'disk bytes'
    121 	 */
    122 	.byte	/* 00 */  0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6
    123 	.byte	/* 08 */  0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3
    124 	.byte	/* 10 */  0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC
    125 	.byte	/* 18 */  0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3
    126 	.byte	/* 20 */  0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE
    127 	.byte	/* 28 */  0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC
    128 	.byte	/* 30 */  0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xf5, 0xF6
    129 	.byte	/* 38 */  0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
    130 
    131 syncPattern:
    132 	/*
    133 	 * This sync pattern creates 4 sync chars with 10 bits each that look
    134 	 * like 0011111111b (i.e. 0x0FF). As the IWM ignores leading zero
    135 	 * bits, it locks on 0xFF after the third sync byte.
    136 	 * For convenience, the bytes of the sector data lead-in
    137 	 * (D5 AA AD) follow.
    138 	 */
    139 	.byte	0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF
    140 	.byte	0xD5, 0xAA, 0xAD
    141 
    142 
    143 
    144 	.text
    145 
    146 /*
    147  * Register conventions:
    148  *	a0	IWM base address
    149  *	a1	VIA1 base address
    150  *
    151  *	d0	return value (0 == no error)
    152  *
    153  * Upper bits in data registers that are not cleared give nasty
    154  * (pseudo-) random errors when building an address. Make sure those
    155  *  registers are cleaned with a moveq before use!
    156  */
    157 
    158 
    159 
    160 /**
    161  **	Export wrappers
    162  **/
    163 
    164 /*
    165  * iwmQueryDrvFlags -- export wrapper for driveStat
    166  *
    167  * Parameters:	stack	l	drive selector
    168  *		stack	l	register selector
    169  * Returns:	d0		flag
    170  */
    171 ENTRY(iwmQueryDrvFlag)
    172 	link	a6,#0
    173 	moveml	d1/a0-a1,sp@-
    174 	movel	_IWMBase,a0
    175 	movel	_Via1Base,a1
    176 
    177 	movel	a6@(8),d0		| Get drive #
    178 	beq	quDrv00
    179 	cmpl	#1,d0
    180 	beq	quDrv01
    181 
    182 	bra	quDone			| Invalid drive #
    183 
    184 quDrv00:
    185 	tstb	a0@(intDrive)		| SELECT; choose drive #0
    186 	bra	queryDrv
    187 
    188 quDrv01:
    189 	tstb	a0@(extDrive)		| SELECT; choose drive #1
    190 
    191 queryDrv:
    192 	movel	a6@(12),d0		| Get register #
    193 	bsr	driveStat
    194 
    195 quDone:
    196 	moveml	sp@+,d1/a0-a1
    197 	unlk	a6
    198 	rts
    199 
    200 
    201 /*
    202  * iwmReadSectHdr -- read and decode the next available sector header.
    203  *
    204  * Parameters:	stack	l	Address of sector header struct (I/O)
    205  *				b	side (0, 1)
    206  *				b	track (0..79)
    207  *				b	sector (0..11)
    208  * Returns:	d0		result code
    209  */
    210 ENTRY(iwmReadSectHdr)
    211 	link	a6,#0
    212 	moveml	d1-d5/a0-a4,sp@-
    213 	movel	a6@(0x08),a4		| Get param block address
    214 	bsr	readSectHdr
    215 	moveml	sp@+,d1-d5/a0-a4
    216 	unlk	a6
    217 	rts
    218 
    219 
    220 
    221 /**
    222  **	Exported functions
    223  **/
    224 
    225 /*
    226  * iwmInit -- Initialize IWM chip.
    227  *
    228  * Parameters:	-
    229  * Returns:	d0		result code
    230  */
    231 ENTRY(iwmInit)
    232 	link	a6,#0
    233 	moveml	d2/a0,sp@-
    234 	movel	_IWMBase,a0
    235 
    236 	/*
    237 	 * Reset IWM to known state (clear disk I/O latches)
    238 	 */
    239 	tstb	a0@(ph0L)		| CA0
    240 	tstb	a0@(ph1L)		| CA1
    241 	tstb	a0@(ph2L)		| CA2
    242 	tstb	a0@(ph3L)		| LSTRB
    243 
    244 	tstb	a0@(mtrOff)		| ENABLE; make sure drive is off
    245 	tstb	a0@(intDrive)		| SELECT; choose drive 1
    246 	moveq	#0x1F,d0		| XXX was 0x17 -- WHY!?
    247 
    248 	/*
    249 	 * First do it quick...
    250 	 */
    251 	tstb	a0@(q6H)
    252 	andb	a0@(q7L),d0		| status register
    253 	tstb	a0@(q6L)
    254 	cmpib	#iwmMode,d0		| all is well??
    255 	beq	initDone
    256 
    257 	/*
    258 	 * If this doesn't succeed (e.g. drive still running),
    259 	 * we do it thoroughly.
    260 	 */
    261 	movel	#0x00080000,d2		| ca. 500,000 retries = 1.5 sec
    262 initLp:
    263 	moveq	#initIWMErr,d0		| Initialization error
    264 	subql	#1,d2
    265 	bmi	initErr
    266 	tstb	a0@(mtrOff)		| disable drive
    267 	tstb	a0@(q6H)
    268 	moveq	#0x3F,d0
    269 	andb	a0@(q7L),d0
    270 	bclr	#5,d0			| Reset bit 5 and set Z flag
    271 					| according to previous state
    272 	bne	initLp			| Loop if drive still on
    273 	cmpib	#iwmMode,d0
    274 	beq	initDone
    275 	moveb	#iwmMode,a0@(q7H)	| Init IWM
    276 	tstb	a0@(q7L)
    277 	bra	initLp
    278 
    279 initDone:
    280 	tstb	a0@(q6L)		| Prepare IWM for data
    281 	moveq	#0,d0			| noErr
    282 
    283 initErr:
    284 	moveml	sp@+,d2/a0
    285 	unlk	a6
    286 	rts
    287 
    288 
    289 /*
    290  * iwmCheckDrive -- Check if given drive is available and return bit vector
    291  *	with capabilities (SS/DS, disk inserted, ...)
    292  *
    293  * Parameters:	stack	l	Drive number (0,1)
    294  * Returns:	d0	Bit	 0 - 0 = Drive is single sided
    295  *				 1 - 0 = Disk inserted
    296  *				 2 - 0 = Motor is running
    297  *				 3 - 0 = Disk is write protected
    298  *				 4 - 0 = Disk is DD
    299  *				31 - (-1) No drive / invalid drive #
    300  */
    301 ENTRY(iwmCheckDrive)
    302 	link	a6,#0
    303 	moveml	d1/a0-a1,sp@-
    304 	movel	_IWMBase,a0
    305 	movel	_Via1Base,a1
    306 
    307 	moveq	#-1,d1			| no drive
    308 
    309 	movel	a6@(0x08),d0		| check drive #
    310 	beq	chkDrv00
    311 	cmpl	#1,d0
    312 	beq	chkDrv01
    313 
    314 	bra	chkDone			| invalid drive #
    315 
    316 chkDrv00:
    317 	tstb	a0@(intDrive)		| SELECT; choose drive #0
    318 	bra	chkDrive
    319 
    320 chkDrv01:
    321 	tstb	a0@(extDrive)		| SELECT; choose drive #1
    322 
    323 chkDrive:
    324 	moveq	#-2,d1			| error code
    325 	moveq	#drvInstalled,d0	| Drive installed?
    326 	bsr	driveStat
    327 	bmi	chkDone			| no drive
    328 
    329 	moveq	#0,d1			| Drive found
    330 	tstb	a0@(mtrOn)		| ENABLE; activate drive
    331 	moveq	#singleSided,d0		| Drive is single-sided?
    332 	bsr	driveStat
    333 	bpl	chkHasDisk
    334 	/*
    335 	 * Drive is double-sided -- this is not really a surprise as the
    336 	 * old ss 400k drive needs disk speed control from the Macintosh
    337 	 * and we're not doing that here. Anyway - just in case...
    338 	 * I am not sure m680x0 Macintoshes (x>0) support 400K drives at all
    339 	 * due to their radically different sound support.
    340 	 */
    341 	bset	#0,d1			| 1 = no.
    342 chkHasDisk:
    343 	moveq	#diskInserted,d0	| Disk inserted?
    344 	bsr	driveStat
    345 	bpl	chkMotorOn
    346 	bset	#1,d1			| 1 = No.
    347 	bra	chkDone
    348 chkMotorOn:
    349 	moveq	#drvMotorState,d0	| Motor is running?
    350 	bsr	driveStat
    351 	bpl	chkWrtProt
    352 	bset	#2,d1			| 1 = No.
    353 chkWrtProt:
    354 	moveq	#writeProtected,d0	| Disk is write protected?
    355 	bsr	driveStat
    356 	bpl	chkDD_HD
    357 	bset	#3,d1			| 1 = No.
    358 chkDD_HD:
    359 	moveq	#diskIsHD,d0		| Disk is HD? (was "drive installed")
    360 	bsr	driveStat
    361 	bpl	chkDone
    362 	bset	#4,d1			| 1 = No.
    363 chkDone:
    364 	movel	d1,d0
    365 	moveml	sp@+,d1/a0-a1
    366 	unlk	a6
    367 	rts
    368 
    369 
    370 /*
    371  * iwmDiskEject -- post EJECT command and toggle LSTRB line to give a
    372  * strobe signal.
    373  * IM III says pulse length = 500 ms, but we seem to get away with
    374  * less delay; after all, we spin lock the CPU with it.
    375  *
    376  * Parameters:	stack	l	drive number (0,1)
    377  *		a0		IWMBase
    378  *		a1		VIABase
    379  * Returns:	d0		result code
    380  */
    381 ENTRY(iwmDiskEject)
    382 	link	a6,#0
    383 	movel	_IWMBase,a0
    384 	movel	_Via1Base,a1
    385 
    386 	movel	a6@(0x08),d0		| Get drive #
    387 	beq	ejDrv00
    388 	cmpw	#1,d0
    389 	beq	ejDrv01
    390 
    391 	bra	ejDone			| Invalid drive #
    392 
    393 ejDrv00:
    394 	tstb	a0@(intDrive)		| SELECT; choose drive #0
    395 	bra	ejDisk
    396 
    397 ejDrv01:
    398 	tstb	a0@(extDrive)		| SELECT; choose drive #1
    399 ejDisk:
    400 	tstb	a0@(mtrOn)		| ENABLE; activate drive
    401 
    402 	moveq	#motorOffCmd,d0		| Motor off
    403  	bsr	driveCmd
    404 
    405 	moveq	#diskInserted,d0	| Disk inserted?
    406 	bsr	driveStat
    407 	bmi	ejDone
    408 
    409 	moveq	#ejectDiskCmd,d0	| Eject it
    410 	bsr	selDriveReg
    411 
    412 	tstb	a0@(ph3H)		| LSTRB high
    413 #if USE_DELAY
    414 	movel	#1000,sp@-		| delay 1 ms
    415 	jsr	_C_LABEL(delay)
    416 	addqw	#4,sp			| clean up stack
    417 #else
    418 	movew	#1,d0
    419 	bsr	iwmDelay
    420 #endif
    421 	tstb	a0@(ph3L)		| LSTRB low
    422 	moveq	#0,d0			| All's well...
    423 ejDone:
    424 	unlk	a6
    425 	rts
    426 
    427 
    428 /*
    429  * iwmSelectDrive -- select internal (0) / external (1) drive.
    430  *
    431  * Parameters:	stack	l	drive ID (0/1)
    432  * Returns:	d0		drive #
    433  */
    434 ENTRY(iwmSelectDrive)
    435 	link	a6,#0
    436 	moveml	a0-a1,sp@-
    437 	movel	_IWMBase,a0
    438 	movel	_Via1Base,a1
    439 
    440 	movel	a6@(8),d0		| Get drive #
    441 	bne	extDrv
    442 	tstb	a0@(intDrive)
    443 	bra	sdDone
    444 extDrv:
    445 	tstb	a0@(extDrive)
    446 sdDone:
    447 	moveml	sp@+,a0-a1
    448 	unlk	a6
    449 	rts
    450 
    451 
    452 /*
    453  * iwmMotor -- switch drive motor on/off
    454  *
    455  * Parameters:	stack	l	drive ID (0/1)
    456  *		stack	l	on(1)/off(0)
    457  * Returns:	d0		motor cmd
    458  */
    459 ENTRY(iwmMotor)
    460 	link	a6,#0
    461 	moveml	a0-a1,sp@-
    462 	movel	_IWMBase,a0
    463 	movel	_Via1Base,a1
    464 
    465 	movel	a6@(8),d0		| Get drive #
    466 	bne	mtDrv1
    467 	tstb	a0@(intDrive)
    468 	bra	mtSwitch
    469 mtDrv1:
    470 	tstb	a0@(extDrive)
    471 mtSwitch:
    472 	movel	#motorOnCmd,d0		| Motor ON
    473 	tstl	a6@(12)
    474 	bne	mtON
    475 	movel	#motorOffCmd,d0
    476 mtON:
    477 	bsr	driveCmd
    478 
    479 	moveml	sp@+,a0-a1
    480 	unlk	a6
    481 	rts
    482 
    483 
    484 /*
    485  * iwmSelectSide -- select side 0 (lower head) / side 1 (upper head).
    486  *
    487  * This MUST be called immediately before an actual read/write access.
    488  *
    489  * Parameters:	stack	l	side bit (0/1)
    490  * Returns:	-
    491  */
    492 ENTRY(iwmSelectSide)
    493 	link	a6,#0
    494 	moveml	d1/a0-a1,sp@-
    495 	movel	_IWMBase,a0
    496 	movel	_Via1Base,a1
    497 
    498 	moveq	#0x0B,d0		| Drive ready for reading?
    499 	bsr	selDriveReg		| (undocumented)
    500 ss01:
    501 	bsr	dstatus
    502 	bmi	ss01
    503 
    504 	moveq	#rdDataFrom0,d0		| Lower head
    505 	movel	a6@(0x08),d1		| Get side #
    506 	beq	ssSide0
    507 	moveq	#rdDataFrom1,d0		| Upper head
    508 ssSide0:
    509 	bsr	driveStat
    510 
    511 	moveml	sp@+,d1/a0-a1
    512 	unlk	a6
    513 	rts
    514 
    515 
    516 /*
    517  * iwmTrack00 -- move head to track 00 for drive calibration.
    518  *
    519  * XXX Drive makes funny noises during resore. Tune delay/retry count?
    520  *
    521  * Parameters:	-
    522  * Returns:	d0		result code
    523  */
    524 ENTRY(iwmTrack00)
    525 	link	a6,#0
    526 	moveml	d1-d4/a0-a1,sp@-
    527 	movel	_IWMBase,a0
    528 	movel	_Via1Base,a1
    529 
    530 	moveq	#motorOnCmd,d0		| Switch drive motor on
    531 	bsr	driveCmd
    532 
    533 	moveq	#stepOutCmd,d0		| Step out
    534 	bsr	driveCmd
    535 
    536 	movew	#100,d2			| Max. tries
    537 t0Retry:
    538 	moveq	#atTrack00,d0		| Already at track 0?
    539 	bsr	driveStat
    540 	bpl	isTrack00		| Track 0 => Bit 7 = 0
    541 
    542 	moveq	#doStepCmd,d0		| otherwise step
    543 	bsr	driveCmd
    544 	movew	#80,d4			| Retries
    545 t0Still:
    546 	moveq	#stillStepping,d0	| Drive is still stepping?
    547 	bsr	driveStat
    548 	dbmi	d4,t0Still
    549 
    550 	cmpiw	#-1,d4
    551 	bne	t002
    552 
    553 	moveq	#cantStepErr,d0		| Not ready after many retries
    554 	bra	t0Done
    555 t002:
    556 
    557 #if USE_DELAY
    558 	movel	#15000,sp@-
    559 	jsr	_C_LABEL(delay)		| in mac68k/clock.c
    560 	addqw	#4,sp
    561 #else
    562 	movew	#15,d0
    563 	bsr	iwmDelay
    564 #endif
    565 
    566 	dbra	d2,t0Retry
    567 
    568 	moveq	#tk0BadErr,d0		| Can't find track 00!!
    569 	bra	t0Done
    570 
    571 isTrack00:
    572 	moveq	#0,d0
    573 t0Done:
    574 	moveml	sp@+,d1-d4/a0-a1
    575 	unlk	a6
    576 	rts
    577 
    578 
    579 /*
    580  * iwmSeek -- do specified # of steps (positive - in, negative - out).
    581  *
    582  * Parameters:	stack	l	# of steps
    583  * returns:	d0		result code
    584  */
    585 ENTRY(iwmSeek)
    586 	link	a6,#0
    587 	moveml	d1-d4/a0-a1,sp@-
    588 	movel	_IWMBase,a0
    589 	movel	_Via1Base,a1
    590 
    591 	moveq	#motorOnCmd,d0		| Switch drive motor on
    592 	bsr	driveCmd
    593 
    594 	moveq	#stepInCmd,d0		| Set step IN
    595 	movel	a6@(8),d2		| Get # of steps from stack
    596 	beq	stDone			| 0 steps? Nothing to do.
    597 	bpl	stepOut
    598 
    599 	moveq	#stepOutCmd,d0		| Set step OUT
    600 	negl	d2			| Make # of steps positive
    601 stepOut:
    602 	subql	#1,d2			| Loop exits for -1
    603 	bsr	driveCmd		| Set direction
    604 stLoop:
    605 	moveq	#doStepCmd,d0
    606 	bsr	driveCmd		| Step one!
    607 	movew	#80,d4			| Retries
    608 st01:
    609 	moveq	#stillStepping, d0	| Drive is still stepping?
    610 	bsr	driveStat
    611 	dbmi	d4,st01
    612 
    613 	cmpiw	#-1,d4
    614 	bne	st02
    615 
    616 	moveq	#cantStepErr,d2		| Not ready after many retries
    617 	bra	stDone
    618 st02:
    619 
    620 #if USE_DELAY
    621 	movel	#30,sp@-
    622 	jsr	_C_LABEL(delay)		| in mac68k/clock.c
    623 	addqw	#4,sp
    624 #else
    625 	movew	_TimeDBRA,d4		| dbra loops per ms
    626 	lsrw	#5,d4			| DIV 32
    627 st03:	dbra	d4,st03			| makes ca. 30 us
    628 #endif
    629 
    630 	dbra	d2,stLoop
    631 
    632 	moveq	#0,d2			| All is well
    633 stDone:
    634 	movel	d2,d0
    635 	moveml	sp@+,d1-d4/a0-a1
    636 	unlk	a6
    637 	rts
    638 
    639 
    640 /*
    641  * iwmReadSector -- read and decode the next available sector.
    642  *
    643  * TODO:	Poll SCC as long as interrupts are disabled (see top comment)
    644  *		Add a branch for Verify (compare to buffer)
    645  *		Understand and document the checksum algorithm!
    646  *
    647  *		XXX make "sizeof cylCache_t" a symbolic constant
    648  *
    649  * Parameters:	fp+08	l	Address of sector data buffer (512 bytes)
    650  *		fp+12	l	Address of sector header struct (I/O)
    651  *		fp+16	l	Address of cache buffer ptr array
    652  * Returns:	d0		result code
    653  * Local:	fp-2	w	CPU status register
    654  *		fp-3	b	side,
    655  *		fp-4	b	track,
    656  *		fp-5	b	sector wanted
    657  *		fp-6	b	retry count
    658  *		fp-7	b	sector read
    659  */
    660 ENTRY(iwmReadSector)
    661 	link	a6,#-8
    662 	moveml	d1-d7/a0-a5,sp@-
    663 
    664  	movel	_Via1Base,a1
    665 	movel	a6@(o_hdr),a4		| Addr of sector header struct
    666 
    667 	moveb	a4@+,a6@(-3)		| Save side bit,
    668 	moveb	a4@+,a6@(-4)		| track#,
    669 	moveb	a4@,a6@(-5)		| sector#
    670 	moveb	#2*maxGCRSectors,a6@(-6) | Max. retry count
    671 
    672 	movew	sr,a6@(-2)		| Save CPU status register
    673 	oriw	#0x0600,sr		| Block all interrupts
    674 
    675 rsNextSect:
    676 	movel	a6@(o_hdr),a4		| Addr of sector header struct
    677 	bsr	readSectHdr		| Get next available SECTOR header
    678 	bne	rsDone			| Return if error
    679 
    680 	/*
    681 	 * Is this the right track & side? If not, return with error
    682 	 */
    683 	movel	a6@(o_hdr),a4		| Sector header struct
    684 
    685 	moveb	a4@(o_side),d1		| Get actual side
    686 	lsrb	#3,d1			| "Normalize" side bit (to bit 0)
    687 	andb	#1,d1
    688 	moveb	a6@(-3),d2		| Get wanted side
    689 	eorb	d1,d2			| Compare side bits
    690 	bne	rsSeekErr		| Should be equal!
    691 
    692 	moveb	a6@(-4),d1		| Get track# we want
    693 	cmpb	a4@(o_track),d1		| Compare to the header we've read
    694 	beq	rsGetSect
    695 
    696 rsSeekErr:
    697 	moveq	#seekErr,d0		| Wrong track or side found
    698 	bra	rsDone
    699 
    700 	/*
    701 	 * Check for sector data lead-in 'D5 AA AD'
    702 	 * Registers:
    703 	 *	a0 points to data register of IWM as set up by readSectHdr
    704 	 *	a2 points to 'diskTo' translation table
    705 	 *	a4 points to tags buffer
    706 	 */
    707 rsGetSect:
    708 	moveb	a4@(2),a6@(-7)		| save sector number
    709 	lea	a4@(3),a4		| Beginning of tag buffer
    710 	moveq	#50,d3			| Max. retries for sector lookup
    711 rsLeadIn:
    712 	lea	dataLeadIn,a3		| Sector data lead-in
    713 	moveq	#0x03,d4		| is 3 bytes long
    714 rsLI1:
    715 	moveb	a0@,d2			| Get next byte
    716 	bpl	rsLI1
    717 	dbra	d3,rsLI2
    718 	moveq	#noDtaMkErr,d0		| Can't find a data mark
    719 	bra	rsDone
    720 
    721 rsLI2:
    722 	cmpb	a3@+,d2
    723 	bne	rsLeadIn		| If ne restart scan
    724 	subqw	#1,d4
    725 	bne	rsLI1
    726 
    727 	/*
    728 	 * We have found the lead-in. Now get the 12 tag bytes.
    729 	 * (We leave a3 pointing to 'dataLeadOut' for later.)
    730 	 */
    731 rsTagNyb0:
    732 	moveb	a0@,d3			| Get a char,
    733 	bpl	rsTagNyb0
    734 	moveb	a2@(0,d3),a4@+		| remap and store it
    735 
    736 	moveq	#0,d5			| Clear checksum registers
    737 	moveq	#0,d6
    738 	moveq	#0,d7
    739 	moveq	#10,d4			| Loop counter
    740 	moveq	#0,d3			| Data scratch reg
    741 
    742 rsTags:
    743 rsTagNyb1:
    744 	moveb	a0@,d3			| Get 2 bit nibbles
    745 	bpl	rsTagNyb1
    746 	moveb	a2@(0,d3),d1		| Remap disk byte
    747 	rolb	#2,d1
    748 	moveb	d1,d2
    749 	andib	#0xC0,d2		| Get top 2 bits for first byte
    750 rsTagNyb2:
    751 	moveb	a0@,d3			| Get first 6 bit nibble
    752 	bpl	rsTagNyb2
    753 	orb	a2@(0,d3),d2		| Remap it and complete first byte
    754 
    755 	moveb	d7,d3			| The X flag bit (a copy of the carry
    756 	addb	d7,d3			| flag) is added with the next addx
    757 
    758 	rolb	#1,d7
    759 	eorb	d7,d2
    760 	moveb	d2,a4@+			| Store tag byte
    761 	addxb	d2,d5			| See above
    762 
    763 	rolb	#2,d1
    764 	moveb	d1,d2
    765 	andib	#0xC0,d2		| Get top 2 bits for second byte
    766 rsTagNyb3:
    767 	moveb	a0@,d3			| Get second 6 bit nibble
    768 	bpl	rsTagNyb3
    769 	orb	a2@(0,d3),d2		| remap it and complete byte
    770 	eorb	d5,d2
    771 	moveb	d2,a4@+			| Store tag byte
    772 	addxb	d2,d6
    773 
    774 	rolb	#2,d1
    775 	andib	#0xC0,d1		| Get top 2 bits for third byte
    776 rsTagNyb4:
    777 	moveb	a0@,d3			| Get third 6 bit nibble
    778 	bpl	rsTagNyb4
    779 	orb	a2@(0,d3),d1		| remap it and complete byte
    780 	eorb	d6,d1
    781 	moveb	d1,a4@+			| Store tag byte
    782 	addxb	d1,d7
    783 
    784 	subqw	#3,d4			| Update byte counter (four 6&2 encoded
    785 	bpl	rsTags			| disk bytes make three data bytes).
    786 
    787 	/*
    788 	 * Jetzt sind wir hier...
    789 	 * ...und Thomas D. hat noch was zu sagen...
    790 	 *
    791 	 * We begin to read in the actual sector data.
    792 	 * Compare sector # to what we wanted: If it matches, read directly
    793 	 * to buffer, else read to track cache.
    794 	 */
    795 	movew	#0x01FE,d4		| Loop counter
    796 	moveq	#0,d1			| Clear d1.L
    797 	moveb	a6@(-7),d1		| Get sector# we have read
    798 	cmpb	a6@(-5),d1		| Compare to the sector# we want
    799 	bne	rsToCache
    800 	movel	a6@(o_buf),a4		| Sector data buffer
    801 	bra	rsData
    802 rsToCache:
    803 	movel	a6@(o_rslots),a4	| Base address of slot array
    804 	lslw	#3,d1			| sizeof cylCacheSlot_t is 8 bytes
    805 	movel	#-1,a4@(o_valid,d1)
    806 	movel	a4@(o_secbuf,d1),a4	| and get its buffer ptr
    807 rsData:
    808 rsDatNyb1:
    809 	moveb	a0@,d3			| Get 2 bit nibbles
    810 	bpl	rsDatNyb1
    811 	moveb	a2@(0,d3),d1		| Remap disk byte
    812 	rolb	#2,d1
    813 	moveb	d1,d2
    814 	andib	#0xC0,d2		| Get top 2 bits for first byte
    815 rsDatNyb2:
    816 	moveb	a0@,d3			| Get first 6 bit nibble
    817 	bpl	rsDatNyb2
    818 	orb	a2@(0,d3),d2		| Remap it and complete first byte
    819 
    820 	moveb	d7,d3			| The X flag bit (a copy of the carry
    821 	addb	d7,d3			| flag) is added with the next addx
    822 
    823 	rolb	#1,d7
    824 	eorb	d7,d2
    825 	moveb	d2,a4@+			| Store data byte
    826 	addxb	d2,d5			| See above
    827 
    828 	rolb	#2,d1
    829 	moveb	d1,d2
    830 	andib	#0xC0,d2		| Get top 2 bits for second byte
    831 rsDatNyb3:
    832 	moveb	a0@,d3			| Get second 6 bit nibble
    833 	bpl	rsDatNyb3
    834 	orb	a2@(0,d3),d2		| Remap it and complete byte
    835 	eorb	d5,d2
    836 	moveb	d2,a4@+			| Store data byte
    837 	addxb	d2,d6
    838 	tstw	d4
    839 	beq	rsCkSum			| Data read, continue with checksums
    840 
    841 	rolb	#2,d1
    842 	andib	#0xC0,d1		| Get top 2 bits for third byte
    843 rsDatNyb4:
    844 	moveb	a0@,d3			| Get third 6 bit nibble
    845 	bpl	rsDatNyb4
    846 	orb	a2@(0,d3),d1		| Remap it and complete byte
    847 	eorb	d6,d1
    848 	moveb	d1,a4@+			| Store data byte
    849 	addxb	d1,d7
    850 	subqw	#3,d4			| Update byte counter
    851 	bra	rsData
    852 
    853 	/*
    854 	 * Next read checksum bytes
    855 	 * While reading the sector data, three separate checksums are
    856 	 * maintained in D5/D6/D7 for the 1st/2nd/3rd data byte of each group.
    857 	 */
    858 rsCkSum:
    859 rsCkS1:
    860 	moveb	a0@,d3			| Get 2 bit nibbles
    861 	bpl	rsCkS1
    862 	moveb	a2@(0,d3),d1		| Remap disk byte
    863 	bmi	rsBadCkSum		| Fault! (Bad read)
    864 	rolb	#2,d1
    865 	moveb	d1,d2
    866 	andib	#0xC0,d2		| Get top 2 bits for first byte
    867 rsCkS2:
    868 	moveb	a0@,d3			| Get first 6 bit nibble
    869 	bpl	rsCkS2
    870 	moveb	a2@(0,d3),d3		| and remap it
    871 	bmi	rsBadCkSum		| Fault! ( > 0x3f is bad read)
    872 	orb	d3,d2			| Merge 6&2
    873 	cmpb	d2,d5			| Compare first checksum to D5
    874 	bne	rsBadCkSum		| Fault! (Checksum)
    875 
    876 	rolb	#2,d1
    877 	moveb	d1,d2
    878 	andib	#0xC0,d2		| Get top 2 bits for second byte
    879 rsCkS3:
    880 	moveb	a0@,d3			| Get second 6 bit nibble
    881 	bpl	rsCkS3
    882 	moveb	a2@(0,d3),d3		| and remap it
    883 	bmi	rsBadCkSum		| Fault! (Bad read)
    884 	orb	d3,d2			| Merge 6&2
    885 	cmpb	d2,d6			| Compare second checksum to D6
    886 	bne	rsBadCkSum		| Fault! (Checksum)
    887 
    888 	rolb	#2,d1
    889 	andib	#0xC0,d1		| Get top 2 bits for second byte
    890 rsCkS4:
    891 	moveb	a0@,d3			| Get third 6 bit nibble
    892 	bpl	rsCkS4
    893 	moveb	a2@(0,d3),d3		| and remap it
    894 	bmi	rsBadCkSum		| Fault! (Bad read)
    895 	orb	d3,d1			| Merge 6&2
    896 	cmpb	d1,d7			| Compare third checksum to D7
    897 	beq	rsLdOut			| Fault! (Checksum)
    898 
    899 rsBadCkSum:
    900 	moveq	#badDCkSum,d0		| Bad data mark checksum
    901 	bra	rsDone
    902 
    903 rsBadDBtSlp:
    904 	moveq	#badDBtSlp,d0		| One of the data mark bit slip
    905 	bra	rsDone			| nibbles was incorrect
    906 
    907 	/*
    908 	 * We have gotten the checksums allright, now look for the
    909 	 * sector data lead-out 'DE AA'
    910 	 * (We have a3 still pointing to 'dataLeadOut'; this part of the
    911 	 * table is used for writing to disk, too.)
    912 	 */
    913 rsLdOut:
    914 	moveq	#1,d4			| Is two bytes long {1,0}
    915 rsLdOut1:
    916 	moveb	a0@,d3			| Get token
    917 	bpl	rsLdOut1
    918 	cmpb	a3@+,d3
    919 	bne	rsBadDBtSlp		| Fault!
    920 	dbra	d4,rsLdOut1
    921 	moveq	#0,d0			| OK.
    922 
    923 	/*
    924 	 * See if we got the sector we wanted. If not, and no error
    925 	 * occurred, mark buffer valid. Else ignore the sector.
    926 	 * Then, read on.
    927 	 */
    928 rsDone:
    929 	movel	a6@(o_hdr),a4		| Addr of sector header struct
    930 	moveb	a4@(o_sector),d1	| Get # of sector we have just read
    931 	cmpb	a6@(-5),d1		| Compare to the sector we want
    932 	beq	rsAllDone
    933 
    934 	tstb	d0			| Any error? Simply ignore data
    935 	beq	rsBufValid
    936 	lslw	#3,d1			| sizeof cylCacheSlot_t is 8 bytes
    937 	movel	a6@(o_rslots),a4
    938 	clrl	a4@(o_valid,d1)		| Mark buffer content "invalid"
    939 
    940 rsBufValid:
    941 	subqb	#1,a6@(-6)		| max. retries
    942 	bne	rsNextSect
    943 					| Sector not found, but
    944 	tstb	d0			| don't set error code if we
    945 	bne	rsAllDone		| already have one.
    946 	moveq	#sectNFErr,d0
    947 rsAllDone:
    948 	movew	a6@(-2),sr		| Restore interrupt mask
    949 	moveml	sp@+,d1-d7/a0-a5
    950 	unlk	a6
    951 	rts
    952 
    953 
    954 /*
    955  * iwmWriteSector -- encode and write data to the specified sector.
    956  *
    957  * TODO:	Poll SCC as long as interrupts are disabled (see top comment)
    958  *		Understand and document the checksum algorithm!
    959  *
    960  *		XXX Use registers more efficiently
    961  *
    962  * Parameters:	fp+8	l	Address of sector header struct (I/O)
    963  *		fp+12	l	Address of cache buffer ptr array
    964  * Returns:	d0		result code
    965  *
    966  * Local:	fp-2	w	CPU status register
    967  *		fp-3	b	side,
    968  *		fp-4	b	track,
    969  *		fp-5	b	sector wanted
    970  *		fp-6	b	retry count
    971  *		fp-10	b	current slot
    972  */
    973 ENTRY(iwmWriteSector)
    974 	link	a6,#-10
    975 	moveml	d1-d7/a0-a5,sp@-
    976 
    977  	movel	_Via1Base,a1
    978 	movel	a6@(o_hdr),a4		| Addr of sector header struct
    979 
    980 	moveb	a4@+,a6@(-3)		| Save side bit,
    981 	moveb	a4@+,a6@(-4)		| track#,
    982 	moveb	a4@,a6@(-5)		| sector#
    983 	moveb	#maxGCRSectors,a6@(-6)	| Max. retry count
    984 
    985 	movew	sr,a6@(-2)		| Save CPU status register
    986 	oriw	#0x0600,sr		| Block all interrupts
    987 
    988 wsNextSect:
    989 	movel	a6@(o_hdr),a4
    990 	bsr	readSectHdr		| Get next available sector header
    991 	bne	wsAllDone		| Return if error
    992 
    993 	/*
    994 	 * Is this the right track & side? If not, return with error
    995 	 */
    996 	movel	a6@(o_hdr),a4		| Sector header struct
    997 
    998 	moveb	a4@(o_side),d1		| Get side#
    999 	lsrb	#3,d1			| "Normalize" side bit...
   1000 	andb	#1,d1
   1001 	moveb	a6@(-3),d2		| Get wanted side
   1002 	eorb	d1,d2			| Compare side bits
   1003 	bne	wsSeekErr
   1004 
   1005 	moveb	a6@(-4),d1		| Get wanted track#
   1006 	cmpb	a4@(o_track),d1		| Compare to the read header
   1007 	beq	wsCompSect
   1008 
   1009 wsSeekErr:
   1010 	moveq	#seekErr,d0		| Wrong track or side
   1011 	bra	wsAllDone
   1012 
   1013 	/*
   1014 	 * Look up the current sector number in the cache.
   1015 	 * If the buffer is dirty ("valid"), write it to disk. If not,
   1016 	 * loop over all the slots and return if all of them are clean.
   1017 	 *
   1018 	 * Alternatively, we could decrement a "dirty sectors" counter here.
   1019 	 */
   1020 wsCompSect:
   1021 	moveq	#0,d1			| Clear register
   1022 	moveb	a4@(o_sector),d1	| get the # of header read
   1023 	lslw	#3,d1			| sizeof cylCacheSlot_t is 8 bytes
   1024 	movel	a6@(o_wslots),a4
   1025 	tstl	a4@(o_valid,d1)		| Sector dirty?
   1026 	bne	wsBufDirty
   1027 
   1028 	moveq	#maxGCRSectors-1,d2	| Any dirty sectors left?
   1029 wsChkDty:
   1030 	movew	d2,d1
   1031 	lslw	#3,d1			| sizeof cylCacheSlot_t is 8 bytes
   1032 	tstl	a4@(o_valid,d1)
   1033 	bne	wsNextSect		| Sector dirty?
   1034 	dbra	d2,wsChkDty
   1035 
   1036 	bra	wsAllDone		| We are through with this track.
   1037 
   1038 
   1039 	/*
   1040 	 * Write sync pattern and sector data lead-in 'D5 AA'. The
   1041 	 * missing 'AD' is made up by piping 0x0B through the nibble
   1042 	 * table (toDisk).
   1043 	 *
   1044 	 * To set up IWM for writing:
   1045 	 *
   1046 	 * access q6H & write first byte to q7H.
   1047 	 * Then check bit 7 of q6L (status reg) for 'IWM ready'
   1048 	 * and write subsequent bytes to q6H.
   1049 	 *
   1050 	 * Registers:
   1051 	 *	a0	IWM base address (later: data register)
   1052 	 *	a1	Via1Base
   1053 	 *	a2	IWM handshake register
   1054 	 *	a3	data (tags buffer, data buffer)
   1055 	 *	a4	Sync pattern, 'toDisk' translation table
   1056 	 */
   1057 wsBufDirty:
   1058 	movel	_IWMBase,a0
   1059 	lea	a4@(0,d1),a3
   1060 	movel	a3,a6@(-10)		| Save ptr to current slot
   1061 	tstb	a0@(q6H)		| Enable writing to disk
   1062 	movel	a6@(o_hdr),a4		| Sector header struct
   1063 	lea	a4@(o_Tags),a3		| Point a3 to tags buffer
   1064 	lea	syncPattern,a4
   1065 
   1066 	moveb	a4@+,a0@(q7H)		| Write first sync byte
   1067 	lea	a0@(q6L),a2		| Point a2 to handshake register
   1068 	lea	a0@(q6H),a0		| Point a0 to IWM data register
   1069 
   1070 	moveq	#6,d0			| Loop counter for sync bytes
   1071 	moveq	#0,d2
   1072 	moveq	#0,d3
   1073 	movel	#0x02010009,d4		| Loop counters for tag/sector data
   1074 
   1075 	/*
   1076 	 * Write 5 sync bytes and first byte of sector data lead-in
   1077 	 */
   1078 wsLeadIn:
   1079 	moveb	a4@+,d1			| Get next sync byte
   1080 wsLI1:
   1081 	tstb	a2@			| IWM ready?
   1082 	bpl	wsLI1
   1083 	moveb	d1,a0@			| Write it to disk
   1084 	subqw	#1,d0
   1085 	bne	wsLeadIn
   1086 
   1087 	moveb	a4@+,d1			| Write 2nd byte of sector lead-in
   1088 	lea	toDisk,a4		| Point a4 to nibble translation table
   1089 wsLI2:
   1090 	tstb	a2@			| IWM ready?
   1091 	bpl	wsLI2
   1092 	moveb	d1,a0@			| Write it to disk
   1093 
   1094 	moveq	#0,d5			| Clear checksum registers
   1095 	moveq	#0,d6
   1096 	moveq	#0,d7
   1097 
   1098 	moveq	#0x0B,d1		| 3rd byte of sector data lead-in
   1099 					| (Gets translated to 0xAD)
   1100 	moveb	a3@+,d2			| Get 1st byte from tags buffer
   1101 	bra	wsDataEntry
   1102 
   1103 	/*
   1104 	 * The following loop reads the content of the tags buffer (12 bytes)
   1105 	 * and the data buffer (512 bytes).
   1106 	 * Each pass reads out three bytes and
   1107 	 * a) splits them 6&2 into three 6 bit nibbles and a fourth byte
   1108 	 *    consisting of the three 2 bit nibbles
   1109 	 * b) encodes the nibbles with a table to disk bytes (bit 7 set, no
   1110 	 *    more than two consecutive zero bits) and writes them to disk as
   1111 	 *
   1112 	 *    00mmnnoo		fragment 2 bit nibbles
   1113 	 *    00mmmmmm		6 bit nibble -- first byte
   1114 	 *    00nnnnnn		6 bit nibble -- second byte
   1115 	 *    00oooooo		6 bit nibble -- third byte
   1116 	 *
   1117 	 * c) adds up three 8 bit checksums, one for each of the bytes written.
   1118 	 */
   1119 wsSD1:
   1120 	movel	a6@(-10),a3		| Get ptr to current slot
   1121 	movel	a3@(o_secbuf),a3	| Get start of sector data buffer
   1122 
   1123 wsData:
   1124 	addxb	d2,d7
   1125 	eorb	d6,d2
   1126 	moveb	d2,d3
   1127 	lsrw	#6,d3			| Put 2 bit nibbles into place
   1128 wsRDY01:
   1129 	tstb	a2@			| IWM ready?
   1130 	bpl	wsRDY01
   1131 	moveb	a4@(0,d3),a0@		| Translate nibble and write
   1132 	subqw	#3,d4			| Update counter
   1133 	moveb	d7,d3
   1134 	addb	d7,d3			| Set X flag (??)
   1135 	rolb	#1,d7
   1136 	andib	#0x3F,d0
   1137 wsRDY02:
   1138 	tstb	a2@			| IWM ready?
   1139 	bpl	wsRDY02
   1140 	moveb	a4@(0,d0),a0@		| Translate nibble and write
   1141 
   1142 	/*
   1143 	 * We enter with the last byte of the sector data lead-in
   1144 	 * between our teeth (D1, that is).
   1145 	 */
   1146 wsDataEntry:
   1147 	moveb	a3@+,d0			| Get first byte
   1148 	addxb	d0,d5
   1149 	eorb	d7,d0
   1150 	moveb	d0,d3			| Keep top two bits
   1151 	rolw	#2,d3			| by shifting them to MSByte
   1152 	andib	#0x3F,d1
   1153 wsRDY03:
   1154 	tstb	a2@			| IWM ready?
   1155 	bpl	wsRDY03
   1156 	moveb	a4@(0,d1),a0@		| Translate nibble and write
   1157 
   1158 	moveb	a3@+,d1			| Get second byte
   1159 	addxb	d1,d6
   1160 	eorb	d5,d1
   1161 	moveb	d1,d3			| Keep top two bits
   1162 	rolw	#2,d3			| by shifting them to MSByte
   1163 	andib	#0x3F,d2
   1164 wsRDY04:
   1165 	tstb	a2@			| IWM ready?
   1166 	bpl	wsRDY04
   1167 	moveb	a4@(0,d2),a0@		| Translate nibble and write
   1168 
   1169 	/*
   1170 	 * XXX We have a classic off-by-one error here: the last access
   1171 	 * reaches beyond the data buffer which bombs with memory
   1172 	 * protection. The value read isn't used anyway...
   1173 	 * Hopefully there is enough time for an additional check
   1174 	 * (exit the last loop cycle before the buffer access).
   1175 	 */
   1176 	tstl	d4			| Last loop cycle?
   1177 	beq	wsSDDone		| Then get out while we can.
   1178 
   1179 	moveb	a3@+,d2			| Get third byte
   1180 	tstw	d4			| First write tag buffer,...
   1181 	bne	wsData
   1182 
   1183 	swap	d4			| ...then write data buffer
   1184 	bne	wsSD1
   1185 
   1186 	/*
   1187 	 * Write nibbles for last 2 bytes, then
   1188 	 * split checksum bytes in 6&2 and write them to disk
   1189 	 */
   1190 wsSDDone:
   1191 	clrb	d3			| No 513th byte
   1192 	lsrw	#6,d3			| Set up 2 bit nibbles
   1193 wsRDY05:
   1194 	tstb	a2@			| IWM ready?
   1195 	bpl	wsRDY05
   1196 	moveb	a4@(0,d3),a0@		| Write fragments
   1197 	moveb	d5,d3
   1198 	rolw	#2,d3
   1199 	moveb	d6,d3
   1200 	rolw	#2,d3
   1201 	andib	#0x3F,d0
   1202 wsRDY06:
   1203 	tstb	a2@			| IWM ready?
   1204 	bpl	wsRDY06
   1205 	moveb	a4@(0,d0),a0@		| Write 511th byte
   1206 	andib	#0x3F,d1
   1207 wsRDY07:
   1208 	tstb	a2@			| IWM ready?
   1209 	bpl	wsRDY07
   1210 	moveb	a4@(0,d1),a0@		| write 512th byte
   1211 	moveb	d7,d3
   1212 	lsrw	#6,d3			| Get fragments ready
   1213 wsRDY08:
   1214 	tstb	a2@			| IWM ready?
   1215 	bpl	wsRDY08
   1216 	moveb	a4@(0,d3),a0@		| Write fragments
   1217 	andib	#0x3F,d5
   1218 wsRDY09:
   1219 	tstb	a2@			| IWM ready?
   1220 	bpl	wsRDY09
   1221 	moveb	a4@(0,d5),a0@		| Write first checksum byte
   1222 	andib	#0x3F,D6
   1223 wsRDY10:
   1224 	tstb	a2@			| IWM ready?
   1225 	bpl	wsRDY10
   1226 	moveb	a4@(0,d6),a0@		| Write second checksum byte
   1227 	andib	#0x3F,d7
   1228 wsRDY11:
   1229 	tstb	a2@			| IWM ready?
   1230 	bpl	wsRDY11
   1231 	moveb	a4@(0,d7),a0@		| Write third checksum byte
   1232 
   1233 	/*
   1234 	 * Write sector data lead-out
   1235 	 */
   1236 	lea	dataLeadOut,a4		| Sector data lead-out
   1237 	moveq	#3,d2			| Four bytes long {3,2,1,0}
   1238 wsLeadOut:
   1239 	moveb	a2@,d1			| IWM ready?
   1240 	bpl	wsLeadOut
   1241 	moveb	a4@+,a0@		| Write lead-out
   1242 	dbf	d2,wsLeadOut
   1243 
   1244 	moveq	#0,d0
   1245 	btst	#6,d1			| Check IWM underrun bit
   1246 	bne	wsNoErr
   1247 
   1248 	moveq	#wrUnderRun,d0		| Could not write
   1249 					| fast enough to keep up with IWM
   1250 wsNoErr:
   1251 	tstb	a0@(0x0200)		| q7L -- Write OFF
   1252 
   1253 wsDone:
   1254 	tstb	d0			| Any error? Simply retry
   1255 	bne	wsBufInvalid
   1256 
   1257 	movel	a6@(-10),a4		| Else, get ptr to current slot
   1258 	clrl	a4@(o_valid)		| Mark current buffer "clean"
   1259 	bra	wsNextSect
   1260 
   1261 wsBufInvalid:
   1262 	subqb	#1,a6@(-6)		| retries
   1263 	bne	wsNextSect
   1264 					| Sector not found, but
   1265 	tstb	d0			| don't set error code if we
   1266 	bne	wsAllDone		| already have one.
   1267 	moveq	#sectNFErr,d0
   1268 
   1269 wsAllDone:
   1270 	movew	a6@(-2),sr		| Restore interrupt mask
   1271 	moveml	sp@+,d1-d7/a0-a5
   1272 	unlk	a6
   1273 	rts
   1274 
   1275 
   1276 
   1277 /**
   1278  **	Local functions
   1279  **/
   1280 
   1281 /*
   1282  * iwmDelay
   1283  *
   1284  * In-kernel calls to delay() in mac68k/clock.c bomb
   1285  *
   1286  * Parameters:	d0	delay in milliseconds
   1287  * Trashes:	d0, d1
   1288  * Returns:	-
   1289  */
   1290 iwmDelay:
   1291 	/* TimeDBRA is ~8K for 040/33 machines, so we need nested loops */
   1292 id00:	movew	_TimeDBRA,d1		| dbra loops per ms
   1293 id01:	dbra	d1,id01			|
   1294 	dbra	d0,id00
   1295 	rts
   1296 
   1297 
   1298 /*
   1299  * selDriveReg -- Select drive status/control register
   1300  *
   1301  * Parameters:	d0	register #
   1302  *			(bit 0 - CA2, bit 1 - SEL, bit 2 - CA0, bit 3 - CA1)
   1303  *		a0	IWM base address
   1304  *		a1	VIA base address
   1305  * Returns:	d0	register # (unchanged)
   1306  */
   1307 selDriveReg:
   1308 	tstb	a0@(ph0H)		| default CA0 to 1 (says IM III)
   1309 	tstb	a0@(ph1H)		| default CA1 to 1
   1310 
   1311 	btst	#0,d0			| bit 0 set => CA2 on
   1312 	beq	se00
   1313 	tstb	a0@(ph2H)
   1314 	bra	se01
   1315 se00:
   1316 	tstb	a0@(ph2L)
   1317 
   1318 se01:
   1319 	btst	#1,d0			| bit 1 set => SEL on (VIA 1)
   1320 	beq	se02
   1321 	bset	#vHeadSel,a1@(vBufA)
   1322 	bra	se03
   1323 se02:
   1324 	bclr	#vHeadSel,a1@(vBufA)
   1325 
   1326 se03:
   1327 	btst	#2,d0			| bit 2 set => CA0 on
   1328 	bne	se04
   1329 	tstb	a0@(ph0L)
   1330 
   1331 se04:
   1332 	btst	#3,d0			| bit 3 set => CA1 on
   1333 	bne	se05
   1334 	tstb	a0@(ph1L)
   1335 se05:
   1336 	rts
   1337 
   1338 
   1339 
   1340 /*
   1341  * dstatus -- check drive status (bit 7 - N flag) wrt. a previously
   1342  * set status tag.
   1343  *
   1344  * Parameters:	d0	register selector
   1345  *		a0	IWM base address
   1346  * Returns:	d0	status
   1347  */
   1348 dstatus:
   1349 	tstb	a0@(q6H)
   1350 	moveb	a0@(q7L),d0
   1351 	tstb	a0@(q6L)		| leave in "read data reg"
   1352 	tstb	d0			| state for safety
   1353 
   1354 	rts
   1355 
   1356 
   1357 /*
   1358  * driveStat -- query drive status.
   1359  *
   1360  * Parameters:	a0	IWMBase
   1361  *		a1	VIABase
   1362  *		d0	register selector
   1363  * Returns:	d0	status (Bit 7)
   1364  */
   1365 driveStat:
   1366 	tstb	a0@(mtrOn)		| ENABLE; turn drive on
   1367 	bsr	selDriveReg
   1368 	bsr	dstatus
   1369 
   1370 	rts
   1371 
   1372 
   1373 /*
   1374  * dtrigger -- toggle LSTRB line to give drive a strobe signal
   1375  * IM III says pulse length = 1 us < t < 1 ms
   1376  *
   1377  * Parameters:	a0	IWMBase
   1378  *		a1	VIABase
   1379  * Returns:	-
   1380  */
   1381 dtrigger:
   1382 	tstb	a0@(ph3H)		| LSTRB high
   1383 	moveb	a1@(vBufA),a1@(vBufA)	| intelligent nop seen in q700 ROM
   1384 	tstb	a0@(ph3L)		| LSTRB low
   1385 
   1386 	rts
   1387 
   1388 
   1389 /*
   1390  * driveCmd -- send command to drive.
   1391  *
   1392  * Parameters:	a0	IWMBase
   1393  *		a1	VIABase
   1394  *		d0	Command token
   1395  * Returns:	-
   1396  */
   1397 driveCmd:
   1398 	bsr	selDriveReg
   1399 	bsr	dtrigger
   1400 
   1401 	rts
   1402 
   1403 
   1404 /*
   1405  * readSectHdr -- read and decode the next available sector header.
   1406  *
   1407  * TODO:	Poll SCC as long as interrupts are disabled.
   1408  *
   1409  * Parameters:	a4	sectorHdr_t address
   1410  * Returns:	d0	result code
   1411  * Uses:	d0-d4, a0, a2-a4
   1412  */
   1413 readSectHdr:
   1414 	moveq	#3,d4			| Read 3 chars from IWM for sync
   1415 	movew	#600,d3			| Retries to sync to disk
   1416 	moveq	#0,d2			| Clear scratch regs
   1417 	moveq	#0,d1
   1418 	moveq	#0,d0
   1419  	movel	_IWMBase,a0		| IWM base address
   1420 
   1421 	tstb	a0@(q7L)
   1422 	lea	a0@(q6L),a0		| IWM data register
   1423 shReadSy:
   1424 	moveb	a0@,d2			| Read char
   1425 	dbra	d3,shSeekSync
   1426 
   1427 	moveq	#noNybErr,d0		| Disk is blank?
   1428 	bra	shDone
   1429 
   1430 shSeekSync:
   1431 	bpl	shReadSy		| No char at IWM, repeat read
   1432 	subqw	#1,d4
   1433 	bne	shReadSy
   1434 	/*
   1435 	 * When we get here, the IWM should be in sync with the data
   1436 	 * stream from disk.
   1437 	 * Next look for sector header lead-in 'D5 AA 96'
   1438 	 */
   1439 	movew	#1500,d3		| Retries to seek header
   1440 shLeadIn:
   1441 	lea	hdrLeadIn,a3		| Sector header lead-in bytes
   1442 	moveq	#0x03,d4		| is 3 bytes long
   1443 shLI1:
   1444 	moveb	a0@,d2			| Get next byte
   1445 	bpl	shLI1			| No char at IWM, repeat read
   1446 	dbra	d3,shLI2
   1447 	moveq	#noAdrMkErr,d0		| Can't find an address mark
   1448 	bra	shDone
   1449 
   1450 shLI2:
   1451 	cmpb	a3@+,d2
   1452 	bne	shLeadIn		| If ne restart scan
   1453 	subqw	#1,d4
   1454 	bne	shLI1
   1455 	/*
   1456 	 * We have found the lead-in. Now get the header information.
   1457 	 * Reg d4 holds the checksum.
   1458 	 */
   1459 	lea	diskTo-0x90,a2		| Translate disk bytes -> 6&2
   1460 shHdr1:
   1461 	moveb	a0@,d0			| Get 1st char
   1462 	bpl	shHdr1
   1463 	moveb	a2@(0,d0),d1		| and remap it
   1464 	moveb	d1,d4
   1465 	rorw	#6,d1			| separate 2:6, drop hi bits
   1466 shHdr2:
   1467 	moveb	a0@,d0			| Get 2nd char
   1468 	bpl	shHdr2
   1469 	moveb	a2@(0,d0),d2		| and remap it
   1470 	eorb	d2,d4
   1471 shHdr3:
   1472 	moveb	a0@,d0			| Get 3rd char
   1473 	bpl	shHdr3
   1474 	moveb	a2@(0,d0),d1		| and remap it
   1475 	eorb	d1,d4
   1476 	rolw	#6,d1			|
   1477 shHdr4:
   1478 	moveb	a0@,d0			| Get 4th char
   1479 	bpl	shHdr4
   1480 	moveb	a2@(0,d0),d3		| and remap it
   1481 	eorb	d3,d4
   1482 shHdr5:
   1483 	moveb	a0@,d0			| Get checksum byte
   1484 	bpl	shHdr5
   1485 	moveb	a2@(0,d0),d5		| and remap it
   1486 	eorb	d5,d4
   1487 	bne	shCsErr			| Checksum ok?
   1488 	/*
   1489 	 * We now have in
   1490 	 * d1/lsb	track number
   1491 	 * d1/msb	bit 3 is side bit
   1492 	 * d2		sector number
   1493 	 * d3		???
   1494 	 * d5		checksum (=0)
   1495 	 *
   1496 	 * Next check for lead-out.
   1497 	 */
   1498 	moveq	#1,d4			| is 2 bytes long
   1499 shHdr6:
   1500 	moveb	a0@,d0			| Get token
   1501 	bpl	shHdr6
   1502 	cmpb	a3@+,d0			| Check
   1503 	bne	shLOErr			| Fault!
   1504 	dbra	d4,shHdr6
   1505 	movew	d1,d0			| Isolate side bit
   1506 	lsrw	#8,d0
   1507 	moveb	d0,a4@+			| and store it
   1508 	moveb	d1,a4@+			| Store track number
   1509 	moveb	d2,a4@+			| and sector number
   1510 	moveq	#0,d0			| All is well
   1511 	bra	shDone
   1512 
   1513 shCsErr:
   1514 	moveq	#badCkSmErr,d0		| Bad sector header checksum
   1515 	bra	shDone
   1516 shLOErr:
   1517 	moveq	#badBtSlpErr,d0		| Bad address mark (no lead-out)
   1518 
   1519 shDone:
   1520 	tstl	d0			| Set flags
   1521 	rts
   1522