Support config driven debugging of each irq handler.
[seabios.git] / src / floppy.c
1 // 16bit code to access floppy drives.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU GPLv3 license.
7
8 #include "types.h" // u8
9 #include "disk.h" // DISK_RET_SUCCESS
10 #include "config.h" // CONFIG_FLOPPY_SUPPORT
11 #include "biosvar.h" // struct bregs
12 #include "util.h" // irq_disable
13 #include "cmos.h" // inb_cmos
14
15 #define DEBUGF1(fmt, args...) bprintf(0, fmt , ##args)
16 #define DEBUGF(fmt, args...)
17
18 #define BX_FLOPPY_ON_CNT 37   /* 2 seconds */
19
20 // New diskette parameter table adding 3 parameters from IBM
21 // Since no provisions are made for multiple drive types, most
22 // values in this table are ignored.  I set parameters for 1.44M
23 // floppy here
24 struct floppy_ext_dbt_s diskette_param_table2 VISIBLE16 = {
25     .dbt = {
26         .specify1       = 0xAF,
27         .specify2       = 0x02, // head load time 0000001, DMA used
28         .shutoff_ticks  = 0x25,
29         .bps_code       = 0x02,
30         .sectors        = 18,
31         .interblock_len = 0x1B,
32         .data_len       = 0xFF,
33         .gap_len        = 0x6C,
34         .fill_byte      = 0xF6,
35         .settle_time    = 0x0F,
36         .startup_time   = 0x08,
37     },
38     .max_track      = 79,   // maximum track
39     .data_rate      = 0,    // data transfer rate
40     .drive_type     = 4,    // drive type in cmos
41 };
42
43 void
44 floppy_drive_setup()
45 {
46     dprintf(3, "init floppy drives\n");
47     u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
48     u8 out = 0;
49     u8 num_floppies = 0;
50
51     if (type & 0xf0) {
52         out |= 0x07;
53         num_floppies++;
54     }
55     if (type & 0x0f) {
56         out |= 0x70;
57         num_floppies++;
58     }
59     SET_BDA(floppy_harddisk_info, out);
60
61     // Update equipment word bits for floppy
62     if (num_floppies == 1)
63         // 1 drive, ready for boot
64         SETBITS_BDA(equipment_list_flags, 0x01);
65     else if (num_floppies == 2)
66         // 2 drives, ready for boot
67         SETBITS_BDA(equipment_list_flags, 0x41);
68
69     outb(0x02, PORT_DMA1_MASK_REG);
70 }
71
72 static inline void
73 set_diskette_current_cyl(u8 drive, u8 cyl)
74 {
75     if (drive)
76         SET_BDA(floppy_track1, cyl);
77     else
78         SET_BDA(floppy_track0, cyl);
79 }
80
81 static u16
82 get_drive_type(u8 drive)
83 {
84     // check CMOS to see if drive exists
85     u8 drive_type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
86     if (drive == 0)
87         drive_type >>= 4;
88     else
89         drive_type &= 0x0f;
90     return drive_type;
91 }
92
93 static u16
94 floppy_media_known(u8 drive)
95 {
96     if (!(GET_BDA(floppy_recalibration_status) & (1<<drive)))
97         return 0;
98     u8 v = GET_BDA(floppy_media_state[drive]);
99     if (!(v & FMS_MEDIA_DRIVE_ESTABLISHED))
100         return 0;
101     return 1;
102 }
103
104 static void
105 floppy_reset_controller()
106 {
107     // Reset controller
108     u8 val8 = inb(PORT_FD_DOR);
109     outb(val8 & ~0x04, PORT_FD_DOR);
110     outb(val8 | 0x04, PORT_FD_DOR);
111
112     // Wait for controller to come out of reset
113     while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
114         ;
115 }
116
117 static void
118 floppy_prepare_controller(u8 drive)
119 {
120     CLEARBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
121
122     // turn on motor of selected drive, DMA & int enabled, normal operation
123     u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
124     u8 dor = 0x10;
125     if (drive)
126         dor = 0x20;
127     dor |= 0x0c;
128     dor |= drive;
129     outb(dor, PORT_FD_DOR);
130
131     // reset the disk motor timeout value of INT 08
132     SET_BDA(floppy_motor_counter, BX_FLOPPY_ON_CNT);
133
134     // wait for drive readiness
135     while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
136         ;
137
138     if (prev_reset == 0) {
139         irq_enable();
140         u8 v;
141         for (;;) {
142             v = GET_BDA(floppy_recalibration_status);
143             if (v & FRS_TIMEOUT)
144                 break;
145             cpu_relax();
146         }
147         irq_disable();
148
149         v &= ~FRS_TIMEOUT;
150         SET_BDA(floppy_recalibration_status, v);
151     }
152 }
153
154 static u8
155 floppy_pio(u8 *cmd, u8 cmdlen)
156 {
157     floppy_prepare_controller(cmd[1] & 1);
158
159     // send command to controller
160     u8 i;
161     for (i=0; i<cmdlen; i++)
162         outb(cmd[i], PORT_FD_DATA);
163
164     irq_enable();
165     u8 v;
166     for (;;) {
167         if (!GET_BDA(floppy_motor_counter)) {
168             irq_disable();
169             floppy_reset_controller();
170             return DISK_RET_ETIMEOUT;
171         }
172         v = GET_BDA(floppy_recalibration_status);
173         if (v & FRS_TIMEOUT)
174             break;
175         cpu_relax();
176     }
177     irq_disable();
178
179     v &= ~FRS_TIMEOUT;
180     SET_BDA(floppy_recalibration_status, v);
181
182     return 0;
183 }
184
185 static u8
186 floppy_cmd(struct bregs *regs, u16 count, u8 *cmd, u8 cmdlen)
187 {
188     // es:bx = pointer to where to place information from diskette
189     // port 04: DMA-1 base and current address, channel 2
190     // port 05: DMA-1 base and current count, channel 2
191     u16 page = regs->es >> 12;   // upper 4 bits
192     u16 base_es = regs->es << 4; // lower 16bits contributed by ES
193     u16 base_address = base_es + regs->bx; // lower 16 bits of address
194     // contributed by ES:BX
195     if (base_address < base_es)
196         // in case of carry, adjust page by 1
197         page++;
198
199     // check for 64K boundary overrun
200     u16 last_addr = base_address + count;
201     if (last_addr < base_address)
202         return DISK_RET_EBOUNDARY;
203
204     u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
205     if (cmd[0] == 0xe6)
206         // read
207         mode_register = 0x46;
208
209     //DEBUGF("floppy dma c2\n");
210     outb(0x06, PORT_DMA1_MASK_REG);
211     outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
212     outb(base_address, PORT_DMA_ADDR_2);
213     outb(base_address>>8, PORT_DMA_ADDR_2);
214     outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
215     outb(count, PORT_DMA_CNT_2);
216     outb(count>>8, PORT_DMA_CNT_2);
217
218     // port 0b: DMA-1 Mode Register
219     // transfer type=write, channel 2
220     outb(mode_register, PORT_DMA1_MODE_REG);
221
222     // port 81: DMA-1 Page Register, channel 2
223     outb(page, PORT_DMA_PAGE_2);
224
225     outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
226
227     u8 ret = floppy_pio(cmd, cmdlen);
228     if (ret)
229         return ret;
230
231     // check port 3f4 for accessibility to status bytes
232     if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
233         BX_PANIC("int13_diskette: ctrl not ready\n");
234
235     // read 7 return status bytes from controller
236     u8 i;
237     for (i=0; i<7; i++) {
238         u8 v = inb(PORT_FD_DATA);
239         cmd[i] = v;
240         SET_BDA(floppy_return_status[i], v);
241     }
242
243     return 0;
244 }
245
246 static void
247 floppy_drive_recal(u8 drive)
248 {
249     // send Recalibrate command (2 bytes) to controller
250     u8 data[12];
251     data[0] = 0x07;  // 07: Recalibrate
252     data[1] = drive; // 0=drive0, 1=drive1
253     floppy_pio(data, 2);
254
255     SETBITS_BDA(floppy_recalibration_status, 1<<drive);
256     set_diskette_current_cyl(drive, 0);
257 }
258
259 static u16
260 floppy_media_sense(u8 drive)
261 {
262     u16 rv;
263     u8 config_data, media_state;
264
265     floppy_drive_recal(drive);
266
267     // for now cheat and get drive type from CMOS,
268     // assume media is same as drive type
269
270     // ** config_data **
271     // Bitfields for diskette media control:
272     // Bit(s)  Description (Table M0028)
273     //  7-6  last data rate set by controller
274     //        00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
275     //  5-4  last diskette drive step rate selected
276     //        00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
277     //  3-2  {data rate at start of operation}
278     //  1-0  reserved
279
280     // ** media_state **
281     // Bitfields for diskette drive media state:
282     // Bit(s)  Description (Table M0030)
283     //  7-6  data rate
284     //    00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
285     //  5  double stepping required (e.g. 360kB in 1.2MB)
286     //  4  media type established
287     //  3  drive capable of supporting 4MB media
288     //  2-0  on exit from BIOS, contains
289     //    000 trying 360kB in 360kB
290     //    001 trying 360kB in 1.2MB
291     //    010 trying 1.2MB in 1.2MB
292     //    011 360kB in 360kB established
293     //    100 360kB in 1.2MB established
294     //    101 1.2MB in 1.2MB established
295     //    110 reserved
296     //    111 all other formats/drives
297
298     switch (get_drive_type(drive)) {
299     case 1:
300         // 360K 5.25" drive
301         config_data = 0x00; // 0000 0000
302         media_state = 0x25; // 0010 0101
303         rv = 1;
304         break;
305     case 2:
306         // 1.2 MB 5.25" drive
307         config_data = 0x00; // 0000 0000
308         media_state = 0x25; // 0010 0101   // need double stepping??? (bit 5)
309         rv = 1;
310         break;
311     case 3:
312         // 720K 3.5" drive
313         config_data = 0x00; // 0000 0000 ???
314         media_state = 0x17; // 0001 0111
315         rv = 1;
316         break;
317     case 4:
318         // 1.44 MB 3.5" drive
319         config_data = 0x00; // 0000 0000
320         media_state = 0x17; // 0001 0111
321         rv = 1;
322         break;
323     case 5:
324         // 2.88 MB 3.5" drive
325         config_data = 0xCC; // 1100 1100
326         media_state = 0xD7; // 1101 0111
327         rv = 1;
328         break;
329     //
330     // Extended floppy size uses special cmos setting
331     case 6:
332         // 160k 5.25" drive
333         config_data = 0x00; // 0000 0000
334         media_state = 0x27; // 0010 0111
335         rv = 1;
336         break;
337     case 7:
338         // 180k 5.25" drive
339         config_data = 0x00; // 0000 0000
340         media_state = 0x27; // 0010 0111
341         rv = 1;
342         break;
343     case 8:
344         // 320k 5.25" drive
345         config_data = 0x00; // 0000 0000
346         media_state = 0x27; // 0010 0111
347         rv = 1;
348         break;
349     default:
350         // not recognized
351         config_data = 0x00; // 0000 0000
352         media_state = 0x00; // 0000 0000
353         rv = 0;
354     }
355
356     SET_BDA(floppy_last_data_rate, config_data);
357     SET_BDA(floppy_media_state[drive], media_state);
358     return rv;
359 }
360
361 #define floppy_ret(regs, code) \
362     __floppy_ret(__func__, (regs), (code))
363
364 void
365 __floppy_ret(const char *fname, struct bregs *regs, u8 code)
366 {
367     SET_BDA(floppy_last_status, code);
368     if (code)
369         __set_code_fail(fname, regs, code);
370     else
371         set_code_success(regs);
372 }
373
374 static inline void
375 floppy_fail(struct bregs *regs, u8 code)
376 {
377     floppy_ret(regs, code);
378     regs->al = 0; // no sectors read
379 }
380
381 static u16
382 check_drive(struct bregs *regs, u8 drive)
383 {
384     // see if drive exists
385     if (drive > 1 || !get_drive_type(drive)) {
386         // XXX - return type doesn't match
387         floppy_fail(regs, DISK_RET_ETIMEOUT);
388         return 1;
389     }
390
391     // see if media in drive, and type is known
392     if (floppy_media_known(drive) == 0 && floppy_media_sense(drive) == 0) {
393         floppy_fail(regs, DISK_RET_EMEDIA);
394         return 1;
395     }
396     return 0;
397 }
398
399 // diskette controller reset
400 static void
401 floppy_1300(struct bregs *regs, u8 drive)
402 {
403     if (drive > 1) {
404         floppy_ret(regs, DISK_RET_EPARAM);
405         return;
406     }
407     if (!get_drive_type(drive)) {
408         floppy_ret(regs, DISK_RET_ETIMEOUT);
409         return;
410     }
411     set_diskette_current_cyl(drive, 0); // current cylinder
412     floppy_ret(regs, DISK_RET_SUCCESS);
413 }
414
415 // Read Diskette Status
416 static void
417 floppy_1301(struct bregs *regs, u8 drive)
418 {
419     u8 v = GET_BDA(floppy_last_status);
420     regs->ah = v;
421     set_cf(regs, v);
422 }
423
424 // Read Diskette Sectors
425 static void
426 floppy_1302(struct bregs *regs, u8 drive)
427 {
428     if (check_drive(regs, drive))
429         return;
430
431     u8 num_sectors = regs->al;
432     u8 track       = regs->ch;
433     u8 sector      = regs->cl;
434     u8 head        = regs->dh;
435
436     if (head > 1 || sector == 0 || num_sectors == 0
437         || track > 79 || num_sectors > 72) {
438         floppy_fail(regs, DISK_RET_EPARAM);
439         return;
440     }
441
442     // send read-normal-data command (9 bytes) to controller
443     u8 data[12];
444     data[0] = 0xe6; // e6: read normal data
445     data[1] = (head << 2) | drive; // HD DR1 DR2
446     data[2] = track;
447     data[3] = head;
448     data[4] = sector;
449     data[5] = 2; // 512 byte sector size
450     data[6] = sector + num_sectors - 1; // last sector to read on track
451     data[7] = 0; // Gap length
452     data[8] = 0xff; // Gap length
453
454     u16 ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
455     if (ret) {
456         floppy_fail(regs, ret);
457         return;
458     }
459
460     if (data[0] & 0xc0) {
461         floppy_fail(regs, DISK_RET_ECONTROLLER);
462         return;
463     }
464
465     // ??? should track be new val from return_status[3] ?
466     set_diskette_current_cyl(drive, track);
467     // AL = number of sectors read (same value as passed)
468     floppy_ret(regs, DISK_RET_SUCCESS);
469 }
470
471 // Write Diskette Sectors
472 static void
473 floppy_1303(struct bregs *regs, u8 drive)
474 {
475     if (check_drive(regs, drive))
476         return;
477
478     u8 num_sectors = regs->al;
479     u8 track       = regs->ch;
480     u8 sector      = regs->cl;
481     u8 head        = regs->dh;
482
483     if (head > 1 || sector == 0 || num_sectors == 0
484         || track > 79 || num_sectors > 72) {
485         floppy_fail(regs, DISK_RET_EPARAM);
486         return;
487     }
488
489     // send write-normal-data command (9 bytes) to controller
490     u8 data[12];
491     data[0] = 0xc5; // c5: write normal data
492     data[1] = (head << 2) | drive; // HD DR1 DR2
493     data[2] = track;
494     data[3] = head;
495     data[4] = sector;
496     data[5] = 2; // 512 byte sector size
497     data[6] = sector + num_sectors - 1; // last sector to write on track
498     data[7] = 0; // Gap length
499     data[8] = 0xff; // Gap length
500
501     u8 ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
502     if (ret) {
503         floppy_fail(regs, ret);
504         return;
505     }
506
507     if (data[0] & 0xc0) {
508         if (data[1] & 0x02) {
509             set_fail(regs);
510             regs->ax = 0x0300;
511             return;
512         }
513         BX_PANIC("int13_diskette_function: read error\n");
514     }
515
516     // ??? should track be new val from return_status[3] ?
517     set_diskette_current_cyl(drive, track);
518     // AL = number of sectors read (same value as passed)
519     floppy_ret(regs, DISK_RET_SUCCESS);
520 }
521
522 // Verify Diskette Sectors
523 static void
524 floppy_1304(struct bregs *regs, u8 drive)
525 {
526     if (check_drive(regs, drive))
527         return;
528
529     u8 num_sectors = regs->al;
530     u8 track       = regs->ch;
531     u8 sector      = regs->cl;
532     u8 head        = regs->dh;
533
534     if (head > 1 || sector == 0 || num_sectors == 0
535         || track > 79 || num_sectors > 72) {
536         floppy_fail(regs, DISK_RET_EPARAM);
537         return;
538     }
539
540     // ??? should track be new val from return_status[3] ?
541     set_diskette_current_cyl(drive, track);
542     // AL = number of sectors verified (same value as passed)
543     floppy_ret(regs, DISK_RET_SUCCESS);
544 }
545
546 // format diskette track
547 static void
548 floppy_1305(struct bregs *regs, u8 drive)
549 {
550     DEBUGF("floppy f05\n");
551
552     if (check_drive(regs, drive))
553         return;
554
555     u8 num_sectors = regs->al;
556     u8 head        = regs->dh;
557
558     if (head > 1 || num_sectors == 0 || num_sectors > 18) {
559         floppy_fail(regs, DISK_RET_EPARAM);
560         return;
561     }
562
563     // send format-track command (6 bytes) to controller
564     u8 data[12];
565     data[0] = 0x4d; // 4d: format track
566     data[1] = (head << 2) | drive; // HD DR1 DR2
567     data[2] = 2; // 512 byte sector size
568     data[3] = num_sectors; // number of sectors per track
569     data[4] = 0; // Gap length
570     data[5] = 0xf6; // Fill byte
571
572     u8 ret = floppy_cmd(regs, (num_sectors * 4) - 1, data, 6);
573     if (ret) {
574         floppy_fail(regs, ret);
575         return;
576     }
577
578     if (data[0] & 0xc0) {
579         if (data[1] & 0x02) {
580             set_fail(regs);
581             regs->ax = 0x0300;
582             return;
583         }
584         BX_PANIC("int13_diskette_function: read error\n");
585     }
586
587     set_diskette_current_cyl(drive, 0);
588     floppy_ret(regs, 0);
589 }
590
591 // read diskette drive parameters
592 static void
593 floppy_1308(struct bregs *regs, u8 drive)
594 {
595     DEBUGF("floppy f08\n");
596
597     u8 drive_type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
598     u8 num_floppies = 0;
599     if (drive_type & 0xf0)
600         num_floppies++;
601     if (drive_type & 0x0f)
602         num_floppies++;
603
604     if (drive > 1) {
605         regs->ax = 0;
606         regs->bx = 0;
607         regs->cx = 0;
608         regs->dx = 0;
609         regs->es = 0;
610         regs->di = 0;
611         regs->dl = num_floppies;
612         set_success(regs);
613         return;
614     }
615
616     if (drive == 0)
617         drive_type >>= 4;
618     else
619         drive_type &= 0x0f;
620
621     regs->bh = 0;
622     regs->bl = drive_type;
623     regs->ah = 0;
624     regs->al = 0;
625     regs->dl = num_floppies;
626
627     switch (drive_type) {
628     case 0: // none
629         regs->cx = 0;
630         regs->dh = 0; // max head #
631         break;
632
633     case 1: // 360KB, 5.25"
634         regs->cx = 0x2709; // 40 tracks, 9 sectors
635         regs->dh = 1; // max head #
636         break;
637
638     case 2: // 1.2MB, 5.25"
639         regs->cx = 0x4f0f; // 80 tracks, 15 sectors
640         regs->dh = 1; // max head #
641         break;
642
643     case 3: // 720KB, 3.5"
644         regs->cx = 0x4f09; // 80 tracks, 9 sectors
645         regs->dh = 1; // max head #
646         break;
647
648     case 4: // 1.44MB, 3.5"
649         regs->cx = 0x4f12; // 80 tracks, 18 sectors
650         regs->dh = 1; // max head #
651         break;
652
653     case 5: // 2.88MB, 3.5"
654         regs->cx = 0x4f24; // 80 tracks, 36 sectors
655         regs->dh = 1; // max head #
656         break;
657
658     case 6: // 160k, 5.25"
659         regs->cx = 0x2708; // 40 tracks, 8 sectors
660         regs->dh = 0; // max head #
661         break;
662
663     case 7: // 180k, 5.25"
664         regs->cx = 0x2709; // 40 tracks, 9 sectors
665         regs->dh = 0; // max head #
666         break;
667
668     case 8: // 320k, 5.25"
669         regs->cx = 0x2708; // 40 tracks, 8 sectors
670         regs->dh = 1; // max head #
671         break;
672
673     default: // ?
674         BX_PANIC("floppy: int13: bad floppy type\n");
675     }
676
677     /* set es & di to point to 11 byte diskette param table in ROM */
678     regs->es = SEG_BIOS;
679     regs->di = (u32)&diskette_param_table2;
680     /* disk status not changed upon success */
681 }
682
683 // read diskette drive type
684 static void
685 floppy_1315(struct bregs *regs, u8 drive)
686 {
687     DEBUGF("floppy f15\n");
688     if (drive > 1) {
689         set_fail(regs);
690         regs->ah = 0; // only 2 drives supported
691         // set_diskette_ret_status here ???
692         return;
693     }
694     u8 drive_type = get_drive_type(drive);
695
696     regs->ah = (drive_type != 0);
697     set_success(regs);
698 }
699
700 // get diskette change line status
701 static void
702 floppy_1316(struct bregs *regs, u8 drive)
703 {
704     DEBUGF("floppy f16\n");
705     if (drive > 1) {
706         floppy_ret(regs, DISK_RET_EPARAM);
707         return;
708     }
709     floppy_ret(regs, DISK_RET_ECHANGED);
710 }
711
712 static void
713 floppy_13XX(struct bregs *regs, u8 drive)
714 {
715     floppy_ret(regs, DISK_RET_EPARAM);
716 }
717
718 void
719 floppy_13(struct bregs *regs, u8 drive)
720 {
721     if (! CONFIG_FLOPPY_SUPPORT) {
722         // Minimal stubs
723         switch (regs->ah) {
724         case 0x01: floppy_1301(regs, drive); break;
725         default:   floppy_13XX(regs, drive); break;
726         }
727         return;
728     }
729     switch (regs->ah) {
730     case 0x00: floppy_1300(regs, drive); break;
731     case 0x01: floppy_1301(regs, drive); break;
732     case 0x02: floppy_1302(regs, drive); break;
733     case 0x03: floppy_1303(regs, drive); break;
734     case 0x04: floppy_1304(regs, drive); break;
735     case 0x05: floppy_1305(regs, drive); break;
736     case 0x08: floppy_1308(regs, drive); break;
737     case 0x15: floppy_1315(regs, drive); break;
738     case 0x16: floppy_1316(regs, drive); break;
739     default:   floppy_13XX(regs, drive); break;
740     }
741 }
742
743 // INT 0Eh Diskette Hardware ISR Entry Point
744 void VISIBLE16
745 handle_0e()
746 {
747     debug_isr(DEBUG_ISR_0e);
748     if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
749         outb(0x08, PORT_FD_DATA); // sense interrupt status
750         while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
751             ;
752         do {
753             inb(PORT_FD_DATA);
754         } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
755     }
756     eoi_master_pic();
757     // diskette interrupt has occurred
758     SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
759 }
760
761 // Called from int08 handler.
762 void
763 floppy_tick()
764 {
765     // time to turn off drive(s)?
766     u8 fcount = GET_BDA(floppy_motor_counter);
767     if (fcount) {
768         fcount--;
769         SET_BDA(floppy_motor_counter, fcount);
770         if (fcount == 0)
771             // turn motor(s) off
772             outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
773     }
774 }