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