Add more linker protections around variables accessed from 16bit mode.
[seabios.git] / src / floppy.c
1 // 16bit code to access floppy drives.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU GPLv3 license.
7
8 #include "types.h" // u8
9 #include "disk.h" // DISK_RET_SUCCESS
10 #include "config.h" // CONFIG_FLOPPY_SUPPORT
11 #include "biosvar.h" // SET_BDA
12 #include "util.h" // irq_disable
13 #include "cmos.h" // inb_cmos
14 #include "pic.h" // eoi_pic1
15 #include "bregs.h" // struct bregs
16
17 #define BX_FLOPPY_ON_CNT 37   /* 2 seconds */
18
19 // New diskette parameter table adding 3 parameters from IBM
20 // Since no provisions are made for multiple drive types, most
21 // values in this table are ignored.  I set parameters for 1.44M
22 // floppy here
23 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(__func__, __LINE__, (regs), (code))
193
194 void
195 __floppy_ret(const char *fname, int lineno, struct bregs *regs, u8 code)
196 {
197     SET_BDA(floppy_last_status, code);
198     if (code)
199         __set_code_fail(fname, lineno, regs, code);
200     else
201         set_code_success(regs);
202 }
203
204 static int
205 floppy_cmd(struct bregs *regs, u16 count, u8 *cmd, u8 cmdlen)
206 {
207     // es:bx = pointer to where to place information from diskette
208     u32 addr = (u32)MAKE_FARPTR(regs->es, regs->bx);
209
210     // check for 64K boundary overrun
211     u32 last_addr = addr + count;
212     if ((addr >> 16) != (last_addr >> 16)) {
213         floppy_ret(regs, DISK_RET_EBOUNDARY);
214         return -1;
215     }
216
217     u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
218     if (cmd[0] == 0xe6)
219         // read
220         mode_register = 0x46;
221
222     //DEBUGF("floppy dma c2\n");
223     outb(0x06, PORT_DMA1_MASK_REG);
224     outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
225     outb(addr, PORT_DMA_ADDR_2);
226     outb(addr>>8, PORT_DMA_ADDR_2);
227     outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
228     outb(count, PORT_DMA_CNT_2);
229     outb(count>>8, PORT_DMA_CNT_2);
230
231     // port 0b: DMA-1 Mode Register
232     // transfer type=write, channel 2
233     outb(mode_register, PORT_DMA1_MODE_REG);
234
235     // port 81: DMA-1 Page Register, channel 2
236     outb(addr>>16, PORT_DMA_PAGE_2);
237
238     outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
239
240     int ret = floppy_pio(cmd, cmdlen);
241     if (ret) {
242         floppy_ret(regs, DISK_RET_ETIMEOUT);
243         return -1;
244     }
245
246     // check port 3f4 for accessibility to status bytes
247     if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
248         BX_PANIC("int13_diskette: ctrl not ready\n");
249
250     // read 7 return status bytes from controller
251     u8 i;
252     for (i=0; i<7; i++) {
253         u8 v = inb(PORT_FD_DATA);
254         cmd[i] = v;
255         SET_BDA(floppy_return_status[i], v);
256     }
257
258     return 0;
259 }
260
261 static void
262 floppy_drive_recal(u8 drive)
263 {
264     // send Recalibrate command (2 bytes) to controller
265     u8 data[12];
266     data[0] = 0x07;  // 07: Recalibrate
267     data[1] = drive; // 0=drive0, 1=drive1
268     floppy_pio(data, 2);
269
270     SETBITS_BDA(floppy_recalibration_status, 1<<drive);
271     set_diskette_current_cyl(drive, 0);
272 }
273
274 static u16
275 floppy_media_sense(u8 drive)
276 {
277     u16 rv;
278     u8 config_data, media_state;
279
280     floppy_drive_recal(drive);
281
282     // for now cheat and get drive type from CMOS,
283     // assume media is same as drive type
284
285     // ** config_data **
286     // Bitfields for diskette media control:
287     // Bit(s)  Description (Table M0028)
288     //  7-6  last data rate set by controller
289     //        00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
290     //  5-4  last diskette drive step rate selected
291     //        00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
292     //  3-2  {data rate at start of operation}
293     //  1-0  reserved
294
295     // ** media_state **
296     // Bitfields for diskette drive media state:
297     // Bit(s)  Description (Table M0030)
298     //  7-6  data rate
299     //    00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
300     //  5  double stepping required (e.g. 360kB in 1.2MB)
301     //  4  media type established
302     //  3  drive capable of supporting 4MB media
303     //  2-0  on exit from BIOS, contains
304     //    000 trying 360kB in 360kB
305     //    001 trying 360kB in 1.2MB
306     //    010 trying 1.2MB in 1.2MB
307     //    011 360kB in 360kB established
308     //    100 360kB in 1.2MB established
309     //    101 1.2MB in 1.2MB established
310     //    110 reserved
311     //    111 all other formats/drives
312
313     switch (get_drive_type(drive)) {
314     case 1:
315         // 360K 5.25" drive
316         config_data = 0x00; // 0000 0000
317         media_state = 0x25; // 0010 0101
318         rv = 1;
319         break;
320     case 2:
321         // 1.2 MB 5.25" drive
322         config_data = 0x00; // 0000 0000
323         media_state = 0x25; // 0010 0101   // need double stepping??? (bit 5)
324         rv = 1;
325         break;
326     case 3:
327         // 720K 3.5" drive
328         config_data = 0x00; // 0000 0000 ???
329         media_state = 0x17; // 0001 0111
330         rv = 1;
331         break;
332     case 4:
333         // 1.44 MB 3.5" drive
334         config_data = 0x00; // 0000 0000
335         media_state = 0x17; // 0001 0111
336         rv = 1;
337         break;
338     case 5:
339         // 2.88 MB 3.5" drive
340         config_data = 0xCC; // 1100 1100
341         media_state = 0xD7; // 1101 0111
342         rv = 1;
343         break;
344     //
345     // Extended floppy size uses special cmos setting
346     case 6:
347         // 160k 5.25" drive
348         config_data = 0x00; // 0000 0000
349         media_state = 0x27; // 0010 0111
350         rv = 1;
351         break;
352     case 7:
353         // 180k 5.25" drive
354         config_data = 0x00; // 0000 0000
355         media_state = 0x27; // 0010 0111
356         rv = 1;
357         break;
358     case 8:
359         // 320k 5.25" drive
360         config_data = 0x00; // 0000 0000
361         media_state = 0x27; // 0010 0111
362         rv = 1;
363         break;
364     default:
365         // not recognized
366         config_data = 0x00; // 0000 0000
367         media_state = 0x00; // 0000 0000
368         rv = 0;
369     }
370
371     SET_BDA(floppy_last_data_rate, config_data);
372     SET_BDA(floppy_media_state[drive], media_state);
373     return rv;
374 }
375
376 static int
377 check_drive(struct bregs *regs, u8 drive)
378 {
379     // see if drive exists
380     if (drive > 1 || !get_drive_type(drive)) {
381         // XXX - return type doesn't match
382         floppy_ret(regs, DISK_RET_ETIMEOUT);
383         return -1;
384     }
385
386     // see if media in drive, and type is known
387     if (floppy_media_known(drive) == 0 && floppy_media_sense(drive) == 0) {
388         floppy_ret(regs, DISK_RET_EMEDIA);
389         return -1;
390     }
391     return 0;
392 }
393
394 // diskette controller reset
395 static void
396 floppy_1300(struct bregs *regs, u8 drive)
397 {
398     if (drive > 1) {
399         floppy_ret(regs, DISK_RET_EPARAM);
400         return;
401     }
402     if (!get_drive_type(drive)) {
403         floppy_ret(regs, DISK_RET_ETIMEOUT);
404         return;
405     }
406     set_diskette_current_cyl(drive, 0); // current cylinder
407     floppy_ret(regs, DISK_RET_SUCCESS);
408 }
409
410 // Read Diskette Status
411 static void
412 floppy_1301(struct bregs *regs, u8 drive)
413 {
414     u8 v = GET_BDA(floppy_last_status);
415     regs->ah = v;
416     set_cf(regs, v);
417 }
418
419 // Read Diskette Sectors
420 static void
421 floppy_1302(struct bregs *regs, u8 drive)
422 {
423     if (check_drive(regs, drive))
424         goto fail;
425
426     u8 num_sectors = regs->al;
427     u8 track       = regs->ch;
428     u8 sector      = regs->cl;
429     u8 head        = regs->dh;
430
431     if (head > 1 || sector == 0 || num_sectors == 0
432         || track > 79 || num_sectors > 72) {
433         floppy_ret(regs, DISK_RET_EPARAM);
434         goto fail;
435     }
436
437     // send read-normal-data command (9 bytes) to controller
438     u8 data[12];
439     data[0] = 0xe6; // e6: read normal data
440     data[1] = (head << 2) | drive; // HD DR1 DR2
441     data[2] = track;
442     data[3] = head;
443     data[4] = sector;
444     data[5] = 2; // 512 byte sector size
445     data[6] = sector + num_sectors - 1; // last sector to read on track
446     data[7] = 0; // Gap length
447     data[8] = 0xff; // Gap length
448
449     int ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
450     if (ret)
451         goto fail;
452
453     if (data[0] & 0xc0) {
454         floppy_ret(regs, DISK_RET_ECONTROLLER);
455         goto fail;
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     return;
463 fail:
464     regs->al = 0; // no sectors read
465 }
466
467 // Write Diskette Sectors
468 static void
469 floppy_1303(struct bregs *regs, u8 drive)
470 {
471     if (check_drive(regs, drive))
472         goto fail;
473
474     u8 num_sectors = regs->al;
475     u8 track       = regs->ch;
476     u8 sector      = regs->cl;
477     u8 head        = regs->dh;
478
479     if (head > 1 || sector == 0 || num_sectors == 0
480         || track > 79 || num_sectors > 72) {
481         floppy_ret(regs, DISK_RET_EPARAM);
482         goto fail;
483     }
484
485     // send write-normal-data command (9 bytes) to controller
486     u8 data[12];
487     data[0] = 0xc5; // c5: write normal data
488     data[1] = (head << 2) | drive; // HD DR1 DR2
489     data[2] = track;
490     data[3] = head;
491     data[4] = sector;
492     data[5] = 2; // 512 byte sector size
493     data[6] = sector + num_sectors - 1; // last sector to write on track
494     data[7] = 0; // Gap length
495     data[8] = 0xff; // Gap length
496
497     int ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
498     if (ret)
499         goto fail;
500
501     if (data[0] & 0xc0) {
502         if (data[1] & 0x02) {
503             floppy_ret(regs, DISK_RET_EWRITEPROTECT);
504             goto fail;
505         }
506         BX_PANIC("int13_diskette_function: read error\n");
507     }
508
509     // ??? should track be new val from return_status[3] ?
510     set_diskette_current_cyl(drive, track);
511     // AL = number of sectors read (same value as passed)
512     floppy_ret(regs, DISK_RET_SUCCESS);
513     return;
514 fail:
515     regs->al = 0; // no sectors read
516 }
517
518 // Verify Diskette Sectors
519 static void
520 floppy_1304(struct bregs *regs, u8 drive)
521 {
522     if (check_drive(regs, drive))
523         goto fail;
524
525     u8 num_sectors = regs->al;
526     u8 track       = regs->ch;
527     u8 sector      = regs->cl;
528     u8 head        = regs->dh;
529
530     if (head > 1 || sector == 0 || num_sectors == 0
531         || track > 79 || num_sectors > 72) {
532         floppy_ret(regs, DISK_RET_EPARAM);
533         goto fail;
534     }
535
536     // ??? should track be new val from return_status[3] ?
537     set_diskette_current_cyl(drive, track);
538     // AL = number of sectors verified (same value as passed)
539     floppy_ret(regs, DISK_RET_SUCCESS);
540     return;
541 fail:
542     regs->al = 0; // no sectors read
543 }
544
545 // format diskette track
546 static void
547 floppy_1305(struct bregs *regs, u8 drive)
548 {
549     dprintf(3, "floppy f05\n");
550
551     if (check_drive(regs, drive))
552         return;
553
554     u8 num_sectors = regs->al;
555     u8 head        = regs->dh;
556
557     if (head > 1 || num_sectors == 0 || num_sectors > 18) {
558         floppy_ret(regs, DISK_RET_EPARAM);
559         return;
560     }
561
562     // send format-track command (6 bytes) to controller
563     u8 data[12];
564     data[0] = 0x4d; // 4d: format track
565     data[1] = (head << 2) | drive; // HD DR1 DR2
566     data[2] = 2; // 512 byte sector size
567     data[3] = num_sectors; // number of sectors per track
568     data[4] = 0; // Gap length
569     data[5] = 0xf6; // Fill byte
570
571     int ret = floppy_cmd(regs, (num_sectors * 4) - 1, data, 6);
572     if (ret)
573         return;
574
575     if (data[0] & 0xc0) {
576         if (data[1] & 0x02) {
577             floppy_ret(regs, DISK_RET_EWRITEPROTECT);
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     dprintf(3, "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_fail(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     set_success(regs);
678 }
679
680 // read diskette drive type
681 static void
682 floppy_1315(struct bregs *regs, u8 drive)
683 {
684     dprintf(6, "floppy f15\n");
685     if (drive > 1) {
686         set_fail(regs);
687         regs->ah = 0; // only 2 drives supported
688         // set_diskette_ret_status here ???
689         return;
690     }
691     u8 drive_type = get_drive_type(drive);
692
693     regs->ah = (drive_type != 0);
694     set_success(regs);
695 }
696
697 // get diskette change line status
698 static void
699 floppy_1316(struct bregs *regs, u8 drive)
700 {
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     floppy_ret(regs, DISK_RET_EPARAM);
712 }
713
714 void
715 floppy_13(struct bregs *regs, u8 drive)
716 {
717     if (! CONFIG_FLOPPY_SUPPORT) {
718         // Minimal stubs
719         switch (regs->ah) {
720         case 0x01: floppy_1301(regs, drive); break;
721         default:   floppy_13XX(regs, drive); break;
722         }
723         return;
724     }
725     switch (regs->ah) {
726     case 0x00: floppy_1300(regs, drive); break;
727     case 0x01: floppy_1301(regs, drive); break;
728     case 0x02: floppy_1302(regs, drive); break;
729     case 0x03: floppy_1303(regs, drive); break;
730     case 0x04: floppy_1304(regs, drive); break;
731     case 0x05: floppy_1305(regs, drive); break;
732     case 0x08: floppy_1308(regs, drive); break;
733     case 0x15: floppy_1315(regs, drive); break;
734     case 0x16: floppy_1316(regs, drive); break;
735     default:   floppy_13XX(regs, drive); break;
736     }
737 }
738
739 // INT 0Eh Diskette Hardware ISR Entry Point
740 void VISIBLE16
741 handle_0e()
742 {
743     debug_isr(DEBUG_ISR_0e);
744     if (! CONFIG_FLOPPY_SUPPORT)
745         goto done;
746
747     if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
748         outb(0x08, PORT_FD_DATA); // sense interrupt status
749         while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
750             ;
751         do {
752             inb(PORT_FD_DATA);
753         } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
754     }
755     // diskette interrupt has occurred
756     SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
757
758 done:
759     eoi_pic1();
760 }
761
762 // Called from int08 handler.
763 void
764 floppy_tick()
765 {
766     if (! CONFIG_FLOPPY_SUPPORT)
767         return;
768
769     // time to turn off drive(s)?
770     u8 fcount = GET_BDA(floppy_motor_counter);
771     if (fcount) {
772         fcount--;
773         SET_BDA(floppy_motor_counter, fcount);
774         if (fcount == 0)
775             // turn motor(s) off
776             outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
777     }
778 }