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