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