diff --git a/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt b/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt index e2c5a6dd9b1..ca53559eda7 100644 --- a/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt +++ b/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt @@ -29,8 +29,12 @@ list(APPEND SOURCE hardware/sound/speaker.c hardware/video/vga.c dos/dos32krnl/bios.c + dos/dos32krnl/condrv.c + dos/dos32krnl/device.c dos/dos32krnl/dos.c dos/dos32krnl/dosfiles.c + dos/dos32krnl/emsdrv.c + dos/dos32krnl/memory.c dos/mouse32.c dos/dem.c clock.c diff --git a/reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.c b/reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.c index 5dfd8f07962..30d428f6dd8 100644 --- a/reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.c +++ b/reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.c @@ -313,9 +313,9 @@ static VOID NTAPI EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size) for (i = FirstPage; i <= LastPage; i++) { - Offset = (i == FirstPage) ? Address & (EMS_PAGE_SIZE - 1) : 0; + Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0; Length = ((i == LastPage) - ? (Address + Size - (LastPage << EMS_PAGE_BITS)) + ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS)) : EMS_PAGE_SIZE) - Offset; if (Mapping[i]) RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Mapping[i] + Offset), Length); @@ -333,9 +333,9 @@ static BOOLEAN NTAPI EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size) for (i = FirstPage; i <= LastPage; i++) { - Offset = (i == FirstPage) ? Address & (EMS_PAGE_SIZE - 1) : 0; + Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0; Length = ((i == LastPage) - ? (Address + Size - (LastPage << EMS_PAGE_BITS)) + ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS)) : EMS_PAGE_SIZE) - Offset; if (Mapping[i]) RtlCopyMemory((PVOID)((ULONG_PTR)Mapping[i] + Offset), Buffer, Length); diff --git a/reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.h b/reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.h index bb13db12036..9714fd647e7 100644 --- a/reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.h +++ b/reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.h @@ -31,8 +31,6 @@ #define EMS_STATUS_INV_PHYSICAL_PAGE 0x8B #define EMS_STATUS_UNKNOWN_FUNCTION 0x8F -#define ARRAY_INDEX(ptr, array) ((ULONG)(((ULONG_PTR)(ptr) - (ULONG_PTR)(array)) / sizeof(*array))) - typedef struct _EMS_HANDLE { BOOLEAN Allocated; diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/bios.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/bios.c index 7c448737673..67b5093997a 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/bios.c +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/bios.c @@ -14,6 +14,7 @@ #include "int32.h" #include "dos.h" +#include "memory.h" #include "bios/bios.h" // This is needed because on UNICODE this symbol is redirected to @@ -27,6 +28,8 @@ #undef FreeEnvironmentStrings #define FreeEnvironmentStrings FreeEnvironmentStringsA +#define CHARACTER_ADDRESS 0x007000FF /* 0070:00FF */ + /* PRIVATE VARIABLES **********************************************************/ // static BYTE CurrentDrive; @@ -38,53 +41,60 @@ CHAR DosReadCharacter(WORD FileHandle) { - CHAR Character = '\0'; + PCHAR Character = (PCHAR)FAR_POINTER(CHARACTER_ADDRESS); WORD BytesRead; + *Character = '\0'; DPRINT("DosReadCharacter\n"); /* Use the file reading function */ - DosReadFile(FileHandle, &Character, 1, &BytesRead); + DosReadFile(FileHandle, CHARACTER_ADDRESS, 1, &BytesRead); - return Character; + return *Character; } BOOLEAN DosCheckInput(VOID) { - HANDLE Handle = DosGetRealHandle(DOS_INPUT_HANDLE); + PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(DOS_INPUT_HANDLE); - if (IsConsoleHandle(Handle)) + switch (SftEntry->Type) { - /* Save AX */ - USHORT AX = getAX(); + case DOS_SFT_ENTRY_WIN32: + { + DWORD FileSizeHigh; + DWORD FileSize = GetFileSize(SftEntry->Handle, &FileSizeHigh); + LONG LocationHigh = 0; + DWORD Location = SetFilePointer(SftEntry->Handle, 0, &LocationHigh, FILE_CURRENT); - /* Call the BIOS */ - setAH(0x01); // or 0x11 for enhanced, but what to choose? - Int32Call(&DosContext, BIOS_KBD_INTERRUPT); + return ((Location != FileSize) || (LocationHigh != FileSizeHigh)); + } - /* Restore AX */ - setAX(AX); + case DOS_SFT_ENTRY_DEVICE: + { + WORD Result; - /* Return keyboard status */ - return (getZF() == 0); - } - else - { - DWORD FileSizeHigh; - DWORD FileSize = GetFileSize(Handle, &FileSizeHigh); - LONG LocationHigh = 0; - DWORD Location = SetFilePointer(Handle, 0, &LocationHigh, FILE_CURRENT); + if (!SftEntry->DeviceNode->InputStatusRoutine) return FALSE; + + Result = SftEntry->DeviceNode->InputStatusRoutine(SftEntry->DeviceNode); + return !(Result & DOS_DEVSTAT_BUSY); + } - return ((Location != FileSize) || (LocationHigh != FileSizeHigh)); + default: + { + /* Invalid handle */ + DosLastError = ERROR_INVALID_HANDLE; + return FALSE; + } } } VOID DosPrintCharacter(WORD FileHandle, CHAR Character) { WORD BytesWritten; + *((PCHAR)FAR_POINTER(CHARACTER_ADDRESS)) = Character; /* Use the file writing function */ - DosWriteFile(FileHandle, &Character, 1, &BytesWritten); + DosWriteFile(FileHandle, CHARACTER_ADDRESS, 1, &BytesWritten); } BOOLEAN DosBIOSInitialize(VOID) diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/condrv.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/condrv.c new file mode 100644 index 00000000000..6406bfaaaad --- /dev/null +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/condrv.c @@ -0,0 +1,155 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: dos/dos32krnl/condrv.c + * PURPOSE: DOS32 CON Driver + * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" + +#include "dos.h" +#include "dos/dem.h" + +#include "bios/bios.h" + +/* PRIVATE VARIABLES **********************************************************/ + +PDOS_DEVICE_NODE ConIn = NULL, ConOut = NULL; + +/* PRIVATE FUNCTIONS **********************************************************/ + +WORD NTAPI ConDrvReadInput(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length) +{ + CHAR Character; + WORD BytesRead; + PCHAR Pointer = (PCHAR)FAR_POINTER(Buffer); + + /* Save AX */ + USHORT AX = getAX(); + + /* + * Use BIOS Get Keystroke function + */ + for (BytesRead = 0; BytesRead < *Length; BytesRead++) + { + /* Call the BIOS INT 16h, AH=00h "Get Keystroke" */ + setAH(0x00); + Int32Call(&DosContext, BIOS_KBD_INTERRUPT); + + /* Retrieve the character in AL (scan code is in AH) */ + Character = getAL(); + + if (DoEcho) DosPrintCharacter(DOS_OUTPUT_HANDLE, Character); + Pointer[BytesRead] = Character; + + /* Stop on first carriage return */ + if (Character == '\r') + { + if (DoEcho) DosPrintCharacter(DOS_OUTPUT_HANDLE, '\n'); + break; + } + } + + *Length = BytesRead; + + /* Restore AX */ + setAX(AX); + return DOS_DEVSTAT_DONE; +} + +WORD NTAPI ConDrvInputStatus(PDOS_DEVICE_NODE Device) +{ + /* Save AX */ + USHORT AX = getAX(); + + /* Call the BIOS */ + setAH(0x01); // or 0x11 for enhanced, but what to choose? + Int32Call(&DosContext, BIOS_KBD_INTERRUPT); + + /* Restore AX */ + setAX(AX); + + /* If ZF is set, set the busy bit */ + if (getZF()) return DOS_DEVSTAT_BUSY; + else return DOS_DEVSTAT_DONE; +} + +WORD NTAPI ConDrvWriteOutput(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length) +{ + WORD BytesWritten; + PCHAR Pointer = (PCHAR)FAR_POINTER(Buffer); + + /* + * Use BIOS Teletype function + */ + + /* Save AX and BX */ + USHORT AX = getAX(); + USHORT BX = getBX(); + + // FIXME: Use BIOS Write String function INT 10h, AH=13h ?? + for (BytesWritten = 0; BytesWritten < *Length; BytesWritten++) + { + /* Set the parameters */ + setAL(Pointer[BytesWritten]); + setBL(DOS_CHAR_ATTRIBUTE); + setBH(Bda->VideoPage); + + /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */ + setAH(0x0E); + Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT); + } + + /* Restore AX and BX */ + setBX(BX); + setAX(AX); + return DOS_DEVSTAT_DONE; +} + +WORD NTAPI ConDrvOpen(PDOS_DEVICE_NODE Device) +{ + DPRINT("Handle to %Z opened\n", &Device->Name); + return DOS_DEVSTAT_DONE; +} + +WORD NTAPI ConDrvClose(PDOS_DEVICE_NODE Device) +{ + DPRINT("Handle to %Z closed\n", &Device->Name); + return DOS_DEVSTAT_DONE; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID ConDrvInitialize(PDOS_DEVICE_NODE *InputDevice, PDOS_DEVICE_NODE *OutputDevice) +{ + ConIn = DosCreateDevice(DOS_DEVATTR_STDIN + | DOS_DEVATTR_CON + | DOS_DEVATTR_CHARACTER, + "CONIN$"); + ConOut = DosCreateDevice(DOS_DEVATTR_STDOUT + | DOS_DEVATTR_CON + | DOS_DEVATTR_CHARACTER, + "CONOUT$"); + ASSERT(ConIn != NULL && ConOut != NULL); + + ConIn->ReadRoutine = ConDrvReadInput; + ConIn->InputStatusRoutine = ConDrvInputStatus; + ConOut->WriteRoutine = ConDrvWriteOutput; + ConIn->OpenRoutine = ConOut->OpenRoutine = ConDrvOpen; + ConIn->CloseRoutine = ConOut->CloseRoutine = ConDrvClose; + + if (InputDevice) *InputDevice = ConIn; + if (OutputDevice) *OutputDevice = ConOut; +} + +VOID ConDrvCleanup(VOID) +{ + if (ConIn) DosDeleteDevice(ConIn); + if (ConOut) DosDeleteDevice(ConOut); +} diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/device.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/device.c new file mode 100644 index 00000000000..a7a4c1f5513 --- /dev/null +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/device.c @@ -0,0 +1,383 @@ +/* + * COPYRIGHT: GPLv2+ - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: device.c + * PURPOSE: DOS Device Support + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include "dos.h" +#include "dos/dem.h" +#include "memory.h" +#include "device.h" + +/* PRIVATE VARIABLES **********************************************************/ + +static LIST_ENTRY DeviceList = { &DeviceList, &DeviceList }; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static VOID DosCallDriver(DWORD Driver, PDOS_REQUEST_HEADER Request) +{ + PDOS_DRIVER DriverBlock = (PDOS_DRIVER)FAR_POINTER(Driver); + PDOS_REQUEST_HEADER RemoteRequest; + + /* Call the strategy routine first */ + Call16(HIWORD(Driver), DriverBlock->StrategyRoutine); + RemoteRequest = (PDOS_REQUEST_HEADER)SEG_OFF_TO_PTR(getES(), getBX()); + + /* Copy the request structure to ES:BX */ + RtlMoveMemory(RemoteRequest, Request, Request->RequestLength); + + /* Call the interrupt routine */ + Call16(HIWORD(Driver), DriverBlock->InterruptRoutine); + + /* Get the request structure from ES:BX */ + RtlMoveMemory(Request, RemoteRequest, RemoteRequest->RequestLength); +} + +static inline WORD NTAPI DosDriverReadInternal(PDOS_DEVICE_NODE DeviceNode, + DWORD Buffer, + PWORD Length, + BOOLEAN IoControl) +{ + DOS_RW_REQUEST Request; + + Request.Header.RequestLength = IoControl ? sizeof(DOS_IOCTL_RW_REQUEST) + : sizeof(DOS_RW_REQUEST); + Request.Header.CommandCode = IoControl ? DOS_DEVCMD_IOCTL_READ : DOS_DEVCMD_READ; + Request.BufferPointer = Buffer; + Request.Length = *Length; + + DosCallDriver(DeviceNode->Driver, &Request.Header); + + *Length = Request.Length; + return Request.Header.Status; +} + +static inline WORD NTAPI DosDriverWriteInternal(PDOS_DEVICE_NODE DeviceNode, + DWORD Buffer, + PWORD Length, + BOOLEAN IoControl) +{ + DOS_RW_REQUEST Request; + + Request.Header.RequestLength = IoControl ? sizeof(DOS_IOCTL_RW_REQUEST) + : sizeof(DOS_RW_REQUEST); + Request.Header.CommandCode = IoControl ? DOS_DEVCMD_IOCTL_WRITE : DOS_DEVCMD_WRITE; + Request.BufferPointer = Buffer; + Request.Length = *Length; + + DosCallDriver(DeviceNode->Driver, &Request.Header); + + *Length = Request.Length; + return Request.Header.Status; +} + +static inline WORD NTAPI DosDriverGenericRequest(PDOS_DEVICE_NODE DeviceNode, + BYTE CommandCode) +{ + DOS_REQUEST_HEADER Request; + + Request.RequestLength = sizeof(DOS_REQUEST_HEADER); + Request.CommandCode = CommandCode; + + DosCallDriver(DeviceNode->Driver, &Request); + + return Request.Status; +} + +static WORD NTAPI DosDriverDispatchIoctlRead(PDOS_DEVICE_NODE DeviceNode, + DWORD Buffer, + PWORD Length) +{ + return DosDriverReadInternal(DeviceNode, Buffer, Length, TRUE); +} + +static WORD NTAPI DosDriverDispatchRead(PDOS_DEVICE_NODE DeviceNode, + DWORD Buffer, + PWORD Length) +{ + return DosDriverReadInternal(DeviceNode, Buffer, Length, FALSE); +} + +static WORD NTAPI DosDriverDispatchPeek(PDOS_DEVICE_NODE DeviceNode, + PBYTE Character) +{ + DOS_PEEK_REQUEST Request; + + Request.Header.RequestLength = sizeof(DOS_PEEK_REQUEST); + Request.Header.CommandCode = DOS_DEVCMD_PEEK; + + DosCallDriver(DeviceNode->Driver, &Request.Header); + + *Character = Request.Character; + return Request.Header.Status; +} + +static WORD NTAPI DosDriverDispatchInputStatus(PDOS_DEVICE_NODE DeviceNode) +{ + return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_INSTAT); +} + +static WORD NTAPI DosDriverDispatchFlushInput(PDOS_DEVICE_NODE DeviceNode) +{ + return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_FLUSH_INPUT); +} + +static WORD NTAPI DosDriverDispatchIoctlWrite(PDOS_DEVICE_NODE DeviceNode, + DWORD Buffer, + PWORD Length) +{ + return DosDriverWriteInternal(DeviceNode, Buffer, Length, TRUE); +} + +static WORD NTAPI DosDriverDispatchWrite(PDOS_DEVICE_NODE DeviceNode, + DWORD Buffer, + PWORD Length) +{ + return DosDriverWriteInternal(DeviceNode, Buffer, Length, FALSE); +} + +static WORD NTAPI DosDriverDispatchOutputStatus(PDOS_DEVICE_NODE DeviceNode) +{ + return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_OUTSTAT); +} + +static WORD NTAPI DosDriverDispatchFlushOutput(PDOS_DEVICE_NODE DeviceNode) +{ + return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_FLUSH_OUTPUT); +} + +static WORD NTAPI DosDriverDispatchOpen(PDOS_DEVICE_NODE DeviceNode) +{ + return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_OPEN); +} + +static WORD NTAPI DosDriverDispatchClose(PDOS_DEVICE_NODE DeviceNode) +{ + return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_CLOSE); +} + +static WORD NTAPI DosDriverDispatchOutputUntilBusy(PDOS_DEVICE_NODE DeviceNode, + DWORD Buffer, + PWORD Length) +{ + DOS_OUTPUT_BUSY_REQUEST Request; + + Request.Header.RequestLength = sizeof(DOS_OUTPUT_BUSY_REQUEST); + Request.Header.CommandCode = DOS_DEVCMD_OUTPUT_BUSY; + Request.BufferPointer = Buffer; + Request.Length = *Length; + + DosCallDriver(DeviceNode->Driver, &Request.Header); + + *Length = Request.Length; + return Request.Header.Status; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +PDOS_DEVICE_NODE DosGetDevice(LPCSTR DeviceName) +{ + PLIST_ENTRY i; + PDOS_DEVICE_NODE Node; + ANSI_STRING DeviceNameString; + + RtlInitAnsiString(&DeviceNameString, DeviceName); + + for (i = DeviceList.Flink; i != &DeviceList; i = i->Flink) + { + Node = CONTAINING_RECORD(i, DOS_DEVICE_NODE, Entry); + if (RtlEqualString(&Node->Name, &DeviceNameString, TRUE)) return Node; + } + + return NULL; +} + +PDOS_DEVICE_NODE DosCreateDevice(WORD Attributes, PCHAR DeviceName) +{ + BYTE i; + PDOS_DEVICE_NODE Node; + + /* Make sure this is a character device */ + if (!(Attributes & DOS_DEVATTR_CHARACTER)) + { + DPRINT1("ERROR: Block devices are not supported.\n"); + return FALSE; + } + + Node = (PDOS_DEVICE_NODE)RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(DOS_DEVICE_NODE)); + if (Node == NULL) return NULL; + + Node->DeviceAttributes = Attributes; + + /* Initialize the name string */ + Node->Name.Buffer = Node->NameBuffer; + Node->Name.MaximumLength = MAX_DEVICE_NAME; + + for (i = 0; i < MAX_DEVICE_NAME; i++) + { + if (DeviceName[i] == '\0' || DeviceName[i] == ' ') break; + Node->Name.Buffer[i] = DeviceName[i]; + } + + Node->Name.Length = i; + + InsertTailList(&DeviceList, &Node->Entry); + return Node; +} + +VOID DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode) +{ + RemoveEntryList(&DeviceNode->Entry); + RtlFreeHeap(RtlGetProcessHeap(), 0, DeviceNode); +} + +DWORD DosLoadDriver(LPCSTR DriverFile) +{ + DWORD Result = ERROR_SUCCESS; + HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL; + LPBYTE Address = NULL; + DWORD Driver; + PDOS_DRIVER DriverHeader; + WORD Segment = 0; + DWORD FileSize; + DWORD DriversLoaded = 0; + DOS_INIT_REQUEST Request; + PDOS_DEVICE_NODE DeviceNode; + + /* Open a handle to the driver file */ + FileHandle = CreateFileA(DriverFile, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (FileHandle == INVALID_HANDLE_VALUE) + { + Result = GetLastError(); + goto Cleanup; + } + + /* Get the file size */ + FileSize = GetFileSize(FileHandle, NULL); + + /* Allocate DOS memory for the driver */ + Segment = DosAllocateMemory(FileSize >> 4, NULL); + if (Segment == 0) + { + Result = DosLastError; + goto Cleanup; + } + + /* Create a mapping object for the file */ + FileMapping = CreateFileMapping(FileHandle, + NULL, + PAGE_READONLY, + 0, + 0, + NULL); + if (FileMapping == NULL) + { + Result = GetLastError(); + goto Cleanup; + } + + /* Map the file into memory */ + Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0); + if (Address == NULL) + { + Result = GetLastError(); + goto Cleanup; + } + + /* Copy the entire file to the DOS memory */ + Driver = MAKELONG(0, Segment); + DriverHeader = (PDOS_DRIVER)FAR_POINTER(Driver); + RtlCopyMemory(DriverHeader, Address, FileSize); + + /* Loop through all the drivers in this file */ + while (TRUE) + { + if (!(DriverHeader->DeviceAttributes & DOS_DEVATTR_CHARACTER)) + { + DPRINT1("Error loading driver at %04X:%04X: " + "Block device drivers are not supported.\n", + HIWORD(Driver), + LOWORD(Driver)); + goto Next; + } + + /* Send the driver an init request */ + RtlZeroMemory(&Request, sizeof(Request)); + Request.Header.RequestLength = sizeof(DOS_INIT_REQUEST); + Request.Header.CommandCode = DOS_DEVCMD_INIT; + // TODO: Set Request.DeviceString to the appropriate line in CONFIG.NT! + DosCallDriver(Driver, &Request.Header); + + if (Request.Header.Status & DOS_DEVSTAT_ERROR) + { + DPRINT1("Error loading driver at %04X:%04X: " + "Initialization routine returned error %u.\n", + HIWORD(Driver), + LOWORD(Driver), + Request.Header.Status & 0x7F); + goto Next; + } + + /* Create the device */ + DeviceNode = DosCreateDevice(DriverHeader->DeviceAttributes, + DriverHeader->DeviceName); + DeviceNode->Driver = Driver; + DeviceNode->IoctlReadRoutine = DosDriverDispatchIoctlRead; + DeviceNode->ReadRoutine = DosDriverDispatchRead; + DeviceNode->PeekRoutine = DosDriverDispatchPeek; + DeviceNode->InputStatusRoutine = DosDriverDispatchInputStatus; + DeviceNode->FlushInputRoutine = DosDriverDispatchFlushInput; + DeviceNode->IoctlWriteRoutine = DosDriverDispatchIoctlWrite; + DeviceNode->WriteRoutine = DosDriverDispatchWrite; + DeviceNode->OutputStatusRoutine = DosDriverDispatchOutputStatus; + DeviceNode->FlushOutputRoutine = DosDriverDispatchFlushOutput; + DeviceNode->OpenRoutine = DosDriverDispatchOpen; + DeviceNode->CloseRoutine = DosDriverDispatchClose; + DeviceNode->OutputUntilBusyRoutine = DosDriverDispatchOutputUntilBusy; + + DriversLoaded++; + +Next: + if (LOWORD(DriverHeader->Link) == 0xFFFF) break; + Driver = DriverHeader->Link; + DriverHeader = (PDOS_DRIVER)FAR_POINTER(Driver); + } + + DPRINT1("%u drivers loaded from %s.\n", DriversLoaded, DriverFile); + +Cleanup: + if (Result != ERROR_SUCCESS) + { + /* It was not successful, cleanup the DOS memory */ + if (Segment) DosFreeMemory(Segment); + } + + /* Unmap the file */ + if (Address != NULL) UnmapViewOfFile(Address); + + /* Close the file mapping object */ + if (FileMapping != NULL) CloseHandle(FileMapping); + + /* Close the file handle */ + if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle); + + return Result; +} + + diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/device.h b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/device.h new file mode 100644 index 00000000000..01d9efd19bf --- /dev/null +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/device.h @@ -0,0 +1,199 @@ +/* + * COPYRIGHT: GPLv2+ - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: device.h + * PURPOSE: DOS Device Support + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _DEVICE_H_ +#define _DEVICE_H_ + +#include + +/* DEFINITIONS ****************************************************************/ + +#define MAX_DEVICE_NAME 8 + +#define DOS_DEVATTR_STDIN (1 << 0) +#define DOS_DEVATTR_STDOUT (1 << 1) +#define DOS_DEVATTR_NUL (1 << 2) +#define DOS_DEVATTR_CLOCK (1 << 3) +#define DOS_DEVATTR_CON (1 << 4) +#define DOS_DEVATTR_OPENCLOSE (1 << 11) +#define DOS_DEVATTR_SPECIAL (1 << 13) +#define DOS_DEVATTR_IOCTL (1 << 14) +#define DOS_DEVATTR_CHARACTER (1 << 15) + +#define DOS_DEVCMD_INIT 0 +#define DOS_DEVCMD_MEDIACHK 1 +#define DOS_DEVCMD_BUILDBPB 2 +#define DOS_DEVCMD_IOCTL_READ 3 +#define DOS_DEVCMD_READ 4 +#define DOS_DEVCMD_PEEK 5 +#define DOS_DEVCMD_INSTAT 6 +#define DOS_DEVCMD_FLUSH_INPUT 7 +#define DOS_DEVCMD_WRITE 8 +#define DOS_DEVCMD_WRITE_VERIFY 9 +#define DOS_DEVCMD_OUTSTAT 10 +#define DOS_DEVCMD_FLUSH_OUTPUT 11 +#define DOS_DEVCMD_IOCTL_WRITE 12 +#define DOS_DEVCMD_OPEN 13 +#define DOS_DEVCMD_CLOSE 14 +#define DOS_DEVCMD_REMOVABLE 15 +#define DOS_DEVCMD_OUTPUT_BUSY 16 + +#define DOS_DEVSTAT_DONE (1 << 8) +#define DOS_DEVSTAT_BUSY (1 << 9) +#define DOS_DEVSTAT_ERROR (1 << 15) + +#define DOS_DEVERR_WRITE_PROTECT 0 +#define DOS_DEVERR_UNKNOWN_UNIT 1 +#define DOS_DEVERR_NOT_READY 2 +#define DOS_DEVERR_UNKNOWN_COMMAND 3 +#define DOS_DEVERR_BAD_DATA_CRC 4 +#define DOS_DEVERR_BAD_REQUEST 5 +#define DOS_DEVERR_INVALID_SEEK 6 +#define DOS_DEVERR_UNKNOWN_MEDIUM 7 +#define DOS_DEVERR_BAD_BLOCK 8 +#define DOS_DEVERR_OUT_OF_PAPER 9 +#define DOS_DEVERR_WRITE_FAULT 10 +#define DOS_DEVERR_READ_FAULT 11 +#define DOS_DEVERR_GENERAL 12 +#define DOS_DEVERR_BAD_MEDIA_CHANGE 15 + +typedef struct _DOS_DEVICE_NODE DOS_DEVICE_NODE, *PDOS_DEVICE_NODE; + +typedef WORD (NTAPI *PDOS_DEVICE_GENERIC_ROUTINE)(PDOS_DEVICE_NODE DeviceNode); + +typedef WORD (NTAPI *PDOS_DEVICE_IO_ROUTINE) +( + PDOS_DEVICE_NODE DeviceNode, + DWORD Buffer, + PWORD Length +); + +typedef WORD (NTAPI *PDOS_DEVICE_PEEK_ROUTINE) +( + PDOS_DEVICE_NODE DeviceNode, + PBYTE Character +); + +struct _DOS_DEVICE_NODE +{ + LIST_ENTRY Entry; + WORD DeviceAttributes; + ANSI_STRING Name; + CHAR NameBuffer[MAX_DEVICE_NAME]; + PDOS_DEVICE_IO_ROUTINE IoctlReadRoutine; + PDOS_DEVICE_IO_ROUTINE ReadRoutine; + PDOS_DEVICE_PEEK_ROUTINE PeekRoutine; + PDOS_DEVICE_GENERIC_ROUTINE InputStatusRoutine; + PDOS_DEVICE_GENERIC_ROUTINE FlushInputRoutine; + PDOS_DEVICE_IO_ROUTINE IoctlWriteRoutine; + PDOS_DEVICE_IO_ROUTINE WriteRoutine; + PDOS_DEVICE_GENERIC_ROUTINE OutputStatusRoutine; + PDOS_DEVICE_GENERIC_ROUTINE FlushOutputRoutine; + PDOS_DEVICE_GENERIC_ROUTINE OpenRoutine; + PDOS_DEVICE_GENERIC_ROUTINE CloseRoutine; + PDOS_DEVICE_IO_ROUTINE OutputUntilBusyRoutine; + DWORD Driver; +}; + +#pragma pack(push, 1) + +typedef struct _DOS_DRIVER +{ + DWORD Link; + WORD DeviceAttributes; + WORD StrategyRoutine; + WORD InterruptRoutine; + + union + { + CHAR DeviceName[MAX_DEVICE_NAME]; // for character devices + + struct // for block devices + { + BYTE UnitCount; + BYTE Reserved[MAX_DEVICE_NAME - 1]; + }; + }; +} DOS_DRIVER, *PDOS_DRIVER; + +typedef struct _DOS_REQUEST_HEADER +{ + IN BYTE RequestLength; + IN BYTE UnitNumber OPTIONAL; + IN BYTE CommandCode; + OUT WORD Status; + + BYTE Reserved[8]; +} DOS_REQUEST_HEADER, *PDOS_REQUEST_HEADER; + +typedef struct _DOS_INIT_REQUEST +{ + DOS_REQUEST_HEADER Header; + + OUT BYTE UnitsInitialized; + OUT DWORD ReturnBreakAddress; + + union + { + IN DWORD DeviceString; // for character devices + + struct // for block devices + { + IN BYTE FirstDriveLetter; + OUT DWORD BpbPointer; + }; + }; + +} DOS_INIT_REQUEST, *PDOS_INIT_REQUEST; + +typedef struct _DOS_IOCTL_RW_REQUEST +{ + DOS_REQUEST_HEADER Header; + + IN BYTE MediaDescriptorByte OPTIONAL; + IN DWORD BufferPointer; + IN OUT WORD Length; + IN WORD StartingBlock OPTIONAL; +} DOS_IOCTL_RW_REQUEST, *PDOS_IOCTL_RW_REQUEST; + +typedef struct _DOS_RW_REQUEST +{ + DOS_REQUEST_HEADER Header; + + IN BYTE MediaDescriptorByte OPTIONAL; + IN DWORD BufferPointer; + IN OUT WORD Length; + IN WORD StartingBlock OPTIONAL; + OUT DWORD VolumeLabelPtr OPTIONAL; +} DOS_RW_REQUEST, *PDOS_RW_REQUEST; + +typedef struct _DOS_PEEK_REQUEST +{ + DOS_REQUEST_HEADER Header; + OUT BYTE Character; +} DOS_PEEK_REQUEST, *PDOS_PEEK_REQUEST; + +typedef struct _DOS_OUTPUT_BUSY_REQUEST +{ + DOS_REQUEST_HEADER Header; + + IN DWORD BufferPointer; + IN OUT WORD Length; +} DOS_OUTPUT_BUSY_REQUEST, *PDOS_OUTPUT_BUSY_REQUEST; + +#pragma pack(pop) + +/* FUNCTIONS ******************************************************************/ + +PDOS_DEVICE_NODE DosGetDevice(LPCSTR DeviceName); +PDOS_DEVICE_NODE DosCreateDevice(WORD Attributes, PCHAR DeviceName); +VOID DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode); + +#endif // _DEVICE_H_ + +/* EOF */ diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c index cdfe827c74f..a4a455debe0 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c @@ -17,6 +17,8 @@ #include "dos.h" #include "dos/dem.h" +#include "device.h" +#include "memory.h" #include "bios/bios.h" @@ -27,382 +29,22 @@ CALLBACK16 DosContext; -static WORD CurrentPsp = SYSTEM_PSP; -static WORD DosLastError = 0; static DWORD DiskTransferArea; /*static*/ BYTE CurrentDrive; static CHAR LastDrive = 'E'; static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH]; - -static struct -{ - HANDLE Handle; - WORD RefCount; -} DosSystemFileTable[DOS_SFT_SIZE]; - -static BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT; -static BOOLEAN DosUmbLinked = FALSE; +static DOS_SFT_ENTRY DosSystemFileTable[DOS_SFT_SIZE]; static WORD DosErrorLevel = 0x0000; +/* PUBLIC VARIABLES ***********************************************************/ + /* Echo state for INT 21h, AH = 01h and AH = 3Fh */ BOOLEAN DoEcho = FALSE; +WORD CurrentPsp = SYSTEM_PSP; +WORD DosLastError = 0; /* PRIVATE FUNCTIONS **********************************************************/ -/* - * Memory management functions - */ -static VOID DosCombineFreeBlocks(WORD StartBlock) -{ - PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb; - - /* If this is the last block or it's not free, quit */ - if (CurrentMcb->BlockType == 'Z' || CurrentMcb->OwnerPsp != 0) return; - - while (TRUE) - { - /* Get a pointer to the next MCB */ - NextMcb = SEGMENT_TO_MCB(StartBlock + CurrentMcb->Size + 1); - - /* Check if the next MCB is free */ - if (NextMcb->OwnerPsp == 0) - { - /* Combine them */ - CurrentMcb->Size += NextMcb->Size + 1; - CurrentMcb->BlockType = NextMcb->BlockType; - NextMcb->BlockType = 'I'; - } - else - { - /* No more adjoining free blocks */ - break; - } - } -} - -static WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable) -{ - WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0; - PDOS_MCB CurrentMcb, NextMcb; - BOOLEAN SearchUmb = FALSE; - - DPRINT("DosAllocateMemory: Size 0x%04X\n", Size); - - if (DosUmbLinked && (DosAllocStrategy & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))) - { - /* Search UMB first */ - Segment = UMB_START_SEGMENT; - SearchUmb = TRUE; - } - - while (TRUE) - { - /* Get a pointer to the MCB */ - CurrentMcb = SEGMENT_TO_MCB(Segment); - - /* Make sure it's valid */ - if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') - { - DPRINT("The DOS memory arena is corrupted!\n"); - DosLastError = ERROR_ARENA_TRASHED; - return 0; - } - - /* Only check free blocks */ - if (CurrentMcb->OwnerPsp != 0) goto Next; - - /* Combine this free block with adjoining free blocks */ - DosCombineFreeBlocks(Segment); - - /* Update the maximum block size */ - if (CurrentMcb->Size > MaxSize) MaxSize = CurrentMcb->Size; - - /* Check if this block is big enough */ - if (CurrentMcb->Size < Size) goto Next; - - switch (DosAllocStrategy & 0x3F) - { - case DOS_ALLOC_FIRST_FIT: - { - /* For first fit, stop immediately */ - Result = Segment; - goto Done; - } - - case DOS_ALLOC_BEST_FIT: - { - /* For best fit, update the smallest block found so far */ - if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size)) - { - Result = Segment; - } - - break; - } - - case DOS_ALLOC_LAST_FIT: - { - /* For last fit, make the current block the result, but keep searching */ - Result = Segment; - break; - } - } - -Next: - /* If this was the last MCB in the chain, quit */ - if (CurrentMcb->BlockType == 'Z') - { - /* Check if nothing was found while searching through UMBs */ - if ((Result == 0) && SearchUmb && (DosAllocStrategy & DOS_ALLOC_HIGH_LOW)) - { - /* Search low memory */ - Segment = FIRST_MCB_SEGMENT; - continue; - } - - break; - } - - /* Otherwise, update the segment and continue */ - Segment += CurrentMcb->Size + 1; - } - -Done: - - /* If we didn't find a free block, return 0 */ - if (Result == 0) - { - DosLastError = ERROR_NOT_ENOUGH_MEMORY; - if (MaxAvailable) *MaxAvailable = MaxSize; - return 0; - } - - /* Get a pointer to the MCB */ - CurrentMcb = SEGMENT_TO_MCB(Result); - - /* Check if the block is larger than requested */ - if (CurrentMcb->Size > Size) - { - /* It is, split it into two blocks */ - NextMcb = SEGMENT_TO_MCB(Result + Size + 1); - - /* Initialize the new MCB structure */ - NextMcb->BlockType = CurrentMcb->BlockType; - NextMcb->Size = CurrentMcb->Size - Size - 1; - NextMcb->OwnerPsp = 0; - - /* Update the current block */ - CurrentMcb->BlockType = 'M'; - CurrentMcb->Size = Size; - } - - /* Take ownership of the block */ - CurrentMcb->OwnerPsp = CurrentPsp; - - /* Return the segment of the data portion of the block */ - return Result + 1; -} - -static BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable) -{ - BOOLEAN Success = TRUE; - WORD Segment = BlockData - 1, ReturnSize = 0, NextSegment; - PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), NextMcb; - - DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n", - BlockData, - NewSize); - - /* Make sure this is a valid, allocated block */ - if ((Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') || Mcb->OwnerPsp == 0) - { - Success = FALSE; - DosLastError = ERROR_INVALID_HANDLE; - goto Done; - } - - ReturnSize = Mcb->Size; - - /* Check if we need to expand or contract the block */ - if (NewSize > Mcb->Size) - { - /* We can't expand the last block */ - if (Mcb->BlockType != 'M') - { - Success = FALSE; - goto Done; - } - - /* Get the pointer and segment of the next MCB */ - NextSegment = Segment + Mcb->Size + 1; - NextMcb = SEGMENT_TO_MCB(NextSegment); - - /* Make sure the next segment is free */ - if (NextMcb->OwnerPsp != 0) - { - DPRINT("Cannot expand memory block: next segment is not free!\n"); - DosLastError = ERROR_NOT_ENOUGH_MEMORY; - Success = FALSE; - goto Done; - } - - /* Combine this free block with adjoining free blocks */ - DosCombineFreeBlocks(NextSegment); - - /* Set the maximum possible size of the block */ - ReturnSize += NextMcb->Size + 1; - - if (ReturnSize < NewSize) - { - DPRINT("Cannot expand memory block: insufficient free segments available!\n"); - DosLastError = ERROR_NOT_ENOUGH_MEMORY; - Success = FALSE; - goto Done; - } - - /* Maximize the current block */ - Mcb->Size = ReturnSize; - Mcb->BlockType = NextMcb->BlockType; - - /* Invalidate the next block */ - NextMcb->BlockType = 'I'; - - /* Check if the block is larger than requested */ - if (Mcb->Size > NewSize) - { - DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n", - Mcb->Size, - NewSize); - - /* It is, split it into two blocks */ - NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1); - - /* Initialize the new MCB structure */ - NextMcb->BlockType = Mcb->BlockType; - NextMcb->Size = Mcb->Size - NewSize - 1; - NextMcb->OwnerPsp = 0; - - /* Update the current block */ - Mcb->BlockType = 'M'; - Mcb->Size = NewSize; - } - } - else if (NewSize < Mcb->Size) - { - DPRINT("Shrinking block from 0x%04X to 0x%04X\n", - Mcb->Size, - NewSize); - - /* Just split the block */ - NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1); - NextMcb->BlockType = Mcb->BlockType; - NextMcb->Size = Mcb->Size - NewSize - 1; - NextMcb->OwnerPsp = 0; - - /* Update the MCB */ - Mcb->BlockType = 'M'; - Mcb->Size = NewSize; - } - -Done: - /* Check if the operation failed */ - if (!Success) - { - DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n", - ReturnSize); - - /* Return the maximum possible size */ - if (MaxAvailable) *MaxAvailable = ReturnSize; - } - - return Success; -} - -static BOOLEAN DosFreeMemory(WORD BlockData) -{ - PDOS_MCB Mcb = SEGMENT_TO_MCB(BlockData - 1); - - DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData); - - /* Make sure the MCB is valid */ - if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') - { - DPRINT("MCB block type '%c' not valid!\n", Mcb->BlockType); - return FALSE; - } - - /* Mark the block as free */ - Mcb->OwnerPsp = 0; - - return TRUE; -} - -static BOOLEAN DosLinkUmb(VOID) -{ - DWORD Segment = FIRST_MCB_SEGMENT; - PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment); - - DPRINT("Linking UMB\n"); - - /* Check if UMBs are already linked */ - if (DosUmbLinked) return FALSE; - - /* Find the last block */ - while ((Mcb->BlockType == 'M') && (Segment <= 0xFFFF)) - { - Segment += Mcb->Size + 1; - Mcb = SEGMENT_TO_MCB(Segment); - } - - /* Make sure it's valid */ - if (Mcb->BlockType != 'Z') return FALSE; - - /* Connect the MCB with the UMB chain */ - Mcb->BlockType = 'M'; - - DosUmbLinked = TRUE; - return TRUE; -} - -static BOOLEAN DosUnlinkUmb(VOID) -{ - DWORD Segment = FIRST_MCB_SEGMENT; - PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment); - - DPRINT("Unlinking UMB\n"); - - /* Check if UMBs are already unlinked */ - if (!DosUmbLinked) return FALSE; - - /* Find the block preceding the MCB that links it with the UMB chain */ - while (Segment <= 0xFFFF) - { - if ((Segment + Mcb->Size) == (FIRST_MCB_SEGMENT + USER_MEMORY_SIZE)) - { - /* This is the last non-UMB segment */ - break; - } - - /* Advance to the next MCB */ - Segment += Mcb->Size + 1; - Mcb = SEGMENT_TO_MCB(Segment); - } - - /* Mark the MCB as the last MCB */ - Mcb->BlockType = 'Z'; - - DosUmbLinked = FALSE; - return TRUE; -} - -static VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner) -{ - PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1); - - /* Just set the owner */ - Mcb->OwnerPsp = NewOwner; -} - static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL, LPCSTR ProgramName) { @@ -471,13 +113,8 @@ static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL, return DestSegment; } - - - - - /* Taken from base/shell/cmd/console.c */ -BOOL IsConsoleHandle(HANDLE hHandle) +static BOOL IsConsoleHandle(HANDLE hHandle) { DWORD dwMode; @@ -500,12 +137,59 @@ BOOL IsConsoleHandle(HANDLE hHandle) return GetConsoleMode(hHandle, &dwMode); } +static inline PDOS_SFT_ENTRY DosFindFreeSftEntry(VOID) +{ + INT i; + + for (i = 0; i < DOS_SFT_SIZE; i++) + { + if (DosSystemFileTable[i].Type == DOS_SFT_ENTRY_NONE) + { + return &DosSystemFileTable[i]; + } + } + + return NULL; +} + +static inline PDOS_SFT_ENTRY DosFindWin32SftEntry(HANDLE Handle) +{ + INT i; + + for (i = 0; i < DOS_SFT_SIZE; i++) + { + if (DosSystemFileTable[i].Type == DOS_SFT_ENTRY_WIN32 + && DosSystemFileTable[i].Handle == Handle) + { + return &DosSystemFileTable[i]; + } + } + + return NULL; +} + +static inline PDOS_SFT_ENTRY DosFindDeviceSftEntry(PDOS_DEVICE_NODE Device) +{ + INT i; + + for (i = 0; i < DOS_SFT_SIZE; i++) + { + if (DosSystemFileTable[i].Type == DOS_SFT_ENTRY_DEVICE + && DosSystemFileTable[i].DeviceNode == Device) + { + return &DosSystemFileTable[i]; + } + } + + return NULL; +} + WORD DosOpenHandle(HANDLE Handle) { - BYTE i; WORD DosHandle; PDOS_PSP PspBlock; LPBYTE HandleTable; + PDOS_SFT_ENTRY SftEntry; /* The system PSP has no handle table */ if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE; @@ -524,59 +208,95 @@ WORD DosOpenHandle(HANDLE Handle) if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE; /* Check if the handle is already in the SFT */ - for (i = 0; i < DOS_SFT_SIZE; i++) - { - /* Check if this is the same handle */ - if (DosSystemFileTable[i].Handle != Handle) continue; + SftEntry = DosFindWin32SftEntry(Handle); + if (SftEntry != NULL) + { /* Already in the table, reference it */ - DosSystemFileTable[i].RefCount++; - - /* Set the JFT entry to that SFT index */ - HandleTable[DosHandle] = i; - - /* Return the new handle */ - return DosHandle; + SftEntry->RefCount++; + goto Finish; } - /* Add the handle to the SFT */ - for (i = 0; i < DOS_SFT_SIZE; i++) + /* Find a free SFT entry to use */ + SftEntry = DosFindFreeSftEntry(); + if (SftEntry == NULL) { - /* Make sure this is an empty table entry */ - if (DosSystemFileTable[i].Handle != INVALID_HANDLE_VALUE) continue; - - /* Initialize the empty table entry */ - DosSystemFileTable[i].Handle = Handle; - DosSystemFileTable[i].RefCount = 1; - - /* Set the JFT entry to that SFT index */ - HandleTable[DosHandle] = i; - - /* Return the new handle */ - return DosHandle; + /* The SFT is full */ + return INVALID_DOS_HANDLE; } - /* The SFT is full */ - return INVALID_DOS_HANDLE; + /* Initialize the empty table entry */ + SftEntry->Type = DOS_SFT_ENTRY_WIN32; + SftEntry->Handle = Handle; + SftEntry->RefCount = 1; + +Finish: + + /* Set the JFT entry to that SFT index */ + HandleTable[DosHandle] = ARRAY_INDEX(SftEntry, DosSystemFileTable); + + /* Return the new handle */ + return DosHandle; } -HANDLE DosGetRealHandle(WORD DosHandle) +WORD DosOpenDevice(PDOS_DEVICE_NODE Device) { + WORD DosHandle; PDOS_PSP PspBlock; LPBYTE HandleTable; + PDOS_SFT_ENTRY SftEntry; + + DPRINT("DosOpenDevice(\"%Z\")\n", &Device->Name); /* The system PSP has no handle table */ - if (CurrentPsp == SYSTEM_PSP) return INVALID_HANDLE_VALUE; + if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE; /* Get a pointer to the handle table */ PspBlock = SEGMENT_TO_PSP(CurrentPsp); HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr); - /* Make sure the handle is open */ - if (HandleTable[DosHandle] == 0xFF) return INVALID_HANDLE_VALUE; + /* Find a free entry in the JFT */ + for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++) + { + if (HandleTable[DosHandle] == 0xFF) break; + } - /* Return the Win32 handle */ - return DosSystemFileTable[HandleTable[DosHandle]].Handle; + /* If there are no free entries, fail */ + if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE; + + /* Check if the device is already in the SFT */ + SftEntry = DosFindDeviceSftEntry(Device); + + if (SftEntry != NULL) + { + /* Already in the table, reference it */ + SftEntry->RefCount++; + goto Finish; + } + + /* Find a free SFT entry to use */ + SftEntry = DosFindFreeSftEntry(); + if (SftEntry == NULL) + { + /* The SFT is full */ + return INVALID_DOS_HANDLE; + } + + /* Initialize the empty table entry */ + SftEntry->Type = DOS_SFT_ENTRY_DEVICE; + SftEntry->DeviceNode = Device; + SftEntry->RefCount = 1; + +Finish: + + /* Call the open routine, if it exists */ + if (Device->OpenRoutine) Device->OpenRoutine(Device); + + /* Set the JFT entry to that SFT index */ + HandleTable[DosHandle] = ARRAY_INDEX(SftEntry, DosSystemFileTable); + + /* Return the new handle */ + return DosHandle; } static VOID DosCopyHandleTable(LPBYTE DestinationTable) @@ -591,31 +311,77 @@ static VOID DosCopyHandleTable(LPBYTE DestinationTable) /* Check if this is the initial process */ if (CurrentPsp == SYSTEM_PSP) { - /* Set up the standard I/O devices */ - for (i = 0; i <= 2; i++) + PDOS_SFT_ENTRY SftEntry; + HANDLE StandardHandles[3]; + PDOS_DEVICE_NODE ConIn = DosGetDevice("CONIN$"); + PDOS_DEVICE_NODE ConOut = DosGetDevice("CONOUT$"); + ASSERT(ConIn != NULL && ConOut != NULL); + + /* Get the native standard handles */ + StandardHandles[0] = GetStdHandle(STD_INPUT_HANDLE); + StandardHandles[1] = GetStdHandle(STD_OUTPUT_HANDLE); + StandardHandles[2] = GetStdHandle(STD_ERROR_HANDLE); + + for (i = 0; i < 3; i++) { - /* Set the index in the SFT */ - DestinationTable[i] = (BYTE)i; + PDOS_DEVICE_NODE Device = (i == 0) ? ConIn : ConOut; + + /* Find the corresponding SFT entry */ + if (IsConsoleHandle(StandardHandles[i])) + { + SftEntry = DosFindDeviceSftEntry(Device); + } + else + { + SftEntry = DosFindWin32SftEntry(StandardHandles[i]); + } + + if (SftEntry == NULL) + { + /* Create a new SFT entry for it */ + SftEntry = DosFindFreeSftEntry(); + + if (SftEntry == NULL) + { + DPRINT1("Cannot create standard handle %d, the SFT is full!\n", i); + continue; + } + + SftEntry->RefCount = 0; + + if (IsConsoleHandle(StandardHandles[i])) + { + SftEntry->Type = DOS_SFT_ENTRY_DEVICE; + SftEntry->DeviceNode = Device; + + /* Call the open routine */ + if (Device->OpenRoutine) Device->OpenRoutine(Device); + } + else + { + SftEntry->Type = DOS_SFT_ENTRY_WIN32; + SftEntry->Handle = StandardHandles[i]; + } + } + + SftEntry->RefCount++; + DestinationTable[i] = ARRAY_INDEX(SftEntry, DosSystemFileTable); + } + } + else + { + /* Get the parent PSP block and handle table */ + PspBlock = SEGMENT_TO_PSP(CurrentPsp); + SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr); + + /* Copy the first 20 handles into the new table */ + for (i = 0; i < DEFAULT_JFT_SIZE; i++) + { + DestinationTable[i] = SourceTable[i]; /* Increase the reference count */ - DosSystemFileTable[i].RefCount++; + DosSystemFileTable[SourceTable[i]].RefCount++; } - - /* Done */ - return; - } - - /* Get the parent PSP block and handle table */ - PspBlock = SEGMENT_TO_PSP(CurrentPsp); - SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr); - - /* Copy the first 20 handles into the new table */ - for (i = 0; i < 20; i++) - { - DestinationTable[i] = SourceTable[i]; - - /* Increase the reference count */ - DosSystemFileTable[SourceTable[i]].RefCount++; } } @@ -634,12 +400,12 @@ static BOOLEAN DosResizeHandleTable(WORD NewSize) return TRUE; } - if (PspBlock->HandleTableSize > 20) + if (PspBlock->HandleTableSize > DEFAULT_JFT_SIZE) { /* Get the segment of the current table */ Segment = (LOWORD(PspBlock->HandleTablePtr) >> 4) + HIWORD(PspBlock->HandleTablePtr); - if (NewSize <= 20) + if (NewSize <= DEFAULT_JFT_SIZE) { /* Get the current handle table */ HandleTable = FAR_POINTER(PspBlock->HandleTablePtr); @@ -679,7 +445,7 @@ static BOOLEAN DosResizeHandleTable(WORD NewSize) PspBlock->HandleTableSize = NewSize; } } - else if (NewSize > 20) + else if (NewSize > DEFAULT_JFT_SIZE) { Segment = DosAllocateMemory(NewSize, NULL); if (Segment == 0) return FALSE; @@ -702,9 +468,9 @@ static BOOLEAN DosResizeHandleTable(WORD NewSize) static BOOLEAN DosCloseHandle(WORD DosHandle) { - BYTE SftIndex; PDOS_PSP PspBlock; LPBYTE HandleTable; + PDOS_SFT_ENTRY SftEntry; DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle); @@ -718,18 +484,45 @@ static BOOLEAN DosCloseHandle(WORD DosHandle) /* Make sure the handle is open */ if (HandleTable[DosHandle] == 0xFF) return FALSE; + /* Make sure the SFT entry is valid */ + SftEntry = &DosSystemFileTable[HandleTable[DosHandle]]; + if (SftEntry->Type == DOS_SFT_ENTRY_NONE) return FALSE; + /* Decrement the reference count of the SFT entry */ - SftIndex = HandleTable[DosHandle]; - DosSystemFileTable[SftIndex].RefCount--; + SftEntry->RefCount--; /* Check if the reference count fell to zero */ - if (!DosSystemFileTable[SftIndex].RefCount) + if (!SftEntry->RefCount) { - /* Close the file, it's no longer needed */ - CloseHandle(DosSystemFileTable[SftIndex].Handle); + switch (SftEntry->Type) + { + case DOS_SFT_ENTRY_WIN32: + { + /* Close the win32 handle and clear it */ + CloseHandle(SftEntry->Handle); - /* Clear the handle */ - DosSystemFileTable[SftIndex].Handle = INVALID_HANDLE_VALUE; + break; + } + + case DOS_SFT_ENTRY_DEVICE: + { + PDOS_DEVICE_NODE Node = SftEntry->DeviceNode; + + /* Call the close routine, if it exists */ + if (Node->CloseRoutine) SftEntry->DeviceNode->CloseRoutine(SftEntry->DeviceNode); + + break; + } + + default: + { + /* Shouldn't happen */ + ASSERT(FALSE); + } + } + + /* Invalidate the SFT entry */ + SftEntry->Type = DOS_SFT_ENTRY_NONE; } /* Clear the entry in the JFT */ @@ -776,12 +569,6 @@ static BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle) return TRUE; } - - - - - - static BOOLEAN DosChangeDrive(BYTE Drive) { WCHAR DirectoryPath[DOS_CMDLINE_LENGTH]; @@ -871,6 +658,25 @@ static BOOLEAN DosChangeDirectory(LPSTR Directory) /* PUBLIC FUNCTIONS ***********************************************************/ +PDOS_SFT_ENTRY DosGetSftEntry(WORD DosHandle) +{ + PDOS_PSP PspBlock; + LPBYTE HandleTable; + + /* The system PSP has no handle table */ + if (CurrentPsp == SYSTEM_PSP) return NULL; + + /* Get a pointer to the handle table */ + PspBlock = SEGMENT_TO_PSP(CurrentPsp); + HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr); + + /* Make sure the handle is open */ + if (HandleTable[DosHandle] == 0xFF) return NULL; + + /* Return a pointer to the SFT entry */ + return &DosSystemFileTable[HandleTable[DosHandle]]; +} + VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment) { PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment); @@ -1020,19 +826,22 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType, /* Make sure it does not pass 0xFFFF */ if (ExeSize > 0xFFFF) ExeSize = 0xFFFF; - /* Reduce the size one by one until the allocation is successful */ - for (i = Header->e_maxalloc; i >= Header->e_minalloc; i--, ExeSize--) - { - /* Try to allocate that much memory */ - Segment = DosAllocateMemory((WORD)ExeSize, NULL); - if (Segment != 0) break; - } + /* Try to allocate that much memory */ + Segment = DosAllocateMemory((WORD)ExeSize, &MaxAllocSize); - /* Check if at least the lowest allocation was successful */ if (Segment == 0) { - Result = DosLastError; - goto Cleanup; + /* Check if there's at least enough memory for the minimum size */ + if (MaxAllocSize < (ExeSize - Header->e_maxalloc + Header->e_minalloc)) + { + Result = DosLastError; + goto Cleanup; + } + + /* Allocate that minimum amount */ + ExeSize = MaxAllocSize; + Segment = DosAllocateMemory((WORD)ExeSize, NULL); + ASSERT(Segment != 0); } /* Initialize the PSP */ @@ -1403,11 +1212,12 @@ Done: BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle) { - HANDLE Handle = DosGetRealHandle(FileHandle); + PDOS_SFT_ENTRY Entry = DosGetSftEntry(FileHandle); + PDOS_DEVICE_NODE Node = Entry->DeviceNode; - if (Handle == INVALID_HANDLE_VALUE) + /* Make sure it exists and is a device */ + if (!Entry || Entry->Type != DOS_SFT_ENTRY_DEVICE) { - /* Doesn't exist */ DosLastError = ERROR_FILE_NOT_FOUND; return FALSE; } @@ -1417,32 +1227,68 @@ BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle) /* Get Device Information */ case 0x00: { - WORD InfoWord = 0; - /* * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm * for a list of possible flags. */ - if (Handle == DosSystemFileTable[DOS_INPUT_HANDLE].Handle) - { - /* Console input */ - InfoWord |= 1 << 0; - - /* It is a device */ - InfoWord |= 1 << 7; - } - else if (Handle == DosSystemFileTable[DOS_OUTPUT_HANDLE].Handle) - { - /* Console output */ - InfoWord |= 1 << 1; - - /* It is a device */ - InfoWord |= 1 << 7; - } - /* Return the device information word */ - setDX(InfoWord); + setDX(Node->DeviceAttributes); + return TRUE; + } + + /* Set Device Information */ + case 0x01: + { + Node->DeviceAttributes = getDX(); + return TRUE; + } + + /* Read From Device I/O Control Channel */ + case 0x02: + { + WORD Length = getCX(); + + if (!(Node->DeviceAttributes & DOS_DEVATTR_IOCTL)) + { + DosLastError = ERROR_INVALID_FUNCTION; + return FALSE; + } + + /* Do nothing if there is no IOCTL routine */ + if (!Node->IoctlReadRoutine) + { + setAX(0); + return TRUE; + } + + Node->IoctlReadRoutine(Node, MAKELONG(getDX(), getDS()), &Length); + + setAX(Length); + return TRUE; + } + + /* Write To Device I/O Control Channel */ + case 0x03: + { + WORD Length = getCX(); + + if (!(Node->DeviceAttributes & DOS_DEVATTR_IOCTL)) + { + DosLastError = ERROR_INVALID_FUNCTION; + return FALSE; + } + + /* Do nothing if there is no IOCTL routine */ + if (!Node->IoctlWriteRoutine) + { + setAX(0); + return TRUE; + } + + Node->IoctlWriteRoutine(Node, MAKELONG(getDX(), getDS()), &Length); + + setAX(Length); return TRUE; } @@ -2130,13 +1976,24 @@ VOID WINAPI DosInt21h(LPWORD Stack) break; } - /* Open File */ + /* Open File or Device */ case 0x3D: { WORD FileHandle; - WORD ErrorCode = DosOpenFile(&FileHandle, - (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()), - getAL()); + WORD ErrorCode; + LPCSTR FileName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()); + PDOS_DEVICE_NODE Device = DosGetDevice(FileName); + + if (Device) + { + FileHandle = DosOpenDevice(Device); + ErrorCode = (FileHandle != INVALID_DOS_HANDLE) + ? ERROR_SUCCESS : ERROR_TOO_MANY_OPEN_FILES; + } + else + { + ErrorCode = DosOpenFile(&FileHandle, FileName, getAL()); + } if (ErrorCode == ERROR_SUCCESS) { @@ -2152,7 +2009,7 @@ VOID WINAPI DosInt21h(LPWORD Stack) break; } - /* Close File */ + /* Close File or Device */ case 0x3E: { if (DosCloseHandle(getBX())) @@ -2174,11 +2031,11 @@ VOID WINAPI DosInt21h(LPWORD Stack) WORD BytesRead = 0; WORD ErrorCode; - DPRINT("INT 21h, AH = 3Fh\n"); + DPRINT1("INT 21h, AH = 3Fh\n"); DoEcho = TRUE; ErrorCode = DosReadFile(getBX(), - SEG_OFF_TO_PTR(getDS(), getDX()), + MAKELONG(getDX(), getDS()), getCX(), &BytesRead); DoEcho = FALSE; @@ -2202,7 +2059,7 @@ VOID WINAPI DosInt21h(LPWORD Stack) { WORD BytesWritten = 0; WORD ErrorCode = DosWriteFile(getBX(), - SEG_OFF_TO_PTR(getDS(), getDX()), + MAKELONG(getDX(), getDS()), getCX(), &BytesWritten); @@ -2335,9 +2192,9 @@ VOID WINAPI DosInt21h(LPWORD Stack) case 0x45: { WORD NewHandle; - HANDLE Handle = DosGetRealHandle(getBX()); + PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX()); - if (Handle == INVALID_HANDLE_VALUE) + if (SftEntry == NULL || SftEntry->Type == DOS_SFT_ENTRY_NONE) { /* The handle is invalid */ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; @@ -2346,7 +2203,26 @@ VOID WINAPI DosInt21h(LPWORD Stack) } /* Open a new handle to the same entry */ - NewHandle = DosOpenHandle(Handle); + switch (SftEntry->Type) + { + case DOS_SFT_ENTRY_WIN32: + { + NewHandle = DosOpenHandle(SftEntry->Handle); + break; + } + + case DOS_SFT_ENTRY_DEVICE: + { + NewHandle = DosOpenDevice(SftEntry->DeviceNode); + break; + } + + default: + { + /* Shouldn't happen */ + ASSERT(FALSE); + } + } if (NewHandle == INVALID_DOS_HANDLE) { @@ -2742,9 +2618,9 @@ VOID WINAPI DosInt21h(LPWORD Stack) /* Lock/Unlock Region of File */ case 0x5C: { - HANDLE Handle = DosGetRealHandle(getBX()); + PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX()); - if (Handle == INVALID_HANDLE_VALUE) + if (SftEntry->Type != DOS_SFT_ENTRY_WIN32) { /* The handle is invalid */ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; @@ -2755,7 +2631,7 @@ VOID WINAPI DosInt21h(LPWORD Stack) if (getAL() == 0x00) { /* Lock region of file */ - if (LockFile(Handle, + if (LockFile(SftEntry->Handle, MAKELONG(getCX(), getDX()), 0, MAKELONG(getSI(), getDI()), 0)) { @@ -2770,7 +2646,7 @@ VOID WINAPI DosInt21h(LPWORD Stack) else if (getAL() == 0x01) { /* Unlock region of file */ - if (UnlockFile(Handle, + if (UnlockFile(SftEntry->Handle, MAKELONG(getCX(), getDX()), 0, MAKELONG(getSI(), getDI()), 0)) { @@ -2979,6 +2855,7 @@ BOOLEAN DosKRNLInitialize(VOID) CHAR CurrentDirectory[MAX_PATH]; CHAR DosDirectory[DOS_DIR_LENGTH]; LPSTR Path; + PDOS_DEVICE_NODE ConInDevice, ConOutDevice; FILE *Stream; WCHAR Buffer[256]; @@ -3031,19 +2908,15 @@ BOOLEAN DosKRNLInitialize(VOID) /* Initialize the SFT */ for (i = 0; i < DOS_SFT_SIZE; i++) { - DosSystemFileTable[i].Handle = INVALID_HANDLE_VALUE; + DosSystemFileTable[i].Type = DOS_SFT_ENTRY_NONE; DosSystemFileTable[i].RefCount = 0; } - /* Get handles to standard I/O devices */ - DosSystemFileTable[0].Handle = GetStdHandle(STD_INPUT_HANDLE); - DosSystemFileTable[1].Handle = GetStdHandle(STD_OUTPUT_HANDLE); - DosSystemFileTable[2].Handle = GetStdHandle(STD_ERROR_HANDLE); + /* Load the EMS driver */ + EmsDrvInitialize(); - /* Initialize the reference counts */ - DosSystemFileTable[0].RefCount = - DosSystemFileTable[1].RefCount = - DosSystemFileTable[2].RefCount = 1; + /* Load the CON driver */ + ConDrvInitialize(&ConInDevice, &ConOutDevice); #endif diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h index e2ad92d2681..1e56838ab96 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h @@ -12,6 +12,7 @@ /* INCLUDES *******************************************************************/ #include "ntvdm.h" +#include "device.h" /**/ #include "int32.h" /**/ @@ -48,13 +49,7 @@ #define NUM_DRIVES ('Z' - 'A' + 1) #define DOS_CHAR_ATTRIBUTE 0x07 #define DOS_PROGRAM_NAME_TAG 0x0001 - -enum DOS_ALLOC_STRATEGY -{ - DOS_ALLOC_FIRST_FIT, - DOS_ALLOC_BEST_FIT, - DOS_ALLOC_LAST_FIT -}; +#define DEFAULT_JFT_SIZE 20 typedef enum { @@ -63,16 +58,26 @@ typedef enum DOS_LOAD_OVERLAY = 0x03 } DOS_EXEC_TYPE; -#pragma pack(push, 1) - -typedef struct _DOS_MCB +typedef enum { - CHAR BlockType; - WORD OwnerPsp; - WORD Size; - BYTE Unused[3]; - CHAR Name[8]; -} DOS_MCB, *PDOS_MCB; + DOS_SFT_ENTRY_NONE, + DOS_SFT_ENTRY_WIN32, + DOS_SFT_ENTRY_DEVICE +} DOS_SFT_ENTRY_TYPE; + +typedef struct _DOS_SFT_ENTRY +{ + DOS_SFT_ENTRY_TYPE Type; + WORD RefCount; + + union + { + HANDLE Handle; + PDOS_DEVICE_NODE DeviceNode; + }; +} DOS_SFT_ENTRY, *PDOS_SFT_ENTRY; + +#pragma pack(push, 1) typedef struct _DOS_FCB { @@ -169,7 +174,11 @@ typedef struct _DOS_COUNTRY_CODE_BUFFER #pragma pack(pop) +/* VARIABLES ******************************************************************/ + extern BOOLEAN DoEcho; +extern WORD CurrentPsp; +extern WORD DosLastError; /* FUNCTIONS ******************************************************************/ @@ -190,15 +199,17 @@ BOOLEAN DosCheckInput(VOID); VOID DosPrintCharacter(WORD FileHandle, CHAR Character); BOOLEAN DosBIOSInitialize(VOID); - +VOID EmsDrvInitialize(VOID); +VOID EmsDrvCleanup(VOID); +VOID ConDrvInitialize(PDOS_DEVICE_NODE *InputDevice, PDOS_DEVICE_NODE *OutputDevice); +VOID ConDrvCleanup(VOID); /* * DOS Kernel Functions * See dos.c */ -BOOL IsConsoleHandle(HANDLE hHandle); WORD DosOpenHandle(HANDLE Handle); -HANDLE DosGetRealHandle(WORD DosHandle); +PDOS_SFT_ENTRY DosGetSftEntry(WORD DosHandle); WORD DosCreateFileEx(LPWORD Handle, LPWORD CreationStatus, @@ -214,11 +225,11 @@ WORD DosOpenFile(LPWORD Handle, LPCSTR FilePath, BYTE AccessShareModes); WORD DosReadFile(WORD FileHandle, - LPVOID Buffer, + DWORD Buffer, WORD Count, LPWORD BytesRead); WORD DosWriteFile(WORD FileHandle, - LPVOID Buffer, + DWORD Buffer, WORD Count, LPWORD BytesWritten); WORD DosSeekFile(WORD FileHandle, diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dosfiles.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dosfiles.c index 9bf4d0839b8..5750a925577 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dosfiles.c +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dosfiles.c @@ -394,129 +394,84 @@ WORD DosOpenFile(LPWORD Handle, } WORD DosReadFile(WORD FileHandle, - LPVOID Buffer, + DWORD Buffer, WORD Count, LPWORD BytesRead) { WORD Result = ERROR_SUCCESS; - DWORD BytesRead32 = 0; - HANDLE Handle = DosGetRealHandle(FileHandle); + PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(FileHandle); DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle, Count); - /* Make sure the handle is valid */ - if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE; - - if (IsConsoleHandle(Handle)) + if (SftEntry->Type == DOS_SFT_ENTRY_WIN32) { - CHAR Character; + DWORD BytesRead32 = 0; - /* - * Use BIOS Get Keystroke function - */ - - /* Save AX */ - USHORT AX = getAX(); - - for (BytesRead32 = 0; BytesRead32 < Count; BytesRead32++) - { - /* Call the BIOS INT 16h, AH=00h "Get Keystroke" */ - setAH(0x00); - Int32Call(&DosContext, BIOS_KBD_INTERRUPT); - - /* Retrieve the character in AL (scan code is in AH) */ - Character = getAL(); - - if (DoEcho) DosPrintCharacter(DOS_OUTPUT_HANDLE, Character); - - ((PCHAR)Buffer)[BytesRead32] = Character; - - /* Stop on first carriage return */ - if (Character == '\r') - { - if (DoEcho) DosPrintCharacter(DOS_OUTPUT_HANDLE, '\n'); - break; - } - - // BytesRead32++; - } - - /* Restore AX */ - setAX(AX); - } - else - { /* Read the file */ - if (!ReadFile(Handle, Buffer, Count /* * sizeof(CHAR) */, &BytesRead32, NULL)) + if (!ReadFile(SftEntry->Handle, FAR_POINTER(Buffer), Count, &BytesRead32, NULL)) { /* Store the error code */ Result = (WORD)GetLastError(); } - } - /* The number of bytes read is always 16-bit */ - *BytesRead = LOWORD(BytesRead32); + /* The number of bytes read is always 16-bit */ + *BytesRead = LOWORD(BytesRead32); + } + else if (SftEntry->Type == DOS_SFT_ENTRY_DEVICE) + { + if (!SftEntry->DeviceNode->ReadRoutine) return ERROR_INVALID_FUNCTION; + + /* Read the device */ + SftEntry->DeviceNode->ReadRoutine(SftEntry->DeviceNode, Buffer, &Count); + *BytesRead = Count; + } + else + { + /* Invalid handle */ + return ERROR_INVALID_HANDLE; + } /* Return the error code */ return Result; } WORD DosWriteFile(WORD FileHandle, - LPVOID Buffer, + DWORD Buffer, WORD Count, LPWORD BytesWritten) { WORD Result = ERROR_SUCCESS; - DWORD BytesWritten32 = 0; - HANDLE Handle = DosGetRealHandle(FileHandle); + PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(FileHandle); DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle, Count); - /* Make sure the handle is valid */ - if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE; - - if (IsConsoleHandle(Handle)) + if (SftEntry->Type == DOS_SFT_ENTRY_WIN32) { - /* - * Use BIOS Teletype function - */ + DWORD BytesWritten32 = 0; - /* Save AX and BX */ - USHORT AX = getAX(); - USHORT BX = getBX(); - - // FIXME: Use BIOS Write String function INT 10h, AH=13h ?? - - for (BytesWritten32 = 0; BytesWritten32 < Count; BytesWritten32++) - { - /* Set the parameters */ - setAL(((PCHAR)Buffer)[BytesWritten32]); - setBL(DOS_CHAR_ATTRIBUTE); - setBH(Bda->VideoPage); - - /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */ - setAH(0x0E); - Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT); - - // BytesWritten32++; - } - - /* Restore AX and BX */ - setBX(BX); - setAX(AX); - } - else - { /* Write the file */ - if (!WriteFile(Handle, Buffer, Count /* * sizeof(CHAR) */, &BytesWritten32, NULL)) + if (!WriteFile(SftEntry->Handle, FAR_POINTER(Buffer), Count, &BytesWritten32, NULL)) { /* Store the error code */ Result = (WORD)GetLastError(); } - } - /* The number of bytes written is always 16-bit */ - *BytesWritten = LOWORD(BytesWritten32); + /* The number of bytes written is always 16-bit */ + *BytesWritten = LOWORD(BytesWritten32); + } + else if (SftEntry->Type == DOS_SFT_ENTRY_DEVICE) + { + if (!SftEntry->DeviceNode->WriteRoutine) return ERROR_INVALID_FUNCTION; + + /* Read the device */ + SftEntry->DeviceNode->WriteRoutine(SftEntry->DeviceNode, Buffer, &Count); + *BytesWritten = Count; + } + else + { + /* Invalid handle */ + return ERROR_INVALID_HANDLE; + } /* Return the error code */ return Result; @@ -529,15 +484,23 @@ WORD DosSeekFile(WORD FileHandle, { WORD Result = ERROR_SUCCESS; DWORD FilePointer; - HANDLE Handle = DosGetRealHandle(FileHandle); + PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(FileHandle); DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n", FileHandle, Offset, Origin); - /* Make sure the handle is valid */ - if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE; + if (SftEntry->Type == DOS_SFT_ENTRY_NONE) + { + /* Invalid handle */ + return ERROR_INVALID_HANDLE; + } + else if (SftEntry->Type == DOS_SFT_ENTRY_DEVICE) + { + /* For character devices, always return success */ + return ERROR_SUCCESS; + } /* Check if the origin is valid */ if (Origin != FILE_BEGIN && Origin != FILE_CURRENT && Origin != FILE_END) @@ -545,17 +508,7 @@ WORD DosSeekFile(WORD FileHandle, return ERROR_INVALID_FUNCTION; } - /* Move the file pointer */ - if (IsConsoleHandle(Handle)) - { - /* Always succeeds when seeking a console handle */ - FilePointer = 0; - Result = ERROR_SUCCESS; - } - else - { - FilePointer = SetFilePointer(Handle, Offset, NULL, Origin); - } + FilePointer = SetFilePointer(SftEntry->Handle, Offset, NULL, Origin); /* Check if there's a possibility the operation failed */ if (FilePointer == INVALID_SET_FILE_POINTER) @@ -579,17 +532,34 @@ WORD DosSeekFile(WORD FileHandle, BOOL DosFlushFileBuffers(WORD FileHandle) { - HANDLE Handle = DosGetRealHandle(FileHandle); + PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(FileHandle); - /* Make sure the handle is valid */ - if (Handle == INVALID_HANDLE_VALUE) return FALSE; + switch (SftEntry->Type) + { + case DOS_SFT_ENTRY_WIN32: + { + return FlushFileBuffers(SftEntry->Handle); + } - /* - * This function can either flush files back to disks, or flush - * console input buffers, in which case there is no need to check - * whether the handle is a console handle. FlushFileBuffers() - * automatically does this check and calls FlushConsoleInputBuffer() - * if needed. - */ - return FlushFileBuffers(Handle); + case DOS_SFT_ENTRY_DEVICE: + { + if (SftEntry->DeviceNode->FlushInputRoutine) + { + SftEntry->DeviceNode->FlushInputRoutine(SftEntry->DeviceNode); + } + + if (SftEntry->DeviceNode->FlushOutputRoutine) + { + SftEntry->DeviceNode->FlushOutputRoutine(SftEntry->DeviceNode); + } + + return TRUE; + } + + default: + { + /* Invalid handle */ + return FALSE; + } + } } diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c new file mode 100644 index 00000000000..6579394b26d --- /dev/null +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c @@ -0,0 +1,48 @@ +/* + * COPYRIGHT: GPLv2+ - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: emsdrv.c + * PURPOSE: DOS EMS Driver + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "dos.h" +#include "dos/dem.h" +#include "device.h" + +#define EMS_DEVICE_NAME "EMMXXXX0" + +/* PRIVATE VARIABLES **********************************************************/ + +static PDOS_DEVICE_NODE Node; + +/* PRIVATE FUNCTIONS **********************************************************/ + +WORD NTAPI EmsDrvDispatchIoctlRead(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length) +{ + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + return DOS_DEVSTAT_DONE; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID EmsDrvInitialize(VOID) +{ + /* Create the device */ + Node = DosCreateDevice(DOS_DEVATTR_IOCTL + | DOS_DEVATTR_CHARACTER, + EMS_DEVICE_NAME); + Node->IoctlReadRoutine = EmsDrvDispatchIoctlRead; +} + +VOID EmsDrvCleanup(VOID) +{ + /* Delete the device */ + DosDeleteDevice(Node); +} diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.c new file mode 100644 index 00000000000..b6fb32d3106 --- /dev/null +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.c @@ -0,0 +1,378 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: dos/dos32krnl/memory.c + * PURPOSE: DOS32 Memory Manager + * PROGRAMMERS: Aleksandar Andrejevic + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" + +#include "dos.h" +#include "dos/dem.h" +#include "memory.h" + +/* PUBLIC VARIABLES ***********************************************************/ + +BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT; +BOOLEAN DosUmbLinked = FALSE; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static VOID DosCombineFreeBlocks(WORD StartBlock) +{ + PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb; + + /* If this is the last block or it's not free, quit */ + if (CurrentMcb->BlockType == 'Z' || CurrentMcb->OwnerPsp != 0) return; + + while (TRUE) + { + /* Get a pointer to the next MCB */ + NextMcb = SEGMENT_TO_MCB(StartBlock + CurrentMcb->Size + 1); + + /* Check if the next MCB is free */ + if (NextMcb->OwnerPsp == 0) + { + /* Combine them */ + CurrentMcb->Size += NextMcb->Size + 1; + CurrentMcb->BlockType = NextMcb->BlockType; + NextMcb->BlockType = 'I'; + } + else + { + /* No more adjoining free blocks */ + break; + } + } +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable) +{ + WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0; + PDOS_MCB CurrentMcb, NextMcb; + BOOLEAN SearchUmb = FALSE; + + DPRINT("DosAllocateMemory: Size 0x%04X\n", Size); + + if (DosUmbLinked && (DosAllocStrategy & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))) + { + /* Search UMB first */ + Segment = UMB_START_SEGMENT; + SearchUmb = TRUE; + } + + while (TRUE) + { + /* Get a pointer to the MCB */ + CurrentMcb = SEGMENT_TO_MCB(Segment); + + /* Make sure it's valid */ + if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') + { + DPRINT("The DOS memory arena is corrupted!\n"); + DosLastError = ERROR_ARENA_TRASHED; + return 0; + } + + /* Only check free blocks */ + if (CurrentMcb->OwnerPsp != 0) goto Next; + + /* Combine this free block with adjoining free blocks */ + DosCombineFreeBlocks(Segment); + + /* Update the maximum block size */ + if (CurrentMcb->Size > MaxSize) MaxSize = CurrentMcb->Size; + + /* Check if this block is big enough */ + if (CurrentMcb->Size < Size) goto Next; + + switch (DosAllocStrategy & 0x3F) + { + case DOS_ALLOC_FIRST_FIT: + { + /* For first fit, stop immediately */ + Result = Segment; + goto Done; + } + + case DOS_ALLOC_BEST_FIT: + { + /* For best fit, update the smallest block found so far */ + if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size)) + { + Result = Segment; + } + + break; + } + + case DOS_ALLOC_LAST_FIT: + { + /* For last fit, make the current block the result, but keep searching */ + Result = Segment; + break; + } + } + +Next: + /* If this was the last MCB in the chain, quit */ + if (CurrentMcb->BlockType == 'Z') + { + /* Check if nothing was found while searching through UMBs */ + if ((Result == 0) && SearchUmb && (DosAllocStrategy & DOS_ALLOC_HIGH_LOW)) + { + /* Search low memory */ + Segment = FIRST_MCB_SEGMENT; + continue; + } + + break; + } + + /* Otherwise, update the segment and continue */ + Segment += CurrentMcb->Size + 1; + } + +Done: + + /* If we didn't find a free block, return 0 */ + if (Result == 0) + { + DosLastError = ERROR_NOT_ENOUGH_MEMORY; + if (MaxAvailable) *MaxAvailable = MaxSize; + return 0; + } + + /* Get a pointer to the MCB */ + CurrentMcb = SEGMENT_TO_MCB(Result); + + /* Check if the block is larger than requested */ + if (CurrentMcb->Size > Size) + { + /* It is, split it into two blocks */ + NextMcb = SEGMENT_TO_MCB(Result + Size + 1); + + /* Initialize the new MCB structure */ + NextMcb->BlockType = CurrentMcb->BlockType; + NextMcb->Size = CurrentMcb->Size - Size - 1; + NextMcb->OwnerPsp = 0; + + /* Update the current block */ + CurrentMcb->BlockType = 'M'; + CurrentMcb->Size = Size; + } + + /* Take ownership of the block */ + CurrentMcb->OwnerPsp = CurrentPsp; + + /* Return the segment of the data portion of the block */ + return Result + 1; +} + +BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable) +{ + BOOLEAN Success = TRUE; + WORD Segment = BlockData - 1, ReturnSize = 0, NextSegment; + PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), NextMcb; + + DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n", + BlockData, + NewSize); + + /* Make sure this is a valid, allocated block */ + if ((Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') || Mcb->OwnerPsp == 0) + { + Success = FALSE; + DosLastError = ERROR_INVALID_HANDLE; + goto Done; + } + + ReturnSize = Mcb->Size; + + /* Check if we need to expand or contract the block */ + if (NewSize > Mcb->Size) + { + /* We can't expand the last block */ + if (Mcb->BlockType != 'M') + { + Success = FALSE; + goto Done; + } + + /* Get the pointer and segment of the next MCB */ + NextSegment = Segment + Mcb->Size + 1; + NextMcb = SEGMENT_TO_MCB(NextSegment); + + /* Make sure the next segment is free */ + if (NextMcb->OwnerPsp != 0) + { + DPRINT("Cannot expand memory block: next segment is not free!\n"); + DosLastError = ERROR_NOT_ENOUGH_MEMORY; + Success = FALSE; + goto Done; + } + + /* Combine this free block with adjoining free blocks */ + DosCombineFreeBlocks(NextSegment); + + /* Set the maximum possible size of the block */ + ReturnSize += NextMcb->Size + 1; + + if (ReturnSize < NewSize) + { + DPRINT("Cannot expand memory block: insufficient free segments available!\n"); + DosLastError = ERROR_NOT_ENOUGH_MEMORY; + Success = FALSE; + goto Done; + } + + /* Maximize the current block */ + Mcb->Size = ReturnSize; + Mcb->BlockType = NextMcb->BlockType; + + /* Invalidate the next block */ + NextMcb->BlockType = 'I'; + + /* Check if the block is larger than requested */ + if (Mcb->Size > NewSize) + { + DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n", + Mcb->Size, + NewSize); + + /* It is, split it into two blocks */ + NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1); + + /* Initialize the new MCB structure */ + NextMcb->BlockType = Mcb->BlockType; + NextMcb->Size = Mcb->Size - NewSize - 1; + NextMcb->OwnerPsp = 0; + + /* Update the current block */ + Mcb->BlockType = 'M'; + Mcb->Size = NewSize; + } + } + else if (NewSize < Mcb->Size) + { + DPRINT("Shrinking block from 0x%04X to 0x%04X\n", + Mcb->Size, + NewSize); + + /* Just split the block */ + NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1); + NextMcb->BlockType = Mcb->BlockType; + NextMcb->Size = Mcb->Size - NewSize - 1; + NextMcb->OwnerPsp = 0; + + /* Update the MCB */ + Mcb->BlockType = 'M'; + Mcb->Size = NewSize; + } + +Done: + /* Check if the operation failed */ + if (!Success) + { + DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n", + ReturnSize); + + /* Return the maximum possible size */ + if (MaxAvailable) *MaxAvailable = ReturnSize; + } + + return Success; +} + +BOOLEAN DosFreeMemory(WORD BlockData) +{ + PDOS_MCB Mcb = SEGMENT_TO_MCB(BlockData - 1); + + DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData); + + /* Make sure the MCB is valid */ + if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') + { + DPRINT("MCB block type '%c' not valid!\n", Mcb->BlockType); + return FALSE; + } + + /* Mark the block as free */ + Mcb->OwnerPsp = 0; + + return TRUE; +} + +BOOLEAN DosLinkUmb(VOID) +{ + DWORD Segment = FIRST_MCB_SEGMENT; + PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment); + + DPRINT("Linking UMB\n"); + + /* Check if UMBs are already linked */ + if (DosUmbLinked) return FALSE; + + /* Find the last block */ + while ((Mcb->BlockType == 'M') && (Segment <= 0xFFFF)) + { + Segment += Mcb->Size + 1; + Mcb = SEGMENT_TO_MCB(Segment); + } + + /* Make sure it's valid */ + if (Mcb->BlockType != 'Z') return FALSE; + + /* Connect the MCB with the UMB chain */ + Mcb->BlockType = 'M'; + + DosUmbLinked = TRUE; + return TRUE; +} + +BOOLEAN DosUnlinkUmb(VOID) +{ + DWORD Segment = FIRST_MCB_SEGMENT; + PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment); + + DPRINT("Unlinking UMB\n"); + + /* Check if UMBs are already unlinked */ + if (!DosUmbLinked) return FALSE; + + /* Find the block preceding the MCB that links it with the UMB chain */ + while (Segment <= 0xFFFF) + { + if ((Segment + Mcb->Size) == (FIRST_MCB_SEGMENT + USER_MEMORY_SIZE)) + { + /* This is the last non-UMB segment */ + break; + } + + /* Advance to the next MCB */ + Segment += Mcb->Size + 1; + Mcb = SEGMENT_TO_MCB(Segment); + } + + /* Mark the MCB as the last MCB */ + Mcb->BlockType = 'Z'; + + DosUmbLinked = FALSE; + return TRUE; +} + +VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner) +{ + PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1); + + /* Just set the owner */ + Mcb->OwnerPsp = NewOwner; +} + diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.h b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.h new file mode 100644 index 00000000000..b70535ca968 --- /dev/null +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.h @@ -0,0 +1,46 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: dos/dos32krnl/memory.h + * PURPOSE: DOS32 Memory Manager + * PROGRAMMERS: Aleksandar Andrejevic + */ + +#ifndef _MEMORY_H_ +#define _MEMORY_H_ + +/* TYPEDEFS *******************************************************************/ + +enum DOS_ALLOC_STRATEGY +{ + DOS_ALLOC_FIRST_FIT, + DOS_ALLOC_BEST_FIT, + DOS_ALLOC_LAST_FIT +}; + +typedef struct _DOS_MCB +{ + CHAR BlockType; + WORD OwnerPsp; + WORD Size; + BYTE Unused[3]; + CHAR Name[8]; +} DOS_MCB, *PDOS_MCB; + +/* VARIABLES ******************************************************************/ + +extern BYTE DosAllocStrategy; +extern BOOLEAN DosUmbLinked; + +/* FUNCTIONS ******************************************************************/ + +WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable); +BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable); +BOOLEAN DosFreeMemory(WORD BlockData); +BOOLEAN DosLinkUmb(VOID); +BOOLEAN DosUnlinkUmb(VOID); +VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner); + +#endif // _MEMORY_H_ + +/* EOF */ diff --git a/reactos/subsystems/mvdm/ntvdm/emulator.h b/reactos/subsystems/mvdm/ntvdm/emulator.h index cf5d202a319..f0cdf849552 100644 --- a/reactos/subsystems/mvdm/ntvdm/emulator.h +++ b/reactos/subsystems/mvdm/ntvdm/emulator.h @@ -34,6 +34,7 @@ #define REAL_TO_PHYS(ptr) (PVOID)((ULONG_PTR)(ptr) + (ULONG_PTR)BaseAddress) #define PHYS_TO_REAL(ptr) (PVOID)((ULONG_PTR)(ptr) - (ULONG_PTR)BaseAddress) +#define ARRAY_INDEX(ptr, array) ((ULONG)(((ULONG_PTR)(ptr) - (ULONG_PTR)(array)) / sizeof(*array))) /* BCD-Binary conversion */