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