VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS-new/floppy.c@ 40754

Last change on this file since 40754 was 38997, checked in by vboxsync, 13 years ago

DMA recompiler bug workaround for floppy, too.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.5 KB
Line 
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.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
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 SET_CF();
732 return;
733 } else {
734 BX_PANIC("%s: read error\n", __func__);
735 }
736 }
737
738 // ??? should track be new val from return_status[3] ?
739 set_diskette_current_cyl(drive, track);
740 // AL = number of sectors read (same value as passed)
741 SET_AH(0x00); // success
742 CLEAR_CF(); // success
743 return;
744 } else { // if (ah == 0x04)
745 // Verify Diskette Sectors
746
747 // ??? should track be new val from return_status[3] ?
748 set_diskette_current_cyl(drive, track);
749 // AL = number of sectors verified (same value as passed)
750 CLEAR_CF(); // success
751 SET_AH(0x00); // success
752 return;
753 }
754 break;
755
756 case 0x05: // format diskette track
757 BX_DEBUG_INT13_FL("floppy f05\n");
758
759 num_sectors = GET_AL();
760 track = GET_CH();
761 head = GET_DH();
762 drive = GET_ELDL();
763
764 if ((drive > 1) || (head > 1) || (track > 79) ||
765 (num_sectors == 0) || (num_sectors > 18)) {
766 SET_AH(1);
767 set_diskette_ret_status(1);
768 SET_CF(); // error occurred
769 }
770
771 // see if drive exists
772 if (floppy_drive_exists(drive) == 0) {
773 SET_AH(0x80); // drive not responding
774 set_diskette_ret_status(0x80);
775 SET_CF(); // error occurred
776 return;
777 }
778
779 // see if media in drive, and type is known
780 if (floppy_media_known(drive) == 0) {
781 if (floppy_media_sense(drive) == 0) {
782 SET_AH(0x0C); // Media type not found
783 set_diskette_ret_status(0x0C);
784 SET_AL(0); // no sectors read
785 SET_CF(); // error occurred
786 return;
787 }
788 }
789
790 // set up DMA controller for transfer
791 // @todo: merge/factor out pointer normalization
792 page = (ES >> 12); // upper 4 bits
793 base_es = (ES << 4); // lower 16bits contributed by ES
794 base_address = base_es + BX; // lower 16 bits of address
795 // contributed by ES:BX
796 if ( base_address < base_es ) {
797 // in case of carry, adjust page by 1
798 page++;
799 }
800 base_count = (num_sectors * 4) - 1;
801
802 // check for 64K boundary overrun
803 last_addr = base_address + base_count;
804 if (last_addr < base_address) {
805 SET_AH(0x09);
806 set_diskette_ret_status(0x09);
807 SET_AL(0); // no sectors read
808 SET_CF(); // error occurred
809 return;
810 }
811
812 outb(0x000a, 0x06);
813 outb(0x000c, 0x00); // clear flip-flop
814 outb(0x0004, base_address);
815 outb(0x0004, base_address>>8);
816 outb(0x000c, 0x00); // clear flip-flop
817 outb(0x0005, base_count);
818 outb(0x0005, base_count>>8);
819 mode_register = 0x4a; // single mode, increment, autoinit disable,
820 // transfer type=read, channel 2
821 outb(0x000b, mode_register);
822 // port 81: DMA-1 Page Register, channel 2
823 outb(0x0081, page);
824 outb(0x000a, 0x02);
825
826 // set up floppy controller for transfer
827 floppy_prepare_controller(drive);
828
829 // send format-track command (6 bytes) to controller
830 outb(0x03f5, 0x4d); // 4d: format track
831 outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
832 outb(0x03f5, 2); // 512 byte sector size
833 outb(0x03f5, num_sectors); // number of sectors per track
834 outb(0x03f5, 0); // Gap length
835 outb(0x03f5, 0xf6); // Fill byte
836 // turn on interrupts
837 int_enable();
838
839 // wait on 40:3e bit 7 to become 1
840 do {
841 val8 = read_byte(0x0040, 0x0040);
842 if (val8 == 0) {
843 floppy_reset_controller();
844 SET_AH(0x80); // drive not ready (timeout)
845 set_diskette_ret_status(0x80);
846 SET_CF(); // error occurred
847 return;
848 }
849 val8 = (read_byte(0x0040, 0x003e) & 0x80);
850 } while ( val8 == 0 );
851
852 val8 = 0; // separate asm from while() loop
853 // turn off interrupts
854 int_disable();
855 // set 40:3e bit 7 to 0
856 val8 = read_byte(0x0040, 0x003e);
857 val8 &= 0x7f;
858 write_byte(0x0040, 0x003e, val8);
859 // check port 3f4 for accessibility to status bytes
860 val8 = inb(0x3f4);
861 if ( (val8 & 0xc0) != 0xc0 )
862 BX_PANIC("%s: ctrl not ready\n", __func__);
863
864 // read 7 return status bytes from controller and store in BDA
865 for (i = 0; i < 7; ++i) {
866 return_status[i] = inb(0x3f5);
867 write_byte(0x0040, 0x0042 + i, return_status[i]);
868 }
869
870 if ( (return_status[0] & 0xc0) != 0 ) {
871 if ( (return_status[1] & 0x02) != 0 ) {
872 // diskette not writable.
873 // AH=status code=0x03 (tried to write on write-protected disk)
874 // AL=number of sectors written=0
875 AX = 0x0300;
876 SET_CF();
877 return;
878 } else {
879 BX_PANIC("%s: write error\n", __func__);
880 }
881 }
882
883 SET_AH(0);
884 set_diskette_ret_status(0);
885 set_diskette_current_cyl(drive, 0);
886 CLEAR_CF(); // successful
887 return;
888
889
890 case 0x08: // read diskette drive parameters
891 BX_DEBUG_INT13_FL("floppy f08\n");
892 drive = GET_ELDL();
893
894 if (drive > 1) {
895 AX = 0;
896 BX = 0;
897 CX = 0;
898 DX = 0;
899 ES = 0;
900 DI = 0;
901 SET_DL(num_floppies);
902 SET_CF();
903 return;
904 }
905
906 // @todo: break out drive type determination
907 drive_type = inb_cmos(0x10);
908 num_floppies = 0;
909 if (drive_type & 0xf0)
910 num_floppies++;
911 if (drive_type & 0x0f)
912 num_floppies++;
913
914 if (drive == 0)
915 drive_type >>= 4;
916 else
917 drive_type &= 0x0f;
918
919 SET_BH(0);
920 SET_BL(drive_type);
921 SET_AH(0);
922 SET_AL(0);
923 SET_DL(num_floppies);
924
925 switch (drive_type) {
926 case 0: // none
927 CX = 0;
928 SET_DH(0); // max head #
929 break;
930
931 case 1: // 360KB, 5.25"
932 CX = 0x2709; // 40 tracks, 9 sectors
933 SET_DH(1); // max head #
934 break;
935
936 case 2: // 1.2MB, 5.25"
937 CX = 0x4f0f; // 80 tracks, 15 sectors
938 SET_DH(1); // max head #
939 break;
940
941 case 3: // 720KB, 3.5"
942 CX = 0x4f09; // 80 tracks, 9 sectors
943 SET_DH(1); // max head #
944 break;
945
946 case 4: // 1.44MB, 3.5"
947 CX = 0x4f12; // 80 tracks, 18 sectors
948 SET_DH(1); // max head #
949 break;
950
951 case 5: // 2.88MB, 3.5"
952 CX = 0x4f24; // 80 tracks, 36 sectors
953 SET_DH(1); // max head #
954 break;
955
956 case 6: // 160k, 5.25"
957 CX = 0x2708; // 40 tracks, 8 sectors
958 SET_DH(0); // max head #
959 break;
960
961 case 7: // 180k, 5.25"
962 CX = 0x2709; // 40 tracks, 9 sectors
963 SET_DH(0); // max head #
964 break;
965
966 case 8: // 320k, 5.25"
967 CX = 0x2708; // 40 tracks, 8 sectors
968 SET_DH(1); // max head #
969 break;
970
971 default: // ?
972 BX_PANIC("%s: bad floppy type\n", __func__);
973 }
974
975 /* set es & di to point to 11 byte diskette param table in ROM */
976 ES = 0xF000; // @todo: any way to make this relocatable?
977 DI = (uint16_t)&diskette_param_table;
978 CLEAR_CF(); // success
979 /* disk status not changed upon success */
980 return;
981
982 case 0x15: // read diskette drive type
983 BX_DEBUG_INT13_FL("floppy f15\n");
984 drive = GET_ELDL();
985 if (drive > 1) {
986 SET_AH(0); // only 2 drives supported
987 // set_diskette_ret_status here ???
988 SET_CF();
989 return;
990 }
991 // @todo: break out drive type determination
992 drive_type = inb_cmos(0x10);
993 if (drive == 0)
994 drive_type >>= 4;
995 else
996 drive_type &= 0x0f;
997 CLEAR_CF(); // successful, not present
998 if (drive_type==0) {
999 SET_AH(0); // drive not present
1000 }
1001 else {
1002 SET_AH(1); // drive present, does not support change line
1003 }
1004
1005 return;
1006
1007 case 0x16: // get diskette change line status
1008 BX_DEBUG_INT13_FL("floppy f16\n");
1009 drive = GET_ELDL();
1010 if (drive > 1) {
1011 SET_AH(0x01); // invalid drive
1012 set_diskette_ret_status(0x01);
1013 SET_CF();
1014 return;
1015 }
1016
1017 SET_AH(0x06); // change line not supported
1018 set_diskette_ret_status(0x06);
1019 SET_CF();
1020 return;
1021
1022 case 0x17: // set diskette type for format(old)
1023 BX_DEBUG_INT13_FL("floppy f17\n");
1024 /* not used for 1.44M floppies */
1025 SET_AH(0x01); // not supported
1026 set_diskette_ret_status(1); /* not supported */
1027 SET_CF();
1028 return;
1029
1030 case 0x18: // set diskette type for format(new)
1031 BX_DEBUG_INT13_FL("floppy f18\n");
1032 SET_AH(0x01); // do later
1033 set_diskette_ret_status(1);
1034 SET_CF();
1035 return;
1036
1037 default:
1038 BX_INFO("%s: unsupported AH=%02x\n", __func__, GET_AH());
1039
1040 // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
1041 SET_AH(0x01); // ???
1042 set_diskette_ret_status(1);
1043 SET_CF();
1044 return;
1045 // }
1046 }
1047}
1048
1049#else // #if BX_SUPPORT_FLOPPY
1050
1051void BIOSCALL int13_diskette_function(disk_regs_t r)
1052{
1053 uint8_t val8;
1054
1055 switch ( GET_AH() ) {
1056
1057 case 0x01: // Read Diskette Status
1058 CLEAR_CF();
1059 val8 = read_byte(0x0000, 0x0441);
1060 SET_AH(val8);
1061 if (val8) {
1062 SET_CF();
1063 }
1064 return;
1065
1066 default:
1067 SET_CF();
1068 write_byte(0x0000, 0x0441, 0x01);
1069 SET_AH(0x01);
1070 }
1071}
1072
1073#endif // #if BX_SUPPORT_FLOPPY
1074
1075#if 0
1076void determine_floppy_media(uint16_t drive)
1077{
1078 uint8_t val8, DOR, ctrl_info;
1079
1080 ctrl_info = read_byte(0x0040, 0x008F);
1081 if (drive==1)
1082 ctrl_info >>= 4;
1083 else
1084 ctrl_info &= 0x0f;
1085
1086#if 0
1087 if (drive == 0) {
1088 DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
1089 }
1090 else {
1091 DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
1092 }
1093#endif
1094
1095 if ( (ctrl_info & 0x04) != 0x04 ) {
1096 // Drive not determined means no drive exists, done.
1097 return;
1098 }
1099
1100#if 0
1101 // check Main Status Register for readiness
1102 val8 = inb(0x03f4) & 0x80; // Main Status Register
1103 if (val8 != 0x80)
1104 BX_PANIC("d_f_m: MRQ bit not set\n");
1105
1106 // change line
1107
1108 // existing BDA values
1109
1110 // turn on drive motor
1111 outb(0x03f2, DOR); // Digital Output Register
1112 //
1113#endif
1114 BX_PANIC("d_f_m: OK so far\n");
1115}
1116#endif
1117
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use