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