VirtualBox

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

最後變更 在這個檔案從69498是 69498,由 vboxsync 提交於 7 年 前

backed out r118835 as it incorrectly updated the 'This file is based on' file headers.

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

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