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