VirtualBox

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

Last change on this file since 47036 was 47036, checked in by vboxsync, 12 years ago

BIOS,DevFdc,DrvBlock: The floppy controller should query the block/host driver for the drive type, or it'll all go real bad if the guest code trusts CMOS/BIOS drive info. Changed the block driver to automatically upgrade the drive type (for fdc and bios/cmos setup only) if the image is larger than the configured drive capacity. Introduced two fake drive types with max capacities, 15.6 MB and 63.5 MB, the first is the max that INT13 can officially access, the second is reinterpreting CL as holding an 8-bit wide sector number and no cylinder bits (which actually seems to be how real bioses work with floppies).

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