|
|
1.1 ! root 1: // 16bit code to access floppy drives. ! 2: // ! 3: // Copyright (C) 2008,2009 Kevin O'Connor <[email protected]> ! 4: // Copyright (C) 2002 MandrakeSoft S.A. ! 5: // ! 6: // This file may be distributed under the terms of the GNU LGPLv3 license. ! 7: ! 8: #include "types.h" // u8 ! 9: #include "disk.h" // DISK_RET_SUCCESS ! 10: #include "config.h" // CONFIG_FLOPPY ! 11: #include "biosvar.h" // SET_BDA ! 12: #include "util.h" // wait_irq ! 13: #include "cmos.h" // inb_cmos ! 14: #include "pic.h" // eoi_pic1 ! 15: #include "bregs.h" // struct bregs ! 16: ! 17: #define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors ! 18: #define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02 ! 19: #define FLOPPY_MOTOR_TICKS 37 // ~2 seconds ! 20: #define FLOPPY_FILLBYTE 0xf6 ! 21: #define FLOPPY_GAPLEN 0x1B ! 22: #define FLOPPY_FORMAT_GAPLEN 0x6c ! 23: ! 24: // New diskette parameter table adding 3 parameters from IBM ! 25: // Since no provisions are made for multiple drive types, most ! 26: // values in this table are ignored. I set parameters for 1.44M ! 27: // floppy here ! 28: struct floppy_ext_dbt_s diskette_param_table2 VAR16VISIBLE = { ! 29: .dbt = { ! 30: .specify1 = 0xAF, // step rate 12ms, head unload 240ms ! 31: .specify2 = 0x02, // head load time 4ms, DMA used ! 32: .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds ! 33: .bps_code = FLOPPY_SIZE_CODE, ! 34: .sectors = 18, ! 35: .interblock_len = FLOPPY_GAPLEN, ! 36: .data_len = FLOPPY_DATALEN, ! 37: .gap_len = FLOPPY_FORMAT_GAPLEN, ! 38: .fill_byte = FLOPPY_FILLBYTE, ! 39: .settle_time = 0x0F, // 15ms ! 40: .startup_time = 0x08, // 1 second ! 41: }, ! 42: .max_track = 79, // maximum track ! 43: .data_rate = 0, // data transfer rate ! 44: .drive_type = 4, // drive type in cmos ! 45: }; ! 46: ! 47: // Since no provisions are made for multiple drive types, most ! 48: // values in this table are ignored. I set parameters for 1.44M ! 49: // floppy here ! 50: struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7) = { ! 51: .specify1 = 0xAF, ! 52: .specify2 = 0x02, ! 53: .shutoff_ticks = FLOPPY_MOTOR_TICKS, ! 54: .bps_code = FLOPPY_SIZE_CODE, ! 55: .sectors = 18, ! 56: .interblock_len = FLOPPY_GAPLEN, ! 57: .data_len = FLOPPY_DATALEN, ! 58: .gap_len = FLOPPY_FORMAT_GAPLEN, ! 59: .fill_byte = FLOPPY_FILLBYTE, ! 60: .settle_time = 0x0F, ! 61: .startup_time = 0x08, ! 62: }; ! 63: ! 64: struct floppyinfo_s { ! 65: struct chs_s chs; ! 66: u8 config_data; ! 67: u8 media_state; ! 68: }; ! 69: ! 70: struct floppyinfo_s FloppyInfo[] VAR16VISIBLE = { ! 71: // Unknown ! 72: { {0, 0, 0}, 0x00, 0x00}, ! 73: // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors ! 74: { {2, 40, 9}, 0x00, 0x25}, ! 75: // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors ! 76: { {2, 80, 15}, 0x00, 0x25}, ! 77: // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors ! 78: { {2, 80, 9}, 0x00, 0x17}, ! 79: // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors ! 80: { {2, 80, 18}, 0x00, 0x17}, ! 81: // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors ! 82: { {2, 80, 36}, 0xCC, 0xD7}, ! 83: // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors ! 84: { {1, 40, 8}, 0x00, 0x27}, ! 85: // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors ! 86: { {1, 40, 9}, 0x00, 0x27}, ! 87: // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors ! 88: { {2, 40, 8}, 0x00, 0x27}, ! 89: }; ! 90: ! 91: struct drive_s * ! 92: addFloppy(int floppyid, int ftype, int driver) ! 93: { ! 94: if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) { ! 95: dprintf(1, "Bad floppy type %d\n", ftype); ! 96: return NULL; ! 97: } ! 98: ! 99: struct drive_s *drive_g = allocDrive(); ! 100: if (!drive_g) ! 101: return NULL; ! 102: drive_g->cntl_id = floppyid; ! 103: drive_g->type = driver; ! 104: drive_g->blksize = DISK_SECTOR_SIZE; ! 105: drive_g->floppy_type = ftype; ! 106: drive_g->sectors = (u64)-1; ! 107: ! 108: memcpy(&drive_g->lchs, &FloppyInfo[ftype].chs ! 109: , sizeof(FloppyInfo[ftype].chs)); ! 110: ! 111: map_floppy_drive(drive_g); ! 112: return drive_g; ! 113: } ! 114: ! 115: void ! 116: describe_floppy(struct drive_s *drive_g) ! 117: { ! 118: printf("drive %c", 'A' + drive_g->cntl_id); ! 119: } ! 120: ! 121: void ! 122: floppy_setup() ! 123: { ! 124: if (! CONFIG_FLOPPY) ! 125: return; ! 126: dprintf(3, "init floppy drives\n"); ! 127: ! 128: if (CONFIG_COREBOOT) { ! 129: // XXX - disable floppies on coreboot for now. ! 130: } else { ! 131: u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE); ! 132: if (type & 0xf0) ! 133: addFloppy(0, type >> 4, DTYPE_FLOPPY); ! 134: if (type & 0x0f) ! 135: addFloppy(1, type & 0x0f, DTYPE_FLOPPY); ! 136: } ! 137: ! 138: outb(0x02, PORT_DMA1_MASK_REG); ! 139: ! 140: enable_hwirq(6, entry_0e); ! 141: } ! 142: ! 143: // Find a floppy type that matches a given image size. ! 144: int ! 145: find_floppy_type(u32 size) ! 146: { ! 147: int i; ! 148: for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) { ! 149: struct chs_s *c = &FloppyInfo[i].chs; ! 150: if (c->cylinders * c->heads * c->spt * DISK_SECTOR_SIZE == size) ! 151: return i; ! 152: } ! 153: return -1; ! 154: } ! 155: ! 156: ! 157: /**************************************************************** ! 158: * Low-level floppy IO ! 159: ****************************************************************/ ! 160: ! 161: static void ! 162: floppy_reset_controller() ! 163: { ! 164: // Reset controller ! 165: u8 val8 = inb(PORT_FD_DOR); ! 166: outb(val8 & ~0x04, PORT_FD_DOR); ! 167: outb(val8 | 0x04, PORT_FD_DOR); ! 168: ! 169: // Wait for controller to come out of reset ! 170: while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80) ! 171: ; ! 172: } ! 173: ! 174: static int ! 175: wait_floppy_irq() ! 176: { ! 177: ASSERT16(); ! 178: u8 v; ! 179: for (;;) { ! 180: if (!GET_BDA(floppy_motor_counter)) ! 181: return -1; ! 182: v = GET_BDA(floppy_recalibration_status); ! 183: if (v & FRS_TIMEOUT) ! 184: break; ! 185: wait_irq(); ! 186: } ! 187: ! 188: v &= ~FRS_TIMEOUT; ! 189: SET_BDA(floppy_recalibration_status, v); ! 190: return 0; ! 191: } ! 192: ! 193: static void ! 194: floppy_prepare_controller(u8 floppyid) ! 195: { ! 196: CLEARBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT); ! 197: ! 198: // turn on motor of selected drive, DMA & int enabled, normal operation ! 199: u8 prev_reset = inb(PORT_FD_DOR) & 0x04; ! 200: u8 dor = 0x10; ! 201: if (floppyid) ! 202: dor = 0x20; ! 203: dor |= 0x0c; ! 204: dor |= floppyid; ! 205: outb(dor, PORT_FD_DOR); ! 206: ! 207: // reset the disk motor timeout value of INT 08 ! 208: SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS); ! 209: ! 210: // wait for drive readiness ! 211: while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80) ! 212: ; ! 213: ! 214: if (!prev_reset) ! 215: wait_floppy_irq(); ! 216: } ! 217: ! 218: static int ! 219: floppy_pio(u8 *cmd, u8 cmdlen) ! 220: { ! 221: floppy_prepare_controller(cmd[1] & 1); ! 222: ! 223: // send command to controller ! 224: u8 i; ! 225: for (i=0; i<cmdlen; i++) ! 226: outb(cmd[i], PORT_FD_DATA); ! 227: ! 228: int ret = wait_floppy_irq(); ! 229: if (ret) { ! 230: floppy_reset_controller(); ! 231: return -1; ! 232: } ! 233: ! 234: return 0; ! 235: } ! 236: ! 237: static int ! 238: floppy_cmd(struct disk_op_s *op, u16 count, u8 *cmd, u8 cmdlen) ! 239: { ! 240: // es:bx = pointer to where to place information from diskette ! 241: u32 addr = (u32)op->buf_fl; ! 242: ! 243: // check for 64K boundary overrun ! 244: u16 end = count - 1; ! 245: u32 last_addr = addr + end; ! 246: if ((addr >> 16) != (last_addr >> 16)) ! 247: return DISK_RET_EBOUNDARY; ! 248: ! 249: u8 mode_register = 0x4a; // single mode, increment, autoinit disable, ! 250: if (cmd[0] == 0xe6) ! 251: // read ! 252: mode_register = 0x46; ! 253: ! 254: //DEBUGF("floppy dma c2\n"); ! 255: outb(0x06, PORT_DMA1_MASK_REG); ! 256: outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop ! 257: outb(addr, PORT_DMA_ADDR_2); ! 258: outb(addr>>8, PORT_DMA_ADDR_2); ! 259: outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop ! 260: outb(end, PORT_DMA_CNT_2); ! 261: outb(end>>8, PORT_DMA_CNT_2); ! 262: ! 263: // port 0b: DMA-1 Mode Register ! 264: // transfer type=write, channel 2 ! 265: outb(mode_register, PORT_DMA1_MODE_REG); ! 266: ! 267: // port 81: DMA-1 Page Register, channel 2 ! 268: outb(addr>>16, PORT_DMA_PAGE_2); ! 269: ! 270: outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2 ! 271: ! 272: int ret = floppy_pio(cmd, cmdlen); ! 273: if (ret) ! 274: return DISK_RET_ETIMEOUT; ! 275: ! 276: // check port 3f4 for accessibility to status bytes ! 277: if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) ! 278: return DISK_RET_ECONTROLLER; ! 279: ! 280: // read 7 return status bytes from controller ! 281: u8 i; ! 282: for (i=0; i<7; i++) { ! 283: u8 v = inb(PORT_FD_DATA); ! 284: cmd[i] = v; ! 285: SET_BDA(floppy_return_status[i], v); ! 286: } ! 287: ! 288: return DISK_RET_SUCCESS; ! 289: } ! 290: ! 291: ! 292: /**************************************************************** ! 293: * Floppy media sense ! 294: ****************************************************************/ ! 295: ! 296: static inline void ! 297: set_diskette_current_cyl(u8 floppyid, u8 cyl) ! 298: { ! 299: SET_BDA(floppy_track[floppyid], cyl); ! 300: } ! 301: ! 302: static void ! 303: floppy_drive_recal(u8 floppyid) ! 304: { ! 305: // send Recalibrate command (2 bytes) to controller ! 306: u8 data[12]; ! 307: data[0] = 0x07; // 07: Recalibrate ! 308: data[1] = floppyid; // 0=drive0, 1=drive1 ! 309: floppy_pio(data, 2); ! 310: ! 311: SETBITS_BDA(floppy_recalibration_status, 1<<floppyid); ! 312: set_diskette_current_cyl(floppyid, 0); ! 313: } ! 314: ! 315: static int ! 316: floppy_media_sense(struct drive_s *drive_g) ! 317: { ! 318: // for now cheat and get drive type from CMOS, ! 319: // assume media is same as drive type ! 320: ! 321: // ** config_data ** ! 322: // Bitfields for diskette media control: ! 323: // Bit(s) Description (Table M0028) ! 324: // 7-6 last data rate set by controller ! 325: // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps ! 326: // 5-4 last diskette drive step rate selected ! 327: // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah ! 328: // 3-2 {data rate at start of operation} ! 329: // 1-0 reserved ! 330: ! 331: // ** media_state ** ! 332: // Bitfields for diskette drive media state: ! 333: // Bit(s) Description (Table M0030) ! 334: // 7-6 data rate ! 335: // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps ! 336: // 5 double stepping required (e.g. 360kB in 1.2MB) ! 337: // 4 media type established ! 338: // 3 drive capable of supporting 4MB media ! 339: // 2-0 on exit from BIOS, contains ! 340: // 000 trying 360kB in 360kB ! 341: // 001 trying 360kB in 1.2MB ! 342: // 010 trying 1.2MB in 1.2MB ! 343: // 011 360kB in 360kB established ! 344: // 100 360kB in 1.2MB established ! 345: // 101 1.2MB in 1.2MB established ! 346: // 110 reserved ! 347: // 111 all other formats/drives ! 348: ! 349: u8 ftype = GET_GLOBAL(drive_g->floppy_type); ! 350: SET_BDA(floppy_last_data_rate, GET_GLOBAL(FloppyInfo[ftype].config_data)); ! 351: u8 floppyid = GET_GLOBAL(drive_g->cntl_id); ! 352: SET_BDA(floppy_media_state[floppyid] ! 353: , GET_GLOBAL(FloppyInfo[ftype].media_state)); ! 354: return DISK_RET_SUCCESS; ! 355: } ! 356: ! 357: static int ! 358: check_recal_drive(struct drive_s *drive_g) ! 359: { ! 360: u8 floppyid = GET_GLOBAL(drive_g->cntl_id); ! 361: if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid)) ! 362: && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED)) ! 363: // Media is known. ! 364: return DISK_RET_SUCCESS; ! 365: ! 366: // Recalibrate drive. ! 367: floppy_drive_recal(floppyid); ! 368: ! 369: // Sense media. ! 370: return floppy_media_sense(drive_g); ! 371: } ! 372: ! 373: ! 374: /**************************************************************** ! 375: * Floppy handlers ! 376: ****************************************************************/ ! 377: ! 378: static void ! 379: lba2chs(struct disk_op_s *op, u8 *track, u8 *sector, u8 *head) ! 380: { ! 381: u32 lba = op->lba; ! 382: ! 383: u32 tmp = lba + 1; ! 384: u16 nlspt = GET_GLOBAL(op->drive_g->lchs.spt); ! 385: *sector = tmp % nlspt; ! 386: ! 387: tmp /= nlspt; ! 388: u16 nlh = GET_GLOBAL(op->drive_g->lchs.heads); ! 389: *head = tmp % nlh; ! 390: ! 391: tmp /= nlh; ! 392: *track = tmp; ! 393: } ! 394: ! 395: // diskette controller reset ! 396: static int ! 397: floppy_reset(struct disk_op_s *op) ! 398: { ! 399: u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id); ! 400: set_diskette_current_cyl(floppyid, 0); // current cylinder ! 401: return DISK_RET_SUCCESS; ! 402: } ! 403: ! 404: // Read Diskette Sectors ! 405: static int ! 406: floppy_read(struct disk_op_s *op) ! 407: { ! 408: int res = check_recal_drive(op->drive_g); ! 409: if (res) ! 410: goto fail; ! 411: ! 412: u8 track, sector, head; ! 413: lba2chs(op, &track, §or, &head); ! 414: ! 415: // send read-normal-data command (9 bytes) to controller ! 416: u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id); ! 417: u8 data[12]; ! 418: data[0] = 0xe6; // e6: read normal data ! 419: data[1] = (head << 2) | floppyid; // HD DR1 DR2 ! 420: data[2] = track; ! 421: data[3] = head; ! 422: data[4] = sector; ! 423: data[5] = FLOPPY_SIZE_CODE; ! 424: data[6] = sector + op->count - 1; // last sector to read on track ! 425: data[7] = FLOPPY_GAPLEN; ! 426: data[8] = FLOPPY_DATALEN; ! 427: ! 428: res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9); ! 429: if (res) ! 430: goto fail; ! 431: ! 432: if (data[0] & 0xc0) { ! 433: res = DISK_RET_ECONTROLLER; ! 434: goto fail; ! 435: } ! 436: ! 437: // ??? should track be new val from return_status[3] ? ! 438: set_diskette_current_cyl(floppyid, track); ! 439: return DISK_RET_SUCCESS; ! 440: fail: ! 441: op->count = 0; // no sectors read ! 442: return res; ! 443: } ! 444: ! 445: // Write Diskette Sectors ! 446: static int ! 447: floppy_write(struct disk_op_s *op) ! 448: { ! 449: int res = check_recal_drive(op->drive_g); ! 450: if (res) ! 451: goto fail; ! 452: ! 453: u8 track, sector, head; ! 454: lba2chs(op, &track, §or, &head); ! 455: ! 456: // send write-normal-data command (9 bytes) to controller ! 457: u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id); ! 458: u8 data[12]; ! 459: data[0] = 0xc5; // c5: write normal data ! 460: data[1] = (head << 2) | floppyid; // HD DR1 DR2 ! 461: data[2] = track; ! 462: data[3] = head; ! 463: data[4] = sector; ! 464: data[5] = FLOPPY_SIZE_CODE; ! 465: data[6] = sector + op->count - 1; // last sector to write on track ! 466: data[7] = FLOPPY_GAPLEN; ! 467: data[8] = FLOPPY_DATALEN; ! 468: ! 469: res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9); ! 470: if (res) ! 471: goto fail; ! 472: ! 473: if (data[0] & 0xc0) { ! 474: if (data[1] & 0x02) ! 475: res = DISK_RET_EWRITEPROTECT; ! 476: else ! 477: res = DISK_RET_ECONTROLLER; ! 478: goto fail; ! 479: } ! 480: ! 481: // ??? should track be new val from return_status[3] ? ! 482: set_diskette_current_cyl(floppyid, track); ! 483: return DISK_RET_SUCCESS; ! 484: fail: ! 485: op->count = 0; // no sectors read ! 486: return res; ! 487: } ! 488: ! 489: // Verify Diskette Sectors ! 490: static int ! 491: floppy_verify(struct disk_op_s *op) ! 492: { ! 493: int res = check_recal_drive(op->drive_g); ! 494: if (res) ! 495: goto fail; ! 496: ! 497: u8 track, sector, head; ! 498: lba2chs(op, &track, §or, &head); ! 499: ! 500: // ??? should track be new val from return_status[3] ? ! 501: u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id); ! 502: set_diskette_current_cyl(floppyid, track); ! 503: return DISK_RET_SUCCESS; ! 504: fail: ! 505: op->count = 0; // no sectors read ! 506: return res; ! 507: } ! 508: ! 509: // format diskette track ! 510: static int ! 511: floppy_format(struct disk_op_s *op) ! 512: { ! 513: int ret = check_recal_drive(op->drive_g); ! 514: if (ret) ! 515: return ret; ! 516: ! 517: u8 head = op->lba; ! 518: ! 519: // send format-track command (6 bytes) to controller ! 520: u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id); ! 521: u8 data[12]; ! 522: data[0] = 0x4d; // 4d: format track ! 523: data[1] = (head << 2) | floppyid; // HD DR1 DR2 ! 524: data[2] = FLOPPY_SIZE_CODE; ! 525: data[3] = op->count; // number of sectors per track ! 526: data[4] = FLOPPY_FORMAT_GAPLEN; ! 527: data[5] = FLOPPY_FILLBYTE; ! 528: ! 529: ret = floppy_cmd(op, op->count * 4, data, 6); ! 530: if (ret) ! 531: return ret; ! 532: ! 533: if (data[0] & 0xc0) { ! 534: if (data[1] & 0x02) ! 535: return DISK_RET_EWRITEPROTECT; ! 536: return DISK_RET_ECONTROLLER; ! 537: } ! 538: ! 539: set_diskette_current_cyl(floppyid, 0); ! 540: return DISK_RET_SUCCESS; ! 541: } ! 542: ! 543: int ! 544: process_floppy_op(struct disk_op_s *op) ! 545: { ! 546: if (!CONFIG_FLOPPY) ! 547: return 0; ! 548: ! 549: switch (op->command) { ! 550: case CMD_RESET: ! 551: return floppy_reset(op); ! 552: case CMD_READ: ! 553: return floppy_read(op); ! 554: case CMD_WRITE: ! 555: return floppy_write(op); ! 556: case CMD_VERIFY: ! 557: return floppy_verify(op); ! 558: case CMD_FORMAT: ! 559: return floppy_format(op); ! 560: default: ! 561: op->count = 0; ! 562: return DISK_RET_EPARAM; ! 563: } ! 564: } ! 565: ! 566: ! 567: /**************************************************************** ! 568: * HW irqs ! 569: ****************************************************************/ ! 570: ! 571: // INT 0Eh Diskette Hardware ISR Entry Point ! 572: void VISIBLE16 ! 573: handle_0e() ! 574: { ! 575: debug_isr(DEBUG_ISR_0e); ! 576: if (! CONFIG_FLOPPY) ! 577: goto done; ! 578: ! 579: if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) { ! 580: outb(0x08, PORT_FD_DATA); // sense interrupt status ! 581: while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) ! 582: ; ! 583: do { ! 584: inb(PORT_FD_DATA); ! 585: } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0); ! 586: } ! 587: // diskette interrupt has occurred ! 588: SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT); ! 589: ! 590: done: ! 591: eoi_pic1(); ! 592: } ! 593: ! 594: // Called from int08 handler. ! 595: void ! 596: floppy_tick() ! 597: { ! 598: if (! CONFIG_FLOPPY) ! 599: return; ! 600: ! 601: // time to turn off drive(s)? ! 602: u8 fcount = GET_BDA(floppy_motor_counter); ! 603: if (fcount) { ! 604: fcount--; ! 605: SET_BDA(floppy_motor_counter, fcount); ! 606: if (fcount == 0) ! 607: // turn motor(s) off ! 608: outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR); ! 609: } ! 610: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.