iwm.s revision 1.1.2.1 1 /* $NetBSD: iwm.s,v 1.1.2.1 1999/05/16 22:38:13 scottr 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 _TimeDBRA | in mac68k/macrom.c
77 .extern _VIA1Base | in mac68k/machdep.c
78 .extern _IWMBase | in iwm_fd.c
79
80
81 .data
82
83 diskTo:
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
105 hdrLeadIn:
106 .byte 0xD5, 0xAA, 0x96
107
108 hdrLeadOut:
109 .byte 0xDE, 0xAA, 0xFF
110
111 dataLeadIn:
112 .byte 0xD5, 0xAA, 0xAD
113
114 dataLeadOut:
115 .byte 0xDE, 0xAA, 0xFF, 0xFF
116
117
118 toDisk:
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
131 syncPattern:
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 */
171 ENTRY(iwmQueryDrvFlag)
172 link a6,#0
173 moveml d1/a0-a1,sp@-
174 movel _IWMBase,a0
175 movel _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
184 quDrv00:
185 tstb a0@(intDrive) | SELECT; choose drive #0
186 bra queryDrv
187
188 quDrv01:
189 tstb a0@(extDrive) | SELECT; choose drive #1
190
191 queryDrv:
192 movel a6@(12),d0 | Get register #
193 bsr driveStat
194
195 quDone:
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 */
210 ENTRY(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 */
231 ENTRY(iwmInit)
232 link a6,#0
233 moveml d2/a0,sp@-
234 movel _IWMBase,a0
235
236 /*
237 * Reset IWM to known state (clear disk I/O latches)
238 */
239 tstb a0@(ph0L) | CA0
240 tstb a0@(ph1L) | CA1
241 tstb a0@(ph2L) | CA2
242 tstb a0@(ph3L) | LSTRB
243
244 tstb a0@(mtrOff) | ENABLE; make sure drive is off
245 tstb a0@(intDrive) | SELECT; choose drive 1
246 moveq #0x1F,d0 | XXX was 0x17 -- WHY!?
247
248 /*
249 * First do it quick...
250 */
251 tstb a0@(q6H)
252 andb a0@(q7L),d0 | status register
253 tstb a0@(q6L)
254 cmpib #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
262 initLp:
263 moveq #initIWMErr,d0 | Initialization error
264 subql #1,d2
265 bmi initErr
266 tstb a0@(mtrOff) | disable drive
267 tstb a0@(q6H)
268 moveq #0x3F,d0
269 andb a0@(q7L),d0
270 bclr #5,d0 | Reset bit 5 and set Z flag
271 | according to previous state
272 bne initLp | Loop if drive still on
273 cmpib #iwmMode,d0
274 beq initDone
275 moveb #iwmMode,a0@(q7H) | Init IWM
276 tstb a0@(q7L)
277 bra initLp
278
279 initDone:
280 tstb a0@(q6L) | Prepare IWM for data
281 moveq #0,d0 | noErr
282
283 initErr:
284 moveml sp@+,d2/a0
285 unlk a6
286 rts
287
288
289 /*
290 * iwmCheckDrive -- Check if given drive is available and return bit vector
291 * with capabilities (SS/DS, disk inserted, ...)
292 *
293 * Parameters: stack l Drive number (0,1)
294 * Returns: d0 Bit 0 - 0 = Drive is single sided
295 * 1 - 0 = Disk inserted
296 * 2 - 0 = Motor is running
297 * 3 - 0 = Disk is write protected
298 * 4 - 0 = Disk is DD
299 * 31 - (-1) No drive / invalid drive #
300 */
301 ENTRY(iwmCheckDrive)
302 link a6,#0
303 moveml d1/a0-a1,sp@-
304 movel _IWMBase,a0
305 movel _Via1Base,a1
306
307 moveq #-1,d1 | no drive
308
309 movel a6@(0x08),d0 | check drive #
310 beq chkDrv00
311 cmpl #1,d0
312 beq chkDrv01
313
314 bra chkDone | invalid drive #
315
316 chkDrv00:
317 tstb a0@(intDrive) | SELECT; choose drive #0
318 bra chkDrive
319
320 chkDrv01:
321 tstb a0@(extDrive) | SELECT; choose drive #1
322
323 chkDrive:
324 moveq #-2,d1 | error code
325 moveq #drvInstalled,d0 | Drive installed?
326 bsr driveStat
327 bmi chkDone | no drive
328
329 moveq #0,d1 | Drive found
330 tstb a0@(mtrOn) | ENABLE; activate drive
331 moveq #singleSided,d0 | Drive is single-sided?
332 bsr driveStat
333 bpl chkHasDisk
334 /*
335 * Drive is double-sided -- this is not really a surprise as the
336 * old ss 400k drive needs disk speed control from the Macintosh
337 * and we're not doing that here. Anyway - just in case...
338 * I am not sure m680x0 Macintoshes (x>0) support 400K drives at all
339 * due to their radically different sound support.
340 */
341 bset #0,d1 | 1 = no.
342 chkHasDisk:
343 moveq #diskInserted,d0 | Disk inserted?
344 bsr driveStat
345 bpl chkMotorOn
346 bset #1,d1 | 1 = No.
347 bra chkDone
348 chkMotorOn:
349 moveq #drvMotorState,d0 | Motor is running?
350 bsr driveStat
351 bpl chkWrtProt
352 bset #2,d1 | 1 = No.
353 chkWrtProt:
354 moveq #writeProtected,d0 | Disk is write protected?
355 bsr driveStat
356 bpl chkDD_HD
357 bset #3,d1 | 1 = No.
358 chkDD_HD:
359 moveq #diskIsHD,d0 | Disk is HD? (was "drive installed")
360 bsr driveStat
361 bpl chkDone
362 bset #4,d1 | 1 = No.
363 chkDone:
364 movel d1,d0
365 moveml sp@+,d1/a0-a1
366 unlk a6
367 rts
368
369
370 /*
371 * iwmDiskEject -- post EJECT command and toggle LSTRB line to give a
372 * strobe signal.
373 * IM III says pulse length = 500 ms, but we seem to get away with
374 * less delay; after all, we spin lock the CPU with it.
375 *
376 * Parameters: stack l drive number (0,1)
377 * a0 IWMBase
378 * a1 VIABase
379 * Returns: d0 result code
380 */
381 ENTRY(iwmDiskEject)
382 link a6,#0
383 movel _IWMBase,a0
384 movel _Via1Base,a1
385
386 movel a6@(0x08),d0 | Get drive #
387 beq ejDrv00
388 cmpw #1,d0
389 beq ejDrv01
390
391 bra ejDone | Invalid drive #
392
393 ejDrv00:
394 tstb a0@(intDrive) | SELECT; choose drive #0
395 bra ejDisk
396
397 ejDrv01:
398 tstb a0@(extDrive) | SELECT; choose drive #1
399 ejDisk:
400 tstb a0@(mtrOn) | ENABLE; activate drive
401
402 moveq #motorOffCmd,d0 | Motor off
403 bsr driveCmd
404
405 moveq #diskInserted,d0 | Disk inserted?
406 bsr driveStat
407 bmi ejDone
408
409 moveq #ejectDiskCmd,d0 | Eject it
410 bsr selDriveReg
411
412 tstb a0@(ph3H) | LSTRB high
413 #if USE_DELAY
414 movel #1000,sp@- | delay 1 ms
415 jsr _C_LABEL(delay)
416 addqw #4,sp | clean up stack
417 #else
418 movew #1,d0
419 bsr iwmDelay
420 #endif
421 tstb a0@(ph3L) | LSTRB low
422 moveq #0,d0 | All's well...
423 ejDone:
424 unlk a6
425 rts
426
427
428 /*
429 * iwmSelectDrive -- select internal (0) / external (1) drive.
430 *
431 * Parameters: stack l drive ID (0/1)
432 * Returns: d0 drive #
433 */
434 ENTRY(iwmSelectDrive)
435 link a6,#0
436 moveml a0-a1,sp@-
437 movel _IWMBase,a0
438 movel _Via1Base,a1
439
440 movel a6@(8),d0 | Get drive #
441 bne extDrv
442 tstb a0@(intDrive)
443 bra sdDone
444 extDrv:
445 tstb a0@(extDrive)
446 sdDone:
447 moveml sp@+,a0-a1
448 unlk a6
449 rts
450
451
452 /*
453 * iwmMotor -- switch drive motor on/off
454 *
455 * Parameters: stack l drive ID (0/1)
456 * stack l on(1)/off(0)
457 * Returns: d0 motor cmd
458 */
459 ENTRY(iwmMotor)
460 link a6,#0
461 moveml a0-a1,sp@-
462 movel _IWMBase,a0
463 movel _Via1Base,a1
464
465 movel a6@(8),d0 | Get drive #
466 bne mtDrv1
467 tstb a0@(intDrive)
468 bra mtSwitch
469 mtDrv1:
470 tstb a0@(extDrive)
471 mtSwitch:
472 movel #motorOnCmd,d0 | Motor ON
473 tstl a6@(12)
474 bne mtON
475 movel #motorOffCmd,d0
476 mtON:
477 bsr driveCmd
478
479 moveml sp@+,a0-a1
480 unlk a6
481 rts
482
483
484 /*
485 * iwmSelectSide -- select side 0 (lower head) / side 1 (upper head).
486 *
487 * This MUST be called immediately before an actual read/write access.
488 *
489 * Parameters: stack l side bit (0/1)
490 * Returns: -
491 */
492 ENTRY(iwmSelectSide)
493 link a6,#0
494 moveml d1/a0-a1,sp@-
495 movel _IWMBase,a0
496 movel _Via1Base,a1
497
498 moveq #0x0B,d0 | Drive ready for reading?
499 bsr selDriveReg | (undocumented)
500 ss01:
501 bsr dstatus
502 bmi ss01
503
504 moveq #rdDataFrom0,d0 | Lower head
505 movel a6@(0x08),d1 | Get side #
506 beq ssSide0
507 moveq #rdDataFrom1,d0 | Upper head
508 ssSide0:
509 bsr driveStat
510
511 moveml sp@+,d1/a0-a1
512 unlk a6
513 rts
514
515
516 /*
517 * iwmTrack00 -- move head to track 00 for drive calibration.
518 *
519 * XXX Drive makes funny noises during resore. Tune delay/retry count?
520 *
521 * Parameters: -
522 * Returns: d0 result code
523 */
524 ENTRY(iwmTrack00)
525 link a6,#0
526 moveml d1-d4/a0-a1,sp@-
527 movel _IWMBase,a0
528 movel _Via1Base,a1
529
530 moveq #motorOnCmd,d0 | Switch drive motor on
531 bsr driveCmd
532
533 moveq #stepOutCmd,d0 | Step out
534 bsr driveCmd
535
536 movew #100,d2 | Max. tries
537 t0Retry:
538 moveq #atTrack00,d0 | Already at track 0?
539 bsr driveStat
540 bpl isTrack00 | Track 0 => Bit 7 = 0
541
542 moveq #doStepCmd,d0 | otherwise step
543 bsr driveCmd
544 movew #80,d4 | Retries
545 t0Still:
546 moveq #stillStepping,d0 | Drive is still stepping?
547 bsr driveStat
548 dbmi d4,t0Still
549
550 cmpiw #-1,d4
551 bne t002
552
553 moveq #cantStepErr,d0 | Not ready after many retries
554 bra t0Done
555 t002:
556
557 #if USE_DELAY
558 movel #15000,sp@-
559 jsr _C_LABEL(delay) | in mac68k/clock.c
560 addqw #4,sp
561 #else
562 movew #15,d0
563 bsr iwmDelay
564 #endif
565
566 dbra d2,t0Retry
567
568 moveq #tk0BadErr,d0 | Can't find track 00!!
569 bra t0Done
570
571 isTrack00:
572 moveq #0,d0
573 t0Done:
574 moveml sp@+,d1-d4/a0-a1
575 unlk a6
576 rts
577
578
579 /*
580 * iwmSeek -- do specified # of steps (positive - in, negative - out).
581 *
582 * Parameters: stack l # of steps
583 * returns: d0 result code
584 */
585 ENTRY(iwmSeek)
586 link a6,#0
587 moveml d1-d4/a0-a1,sp@-
588 movel _IWMBase,a0
589 movel _Via1Base,a1
590
591 moveq #motorOnCmd,d0 | Switch drive motor on
592 bsr driveCmd
593
594 moveq #stepInCmd,d0 | Set step IN
595 movel a6@(8),d2 | Get # of steps from stack
596 beq stDone | 0 steps? Nothing to do.
597 bpl stepOut
598
599 moveq #stepOutCmd,d0 | Set step OUT
600 negl d2 | Make # of steps positive
601 stepOut:
602 subql #1,d2 | Loop exits for -1
603 bsr driveCmd | Set direction
604 stLoop:
605 moveq #doStepCmd,d0
606 bsr driveCmd | Step one!
607 movew #80,d4 | Retries
608 st01:
609 moveq #stillStepping, d0 | Drive is still stepping?
610 bsr driveStat
611 dbmi d4,st01
612
613 cmpiw #-1,d4
614 bne st02
615
616 moveq #cantStepErr,d2 | Not ready after many retries
617 bra stDone
618 st02:
619
620 #if USE_DELAY
621 movel #30,sp@-
622 jsr _C_LABEL(delay) | in mac68k/clock.c
623 addqw #4,sp
624 #else
625 movew _TimeDBRA,d4 | dbra loops per ms
626 lsrw #5,d4 | DIV 32
627 st03: dbra d4,st03 | makes ca. 30 us
628 #endif
629
630 dbra d2,stLoop
631
632 moveq #0,d2 | All is well
633 stDone:
634 movel d2,d0
635 moveml sp@+,d1-d4/a0-a1
636 unlk a6
637 rts
638
639
640 /*
641 * iwmReadSector -- read and decode the next available sector.
642 *
643 * TODO: Poll SCC as long as interrupts are disabled (see top comment)
644 * Add a branch for Verify (compare to buffer)
645 * Understand and document the checksum algorithm!
646 *
647 * 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 */
660 ENTRY(iwmReadSector)
661 link a6,#-8
662 moveml d1-d7/a0-a5,sp@-
663
664 movel _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
675 rsNextSect:
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
696 rsSeekErr:
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 */
707 rsGetSect:
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
711 rsLeadIn:
712 lea dataLeadIn,a3 | Sector data lead-in
713 moveq #0x03,d4 | is 3 bytes long
714 rsLI1:
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
721 rsLI2:
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 */
731 rsTagNyb0:
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
742 rsTags:
743 rsTagNyb1:
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
750 rsTagNyb2:
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
766 rsTagNyb3:
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
776 rsTagNyb4:
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.L
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
802 rsToCache:
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
807 rsData:
808 rsDatNyb1:
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
815 rsDatNyb2:
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
831 rsDatNyb3:
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
843 rsDatNyb4:
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 each group.
857 */
858 rsCkSum:
859 rsCkS1:
860 moveb a0@,d3 | Get 2 bit nibbles
861 bpl rsCkS1
862 moveb a2@(0,d3),d1 | Remap disk byte
863 bmi rsBadCkSum | Fault! (Bad read)
864 rolb #2,d1
865 moveb d1,d2
866 andib #0xC0,d2 | Get top 2 bits for first byte
867 rsCkS2:
868 moveb a0@,d3 | Get first 6 bit nibble
869 bpl rsCkS2
870 moveb a2@(0,d3),d3 | and remap it
871 bmi rsBadCkSum | Fault! ( > 0x3f is bad read)
872 orb d3,d2 | Merge 6&2
873 cmpb d2,d5 | Compare first checksum to D5
874 bne rsBadCkSum | Fault! (Checksum)
875
876 rolb #2,d1
877 moveb d1,d2
878 andib #0xC0,d2 | Get top 2 bits for second byte
879 rsCkS3:
880 moveb a0@,d3 | Get second 6 bit nibble
881 bpl rsCkS3
882 moveb a2@(0,d3),d3 | and remap it
883 bmi rsBadCkSum | Fault! (Bad read)
884 orb d3,d2 | Merge 6&2
885 cmpb d2,d6 | Compare second checksum to D6
886 bne rsBadCkSum | Fault! (Checksum)
887
888 rolb #2,d1
889 andib #0xC0,d1 | Get top 2 bits for second byte
890 rsCkS4:
891 moveb a0@,d3 | Get third 6 bit nibble
892 bpl rsCkS4
893 moveb a2@(0,d3),d3 | and remap it
894 bmi rsBadCkSum | Fault! (Bad read)
895 orb d3,d1 | Merge 6&2
896 cmpb d1,d7 | Compare third checksum to D7
897 beq rsLdOut | Fault! (Checksum)
898
899 rsBadCkSum:
900 moveq #badDCkSum,d0 | Bad data mark checksum
901 bra rsDone
902
903 rsBadDBtSlp:
904 moveq #badDBtSlp,d0 | One of the data mark bit slip
905 bra rsDone | nibbles was incorrect
906
907 /*
908 * We have gotten the checksums allright, now look for the
909 * sector data lead-out 'DE AA'
910 * (We have a3 still pointing to 'dataLeadOut'; this part of the
911 * table is used for writing to disk, too.)
912 */
913 rsLdOut:
914 moveq #1,d4 | Is two bytes long {1,0}
915 rsLdOut1:
916 moveb a0@,d3 | Get token
917 bpl rsLdOut1
918 cmpb a3@+,d3
919 bne rsBadDBtSlp | Fault!
920 dbra d4,rsLdOut1
921 moveq #0,d0 | OK.
922
923 /*
924 * See if we got the sector we wanted. If not, and no error
925 * occurred, mark buffer valid. Else ignore the sector.
926 * Then, read on.
927 */
928 rsDone:
929 movel a6@(o_hdr),a4 | Addr of sector header struct
930 moveb a4@(o_sector),d1 | Get # of sector we have just read
931 cmpb a6@(-5),d1 | Compare to the sector we want
932 beq rsAllDone
933
934 tstb d0 | Any error? Simply ignore data
935 beq rsBufValid
936 lslw #3,d1 | sizeof cylCacheSlot_t is 8 bytes
937 movel a6@(o_rslots),a4
938 clrl a4@(o_valid,d1) | Mark buffer content "invalid"
939
940 rsBufValid:
941 subqb #1,a6@(-6) | max. retries
942 bne rsNextSect
943 | Sector not found, but
944 tstb d0 | don't set error code if we
945 bne rsAllDone | already have one.
946 moveq #sectNFErr,d0
947 rsAllDone:
948 movew a6@(-2),sr | Restore interrupt mask
949 moveml sp@+,d1-d7/a0-a5
950 unlk a6
951 rts
952
953
954 /*
955 * iwmWriteSector -- encode and write data to the specified sector.
956 *
957 * TODO: Poll SCC as long as interrupts are disabled (see top comment)
958 * Understand and document the checksum algorithm!
959 *
960 * XXX Use registers more efficiently
961 *
962 * Parameters: fp+8 l Address of sector header struct (I/O)
963 * fp+12 l Address of cache buffer ptr array
964 * Returns: d0 result code
965 *
966 * Local: fp-2 w CPU status register
967 * fp-3 b side,
968 * fp-4 b track,
969 * fp-5 b sector wanted
970 * fp-6 b retry count
971 * fp-10 b current slot
972 */
973 ENTRY(iwmWriteSector)
974 link a6,#-10
975 moveml d1-d7/a0-a5,sp@-
976
977 movel _Via1Base,a1
978 movel a6@(o_hdr),a4 | Addr of sector header struct
979
980 moveb a4@+,a6@(-3) | Save side bit,
981 moveb a4@+,a6@(-4) | track#,
982 moveb a4@,a6@(-5) | sector#
983 moveb #maxGCRSectors,a6@(-6) | Max. retry count
984
985 movew sr,a6@(-2) | Save CPU status register
986 oriw #0x0600,sr | Block all interrupts
987
988 wsNextSect:
989 movel a6@(o_hdr),a4
990 bsr readSectHdr | Get next available sector header
991 bne wsAllDone | Return if error
992
993 /*
994 * Is this the right track & side? If not, return with error
995 */
996 movel a6@(o_hdr),a4 | Sector header struct
997
998 moveb a4@(o_side),d1 | Get side#
999 lsrb #3,d1 | "Normalize" side bit...
1000 andb #1,d1
1001 moveb a6@(-3),d2 | Get wanted side
1002 eorb d1,d2 | Compare side bits
1003 bne wsSeekErr
1004
1005 moveb a6@(-4),d1 | Get wanted track#
1006 cmpb a4@(o_track),d1 | Compare to the read header
1007 beq wsCompSect
1008
1009 wsSeekErr:
1010 moveq #seekErr,d0 | Wrong track or side
1011 bra wsAllDone
1012
1013 /*
1014 * Look up the current sector number in the cache.
1015 * If the buffer is dirty ("valid"), write it to disk. If not,
1016 * loop over all the slots and return if all of them are clean.
1017 *
1018 * Alternatively, we could decrement a "dirty sectors" counter here.
1019 */
1020 wsCompSect:
1021 moveq #0,d1 | Clear register
1022 moveb a4@(o_sector),d1 | get the # of header read
1023 lslw #3,d1 | sizeof cylCacheSlot_t is 8 bytes
1024 movel a6@(o_wslots),a4
1025 tstl a4@(o_valid,d1) | Sector dirty?
1026 bne wsBufDirty
1027
1028 moveq #maxGCRSectors-1,d2 | Any dirty sectors left?
1029 wsChkDty:
1030 movew d2,d1
1031 lslw #3,d1 | sizeof cylCacheSlot_t is 8 bytes
1032 tstl a4@(o_valid,d1)
1033 bne wsNextSect | Sector dirty?
1034 dbra d2,wsChkDty
1035
1036 bra wsAllDone | We are through with this track.
1037
1038
1039 /*
1040 * Write sync pattern and sector data lead-in 'D5 AA'. The
1041 * missing 'AD' is made up by piping 0x0B through the nibble
1042 * table (toDisk).
1043 *
1044 * To set up IWM for writing:
1045 *
1046 * access q6H & write first byte to q7H.
1047 * Then check bit 7 of q6L (status reg) for 'IWM ready'
1048 * and write subsequent bytes to q6H.
1049 *
1050 * Registers:
1051 * a0 IWM base address (later: data register)
1052 * a1 Via1Base
1053 * a2 IWM handshake register
1054 * a3 data (tags buffer, data buffer)
1055 * a4 Sync pattern, 'toDisk' translation table
1056 */
1057 wsBufDirty:
1058 movel _IWMBase,a0
1059 lea a4@(0,d1),a3
1060 movel a3,a6@(-10) | Save ptr to current slot
1061 tstb a0@(q6H) | Enable writing to disk
1062 movel a6@(o_hdr),a4 | Sector header struct
1063 lea a4@(o_Tags),a3 | Point a3 to tags buffer
1064 lea syncPattern,a4
1065
1066 moveb a4@+,a0@(q7H) | Write first sync byte
1067 lea a0@(q6L),a2 | Point a2 to handshake register
1068 lea a0@(q6H),a0 | Point a0 to IWM data register
1069
1070 moveq #6,d0 | Loop counter for sync bytes
1071 moveq #0,d2
1072 moveq #0,d3
1073 movel #0x02010009,d4 | Loop counters for tag/sector data
1074
1075 /*
1076 * Write 5 sync bytes and first byte of sector data lead-in
1077 */
1078 wsLeadIn:
1079 moveb a4@+,d1 | Get next sync byte
1080 wsLI1:
1081 tstb a2@ | IWM ready?
1082 bpl wsLI1
1083 moveb d1,a0@ | Write it to disk
1084 subqw #1,d0
1085 bne wsLeadIn
1086
1087 moveb a4@+,d1 | Write 2nd byte of sector lead-in
1088 lea toDisk,a4 | Point a4 to nibble translation table
1089 wsLI2:
1090 tstb a2@ | IWM ready?
1091 bpl wsLI2
1092 moveb d1,a0@ | Write it to disk
1093
1094 moveq #0,d5 | Clear checksum registers
1095 moveq #0,d6
1096 moveq #0,d7
1097
1098 moveq #0x0B,d1 | 3rd byte of sector data lead-in
1099 | (Gets translated to 0xAD)
1100 moveb a3@+,d2 | Get 1st byte from tags buffer
1101 bra wsDataEntry
1102
1103 /*
1104 * The following loop reads the content of the tags buffer (12 bytes)
1105 * and the data buffer (512 bytes).
1106 * Each pass reads out three bytes and
1107 * a) splits them 6&2 into three 6 bit nibbles and a fourth byte
1108 * consisting of the three 2 bit nibbles
1109 * b) encodes the nibbles with a table to disk bytes (bit 7 set, no
1110 * more than two consecutive zero bits) and writes them to disk as
1111 *
1112 * 00mmnnoo fragment 2 bit nibbles
1113 * 00mmmmmm 6 bit nibble -- first byte
1114 * 00nnnnnn 6 bit nibble -- second byte
1115 * 00oooooo 6 bit nibble -- third byte
1116 *
1117 * c) adds up three 8 bit checksums, one for each of the bytes written.
1118 */
1119 wsSD1:
1120 movel a6@(-10),a3 | Get ptr to current slot
1121 movel a3@(o_secbuf),a3 | Get start of sector data buffer
1122
1123 wsData:
1124 addxb d2,d7
1125 eorb d6,d2
1126 moveb d2,d3
1127 lsrw #6,d3 | Put 2 bit nibbles into place
1128 wsRDY01:
1129 tstb a2@ | IWM ready?
1130 bpl wsRDY01
1131 moveb a4@(0,d3),a0@ | Translate nibble and write
1132 subqw #3,d4 | Update counter
1133 moveb d7,d3
1134 addb d7,d3 | Set X flag (??)
1135 rolb #1,d7
1136 andib #0x3F,d0
1137 wsRDY02:
1138 tstb a2@ | IWM ready?
1139 bpl wsRDY02
1140 moveb a4@(0,d0),a0@ | Translate nibble and write
1141
1142 /*
1143 * We enter with the last byte of the sector data lead-in
1144 * between our teeth (D1, that is).
1145 */
1146 wsDataEntry:
1147 moveb a3@+,d0 | Get first byte
1148 addxb d0,d5
1149 eorb d7,d0
1150 moveb d0,d3 | Keep top two bits
1151 rolw #2,d3 | by shifting them to MSByte
1152 andib #0x3F,d1
1153 wsRDY03:
1154 tstb a2@ | IWM ready?
1155 bpl wsRDY03
1156 moveb a4@(0,d1),a0@ | Translate nibble and write
1157
1158 moveb a3@+,d1 | Get second byte
1159 addxb d1,d6
1160 eorb d5,d1
1161 moveb d1,d3 | Keep top two bits
1162 rolw #2,d3 | by shifting them to MSByte
1163 andib #0x3F,d2
1164 wsRDY04:
1165 tstb a2@ | IWM ready?
1166 bpl wsRDY04
1167 moveb a4@(0,d2),a0@ | Translate nibble and write
1168
1169 /*
1170 * XXX We have a classic off-by-one error here: the last access
1171 * reaches beyond the data buffer which bombs with memory
1172 * protection. The value read isn't used anyway...
1173 * Hopefully there is enough time for an additional check
1174 * (exit the last loop cycle before the buffer access).
1175 */
1176 tstl d4 | Last loop cycle?
1177 beq wsSDDone | Then get out while we can.
1178
1179 moveb a3@+,d2 | Get third byte
1180 tstw d4 | First write tag buffer,...
1181 bne wsData
1182
1183 swap d4 | ...then write data buffer
1184 bne wsSD1
1185
1186 /*
1187 * Write nibbles for last 2 bytes, then
1188 * split checksum bytes in 6&2 and write them to disk
1189 */
1190 wsSDDone:
1191 clrb d3 | No 513th byte
1192 lsrw #6,d3 | Set up 2 bit nibbles
1193 wsRDY05:
1194 tstb a2@ | IWM ready?
1195 bpl wsRDY05
1196 moveb a4@(0,d3),a0@ | Write fragments
1197 moveb d5,d3
1198 rolw #2,d3
1199 moveb d6,d3
1200 rolw #2,d3
1201 andib #0x3F,d0
1202 wsRDY06:
1203 tstb a2@ | IWM ready?
1204 bpl wsRDY06
1205 moveb a4@(0,d0),a0@ | Write 511th byte
1206 andib #0x3F,d1
1207 wsRDY07:
1208 tstb a2@ | IWM ready?
1209 bpl wsRDY07
1210 moveb a4@(0,d1),a0@ | write 512th byte
1211 moveb d7,d3
1212 lsrw #6,d3 | Get fragments ready
1213 wsRDY08:
1214 tstb a2@ | IWM ready?
1215 bpl wsRDY08
1216 moveb a4@(0,d3),a0@ | Write fragments
1217 andib #0x3F,d5
1218 wsRDY09:
1219 tstb a2@ | IWM ready?
1220 bpl wsRDY09
1221 moveb a4@(0,d5),a0@ | Write first checksum byte
1222 andib #0x3F,D6
1223 wsRDY10:
1224 tstb a2@ | IWM ready?
1225 bpl wsRDY10
1226 moveb a4@(0,d6),a0@ | Write second checksum byte
1227 andib #0x3F,d7
1228 wsRDY11:
1229 tstb a2@ | IWM ready?
1230 bpl wsRDY11
1231 moveb a4@(0,d7),a0@ | Write third checksum byte
1232
1233 /*
1234 * Write sector data lead-out
1235 */
1236 lea dataLeadOut,a4 | Sector data lead-out
1237 moveq #3,d2 | Four bytes long {3,2,1,0}
1238 wsLeadOut:
1239 moveb a2@,d1 | IWM ready?
1240 bpl wsLeadOut
1241 moveb a4@+,a0@ | Write lead-out
1242 dbf d2,wsLeadOut
1243
1244 moveq #0,d0
1245 btst #6,d1 | Check IWM underrun bit
1246 bne wsNoErr
1247
1248 moveq #wrUnderRun,d0 | Could not write
1249 | fast enough to keep up with IWM
1250 wsNoErr:
1251 tstb a0@(0x0200) | q7L -- Write OFF
1252
1253 wsDone:
1254 tstb d0 | Any error? Simply retry
1255 bne wsBufInvalid
1256
1257 movel a6@(-10),a4 | Else, get ptr to current slot
1258 clrl a4@(o_valid) | Mark current buffer "clean"
1259 bra wsNextSect
1260
1261 wsBufInvalid:
1262 subqb #1,a6@(-6) | retries
1263 bne wsNextSect
1264 | Sector not found, but
1265 tstb d0 | don't set error code if we
1266 bne wsAllDone | already have one.
1267 moveq #sectNFErr,d0
1268
1269 wsAllDone:
1270 movew a6@(-2),sr | Restore interrupt mask
1271 moveml sp@+,d1-d7/a0-a5
1272 unlk a6
1273 rts
1274
1275
1276
1277 /**
1278 ** Local functions
1279 **/
1280
1281 /*
1282 * iwmDelay
1283 *
1284 * In-kernel calls to delay() in mac68k/clock.c bomb
1285 *
1286 * Parameters: d0 delay in milliseconds
1287 * Trashes: d0, d1
1288 * Returns: -
1289 */
1290 iwmDelay:
1291 /* TimeDBRA is ~8K for 040/33 machines, so we need nested loops */
1292 id00: movew _TimeDBRA,d1 | dbra loops per ms
1293 id01: dbra d1,id01 |
1294 dbra d0,id00
1295 rts
1296
1297
1298 /*
1299 * selDriveReg -- Select drive status/control register
1300 *
1301 * Parameters: d0 register #
1302 * (bit 0 - CA2, bit 1 - SEL, bit 2 - CA0, bit 3 - CA1)
1303 * a0 IWM base address
1304 * a1 VIA base address
1305 * Returns: d0 register # (unchanged)
1306 */
1307 selDriveReg:
1308 tstb a0@(ph0H) | default CA0 to 1 (says IM III)
1309 tstb a0@(ph1H) | default CA1 to 1
1310
1311 btst #0,d0 | bit 0 set => CA2 on
1312 beq se00
1313 tstb a0@(ph2H)
1314 bra se01
1315 se00:
1316 tstb a0@(ph2L)
1317
1318 se01:
1319 btst #1,d0 | bit 1 set => SEL on (VIA 1)
1320 beq se02
1321 bset #vHeadSel,a1@(vBufA)
1322 bra se03
1323 se02:
1324 bclr #vHeadSel,a1@(vBufA)
1325
1326 se03:
1327 btst #2,d0 | bit 2 set => CA0 on
1328 bne se04
1329 tstb a0@(ph0L)
1330
1331 se04:
1332 btst #3,d0 | bit 3 set => CA1 on
1333 bne se05
1334 tstb a0@(ph1L)
1335 se05:
1336 rts
1337
1338
1339
1340 /*
1341 * dstatus -- check drive status (bit 7 - N flag) wrt. a previously
1342 * set status tag.
1343 *
1344 * Parameters: d0 register selector
1345 * a0 IWM base address
1346 * Returns: d0 status
1347 */
1348 dstatus:
1349 tstb a0@(q6H)
1350 moveb a0@(q7L),d0
1351 tstb a0@(q6L) | leave in "read data reg"
1352 tstb d0 | state for safety
1353
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 */
1365 driveStat:
1366 tstb a0@(mtrOn) | ENABLE; turn drive on
1367 bsr selDriveReg
1368 bsr dstatus
1369
1370 rts
1371
1372
1373 /*
1374 * dtrigger -- toggle LSTRB line to give drive a strobe signal
1375 * IM III says pulse length = 1 us < t < 1 ms
1376 *
1377 * Parameters: a0 IWMBase
1378 * a1 VIABase
1379 * Returns: -
1380 */
1381 dtrigger:
1382 tstb a0@(ph3H) | LSTRB high
1383 moveb a1@(vBufA),a1@(vBufA) | intelligent nop seen in q700 ROM
1384 tstb a0@(ph3L) | LSTRB low
1385
1386 rts
1387
1388
1389 /*
1390 * driveCmd -- send command to drive.
1391 *
1392 * Parameters: a0 IWMBase
1393 * a1 VIABase
1394 * d0 Command token
1395 * Returns: -
1396 */
1397 driveCmd:
1398 bsr selDriveReg
1399 bsr dtrigger
1400
1401 rts
1402
1403
1404 /*
1405 * readSectHdr -- read and decode the next available sector header.
1406 *
1407 * TODO: Poll SCC as long as interrupts are disabled.
1408 *
1409 * Parameters: a4 sectorHdr_t address
1410 * Returns: d0 result code
1411 * Uses: d0-d4, a0, a2-a4
1412 */
1413 readSectHdr:
1414 moveq #3,d4 | Read 3 chars from IWM for sync
1415 movew #600,d3 | Retries to sync to disk
1416 moveq #0,d2 | Clear scratch regs
1417 moveq #0,d1
1418 moveq #0,d0
1419 movel _IWMBase,a0 | IWM base address
1420
1421 tstb a0@(q7L)
1422 lea a0@(q6L),a0 | IWM data register
1423 shReadSy:
1424 moveb a0@,d2 | Read char
1425 dbra d3,shSeekSync
1426
1427 moveq #noNybErr,d0 | Disk is blank?
1428 bra shDone
1429
1430 shSeekSync:
1431 bpl shReadSy | No char at IWM, repeat read
1432 subqw #1,d4
1433 bne shReadSy
1434 /*
1435 * When we get here, the IWM should be in sync with the data
1436 * stream from disk.
1437 * Next look for sector header lead-in 'D5 AA 96'
1438 */
1439 movew #1500,d3 | Retries to seek header
1440 shLeadIn:
1441 lea hdrLeadIn,a3 | Sector header lead-in bytes
1442 moveq #0x03,d4 | is 3 bytes long
1443 shLI1:
1444 moveb a0@,d2 | Get next byte
1445 bpl shLI1 | No char at IWM, repeat read
1446 dbra d3,shLI2
1447 moveq #noAdrMkErr,d0 | Can't find an address mark
1448 bra shDone
1449
1450 shLI2:
1451 cmpb a3@+,d2
1452 bne shLeadIn | If ne restart scan
1453 subqw #1,d4
1454 bne shLI1
1455 /*
1456 * We have found the lead-in. Now get the header information.
1457 * Reg d4 holds the checksum.
1458 */
1459 lea diskTo-0x90,a2 | Translate disk bytes -> 6&2
1460 shHdr1:
1461 moveb a0@,d0 | Get 1st char
1462 bpl shHdr1
1463 moveb a2@(0,d0),d1 | and remap it
1464 moveb d1,d4
1465 rorw #6,d1 | separate 2:6, drop hi bits
1466 shHdr2:
1467 moveb a0@,d0 | Get 2nd char
1468 bpl shHdr2
1469 moveb a2@(0,d0),d2 | and remap it
1470 eorb d2,d4
1471 shHdr3:
1472 moveb a0@,d0 | Get 3rd char
1473 bpl shHdr3
1474 moveb a2@(0,d0),d1 | and remap it
1475 eorb d1,d4
1476 rolw #6,d1 |
1477 shHdr4:
1478 moveb a0@,d0 | Get 4th char
1479 bpl shHdr4
1480 moveb a2@(0,d0),d3 | and remap it
1481 eorb d3,d4
1482 shHdr5:
1483 moveb a0@,d0 | Get checksum byte
1484 bpl shHdr5
1485 moveb a2@(0,d0),d5 | and remap it
1486 eorb d5,d4
1487 bne shCsErr | Checksum ok?
1488 /*
1489 * We now have in
1490 * d1/lsb track number
1491 * d1/msb bit 3 is side bit
1492 * d2 sector number
1493 * d3 ???
1494 * d5 checksum (=0)
1495 *
1496 * Next check for lead-out.
1497 */
1498 moveq #1,d4 | is 2 bytes long
1499 shHdr6:
1500 moveb a0@,d0 | Get token
1501 bpl shHdr6
1502 cmpb a3@+,d0 | Check
1503 bne shLOErr | Fault!
1504 dbra d4,shHdr6
1505 movew d1,d0 | Isolate side bit
1506 lsrw #8,d0
1507 moveb d0,a4@+ | and store it
1508 moveb d1,a4@+ | Store track number
1509 moveb d2,a4@+ | and sector number
1510 moveq #0,d0 | All is well
1511 bra shDone
1512
1513 shCsErr:
1514 moveq #badCkSmErr,d0 | Bad sector header checksum
1515 bra shDone
1516 shLOErr:
1517 moveq #badBtSlpErr,d0 | Bad address mark (no lead-out)
1518
1519 shDone:
1520 tstl d0 | Set flags
1521 rts
1522