Home | History | Annotate | Line # | Download | only in obio
iwm.s revision 1.3
      1 /*	$NetBSD: iwm.s,v 1.3 2001/11/20 03:19:44 chs 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	_C_LABEL(TimeDBRA)	| in mac68k/macrom.c
     77 	.extern _C_LABEL(Via1Base)	| in mac68k/machdep.c
     78 	.extern	_C_LABEL(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	_C_LABEL(IWMBase),%a0
    175 	movel	_C_LABEL(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	_C_LABEL(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	_C_LABEL(IWMBase),%a0
    305 	movel	_C_LABEL(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	_C_LABEL(IWMBase),%a0
    384 	movel	_C_LABEL(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	_C_LABEL(IWMBase),%a0
    438 	movel	_C_LABEL(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	_C_LABEL(IWMBase),%a0
    463 	movel	_C_LABEL(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	_C_LABEL(IWMBase),%a0
    496 	movel	_C_LABEL(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	_C_LABEL(IWMBase),%a0
    528 	movel	_C_LABEL(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	_C_LABEL(IWMBase),%a0
    589 	movel	_C_LABEL(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	_C_LABEL(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	_C_LABEL(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
    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
    857 	 * each group.
    858 	 */
    859 rsCkSum:
    860 rsCkS1:
    861 	moveb	%a0@,%d3		| Get 2 bit nibbles
    862 	bpl	rsCkS1
    863 	moveb	%a2@(0,%d3),%d1		| Remap disk byte
    864 	bmi	rsBadCkSum		| Fault! (Bad read)
    865 	rolb	#2,%d1
    866 	moveb	%d1,%d2
    867 	andib	#0xC0,%d2		| Get top 2 bits for first byte
    868 rsCkS2:
    869 	moveb	%a0@,%d3		| Get first 6 bit nibble
    870 	bpl	rsCkS2
    871 	moveb	%a2@(0,%d3),%d3		| and remap it
    872 	bmi	rsBadCkSum		| Fault! ( > 0x3f is bad read)
    873 	orb	%d3,%d2			| Merge 6&2
    874 	cmpb	%d2,%d5			| Compare first checksum to %D5
    875 	bne	rsBadCkSum		| Fault! (Checksum)
    876 
    877 	rolb	#2,%d1
    878 	moveb	%d1,%d2
    879 	andib	#0xC0,%d2		| Get top 2 bits for second byte
    880 rsCkS3:
    881 	moveb	%a0@,%d3		| Get second 6 bit nibble
    882 	bpl	rsCkS3
    883 	moveb	%a2@(0,%d3),%d3		| and remap it
    884 	bmi	rsBadCkSum		| Fault! (Bad read)
    885 	orb	%d3,%d2			| Merge 6&2
    886 	cmpb	%d2,%d6			| Compare second checksum to %D6
    887 	bne	rsBadCkSum		| Fault! (Checksum)
    888 
    889 	rolb	#2,%d1
    890 	andib	#0xC0,%d1		| Get top 2 bits for second byte
    891 rsCkS4:
    892 	moveb	%a0@,%d3		| Get third 6 bit nibble
    893 	bpl	rsCkS4
    894 	moveb	%a2@(0,%d3),%d3		| and remap it
    895 	bmi	rsBadCkSum		| Fault! (Bad read)
    896 	orb	%d3,%d1			| Merge 6&2
    897 	cmpb	%d1,%d7			| Compare third checksum to %D7
    898 	beq	rsLdOut			| Fault! (Checksum)
    899 
    900 rsBadCkSum:
    901 	moveq	#badDCkSum,%d0		| Bad data mark checksum
    902 	bra	rsDone
    903 
    904 rsBadDBtSlp:
    905 	moveq	#badDBtSlp,%d0		| One of the data mark bit slip
    906 	bra	rsDone			| nibbles was incorrect
    907 
    908 	/*
    909 	 * We have gotten the checksums allright, now look for the
    910 	 * sector data lead-out 'DE AA'
    911 	 * (We have %a3 still pointing to 'dataLeadOut'; this part of the
    912 	 * table is used for writing to disk, too.)
    913 	 */
    914 rsLdOut:
    915 	moveq	#1,%d4			| Is two bytes long {1,0}
    916 rsLdOut1:
    917 	moveb	%a0@,%d3		| Get token
    918 	bpl	rsLdOut1
    919 	cmpb	%a3@+,%d3
    920 	bne	rsBadDBtSlp		| Fault!
    921 	dbra	%d4,rsLdOut1
    922 	moveq	#0,%d0			| OK.
    923 
    924 	/*
    925 	 * See if we got the sector we wanted. If not, and no error
    926 	 * occurred, mark buffer valid. Else ignore the sector.
    927 	 * Then, read on.
    928 	 */
    929 rsDone:
    930 	movel	%a6@(o_hdr),%a4		| Addr of sector header struct
    931 	moveb	%a4@(o_sector),%d1	| Get # of sector we have just read
    932 	cmpb	%a6@(-5),%d1		| Compare to the sector we want
    933 	beq	rsAllDone
    934 
    935 	tstb	%d0			| Any error? Simply ignore data
    936 	beq	rsBufValid
    937 	lslw	#3,%d1			| sizeof cylCacheSlot_t is 8 bytes
    938 	movel	%a6@(o_rslots),%a4
    939 	clrl	%a4@(o_valid,%d1)	| Mark buffer content "invalid"
    940 
    941 rsBufValid:
    942 	subqb	#1,%a6@(-6)		| max. retries
    943 	bne	rsNextSect
    944 					| Sector not found, but
    945 	tstb	%d0			| don't set error code if we
    946 	bne	rsAllDone		| already have one.
    947 	moveq	#sectNFErr,%d0
    948 rsAllDone:
    949 	movew	%a6@(-2),%sr		| Restore interrupt mask
    950 	moveml	%sp@+,%d1-%d7/%a0-%a5
    951 	unlk	%a6
    952 	rts
    953 
    954 
    955 /*
    956  * iwmWriteSector -- encode and write data to the specified sector.
    957  *
    958  * TODO:	Poll SCC as long as interrupts are disabled (see top comment)
    959  *		Understand and document the checksum algorithm!
    960  *
    961  *		XXX Use registers more efficiently
    962  *
    963  * Parameters:	%fp+8	l	Address of sector header struct (I/O)
    964  *		%fp+12	l	Address of cache buffer ptr array
    965  * Returns:	%d0		result code
    966  *
    967  * Local:	%fp-2	w	CPU status register
    968  *		%fp-3	b	side,
    969  *		%fp-4	b	track,
    970  *		%fp-5	b	sector wanted
    971  *		%fp-6	b	retry count
    972  *		%fp-10	b	current slot
    973  */
    974 ENTRY(iwmWriteSector)
    975 	link	%a6,#-10
    976 	moveml	%d1-%d7/%a0-%a5,%sp@-
    977 
    978  	movel	_C_LABEL(Via1Base),%a1
    979 	movel	%a6@(o_hdr),%a4		| Addr of sector header struct
    980 
    981 	moveb	%a4@+,%a6@(-3)		| Save side bit,
    982 	moveb	%a4@+,%a6@(-4)		| track#,
    983 	moveb	%a4@,%a6@(-5)		| sector#
    984 	moveb	#maxGCRSectors,%a6@(-6)	| Max. retry count
    985 
    986 	movew	%sr,%a6@(-2)		| Save CPU status register
    987 	oriw	#0x0600,%sr		| Block all interrupts
    988 
    989 wsNextSect:
    990 	movel	%a6@(o_hdr),%a4
    991 	bsr	readSectHdr		| Get next available sector header
    992 	bne	wsAllDone		| Return if error
    993 
    994 	/*
    995 	 * Is this the right track & side? If not, return with error
    996 	 */
    997 	movel	%a6@(o_hdr),%a4		| Sector header struct
    998 
    999 	moveb	%a4@(o_side),%d1	| Get side#
   1000 	lsrb	#3,%d1			| "Normalize" side bit...
   1001 	andb	#1,%d1
   1002 	moveb	%a6@(-3),%d2		| Get wanted side
   1003 	eorb	%d1,%d2			| Compare side bits
   1004 	bne	wsSeekErr
   1005 
   1006 	moveb	%a6@(-4),%d1		| Get wanted track#
   1007 	cmpb	%a4@(o_track),%d1	| Compare to the read header
   1008 	beq	wsCompSect
   1009 
   1010 wsSeekErr:
   1011 	moveq	#seekErr,%d0		| Wrong track or side
   1012 	bra	wsAllDone
   1013 
   1014 	/*
   1015 	 * Look up the current sector number in the cache.
   1016 	 * If the buffer is dirty ("valid"), write it to disk. If not,
   1017 	 * loop over all the slots and return if all of them are clean.
   1018 	 *
   1019 	 * Alternatively, we could decrement a "dirty sectors" counter here.
   1020 	 */
   1021 wsCompSect:
   1022 	moveq	#0,%d1			| Clear register
   1023 	moveb	%a4@(o_sector),%d1	| get the # of header read
   1024 	lslw	#3,%d1			| sizeof cylCacheSlot_t is 8 bytes
   1025 	movel	%a6@(o_wslots),%a4
   1026 	tstl	%a4@(o_valid,%d1)	| Sector dirty?
   1027 	bne	wsBufDirty
   1028 
   1029 	moveq	#maxGCRSectors-1,%d2	| Any dirty sectors left?
   1030 wsChkDty:
   1031 	movew	%d2,%d1
   1032 	lslw	#3,%d1			| sizeof cylCacheSlot_t is 8 bytes
   1033 	tstl	%a4@(o_valid,%d1)
   1034 	bne	wsNextSect		| Sector dirty?
   1035 	dbra	%d2,wsChkDty
   1036 
   1037 	bra	wsAllDone		| We are through with this track.
   1038 
   1039 
   1040 	/*
   1041 	 * Write sync pattern and sector data lead-in 'D5 AA'. The
   1042 	 * missing 'AD' is made up by piping 0x0B through the nibble
   1043 	 * table (toDisk).
   1044 	 *
   1045 	 * To set up IWM for writing:
   1046 	 *
   1047 	 * access q6H & write first byte to q7H.
   1048 	 * Then check bit 7 of q6L (status reg) for 'IWM ready'
   1049 	 * and write subsequent bytes to q6H.
   1050 	 *
   1051 	 * Registers:
   1052 	 *	%a0	IWM base address (later: data register)
   1053 	 *	%a1	Via1Base
   1054 	 *	%a2	IWM handshake register
   1055 	 *	%a3	data (tags buffer, data buffer)
   1056 	 *	%a4	Sync pattern, 'toDisk' translation table
   1057 	 */
   1058 wsBufDirty:
   1059 	movel	_C_LABEL(IWMBase),%a0
   1060 	lea	%a4@(0,%d1),%a3
   1061 	movel	%a3,%a6@(-10)		| Save ptr to current slot
   1062 	tstb	%a0@(q6H)		| Enable writing to disk
   1063 	movel	%a6@(o_hdr),%a4		| Sector header struct
   1064 	lea	%a4@(o_Tags),%a3	| Point %a3 to tags buffer
   1065 	lea	syncPattern,%a4
   1066 
   1067 	moveb	%a4@+,%a0@(q7H)		| Write first sync byte
   1068 	lea	%a0@(q6L),%a2		| Point %a2 to handshake register
   1069 	lea	%a0@(q6H),%a0		| Point %a0 to IWM data register
   1070 
   1071 	moveq	#6,%d0			| Loop counter for sync bytes
   1072 	moveq	#0,%d2
   1073 	moveq	#0,%d3
   1074 	movel	#0x02010009,%d4		| Loop counters for tag/sector data
   1075 
   1076 	/*
   1077 	 * Write 5 sync bytes and first byte of sector data lead-in
   1078 	 */
   1079 wsLeadIn:
   1080 	moveb	%a4@+,%d1		| Get next sync byte
   1081 wsLI1:
   1082 	tstb	%a2@			| IWM ready?
   1083 	bpl	wsLI1
   1084 	moveb	%d1,%a0@		| Write it to disk
   1085 	subqw	#1,%d0
   1086 	bne	wsLeadIn
   1087 
   1088 	moveb	%a4@+,%d1		| Write 2nd byte of sector lead-in
   1089 	lea	toDisk,%a4		| Point %a4 to nibble translation table
   1090 wsLI2:
   1091 	tstb	%a2@			| IWM ready?
   1092 	bpl	wsLI2
   1093 	moveb	%d1,%a0@		| Write it to disk
   1094 
   1095 	moveq	#0,%d5			| Clear checksum registers
   1096 	moveq	#0,%d6
   1097 	moveq	#0,%d7
   1098 
   1099 	moveq	#0x0B,%d1		| 3rd byte of sector data lead-in
   1100 					| (Gets translated to 0xAD)
   1101 	moveb	%a3@+,%d2		| Get 1st byte from tags buffer
   1102 	bra	wsDataEntry
   1103 
   1104 	/*
   1105 	 * The following loop reads the content of the tags buffer (12 bytes)
   1106 	 * and the data buffer (512 bytes).
   1107 	 * Each pass reads out three bytes and
   1108 	 * a) splits them 6&2 into three 6 bit nibbles and a fourth byte
   1109 	 *    consisting of the three 2 bit nibbles
   1110 	 * b) encodes the nibbles with a table to disk bytes (bit 7 set, no
   1111 	 *    more than two consecutive zero bits) and writes them to disk as
   1112 	 *
   1113 	 *    00mmnnoo		fragment 2 bit nibbles
   1114 	 *    00mmmmmm		6 bit nibble -- first byte
   1115 	 *    00nnnnnn		6 bit nibble -- second byte
   1116 	 *    00oooooo		6 bit nibble -- third byte
   1117 	 *
   1118 	 * c) adds up three 8 bit checksums, one for each of the bytes written.
   1119 	 */
   1120 wsSD1:
   1121 	movel	%a6@(-10),%a3		| Get ptr to current slot
   1122 	movel	%a3@(o_secbuf),%a3	| Get start of sector data buffer
   1123 
   1124 wsData:
   1125 	addxb	%d2,%d7
   1126 	eorb	%d6,%d2
   1127 	moveb	%d2,%d3
   1128 	lsrw	#6,%d3			| Put 2 bit nibbles into place
   1129 wsRDY01:
   1130 	tstb	%a2@			| IWM ready?
   1131 	bpl	wsRDY01
   1132 	moveb	%a4@(0,%d3),%a0@	| Translate nibble and write
   1133 	subqw	#3,%d4			| Update counter
   1134 	moveb	%d7,%d3
   1135 	addb	%d7,%d3			| Set X flag (??)
   1136 	rolb	#1,%d7
   1137 	andib	#0x3F,%d0
   1138 wsRDY02:
   1139 	tstb	%a2@			| IWM ready?
   1140 	bpl	wsRDY02
   1141 	moveb	%a4@(0,%d0),%a0@	| Translate nibble and write
   1142 
   1143 	/*
   1144 	 * We enter with the last byte of the sector data lead-in
   1145 	 * between our teeth (%D1, that is).
   1146 	 */
   1147 wsDataEntry:
   1148 	moveb	%a3@+,%d0		| Get first byte
   1149 	addxb	%d0,%d5
   1150 	eorb	%d7,%d0
   1151 	moveb	%d0,%d3			| Keep top two bits
   1152 	rolw	#2,%d3			| by shifting them to MSByte
   1153 	andib	#0x3F,%d1
   1154 wsRDY03:
   1155 	tstb	%a2@			| IWM ready?
   1156 	bpl	wsRDY03
   1157 	moveb	%a4@(0,%d1),%a0@	| Translate nibble and write
   1158 
   1159 	moveb	%a3@+,%d1			| Get second byte
   1160 	addxb	%d1,%d6
   1161 	eorb	%d5,%d1
   1162 	moveb	%d1,%d3			| Keep top two bits
   1163 	rolw	#2,%d3			| by shifting them to MSByte
   1164 	andib	#0x3F,%d2
   1165 wsRDY04:
   1166 	tstb	%a2@			| IWM ready?
   1167 	bpl	wsRDY04
   1168 	moveb	%a4@(0,%d2),%a0@	| Translate nibble and write
   1169 
   1170 	/*
   1171 	 * XXX We have a classic off-by-one error here: the last access
   1172 	 * reaches beyond the data buffer which bombs with memory
   1173 	 * protection. The value read isn't used anyway...
   1174 	 * Hopefully there is enough time for an additional check
   1175 	 * (exit the last loop cycle before the buffer access).
   1176 	 */
   1177 	tstl	%d4			| Last loop cycle?
   1178 	beq	wsSDDone		| Then get out while we can.
   1179 
   1180 	moveb	%a3@+,%d2		| Get third byte
   1181 	tstw	%d4			| First write tag buffer,...
   1182 	bne	wsData
   1183 
   1184 	swap	%d4			| ...then write data buffer
   1185 	bne	wsSD1
   1186 
   1187 	/*
   1188 	 * Write nibbles for last 2 bytes, then
   1189 	 * split checksum bytes in 6&2 and write them to disk
   1190 	 */
   1191 wsSDDone:
   1192 	clrb	%d3			| No 513th byte
   1193 	lsrw	#6,%d3			| Set up 2 bit nibbles
   1194 wsRDY05:
   1195 	tstb	%a2@			| IWM ready?
   1196 	bpl	wsRDY05
   1197 	moveb	%a4@(0,%d3),%a0@	| Write fragments
   1198 	moveb	%d5,%d3
   1199 	rolw	#2,%d3
   1200 	moveb	%d6,%d3
   1201 	rolw	#2,%d3
   1202 	andib	#0x3F,%d0
   1203 wsRDY06:
   1204 	tstb	%a2@			| IWM ready?
   1205 	bpl	wsRDY06
   1206 	moveb	%a4@(0,%d0),%a0@	| Write 511th byte
   1207 	andib	#0x3F,%d1
   1208 wsRDY07:
   1209 	tstb	%a2@			| IWM ready?
   1210 	bpl	wsRDY07
   1211 	moveb	%a4@(0,%d1),%a0@	| write 512th byte
   1212 	moveb	%d7,%d3
   1213 	lsrw	#6,%d3			| Get fragments ready
   1214 wsRDY08:
   1215 	tstb	%a2@			| IWM ready?
   1216 	bpl	wsRDY08
   1217 	moveb	%a4@(0,%d3),%a0@	| Write fragments
   1218 	andib	#0x3F,%d5
   1219 wsRDY09:
   1220 	tstb	%a2@			| IWM ready?
   1221 	bpl	wsRDY09
   1222 	moveb	%a4@(0,%d5),%a0@	| Write first checksum byte
   1223 	andib	#0x3F,%D6
   1224 wsRDY10:
   1225 	tstb	%a2@			| IWM ready?
   1226 	bpl	wsRDY10
   1227 	moveb	%a4@(0,%d6),%a0@	| Write second checksum byte
   1228 	andib	#0x3F,%d7
   1229 wsRDY11:
   1230 	tstb	%a2@			| IWM ready?
   1231 	bpl	wsRDY11
   1232 	moveb	%a4@(0,%d7),%a0@	| Write third checksum byte
   1233 
   1234 	/*
   1235 	 * Write sector data lead-out
   1236 	 */
   1237 	lea	dataLeadOut,%a4		| Sector data lead-out
   1238 	moveq	#3,%d2			| Four bytes long {3,2,1,0}
   1239 wsLeadOut:
   1240 	moveb	%a2@,%d1		| IWM ready?
   1241 	bpl	wsLeadOut
   1242 	moveb	%a4@+,%a0@		| Write lead-out
   1243 	dbf	%d2,wsLeadOut
   1244 
   1245 	moveq	#0,%d0
   1246 	btst	#6,%d1			| Check IWM underrun bit
   1247 	bne	wsNoErr
   1248 
   1249 	moveq	#wrUnderRun,%d0		| Could not write
   1250 					| fast enough to keep up with IWM
   1251 wsNoErr:
   1252 	tstb	%a0@(0x0200)		| q7L -- Write OFF
   1253 
   1254 wsDone:
   1255 	tstb	%d0			| Any error? Simply retry
   1256 	bne	wsBufInvalid
   1257 
   1258 	movel	%a6@(-10),%a4		| Else, get ptr to current slot
   1259 	clrl	%a4@(o_valid)		| Mark current buffer "clean"
   1260 	bra	wsNextSect
   1261 
   1262 wsBufInvalid:
   1263 	subqb	#1,%a6@(-6)		| retries
   1264 	bne	wsNextSect
   1265 					| Sector not found, but
   1266 	tstb	%d0			| don't set error code if we
   1267 	bne	wsAllDone		| already have one.
   1268 	moveq	#sectNFErr,%d0
   1269 
   1270 wsAllDone:
   1271 	movew	%a6@(-2),%sr		| Restore interrupt mask
   1272 	moveml	%sp@+,%d1-%d7/%a0-%a5
   1273 	unlk	%a6
   1274 	rts
   1275 
   1276 
   1277 
   1278 /**
   1279  **	Local functions
   1280  **/
   1281 
   1282 /*
   1283  * iwmDelay
   1284  *
   1285  * In-kernel calls to delay() in mac68k/clock.c bomb
   1286  *
   1287  * Parameters:	%d0	delay in milliseconds
   1288  * Trashes:	%d0, %d1
   1289  * Returns:	-
   1290  */
   1291 iwmDelay:
   1292 	/* TimeDBRA is ~8K for 040/33 machines, so we need nested loops */
   1293 id00:	movew	_C_LABEL(TimeDBRA),%d1	| dbra loops per ms
   1294 id01:	dbra	%d1,id01		|
   1295 	dbra	%d0,id00
   1296 	rts
   1297 
   1298 
   1299 /*
   1300  * selDriveReg -- Select drive status/control register
   1301  *
   1302  * Parameters:	%d0	register #
   1303  *			(bit 0 - CA2, bit 1 - SEL, bit 2 - CA0, bit 3 - CA1)
   1304  *		%a0	IWM base address
   1305  *		%a1	VIA base address
   1306  * Returns:	%d0	register # (unchanged)
   1307  */
   1308 selDriveReg:
   1309 	tstb	%a0@(ph0H)		| default CA0 to 1 (says IM III)
   1310 	tstb	%a0@(ph1H)		| default CA1 to 1
   1311 
   1312 	btst	#0,%d0			| bit 0 set => CA2 on
   1313 	beq	se00
   1314 	tstb	%a0@(ph2H)
   1315 	bra	se01
   1316 se00:
   1317 	tstb	%a0@(ph2L)
   1318 
   1319 se01:
   1320 	btst	#1,%d0			| bit 1 set => SEL on (VIA 1)
   1321 	beq	se02
   1322 	bset	#vHeadSel,%a1@(vBufA)
   1323 	bra	se03
   1324 se02:
   1325 	bclr	#vHeadSel,%a1@(vBufA)
   1326 
   1327 se03:
   1328 	btst	#2,%d0			| bit 2 set => CA0 on
   1329 	bne	se04
   1330 	tstb	%a0@(ph0L)
   1331 
   1332 se04:
   1333 	btst	#3,%d0			| bit 3 set => CA1 on
   1334 	bne	se05
   1335 	tstb	%a0@(ph1L)
   1336 se05:
   1337 	rts
   1338 
   1339 
   1340 
   1341 /*
   1342  * dstatus -- check drive status (bit 7 - N flag) wrt. a previously
   1343  * set status tag.
   1344  *
   1345  * Parameters:	%d0	register selector
   1346  *		%a0	IWM base address
   1347  * Returns:	%d0	status
   1348  */
   1349 dstatus:
   1350 	tstb	%a0@(q6H)
   1351 	moveb	%a0@(q7L),%d0
   1352 	tstb	%a0@(q6L)		| leave in "read data reg"
   1353 	tstb	%d0			| state for safety
   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 	rts
   1370 
   1371 
   1372 /*
   1373  * dtrigger -- toggle LSTRB line to give drive a strobe signal
   1374  * IM III says pulse length = 1 us < t < 1 ms
   1375  *
   1376  * Parameters:	%a0	IWMBase
   1377  *		%a1	VIABase
   1378  * Returns:	-
   1379  */
   1380 dtrigger:
   1381 	tstb	%a0@(ph3H)		| LSTRB high
   1382 	moveb	%a1@(vBufA),%a1@(vBufA)	| intelligent nop seen in q700 ROM
   1383 	tstb	%a0@(ph3L)		| LSTRB low
   1384 	rts
   1385 
   1386 
   1387 /*
   1388  * driveCmd -- send command to drive.
   1389  *
   1390  * Parameters:	%a0	IWMBase
   1391  *		%a1	VIABase
   1392  *		%d0	Command token
   1393  * Returns:	-
   1394  */
   1395 driveCmd:
   1396 	bsr	selDriveReg
   1397 	bsr	dtrigger
   1398 	rts
   1399 
   1400 
   1401 /*
   1402  * readSectHdr -- read and decode the next available sector header.
   1403  *
   1404  * TODO:	Poll SCC as long as interrupts are disabled.
   1405  *
   1406  * Parameters:	%a4	sectorHdr_t address
   1407  * Returns:	%d0	result code
   1408  * Uses:	%d0-%d4, %a0, %a2-%a4
   1409  */
   1410 readSectHdr:
   1411 	moveq	#3,%d4			| Read 3 chars from IWM for sync
   1412 	movew	#600,%d3		| Retries to sync to disk
   1413 	moveq	#0,%d2			| Clear scratch regs
   1414 	moveq	#0,%d1
   1415 	moveq	#0,%d0
   1416  	movel	_C_LABEL(IWMBase),%a0	| IWM base address
   1417 
   1418 	tstb	%a0@(q7L)
   1419 	lea	%a0@(q6L),%a0		| IWM data register
   1420 shReadSy:
   1421 	moveb	%a0@,%d2		| Read char
   1422 	dbra	%d3,shSeekSync
   1423 
   1424 	moveq	#noNybErr,%d0		| Disk is blank?
   1425 	bra	shDone
   1426 
   1427 shSeekSync:
   1428 	bpl	shReadSy		| No char at IWM, repeat read
   1429 	subqw	#1,%d4
   1430 	bne	shReadSy
   1431 	/*
   1432 	 * When we get here, the IWM should be in sync with the data
   1433 	 * stream from disk.
   1434 	 * Next look for sector header lead-in 'D5 AA 96'
   1435 	 */
   1436 	movew	#1500,%d3		| Retries to seek header
   1437 shLeadIn:
   1438 	lea	hdrLeadIn,%a3		| Sector header lead-in bytes
   1439 	moveq	#0x03,%d4		| is 3 bytes long
   1440 shLI1:
   1441 	moveb	%a0@,%d2		| Get next byte
   1442 	bpl	shLI1			| No char at IWM, repeat read
   1443 	dbra	%d3,shLI2
   1444 	moveq	#noAdrMkErr,%d0		| Can't find an address mark
   1445 	bra	shDone
   1446 
   1447 shLI2:
   1448 	cmpb	%a3@+,%d2
   1449 	bne	shLeadIn		| If ne restart scan
   1450 	subqw	#1,%d4
   1451 	bne	shLI1
   1452 	/*
   1453 	 * We have found the lead-in. Now get the header information.
   1454 	 * Reg %d4 holds the checksum.
   1455 	 */
   1456 	lea	diskTo-0x90,%a2		| Translate disk bytes -> 6&2
   1457 shHdr1:
   1458 	moveb	%a0@,%d0		| Get 1st char
   1459 	bpl	shHdr1
   1460 	moveb	%a2@(0,%d0),%d1		| and remap it
   1461 	moveb	%d1,%d4
   1462 	rorw	#6,%d1			| separate 2:6, drop hi bits
   1463 shHdr2:
   1464 	moveb	%a0@,%d0		| Get 2nd char
   1465 	bpl	shHdr2
   1466 	moveb	%a2@(0,%d0),%d2		| and remap it
   1467 	eorb	%d2,%d4
   1468 shHdr3:
   1469 	moveb	%a0@,%d0		| Get 3rd char
   1470 	bpl	shHdr3
   1471 	moveb	%a2@(0,%d0),%d1		| and remap it
   1472 	eorb	%d1,%d4
   1473 	rolw	#6,%d1			|
   1474 shHdr4:
   1475 	moveb	%a0@,%d0		| Get 4th char
   1476 	bpl	shHdr4
   1477 	moveb	%a2@(0,%d0),%d3		| and remap it
   1478 	eorb	%d3,%d4
   1479 shHdr5:
   1480 	moveb	%a0@,%d0		| Get checksum byte
   1481 	bpl	shHdr5
   1482 	moveb	%a2@(0,%d0),%d5		| and remap it
   1483 	eorb	%d5,%d4
   1484 	bne	shCsErr			| Checksum ok?
   1485 	/*
   1486 	 * We now have in
   1487 	 * %d1/lsb	track number
   1488 	 * %d1/msb	bit 3 is side bit
   1489 	 * %d2		sector number
   1490 	 * %d3		???
   1491 	 * %d5		checksum (=0)
   1492 	 *
   1493 	 * Next check for lead-out.
   1494 	 */
   1495 	moveq	#1,%d4			| is 2 bytes long
   1496 shHdr6:
   1497 	moveb	%a0@,%d0		| Get token
   1498 	bpl	shHdr6
   1499 	cmpb	%a3@+,%d0		| Check
   1500 	bne	shLOErr			| Fault!
   1501 	dbra	%d4,shHdr6
   1502 	movew	%d1,%d0			| Isolate side bit
   1503 	lsrw	#8,%d0
   1504 	moveb	%d0,%a4@+		| and store it
   1505 	moveb	%d1,%a4@+		| Store track number
   1506 	moveb	%d2,%a4@+		| and sector number
   1507 	moveq	#0,%d0			| All is well
   1508 	bra	shDone
   1509 
   1510 shCsErr:
   1511 	moveq	#badCkSmErr,%d0		| Bad sector header checksum
   1512 	bra	shDone
   1513 shLOErr:
   1514 	moveq	#badBtSlpErr,%d0	| Bad address mark (no lead-out)
   1515 
   1516 shDone:
   1517 	tstl	%d0			| Set flags
   1518 	rts
   1519