[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / bios / bios32 / dskbios32.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dskbios32.c
5 * PURPOSE: VDM 32-bit Disk BIOS
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "ntvdm.h"
14 #include "emulator.h"
15 // #include "../../memory.h"
16 // #include "cpu/bop.h"
17 #include "cpu/cpu.h" // for EMULATOR_FLAG_ZF
18 #include "int32.h"
19
20 #include "dskbios32.h"
21 // #include <bios/dskbios.h>
22 #include "bios32p.h"
23
24 #include "hardware/disk.h"
25
26
27 /* DEFINES ********************************************************************/
28
29 // Disks which are currently supported by the BIOS Disk module.
30 // NOTE: For the current implementation those are arrays of pointers to
31 // DISK_IMAGEs maintained by the Generic Disk Controller. In the future
32 // they will be arrays of objects containing disk information needed by
33 // the BIOS only.
34 static PDISK_IMAGE FloppyDrive[2] = {NULL};
35 static PDISK_IMAGE HardDrive[1] = {NULL};
36
37 #pragma pack(push, 1)
38
39 // See: http://www.ctyme.com/intr/rb-2445.htm
40 typedef struct _FLOPPY_PARAM_TABLE
41 {
42 BYTE Unused0;
43 BYTE Unused1;
44 BYTE MotorOffDelay;
45 BYTE SectorSize;
46 BYTE SectorsPerTrack;
47 BYTE SectorGapLength;
48 BYTE DataLength;
49 BYTE FormatGapLength;
50 BYTE FormatFillByte;
51 BYTE HeadSettleTime;
52 BYTE MotorStartTime;
53 } FLOPPY_PARAM_TABLE, *PFLOPPY_PARAM_TABLE;
54
55 typedef struct _FLOPPY_PARAM_TABLE_EX
56 {
57 FLOPPY_PARAM_TABLE FloppyParamTable;
58
59 // IBM Additions
60 BYTE MaxTrackNumber;
61 BYTE DataTransferRate;
62 BYTE CmosDriveType;
63 } FLOPPY_PARAM_TABLE_EX, *PFLOPPY_PARAM_TABLE_EX;
64
65 #pragma pack(pop)
66
67 // Parameters for 1.44 MB Floppy, taken from SeaBIOS
68
69 #define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
70 #define FLOPPY_DATALEN 0xFF // Not used - because size code is 0x02
71 #define FLOPPY_MOTOR_TICKS 37 // ~2 seconds
72 #define FLOPPY_FILLBYTE 0xF6
73 #define FLOPPY_GAPLEN 0x1B
74 #define FLOPPY_FORMAT_GAPLEN 0x6C
75
76 static const FLOPPY_PARAM_TABLE_EX FloppyParamTable =
77 {
78 // PC-AT compatible table
79 {
80 0xAF, // step rate 12ms, head unload 240ms
81 0x02, // head load time 4ms, DMA used
82 FLOPPY_MOTOR_TICKS, // ~2 seconds
83 FLOPPY_SIZE_CODE,
84 18,
85 FLOPPY_GAPLEN,
86 FLOPPY_DATALEN,
87 FLOPPY_FORMAT_GAPLEN,
88 FLOPPY_FILLBYTE,
89 0x0F, // 15ms
90 0x08, // 1 second
91 },
92
93 // IBM Additions
94 79, // maximum track
95 0, // data transfer rate
96 4, // drive type in CMOS
97 };
98
99
100 #pragma pack(push, 1)
101
102 // See: http://www.ctyme.com/intr/rb-6135.htm
103 typedef struct _HARDDISK_PARAM_TABLE
104 {
105 WORD Cylinders;
106 BYTE Heads;
107 WORD Unused0;
108 WORD Unused1;
109 BYTE Unused2;
110 BYTE Control;
111 BYTE StandardTimeout;
112 BYTE FormatTimeout;
113 BYTE CheckingTimeout;
114 WORD LandZoneCylinder;
115 BYTE SectorsPerTrack;
116 BYTE Reserved;
117 } HARDDISK_PARAM_TABLE, *PHARDDISK_PARAM_TABLE;
118
119 #pragma pack(pop)
120
121 // static const HARDDISK_PARAM_TABLE HardDiskParamTable =
122 // {0};
123
124
125 /* PRIVATE FUNCTIONS **********************************************************/
126
127 static PDISK_IMAGE
128 GetDisk(IN BYTE DiskNumber)
129 {
130 if (DiskNumber & 0x80)
131 {
132 DiskNumber &= ~0x80;
133
134 if (DiskNumber >= ARRAYSIZE(HardDrive))
135 {
136 DPRINT1("GetDisk: HDD number 0x%02X invalid\n", DiskNumber | 0x80);
137 return NULL;
138 }
139
140 return HardDrive[DiskNumber];
141 }
142 else
143 {
144 if (DiskNumber >= ARRAYSIZE(FloppyDrive))
145 {
146 DPRINT1("GetDisk: Floppy number 0x%02X invalid\n", DiskNumber);
147 return NULL;
148 }
149
150 return FloppyDrive[DiskNumber];
151 }
152 }
153
154 static VOID
155 AllDisksReset(VOID)
156 {
157 Bda->LastDisketteOperation = 0;
158 Bda->LastDiskOperation = 0;
159 }
160
161 /*static*/
162 VOID WINAPI BiosDiskService(LPWORD Stack)
163 {
164 BYTE Drive;
165 PDISK_IMAGE DiskImage;
166
167 switch (getAH())
168 {
169 /* Disk -- Reset Disk System */
170 case 0x00:
171 {
172 Drive = getDL();
173
174 if (Drive & 0x80)
175 {
176 AllDisksReset();
177
178 /* Return success */
179 setAH(0x00);
180 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
181 break;
182 }
183
184 Drive &= ~0x80;
185
186 if (Drive >= ARRAYSIZE(FloppyDrive))
187 {
188 DPRINT1("BiosDiskService(0x00): Drive number 0x%02X invalid\n", Drive);
189
190 /* Return error */
191 setAH(0x01);
192 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
193 break;
194 }
195
196 // TODO: Reset drive
197
198 /* Return success */
199 setAH(0x00);
200 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
201 break;
202 }
203
204 /* Disk -- Get Status of Last Operation */
205 case 0x01:
206 {
207 BYTE LastOperationStatus = 0x00;
208
209 Drive = getDL();
210 DiskImage = GetDisk(Drive);
211 if (!DiskImage || !IsDiskPresent(DiskImage))
212 {
213 DPRINT1("BiosDiskService(0x01): Disk number 0x%02X invalid\n", Drive);
214
215 /* Return error */
216 setAH(0x01);
217 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
218 break;
219 }
220
221 LastOperationStatus = DiskImage->LastOperationStatus;
222
223 if (Drive & 0x80)
224 Bda->LastDiskOperation = LastOperationStatus;
225 else
226 Bda->LastDisketteOperation = LastOperationStatus;
227
228 /* Return last error */
229 setAH(LastOperationStatus);
230 if (LastOperationStatus == 0x00)
231 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
232 else
233 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
234
235 break;
236 }
237
238 /* Disk -- Read Sectors into Memory */
239 case 0x02:
240 {
241 BYTE Status;
242 BYTE Head = getDH();
243 BYTE NumSectors = getAL();
244
245 // CH: Low eight bits of cylinder number
246 // CL: High two bits of cylinder (bits 6-7, hard disk only)
247 WORD Cylinder = MAKEWORD(getCH(), (getCL() >> 6) & 0x02);
248
249 // CL: Sector number 1-63 (bits 0-5)
250 BYTE Sector = (getCL() & 0x3F); // 1-based
251
252 Drive = getDL();
253 DiskImage = GetDisk(Drive);
254 if (!DiskImage || !IsDiskPresent(DiskImage))
255 {
256 DPRINT1("BiosDiskService(0x02): Disk number 0x%02X invalid\n", Drive);
257
258 /* Return error */
259 setAH(0x01);
260 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
261 break;
262 }
263
264 /* Read the sectors */
265 Status = ReadDisk(DiskImage, Cylinder, Head, Sector, NumSectors);
266 if (Status == 0x00)
267 {
268 /* Return success */
269 setAH(0x00);
270 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
271 }
272 else
273 {
274 DPRINT1("BiosDiskService(0x02): Error when reading from disk number 0x%02X (0x%02X)\n", Drive, Status);
275
276 /* Return error */
277 setAH(Status);
278 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
279 }
280
281 break;
282 }
283
284 /* Disk -- Write Disk Sectors */
285 case 0x03:
286 {
287 BYTE Status;
288 BYTE Head = getDH();
289 BYTE NumSectors = getAL();
290
291 // CH: Low eight bits of cylinder number
292 // CL: High two bits of cylinder (bits 6-7, hard disk only)
293 WORD Cylinder = MAKEWORD(getCH(), (getCL() >> 6) & 0x02);
294
295 // CL: Sector number 1-63 (bits 0-5)
296 BYTE Sector = (getCL() & 0x3F); // 1-based
297
298 Drive = getDL();
299 DiskImage = GetDisk(Drive);
300 if (!DiskImage || !IsDiskPresent(DiskImage))
301 {
302 DPRINT1("BiosDiskService(0x03): Disk number 0x%02X invalid\n", Drive);
303
304 /* Return error */
305 setAH(0x01);
306 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
307 break;
308 }
309
310 /* Write the sectors */
311 Status = WriteDisk(DiskImage, Cylinder, Head, Sector, NumSectors);
312 if (Status == 0x00)
313 {
314 /* Return success */
315 setAH(0x00);
316 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
317 }
318 else
319 {
320 DPRINT1("BiosDiskService(0x03): Error when writing to disk number 0x%02X (0x%02X)\n", Drive, Status);
321
322 /* Return error */
323 setAH(Status);
324 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
325 }
326
327 break;
328 }
329
330 /* Disk -- Verify Disk Sectors */
331 case 0x04:
332
333 /* Floppy/Fixed Disk -- Format Track */
334 case 0x05:
335
336 /* Fixed Disk -- Format Track and Set Bad Sector Flags */
337 case 0x06:
338
339 /* Fixed Disk -- Format Drive starting at Given Track */
340 case 0x07:
341 goto Default;
342
343 /* Disk -- Get Drive Parameters */
344 case 0x08:
345 {
346 WORD MaxCylinders;
347 BYTE MaxHeads;
348 BYTE PresentDrives = 0;
349 BYTE i;
350
351 Drive = getDL();
352 DiskImage = GetDisk(Drive);
353 if (!DiskImage || !IsDiskPresent(DiskImage))
354 {
355 DPRINT1("BiosDiskService(0x08): Disk number 0x%02X invalid\n", Drive);
356
357 /* Return error */
358 setAH(0x01);
359 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
360 break;
361 }
362
363 // Minus 2 because it's the maximum cylinder number (not count),
364 // and the last cylinder is reserved (for compatibility with BIOSes
365 // which reserve it for testing purposes).
366 MaxCylinders = DiskImage->DiskInfo.Cylinders - 2;
367 // Minus 1 because it's the maximum head number (not count).
368 MaxHeads = DiskImage->DiskInfo.Heads - 1;
369
370 // CL: Sector number 1-63 (bits 0-5)
371 // High two bits of cylinder (bits 6-7, hard disk only)
372 setCL((DiskImage->DiskInfo.Sectors & 0x3F) |
373 ((HIBYTE(MaxCylinders) & 0x02) << 6));
374 // CH: Low eight bits of cylinder number
375 setCH(LOBYTE(MaxCylinders));
376
377 setDH(MaxHeads);
378
379 if (Drive & 0x80)
380 {
381 /* Count the number of active HDDs */
382 for (i = 0; i < ARRAYSIZE(HardDrive); ++i)
383 {
384 if (IsDiskPresent(HardDrive[i]))
385 ++PresentDrives;
386 }
387
388 /* Reset ES:DI to NULL */
389 // FIXME: NONONO!! Apps expect (for example, MS-DOS kernel)
390 // that this function does not modify ES:DI if it was called
391 // for a HDD.
392 // setES(0x0000);
393 // setDI(0x0000);
394 }
395 else
396 {
397 /* Count the number of active floppies */
398 for (i = 0; i < ARRAYSIZE(FloppyDrive); ++i)
399 {
400 if (IsDiskPresent(FloppyDrive[i]))
401 ++PresentDrives;
402 }
403
404 /* ES:DI points to the floppy parameter table */
405 setES(HIWORD(((PULONG)BaseAddress)[0x1E]));
406 setDI(LOWORD(((PULONG)BaseAddress)[0x1E]));
407 }
408 setDL(PresentDrives);
409
410 setBL(DiskImage->DiskType); // DiskGeometryList[DiskImage->DiskType].biosval
411 setAL(0x00);
412
413 /* Return success */
414 setAH(0x00);
415 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
416 break;
417 }
418
419 /* Hard Disk -- Initialize Controller with Drive Parameters */
420 case 0x09:
421
422 /* Hard Disk -- Read Long Sectors */
423 case 0x0A:
424
425 /* Hard Disk -- Write Long Sectors */
426 case 0x0B:
427 goto Default;
428
429 /* Hard Disk -- Seek to Cylinder */
430 case 0x0C:
431 {
432 BYTE Status;
433 BYTE Head = getDH();
434
435 // CH: Low eight bits of cylinder number
436 // CL: High two bits of cylinder (bits 6-7, hard disk only)
437 WORD Cylinder = MAKEWORD(getCH(), (getCL() >> 6) & 0x02);
438
439 // CL: Sector number 1-63 (bits 0-5)
440 BYTE Sector = (getCL() & 0x3F); // 1-based
441
442 Drive = getDL();
443 if (!(Drive & 0x80))
444 {
445 DPRINT1("BiosDiskService(0x0C): Disk number 0x%02X is not a HDD\n", Drive);
446
447 /* Return error */
448 setAH(0x01);
449 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
450 break;
451 }
452
453 DiskImage = GetDisk(Drive);
454 if (!DiskImage || !IsDiskPresent(DiskImage))
455 {
456 DPRINT1("BiosDiskService(0x0C): Disk number 0x%02X invalid\n", Drive);
457
458 /* Return error */
459 setAH(0x01);
460 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
461 break;
462 }
463
464 /* Set position */
465 Status = SeekDisk(DiskImage, Cylinder, Head, Sector);
466 if (Status == 0x00)
467 {
468 /* Return success */
469 setAH(0x00);
470 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
471 }
472 else
473 {
474 DPRINT1("BiosDiskService(0x0C): Error when seeking in disk number 0x%02X (0x%02X)\n", Drive, Status);
475
476 /* Return error */
477 setAH(Status);
478 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
479 }
480
481 break;
482 }
483
484 /* Hard Disk -- Reset Hard Disks */
485 case 0x0D:
486 {
487 // FIXME: Should do what 0x11 does.
488 UNIMPLEMENTED;
489 }
490
491 /* Hard Disk -- Read Sector Buffer (XT only) */
492 case 0x0E:
493
494 /* Hard Disk -- Write Sector Buffer (XT only) */
495 case 0x0F:
496 goto Default;
497
498 /* Hard Disk -- Check if Drive is ready */
499 case 0x10:
500 {
501 Drive = getDL();
502 if (!(Drive & 0x80))
503 {
504 DPRINT1("BiosDiskService(0x10): Disk number 0x%02X is not a HDD\n", Drive);
505
506 /* Return error */
507 setAH(0x01);
508 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
509 break;
510 }
511
512 DiskImage = GetDisk(Drive);
513 if (!DiskImage || !IsDiskPresent(DiskImage))
514 {
515 DPRINT1("BiosDiskService(0x10): Disk number 0x%02X invalid\n", Drive);
516
517 /* Return error */
518 setAH(0x01);
519 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
520 break;
521 }
522
523 /* Return success */
524 setAH(0x00);
525 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
526 break;
527 }
528
529 /* Hard Disk -- Recalibrate Drive */
530 case 0x11:
531 {
532 BYTE Status;
533
534 Drive = getDL();
535 if (!(Drive & 0x80))
536 {
537 DPRINT1("BiosDiskService(0x11): Disk number 0x%02X is not a HDD\n", Drive);
538
539 /* Return error */
540 setAH(0x01);
541 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
542 break;
543 }
544
545 DiskImage = GetDisk(Drive);
546 if (!DiskImage || !IsDiskPresent(DiskImage))
547 {
548 DPRINT1("BiosDiskService(0x11): Disk number 0x%02X invalid\n", Drive);
549
550 /* Return error */
551 setAH(0x01);
552 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
553 break;
554 }
555
556 /* Set position to zero */
557 Status = SeekDisk(DiskImage, /*Cylinder*/ 0, /*Head*/ 0, /*Sector*/ 1);
558 if (Status == 0x00)
559 {
560 /* Return success */
561 setAH(0x00);
562 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
563 }
564 else
565 {
566 DPRINT1("BiosDiskService(0x11): Error when recalibrating disk number 0x%02X (0x%02X)\n", Drive, Status);
567
568 /* Return error */
569 setAH(Status);
570 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
571 }
572
573 break;
574 }
575
576 /* Hard Disk -- Controller RAM Diagnostic */
577 case 0x12:
578
579 /* Hard Disk -- Drive Diagnostic */
580 case 0x13:
581
582 /* Hard Disk -- Controller Internal Diagnostic */
583 case 0x14:
584 goto Default;
585
586 /* Disk -- Get Disk Type */
587 case 0x15:
588 {
589 Drive = getDL();
590 DiskImage = GetDisk(Drive);
591 if (!DiskImage || !IsDiskPresent(DiskImage))
592 {
593 DPRINT1("BiosDiskService(0x15): Disk number 0x%02X invalid\n", Drive);
594
595 /* Return error */
596 setAH(0x01);
597 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
598 break;
599 }
600
601 if (Drive & 0x80)
602 {
603 ULONG NumSectors;
604
605 /* Hard disk */
606 setAH(0x03);
607
608 /* Number of 512-byte sectors in CX:DX */
609 NumSectors = DiskImage->DiskInfo.Cylinders * DiskImage->DiskInfo.Heads
610 * DiskImage->DiskInfo.Sectors;
611 setCX(HIWORD(NumSectors));
612 setDX(LOWORD(NumSectors));
613 }
614 else
615 {
616 /* Floppy */
617 setAH(0x01);
618 }
619
620 /* Return success */
621 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
622 break;
623 }
624
625 /* Floppy Disk -- Detect Disk Change */
626 case 0x16:
627
628 /* Floppy Disk -- Set Disk Type for Format */
629 case 0x17:
630
631 /* Disk -- Set Media Type for Format */
632 case 0x18:
633 goto Default;
634
635 default: Default:
636 {
637 DPRINT1("BIOS Function INT 13h, AH = 0x%02X, AL = 0x%02X, BH = 0x%02X NOT IMPLEMENTED\n",
638 getAH(), getAL(), getBH());
639 }
640 }
641 }
642
643 /* PUBLIC FUNCTIONS ***********************************************************/
644
645 VOID DiskBios32Post(VOID)
646 {
647 /*
648 * Initialize BIOS Disk RAM dynamic data
649 */
650
651 /* Some vectors are in fact addresses to tables */
652 // Diskette Parameters
653 ((PULONG)BaseAddress)[0x1E] = MAKELONG(0xEFC7, BIOS_SEGMENT);
654 // Hard Disk 0 Parameter Table Address
655 ((PULONG)BaseAddress)[0x41] = (ULONG)NULL;
656 // Hard Disk 1 Drive Parameter Table Address
657 ((PULONG)BaseAddress)[0x46] = (ULONG)NULL;
658
659 /* Relocated services by the BIOS (when needed) */
660 ((PULONG)BaseAddress)[0x40] = (ULONG)NULL; // ROM BIOS Diskette Handler relocated by Hard Disk BIOS
661 // RegisterBiosInt32(0x40, NULL); // ROM BIOS Diskette Handler relocated by Hard Disk BIOS
662
663 /* Register the BIOS 32-bit Interrupts */
664 RegisterBiosInt32(BIOS_DISK_INTERRUPT, BiosDiskService);
665
666 /* Initialize the BDA */
667 // Bda->LastDisketteOperation = 0;
668 // Bda->LastDiskOperation = 0;
669 AllDisksReset();
670 }
671
672 BOOLEAN DiskBios32Initialize(VOID)
673 {
674 /*
675 * Initialize BIOS Disk ROM static data
676 */
677
678 /* Floppy Parameter Table */
679 RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xEFC7),
680 &FloppyParamTable.FloppyParamTable,
681 sizeof(FloppyParamTable.FloppyParamTable));
682
683 //
684 // FIXME: Must be done by HW floppy controller!
685 //
686
687 /* Detect and initialize the supported disks */
688 // TODO: the "Detect" part is missing.
689 FloppyDrive[0] = &XDCFloppyDrive[0];
690 FloppyDrive[1] = &XDCFloppyDrive[1];
691 HardDrive[0] = &XDCHardDrive[0];
692
693 return TRUE;
694 }
695
696 VOID DiskBios32Cleanup(VOID)
697 {
698 }
699
700 /* EOF */