VirtualBox

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

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