VirtualBox

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

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

Converted system BIOS to Watcom C.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use