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] = NULL32;
658     // Hard Disk 1 Drive Parameter Table Address
659     ((PULONG)BaseAddress)[0x46] = NULL32;
660 
661     /* Relocated services by the BIOS (when needed) */
662     ((PULONG)BaseAddress)[0x40] = NULL32; // 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 */
706