VirtualBox

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

Last change on this file since 47531 was 47531, checked in by vboxsync, 11 years ago

BIOS: Report floppy change line support.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.0 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 } else if (drive_type > 1) {
1115 SET_AH(2); // drive present, supports change line
1116 } else {
1117 SET_AH(1); // drive present, does not support change line
1118 }
1119
1120 return;
1121
1122 case 0x16: // get diskette change line status
1123 BX_DEBUG_INT13_FL("floppy f16\n");
1124 drive = GET_ELDL();
1125 if (drive > 1) {
1126 SET_AH(0x01); // invalid drive
1127 set_diskette_ret_status(0x01);
1128 SET_CF();
1129 return;
1130 }
1131
1132 SET_AH(0x06); // change line not supported
1133 set_diskette_ret_status(0x06);
1134 SET_CF();
1135 return;
1136
1137 case 0x17: // set diskette type for format(old)
1138 BX_DEBUG_INT13_FL("floppy f17\n");
1139 /* not used for 1.44M floppies */
1140 SET_AH(0x01); // not supported
1141 set_diskette_ret_status(1); /* not supported */
1142 SET_CF();
1143 return;
1144
1145 case 0x18: // set diskette type for format(new)
1146 BX_DEBUG_INT13_FL("floppy f18\n");
1147 SET_AH(0x01); // do later
1148 set_diskette_ret_status(1);
1149 SET_CF();
1150 return;
1151
1152 default:
1153 BX_INFO("%s: unsupported AH=%02x\n", __func__, GET_AH());
1154
1155 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
1156 SET_AH(0x01); // ???
1157 set_diskette_ret_status(1);
1158 SET_CF();
1159 return;
1160 // }
1161 }
1162}
1163
1164#else // #if BX_SUPPORT_FLOPPY
1165
1166void BIOSCALL int13_diskette_function(disk_regs_t r)
1167{
1168 uint8_t val8;
1169
1170 switch ( GET_AH() ) {
1171
1172 case 0x01: // Read Diskette Status
1173 CLEAR_CF();
1174 val8 = read_byte(0x0000, 0x0441);
1175 SET_AH(val8);
1176 if (val8) {
1177 SET_CF();
1178 }
1179 return;
1180
1181 default:
1182 SET_CF();
1183 write_byte(0x0000, 0x0441, 0x01);
1184 SET_AH(0x01);
1185 }
1186}
1187
1188#endif // #if BX_SUPPORT_FLOPPY
1189
1190#if 0
1191void determine_floppy_media(uint16_t drive)
1192{
1193 uint8_t val8, DOR, ctrl_info;
1194
1195 ctrl_info = read_byte(0x0040, 0x008F);
1196 if (drive==1)
1197 ctrl_info >>= 4;
1198 else
1199 ctrl_info &= 0x0f;
1200
1201#if 0
1202 if (drive == 0) {
1203 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
1204 }
1205 else {
1206 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
1207 }
1208#endif
1209
1210 if ( (ctrl_info & 0x04) != 0x04 ) {
1211 // Drive not determined means no drive exists, done.
1212 return;
1213 }
1214
1215#if 0
1216 // check Main Status Register for readiness
1217 val8 = inb(0x03f4) & 0x80; // Main Status Register
1218 if (val8 != 0x80)
1219 BX_PANIC("d_f_m: MRQ bit not set\n");
1220
1221 // change line
1222
1223 // existing BDA values
1224
1225 // turn on drive motor
1226 outb(0x03f2, DOR); // Digital Output Register
1227 //
1228#endif
1229 BX_PANIC("d_f_m: OK so far\n");
1230}
1231#endif
1232
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