list(APPEND SOURCE
bios/bios32/bios32.c
+ bios/bios32/dskbios32.c
bios/bios32/kbdbios32.c
+ bios/bios32/moubios32.c
bios/bios32/vbe.c
bios/bios32/vidbios32.c
- bios/bios32/moubios32.c
bios/bios.c
bios/kbdbios.c
bios/rom.c
cpu/cpu.c
cpu/registers.c
hardware/cmos.c
+ hardware/disk.c
hardware/dma.c
hardware/keyboard.c
hardware/mouse.c
#define BDA_SEGMENT 0x40
#define BIOS_SEGMENT 0xF000
-#define BIOS_EQUIPMENT_LIST 0x2E // Bit1: FPU, Bit 2: Mouse
-
#pragma pack(push, 1)
/*
DWORD TickCounter; // 0x6c
BYTE MidnightPassed; // 0x70
- BYTE BreakFlag; // 0x71
+ BYTE CtrlBreakFlag; // 0x71
WORD SoftReset; // 0x72
BYTE LastDiskOperation; // 0x74
BYTE NumDisks; // 0x75
BYTE VGADccIDActive; // 0x8a
DWORD Reserved3; // 0x8b
BYTE Reserved4; // 0x8f
- BYTE Reserved5[2]; // 0x90
+ BYTE FloppyDriveState[2]; // 0x90
BYTE Reserved6[2]; // 0x92
BYTE Reserved7[2]; // 0x94
BYTE KeybdStatusFlags; // 0x96
#include <bios/rom.h>
#include "bios32.h"
#include "bios32p.h"
+#include "dskbios32.h"
#include "kbdbios32.h"
#include "vidbios32.h"
#include "moubios32.h"
}
-VOID DosBootsectorInitialize(VOID);
+extern VOID DosBootsectorInitialize(VOID);
+extern VOID WINAPI BiosDiskService(LPWORD Stack);
static VOID WINAPI BiosBootstrapLoader(LPWORD Stack)
{
DPRINT("BiosBootstrapLoader -->\n");
- /* Load DOS */
+ {
+ USHORT i;
+
+ USHORT AX, BX, CX, DX, ES;
+ AX = getAX();
+ BX = getBX();
+ CX = getCX();
+ DX = getDX();
+ ES = getES();
+
+ // i = 0;
+ i = 1;
+ do
+ {
+ if (i == 0)
+ {
+ /* Boot from 1st floppy drive */
+
+ setAH(0x02); // Read sectors
+ setAL(0x01); // Number of sectors
+ setDH(0x00); // First head
+ setCH(0x00); // First cylinder
+ setCL(0x01); // First sector
+ setDL(0x00); // First diskette drive (used by loader code, so should not be cleared)
+ setES(0x0000); // Place data in 0000:7C00
+ setBX(0x7C00);
+ BiosDiskService(Stack);
+ if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit;
+ DPRINT1("An error happened while loading the bootsector from floppy 0, error = %d\n", getAH());
+ }
+ else if (i == 1)
+ {
+ /* Boot from 1st HDD drive */
+
+ setAH(0x02); // Read sectors
+ setAL(0x01); // Number of sectors
+ setDH(0x00); // First head
+ setCH(0x00); // First cylinder
+ setCL(0x01); // First sector
+ setDL(0x80); // First HDD drive (used by loader code, so should not be cleared)
+ setES(0x0000); // Place data in 0000:7C00
+ setBX(0x7C00);
+ BiosDiskService(Stack);
+ if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit;
+ DPRINT1("An error happened while loading the bootsector from HDD 0, error = %d\n", getAH());
+ }
+ // } while (i++ < 1);
+ } while (i-- > 0);
+
+ /* Clear everything, we are going to load DOS32 */
+ setAX(AX);
+ setBX(BX);
+ setCX(CX);
+ setDX(DX);
+ setES(ES);
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ }
+
+ /* Load our DOS */
DosBootsectorInitialize();
+Quit:
/*
* Position CPU to 0000:7C00 to boot the OS.
*
// BIOS_VIDEO_INTERRUPT : 0x10 (vidbios32.c)
RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService );
RegisterBiosInt32(BIOS_MEMORY_SIZE , BiosGetMemorySize );
- // BIOS_DISK_INTERRUPT : 0x13 -- UNIMPLEMENTED
+ // BIOS_DISK_INTERRUPT : 0x13 (dskbios32.c)
// BIOS_SERIAL_INTERRUPT : 0x14 -- UNIMPLEMENTED
RegisterBiosInt32(BIOS_MISC_INTERRUPT , BiosMiscService );
// BIOS_KBD_INTERRUPT : 0x16 (kbdbios32.c)
RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt);
/* Vectors that should be implemented (see above) */
- RegisterBiosInt32(0x13, NULL);
RegisterBiosInt32(0x14, NULL);
RegisterBiosInt32(0x17, NULL);
RegisterBiosInt32(0x1B, NULL);
/* Initialize the BDA contents */
RtlZeroMemory(Bda, sizeof(*Bda));
- Bda->EquipmentList = BIOS_EQUIPMENT_LIST;
+
+ /*
+ * Retrieve the basic equipment list from the CMOS
+ */
+ IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_EQUIPMENT_LIST | CMOS_DISABLE_NMI);
+ Bda->EquipmentList = IOReadB(CMOS_DATA_PORT);
+ // TODO: Update it if required.
+ Bda->EquipmentList &= 0x00FF; // High byte cleared for now...
/*
* Retrieve the conventional memory size
- * in kB from CMOS, typically 640 kB.
+ * in kB from the CMOS, typically 640 kB.
*/
IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_LOW | CMOS_DISABLE_NMI);
Low = IOReadB(CMOS_DATA_PORT);
KbdBios32Post();
VidBiosPost();
MouseBios32Post();
+ DiskBios32Post();
// WriteProtectRom(...);
*(PBYTE)(SEG_OFF_TO_PTR(BIOS_SEGMENT, 0xFFFE)) = BIOS_MODEL;
/* Initialize the Keyboard and Video BIOS */
- if (!KbdBiosInitialize() || !VidBiosInitialize() || !MouseBiosInitialize())
+ if (!KbdBiosInitialize() || !VidBiosInitialize() || !MouseBiosInitialize() || !DiskBios32Initialize())
{
/* Stop the VDM */
EmulatorTerminate();
VOID Bios32Cleanup(VOID)
{
+ DiskBios32Cleanup();
MouseBios32Cleanup();
VidBios32Cleanup();
KbdBiosCleanup();
--- /dev/null
+/*
+ * 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 *******************************************************************/
+
+#define NDEBUG
+
+#include "ntvdm.h"
+#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 <bios/dskbios.h>
+#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[1] = {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 = 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] = (ULONG)NULL;
+ // Hard Disk 1 Drive Parameter Table Address
+ ((PULONG)BaseAddress)[0x46] = (ULONG)NULL;
+
+ /* Relocated services by the BIOS (when needed) */
+ ((PULONG)BaseAddress)[0x40] = (ULONG)NULL; // 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] = &XDCFloppyDrive[0];
+ FloppyDrive[1] = &XDCFloppyDrive[1];
+ HardDrive[0] = &XDCHardDrive[0];
+
+ return TRUE;
+}
+
+VOID DiskBios32Cleanup(VOID)
+{
+}
+
+/* EOF */
--- /dev/null
+/*
+ * COPYRIGHT: GPL - See COPYING in the top level directory
+ * PROJECT: ReactOS Virtual DOS Machine
+ * FILE: dskbios32.h
+ * PURPOSE: VDM 32-bit Disk BIOS
+ * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
+ */
+
+#ifndef _DSKBIOS32_H_
+#define _DSKBIOS32_H_
+
+/* DEFINES ********************************************************************/
+
+#define BIOS_DISK_INTERRUPT 0x13
+
+/* FUNCTIONS ******************************************************************/
+
+VOID DiskBios32Post(VOID);
+
+BOOLEAN DiskBios32Initialize(VOID);
+VOID DiskBios32Cleanup(VOID);
+
+#endif /* _DSKBIOS32_H_ */
+
+/* EOF */
#include "clock.h"
#include "bios/rom.h"
#include "hardware/cmos.h"
+#include "hardware/disk.h"
#include "hardware/dma.h"
#include "hardware/keyboard.h"
#include "hardware/mouse.h"
return FALSE;
}
+ /* Initialize the disk controller */
+ if (!DiskCtrlInitialize())
+ {
+ wprintf(L"FATAL: Failed to completely initialize the disk controller.\n");
+ EmulatorCleanup();
+ return FALSE;
+ }
+
/* Initialize the software callback system and register the emulator BOPs */
InitializeInt32();
RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop);
VOID EmulatorCleanup(VOID)
{
+ DiskCtrlCleanup();
+
VgaCleanup();
/* Close the input thread handle */
case CMOS_REG_STATUS_C:
{
- /* Return the old value */
+ /* Return the old status register value, then clear it */
Value = CmosMemory.StatusRegC;
-
- /* Clear status register C */
CmosMemory.StatusRegC = 0x00;
-
break;
}
ChangeTime = TRUE;
/* Clear everything except the century */
- CurrentTime.wYear = (CurrentTime.wYear / 100) * 100;
-
+ CurrentTime.wYear = (CurrentTime.wYear / 100) * 100;
CurrentTime.wYear += WRITE_CMOS_DATA(CmosMemory, Data);
break;
}
case CMOS_REG_ACTUAL_EXT_MEMORY_LOW:
{
/* Sync EMS and UMS */
- CmosMemory.Regs[CMOS_REG_EXT_MEMORY_LOW] =
- CmosMemory.Regs[CMOS_REG_ACTUAL_EXT_MEMORY_LOW] = Data;
+ CmosMemory.ExtMemoryLow =
+ CmosMemory.ActualExtMemoryLow = Data;
break;
}
case CMOS_REG_ACTUAL_EXT_MEMORY_HIGH:
{
/* Sync EMS and UMS */
- CmosMemory.Regs[CMOS_REG_EXT_MEMORY_HIGH] =
- CmosMemory.Regs[CMOS_REG_ACTUAL_EXT_MEMORY_HIGH] = Data;
+ CmosMemory.ExtMemoryHigh =
+ CmosMemory.ActualExtMemoryHigh = Data;
break;
}
Success = ReadFile(hCmosRam, &CmosMemory, CmosSize, &CmosSize, NULL);
if (CmosSize != sizeof(CmosMemory))
{
- /* Bad CMOS Ram file. Reinitialize the CMOS memory. */
+ /* Bad CMOS RAM file. Reinitialize the CMOS memory. */
DPRINT1("Invalid CMOS file, read bytes %u, expected bytes %u\n", CmosSize, sizeof(CmosMemory));
RtlZeroMemory(&CmosMemory, sizeof(CmosMemory));
}
CmosMemory.StatusRegD = CMOS_BATTERY_OK; // Our CMOS battery works perfectly forever.
CmosMemory.Diagnostics = 0x00; // Diagnostics must not find any errors.
CmosMemory.ShutdownStatus = 0x00;
+ CmosMemory.EquipmentList = CMOS_EQUIPMENT_LIST;
/* Memory settings */
* and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm
* for more information.
*/
- CmosMemory.Regs[CMOS_REG_BASE_MEMORY_LOW ] = LOBYTE(0x0280);
- CmosMemory.Regs[CMOS_REG_BASE_MEMORY_HIGH] = HIBYTE(0x0280);
-
- CmosMemory.Regs[CMOS_REG_EXT_MEMORY_LOW] =
- CmosMemory.Regs[CMOS_REG_ACTUAL_EXT_MEMORY_LOW] = LOBYTE((MAX_ADDRESS - 0x100000) / 1024);
+ CmosMemory.BaseMemoryLow = LOBYTE(0x0280);
+ CmosMemory.BaseMemoryHigh = HIBYTE(0x0280);
- CmosMemory.Regs[CMOS_REG_EXT_MEMORY_HIGH] =
- CmosMemory.Regs[CMOS_REG_ACTUAL_EXT_MEMORY_HIGH] = HIBYTE((MAX_ADDRESS - 0x100000) / 1024);
+ CmosMemory.ExtMemoryLow =
+ CmosMemory.ActualExtMemoryLow = LOBYTE((MAX_ADDRESS - 0x100000) / 1024);
+ CmosMemory.ExtMemoryHigh =
+ CmosMemory.ActualExtMemoryHigh = HIBYTE((MAX_ADDRESS - 0x100000) / 1024);
/* Register the I/O Ports */
RegisterIoPort(CMOS_ADDRESS_PORT, NULL, CmosWriteAddress);
#define CMOS_DEFAULT_STA 0x26
#define CMOS_DEFAULT_STB CMOS_STB_24HOUR
+// Bit 0: Floppy, Bit 1: FPU, Bit 2: Mouse, Bits 4-5: 80x25 Color Video, Bits 6-7: 2 floppy drives
+#define CMOS_EQUIPMENT_LIST 0x6F
+
+
#define WRITE_CMOS_DATA(Cmos, Value) \
((Cmos).StatusRegB & CMOS_STB_BINARY) ? (Value) : BCD_TO_BINARY(Value)
CMOS_REG_STATUS_D,
CMOS_REG_DIAGNOSTICS,
CMOS_REG_SHUTDOWN_STATUS,
+ CMOS_REG_EQUIPMENT_LIST = 0x14,
CMOS_REG_BASE_MEMORY_LOW = 0x15,
CMOS_REG_BASE_MEMORY_HIGH = 0x16,
CMOS_REG_EXT_MEMORY_LOW = 0x17,
{
struct
{
- CMOS_CLOCK; // 0x00 - 0x0b
- BYTE StatusRegC; // 0x0c
- BYTE StatusRegD; // 0x0d
- BYTE Diagnostics; // 0x0e
- BYTE ShutdownStatus; // 0x0f
- BYTE Padding[0x22]; // 0x10
- BYTE Century; // 0x32
+ CMOS_CLOCK; // 0x00 - 0x0b
+ BYTE StatusRegC; // 0x0c
+ BYTE StatusRegD; // 0x0d
+ BYTE Diagnostics; // 0x0e
+ BYTE ShutdownStatus; // 0x0f
+ BYTE FloppyDrivesType; // 0x10
+ BYTE Reserved0; // 0x11
+ BYTE HardDrivesType; // 0x12
+ BYTE Reserved1; // 0x13
+ BYTE EquipmentList; // 0x14
+ BYTE BaseMemoryLow; // 0x15
+ BYTE BaseMemoryHigh; // 0x16
+ BYTE ExtMemoryLow; // 0x17
+ BYTE ExtMemoryHigh; // 0x18
+ BYTE ExtHardDrivesType[2]; // 0x19 - 0x1a
+ BYTE Reserved2[0x15]; // 0x1b
+ BYTE ActualExtMemoryLow; // 0x30
+ BYTE ActualExtMemoryHigh; // 0x31
+ BYTE Century; // 0x32
};
- BYTE Regs1[0x10]; // 0x00 - 0x0f
- BYTE Regs [0x40]; // 0x00 - 0x3f
+ BYTE Regs1[0x10]; // 0x00 - 0x0f
+ BYTE Regs [0x40]; // 0x00 - 0x3f
};
/*
--- /dev/null
+/*
+ * COPYRIGHT: GPL - See COPYING in the top level directory
+ * PROJECT: ReactOS Virtual DOS Machine
+ * FILE: subsystems/mvdm/ntvdm/hardware/disk.c
+ * PURPOSE: Generic Disk Controller (Floppy, Hard Disk, ...)
+ * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
+ *
+ * NOTE 1: This file is meant to be splitted into FDC and HDC
+ * when its code will grow out of control!
+ *
+ * NOTE 2: This is poor-man implementation, i.e. at the moment this file
+ * contains an API for manipulating the disks for the rest of NTVDM,
+ * but does not implement real hardware emulation (IO ports, etc...).
+ * One will have to progressively transform it into a real HW emulation
+ * and, in case the disk APIs are needed, move them elsewhere.
+ *
+ * FIXME: The big endian support (which is hardcoded here for machines
+ * in little endian) *MUST* be fixed!
+ */
+
+/* INCLUDES *******************************************************************/
+
+#define NDEBUG
+
+#include "ntvdm.h"
+#include "emulator.h"
+#include "disk.h"
+
+// #include "io.h"
+#include "memory.h"
+
+#include "utils.h"
+
+
+/**************** HARD DRIVES -- VHD FIXED DISK FORMAT SUPPORT ****************/
+
+// http://citrixblogger.org/2008/12/01/dynamic-vhd-walkthrough/
+// http://www.microsoft.com/en-us/download/details.aspx?id=23850
+// https://projects.honeynet.org/svn/sebek/virtualization/qebek/trunk/block/vpc.c
+// https://git.virtualopensystems.com/trescca/qemu/raw/40645c7bfd7c4d45381927e1e80081fa827c368a/block/vpc.c
+// https://gitweb.gentoo.org/proj/qemu-kvm.git/tree/block/vpc.c?h=qemu-kvm-0.12.4-gentoo&id=827dccd6740639c64732418539bf17e6e4c99d77
+
+#pragma pack(push, 1)
+
+enum VHD_TYPE
+{
+ VHD_FIXED = 2,
+ VHD_DYNAMIC = 3,
+ VHD_DIFFERENCING = 4,
+};
+
+// Seconds since Jan 1, 2000 0:00:00 (UTC)
+#define VHD_TIMESTAMP_BASE 946684800
+
+// Always in BIG-endian format!
+typedef struct _VHD_FOOTER
+{
+ CHAR creator[8]; // "conectix"
+ ULONG features;
+ ULONG version;
+
+ // Offset of next header structure, 0xFFFFFFFF if none
+ ULONG64 data_offset;
+
+ // Seconds since Jan 1, 2000 0:00:00 (UTC)
+ ULONG timestamp;
+
+ CHAR creator_app[4]; // "vpc "; "win"
+ USHORT major;
+ USHORT minor;
+ CHAR creator_os[4]; // "Wi2k"
+
+ ULONG64 orig_size;
+ ULONG64 size;
+
+ USHORT cyls;
+ BYTE heads;
+ BYTE secs_per_cyl;
+
+ ULONG type; // VHD_TYPE
+
+ // Checksum of the Hard Disk Footer ("one's complement of the sum of all
+ // the bytes in the footer without the checksum field")
+ ULONG checksum;
+
+ // UUID used to identify a parent hard disk (backing file)
+ BYTE uuid[16];
+
+ BYTE in_saved_state;
+
+ BYTE Padding[0x200-0x55];
+
+} VHD_FOOTER, *PVHD_FOOTER;
+C_ASSERT(sizeof(VHD_FOOTER) == 0x200);
+
+#pragma pack(pop)
+
+#if 0
+/*
+ * Calculates the number of cylinders, heads and sectors per cylinder
+ * based on a given number of sectors. This is the algorithm described
+ * in the VHD specification.
+ *
+ * Note that the geometry doesn't always exactly match total_sectors but
+ * may round it down.
+ *
+ * Returns TRUE on success, FALSE if the size is larger than 127 GB
+ */
+static BOOLEAN
+calculate_geometry(ULONG64 total_sectors, PUSHORT cyls,
+ PBYTE heads, PBYTE secs_per_cyl)
+{
+ ULONG cyls_times_heads;
+
+ if (total_sectors > 65535 * 16 * 255)
+ return FALSE;
+
+ if (total_sectors > 65535 * 16 * 63)
+ {
+ *secs_per_cyl = 255;
+ *heads = 16;
+ cyls_times_heads = total_sectors / *secs_per_cyl;
+ }
+ else
+ {
+ *secs_per_cyl = 17;
+ cyls_times_heads = total_sectors / *secs_per_cyl;
+ *heads = (cyls_times_heads + 1023) / 1024;
+
+ if (*heads < 4)
+ *heads = 4;
+
+ if (cyls_times_heads >= (*heads * 1024) || *heads > 16)
+ {
+ *secs_per_cyl = 31;
+ *heads = 16;
+ cyls_times_heads = total_sectors / *secs_per_cyl;
+ }
+
+ if (cyls_times_heads >= (*heads * 1024))
+ {
+ *secs_per_cyl = 63;
+ *heads = 16;
+ cyls_times_heads = total_sectors / *secs_per_cyl;
+ }
+ }
+
+ *cyls = cyls_times_heads / *heads;
+
+ return TRUE;
+}
+#endif
+
+
+
+/*************************** FLOPPY DISK CONTROLLER ***************************/
+
+// A Floppy Controller can support up to 4 floppy drives.
+/*static*/
+DISK_IMAGE XDCFloppyDrive[4];
+
+// Taken from DOSBox
+typedef struct _DISK_GEO
+{
+ DWORD ksize; /* Size in kilobytes */
+ WORD secttrack; /* Sectors per track */
+ WORD headscyl; /* Heads per cylinder */
+ WORD cylcount; /* Cylinders per side */
+ WORD biosval; /* Type to return from BIOS & CMOS */
+} DISK_GEO, *PDISK_GEO;
+
+// FIXME: At the moment, all of our diskettes have 512 bytes per sector...
+static WORD HackSectorSize = 512;
+static DISK_GEO DiskGeometryList[] =
+{
+ { 160, 8, 1, 40, 0},
+ { 180, 9, 1, 40, 0},
+ { 200, 10, 1, 40, 0},
+ { 320, 8, 2, 40, 1},
+ { 360, 9, 2, 40, 1},
+ { 400, 10, 2, 40, 1},
+ { 720, 9, 2, 80, 3},
+ {1200, 15, 2, 80, 2},
+ {1440, 18, 2, 80, 4},
+ {2880, 36, 2, 80, 6},
+};
+
+BOOLEAN
+MountFDI(IN PDISK_IMAGE DiskImage,
+ IN HANDLE hFile)
+{
+ ULONG FileSize;
+ USHORT i;
+
+ /*
+ * Retrieve the size of the file. In NTVDM we will handle files
+ * of maximum 1Mb so we can largely use GetFileSize only.
+ */
+ FileSize = GetFileSize(hFile, NULL);
+ if (FileSize == INVALID_FILE_SIZE && GetLastError() != ERROR_SUCCESS)
+ {
+ /* We failed, bail out */
+ DisplayMessage(L"Error when retrieving file size, or size too large (%d).", FileSize);
+ return FALSE;
+ }
+
+ /* Convert the size in kB */
+ FileSize /= 1024;
+
+ /* Find the floppy format in the list, and mount it if found */
+ for (i = 0; i < ARRAYSIZE(DiskGeometryList); ++i)
+ {
+ if (DiskGeometryList[i].ksize == FileSize ||
+ DiskGeometryList[i].ksize + 1 == FileSize)
+ {
+ /* Found, mount it */
+ DiskImage->DiskType = DiskGeometryList[i].biosval;
+ DiskImage->DiskInfo.Cylinders = DiskGeometryList[i].cylcount;
+ DiskImage->DiskInfo.Heads = DiskGeometryList[i].headscyl;
+ DiskImage->DiskInfo.Sectors = DiskGeometryList[i].secttrack;
+ DiskImage->DiskInfo.SectorSize = HackSectorSize;
+
+ /* Set the file pointer to the beginning */
+ SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
+
+ DiskImage->hDisk = hFile;
+ return TRUE;
+ }
+ }
+
+ /* If we are here, we failed to find a suitable format. Bail out. */
+ DisplayMessage(L"MountFDI: Floppy image of invalid size %d.", FileSize);
+ return FALSE;
+}
+
+
+/************************** IDE HARD DISK CONTROLLER **************************/
+
+// An IDE Hard Disk Controller can support up to 4 drives:
+// Primary Master Drive, Primary Slave Drive,
+// Secondary Master Drive, Secondary Slave Drive.
+/*static*/
+DISK_IMAGE XDCHardDrive[4];
+
+BOOLEAN
+MountHDD(IN PDISK_IMAGE DiskImage,
+ IN HANDLE hFile)
+{
+ /**** Support for VHD fixed disks ****/
+ DWORD FilePointer, BytesToRead;
+ VHD_FOOTER vhd_footer;
+
+ /* Go to the end of the file and retrieve the footer */
+ FilePointer = SetFilePointer(hFile, -(LONG)sizeof(VHD_FOOTER), NULL, FILE_END);
+ if (FilePointer == INVALID_SET_FILE_POINTER)
+ {
+ DPRINT1("MountHDD: Error when seeking HDD footer, last error = %d\n", GetLastError());
+ return FALSE;
+ }
+
+ /* Read footer */
+ // FIXME: We may consider just mapping section to the file...
+ BytesToRead = sizeof(VHD_FOOTER);
+ if (!ReadFile(hFile, &vhd_footer, BytesToRead, &BytesToRead, NULL))
+ {
+ DPRINT1("MountHDD: Error when reading HDD footer, last error = %d\n", GetLastError());
+ return FALSE;
+ }
+
+ /* Perform validity checks */
+ if (RtlCompareMemory(vhd_footer.creator, "conectix",
+ sizeof(vhd_footer.creator)) != sizeof(vhd_footer.creator))
+ {
+ DisplayMessage(L"MountHDD: Invalid HDD image (expected VHD).");
+ return FALSE;
+ }
+ if (vhd_footer.version != 0x00000100 &&
+ vhd_footer.version != 0x00000500) // FIXME: Big endian!
+ {
+ DisplayMessage(L"MountHDD: VHD HDD image of unexpected version %d.", vhd_footer.version);
+ return FALSE;
+ }
+ if (RtlUlongByteSwap(vhd_footer.type) != VHD_FIXED) // FIXME: Big endian!
+ {
+ DisplayMessage(L"MountHDD: Only VHD HDD fixed images are supported.");
+ return FALSE;
+ }
+ if (vhd_footer.data_offset != 0xFFFFFFFFFFFFFFFF)
+ {
+ DisplayMessage(L"MountHDD: Unexpected data offset for VHD HDD fixed image.");
+ return FALSE;
+ }
+ if (vhd_footer.orig_size != vhd_footer.size)
+ {
+ DisplayMessage(L"MountHDD: VHD HDD fixed image size should be the same as its original size.");
+ return FALSE;
+ }
+ // FIXME: Checksum!
+
+ /* Found, mount it */
+ DiskImage->DiskType = 0;
+ DiskImage->DiskInfo.Cylinders = RtlUshortByteSwap(vhd_footer.cyls); // FIXME: Big endian!
+ DiskImage->DiskInfo.Heads = vhd_footer.heads;
+ DiskImage->DiskInfo.Sectors = vhd_footer.secs_per_cyl;
+ DiskImage->DiskInfo.SectorSize = RtlUlonglongByteSwap(vhd_footer.size) / // FIXME: Big endian!
+ DiskImage->DiskInfo.Cylinders /
+ DiskImage->DiskInfo.Heads / DiskImage->DiskInfo.Sectors;
+
+ /* Set the file pointer to the beginning */
+ SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
+
+ DiskImage->hDisk = hFile;
+ return TRUE;
+}
+
+
+
+/************************ GENERIC DISK CONTROLLER API *************************/
+
+BOOLEAN
+IsDiskPresent(IN PDISK_IMAGE DiskImage)
+{
+ ASSERT(DiskImage);
+ return (DiskImage->hDisk != INVALID_HANDLE_VALUE && DiskImage->hDisk != NULL);
+}
+
+BYTE
+SeekDisk(IN PDISK_IMAGE DiskImage,
+ IN WORD Cylinder,
+ IN BYTE Head,
+ IN BYTE Sector)
+{
+ DWORD FilePointer;
+
+ /* Check that the sector number is 1-based */
+ // FIXME: Or do it in the caller?
+ if (Sector == 0)
+ {
+ /* Return error */
+ return 0x01;
+ }
+
+ /* Set position */
+ // Compute the offset
+ FilePointer = ((Cylinder * DiskImage->DiskInfo.Heads + Head)
+ * DiskImage->DiskInfo.Sectors + (Sector - 1))
+ * DiskImage->DiskInfo.SectorSize;
+ FilePointer = SetFilePointer(DiskImage->hDisk, FilePointer, NULL, FILE_BEGIN);
+ if (FilePointer == INVALID_SET_FILE_POINTER)
+ {
+ /* Return error */
+ return 0x40;
+ }
+
+ return 0x00;
+}
+
+BYTE
+ReadDisk(IN PDISK_IMAGE DiskImage,
+ IN WORD Cylinder,
+ IN BYTE Head,
+ IN BYTE Sector,
+ IN BYTE NumSectors)
+{
+ BYTE Result;
+ DWORD BytesToRead;
+
+ PVOID LocalBuffer;
+ BYTE StaticBuffer[1024];
+
+ /* Read the sectors */
+ Result = SeekDisk(DiskImage, Cylinder, Head, Sector);
+ if (Result != 0x00)
+ return Result;
+
+ BytesToRead = NumSectors * DiskImage->DiskInfo.SectorSize;
+
+ // FIXME: Consider just looping around filling each time the buffer...
+
+ if (BytesToRead <= sizeof(StaticBuffer))
+ {
+ LocalBuffer = StaticBuffer;
+ }
+ else
+ {
+ LocalBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BytesToRead);
+ ASSERT(LocalBuffer != NULL);
+ }
+
+ if (ReadFile(DiskImage->hDisk, LocalBuffer, BytesToRead, &BytesToRead, NULL))
+ {
+ /* Write to the memory */
+ EmulatorWriteMemory(&EmulatorContext,
+ TO_LINEAR(getES(), getBX()),
+ LocalBuffer,
+ BytesToRead);
+
+ Result = 0x00;
+ }
+ else
+ {
+ Result = 0x04;
+ }
+
+ if (LocalBuffer != StaticBuffer)
+ RtlFreeHeap(RtlGetProcessHeap(), 0, LocalBuffer);
+
+ /* Return success or error */
+ return Result;
+}
+
+BYTE
+WriteDisk(IN PDISK_IMAGE DiskImage,
+ IN WORD Cylinder,
+ IN BYTE Head,
+ IN BYTE Sector,
+ IN BYTE NumSectors)
+{
+ BYTE Result;
+ DWORD BytesToWrite;
+
+ PVOID LocalBuffer;
+ BYTE StaticBuffer[1024];
+
+ /* Check for write protection */
+ if (DiskImage->ReadOnly)
+ {
+ /* Return error */
+ return 0x03;
+ }
+
+ /* Write the sectors */
+ Result = SeekDisk(DiskImage, Cylinder, Head, Sector);
+ if (Result != 0x00)
+ return Result;
+
+ BytesToWrite = NumSectors * DiskImage->DiskInfo.SectorSize;
+
+ // FIXME: Consider just looping around filling each time the buffer...
+
+ if (BytesToWrite <= sizeof(StaticBuffer))
+ {
+ LocalBuffer = StaticBuffer;
+ }
+ else
+ {
+ LocalBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BytesToWrite);
+ ASSERT(LocalBuffer != NULL);
+ }
+
+ /* Read from the memory */
+ EmulatorReadMemory(&EmulatorContext,
+ TO_LINEAR(getES(), getBX()),
+ LocalBuffer,
+ BytesToWrite);
+
+ if (WriteFile(DiskImage->hDisk, LocalBuffer, BytesToWrite, &BytesToWrite, NULL))
+ Result = 0x00;
+ else
+ Result = 0x04;
+
+ if (LocalBuffer != StaticBuffer)
+ RtlFreeHeap(RtlGetProcessHeap(), 0, LocalBuffer);
+
+ /* Return success or error */
+ return Result;
+}
+
+typedef BOOLEAN (*MOUNT_DISK_HANDLER)(IN PDISK_IMAGE DiskImage, IN HANDLE hFile);
+
+BOOLEAN
+MountDisk(IN PDISK_IMAGE DiskImage,
+ MOUNT_DISK_HANDLER MountDiskHelper,
+ IN PCSTR FileName,
+ IN BOOLEAN ReadOnly)
+{
+ BOOLEAN Success = FALSE;
+ HANDLE hFile;
+
+ BY_HANDLE_FILE_INFORMATION FileInformation;
+
+ if (IsDiskPresent(DiskImage))
+ {
+ DisplayMessage(L"MountDisk: Disk 0x%p already in use.", DiskImage);
+ return FALSE;
+ }
+
+ /* Try to open the file */
+ SetLastError(0); // For debugging purposes
+ if (ReadOnly)
+ {
+ hFile = CreateFileA(FileName,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ }
+ else
+ {
+ hFile = CreateFileA(FileName,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, // No sharing access
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ }
+ DPRINT1("File '%s' opening %s ; GetLastError() = %u\n",
+ FileName, hFile != INVALID_HANDLE_VALUE ? "succeeded" : "failed", GetLastError());
+
+ /* If we failed, bail out */
+ if (hFile == INVALID_HANDLE_VALUE) return FALSE;
+
+ /* OK, we have a handle to the file */
+
+ /*
+ * Check that it is really a file, and not a physical drive.
+ * For obvious security reasons, we do not want to be able to
+ * write directly to physical drives.
+ *
+ * Redundant checks
+ */
+ SetLastError(0);
+ if (!GetFileInformationByHandle(hFile, &FileInformation) &&
+ GetLastError() == ERROR_INVALID_FUNCTION)
+ {
+ /* Objects other than real files are not supported */
+ DisplayMessage(L"'%S' is not a valid disk file.", FileName);
+ goto Quit;
+ }
+ SetLastError(0);
+ if (GetFileSize(hFile, NULL) == INVALID_FILE_SIZE &&
+ GetLastError() == ERROR_INVALID_FUNCTION)
+ {
+ /* Objects other than real files are not supported */
+ DisplayMessage(L"'%S' is not a valid disk file.", FileName);
+ goto Quit;
+ }
+
+ /* Success, mount the image */
+ if (!MountDiskHelper(DiskImage, hFile))
+ {
+ DisplayMessage(L"MountDisk: Failed to mount disk file '%S' in 0x%p.", FileName, DiskImage);
+ goto Quit;
+ }
+ Success = TRUE;
+
+Quit:
+ if (!Success) FileClose(hFile);
+ return Success;
+}
+
+BOOLEAN
+UnmountDisk(IN PDISK_IMAGE DiskImage)
+{
+ if (!IsDiskPresent(DiskImage))
+ {
+ DisplayMessage(L"UnmountDisk: Disk 0x%p is already unmounted.", DiskImage);
+ return FALSE;
+ }
+
+ /* Flush the image and unmount it */
+ FlushFileBuffers(DiskImage->hDisk);
+ FileClose(DiskImage->hDisk);
+ DiskImage->hDisk = NULL;
+ return TRUE;
+}
+
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+BOOLEAN DiskCtrlInitialize(VOID)
+{
+#if 0
+ // The following commands are examples of MountDisk usage.
+ // NOTE: Those values are hardcoded paths on my local test machines!!
+
+ // MountDisk(&XDCFloppyDrive[0], MountFDI, "H:\\trunk\\ntvdm_studies\\diskette_high.vfd", TRUE);
+ // MountDisk(&XDCFloppyDrive[0], MountFDI, "H:\\DOS_tests\\Dos5.0.img", TRUE);
+ // MountDisk(&XDCHardDrive[0] , MountHDD, "H:\\trunk\\ntvdm_studies\\hdd_10Mo_fixed.vhd", TRUE);
+
+ MountDisk(&XDCFloppyDrive[0], MountFDI, "H:\\DOS_tests\\diskette_test.vfd", FALSE);
+ MountDisk(&XDCHardDrive[0] , MountHDD, "H:\\DOS_tests\\MS-DOS 6_fixed_size.vhd", FALSE);
+#endif
+
+ return TRUE;
+}
+
+VOID DiskCtrlCleanup(VOID)
+{
+#if 0
+ // The following commands are examples of UnmountDisk usage.
+ UnmountDisk(&XDCHardDrive[0]);
+ UnmountDisk(&XDCFloppyDrive[0]);
+#endif
+}
+
+/* EOF */
--- /dev/null
+/*
+ * COPYRIGHT: GPL - See COPYING in the top level directory
+ * PROJECT: ReactOS Virtual DOS Machine
+ * FILE: subsystems/mvdm/ntvdm/hardware/disk.h
+ * PURPOSE: Generic Disk Controller (Floppy, Hard Disk, ...)
+ * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
+ */
+
+#ifndef _DISK_H_
+#define _DISK_H_
+
+/* DEFINES ********************************************************************/
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363972(v=vs.85).aspx
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363976(v=vs.85).aspx
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363969(v=vs.85).aspx
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365231(v=vs.85).aspx
+
+typedef struct _DISK_INFO
+{
+ WORD Cylinders; // DWORD
+ BYTE Heads; // DWORD
+ BYTE Sectors; // QWORD
+ // SectorPerTrack; ??? // DWORD
+ WORD SectorSize;
+} DISK_INFO, *PDISK_INFO;
+
+typedef struct _DISK_IMAGE
+{
+ DISK_INFO DiskInfo;
+ BYTE DiskType; // Type to return from BIOS & CMOS
+
+ BYTE LastOperationStatus;
+ // CurrentPos;
+
+ HANDLE hDisk;
+ BOOLEAN ReadOnly;
+ // WCHAR ImageFile[MAX_PATH];
+
+} DISK_IMAGE, *PDISK_IMAGE;
+
+// HACKHACK! For dskbios32.c
+extern DISK_IMAGE XDCFloppyDrive[];
+extern DISK_IMAGE XDCHardDrive[];
+
+/* FUNCTIONS ******************************************************************/
+
+BOOLEAN
+IsDiskPresent(IN PDISK_IMAGE DiskImage);
+
+BYTE
+SeekDisk(IN PDISK_IMAGE DiskImage,
+ IN WORD Cylinder,
+ IN BYTE Head,
+ IN BYTE Sector);
+
+BYTE
+ReadDisk(IN PDISK_IMAGE DiskImage,
+ IN WORD Cylinder,
+ IN BYTE Head,
+ IN BYTE Sector,
+ IN BYTE NumSectors);
+
+BYTE
+WriteDisk(IN PDISK_IMAGE DiskImage,
+ IN WORD Cylinder,
+ IN BYTE Head,
+ IN BYTE Sector,
+ IN BYTE NumSectors);
+
+BOOLEAN DiskCtrlInitialize(VOID);
+VOID DiskCtrlCleanup(VOID);
+
+#endif // _DISK_H_
+
+/* EOF */