/* * COPYRIGHT: GPL - See COPYING in the top level directory * PROJECT: ReactOS Virtual DOS Machine * FILE: dskbios32.c * PURPOSE: VDM 32-bit Disk BIOS * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) */ /* INCLUDES *******************************************************************/ #include "ntvdm.h" #define NDEBUG #include #include "emulator.h" // #include "../../memory.h" // #include "cpu/bop.h" #include "cpu/cpu.h" // for EMULATOR_FLAG_ZF #include "int32.h" #include "dskbios32.h" // #include #include "bios32p.h" #include "hardware/disk.h" /* DEFINES ********************************************************************/ // Disks which are currently supported by the BIOS Disk module. // NOTE: For the current implementation those are arrays of pointers to // DISK_IMAGEs maintained by the Generic Disk Controller. In the future // they will be arrays of objects containing disk information needed by // the BIOS only. static PDISK_IMAGE FloppyDrive[2] = {NULL}; static PDISK_IMAGE HardDrive[4] = {NULL}; #pragma pack(push, 1) // See: http://www.ctyme.com/intr/rb-2445.htm typedef struct _FLOPPY_PARAM_TABLE { BYTE Unused0; BYTE Unused1; BYTE MotorOffDelay; BYTE SectorSize; BYTE SectorsPerTrack; BYTE SectorGapLength; BYTE DataLength; BYTE FormatGapLength; BYTE FormatFillByte; BYTE HeadSettleTime; BYTE MotorStartTime; } FLOPPY_PARAM_TABLE, *PFLOPPY_PARAM_TABLE; typedef struct _FLOPPY_PARAM_TABLE_EX { FLOPPY_PARAM_TABLE FloppyParamTable; // IBM Additions BYTE MaxTrackNumber; BYTE DataTransferRate; BYTE CmosDriveType; } FLOPPY_PARAM_TABLE_EX, *PFLOPPY_PARAM_TABLE_EX; #pragma pack(pop) // Parameters for 1.44 MB Floppy, taken from SeaBIOS #define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors #define FLOPPY_DATALEN 0xFF // Not used - because size code is 0x02 #define FLOPPY_MOTOR_TICKS 37 // ~2 seconds #define FLOPPY_FILLBYTE 0xF6 #define FLOPPY_GAPLEN 0x1B #define FLOPPY_FORMAT_GAPLEN 0x6C static const FLOPPY_PARAM_TABLE_EX FloppyParamTable = { // PC-AT compatible table { 0xAF, // step rate 12ms, head unload 240ms 0x02, // head load time 4ms, DMA used FLOPPY_MOTOR_TICKS, // ~2 seconds FLOPPY_SIZE_CODE, 18, FLOPPY_GAPLEN, FLOPPY_DATALEN, FLOPPY_FORMAT_GAPLEN, FLOPPY_FILLBYTE, 0x0F, // 15ms 0x08, // 1 second }, // IBM Additions 79, // maximum track 0, // data transfer rate 4, // drive type in CMOS }; #pragma pack(push, 1) // See: http://www.ctyme.com/intr/rb-6135.htm typedef struct _HARDDISK_PARAM_TABLE { WORD Cylinders; BYTE Heads; WORD Unused0; WORD Unused1; BYTE Unused2; BYTE Control; BYTE StandardTimeout; BYTE FormatTimeout; BYTE CheckingTimeout; WORD LandZoneCylinder; BYTE SectorsPerTrack; BYTE Reserved; } HARDDISK_PARAM_TABLE, *PHARDDISK_PARAM_TABLE; #pragma pack(pop) // static const HARDDISK_PARAM_TABLE HardDiskParamTable = // {0}; /* PRIVATE FUNCTIONS **********************************************************/ static PDISK_IMAGE GetDisk(IN BYTE DiskNumber) { if (DiskNumber & 0x80) { DiskNumber &= ~0x80; if (DiskNumber >= ARRAYSIZE(HardDrive)) { DPRINT1("GetDisk: HDD number 0x%02X invalid\n", DiskNumber | 0x80); return NULL; } return HardDrive[DiskNumber]; } else { if (DiskNumber >= ARRAYSIZE(FloppyDrive)) { DPRINT1("GetDisk: Floppy number 0x%02X invalid\n", DiskNumber); return NULL; } return FloppyDrive[DiskNumber]; } } static VOID AllDisksReset(VOID) { Bda->LastDisketteOperation = 0; Bda->LastDiskOperation = 0; } /*static*/ VOID WINAPI BiosDiskService(LPWORD Stack) { BYTE Drive; PDISK_IMAGE DiskImage; switch (getAH()) { /* Disk -- Reset Disk System */ case 0x00: { Drive = getDL(); if (Drive & 0x80) { AllDisksReset(); /* Return success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } Drive &= ~0x80; if (Drive >= ARRAYSIZE(FloppyDrive)) { DPRINT1("BiosDiskService(0x00): Drive number 0x%02X invalid\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } // TODO: Reset drive /* Return success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Disk -- Get Status of Last Operation */ case 0x01: { BYTE LastOperationStatus = 0x00; Drive = getDL(); DiskImage = GetDisk(Drive); if (!DiskImage || !IsDiskPresent(DiskImage)) { DPRINT1("BiosDiskService(0x01): Disk number 0x%02X invalid\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } LastOperationStatus = DiskImage->LastOperationStatus; if (Drive & 0x80) Bda->LastDiskOperation = LastOperationStatus; else Bda->LastDisketteOperation = LastOperationStatus; /* Return last error */ setAH(LastOperationStatus); if (LastOperationStatus == 0x00) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; else Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Disk -- Read Sectors into Memory */ case 0x02: { BYTE Status; BYTE Head = getDH(); BYTE NumSectors = getAL(); // CH: Low eight bits of cylinder number // CL: High two bits of cylinder (bits 6-7, hard disk only) WORD Cylinder = MAKEWORD(getCH(), (getCL() >> 6) & 0x02); // CL: Sector number 1-63 (bits 0-5) BYTE Sector = (getCL() & 0x3F); // 1-based Drive = getDL(); DiskImage = GetDisk(Drive); if (!DiskImage || !IsDiskPresent(DiskImage)) { DPRINT1("BiosDiskService(0x02): Disk number 0x%02X invalid\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Read the sectors */ Status = ReadDisk(DiskImage, Cylinder, Head, Sector, NumSectors); if (Status == 0x00) { /* Return success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; } else { DPRINT1("BiosDiskService(0x02): Error when reading from disk number 0x%02X (0x%02X)\n", Drive, Status); /* Return error */ setAH(Status); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; } break; } /* Disk -- Write Disk Sectors */ case 0x03: { BYTE Status; BYTE Head = getDH(); BYTE NumSectors = getAL(); // CH: Low eight bits of cylinder number // CL: High two bits of cylinder (bits 6-7, hard disk only) WORD Cylinder = MAKEWORD(getCH(), (getCL() >> 6) & 0x02); // CL: Sector number 1-63 (bits 0-5) BYTE Sector = (getCL() & 0x3F); // 1-based Drive = getDL(); DiskImage = GetDisk(Drive); if (!DiskImage || !IsDiskPresent(DiskImage)) { DPRINT1("BiosDiskService(0x03): Disk number 0x%02X invalid\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Write the sectors */ Status = WriteDisk(DiskImage, Cylinder, Head, Sector, NumSectors); if (Status == 0x00) { /* Return success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; } else { DPRINT1("BiosDiskService(0x03): Error when writing to disk number 0x%02X (0x%02X)\n", Drive, Status); /* Return error */ setAH(Status); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; } break; } /* Disk -- Verify Disk Sectors */ case 0x04: /* Floppy/Fixed Disk -- Format Track */ case 0x05: /* Fixed Disk -- Format Track and Set Bad Sector Flags */ case 0x06: /* Fixed Disk -- Format Drive starting at Given Track */ case 0x07: goto Default; /* Disk -- Get Drive Parameters */ case 0x08: { WORD MaxCylinders; BYTE MaxHeads; BYTE PresentDrives = 0; BYTE i; Drive = getDL(); DiskImage = GetDisk(Drive); if (!DiskImage || !IsDiskPresent(DiskImage)) { DPRINT1("BiosDiskService(0x08): Disk number 0x%02X invalid\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } // Minus 2 because it's the maximum cylinder number (not count), // and the last cylinder is reserved (for compatibility with BIOSes // which reserve it for testing purposes). MaxCylinders = DiskImage->DiskInfo.Cylinders - 2; // Minus 1 because it's the maximum head number (not count). MaxHeads = DiskImage->DiskInfo.Heads - 1; // CL: Sector number 1-63 (bits 0-5) // High two bits of cylinder (bits 6-7, hard disk only) setCL((DiskImage->DiskInfo.Sectors & 0x3F) | ((HIBYTE(MaxCylinders) & 0x02) << 6)); // CH: Low eight bits of cylinder number setCH(LOBYTE(MaxCylinders)); setDH(MaxHeads); if (Drive & 0x80) { /* Count the number of active HDDs */ for (i = 0; i < ARRAYSIZE(HardDrive); ++i) { if (IsDiskPresent(HardDrive[i])) ++PresentDrives; } /* Reset ES:DI to NULL */ // FIXME: NONONO!! Apps expect (for example, MS-DOS kernel) // that this function does not modify ES:DI if it was called // for a HDD. // setES(0x0000); // setDI(0x0000); } else { /* Count the number of active floppies */ for (i = 0; i < ARRAYSIZE(FloppyDrive); ++i) { if (IsDiskPresent(FloppyDrive[i])) ++PresentDrives; } /* ES:DI points to the floppy parameter table */ setES(HIWORD(((PULONG)BaseAddress)[0x1E])); setDI(LOWORD(((PULONG)BaseAddress)[0x1E])); } setDL(PresentDrives); setBL(DiskImage->DiskType); // DiskGeometryList[DiskImage->DiskType].biosval setAL(0x00); /* Return success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Hard Disk -- Initialize Controller with Drive Parameters */ case 0x09: /* Hard Disk -- Read Long Sectors */ case 0x0A: /* Hard Disk -- Write Long Sectors */ case 0x0B: goto Default; /* Hard Disk -- Seek to Cylinder */ case 0x0C: { BYTE Status; BYTE Head = getDH(); // CH: Low eight bits of cylinder number // CL: High two bits of cylinder (bits 6-7, hard disk only) WORD Cylinder = MAKEWORD(getCH(), (getCL() >> 6) & 0x02); // CL: Sector number 1-63 (bits 0-5) BYTE Sector = (getCL() & 0x3F); // 1-based Drive = getDL(); if (!(Drive & 0x80)) { DPRINT1("BiosDiskService(0x0C): Disk number 0x%02X is not a HDD\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } DiskImage = GetDisk(Drive); if (!DiskImage || !IsDiskPresent(DiskImage)) { DPRINT1("BiosDiskService(0x0C): Disk number 0x%02X invalid\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Set position */ Status = SeekDisk(DiskImage, Cylinder, Head, Sector); if (Status == 0x00) { /* Return success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; } else { DPRINT1("BiosDiskService(0x0C): Error when seeking in disk number 0x%02X (0x%02X)\n", Drive, Status); /* Return error */ setAH(Status); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; } break; } /* Hard Disk -- Reset Hard Disks */ case 0x0D: { // FIXME: Should do what 0x11 does. UNIMPLEMENTED; } /* Hard Disk -- Read Sector Buffer (XT only) */ case 0x0E: /* Hard Disk -- Write Sector Buffer (XT only) */ case 0x0F: goto Default; /* Hard Disk -- Check if Drive is ready */ case 0x10: { Drive = getDL(); if (!(Drive & 0x80)) { DPRINT1("BiosDiskService(0x10): Disk number 0x%02X is not a HDD\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } DiskImage = GetDisk(Drive); if (!DiskImage || !IsDiskPresent(DiskImage)) { DPRINT1("BiosDiskService(0x10): Disk number 0x%02X invalid\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Return success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Hard Disk -- Recalibrate Drive */ case 0x11: { BYTE Status; Drive = getDL(); if (!(Drive & 0x80)) { DPRINT1("BiosDiskService(0x11): Disk number 0x%02X is not a HDD\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } DiskImage = GetDisk(Drive); if (!DiskImage || !IsDiskPresent(DiskImage)) { DPRINT1("BiosDiskService(0x11): Disk number 0x%02X invalid\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Set position to zero */ Status = SeekDisk(DiskImage, /*Cylinder*/ 0, /*Head*/ 0, /*Sector*/ 1); if (Status == 0x00) { /* Return success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; } else { DPRINT1("BiosDiskService(0x11): Error when recalibrating disk number 0x%02X (0x%02X)\n", Drive, Status); /* Return error */ setAH(Status); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; } break; } /* Hard Disk -- Controller RAM Diagnostic */ case 0x12: /* Hard Disk -- Drive Diagnostic */ case 0x13: /* Hard Disk -- Controller Internal Diagnostic */ case 0x14: goto Default; /* Disk -- Get Disk Type */ case 0x15: { Drive = getDL(); DiskImage = GetDisk(Drive); if (!DiskImage || !IsDiskPresent(DiskImage)) { DPRINT1("BiosDiskService(0x15): Disk number 0x%02X invalid\n", Drive); /* Return error */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } if (Drive & 0x80) { ULONG NumSectors; /* Hard disk */ setAH(0x03); /* Number of 512-byte sectors in CX:DX */ NumSectors = (ULONG)((ULONG)DiskImage->DiskInfo.Cylinders * DiskImage->DiskInfo.Heads) * DiskImage->DiskInfo.Sectors; setCX(HIWORD(NumSectors)); setDX(LOWORD(NumSectors)); } else { /* Floppy */ setAH(0x01); } /* Return success */ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Floppy Disk -- Detect Disk Change */ case 0x16: /* Floppy Disk -- Set Disk Type for Format */ case 0x17: /* Disk -- Set Media Type for Format */ case 0x18: goto Default; default: Default: { DPRINT1("BIOS Function INT 13h, AH = 0x%02X, AL = 0x%02X, BH = 0x%02X NOT IMPLEMENTED\n", getAH(), getAL(), getBH()); } } } /* PUBLIC FUNCTIONS ***********************************************************/ VOID DiskBios32Post(VOID) { /* * Initialize BIOS Disk RAM dynamic data */ /* Some vectors are in fact addresses to tables */ // Diskette Parameters ((PULONG)BaseAddress)[0x1E] = MAKELONG(0xEFC7, BIOS_SEGMENT); // Hard Disk 0 Parameter Table Address ((PULONG)BaseAddress)[0x41] = NULL32; // Hard Disk 1 Drive Parameter Table Address ((PULONG)BaseAddress)[0x46] = NULL32; /* Relocated services by the BIOS (when needed) */ ((PULONG)BaseAddress)[0x40] = NULL32; // ROM BIOS Diskette Handler relocated by Hard Disk BIOS // RegisterBiosInt32(0x40, NULL); // ROM BIOS Diskette Handler relocated by Hard Disk BIOS /* Register the BIOS 32-bit Interrupts */ RegisterBiosInt32(BIOS_DISK_INTERRUPT, BiosDiskService); /* Initialize the BDA */ // Bda->LastDisketteOperation = 0; // Bda->LastDiskOperation = 0; AllDisksReset(); } BOOLEAN DiskBios32Initialize(VOID) { /* * Initialize BIOS Disk ROM static data */ /* Floppy Parameter Table */ RtlCopyMemory(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xEFC7), &FloppyParamTable.FloppyParamTable, sizeof(FloppyParamTable.FloppyParamTable)); // // FIXME: Must be done by HW floppy controller! // /* Detect and initialize the supported disks */ // TODO: the "Detect" part is missing. FloppyDrive[0] = RetrieveDisk(FLOPPY_DISK, 0); FloppyDrive[1] = RetrieveDisk(FLOPPY_DISK, 1); HardDrive[0] = RetrieveDisk(HARD_DISK, 0); HardDrive[1] = RetrieveDisk(HARD_DISK, 1); HardDrive[2] = RetrieveDisk(HARD_DISK, 2); HardDrive[3] = RetrieveDisk(HARD_DISK, 3); return TRUE; } VOID DiskBios32Cleanup(VOID) { } /* EOF */