[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Thu, 26 Mar 2015 00:21:25 +0000 (00:21 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Thu, 26 Mar 2015 00:21:25 +0000 (00:21 +0000)
Implement DOS character device support and driver loading support.
Separate the DOS memory manager code from the main DOS kernel source file.

CORE-9370 #resolve #comment Committed in revision r66895.

svn path=/trunk/; revision=66895

14 files changed:
reactos/subsystems/mvdm/ntvdm/CMakeLists.txt
reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.c
reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.h
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/bios.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/condrv.c [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/device.c [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/device.h [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dosfiles.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.c [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.h [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/emulator.h

index e2c5a6d..ca53559 100644 (file)
@@ -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
index 5dfd8f0..30d428f 100644 (file)
@@ -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);
index bb13db1..9714fd6 100644 (file)
@@ -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;
index 7c44873..67b5093 100644 (file)
@@ -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;
 
 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 (file)
index 0000000..6406bfa
--- /dev/null
@@ -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 <theflash AT sdf DOT lonestar DOT org>
+ *                  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 (file)
index 0000000..a7a4c1f
--- /dev/null
@@ -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 <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* 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 (file)
index 0000000..01d9efd
--- /dev/null
@@ -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 <theflash AT sdf DOT lonestar DOT org>
+ */
+
+#ifndef _DEVICE_H_
+#define _DEVICE_H_
+
+#include <ndk/rtlfuncs.h>
+
+/* 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 */
index cdfe827..a4a455d 100644 (file)
@@ -17,6 +17,8 @@
 
 #include "dos.h"
 #include "dos/dem.h"
+#include "device.h"
+#include "memory.h"
 
 #include "bios/bios.h"
 
 
 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;
+        /* The SFT is full */
+        return INVALID_DOS_HANDLE;
+    }
 
-        /* Initialize the empty table entry */
-        DosSystemFileTable[i].Handle   = Handle;
-        DosSystemFileTable[i].RefCount = 1;
+    /* Initialize the empty table entry */
+    SftEntry->Type       = DOS_SFT_ENTRY_WIN32;
+    SftEntry->Handle     = Handle;
+    SftEntry->RefCount   = 1;
 
-        /* Set the JFT entry to that SFT index */
-        HandleTable[DosHandle] = i;
+Finish:
 
-        /* Return the new handle */
-        return DosHandle;
-    }
+    /* Set the JFT entry to that SFT index */
+    HandleTable[DosHandle] = ARRAY_INDEX(SftEntry, DosSystemFileTable);
 
-    /* The SFT is full */
-    return INVALID_DOS_HANDLE;
+    /* 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;
+    }
+
+    /* 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);
 
-    /* Return the Win32 handle */
-    return DosSystemFileTable[HandleTable[DosHandle]].Handle;
+    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;
 
-            /* Increase the reference count */
-            DosSystemFileTable[i].RefCount++;
-        }
+            /* Find the corresponding SFT entry */
+            if (IsConsoleHandle(StandardHandles[i]))
+            {
+                SftEntry = DosFindDeviceSftEntry(Device);
+            }
+            else
+            {
+                SftEntry = DosFindWin32SftEntry(StandardHandles[i]);
+            }
 
-        /* Done */
-        return;
-    }
+            if (SftEntry == NULL)
+            {
+                /* Create a new SFT entry for it */
+                SftEntry = DosFindFreeSftEntry();
 
-    /* Get the parent PSP block and handle table */
-    PspBlock = SEGMENT_TO_PSP(CurrentPsp);
-    SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
+                if (SftEntry == NULL)
+                {
+                    DPRINT1("Cannot create standard handle %d, the SFT is full!\n", i);
+                    continue;
+                }
 
-    /* Copy the first 20 handles into the new table */
-    for (i = 0; i < 20; i++)
+                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
     {
-        DestinationTable[i] = SourceTable[i];
+        /* Get the parent PSP block and handle table */
+        PspBlock = SEGMENT_TO_PSP(CurrentPsp);
+        SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
 
-        /* Increase the reference count */
-        DosSystemFileTable[SourceTable[i]].RefCount++;
+        /* 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[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);
+
+                break;
+            }
 
-        /* Clear the handle */
-        DosSystemFileTable[SftIndex].Handle = INVALID_HANDLE_VALUE;
+            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)
+            /* Return the device information word */
+            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))
             {
-                /* Console input */
-                InfoWord |= 1 << 0;
+                DosLastError = ERROR_INVALID_FUNCTION;
+                return FALSE;
+            }
 
-                /* It is a device */
-                InfoWord |= 1 << 7;
+            /* Do nothing if there is no IOCTL routine */
+            if (!Node->IoctlReadRoutine)
+            {
+                setAX(0);
+                return TRUE;
             }
-            else if (Handle == DosSystemFileTable[DOS_OUTPUT_HANDLE].Handle)
+
+            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))
             {
-                /* Console output */
-                InfoWord |= 1 << 1;
+                DosLastError = ERROR_INVALID_FUNCTION;
+                return FALSE;
+            }
 
