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