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