VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/floppy.c@ 72300

Last change on this file since 72300 was 69501, checked in by vboxsync, 7 years ago

PC/BIOS: Added LGPL disclaimer text where appropriate.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.5 KB
Line 
1/* $Id: floppy.c 69501 2017-10-28 16:12:47Z vboxsync $ */
2/** @file
3 * PC BIOS - ???
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on:
19 *
20 * ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
21 *
22 * Copyright (C) 2002 MandrakeSoft S.A.
23 *
24 * MandrakeSoft S.A.
25 * 43, rue d'Aboukir
26 * 75002 Paris - France
27 * http://www.linux-mandrake.com/
28 * http://www.mandrakesoft.com/
29 *
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public
32 * License as published by the Free Software Foundation; either
33 * version 2 of the License, or (at your option) any later version.
34 *
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Lesser General Public License for more details.
39 *
40 * You should have received a copy of the GNU Lesser General Public
41 * License along with this library; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
43 *
44 */
45
46/*
47 * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
48 * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
49 * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
50 * a choice of LGPL license versions is made available with the language indicating
51 * that LGPLv2 or any later version may be used, or where a choice of which version
52 * of the LGPL is applied is otherwise unspecified.
53 */
54
55
56#include <stdint.h>
57#include "inlines.h"
58#include "biosint.h"
59
60extern uint16_t get_floppy_dpt(uint8_t drive_type);
61
62// Local copies to slihgtly reduce stack usage.
63inline uint8_t read_byte(uint16_t seg, uint16_t offset)
64{
65 return( *(seg:>(uint8_t *)offset) );
66}
67
68inline void write_byte(uint16_t seg, uint16_t offset, uint8_t data)
69{
70 *(seg:>(uint8_t *)offset) = data;
71}
72
73
74//////////////////////
75// FLOPPY functions //
76//////////////////////
77
78inline void set_diskette_ret_status(uint8_t value)
79{
80 write_byte(0x0040, 0x0041, value);
81}
82
83void set_diskette_current_cyl(uint8_t drive, uint8_t cyl)
84{
85 if (drive > 1)
86 BX_PANIC("set_diskette_current_cyl: drive > 1\n");
87 write_byte(0x0040, 0x0094+drive, cyl);
88}
89
90#if 1 //BX_SUPPORT_FLOPPY
91
92#if DEBUG_INT13_FL
93# define BX_DEBUG_INT13_FL(...) BX_DEBUG(__VA_ARGS__)
94#else
95# define BX_DEBUG_INT13_FL(...)
96#endif
97
98#define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
99
100extern int diskette_param_table; /* At a fixed location. */
101
102#ifndef VBOX_WITH_FLOPPY_IRQ_POLLING
103
104/**
105 * Wait for the 7th bit of 0040:003e to be set by int0e_handler.
106 * @returns first 7 bits of byte 0040:003e, interrupts disabled.
107 */
108uint8_t floppy_wait_for_interrupt(void)
109{
110 int_disable();
111 for (;;)
112 {
113 uint8_t val8 = read_byte(0x0040, 0x003e);
114 if (val8 & 0x80)
115 return val8 & ~0x7f;
116 int_enable_hlt_disable();
117 }
118}
119
120/**
121 * Wait for the 7th bit of 0040:003e to be set by int0e_handler or 0040:0040 to
122 * be cleared by the timer, clearing the interrupt flag on success.
123 *
124 * @returns 0 on timeout with interrupts enabled.
125 * All 8 bits at 0040:003e on interrupt with interrupts disabled (i.e.
126 * non-zero), after first clearing the 7th bit at 0040:003e.
127 */
128uint8_t floppy_wait_for_interrupt_or_timeout(void)
129{
130 int_disable();
131 for (;;)
132 {
133 uint8_t val8 = read_byte(0x0040, 0x0040);
134 if (val8 == 0) {
135 int_enable();
136 return 0;
137 }
138
139 val8 = read_byte(0x0040, 0x003e);
140 if (val8 & 0x80) {
141 write_byte(0x0040, 0x003e, val8 & 0x7f);
142 return val8;
143 }
144 int_enable_hlt_disable();
145 }
146}
147
148#endif /* !VBOX_WITH_FLOPPY_IRQ_POLLING */
149
150void floppy_reset_controller(uint16_t drive)
151{
152 uint8_t val8;
153
154 // Reset controller
155 val8 = inb(0x03f2);
156 outb(0x03f2, val8 & ~0x04);
157 outb(0x03f2, val8 | 0x04);
158
159 // Wait for controller to come out of reset
160 do {
161 val8 = inb(0x3f4);
162 } while ( (val8 & 0xc0) != 0x80 );
163
164 // Mark media in drive as unknown
165 val8 = read_byte(0x0040, 0x0090 + drive);
166 val8 &= ~0x10;
167 write_byte(0x0040, 0x90 + drive, val8);
168
169}
170
171void floppy_prepare_controller(uint16_t drive)
172{
173 uint8_t val8, dor, prev_reset;
174
175 // set 40:3e bit 7 to 0
176 val8 = read_byte(0x0040, 0x003e);
177 val8 &= 0x7f;
178 write_byte(0x0040, 0x003e, val8);
179
180 // turn on motor of selected drive, DMA & int enabled, normal operation
181 prev_reset = inb(0x03f2) & 0x04;
182 if (drive)
183 dor = 0x20;
184 else
185 dor = 0x10;
186 dor |= 0x0c;
187 dor |= drive;
188 outb(0x03f2, dor);
189
190 // reset the disk motor timeout value of INT 08
191 write_byte(0x0040,0x0040, BX_FLOPPY_ON_CNT);
192
193 // program data rate
194 val8 = read_byte(0x0040, 0x008b);
195 val8 >>= 6;
196 outb(0x03f7, val8);
197
198 // wait for drive readiness
199 do {
200 val8 = inb(0x3f4);
201 } while ( (val8 & 0xc0) != 0x80 );
202
203 if (prev_reset == 0) {
204#ifdef VBOX_WITH_FLOPPY_IRQ_POLLING
205 // turn on interrupts
206 int_enable();
207 // wait on 40:3e bit 7 to become 1
208 do {
209 val8 = read_byte(0x0040, 0x003e);
210 } while ( (val8 & 0x80) == 0 );
211 val8 &= 0x7f;
212 int_disable();
213#else
214 val8 = floppy_wait_for_interrupt(); /* (7th bit cleared in ret val) */
215#endif
216 write_byte(0x0040, 0x003e, val8);
217 }
218}
219
220bx_bool floppy_media_known(uint16_t drive)
221{
222 uint8_t val8;
223 uint16_t media_state_offset;
224
225 val8 = read_byte(0x0040, 0x003e); // diskette recal status
226 if (drive)
227 val8 >>= 1;
228 val8 &= 0x01;
229 if (val8 == 0)
230 return 0;
231
232 media_state_offset = 0x0090;
233 if (drive)
234 media_state_offset += 1;
235
236 val8 = read_byte(0x0040, media_state_offset);
237 val8 = (val8 >> 4) & 0x01;
238 if (val8 == 0)
239 return 0;
240
241 // checks passed, return KNOWN
242 return 1;
243}
244
245bx_bool floppy_read_id(uint16_t drive)
246{
247#ifdef VBOX_WITH_FLOPPY_IRQ_POLLING
248 uint8_t val8;
249#endif
250 int i;
251
252 floppy_prepare_controller(drive);
253
254 // send Read ID command (2 bytes) to controller
255 outb(0x03f5, 0x4a); // 4a: Read ID (MFM)
256 outb(0x03f5, drive); // 0=drive0, 1=drive1, head always 0
257
258#ifdef VBOX_WITH_FLOPPY_IRQ_POLLING
259 // turn on interrupts
260 int_enable();
261
262 // wait on 40:3e bit 7 to become 1
263 do {
264 val8 = (read_byte(0x0040, 0x003e) & 0x80);
265 } while ( val8 == 0 );
266
267 val8 = 0; // separate asm from while() loop
268 // turn off interrupts
269 int_disable();
270#else
271 floppy_wait_for_interrupt();
272#endif
273
274 // read 7 return status bytes from controller
275 for (i = 0; i < 7; ++i)
276 write_byte(0x0040, 0x0042 + i, inb(0x3f5));
277
278 if ((read_byte(0x0040, 0x0042 + 0) & 0xc0) != 0)
279 return 0;
280 else
281 return 1;
282}
283
284bx_bool floppy_drive_recal(uint16_t drive)
285{
286 uint8_t val8;
287 uint16_t curr_cyl_offset;
288
289 floppy_prepare_controller(drive);
290
291 // send Recalibrate command (2 bytes) to controller
292 outb(0x03f5, 0x07); // 07: Recalibrate
293 outb(0x03f5, drive); // 0=drive0, 1=drive1
294
295#ifdef VBOX_WITH_FLOPPY_IRQ_POLLING
296 // turn on interrupts
297 int_enable();
298
299 // wait on 40:3e bit 7 to become 1
300 do {
301 val8 = (read_byte(0x0040, 0x003e) & 0x80);
302 } while ( val8 == 0 );
303
304 val8 = 0; // separate asm from while() loop
305 // turn off interrupts
306 int_disable();
307
308 // set 40:3e bit 7 to 0, and calibrated bit
309 val8 = read_byte(0x0040, 0x003e);
310 val8 &= 0x7f;
311#else
312 val8 = floppy_wait_for_interrupt(); /* (7th bit cleared in ret val) */
313
314 // set 40:3e bit 7 to 0, and calibrated bit
315#endif
316 if (drive) {
317 val8 |= 0x02; // Drive 1 calibrated
318 curr_cyl_offset = 0x0095;
319 } else {
320 val8 |= 0x01; // Drive 0 calibrated
321 curr_cyl_offset = 0x0094;
322 }
323 write_byte(0x0040, 0x003e, val8);
324 write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
325
326 return 1;
327}
328
329
330bx_bool floppy_media_sense(uint16_t drive)
331{
332 bx_bool retval;
333 uint16_t media_state_offset;
334 uint8_t drive_type, config_data, media_state;
335
336 if (floppy_drive_recal(drive) == 0)
337 return 0;
338
339 // Try the diskette data rates in the following order:
340 // 1 Mbps -> 500 Kbps -> 300 Kbps -> 250 Kbps
341 // The 1 Mbps rate is only tried for 2.88M drives.
342
343 // ** config_data **
344 // Bitfields for diskette media control:
345 // Bit(s) Description (Table M0028)
346 // 7-6 last data rate set by controller
347 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
348 // 5-4 last diskette drive step rate selected
349 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
350 // 3-2 {data rate at start of operation}
351 // 1-0 reserved
352
353 // ** media_state **
354 // Bitfields for diskette drive media state:
355 // Bit(s) Description (Table M0030)
356 // 7-6 data rate
357 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
358 // 5 double stepping required (e.g. 360kB in 1.2MB)
359 // 4 media type established
360 // 3 drive capable of supporting 4MB media
361 // 2-0 on exit from BIOS, contains
362 // 000 trying 360kB in 360kB
363 // 001 trying 360kB in 1.2MB
364 // 010 trying 1.2MB in 1.2MB
365 // 011 360kB in 360kB established
366 // 100 360kB in 1.2MB established
367 // 101 1.2MB in 1.2MB established
368 // 110 reserved
369 // 111 all other formats/drives
370
371 /// @todo break out drive type determination
372 drive_type = inb_cmos(0x10);
373 if (drive == 0)
374 drive_type >>= 4;
375 else
376 drive_type &= 0x0f;
377 if ( drive_type == 1 ) {
378 // 360K 5.25" drive
379 config_data = 0x00; // 0000 0000
380 media_state = 0x15; // 0001 0101
381 retval = 1;
382 }
383 else if ( drive_type == 2 ) {
384 // 1.2 MB 5.25" drive
385 config_data = 0x00; // 0000 0000
386 media_state = 0x35; // 0011 0101 // need double stepping??? (bit 5)
387 retval = 1;
388 }
389 else if ( drive_type == 3 ) {
390 // 720K 3.5" drive
391 config_data = 0x00; // 0000 0000 ???
392 media_state = 0x17; // 0001 0111
393 retval = 1;
394 }
395 else if ( drive_type == 4 ) {
396 // 1.44 MB 3.5" drive
397 config_data = 0x00; // 0000 0000
398 media_state = 0x17; // 0001 0111
399 retval = 1;
400 }
401 else if ( drive_type == 5 ) {
402 // 2.88 MB 3.5" drive
403 config_data = 0xCC; // 1100 1100
404 media_state = 0xD7; // 1101 0111
405 retval = 1;
406 }
407 // Extended floppy size uses special cmos setting
408 else if ( drive_type == 14 || drive_type == 15 ) {
409 // 15.6 MB 3.5" (fake) || 63.5 MB 3.5" (fake) - report same as 2.88 MB.
410 config_data = 0xCC; // 1100 1100
411 media_state = 0xD7; // 1101 0111
412 retval = 1;
413 }
414 else {
415 // not recognized
416 config_data = 0x00; // 0000 0000
417 media_state = 0x00; // 0000 0000
418 retval = 0;
419 }
420
421 write_byte(0x0040, 0x008B, config_data);
422 while (!floppy_read_id(drive)) {
423 if ((config_data & 0xC0) == 0x80) {
424 // If even 250 Kbps failed, we can't do much
425 break;
426 }
427 switch (config_data & 0xC0) {
428 case 0xC0: // 1 Mbps
429 config_data = config_data & 0x3F | 0x00;
430 break;
431 case 0x00: // 500 Kbps
432 config_data = config_data & 0x3F | 0x40;
433 break;
434 case 0x40: // 300 Kbps
435 config_data = config_data & 0x3F | 0x80;
436 break;
437 }
438 write_byte(0x0040, 0x008B, config_data);
439 }
440
441 if (drive == 0)
442 media_state_offset = 0x0090;
443 else
444 media_state_offset = 0x0091;
445 write_byte(0x0040, 0x008B, config_data);
446 write_byte(0x0040, media_state_offset, media_state);
447
448 return retval;
449}
450
451
452bx_bool floppy_drive_exists(uint16_t drive)
453{
454 uint8_t drive_type;
455
456 // check CMOS to see if drive exists
457 /// @todo break out drive type determination
458 drive_type = inb_cmos(0x10);
459 if (drive == 0)
460 drive_type >>= 4;
461 else
462 drive_type &= 0x0f;
463 return drive_type != 0;
464}
465
466/// @todo put in a header
467#define AX r.gr.u.r16.ax
468#define BX r.gr.u.r16.bx
469#define CX r.gr.u.r16.cx
470#define DX r.gr.u.r16.dx
471#define SI r.gr.u.r16.si // not used
472#define DI r.gr.u.r16.di
473#define BP r.gr.u.r16.bp // not used
474#define ELDX r.gr.u.r16.sp
475#define DS r.ds // not used
476#define ES r.es
477#define FLAGS r.ra.flags.u.r16.flags
478
479void BIOSCALL int13_diskette_function(disk_regs_t r)
480{
481 uint8_t drive, num_sectors, track, sector, head;
482 uint16_t base_address, base_count, base_es;
483 uint8_t page, mode_register, val8, media_state;
484 uint8_t drive_type, num_floppies;
485 uint16_t last_addr;
486 int i;
487
488 BX_DEBUG_INT13_FL("%s: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", __func__, AX, BX, CX, DX, ES);
489
490 SET_IF(); /* INT 13h always returns with interrupts enabled. */
491
492 switch ( GET_AH() ) {
493 case 0x00: // diskette controller reset
494 BX_DEBUG_INT13_FL("floppy f00\n");
495 drive = GET_ELDL();
496 if (drive > 1) {
497 SET_AH(1); // invalid param
498 set_diskette_ret_status(1);
499 SET_CF();
500 return;
501 }
502 /// @todo break out drive type determination
503 drive_type = inb_cmos(0x10);
504 if (drive == 0)
505 drive_type >>= 4;
506 else
507 drive_type &= 0x0f;
508 if (drive_type == 0) {
509 SET_AH(0x80); // drive not responding
510 set_diskette_ret_status(0x80);
511 SET_CF();
512 return;
513 }
514
515 // force re-calibration etc.
516 write_byte(0x0040, 0x003e, 0);
517
518 SET_AH(0);
519 set_diskette_ret_status(0);
520 CLEAR_CF(); // successful
521 set_diskette_current_cyl(drive, 0); // current cylinder
522 return;
523
524 case 0x01: // Read Diskette Status
525 CLEAR_CF();
526 val8 = read_byte(0x0000, 0x0441);
527 SET_AH(val8);
528 if (val8) {
529 SET_CF();
530 }
531 return;
532
533 case 0x02: // Read Diskette Sectors
534 case 0x03: // Write Diskette Sectors
535 case 0x04: // Verify Diskette Sectors
536 num_sectors = GET_AL();
537 track = GET_CH();
538 sector = GET_CL();
539 head = GET_DH();
540 drive = GET_ELDL();
541
542 if ( (drive > 1) || (head > 1) ||
543 (num_sectors == 0) || (num_sectors > 72) ) {
544 BX_INFO("%s: drive>1 || head>1 ...\n", __func__);
545 SET_AH(1);
546 set_diskette_ret_status(1);
547 SET_AL(0); // no sectors read
548 SET_CF(); // error occurred
549 return;
550 }
551
552 // see if drive exists
553 if (floppy_drive_exists(drive) == 0) {
554 BX_DEBUG_INT13_FL("failed (not ready)\n");
555 SET_AH(0x80); // not responding
556 set_diskette_ret_status(0x80);
557 SET_AL(0); // no sectors read
558 SET_CF(); // error occurred
559 return;
560 }
561
562 // see if media in drive, and type is known
563 if (floppy_media_known(drive) == 0) {
564 if (floppy_media_sense(drive) == 0) {
565 BX_DEBUG_INT13_FL("media not found\n");
566 SET_AH(0x0C); // Media type not found
567 set_diskette_ret_status(0x0C);
568 SET_AL(0); // no sectors read
569 SET_CF(); // error occurred
570 return;
571 }
572 }
573
574 if (GET_AH() == 0x02) {
575 // Read Diskette Sectors
576
577 //-----------------------------------
578 // set up DMA controller for transfer
579 //-----------------------------------
580
581 // es:bx = pointer to where to place information from diskette
582 // port 04: DMA-1 base and current address, channel 2
583 // port 05: DMA-1 base and current count, channel 2
584 /// @todo merge/factor out pointer normalization
585 page = (ES >> 12); // upper 4 bits
586 base_es = (ES << 4); // lower 16bits contributed by ES
587 base_address = base_es + BX; // lower 16 bits of address
588 // contributed by ES:BX
589 if ( base_address < base_es ) {
590 // in case of carry, adjust page by 1
591 page++;
592 }
593 base_count = (num_sectors * 512) - 1;
594
595 // check for 64K boundary overrun
596 last_addr = base_address + base_count;
597 if (last_addr < base_address) {
598 SET_AH(0x09);
599 set_diskette_ret_status(0x09);
600 SET_AL(0); // no sectors read
601 SET_CF(); // error occurred
602 return;
603 }
604
605 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
606 outb(0x000a, 0x06);
607
608 BX_DEBUG_INT13_FL("clear flip-flop\n");
609 outb(0x000c, 0x00); // clear flip-flop
610 outb(0x0004, base_address);
611 outb(0x0004, base_address>>8);
612 BX_DEBUG_INT13_FL("clear flip-flop\n");
613 outb(0x000c, 0x00); // clear flip-flop
614 outb(0x0005, base_count);
615 outb(0x0005, base_count>>8);
616 BX_DEBUG_INT13_FL("xfer buf %x bytes at %x:%x\n",
617 base_count + 1, page, base_address);
618
619 // port 0b: DMA-1 Mode Register
620 mode_register = 0x46; // single mode, increment, autoinit disable,
621 // transfer type=write, channel 2
622 BX_DEBUG_INT13_FL("setting mode register\n");
623 outb(0x000b, mode_register);
624
625 BX_DEBUG_INT13_FL("setting page register\n");
626 // port 81: DMA-1 Page Register, channel 2
627 outb(0x0081, page);
628
629 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
630 outb(0x000a, 0x02); // unmask channel 2
631
632 //--------------------------------------
633 // set up floppy controller for transfer
634 //--------------------------------------
635 floppy_prepare_controller(drive);
636
637 // send read-normal-data command (9 bytes) to controller
638 outb(0x03f5, 0xe6); // e6: read normal data
639 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
640 outb(0x03f5, track);
641 outb(0x03f5, head);
642 outb(0x03f5, sector);
643 outb(0x03f5, 2); // 512 byte sector size
644 outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
645 outb(0x03f5, 0); // Gap length
646 outb(0x03f5, 0xff); // Gap length
647 BX_DEBUG_INT13_FL("read initiated\n");
648
649#ifdef VBOX_WITH_FLOPPY_IRQ_POLLING
650 // turn on interrupts
651 int_enable();
652
653 // wait on 40:3e bit 7 to become 1 or timeout (latter isn't armed so it won't happen)
654 do {
655 val8 = read_byte(0x0040, 0x0040);
656 if (val8 == 0) {
657 BX_DEBUG_INT13_FL("failed (not ready)\n");
658 floppy_reset_controller(drive);
659 SET_AH(0x80); // drive not ready (timeout)
660 set_diskette_ret_status(0x80);
661 SET_AL(0); // no sectors read
662 SET_CF(); // error occurred
663 return;
664 }
665 val8 = (read_byte(0x0040, 0x003e) & 0x80);
666 } while ( val8 == 0 );
667
668 val8 = 0; // separate asm from while() loop
669 // turn off interrupts
670 int_disable();
671
672 // set 40:3e bit 7 to 0
673 val8 = read_byte(0x0040, 0x003e);
674 val8 &= 0x7f;
675 write_byte(0x0040, 0x003e, val8);
676
677#else
678 val8 = floppy_wait_for_interrupt_or_timeout();
679 if (val8 == 0) { /* Note! Interrupts enabled in this branch. */
680 BX_DEBUG_INT13_FL("failed (not ready)\n");
681 floppy_reset_controller(drive);
682 SET_AH(0x80); // drive not ready (timeout)
683 set_diskette_ret_status(0x80);
684 SET_AL(0); // no sectors read
685 SET_CF(); // error occurred
686 return;
687 }
688#endif
689
690 // check port 3f4 for accessibility to status bytes
691 val8 = inb(0x3f4);
692 if ( (val8 & 0xc0) != 0xc0 )
693 BX_PANIC("%s: ctrl not ready\n", __func__);
694
695 // read 7 return status bytes from controller and store in BDA
696 for (i = 0; i < 7; ++i)
697 write_byte(0x0040, 0x0042 + i, inb(0x3f5));
698
699 if ((read_byte(0x0040, 0x0042 + 0) & 0xc0) != 0) {
700 BX_DEBUG_INT13_FL("failed (FDC failure)\n");
701 floppy_reset_controller(drive);
702 SET_AH(0x20);
703 set_diskette_ret_status(0x20);
704 SET_AL(0); // no sectors read
705 SET_CF(); // error occurred
706 return;
707 }
708
709#ifdef DMA_WORKAROUND
710 rep_movsw(ES :> BX, ES :> BX, num_sectors * 512 / 2);
711#endif
712 BX_DEBUG_INT13_FL("success!\n");
713 // ??? should track be new val from return_status[3] ?
714 set_diskette_current_cyl(drive, track);
715 // AL = number of sectors read (same value as passed)
716 SET_AH(0x00); // success
717 CLEAR_CF(); // success
718 return;
719 } else if (GET_AH() == 0x03) {
720 // Write Diskette Sectors
721
722 //-----------------------------------
723 // set up DMA controller for transfer
724 //-----------------------------------
725
726 // es:bx = pointer to where to place information from diskette
727 // port 04: DMA-1 base and current address, channel 2
728 // port 05: DMA-1 base and current count, channel 2
729 /// @todo merge/factor out pointer normalization
730 page = (ES >> 12); // upper 4 bits
731 base_es = (ES << 4); // lower 16bits contributed by ES
732 base_address = base_es + BX; // lower 16 bits of address
733 // contributed by ES:BX
734 if ( base_address < base_es ) {
735 // in case of carry, adjust page by 1
736 page++;
737 }
738 base_count = (num_sectors * 512) - 1;
739
740 // check for 64K boundary overrun
741 last_addr = base_address + base_count;
742 if (last_addr < base_address) {
743 SET_AH(0x09);
744 set_diskette_ret_status(0x09);
745 SET_AL(0); // no sectors read
746 SET_CF(); // error occurred
747 return;
748 }
749
750 BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
751 outb(0x000a, 0x06);
752
753 outb(0x000c, 0x00); // clear flip-flop
754 outb(0x0004, base_address);
755 outb(0x0004, base_address>>8);
756 outb(0x000c, 0x00); // clear flip-flop
757 outb(0x0005, base_count);
758 outb(0x0005, base_count>>8);
759 BX_DEBUG_INT13_FL("xfer buf %x bytes at %x:%x\n",
760 base_count, page, base_address);
761
762 // port 0b: DMA-1 Mode Register
763 mode_register = 0x4a; // single mode, increment, autoinit disable,
764 // transfer type=read, channel 2
765 outb(0x000b, mode_register);
766
767 // port 81: DMA-1 Page Register, channel 2
768 outb(0x0081, page);
769
770 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
771 outb(0x000a, 0x02);
772
773 //--------------------------------------
774 // set up floppy controller for transfer
775 //--------------------------------------
776 floppy_prepare_controller(drive);
777
778 // send write-normal-data command (9 bytes) to controller
779 outb(0x03f5, 0xc5); // c5: write normal data
780 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
781 outb(0x03f5, track);
782 outb(0x03f5, head);
783 outb(0x03f5, sector);
784 outb(0x03f5, 2); // 512 byte sector size
785 outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
786 outb(0x03f5, 0); // Gap length
787 outb(0x03f5, 0xff); // Gap length
788
789#ifdef VBOX_WITH_FLOPPY_IRQ_POLLING
790 // turn on interrupts
791 int_enable();
792
793 // wait on 40:3e bit 7 to become 1
794 do {
795 val8 = read_byte(0x0040, 0x0040);
796 if (val8 == 0) {
797 floppy_reset_controller(drive);
798 SET_AH(0x80); // drive not ready (timeout)
799 set_diskette_ret_status(0x80);
800 SET_AL(0); // no sectors written
801 SET_CF(); // error occurred
802 return;
803 }
804 val8 = (read_byte(0x0040, 0x003e) & 0x80);
805 } while ( val8 == 0 );
806
807 val8 = 0; // separate asm from while() loop @todo: why??
808 // turn off interrupts
809 int_disable();
810
811 // set 40:3e bit 7 to 0
812 val8 = read_byte(0x0040, 0x003e);
813 val8 &= 0x7f;
814 write_byte(0x0040, 0x003e, val8);
815#else
816 val8 = floppy_wait_for_interrupt_or_timeout();
817 if (val8 == 0) { /* Note! Interrupts enabled in this branch. */
818 floppy_reset_controller(drive);
819 SET_AH(0x80); // drive not ready (timeout)
820 set_diskette_ret_status(0x80);
821 SET_AL(0); // no sectors written
822 SET_CF(); // error occurred
823 return;
824 }
825#endif
826
827 // check port 3f4 for accessibility to status bytes
828 val8 = inb(0x3f4);
829 if ( (val8 & 0xc0) != 0xc0 )
830 BX_PANIC("%s: ctrl not ready\n", __func__);
831
832 // read 7 return status bytes from controller and store in BDA
833 for (i = 0; i < 7; ++i)
834 write_byte(0x0040, 0x0042 + i, inb(0x3f5));
835
836 if ((read_byte(0x0040, 0x0042 + 0) & 0xc0) != 0) {
837 if ((read_byte(0x0040, 0x0042 + 1) & 0x02) != 0) {
838 // diskette not writable.
839 // AH=status code=0x03 (tried to write on write-protected disk)
840 // AL=number of sectors written=0
841 AX = 0x0300;
842 } else {
843 // Some other problem occurred.
844 AX = 0x0100;
845 }
846 SET_CF();
847 return;
848 }
849
850 // ??? should track be new val from return_status[3] ?
851 set_diskette_current_cyl(drive, track);
852 // AL = number of sectors read (same value as passed)
853 SET_AH(0x00); // success
854 CLEAR_CF(); // success
855 return;
856 } else { // if (ah == 0x04)
857 // Verify Diskette Sectors
858
859 // ??? should track be new val from return_status[3] ?
860 set_diskette_current_cyl(drive, track);
861 // AL = number of sectors verified (same value as passed)
862 CLEAR_CF(); // success
863 SET_AH(0x00); // success
864 return;
865 }
866 break;
867
868 case 0x05: // format diskette track
869 BX_DEBUG_INT13_FL("floppy f05\n");
870
871 num_sectors = GET_AL();
872 track = GET_CH();
873 head = GET_DH();
874 drive = GET_ELDL();
875
876 if ((drive > 1) || (head > 1) || (track > 79) ||
877 (num_sectors == 0) || (num_sectors > 18)) {
878 SET_AH(1);
879 set_diskette_ret_status(1);
880 SET_CF(); // error occurred
881 }
882
883 // see if drive exists
884 if (floppy_drive_exists(drive) == 0) {
885 SET_AH(0x80); // drive not responding
886 set_diskette_ret_status(0x80);
887 SET_CF(); // error occurred
888 return;
889 }
890
891 // see if media in drive, and type is known
892 if (floppy_media_known(drive) == 0) {
893 if (floppy_media_sense(drive) == 0) {
894 SET_AH(0x0C); // Media type not found
895 set_diskette_ret_status(0x0C);
896 SET_AL(0); // no sectors read
897 SET_CF(); // error occurred
898 return;
899 }
900 }
901
902 // set up DMA controller for transfer
903 /// @todo merge/factor out pointer normalization
904 page = (ES >> 12); // upper 4 bits
905 base_es = (ES << 4); // lower 16bits contributed by ES
906 base_address = base_es + BX; // lower 16 bits of address
907 // contributed by ES:BX
908 if ( base_address < base_es ) {
909 // in case of carry, adjust page by 1
910 page++;
911 }
912 base_count = (num_sectors * 4) - 1;
913
914 // check for 64K boundary overrun
915 last_addr = base_address + base_count;
916 if (last_addr < base_address) {
917 SET_AH(0x09);
918 set_diskette_ret_status(0x09);
919 SET_AL(0); // no sectors read
920 SET_CF(); // error occurred
921 return;
922 }
923
924 outb(0x000a, 0x06);
925 outb(0x000c, 0x00); // clear flip-flop
926 outb(0x0004, base_address);
927 outb(0x0004, base_address>>8);
928 outb(0x000c, 0x00); // clear flip-flop
929 outb(0x0005, base_count);
930 outb(0x0005, base_count>>8);
931 mode_register = 0x4a; // single mode, increment, autoinit disable,
932 // transfer type=read, channel 2
933 outb(0x000b, mode_register);
934 // port 81: DMA-1 Page Register, channel 2
935 outb(0x0081, page);
936 outb(0x000a, 0x02);
937
938 // set up floppy controller for transfer
939 floppy_prepare_controller(drive);
940
941 // send seek command to controller
942 outb(0x03f5, 0x0f); // 0f: seek
943 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
944 outb(0x03f5, track);
945
946 // send format-track command (6 bytes) to controller
947 outb(0x03f5, 0x4d); // 4d: format track
948 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
949 outb(0x03f5, 2); // 512 byte sector size
950 outb(0x03f5, num_sectors); // number of sectors per track
951 outb(0x03f5, 0); // Gap length
952 outb(0x03f5, 0xf6); // Fill byte
953
954#ifdef VBOX_WITH_FLOPPY_IRQ_POLLING
955 // turn on interrupts
956 int_enable();
957
958 // wait on 40:3e bit 7 to become 1
959 do {
960 val8 = read_byte(0x0040, 0x0040);
961 if (val8 == 0) {
962 floppy_reset_controller(drive);
963 SET_AH(0x80); // drive not ready (timeout)
964 set_diskette_ret_status(0x80);
965 SET_CF(); // error occurred
966 return;
967 }
968 val8 = (read_byte(0x0040, 0x003e) & 0x80);
969 } while ( val8 == 0 );
970
971 val8 = 0; // separate asm from while() loop
972 // turn off interrupts
973 int_disable();
974
975 // set 40:3e bit 7 to 0
976 val8 = read_byte(0x0040, 0x003e);
977 val8 &= 0x7f;
978 write_byte(0x0040, 0x003e, val8);
979#else
980 val8 = floppy_wait_for_interrupt_or_timeout();
981 if (val8 == 0) { /* Note! Interrupts enabled in this branch. */
982 floppy_reset_controller(drive);
983 SET_AH(0x80); // drive not ready (timeout)
984 set_diskette_ret_status(0x80);
985 SET_CF(); // error occurred
986 return;
987 }
988#endif
989
990 // check port 3f4 for accessibility to status bytes
991 val8 = inb(0x3f4);
992 if ( (val8 & 0xc0) != 0xc0 )
993 BX_PANIC("%s: ctrl not ready\n", __func__);
994
995 // read 7 return status bytes from controller and store in BDA
996 for (i = 0; i < 7; ++i)
997 write_byte(0x0040, 0x0042 + i, inb(0x3f5));
998
999 if ((read_byte(0x0040, 0x0042 + 0) & 0xc0) != 0) {
1000 if ((read_byte(0x0040, 0x0042 + 1) & 0x02) != 0) {
1001 // diskette not writable.
1002 // AH=status code=0x03 (tried to write on write-protected disk)
1003 // AL=number of sectors written=0
1004 AX = 0x0300;
1005 SET_CF();
1006 return;
1007 } else {
1008 BX_PANIC("%s: write error\n", __func__);
1009 }
1010 }
1011
1012 SET_AH(0);
1013 set_diskette_ret_status(0);
1014 set_diskette_current_cyl(drive, 0);
1015 CLEAR_CF(); // successful
1016 return;
1017
1018
1019 case 0x08: // read diskette drive parameters
1020 BX_DEBUG_INT13_FL("floppy f08\n");
1021 drive = GET_ELDL();
1022
1023 if (drive > 1) {
1024 AX = 0;
1025 BX = 0;
1026 CX = 0;
1027 DX = 0;
1028 ES = 0;
1029 DI = 0;
1030 SET_DL(num_floppies);
1031 SET_CF();
1032 return;
1033 }
1034
1035 /// @todo break out drive type determination
1036 drive_type = inb_cmos(0x10);
1037 num_floppies = 0;
1038 if (drive_type & 0xf0)
1039 num_floppies++;
1040 if (drive_type & 0x0f)
1041 num_floppies++;
1042
1043 if (drive == 0)
1044 drive_type >>= 4;
1045 else
1046 drive_type &= 0x0f;
1047
1048 SET_BH(0);
1049 SET_BL(drive_type);
1050 SET_AH(0);
1051 SET_AL(0);
1052 SET_DL(num_floppies);
1053 SET_DH(1); // max head #
1054
1055 switch (drive_type) {
1056 case 0: // none
1057 CX = 0;
1058 SET_DH(0); // max head #
1059 break;
1060
1061 case 1: // 360KB, 5.25"
1062 CX = 0x2709; // 40 tracks, 9 sectors
1063 break;
1064
1065 case 2: // 1.2MB, 5.25"
1066 CX = 0x4f0f; // 80 tracks, 15 sectors
1067 break;
1068
1069 case 3: // 720KB, 3.5"
1070 CX = 0x4f09; // 80 tracks, 9 sectors
1071 break;
1072
1073 case 4: // 1.44MB, 3.5"
1074 CX = 0x4f12; // 80 tracks, 18 sectors
1075 break;
1076
1077 case 5: // 2.88MB, 3.5"
1078 CX = 0x4f24; // 80 tracks, 36 sectors
1079 break;
1080
1081 case 14: // 15.6 MB 3.5" (fake)
1082 CX = 0xfe3f; // 255 tracks, 63 sectors
1083 break;
1084
1085 case 15: // 63.5 MB 3.5" (fake)
1086 CX = 0xfeff; // 255 tracks, 255 sectors - This works because the cylinder
1087 break; // and sectors limits/encoding aren't checked by the BIOS
1088 // due to copy protection schemes and such stuff.
1089
1090 default: // ?
1091 BX_PANIC("%s: bad floppy type\n", __func__);
1092 }
1093
1094 /* set es & di to point to 11 byte diskette param table in ROM */
1095 ES = 0xF000; /// @todo any way to make this relocatable?
1096 DI = get_floppy_dpt(drive_type);
1097 CLEAR_CF(); // success
1098 /* disk status not changed upon success */
1099 return;
1100
1101 case 0x15: // read diskette drive type
1102 BX_DEBUG_INT13_FL("floppy f15\n");
1103 drive = GET_ELDL();
1104 if (drive > 1) {
1105 SET_AH(0); // only 2 drives supported
1106 // set_diskette_ret_status here ???
1107 SET_CF();
1108 return;
1109 }
1110 /// @todo break out drive type determination
1111 drive_type = inb_cmos(0x10);
1112 if (drive == 0)
1113 drive_type >>= 4;
1114 else
1115 drive_type &= 0x0f;
1116 CLEAR_CF(); // successful, not present
1117 if (drive_type==0) {
1118 SET_AH(0); // drive not present
1119 } else if (drive_type > 1) {
1120 SET_AH(2); // drive present, supports change line
1121 } else {
1122 SET_AH(1); // drive present, does not support change line
1123 }
1124
1125 return;
1126
1127 case 0x16: // get diskette change line status
1128 BX_DEBUG_INT13_FL("floppy f16\n");
1129 drive = GET_ELDL();
1130 if (drive > 1) {
1131 SET_AH(0x01); // invalid drive
1132 set_diskette_ret_status(0x01);
1133 SET_CF();
1134 return;
1135 }
1136
1137 SET_AH(0x06); // change line not supported
1138 set_diskette_ret_status(0x06);
1139 SET_CF();
1140 return;
1141
1142 case 0x17: // set diskette type for format(old)
1143 BX_DEBUG_INT13_FL("floppy f17\n");
1144 // NOTE: 1.44M diskette not supported by this function, use INT14h/18h instead.
1145 // Drive number (0 or 1) values allowed
1146 drive = GET_ELDL();
1147
1148 // Format type (AL)
1149 // 00 - NOT USED
1150 // 01 - DISKETTE 360K IN 360K DRIVE
1151 // 02 - DISKETTE 360K IN 1.2M DRIVE
1152 // 03 - DISKETTE 1.2M IN 1.2M DRIVE
1153 // 04 - DISKETTE 720K IN 720K DRIVE
1154 val8 = GET_AL();
1155
1156 BX_DEBUG_INT13_FL("floppy f17 - drive: %d, format type: %d\n", drive, val8);
1157
1158 if (drive > 1) {
1159 SET_AH(0x01); // invalid drive
1160 set_diskette_ret_status(0x01); // bad parameter
1161 SET_CF();
1162 return;
1163 }
1164
1165 // see if drive exists
1166 if (floppy_drive_exists(drive) == 0) {
1167 SET_AH(0x80); // not responding/time out
1168 set_diskette_ret_status(0x80);
1169 SET_CF();
1170 return;
1171 }
1172
1173 // Get current drive state. Set 'base_address' to media status offset address
1174 base_address = (drive) ? 0x0091 : 0x0090;
1175 media_state = read_byte(0x0040, base_address);
1176
1177 // Mask out (clear) bits 4-7 (4:media type established, 5:double stepping, 6-7:data rate)
1178 media_state &= 0x0f;
1179
1180 switch (val8) {
1181 case 1:
1182 // 360K media in 360K drive
1183 media_state |= 0x90; // 1001 0000 (media type established, 250 kbps)
1184 break;
1185 case 2:
1186 // 360K media in 1.2M drive
1187 media_state |= 0x70; // 0111 0000 (media type established, double stepping, 300 kbps)
1188 break;
1189 case 3:
1190 // 1.2M media in 1.2M drive
1191 media_state |= 0x10; // 0001 0000 (media type established, 500 kbps)
1192 break;
1193 case 4:
1194 // 720K media in 720K drive
1195 media_state |= 0x90; // 1001 0000 (media type established, 250 kbps)
1196 break;
1197 default:
1198 // bad parameter
1199 SET_AH(0x01); // invalid format mode parameter
1200 set_diskette_ret_status(0x01);
1201 SET_CF();
1202 return;
1203 }
1204
1205 // Update media status
1206 write_byte(0x0040, base_address, media_state);
1207 BX_DEBUG_INT13_FL("floppy f17 - media status set to: %02x\n", media_state);
1208
1209 // return success!
1210 SET_AH(0);
1211 set_diskette_ret_status(0);
1212 CLEAR_CF();
1213 return;
1214
1215 case 0x18: // set diskette type for format(new)
1216 BX_DEBUG_INT13_FL("floppy f18\n");
1217 // Set Media Type for Format. Verifies that the device supports a specific geometry.
1218 // Unlike INT13h/17h, this service supports higher capacity drives (1.44M and 2.88M).
1219 // Drive number (0 or 1) values allowed
1220 drive = GET_ELDL();
1221
1222 val8 = GET_CL();
1223 num_sectors = val8 & 0x3f; // max sector number per cylinder
1224 track = ((val8 >> 6) << 8) + GET_CH(); // max cylinder number (max cylinders - 1)
1225
1226 BX_DEBUG_INT13_FL("floppy f18 - drive: %d, max cylinder/track number: %d, sectors-per-tracks: %d\n",
1227 drive, track, num_sectors);
1228
1229 if (drive > 1) {
1230 SET_AH(0x01); // invalid drive
1231 set_diskette_ret_status(0x01);
1232 SET_CF();
1233 return;
1234 }
1235
1236 // see if drive exists
1237 if (floppy_drive_exists(drive) == 0) {
1238 SET_AH(0x80); // not responding/time out
1239 set_diskette_ret_status(0x80);
1240 SET_CF();
1241 return;
1242 }
1243
1244 // see if media in drive, and media type is known
1245 if (floppy_media_known(drive) == 0) {
1246 if (floppy_media_sense(drive) == 0) {
1247 SET_AH(0x0C); // drive/media type unknown
1248 set_diskette_ret_status(0x0C);
1249 SET_CF();
1250 return;
1251 }
1252 }
1253
1254 /// @todo break out drive type determination
1255 drive_type = inb_cmos(0x10);
1256 if (drive == 0)
1257 drive_type >>= 4;
1258 else
1259 drive_type &= 0x0f;
1260
1261 // Get current drive state. Set 'base_address' to media status offset address
1262 base_address = (drive) ? 0x0091 : 0x0090;
1263 media_state = read_byte(0x0040, base_address);
1264
1265 // Mask out (clear) bits 4-7 (4:media type established, 5:double stepping, 6-7:data rate)
1266 media_state &= 0x0f;
1267
1268 switch (drive_type) {
1269 case 1: // 360KB, 5.25"
1270 if (track == 39 && num_sectors == 9)
1271 media_state |= 0x90; // 1001 0000 (media type established, 250 kbps)
1272
1273 break;
1274 case 2: // 1.2MB, 5.25"
1275 if (track == 39 && num_sectors == 9) { // 360K disk in 1.2M drive
1276 media_state |= 0x70; // 0111 0000 (media type established, double stepping, 300 kbps)
1277 } else if (track == 79 && num_sectors == 15) { // 1.2M disk in 1.2M drive
1278 media_state |= 0x10; // 0001 0000 (media type established, 500 kbps)
1279 }
1280 break;
1281 case 3: // 720KB, 3.5"
1282 if (track == 79 && num_sectors == 9)
1283 media_state |= 0x90; // 1001 0000 (media type established, 250 kbps)
1284
1285 break;
1286 case 4: // 1.44MB, 3.5"
1287 if (track == 79) {
1288 if (num_sectors == 9) { // 720K disk in 1.44M drive
1289 media_state |= 0x90; // 1001 0000 (media type established, 250 kbps)
1290 } else if (num_sectors == 18) { // 1.44M disk in 1.44M drive
1291 media_state |= 0x10; // 0001 0000 (media type established, 500 kbps)
1292 }
1293 }
1294 break;
1295 case 5: // 2.88MB, 3.5"
1296 if (track == 79) {
1297 if (num_sectors == 9) { // 720K disk in 2.88M drive
1298 media_state |= 0x90; // 1001 0000 (media type established, 250 kbps)
1299 } else if (num_sectors == 18) { // 1.44M disk in 2.88M drive
1300 media_state |= 0x10; // 0001 0000 (media type established, 500 kbps)
1301 } else if (num_sectors == 36) { // 2.88M disk in 2.88M drive
1302 media_state |= 0xD0; // 1101 0000 (media type established, 1 Mbps)
1303 }
1304 }
1305 break;
1306 default:
1307 break;
1308 }
1309
1310 // Error if bit 4 (media type established) has not just been set above.
1311 if (((media_state >> 4) & 0x01) == 0) {
1312 // Error - assume requested tracks/sectors-per-track not supported
1313 // for current drive type - or drive type is unknown!
1314 SET_AH(0x0C);
1315 set_diskette_ret_status(0x0C);
1316 SET_CF();
1317 return;
1318 }
1319
1320 // Update media status
1321 write_byte(0x0040, base_address, media_state);
1322
1323 // set es & di to point to 11 byte diskette param table in ROM
1324 ES = 0xF000; /// @todo any way to make this relocatable?
1325 DI = get_floppy_dpt(drive_type);
1326
1327 // return success!
1328 SET_AH(0);
1329 set_diskette_ret_status(0);
1330 CLEAR_CF();
1331 return;
1332
1333 default:
1334 BX_INFO("%s: unsupported AH=%02x\n", __func__, GET_AH());
1335
1336 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
1337 SET_AH(0x01); // ???
1338 set_diskette_ret_status(1);
1339 SET_CF();
1340 return;
1341 // }
1342 }
1343}
1344
1345#else // #if BX_SUPPORT_FLOPPY
1346
1347void BIOSCALL int13_diskette_function(disk_regs_t r)
1348{
1349 uint8_t val8;
1350
1351 switch ( GET_AH() ) {
1352
1353 case 0x01: // Read Diskette Status
1354 CLEAR_CF();
1355 val8 = read_byte(0x0000, 0x0441);
1356 SET_AH(val8);
1357 if (val8) {
1358 SET_CF();
1359 }
1360 return;
1361
1362 default:
1363 SET_CF();
1364 write_byte(0x0000, 0x0441, 0x01);
1365 SET_AH(0x01);
1366 }
1367}
1368
1369#endif // #if BX_SUPPORT_FLOPPY
1370
1371/* Avoid saving general registers already saved by caller (PUSHA). */
1372#pragma aux int13_diskette_function modify [di si cx dx bx];
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette