VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use