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