VirtualBox

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

最後變更 在這個檔案從43804是 43676,由 vboxsync 提交於 12 年 前

BIOS: Don't panic if floppy write fails.

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

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