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