iwm.s revision 1.3 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
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 _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
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 _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
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 _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
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 _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
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 _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
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 _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
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 _C_LABEL(IWMBase),%a0
496 movel _C_LABEL(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 _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
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 _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
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 _C_LABEL(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 _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
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
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
857 * each group.
858 */
859 rsCkSum:
860 rsCkS1:
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
868 rsCkS2:
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
880 rsCkS3:
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
891 rsCkS4:
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
900 rsBadCkSum:
901 moveq #badDCkSum,%d0 | Bad data mark checksum
902 bra rsDone
903
904 rsBadDBtSlp:
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 */
914 rsLdOut:
915 moveq #1,%d4 | Is two bytes long {1,0}
916 rsLdOut1:
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 */
929 rsDone:
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
941 rsBufValid:
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
948 rsAllDone:
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 */
974 ENTRY(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
989 wsNextSect:
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
1010 wsSeekErr:
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 */
1021 wsCompSect:
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?
1030 wsChkDty:
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 */
1058 wsBufDirty:
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 */
1079 wsLeadIn:
1080 moveb %a4@+,%d1 | Get next sync byte
1081 wsLI1:
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
1090 wsLI2:
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 */
1120 wsSD1:
1121 movel %a6@(-10),%a3 | Get ptr to current slot
1122 movel %a3@(o_secbuf),%a3 | Get start of sector data buffer
1123
1124 wsData:
1125 addxb %d2,%d7
1126 eorb %d6,%d2
1127 moveb %d2,%d3
1128 lsrw #6,%d3 | Put 2 bit nibbles into place
1129 wsRDY01:
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
1138 wsRDY02:
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 */
1147 wsDataEntry:
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
1154 wsRDY03:
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
1165 wsRDY04:
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 */
1191 wsSDDone:
1192 clrb %d3 | No 513th byte
1193 lsrw #6,%d3 | Set up 2 bit nibbles
1194 wsRDY05:
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
1203 wsRDY06:
1204 tstb %a2@ | IWM ready?
1205 bpl wsRDY06
1206 moveb %a4@(0,%d0),%a0@ | Write 511th byte
1207 andib #0x3F,%d1
1208 wsRDY07:
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
1214 wsRDY08:
1215 tstb %a2@ | IWM ready?
1216 bpl wsRDY08
1217 moveb %a4@(0,%d3),%a0@ | Write fragments
1218 andib #0x3F,%d5
1219 wsRDY09:
1220 tstb %a2@ | IWM ready?
1221 bpl wsRDY09
1222 moveb %a4@(0,%d5),%a0@ | Write first checksum byte
1223 andib #0x3F,%D6
1224 wsRDY10:
1225 tstb %a2@ | IWM ready?
1226 bpl wsRDY10
1227 moveb %a4@(0,%d6),%a0@ | Write second checksum byte
1228 andib #0x3F,%d7
1229 wsRDY11:
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}
1239 wsLeadOut:
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
1251 wsNoErr:
1252 tstb %a0@(0x0200) | q7L -- Write OFF
1253
1254 wsDone:
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
1262 wsBufInvalid:
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
1270 wsAllDone:
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 */
1291 iwmDelay:
1292 /* TimeDBRA is ~8K for 040/33 machines, so we need nested loops */
1293 id00: movew _C_LABEL(TimeDBRA),%d1 | dbra loops per ms
1294 id01: 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 */
1308 selDriveReg:
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
1316 se00:
1317 tstb %a0@(ph2L)
1318
1319 se01:
1320 btst #1,%d0 | bit 1 set => SEL on (VIA 1)
1321 beq se02
1322 bset #vHeadSel,%a1@(vBufA)
1323 bra se03
1324 se02:
1325 bclr #vHeadSel,%a1@(vBufA)
1326
1327 se03:
1328 btst #2,%d0 | bit 2 set => CA0 on
1329 bne se04
1330 tstb %a0@(ph0L)
1331
1332 se04:
1333 btst #3,%d0 | bit 3 set => CA1 on
1334 bne se05
1335 tstb %a0@(ph1L)
1336 se05:
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 */
1349 dstatus:
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 */
1365 driveStat:
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 */
1380 dtrigger:
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 */
1395 driveCmd:
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 */
1410 readSectHdr:
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
1420 shReadSy:
1421 moveb %a0@,%d2 | Read char
1422 dbra %d3,shSeekSync
1423
1424 moveq #noNybErr,%d0 | Disk is blank?
1425 bra shDone
1426
1427 shSeekSync:
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
1437 shLeadIn:
1438 lea hdrLeadIn,%a3 | Sector header lead-in bytes
1439 moveq #0x03,%d4 | is 3 bytes long
1440 shLI1:
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
1447 shLI2:
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
1457 shHdr1:
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
1463 shHdr2:
1464 moveb %a0@,%d0 | Get 2nd char
1465 bpl shHdr2
1466 moveb %a2@(0,%d0),%d2 | and remap it
1467 eorb %d2,%d4
1468 shHdr3:
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 |
1474 shHdr4:
1475 moveb %a0@,%d0 | Get 4th char
1476 bpl shHdr4
1477 moveb %a2@(0,%d0),%d3 | and remap it
1478 eorb %d3,%d4
1479 shHdr5:
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
1496 shHdr6:
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
1510 shCsErr:
1511 moveq #badCkSmErr,%d0 | Bad sector header checksum
1512 bra shDone
1513 shLOErr:
1514 moveq #badBtSlpErr,%d0 | Bad address mark (no lead-out)
1515
1516 shDone:
1517 tstl %d0 | Set flags
1518 rts
1519