-                /* It is a device */
-                InfoWord |= 1 << 7;
+            /* Do nothing if there is no IOCTL routine */
+            if (!Node->IoctlWriteRoutine)
+            {
+                setAX(0);
+                return TRUE;
             }
 
-            /* Return the device information word */
-            setDX(InfoWord);
+            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
 
index e2ad92d..1e56838 100644 (file)
@@ -12,6 +12,7 @@
 /* INCLUDES *******************************************************************/
 
 #include "ntvdm.h"
+#include "device.h"
 
 /**/ #include "int32.h" /**/
 
 #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 enum
+{
+    DOS_SFT_ENTRY_NONE,
+    DOS_SFT_ENTRY_WIN32,
+    DOS_SFT_ENTRY_DEVICE
+} DOS_SFT_ENTRY_TYPE;
 
-typedef struct _DOS_MCB
+typedef struct _DOS_SFT_ENTRY
 {
-    CHAR BlockType;
-    WORD OwnerPsp;
-    WORD Size;
-    BYTE Unused[3];
-    CHAR Name[8];
-} DOS_MCB, *PDOS_MCB;
+    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,
index 9bf4d08..5750a92 100644 (file)
@@ -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;
-
-        /*
-         * 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);
+        DWORD BytesRead32 = 0;
 
-            /* 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);
     }
+    else if (SftEntry->Type == DOS_SFT_ENTRY_DEVICE)
+    {
+        if (!SftEntry->DeviceNode->ReadRoutine) return ERROR_INVALID_FUNCTION;
 
-    /* The number of bytes read is always 16-bit */
-    *BytesRead = LOWORD(BytesRead32);
+        /* 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
-         */
-
-        /* Save AX and BX */
-        USHORT AX = getAX();
-        USHORT BX = getBX();
-
-        // FIXME: Use BIOS Write String function INT 10h, AH=13h ??
+        DWORD BytesWritten32 = 0;
 
-        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);
     }
+    else if (SftEntry->Type == DOS_SFT_ENTRY_DEVICE)
+    {
+        if (!SftEntry->DeviceNode->WriteRoutine) return ERROR_INVALID_FUNCTION;
 
-    /* The number of bytes written is always 16-bit */
-    *BytesWritten = LOWORD(BytesWritten32);
+        /* 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);
+
+    switch (SftEntry->Type)
+    {
+        case DOS_SFT_ENTRY_WIN32:
+        {
+            return FlushFileBuffers(SftEntry->Handle);
+        }
 
-    /* Make sure the handle is valid */
-    if (Handle == INVALID_HANDLE_VALUE) return FALSE;
+        case DOS_SFT_ENTRY_DEVICE:
+        {
+            if (SftEntry->DeviceNode->FlushInputRoutine)
+            {
+                SftEntry->DeviceNode->FlushInputRoutine(SftEntry->DeviceNode);
+            }
 
-    /*
-     * 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);
+            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 (file)
index 0000000..6579394
--- /dev/null
@@ -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 <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* 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 (file)
index 0000000..b6fb32d
--- /dev/null
@@ -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 <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* 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 (file)
index 0000000..b70535c
--- /dev/null
@@ -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 <theflash AT sdf DOT lonestar DOT org>
+ */
+
+#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 */
index cf5d202..f0cdf84 100644 (file)
@@ -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 */