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