Improve control of debug messages.
[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         floppy_fail(regs, DISK_RET_EPARAM);
438         return;
439     }
440
441     // send read-normal-data command (9 bytes) to controller
442     u8 data[12];
443     data[0] = 0xe6; // e6: read normal data
444     data[1] = (head << 2) | drive; // HD DR1 DR2
445     data[2] = track;
446     data[3] = head;
447     data[4] = sector;
448     data[5] = 2; // 512 byte sector size
449     data[6] = sector + num_sectors - 1; // last sector to read on track
450     data[7] = 0; // Gap length
451     data[8] = 0xff; // Gap length
452
453     u16 ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
454     if (ret) {
455         floppy_fail(regs, ret);
456         return;
457     }
458
459     if (data[0] & 0xc0) {
460         floppy_fail(regs, DISK_RET_ECONTROLLER);
461         return;
462     }
463
464     // ??? should track be new val from return_status[3] ?
465     set_diskette_current_cyl(drive, track);
466     // AL = number of sectors read (same value as passed)
467     floppy_ret(regs, DISK_RET_SUCCESS);
468 }
469
470 // Write Diskette Sectors
471 static void
472 floppy_1303(struct bregs *regs, u8 drive)
473 {
474     if (check_drive(regs, drive))
475         return;
476
477     u8 num_sectors = regs->al;
478     u8 track       = regs->ch;
479     u8 sector      = regs->cl;
480     u8 head        = regs->dh;
481
482     if (head > 1 || sector == 0 || num_sectors == 0
483         || track > 79 || num_sectors > 72) {
484         floppy_fail(regs, DISK_RET_EPARAM);
485         return;
486     }
487
488     // send write-normal-data command (9 bytes) to controller
489     u8 data[12];
490     data[0] = 0xc5; // c5: write normal data
491     data[1] = (head << 2) | drive; // HD DR1 DR2
492     data[2] = track;
493     data[3] = head;
494     data[4] = sector;
495     data[5] = 2; // 512 byte sector size
496     data[6] = sector + num_sectors - 1; // last sector to write on track
497     data[7] = 0; // Gap length
498     data[8] = 0xff; // Gap length
499
500     u8 ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
501     if (ret) {
502         floppy_fail(regs, ret);
503         return;
504     }
505
506     if (data[0] & 0xc0) {
507         if (data[1] & 0x02) {
508             set_fail(regs);
509             regs->ax = 0x0300;
510             return;
511         }
512         BX_PANIC("int13_diskette_function: read error\n");
513     }
514
515     // ??? should track be new val from return_status[3] ?
516     set_diskette_current_cyl(drive, track);
517     // AL = number of sectors read (same value as passed)
518     floppy_ret(regs, DISK_RET_SUCCESS);
519 }
520
521 // Verify Diskette Sectors
522 static void
523 floppy_1304(struct bregs *regs, u8 drive)
524 {
525     if (check_drive(regs, drive))
526         return;
527
528     u8 num_sectors = regs->al;
529     u8 track       = regs->ch;
530     u8 sector      = regs->cl;
531     u8 head        = regs->dh;
532
533     if (head > 1 || sector == 0 || num_sectors == 0
534         || track > 79 || num_sectors > 72) {
535         floppy_fail(regs, DISK_RET_EPARAM);
536         return;
537     }
538
539     // ??? should track be new val from return_status[3] ?
540     set_diskette_current_cyl(drive, track);
541     // AL = number of sectors verified (same value as passed)
542     floppy_ret(regs, DISK_RET_SUCCESS);
543 }
544
545 // format diskette track
546 static void
547 floppy_1305(struct bregs *regs, u8 drive)
548 {
549     DEBUGF("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_fail(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     u8 ret = floppy_cmd(regs, (num_sectors * 4) - 1, data, 6);
572     if (ret) {
573         floppy_fail(regs, ret);
574         return;
575     }
576
577     if (data[0] & 0xc0) {
578         if (data[1] & 0x02) {
579             set_fail(regs);
580             regs->ax = 0x0300;
581             return;
582         }
583         BX_PANIC("int13_diskette_function: read error\n");
584     }
585
586     set_diskette_current_cyl(drive, 0);
587     floppy_ret(regs, 0);
588 }
589
590 // read diskette drive parameters
591 static void
592 floppy_1308(struct bregs *regs, u8 drive)
593 {
594     DEBUGF("floppy f08\n");
595
596     u8 drive_type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
597     u8 num_floppies = 0;
598     if (drive_type & 0xf0)
599         num_floppies++;
600     if (drive_type & 0x0f)
601         num_floppies++;
602
603     if (drive > 1) {
604         regs->ax = 0;
605         regs->bx = 0;
606         regs->cx = 0;
607         regs->dx = 0;
608         regs->es = 0;
609         regs->di = 0;
610         regs->dl = num_floppies;
611         set_success(regs);
612         return;
613     }
614
615     if (drive == 0)
616         drive_type >>= 4;
617     else
618         drive_type &= 0x0f;
619
620     regs->bh = 0;
621     regs->bl = drive_type;
622     regs->ah = 0;
623     regs->al = 0;
624     regs->dl = num_floppies;
625
626     switch (drive_type) {
627     case 0: // none
628         regs->cx = 0;
629         regs->dh = 0; // max head #
630         break;
631
632     case 1: // 360KB, 5.25"
633         regs->cx = 0x2709; // 40 tracks, 9 sectors
634         regs->dh = 1; // max head #
635         break;
636
637     case 2: // 1.2MB, 5.25"
638         regs->cx = 0x4f0f; // 80 tracks, 15 sectors
639         regs->dh = 1; // max head #
640         break;
641
642     case 3: // 720KB, 3.5"
643         regs->cx = 0x4f09; // 80 tracks, 9 sectors
644         regs->dh = 1; // max head #
645         break;
646
647     case 4: // 1.44MB, 3.5"
648         regs->cx = 0x4f12; // 80 tracks, 18 sectors
649         regs->dh = 1; // max head #
650         break;
651
652     case 5: // 2.88MB, 3.5"
653         regs->cx = 0x4f24; // 80 tracks, 36 sectors
654         regs->dh = 1; // max head #
655         break;
656
657     case 6: // 160k, 5.25"
658         regs->cx = 0x2708; // 40 tracks, 8 sectors
659         regs->dh = 0; // max head #
660         break;
661
662     case 7: // 180k, 5.25"
663         regs->cx = 0x2709; // 40 tracks, 9 sectors
664         regs->dh = 0; // max head #
665         break;
666
667     case 8: // 320k, 5.25"
668         regs->cx = 0x2708; // 40 tracks, 8 sectors
669         regs->dh = 1; // max head #
670         break;
671
672     default: // ?
673         BX_PANIC("floppy: int13: bad floppy type\n");
674     }
675
676     /* set es & di to point to 11 byte diskette param table in ROM */
677     regs->es = SEG_BIOS;
678     regs->di = (u32)&diskette_param_table2;
679     /* disk status not changed upon success */
680 }
681
682 // read diskette drive type
683 static void
684 floppy_1315(struct bregs *regs, u8 drive)
685 {
686     DEBUGF("floppy f15\n");
687     if (drive > 1) {
688         set_fail(regs);
689         regs->ah = 0; // only 2 drives supported
690         // set_diskette_ret_status here ???
691         return;
692     }
693     u8 drive_type = get_drive_type(drive);
694
695     regs->ah = (drive_type != 0);
696     set_success(regs);
697 }
698
699 // get diskette change line status
700 static void
701 floppy_1316(struct bregs *regs, u8 drive)
702 {
703     DEBUGF("floppy f16\n");
704     if (drive > 1) {
705         floppy_ret(regs, DISK_RET_EPARAM);
706         return;
707     }
708     floppy_ret(regs, DISK_RET_ECHANGED);
709 }
710
711 static void
712 floppy_13XX(struct bregs *regs, u8 drive)
713 {
714     floppy_ret(regs, DISK_RET_EPARAM);
715 }
716
717 void
718 floppy_13(struct bregs *regs, u8 drive)
719 {
720     if (! CONFIG_FLOPPY_SUPPORT) {
721         // Minimal stubs
722         switch (regs->ah) {
723         case 0x01: floppy_1301(regs, drive); break;
724         default:   floppy_13XX(regs, drive); break;
725         }
726         return;
727     }
728     switch (regs->ah) {
729     case 0x00: floppy_1300(regs, drive); break;
730     case 0x01: floppy_1301(regs, drive); break;
731     case 0x02: floppy_1302(regs, drive); break;
732     case 0x03: floppy_1303(regs, drive); break;
733     case 0x04: floppy_1304(regs, drive); break;
734     case 0x05: floppy_1305(regs, drive); break;
735     case 0x08: floppy_1308(regs, drive); break;
736     case 0x15: floppy_1315(regs, drive); break;
737     case 0x16: floppy_1316(regs, drive); break;
738     default:   floppy_13XX(regs, drive); break;
739     }
740 }
741
742 // INT 0Eh Diskette Hardware ISR Entry Point
743 void VISIBLE16
744 handle_0e()
745 {
746     //debug_isr();
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     eoi_master_pic();
756     // diskette interrupt has occurred
757     SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
758 }
759
760 // Called from int08 handler.
761 void
762 floppy_tick()
763 {
764     // time to turn off drive(s)?
765     u8 fcount = GET_BDA(floppy_motor_counter);
766     if (fcount) {
767         fcount--;
768         SET_BDA(floppy_motor_counter, fcount);
769         if (fcount == 0)
770             // turn motor(s) off
771             outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
772     }
773 }