Improve some debugging output.
[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 #include "pic.h" // unmask_pic1
15
16 #define BX_FLOPPY_ON_CNT 37   /* 2 seconds */
17
18 // New diskette parameter table adding 3 parameters from IBM
19 // Since no provisions are made for multiple drive types, most
20 // values in this table are ignored.  I set parameters for 1.44M
21 // floppy here
22 struct floppy_ext_dbt_s diskette_param_table2 VISIBLE16 = {
23     .dbt = {
24         .specify1       = 0xAF,
25         .specify2       = 0x02, // head load time 0000001, DMA used
26         .shutoff_ticks  = 0x25,
27         .bps_code       = 0x02,
28         .sectors        = 18,
29         .interblock_len = 0x1B,
30         .data_len       = 0xFF,
31         .gap_len        = 0x6C,
32         .fill_byte      = 0xF6,
33         .settle_time    = 0x0F,
34         .startup_time   = 0x08,
35     },
36     .max_track      = 79,   // maximum track
37     .data_rate      = 0,    // data transfer rate
38     .drive_type     = 4,    // drive type in cmos
39 };
40
41 void
42 floppy_drive_setup()
43 {
44     if (! CONFIG_FLOPPY_SUPPORT)
45         return;
46     dprintf(3, "init floppy drives\n");
47     u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
48     u8 out = 0;
49     u8 num_floppies = 0;
50
51     if (type & 0xf0) {
52         out |= 0x07;
53         num_floppies++;
54     }
55     if (type & 0x0f) {
56         out |= 0x70;
57         num_floppies++;
58     }
59     SET_BDA(floppy_harddisk_info, out);
60
61     // Update equipment word bits for floppy
62     if (num_floppies == 1)
63         // 1 drive, ready for boot
64         SETBITS_BDA(equipment_list_flags, 0x01);
65     else if (num_floppies == 2)
66         // 2 drives, ready for boot
67         SETBITS_BDA(equipment_list_flags, 0x41);
68
69     outb(0x02, PORT_DMA1_MASK_REG);
70
71     // Enable IRQ6 (handle_0e)
72     unmask_pic1(PIC1_IRQ6);
73 }
74
75 static inline void
76 set_diskette_current_cyl(u8 drive, u8 cyl)
77 {
78     if (drive)
79         SET_BDA(floppy_track1, cyl);
80     else
81         SET_BDA(floppy_track0, cyl);
82 }
83
84 static u16
85 get_drive_type(u8 drive)
86 {
87     // check CMOS to see if drive exists
88     u8 drive_type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
89     if (drive == 0)
90         drive_type >>= 4;
91     else
92         drive_type &= 0x0f;
93     return drive_type;
94 }
95
96 static u16
97 floppy_media_known(u8 drive)
98 {
99     if (!(GET_BDA(floppy_recalibration_status) & (1<<drive)))
100         return 0;
101     u8 v = GET_BDA(floppy_media_state[drive]);
102     if (!(v & FMS_MEDIA_DRIVE_ESTABLISHED))
103         return 0;
104     return 1;
105 }
106
107 static void
108 floppy_reset_controller()
109 {
110     // Reset controller
111     u8 val8 = inb(PORT_FD_DOR);
112     outb(val8 & ~0x04, PORT_FD_DOR);
113     outb(val8 | 0x04, PORT_FD_DOR);
114
115     // Wait for controller to come out of reset
116     while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
117         ;
118 }
119
120 static void
121 floppy_prepare_controller(u8 drive)
122 {
123     CLEARBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
124
125     // turn on motor of selected drive, DMA & int enabled, normal operation
126     u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
127     u8 dor = 0x10;
128     if (drive)
129         dor = 0x20;
130     dor |= 0x0c;
131     dor |= drive;
132     outb(dor, PORT_FD_DOR);
133
134     // reset the disk motor timeout value of INT 08
135     SET_BDA(floppy_motor_counter, BX_FLOPPY_ON_CNT);
136
137     // wait for drive readiness
138     while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
139         ;
140
141     if (prev_reset == 0) {
142         irq_enable();
143         u8 v;
144         for (;;) {
145             v = GET_BDA(floppy_recalibration_status);
146             if (v & FRS_TIMEOUT)
147                 break;
148             cpu_relax();
149         }
150         irq_disable();
151
152         v &= ~FRS_TIMEOUT;
153         SET_BDA(floppy_recalibration_status, v);
154     }
155 }
156
157 static u8
158 floppy_pio(u8 *cmd, u8 cmdlen)
159 {
160     floppy_prepare_controller(cmd[1] & 1);
161
162     // send command to controller
163     u8 i;
164     for (i=0; i<cmdlen; i++)
165         outb(cmd[i], PORT_FD_DATA);
166
167     irq_enable();
168     u8 v;
169     for (;;) {
170         if (!GET_BDA(floppy_motor_counter)) {
171             irq_disable();
172             floppy_reset_controller();
173             return DISK_RET_ETIMEOUT;
174         }
175         v = GET_BDA(floppy_recalibration_status);
176         if (v & FRS_TIMEOUT)
177             break;
178         cpu_relax();
179     }
180     irq_disable();
181
182     v &= ~FRS_TIMEOUT;
183     SET_BDA(floppy_recalibration_status, v);
184
185     return 0;
186 }
187
188 static u8
189 floppy_cmd(struct bregs *regs, u16 count, u8 *cmd, u8 cmdlen)
190 {
191     // es:bx = pointer to where to place information from diskette
192     // port 04: DMA-1 base and current address, channel 2
193     // port 05: DMA-1 base and current count, channel 2
194     u16 page = regs->es >> 12;   // upper 4 bits
195     u16 base_es = regs->es << 4; // lower 16bits contributed by ES
196     u16 base_address = base_es + regs->bx; // lower 16 bits of address
197     // contributed by ES:BX
198     if (base_address < base_es)
199         // in case of carry, adjust page by 1
200         page++;
201
202     // check for 64K boundary overrun
203     u16 last_addr = base_address + count;
204     if (last_addr < base_address)
205         return DISK_RET_EBOUNDARY;
206
207     u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
208     if (cmd[0] == 0xe6)
209         // read
210         mode_register = 0x46;
211
212     //DEBUGF("floppy dma c2\n");
213     outb(0x06, PORT_DMA1_MASK_REG);
214     outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
215     outb(base_address, PORT_DMA_ADDR_2);
216     outb(base_address>>8, PORT_DMA_ADDR_2);
217     outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
218     outb(count, PORT_DMA_CNT_2);
219     outb(count>>8, PORT_DMA_CNT_2);
220
221     // port 0b: DMA-1 Mode Register
222     // transfer type=write, channel 2
223     outb(mode_register, PORT_DMA1_MODE_REG);
224
225     // port 81: DMA-1 Page Register, channel 2
226     outb(page, PORT_DMA_PAGE_2);
227
228     outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
229
230     u8 ret = floppy_pio(cmd, cmdlen);
231     if (ret)
232         return ret;
233
234     // check port 3f4 for accessibility to status bytes
235     if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
236         BX_PANIC("int13_diskette: ctrl not ready\n");
237
238     // read 7 return status bytes from controller
239     u8 i;
240     for (i=0; i<7; i++) {
241         u8 v = inb(PORT_FD_DATA);
242         cmd[i] = v;
243         SET_BDA(floppy_return_status[i], v);
244     }
245
246     return 0;
247 }
248
249 static void
250 floppy_drive_recal(u8 drive)
251 {
252     // send Recalibrate command (2 bytes) to controller
253     u8 data[12];
254     data[0] = 0x07;  // 07: Recalibrate
255     data[1] = drive; // 0=drive0, 1=drive1
256     floppy_pio(data, 2);
257
258     SETBITS_BDA(floppy_recalibration_status, 1<<drive);
259     set_diskette_current_cyl(drive, 0);
260 }
261
262 static u16
263 floppy_media_sense(u8 drive)
264 {
265     u16 rv;
266     u8 config_data, media_state;
267
268     floppy_drive_recal(drive);
269
270     // for now cheat and get drive type from CMOS,
271     // assume media is same as drive type
272
273     // ** config_data **
274     // Bitfields for diskette media control:
275     // Bit(s)  Description (Table M0028)
276     //  7-6  last data rate set by controller
277     //        00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
278     //  5-4  last diskette drive step rate selected
279     //        00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
280     //  3-2  {data rate at start of operation}
281     //  1-0  reserved
282
283     // ** media_state **
284     // Bitfields for diskette drive media state:
285     // Bit(s)  Description (Table M0030)
286     //  7-6  data rate
287     //    00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
288     //  5  double stepping required (e.g. 360kB in 1.2MB)
289     //  4  media type established
290     //  3  drive capable of supporting 4MB media
291     //  2-0  on exit from BIOS, contains
292     //    000 trying 360kB in 360kB
293     //    001 trying 360kB in 1.2MB
294     //    010 trying 1.2MB in 1.2MB
295     //    011 360kB in 360kB established
296     //    100 360kB in 1.2MB established
297     //    101 1.2MB in 1.2MB established
298     //    110 reserved
299     //    111 all other formats/drives
300
301     switch (get_drive_type(drive)) {
302     case 1:
303         // 360K 5.25" drive
304         config_data = 0x00; // 0000 0000
305         media_state = 0x25; // 0010 0101
306         rv = 1;
307         break;
308     case 2:
309         // 1.2 MB 5.25" drive
310         config_data = 0x00; // 0000 0000
311         media_state = 0x25; // 0010 0101   // need double stepping??? (bit 5)
312         rv = 1;
313         break;
314     case 3:
315         // 720K 3.5" drive
316         config_data = 0x00; // 0000 0000 ???
317         media_state = 0x17; // 0001 0111
318         rv = 1;
319         break;
320     case 4:
321         // 1.44 MB 3.5" drive
322         config_data = 0x00; // 0000 0000
323         media_state = 0x17; // 0001 0111
324         rv = 1;
325         break;
326     case 5:
327         // 2.88 MB 3.5" drive
328         config_data = 0xCC; // 1100 1100
329         media_state = 0xD7; // 1101 0111
330         rv = 1;
331         break;
332     //
333     // Extended floppy size uses special cmos setting
334     case 6:
335         // 160k 5.25" drive
336         config_data = 0x00; // 0000 0000
337         media_state = 0x27; // 0010 0111
338         rv = 1;
339         break;
340     case 7:
341         // 180k 5.25" drive
342         config_data = 0x00; // 0000 0000
343         media_state = 0x27; // 0010 0111
344         rv = 1;
345         break;
346     case 8:
347         // 320k 5.25" drive
348         config_data = 0x00; // 0000 0000
349         media_state = 0x27; // 0010 0111
350         rv = 1;
351         break;
352     default:
353         // not recognized
354         config_data = 0x00; // 0000 0000
355         media_state = 0x00; // 0000 0000
356         rv = 0;
357     }
358
359     SET_BDA(floppy_last_data_rate, config_data);
360     SET_BDA(floppy_media_state[drive], media_state);
361     return rv;
362 }
363
364 #define floppy_ret(regs, code) \
365     __floppy_ret(__func__, (regs), (code))
366
367 void
368 __floppy_ret(const char *fname, struct bregs *regs, u8 code)
369 {
370     SET_BDA(floppy_last_status, code);
371     if (code)
372         __set_code_fail(fname, regs, code);
373     else
374         set_code_success(regs);
375 }
376
377 static inline void
378 floppy_fail(struct bregs *regs, u8 code)
379 {
380     floppy_ret(regs, code);
381     regs->al = 0; // no sectors read
382 }
383
384 static u16
385 check_drive(struct bregs *regs, u8 drive)
386 {
387     // see if drive exists
388     if (drive > 1 || !get_drive_type(drive)) {
389         // XXX - return type doesn't match
390         floppy_fail(regs, DISK_RET_ETIMEOUT);
391         return 1;
392     }
393
394     // see if media in drive, and type is known
395     if (floppy_media_known(drive) == 0 && floppy_media_sense(drive) == 0) {
396         floppy_fail(regs, DISK_RET_EMEDIA);
397         return 1;
398     }
399     return 0;
400 }
401
402 // diskette controller reset
403 static void
404 floppy_1300(struct bregs *regs, u8 drive)
405 {
406     if (drive > 1) {
407         floppy_ret(regs, DISK_RET_EPARAM);
408         return;
409     }
410     if (!get_drive_type(drive)) {
411         floppy_ret(regs, DISK_RET_ETIMEOUT);
412         return;
413     }
414     set_diskette_current_cyl(drive, 0); // current cylinder
415     floppy_ret(regs, DISK_RET_SUCCESS);
416 }
417
418 // Read Diskette Status
419 static void
420 floppy_1301(struct bregs *regs, u8 drive)
421 {
422     u8 v = GET_BDA(floppy_last_status);
423     regs->ah = v;
424     set_cf(regs, v);
425 }
426
427 // Read Diskette Sectors
428 static void
429 floppy_1302(struct bregs *regs, u8 drive)
430 {
431     if (check_drive(regs, drive))
432         return;
433
434     u8 num_sectors = regs->al;
435     u8 track       = regs->ch;
436     u8 sector      = regs->cl;
437     u8 head        = regs->dh;
438
439     if (head > 1 || sector == 0 || num_sectors == 0
440         || track > 79 || num_sectors > 72) {
441         floppy_fail(regs, DISK_RET_EPARAM);
442         return;
443     }
444
445     // send read-normal-data command (9 bytes) to controller
446     u8 data[12];
447     data[0] = 0xe6; // e6: read normal data
448     data[1] = (head << 2) | drive; // HD DR1 DR2
449     data[2] = track;
450     data[3] = head;
451     data[4] = sector;
452     data[5] = 2; // 512 byte sector size
453     data[6] = sector + num_sectors - 1; // last sector to read on track
454     data[7] = 0; // Gap length
455     data[8] = 0xff; // Gap length
456
457     u16 ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
458     if (ret) {
459         floppy_fail(regs, ret);
460         return;
461     }
462
463     if (data[0] & 0xc0) {
464         floppy_fail(regs, DISK_RET_ECONTROLLER);
465         return;
466     }
467
468     // ??? should track be new val from return_status[3] ?
469     set_diskette_current_cyl(drive, track);
470     // AL = number of sectors read (same value as passed)
471     floppy_ret(regs, DISK_RET_SUCCESS);
472 }
473
474 // Write Diskette Sectors
475 static void
476 floppy_1303(struct bregs *regs, u8 drive)
477 {
478     if (check_drive(regs, drive))
479         return;
480
481     u8 num_sectors = regs->al;
482     u8 track       = regs->ch;
483     u8 sector      = regs->cl;
484     u8 head        = regs->dh;
485
486     if (head > 1 || sector == 0 || num_sectors == 0
487         || track > 79 || num_sectors > 72) {
488         floppy_fail(regs, DISK_RET_EPARAM);
489         return;
490     }
491
492     // send write-normal-data command (9 bytes) to controller
493     u8 data[12];
494     data[0] = 0xc5; // c5: write normal data
495     data[1] = (head << 2) | drive; // HD DR1 DR2
496     data[2] = track;
497     data[3] = head;
498     data[4] = sector;
499     data[5] = 2; // 512 byte sector size
500     data[6] = sector + num_sectors - 1; // last sector to write on track
501     data[7] = 0; // Gap length
502     data[8] = 0xff; // Gap length
503
504     u8 ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
505     if (ret) {
506         floppy_fail(regs, ret);
507         return;
508     }
509
510     if (data[0] & 0xc0) {
511         if (data[1] & 0x02) {
512             set_fail(regs);
513             regs->ax = 0x0300;
514             return;
515         }
516         BX_PANIC("int13_diskette_function: read error\n");
517     }
518
519     // ??? should track be new val from return_status[3] ?
520     set_diskette_current_cyl(drive, track);
521     // AL = number of sectors read (same value as passed)
522     floppy_ret(regs, DISK_RET_SUCCESS);
523 }
524
525 // Verify Diskette Sectors
526 static void
527 floppy_1304(struct bregs *regs, u8 drive)
528 {
529     if (check_drive(regs, drive))
530         return;
531
532     u8 num_sectors = regs->al;
533     u8 track       = regs->ch;
534     u8 sector      = regs->cl;
535     u8 head        = regs->dh;
536
537     if (head > 1 || sector == 0 || num_sectors == 0
538         || track > 79 || num_sectors > 72) {
539         floppy_fail(regs, DISK_RET_EPARAM);
540         return;
541     }
542
543     // ??? should track be new val from return_status[3] ?
544     set_diskette_current_cyl(drive, track);
545     // AL = number of sectors verified (same value as passed)
546     floppy_ret(regs, DISK_RET_SUCCESS);
547 }
548
549 // format diskette track
550 static void
551 floppy_1305(struct bregs *regs, u8 drive)
552 {
553     dprintf(3, "floppy f05\n");
554
555     if (check_drive(regs, drive))
556         return;
557
558     u8 num_sectors = regs->al;
559     u8 head        = regs->dh;
560
561     if (head > 1 || num_sectors == 0 || num_sectors > 18) {
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     dprintf(3, "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     dprintf(6, "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     if (drive > 1) {
708         floppy_ret(regs, DISK_RET_EPARAM);
709         return;
710     }
711     floppy_ret(regs, DISK_RET_ECHANGED);
712 }
713
714 static void
715 floppy_13XX(struct bregs *regs, u8 drive)
716 {
717     floppy_ret(regs, DISK_RET_EPARAM);
718 }
719
720 void
721 floppy_13(struct bregs *regs, u8 drive)
722 {
723     if (! CONFIG_FLOPPY_SUPPORT) {
724         // Minimal stubs
725         switch (regs->ah) {
726         case 0x01: floppy_1301(regs, drive); break;
727         default:   floppy_13XX(regs, drive); break;
728         }
729         return;
730     }
731     switch (regs->ah) {
732     case 0x00: floppy_1300(regs, drive); break;
733     case 0x01: floppy_1301(regs, drive); break;
734     case 0x02: floppy_1302(regs, drive); break;
735     case 0x03: floppy_1303(regs, drive); break;
736     case 0x04: floppy_1304(regs, drive); break;
737     case 0x05: floppy_1305(regs, drive); break;
738     case 0x08: floppy_1308(regs, drive); break;
739     case 0x15: floppy_1315(regs, drive); break;
740     case 0x16: floppy_1316(regs, drive); break;
741     default:   floppy_13XX(regs, drive); break;
742     }
743 }
744
745 // INT 0Eh Diskette Hardware ISR Entry Point
746 void VISIBLE16
747 handle_0e()
748 {
749     debug_isr(DEBUG_ISR_0e);
750     if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
751         outb(0x08, PORT_FD_DATA); // sense interrupt status
752         while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
753             ;
754         do {
755             inb(PORT_FD_DATA);
756         } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
757     }
758     eoi_pic1();
759     // diskette interrupt has occurred
760     SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
761 }
762
763 // Called from int08 handler.
764 void
765 floppy_tick()
766 {
767     // time to turn off drive(s)?
768     u8 fcount = GET_BDA(floppy_motor_counter);
769     if (fcount) {
770         fcount--;
771         SET_BDA(floppy_motor_counter, fcount);
772         if (fcount == 0)
773             // turn motor(s) off
774             outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
775     }
776 }