xref: /netbsd/sys/arch/mac68k/obio/iwm.s (revision 5cd358db)
1/*	$NetBSD: iwm.s,v 1.7 2015/01/02 15:50:28 christos 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
81diskTo:
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
103hdrLeadIn:
104	.byte	0xD5, 0xAA, 0x96
105
106hdrLeadOut:
107	.byte	0xDE, 0xAA, 0xFF
108
109dataLeadIn:
110	.byte	0xD5, 0xAA, 0xAD
111
112dataLeadOut:
113	.byte	0xDE, 0xAA, 0xFF, 0xFF
114
115
116toDisk:
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
129syncPattern:
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 */
169ENTRY(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
182quDrv00:
183	tstb	%a0@(intDrive)		| SELECT; choose drive #0
184	bra	queryDrv
185
186quDrv01:
187	tstb	%a0@(extDrive)		| SELECT; choose drive #1
188
189queryDrv:
190	movel	%a6@(12),%d0		| Get register #
191	bsr	driveStat
192
193quDone:
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 */
208ENTRY(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 */
229ENTRY(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
260initLp:
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
277initDone:
278	tstb	%a0@(q6L)		| Prepare IWM for data
279	moveq	#0,%d0			| noErr
280
281initErr:
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 */
299ENTRY(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
314chkDrv00:
315	tstb	%a0@(intDrive)		| SELECT; choose drive #0
316	bra	chkDrive
317
318chkDrv01:
319	tstb	%a0@(extDrive)		| SELECT; choose drive #1
320
321chkDrive:
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.
340chkHasDisk:
341	moveq	#diskInserted,%d0	| Disk inserted?
342	bsr	driveStat
343	bpl	chkMotorOn
344	bset	#1,%d1			| 1 = No.
345	bra	chkDone
346chkMotorOn:
347	moveq	#drvMotorState,%d0	| Motor is running?
348	bsr	driveStat
349	bpl	chkWrtProt
350	bset	#2,%d1			| 1 = No.
351chkWrtProt:
352	moveq	#writeProtected,%d0	| Disk is write protected?
353	bsr	driveStat
354	bpl	chkDD_HD
355	bset	#3,%d1			| 1 = No.
356chkDD_HD:
357	moveq	#diskIsHD,%d0		| Disk is HD? (was "drive installed")
358	bsr	driveStat
359	bpl	chkDone
360	bset	#4,%d1			| 1 = No.
361chkDone:
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 */
379ENTRY(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
391ejDrv00:
392	tstb	%a0@(intDrive)		| SELECT; choose drive #0
393	bra	ejDisk
394
395ejDrv01:
396	tstb	%a0@(extDrive)		| SELECT; choose drive #1
397ejDisk:
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...
421ejDone:
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 */
432ENTRY(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
442extDrv:
443	tstb	%a0@(extDrive)
444sdDone:
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 */
457ENTRY(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
467mtDrv1:
468	tstb	%a0@(extDrive)
469mtSwitch:
470	movel	#motorOnCmd,%d0		| Motor ON
471	tstl	%a6@(12)
472	bne	mtON
473	movel	#motorOffCmd,%d0
474mtON:
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 */
490ENTRY(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)
498ss01:
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
506ssSide0:
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 */
522ENTRY(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
535t0Retry:
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
543t0Still:
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
553t002:
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
569isTrack00:
570	moveq	#0,%d0
571t0Done:
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 */
583ENTRY(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
599stepOut:
600	subql	#1,%d2			| Loop exits for -1
601	bsr	driveCmd		| Set direction
602stLoop:
603	moveq	#doStepCmd,%d0
604	bsr	driveCmd		| Step one!
605	movew	#80,%d4			| Retries
606st01:
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
616st02:
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
625st03:	dbra	%d4,st03		| makes ca. 30 us
626#endif
627
628	dbra	%d2,stLoop
629
630	moveq	#0,%d2			| All is well
631stDone:
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 */
658ENTRY(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
673rsNextSect:
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
694rsSeekErr:
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	 */
705rsGetSect:
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
709rsLeadIn:
710	lea	dataLeadIn,%a3		| Sector data lead-in
711	moveq	#0x03,%d4		| is 3 bytes long
712rsLI1:
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
719rsLI2:
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	 */
729rsTagNyb0:
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
740rsTags:
741rsTagNyb1:
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
748rsTagNyb2:
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
764rsTagNyb3:
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
774rsTagNyb4:
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
800rsToCache:
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
805rsData:
806rsDatNyb1:
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
813rsDatNyb2:
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
829rsDatNyb3:
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
841rsDatNyb4:
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	 */
857rsCkSum:
858rsCkS1:
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
866rsCkS2:
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
878rsCkS3:
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
889rsCkS4:
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
898rsBadCkSum:
899	moveq	#badDCkSum,%d0		| Bad data mark checksum
900	bra	rsDone
901
902rsBadDBtSlp:
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	 */
912rsLdOut:
913	moveq	#1,%d4			| Is two bytes long {1,0}
914rsLdOut1:
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	 */
927rsDone:
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
939rsBufValid:
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
946rsAllDone:
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 */
972ENTRY(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
987wsNextSect:
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
1008wsSeekErr:
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	 */
1019wsCompSect:
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?
1028wsChkDty:
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	 */
1056wsBufDirty:
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	 */
1077wsLeadIn:
1078	moveb	%a4@+,%d1		| Get next sync byte
1079wsLI1:
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
1088wsLI2:
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	 */
1118wsSD1:
1119	movel	%a6@(-10),%a3		| Get ptr to current slot
1120	movel	%a3@(o_secbuf),%a3	| Get start of sector data buffer
1121
1122wsData:
1123	addxb	%d2,%d7
1124	eorb	%d6,%d2
1125	moveb	%d2,%d3
1126	lsrw	#6,%d3			| Put 2 bit nibbles into place
1127wsRDY01:
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
1136wsRDY02:
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	 */
1145wsDataEntry:
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
1152wsRDY03:
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
1163wsRDY04:
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	 */
1189wsSDDone:
1190	clrb	%d3			| No 513th byte
1191	lsrw	#6,%d3			| Set up 2 bit nibbles
1192wsRDY05:
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
1201wsRDY06:
1202	tstb	%a2@			| IWM ready?
1203	bpl	wsRDY06
1204	moveb	%a4@(0,%d0),%a0@	| Write 511th byte
1205	andib	#0x3F,%d1
1206wsRDY07:
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
1212wsRDY08:
1213	tstb	%a2@			| IWM ready?
1214	bpl	wsRDY08
1215	moveb	%a4@(0,%d3),%a0@	| Write fragments
1216	andib	#0x3F,%d5
1217wsRDY09:
1218	tstb	%a2@			| IWM ready?
1219	bpl	wsRDY09
1220	moveb	%a4@(0,%d5),%a0@	| Write first checksum byte
1221	andib	#0x3F,%D6
1222wsRDY10:
1223	tstb	%a2@			| IWM ready?
1224	bpl	wsRDY10
1225	moveb	%a4@(0,%d6),%a0@	| Write second checksum byte
1226	andib	#0x3F,%d7
1227wsRDY11:
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}
1237wsLeadOut:
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
1249wsNoErr:
1250	tstb	%a0@(0x0200)		| q7L -- Write OFF
1251
1252wsDone:
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
1260wsBufInvalid:
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
1268wsAllDone:
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 */
1289iwmDelay:
1290	/* TimeDBRA is ~8K for 040/33 machines, so we need nested loops */
1291id00:	movew	_C_LABEL(TimeDBRA),%d1	| dbra loops per ms
1292id01:	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 */
1306selDriveReg:
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
1314se00:
1315	tstb	%a0@(ph2L)
1316
1317se01:
1318	btst	#1,%d0			| bit 1 set => SEL on (VIA 1)
1319	beq	se02
1320	bset	#vHeadSel,%a1@(vBufA)
1321	bra	se03
1322se02:
1323	bclr	#vHeadSel,%a1@(vBufA)
1324
1325se03:
1326	btst	#2,%d0			| bit 2 set => CA0 on
1327	bne	se04
1328	tstb	%a0@(ph0L)
1329
1330se04:
1331	btst	#3,%d0			| bit 3 set => CA1 on
1332	bne	se05
1333	tstb	%a0@(ph1L)
1334se05:
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 */
1347dstatus:
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 */
1363driveStat:
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 */
1378dtrigger:
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 */
1393driveCmd:
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 */
1408readSectHdr:
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
1418shReadSy:
1419	moveb	%a0@,%d2		| Read char
1420	dbra	%d3,shSeekSync
1421
1422	moveq	#noNybErr,%d0		| Disk is blank?
1423	bra	shDone
1424
1425shSeekSync:
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
1435shLeadIn:
1436	lea	hdrLeadIn,%a3		| Sector header lead-in bytes
1437	moveq	#0x03,%d4		| is 3 bytes long
1438shLI1:
1439	moveb	%a0@,%d2		| Get next byte
1440	bpl	shLI1			| No char at IWM, repeat read
1441	dbra	%d3,shLI2
1442	moveq	#noAdrMkErr,%d0		| Can't find an address mark
1443	bra	shDone
1444
1445shLI2:
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
1455shHdr1:
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
1461shHdr2:
1462	moveb	%a0@,%d0		| Get 2nd char
1463	bpl	shHdr2
1464	moveb	%a2@(0,%d0),%d2		| and remap it
1465	eorb	%d2,%d4
1466shHdr3:
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			|
1472shHdr4:
1473	moveb	%a0@,%d0		| Get 4th char
1474	bpl	shHdr4
1475	moveb	%a2@(0,%d0),%d3		| and remap it
1476	eorb	%d3,%d4
1477shHdr5:
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
1494shHdr6:
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
1508shCsErr:
1509	moveq	#badCkSmErr,%d0		| Bad sector header checksum
1510	bra	shDone
1511shLOErr:
1512	moveq	#badBtSlpErr,%d0	| Bad address mark (no lead-out)
1513
1514shDone:
1515	tstl	%d0			| Set flags
1516	rts
1517