1 /* $NetBSD: iwm.s,v 1.4 2002/08/29 09:26:23 hannken Exp $ */
4 * Copyright (c) 1996-99 Hauke Fath. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
28 * iwm.s -- low level routines for Sony floppy disk access.
29 * The present implementation supports the 800K GCR format on non-DMA
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.
37 * o The local routines do not block any interrupts.
39 * o The iwmXXX() routines that set/get IWM or drive settings are not
40 * time critical and do not block interrupts.
42 * o The iwmXXX() routines that are called to perform data transfers
43 * block all interrupts because otherwise the current sector data
45 * The old status register content is stored on the stack.
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.
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.
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'.
66 #include <mac68k/obio/iwmreg.h>
68 #define USE_DELAY 0 /* "1" bombs for unknown reasons */
72 * References to global name space
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
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).
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
104 .byte 0xD5, 0xAA, 0x96
107 .byte 0xDE, 0xAA, 0xFF
110 .byte 0xD5, 0xAA, 0xAD
113 .byte 0xDE, 0xAA, 0xFF, 0xFF
118 * Translation table from 6-bit nibbles [0x00..0x3f] to 'disk bytes'
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
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
137 .byte 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF
138 .byte 0xD5, 0xAA, 0xAD
145 * Register conventions:
146 * %a0 IWM base address
147 * %a1 VIA1 base address
149 * %d0 return value (0 == no error)
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!
163 * iwmQueryDrvFlags -- export wrapper for driveStat
165 * Parameters: stack l drive selector
166 * stack l register selector
169 ENTRY
(iwmQueryDrvFlag
)
171 moveml
%d1
/%a0-
%a1
,%sp@
-
172 movel _C_LABEL
(IWMBase
),%a0
173 movel _C_LABEL
(Via1Base
),%a1
175 movel
%a6@
(8),%d0 | Get drive
#
180 bra quDone | Invalid drive
#
183 tstb
%a0@
(intDrive
) | SELECT; choose drive
#0
187 tstb
%a0@
(extDrive
) | SELECT; choose drive
#1
190 movel
%a6@
(12),%d0 | Get register
#
194 moveml
%sp@
+,%d1
/%a0-
%a1
200 * iwmReadSectHdr -- read and decode the next available sector header.
202 * Parameters: stack l Address of sector header struct (I/O)
206 * Returns: %d0 result code
208 ENTRY
(iwmReadSectHdr
)
210 moveml
%d1-
%d5
/%a0-
%a4
,%sp@
-
211 movel
%a6@
(0x08),%a4 | Get param block address
213 moveml
%sp@
+,%d1-
%d5
/%a0-
%a4
220 ** Exported functions
224 * iwmInit -- Initialize IWM chip.
227 * Returns: %d0 result code
232 movel _C_LABEL
(IWMBase
),%a0
235 * Reset IWM to known state (clear disk I/O latches)
237 tstb
%a0@
(ph0L
) | CA0
238 tstb
%a0@
(ph1L
) | CA1
239 tstb
%a0@
(ph2L
) | CA2
240 tstb
%a0@
(ph3L
) | LSTRB
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!?
247 * First do it quick...
250 andb
%a0@
(q7L
),%d0 | status register
252 cmpib
#iwmMode,%d0 | all is well??
256 * If this doesn't succeed (e.g. drive still running),
257 * we do it thoroughly.
259 movel
#0x00080000,%d2 | ca. 500,000 retries = 1.5 sec
261 moveq
#initIWMErr,%d0 | Initialization error
264 tstb
%a0@
(mtrOff
) | disable drive
268 bclr #5,%d0 | Reset bit 5 and set Z flag
269 | according to previous state
270 bne initLp | Loop if drive still on
273 moveb
#iwmMode,%a0@(q7H) | Init IWM
278 tstb
%a0@
(q6L
) | Prepare IWM for data
288 * iwmCheckDrive -- Check if given drive is available and return bit vector
289 * with capabilities (SS/DS, disk inserted, ...)
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
297 * 31 - (-1) No drive / invalid drive #
301 moveml
%d1
/%a0-
%a1
,%sp@
-
302 movel _C_LABEL
(IWMBase
),%a0
303 movel _C_LABEL
(Via1Base
),%a1
305 moveq
#-1,%d1 | no drive
307 movel
%a6@
(0x08),%d0 | check drive
#
312 bra chkDone | invalid drive
#
315 tstb
%a0@
(intDrive
) | SELECT; choose drive
#0
319 tstb
%a0@
(extDrive
) | SELECT; choose drive
#1
322 moveq
#-2,%d1 | error code
323 moveq
#drvInstalled,%d0 | Drive installed?
325 bmi chkDone | no drive
327 moveq
#0,%d1 | Drive found
328 tstb
%a0@
(mtrOn
) | ENABLE; activate drive
329 moveq
#singleSided,%d0 | Drive is single-sided?
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.
339 bset
#0,%d1 | 1 = no.
341 moveq
#diskInserted,%d0 | Disk inserted?
344 bset
#1,%d1 | 1 = No.
347 moveq
#drvMotorState,%d0 | Motor is running?
350 bset
#2,%d1 | 1 = No.
352 moveq
#writeProtected,%d0 | Disk is write protected?
355 bset
#3,%d1 | 1 = No.
357 moveq
#diskIsHD,%d0 | Disk is HD? (was "drive installed")
360 bset
#4,%d1 | 1 = No.
363 moveml
%sp@
+,%d1
/%a0-
%a1
369 * iwmDiskEject -- post EJECT command and toggle LSTRB line to give a
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.
374 * Parameters: stack l drive number (0,1)
377 * Returns: %d0 result code
381 movel _C_LABEL
(IWMBase
),%a0
382 movel _C_LABEL
(Via1Base
),%a1
384 movel
%a6@
(0x08),%d0 | Get drive
#
389 bra ejDone | Invalid drive
#
392 tstb
%a0@
(intDrive
) | SELECT; choose drive
#0
396 tstb
%a0@
(extDrive
) | SELECT; choose drive
#1
398 tstb
%a0@
(mtrOn
) | ENABLE; activate drive
400 moveq
#motorOffCmd,%d0 | Motor off
403 moveq
#diskInserted,%d0 | Disk inserted?
407 moveq
#ejectDiskCmd,%d0 | Eject it
410 tstb
%a0@
(ph3H
) | LSTRB high
412 movel
#1000,%sp@- | delay 1 ms
414 addqw
#4,%sp | clean up stack
419 tstb
%a0@
(ph3L
) | LSTRB low
420 moveq
#0,%d0 | All's well...
427 * iwmSelectDrive -- select internal (0) / external (1) drive.
429 * Parameters: stack l drive ID (0/1)
430 * Returns: %d0 drive #
432 ENTRY
(iwmSelectDrive
)
435 movel _C_LABEL
(IWMBase
),%a0
436 movel _C_LABEL
(Via1Base
),%a1
438 movel
%a6@
(8),%d0 | Get drive
#
451 * iwmMotor -- switch drive motor on/off
453 * Parameters: stack l drive ID (0/1)
454 * stack l on(1)/off(0)
455 * Returns: %d0 motor cmd
460 movel _C_LABEL
(IWMBase
),%a0
461 movel _C_LABEL
(Via1Base
),%a1
463 movel
%a6@
(8),%d0 | Get drive
#
470 movel
#motorOnCmd,%d0 | Motor ON
473 movel
#motorOffCmd,%d0
483 * iwmSelectSide -- select side 0 (lower head) / side 1 (upper head).
485 * This MUST be called immediately before an actual read/write access.
487 * Parameters: stack l side bit (0/1)
492 moveml
%d1
/%a0-
%a1
,%sp@
-
493 movel _C_LABEL
(IWMBase
),%a0
494 movel _C_LABEL
(Via1Base
),%a1
496 moveq
#0x0B,%d0 | Drive ready for reading?
497 bsr selDriveReg |
(undocumented
)
502 moveq
#rdDataFrom0,%d0 | Lower head
503 movel
%a6@
(0x08),%d1 | Get side
#
505 moveq
#rdDataFrom1,%d0 | Upper head
509 moveml
%sp@
+,%d1
/%a0-
%a1
515 * iwmTrack00 -- move head to track 00 for drive calibration.
517 * XXX Drive makes funny noises during resore. Tune delay/retry count?
520 * Returns: %d0 result code
524 moveml
%d1-
%d4
/%a0-
%a1
,%sp@
-
525 movel _C_LABEL
(IWMBase
),%a0
526 movel _C_LABEL
(Via1Base
),%a1
528 moveq
#motorOnCmd,%d0 | Switch drive motor on
531 moveq
#stepOutCmd,%d0 | Step out
534 movew
#100,%d2 | Max. tries
536 moveq
#atTrack00,%d0 | Already at track 0?
538 bpl isTrack00 | Track
0 => Bit
7 = 0
540 moveq
#doStepCmd,%d0 | otherwise step
542 movew
#80,%d4 | Retries
544 moveq
#stillStepping,%d0 | Drive is still stepping?
551 moveq
#cantStepErr,%d0 | Not ready after many retries
557 jsr _C_LABEL
(delay
) | in mac68k
/clock.c
566 moveq
#tk0BadErr,%d0 | Can't find track 00!!
572 moveml
%sp@
+,%d1-
%d4
/%a0-
%a1
578 * iwmSeek -- do specified # of steps (positive - in, negative - out).
580 * Parameters: stack l # of steps
581 * returns: %d0 result code
585 moveml
%d1-
%d4
/%a0-
%a1
,%sp@
-
586 movel _C_LABEL
(IWMBase
),%a0
587 movel _C_LABEL
(Via1Base
),%a1
589 moveq
#motorOnCmd,%d0 | Switch drive motor on
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.
597 moveq
#stepOutCmd,%d0 | Set step OUT
598 negl
%d2 | Make
# of steps positive
600 subql
#1,%d2 | Loop exits for -1
601 bsr driveCmd | Set direction
604 bsr driveCmd | Step one
!
605 movew
#80,%d4 | Retries
607 moveq
#stillStepping, %d0 | Drive is still stepping?
614 moveq
#cantStepErr,%d2 | Not ready after many retries
620 jsr _C_LABEL
(delay
) | in mac68k
/clock.c
623 movew _C_LABEL
(TimeDBRA
),%d4 | dbra loops per ms
625 st03
: dbra
%d4
,st03 | makes ca.
30 us
630 moveq
#0,%d2 | All is well
633 moveml
%sp@
+,%d1-
%d4
/%a0-
%a1
639 * iwmReadSector -- read and decode the next available sector.
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!
645 * XXX make "sizeof cylCache_t" a symbolic constant
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
654 * %fp-5 b sector wanted
655 * %fp-6 b retry count
656 * %fp-7 b sector read
660 moveml
%d1-
%d7
/%a0-
%a5
,%sp@
-
662 movel _C_LABEL
(Via1Base
),%a1
663 movel
%a6@
(o_hdr
),%a4 | Addr of sector header struct
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
670 movew
%sr,%a6@
(-2) | Save CPU status register
671 oriw
#0x0600,%sr | Block all interrupts
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
679 * Is this the right track & side? If not, return with error
681 movel
%a6@
(o_hdr
),%a4 | Sector header struct
683 moveb
%a4@
(o_side
),%d1 | Get actual side
684 lsrb
#3,%d1 | "Normalize" side bit (to bit 0)
686 moveb
%a6@
(-3),%d2 | Get wanted side
687 eorb
%d1
,%d2 | Compare side bits
688 bne rsSeekErr | Should
be equal
!
690 moveb
%a6@
(-4),%d1 | Get track
# we want
691 cmpb
%a4@
(o_track
),%d1 | Compare to the header we
've read
695 moveq #seekErr,%d0 | Wrong track or side found
699 * Check for sector data lead-in 'D5 AA AD
'
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
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
710 lea dataLeadIn,%a3 | Sector data lead-in
711 moveq #0x03,%d4 | is 3 bytes long
713 moveb %a0@,%d2 | Get next byte
716 moveq #noDtaMkErr,%d0 | Can't find
a data mark
721 bne rsLeadIn | If ne restart scan
726 * We have found the lead-in. Now get the 12 tag bytes.
727 * (We leave %a3 pointing to 'dataLeadOut' for later.)
730 moveb
%a0@
,%d3 | Get
a char
,
732 moveb
%a2@
(0,%d3
),%a4@
+ | remap
and store it
734 moveq
#0,%d5 | Clear checksum registers
737 moveq
#10,%d4 | Loop counter
738 moveq
#0,%d3 | Data scratch reg
742 moveb
%a0@
,%d3 | Get
2 bit nibbles
744 moveb
%a2@
(0,%d3
),%d1 | Remap disk byte
747 andib
#0xC0,%d2 | Get top 2 bits for first byte
749 moveb
%a0@
,%d3 | Get first
6 bit nibble
751 orb
%a2@
(0,%d3
),%d2 | Remap it
and complete first byte
753 moveb
%d7
,%d3 | The X flag bit
(a copy of the carry
754 addb
%d7
,%d3 | flag
) is added with the next addx
758 moveb
%d2
,%a4@
+ | Store tag byte
759 addxb
%d2
,%d5 | See above
763 andib
#0xC0,%d2 | Get top 2 bits for second byte
765 moveb
%a0@
,%d3 | Get second
6 bit nibble
767 orb
%a2@
(0,%d3
),%d2 | remap it
and complete byte
769 moveb
%d2
,%a4@
+ | Store tag byte
773 andib
#0xC0,%d1 | Get top 2 bits for third byte
775 moveb
%a0@
,%d3 | Get third
6 bit nibble
777 orb
%a2@
(0,%d3
),%d1 | remap it
and complete byte
779 moveb
%d1
,%a4@
+ | Store tag byte
782 subqw
#3,%d4 | Update byte counter (four 6&2 encoded
783 bpl rsTags | disk bytes make three data bytes
).
786 * Jetzt sind wir hier...
787 * ...und Thomas D. hat noch was zu sagen...
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.
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
798 movel
%a6@
(o_buf
),%a4 | Sector data buffer
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
807 moveb
%a0@
,%d3 | Get
2 bit nibbles
809 moveb
%a2@
(0,%d3
),%d1 | Remap disk byte
812 andib
#0xC0,%d2 | Get top 2 bits for first byte
814 moveb
%a0@
,%d3 | Get first
6 bit nibble
816 orb
%a2@
(0,%d3
),%d2 | Remap it
and complete first byte
818 moveb
%d7
,%d3 | The X flag bit
(a copy of the carry
819 addb
%d7
,%d3 | flag
) is added with the next addx
823 moveb
%d2
,%a4@
+ | Store data byte
824 addxb
%d2
,%d5 | See above
828 andib
#0xC0,%d2 | Get top 2 bits for second byte
830 moveb
%a0@
,%d3 | Get second
6 bit nibble
832 orb
%a2@
(0,%d3
),%d2 | Remap it
and complete byte
834 moveb
%d2
,%a4@
+ | Store data byte
837 beq rsCkSum | Data read
, continue with checksums
840 andib
#0xC0,%d1 | Get top 2 bits for third byte
842 moveb
%a0@
,%d3 | Get third
6 bit nibble
844 orb
%a2@
(0,%d3
),%d1 | Remap it
and complete byte
846 moveb
%d1
,%a4@
+ | Store data byte
848 subqw
#3,%d4 | Update byte counter
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
859 moveb
%a0@
,%d3 | Get
2 bit nibbles
861 moveb
%a2@
(0,%d3
),%d1 | Remap disk byte
862 bmi rsBadCkSum | Fault
! (Bad read
)
865 andib
#0xC0,%d2 | Get top 2 bits for first byte
867 moveb
%a0@
,%d3 | Get first
6 bit nibble
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
)
877 andib
#0xC0,%d2 | Get top 2 bits for second byte
879 moveb
%a0@
,%d3 | Get second
6 bit nibble
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
)
888 andib
#0xC0,%d1 | Get top 2 bits for second byte
890 moveb
%a0@
,%d3 | Get third
6 bit nibble
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
)
899 moveq
#badDCkSum,%d0 | Bad data mark checksum
903 moveq
#badDBtSlp,%d0 | One of the data mark bit slip
904 bra rsDone | nibbles was incorrect
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.)
913 moveq
#1,%d4 | Is two bytes long {1,0}
915 moveb
%a0@
,%d3 | Get token
918 bne rsBadDBtSlp | Fault
!
923 * See if we got the sector we wanted. If not, and no error
924 * occurred, mark buffer valid. Else ignore the sector.
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
933 tstb
%d0 | Any error? Simply ignore data
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"
940 subqb
#1,%a6@(-6) | max. retries
942 | Sector
not found
, but
943 tstb
%d0 | don
't set error code if we
944 bne rsAllDone | already have one.
947 movew %a6@(-2),%sr | Restore interrupt mask
948 moveml %sp@+,%d1-%d7/%a0-%a5
954 * iwmWriteSector -- encode and write data to the specified sector.
956 * TODO: Poll SCC as long as interrupts are disabled (see top comment)
957 * Understand and document the checksum algorithm!
959 * XXX Use registers more efficiently
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
965 * Local: %fp-2 w CPU status register
968 * %fp-5 b sector wanted
969 * %fp-6 b retry count
970 * %fp-10 b current slot
972 ENTRY(iwmWriteSector)
974 moveml %d1-%d7/%a0-%a5,%sp@-
976 movel _C_LABEL(Via1Base),%a1
977 movel %a6@(o_hdr),%a4 | Addr of sector header struct
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
984 movew %sr,%a6@(-2) | Save CPU status register
985 oriw #0x0600,%sr | Block all interrupts
988 movel %a6@(o_hdr),%a4
989 bsr readSectHdr | Get next available sector header
990 bne wsAllDone | Return if error
993 * Is this the right track & side? If not, return with error
995 movel %a6@(o_hdr),%a4 | Sector header struct
997 moveb %a4@(o_side),%d1 | Get side#
998 lsrb #3,%d1 | "Normalize" side bit...
1000 moveb %a6@(-3),%d2 | Get wanted side
1001 eorb %d1,%d2 | Compare side bits
1004 moveb %a6@(-4),%d1 | Get wanted track#
1005 cmpb %a4@(o_track),%d1 | Compare to the read header
1009 moveq #seekErr,%d0 | Wrong track or side
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.
1017 * Alternatively, we could decrement a "dirty sectors" counter here.
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?
1027 moveq #maxGCRSectors-1,%d2 | Any dirty sectors left?
1030 lslw #3,%d1 | sizeof cylCacheSlot_t is 8 bytes
1031 tstl %a4@(o_valid,%d1)
1032 bne wsNextSect | Sector dirty?
1035 bra wsAllDone | We are through with this track.
1039 * Write sync pattern and sector data lead-in 'D5 AA
'. The
1040 * missing 'AD
' is made up by piping 0x0B through the nibble
1043 * To set up IWM for writing:
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.
1050 * %a0 IWM base address (later: data register)
1052 * %a2 IWM handshake register
1053 * %a3 data (tags buffer, data buffer)
1054 * %a4 Sync pattern, 'toDisk
' translation table
1057 movel _C_LABEL(IWMBase),%a0
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
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
1069 moveq #6,%d0 | Loop counter for sync bytes
1072 movel #0x02010009,%d4 | Loop counters for tag/sector data
1075 * Write 5 sync bytes and first byte of sector data lead-in
1078 moveb %a4@+,%d1 | Get next sync byte
1080 tstb %a2@ | IWM ready?
1082 moveb %d1,%a0@ | Write it to disk
1086 moveb %a4@+,%d1 | Write 2nd byte of sector lead-in
1087 lea toDisk,%a4 | Point %a4 to nibble translation table
1089 tstb %a2@ | IWM ready?
1091 moveb %d1,%a0@ | Write it to disk
1093 moveq #0,%d5 | Clear checksum registers
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
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
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
1116 * c) adds up three 8 bit checksums, one for each of the bytes written.
1119 movel %a6@(-10),%a3 | Get ptr to current slot
1120 movel %a3@(o_secbuf),%a3 | Get start of sector data buffer
1126 lsrw #6,%d3 | Put 2 bit nibbles into place
1128 tstb %a2@ | IWM ready?
1130 moveb %a4@(0,%d3),%a0@ | Translate nibble and write
1131 subqw #3,%d4 | Update counter
1133 addb %d7,%d3 | Set X flag (??)
1137 tstb %a2@ | IWM ready?
1139 moveb %a4@(0,%d0),%a0@ | Translate nibble and write
1142 * We enter with the last byte of the sector data lead-in
1143 * between our teeth (%D1, that is).
1146 moveb %a3@+,%d0 | Get first byte
1149 moveb %d0,%d3 | Keep top two bits
1150 rolw #2,%d3 | by shifting them to MSByte
1153 tstb %a2@ | IWM ready?
1155 moveb %a4@(0,%d1),%a0@ | Translate nibble and write
1157 moveb %a3@+,%d1 | Get second byte
1160 moveb %d1,%d3 | Keep top two bits
1161 rolw #2,%d3 | by shifting them to MSByte
1164 tstb %a2@ | IWM ready?
1166 moveb %a4@(0,%d2),%a0@ | Translate nibble and write
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
).
1175 tstl
%d4 | Last loop cycle?
1176 beq wsSDDone | Then get out while we can.
1178 moveb
%a3@
+,%d2 | Get third byte
1179 tstw
%d4 | First write tag buffer
,...
1182 swap
%d4 |
...then write data buffer
1186 * Write nibbles for last 2 bytes, then
1187 * split checksum bytes in 6&2 and write them to disk
1190 clrb
%d3 | No
513th byte
1191 lsrw
#6,%d3 | Set up 2 bit nibbles
1193 tstb
%a2@ | IWM ready?
1195 moveb
%a4@
(0,%d3
),%a0@ | Write fragments
1202 tstb
%a2@ | IWM ready?
1204 moveb
%a4@
(0,%d0
),%a0@ | Write
511th byte
1207 tstb
%a2@ | IWM ready?
1209 moveb
%a4@
(0,%d1
),%a0@ | write
512th byte
1211 lsrw
#6,%d3 | Get fragments ready
1213 tstb
%a2@ | IWM ready?
1215 moveb
%a4@
(0,%d3
),%a0@ | Write fragments
1218 tstb
%a2@ | IWM ready?
1220 moveb
%a4@
(0,%d5
),%a0@ | Write first checksum byte
1223 tstb
%a2@ | IWM ready?
1225 moveb
%a4@
(0,%d6
),%a0@ | Write second checksum byte
1228 tstb
%a2@ | IWM ready?
1230 moveb
%a4@
(0,%d7
),%a0@ | Write third checksum byte
1233 * Write sector data lead-out
1235 lea dataLeadOut
,%a4 | Sector data lead-out
1236 moveq
#3,%d2 | Four bytes long {3,2,1,0}
1238 moveb
%a2@
,%d1 | IWM ready?
1240 moveb
%a4@
+,%a0@ | Write lead-out
1244 btst
#6,%d1 | Check IWM underrun bit
1247 moveq
#wrUnderRun,%d0 | Could not write
1248 | fast enough to keep up with IWM
1250 tstb
%a0@
(0x0200) | q7L
-- Write OFF
1253 tstb
%d0 | Any error? Simply retry
1256 movel
%a6@
(-10),%a4 | Else
, get ptr to current slot
1257 clrl
%a4@
(o_valid
) | Mark current buffer
"clean"
1261 subqb
#1,%a6@(-6) | retries
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
1269 movew %a6@(-2),%sr | Restore interrupt mask
1270 moveml %sp@+,%d1-%d7/%a0-%a5
1283 * In-kernel calls to delay() in mac68k/clock.c bomb
1285 * Parameters: %d0 delay in milliseconds
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 |
1298 * selDriveReg -- Select drive status/control register
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)
1307 tstb %a0@(ph0H) | default CA0 to 1 (says IM III)
1308 tstb %a0@(ph1H) | default CA1 to 1
1310 btst #0,%d0 | bit 0 set => CA2 on
1318 btst #1,%d0 | bit 1 set => SEL on (VIA 1)
1320 bset #vHeadSel,%a1@(vBufA)
1323 bclr #vHeadSel,%a1@(vBufA)
1326 btst #2,%d0 | bit 2 set => CA0 on
1331 btst #3,%d0 | bit 3 set => CA1 on
1340 * dstatus -- check drive status (bit 7 - N flag) wrt. a previously
1343 * Parameters: %d0 register selector
1344 * %a0 IWM base address
1345 * Returns: %d0 status
1350 tstb %a0@(q6L) | leave in "read data reg"
1351 tstb %d0 | state for safety
1356 * driveStat -- query drive status.
1358 * Parameters: %a0 IWMBase
1360 * %d0 register selector
1361 * Returns: %d0 status (Bit 7)
1364 tstb %a0@(mtrOn) | ENABLE; turn drive on
1371 * dtrigger -- toggle LSTRB line to give drive a strobe signal
1372 * IM III says pulse length = 1 us < t < 1 ms
1374 * Parameters: %a0 IWMBase
1379 tstb %a0@(ph3H) | LSTRB high
1380 moveb %a1@(vBufA),%a1@(vBufA) | intelligent nop seen in q700 ROM
1381 tstb %a0@(ph3L) | LSTRB low
1386 * driveCmd -- send command to drive.
1388 * Parameters: %a0 IWMBase
1400 * readSectHdr -- read and decode the next available sector header.
1402 * TODO: Poll SCC as long as interrupts are disabled.
1404 * Parameters: %a4 sectorHdr_t address
1405 * Returns: %d0 result code
1406 * Uses: %d0-%d4, %a0, %a2-%a4
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
1414 movel _C_LABEL(IWMBase),%a0 | IWM base address
1417 lea %a0@(q6L),%a0 | IWM data register
1419 moveb %a0@,%d2 | Read char
1422 moveq #noNybErr,%d0 | Disk is blank?
1426 bpl shReadSy | No char at IWM, repeat read
1430 * When we get here, the IWM should be in sync with the data
1432 * Next look for sector header lead-in 'D5 AA
96'
1434 movew #1500,%d3 | Retries to seek header
1436 lea hdrLeadIn,%a3 | Sector header lead-in bytes
1437 moveq #0x03,%d4 | is 3 bytes long
1439 moveb %a0@,%d2 | Get next byte
1440 bpl shLI1 | No char at IWM, repeat read
1442 moveq #noAdrMkErr,%d0 | Can't find an address mark
1447 bne shLeadIn | If ne restart scan
1451 * We have found the lead-in. Now get the header information.
1452 * Reg %d4 holds the checksum.
1454 lea diskTo-
0x90,%a2 | Translate disk bytes
-> 6&2
1456 moveb
%a0@
,%d0 | Get
1st char
1458 moveb
%a2@
(0,%d0
),%d1 |
and remap it
1460 rorw
#6,%d1 | separate 2:6, drop hi bits
1462 moveb
%a0@
,%d0 | Get
2nd char
1464 moveb
%a2@
(0,%d0
),%d2 |
and remap it
1467 moveb
%a0@
,%d0 | Get
3rd char
1469 moveb
%a2@
(0,%d0
),%d1 |
and remap it
1473 moveb
%a0@
,%d0 | Get
4th char
1475 moveb
%a2@
(0,%d0
),%d3 |
and remap it
1478 moveb
%a0@
,%d0 | Get checksum byte
1480 moveb
%a2@
(0,%d0
),%d5 |
and remap it
1482 bne shCsErr | Checksum ok?
1485 * %d1/lsb track number
1486 * %d1/msb bit 3 is side bit
1491 * Next check for lead-out.
1493 moveq
#1,%d4 | is 2 bytes long
1495 moveb
%a0@
,%d0 | Get token
1497 cmpb
%a3@
+,%d0 | Check
1498 bne shLOErr | Fault
!
1500 movew
%d1
,%d0 | Isolate side bit
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
1509 moveq
#badCkSmErr,%d0 | Bad sector header checksum
1512 moveq
#badBtSlpErr,%d0 | Bad address mark (no lead-out)
1515 tstl
%d0 | Set flags