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