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