Minor - cleanup return code of floppy_media_sense.
[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     int rv = 0;
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         break;
325     case 2:
326         // 1.2 MB 5.25" drive
327         config_data = 0x00; // 0000 0000
328         media_state = 0x25; // 0010 0101   // need double stepping??? (bit 5)
329         break;
330     case 3:
331         // 720K 3.5" drive
332         config_data = 0x00; // 0000 0000 ???
333         media_state = 0x17; // 0001 0111
334         break;
335     case 4:
336         // 1.44 MB 3.5" drive
337         config_data = 0x00; // 0000 0000
338         media_state = 0x17; // 0001 0111
339         break;
340     case 5:
341         // 2.88 MB 3.5" drive
342         config_data = 0xCC; // 1100 1100
343         media_state = 0xD7; // 1101 0111
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         break;
352     case 7:
353         // 180k 5.25" drive
354         config_data = 0x00; // 0000 0000
355         media_state = 0x27; // 0010 0111
356         break;
357     case 8:
358         // 320k 5.25" drive
359         config_data = 0x00; // 0000 0000
360         media_state = 0x27; // 0010 0111
361         break;
362     default:
363         // not recognized
364         config_data = 0x00; // 0000 0000
365         media_state = 0x00; // 0000 0000
366         rv = -1;
367     }
368
369     SET_BDA(floppy_last_data_rate, config_data);
370     SET_BDA(floppy_media_state[drive], media_state);
371     return rv;
372 }
373
374 static int
375 check_drive(struct bregs *regs, u8 drive)
376 {
377     // see if drive exists
378     if (drive > 1 || !GET_GLOBAL(FloppyTypes[drive])) {
379         floppy_ret(regs, DISK_RET_ETIMEOUT);
380         return -1;
381     }
382
383     if ((GET_BDA(floppy_recalibration_status) & (1<<drive))
384         && (GET_BDA(floppy_media_state[drive]) & FMS_MEDIA_DRIVE_ESTABLISHED))
385         // Media is known.
386         return 0;
387
388     // Recalibrate drive.
389     floppy_drive_recal(drive);
390
391     // Sense media.
392     int ret = floppy_media_sense(drive);
393     if (ret) {
394         floppy_ret(regs, DISK_RET_EMEDIA);
395         return -1;
396     }
397     return 0;
398 }
399
400
401 /****************************************************************
402  * Floppy int13 handlers
403  ****************************************************************/
404
405 // diskette controller reset
406 static void
407 floppy_1300(struct bregs *regs, u8 drive)
408 {
409     if (drive > 1) {
410         floppy_ret(regs, DISK_RET_EPARAM);
411         return;
412     }
413     if (!GET_GLOBAL(FloppyTypes[drive])) {
414         floppy_ret(regs, DISK_RET_ETIMEOUT);
415         return;
416     }
417     set_diskette_current_cyl(drive, 0); // current cylinder
418     floppy_ret(regs, DISK_RET_SUCCESS);
419 }
420
421 // Read Diskette Status
422 static void
423 floppy_1301(struct bregs *regs, u8 drive)
424 {
425     u8 v = GET_BDA(floppy_last_status);
426     regs->ah = v;
427     set_cf(regs, v);
428 }
429
430 // Read Diskette Sectors
431 static void
432 floppy_1302(struct bregs *regs, u8 drive)
433 {
434     if (check_drive(regs, drive))
435         goto fail;
436
437     u8 num_sectors = regs->al;
438     u8 track       = regs->ch;
439     u8 sector      = regs->cl;
440     u8 head        = regs->dh;
441
442     if (head > 1 || sector == 0 || num_sectors == 0
443         || track > 79 || num_sectors > 72) {
444         floppy_ret(regs, DISK_RET_EPARAM);
445         goto fail;
446     }
447
448     // send read-normal-data command (9 bytes) to controller
449     u8 data[12];
450     data[0] = 0xe6; // e6: read normal data
451     data[1] = (head << 2) | drive; // HD DR1 DR2
452     data[2] = track;
453     data[3] = head;
454     data[4] = sector;
455     data[5] = 2; // 512 byte sector size
456     data[6] = sector + num_sectors - 1; // last sector to read on track
457     data[7] = 0; // Gap length
458     data[8] = 0xff; // Gap length
459
460     int ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
461     if (ret)
462         goto fail;
463
464     if (data[0] & 0xc0) {
465         floppy_ret(regs, DISK_RET_ECONTROLLER);
466         goto fail;
467     }
468
469     // ??? should track be new val from return_status[3] ?
470     set_diskette_current_cyl(drive, track);
471     // AL = number of sectors read (same value as passed)
472     floppy_ret(regs, DISK_RET_SUCCESS);
473     return;
474 fail:
475     regs->al = 0; // no sectors read
476 }
477
478 // Write Diskette Sectors
479 static void
480 floppy_1303(struct bregs *regs, u8 drive)
481 {
482     if (check_drive(regs, drive))
483         goto fail;
484
485     u8 num_sectors = regs->al;
486     u8 track       = regs->ch;
487     u8 sector      = regs->cl;
488     u8 head        = regs->dh;
489
490     if (head > 1 || sector == 0 || num_sectors == 0
491         || track > 79 || num_sectors > 72) {
492         floppy_ret(regs, DISK_RET_EPARAM);
493         goto fail;
494     }
495
496     // send write-normal-data command (9 bytes) to controller
497     u8 data[12];
498     data[0] = 0xc5; // c5: write normal data
499     data[1] = (head << 2) | drive; // HD DR1 DR2
500     data[2] = track;
501     data[3] = head;
502     data[4] = sector;
503     data[5] = 2; // 512 byte sector size
504     data[6] = sector + num_sectors - 1; // last sector to write on track
505     data[7] = 0; // Gap length
506     data[8] = 0xff; // Gap length
507
508     int ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
509     if (ret)
510         goto fail;
511
512     if (data[0] & 0xc0) {
513         if (data[1] & 0x02)
514             floppy_ret(regs, DISK_RET_EWRITEPROTECT);
515         else
516             floppy_ret(regs, DISK_RET_ECONTROLLER);
517         goto fail;
518     }
519
520     // ??? should track be new val from return_status[3] ?
521     set_diskette_current_cyl(drive, track);
522     // AL = number of sectors read (same value as passed)
523     floppy_ret(regs, DISK_RET_SUCCESS);
524     return;
525 fail:
526     regs->al = 0; // no sectors read
527 }
528
529 // Verify Diskette Sectors
530 static void
531 floppy_1304(struct bregs *regs, u8 drive)
532 {
533     if (check_drive(regs, drive))
534         goto fail;
535
536     u8 num_sectors = regs->al;
537     u8 track       = regs->ch;
538     u8 sector      = regs->cl;
539     u8 head        = regs->dh;
540
541     if (head > 1 || sector == 0 || num_sectors == 0
542         || track > 79 || num_sectors > 72) {
543         floppy_ret(regs, DISK_RET_EPARAM);
544         goto fail;
545     }
546
547     // ??? should track be new val from return_status[3] ?
548     set_diskette_current_cyl(drive, track);
549     // AL = number of sectors verified (same value as passed)
550     floppy_ret(regs, DISK_RET_SUCCESS);
551     return;
552 fail:
553     regs->al = 0; // no sectors read
554 }
555
556 // format diskette track
557 static void
558 floppy_1305(struct bregs *regs, u8 drive)
559 {
560     dprintf(3, "floppy f05\n");
561
562     if (check_drive(regs, drive))
563         return;
564
565     u8 num_sectors = regs->al;
566     u8 head        = regs->dh;
567
568     if (head > 1 || num_sectors == 0 || num_sectors > 18) {
569         floppy_ret(regs, DISK_RET_EPARAM);
570         return;
571     }
572
573     // send format-track command (6 bytes) to controller
574     u8 data[12];
575     data[0] = 0x4d; // 4d: format track
576     data[1] = (head << 2) | drive; // HD DR1 DR2
577     data[2] = 2; // 512 byte sector size
578     data[3] = num_sectors; // number of sectors per track
579     data[4] = 0; // Gap length
580     data[5] = 0xf6; // Fill byte
581
582     int ret = floppy_cmd(regs, (num_sectors * 4) - 1, data, 6);
583     if (ret)
584         return;
585
586     if (data[0] & 0xc0) {
587         if (data[1] & 0x02)
588             floppy_ret(regs, DISK_RET_EWRITEPROTECT);
589         else
590             floppy_ret(regs, DISK_RET_ECONTROLLER);
591         return;
592     }
593
594     set_diskette_current_cyl(drive, 0);
595     floppy_ret(regs, 0);
596 }
597
598 // read diskette drive parameters
599 static void
600 floppy_1308(struct bregs *regs, u8 drive)
601 {
602     dprintf(3, "floppy f08\n");
603
604     regs->ax = 0;
605     regs->dx = GET_GLOBAL(FloppyCount);
606     if (drive > 1) {
607         regs->bx = 0;
608         regs->cx = 0;
609         regs->es = 0;
610         regs->di = 0;
611         set_fail(regs);
612         return;
613     }
614
615     u8 drive_type = GET_GLOBAL(FloppyTypes[drive]);
616     regs->bx = drive_type;
617
618     switch (drive_type) {
619     default: // ?
620         dprintf(1, "floppy: int13: bad floppy type\n");
621         // NO BREAK
622     case 0: // none
623         regs->cx = 0;
624         regs->dh = 0; // max head #
625         break;
626     case 1: // 360KB, 5.25"
627         regs->cx = 0x2709; // 40 tracks, 9 sectors
628         regs->dh = 1; // max head #
629         break;
630     case 2: // 1.2MB, 5.25"
631         regs->cx = 0x4f0f; // 80 tracks, 15 sectors
632         regs->dh = 1; // max head #
633         break;
634     case 3: // 720KB, 3.5"
635         regs->cx = 0x4f09; // 80 tracks, 9 sectors
636         regs->dh = 1; // max head #
637         break;
638     case 4: // 1.44MB, 3.5"
639         regs->cx = 0x4f12; // 80 tracks, 18 sectors
640         regs->dh = 1; // max head #
641         break;
642     case 5: // 2.88MB, 3.5"
643         regs->cx = 0x4f24; // 80 tracks, 36 sectors
644         regs->dh = 1; // max head #
645         break;
646     case 6: // 160k, 5.25"
647         regs->cx = 0x2708; // 40 tracks, 8 sectors
648         regs->dh = 0; // max head #
649         break;
650     case 7: // 180k, 5.25"
651         regs->cx = 0x2709; // 40 tracks, 9 sectors
652         regs->dh = 0; // max head #
653         break;
654     case 8: // 320k, 5.25"
655         regs->cx = 0x2708; // 40 tracks, 8 sectors
656         regs->dh = 1; // max head #
657         break;
658     }
659
660     /* set es & di to point to 11 byte diskette param table in ROM */
661     regs->es = SEG_BIOS;
662     regs->di = (u32)&diskette_param_table2;
663     /* disk status not changed upon success */
664     set_success(regs);
665 }
666
667 // read diskette drive type
668 static void
669 floppy_1315(struct bregs *regs, u8 drive)
670 {
671     dprintf(6, "floppy f15\n");
672     if (drive > 1) {
673         set_fail(regs);
674         regs->ah = 0; // only 2 drives supported
675         // set_diskette_ret_status here ???
676         return;
677     }
678     u8 drive_type = GET_GLOBAL(FloppyTypes[drive]);
679
680     regs->ah = (drive_type != 0);
681     set_success(regs);
682 }
683
684 // get diskette change line status
685 static void
686 floppy_1316(struct bregs *regs, u8 drive)
687 {
688     if (drive > 1) {
689         floppy_ret(regs, DISK_RET_EPARAM);
690         return;
691     }
692     floppy_ret(regs, DISK_RET_ECHANGED);
693 }
694
695 static void
696 floppy_13XX(struct bregs *regs, u8 drive)
697 {
698     floppy_ret(regs, DISK_RET_EPARAM);
699 }
700
701 void
702 floppy_13(struct bregs *regs, u8 drive)
703 {
704     if (! CONFIG_FLOPPY_SUPPORT) {
705         // Minimal stubs
706         switch (regs->ah) {
707         case 0x01: floppy_1301(regs, drive); break;
708         default:   floppy_13XX(regs, drive); break;
709         }
710         return;
711     }
712     switch (regs->ah) {
713     case 0x00: floppy_1300(regs, drive); break;
714     case 0x01: floppy_1301(regs, drive); break;
715     case 0x02: floppy_1302(regs, drive); break;
716     case 0x03: floppy_1303(regs, drive); break;
717     case 0x04: floppy_1304(regs, drive); break;
718     case 0x05: floppy_1305(regs, drive); break;
719     case 0x08: floppy_1308(regs, drive); break;
720     case 0x15: floppy_1315(regs, drive); break;
721     case 0x16: floppy_1316(regs, drive); break;
722     default:   floppy_13XX(regs, drive); break;
723     }
724 }
725
726
727 /****************************************************************
728  * HW irqs
729  ****************************************************************/
730
731 // INT 0Eh Diskette Hardware ISR Entry Point
732 void VISIBLE16
733 handle_0e()
734 {
735     debug_isr(DEBUG_ISR_0e);
736     if (! CONFIG_FLOPPY_SUPPORT)
737         goto done;
738
739     if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
740         outb(0x08, PORT_FD_DATA); // sense interrupt status
741         while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
742             ;
743         do {
744             inb(PORT_FD_DATA);
745         } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
746     }
747     // diskette interrupt has occurred
748     SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
749
750 done:
751     eoi_pic1();
752 }
753
754 // Called from int08 handler.
755 void
756 floppy_tick()
757 {
758     if (! CONFIG_FLOPPY_SUPPORT)
759         return;
760
761     // time to turn off drive(s)?
762     u8 fcount = GET_BDA(floppy_motor_counter);
763     if (fcount) {
764         fcount--;
765         SET_BDA(floppy_motor_counter, fcount);
766         if (fcount == 0)
767             // turn motor(s) off
768             outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
769     }
770 }