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