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