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