[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Tue, 21 Apr 2015 22:48:28 +0000 (22:48 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Tue, 21 Apr 2015 22:48:28 +0000 (22:48 +0000)
- Move the EMS code from the BIOS to the DOS driver where it belongs.
- Expand the DOS device API with a new function, DosCreateDeviceEx, which
will allow 32-bit DOS driver to reserve private memory.
- For each DOS device, create an entry in guest memory so that 16-bit code
can call 32-bit DOS drivers directly.
- Implement an XMS driver stub that uses the above.
- Arch, that's not how the DOS driver strategy routine works, you need to
give it the request in ES:BX which it will store somewhere, and then call
the interrupt routine.

svn path=/trunk/; revision=67339

14 files changed:
reactos/subsystems/mvdm/ntvdm/CMakeLists.txt
reactos/subsystems/mvdm/ntvdm/bios/bios32/bios32.c
reactos/subsystems/mvdm/ntvdm/bios/bios32/bios32.h
reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.c [deleted file]
reactos/subsystems/mvdm/ntvdm/dos/dem.c
reactos/subsystems/mvdm/ntvdm/dos/dem.h
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/device.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/device.h
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.h [moved from reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.h with 88% similarity]
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.h [new file with mode: 0644]

index ca53559..a27845d 100644 (file)
@@ -10,7 +10,6 @@ list(APPEND SOURCE
     bios/bios32/kbdbios32.c
     bios/bios32/vidbios32.c
     bios/bios32/moubios32.c
-    bios/bios32/ems.c
     bios/bios.c
     bios/kbdbios.c
     bios/rom.c
@@ -34,6 +33,7 @@ list(APPEND SOURCE
     dos/dos32krnl/dos.c
     dos/dos32krnl/dosfiles.c
     dos/dos32krnl/emsdrv.c
+    dos/dos32krnl/himem.c
     dos/dos32krnl/memory.c
     dos/mouse32.c
     dos/dem.c
index 6f43a49..3546de6 100644 (file)
@@ -27,8 +27,6 @@
 #include "vidbios32.h"
 #include "moubios32.h"
 
-#include "ems.h"
-
 #include "io.h"
 #include "hardware/cmos.h"
 #include "hardware/pic.h"
@@ -665,13 +663,6 @@ Bios32Post(VOID)
 
     SearchAndInitRoms(&BiosContext);
 
-    /* Initialize EMS */
-    if (!EmsInitialize(EMS_TOTAL_PAGES))
-    {
-        DPRINT1("Could not initialize EMS. EMS will not be available.\n"
-                "Try reducing the number of EMS pages.\n");
-    }
-
     /*
      * End of the 32-bit POST portion. We then fall back into 16-bit where
      * the rest of the POST code is executed, typically calling INT 19h
@@ -733,7 +724,6 @@ BOOLEAN Bios32Initialize(VOID)
 
 VOID Bios32Cleanup(VOID)
 {
-    EmsCleanup();
     MouseBios32Cleanup();
     VidBios32Cleanup();
     KbdBios32Cleanup();
index 6232c89..4f1523f 100644 (file)
@@ -21,9 +21,6 @@
 // #define BIOS_TIME_INTERRUPT         0x1A
 // #define BIOS_SYS_TIMER_INTERRUPT    0x1C
 
-/* 16 MB of EMS memory */
-#define EMS_TOTAL_PAGES 1024
-
 /* FUNCTIONS ******************************************************************/
 
 BOOLEAN Bios32Initialize(VOID);
diff --git a/reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.c b/reactos/subsystems/mvdm/ntvdm/bios/bios32/ems.c
deleted file mode 100644 (file)
index 7dcdde8..0000000
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * COPYRIGHT:       GPLv2+ - See COPYING in the top level directory
- * PROJECT:         ReactOS Virtual DOS Machine
- * FILE:            ems.c
- * PURPOSE:         Expanded Memory Support
- * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
- */
-
-/* INCLUDES *******************************************************************/
-
-#define NDEBUG
-
-#include "ntvdm.h"
-#include "emulator.h"
-#include "bios/bios32/bios32p.h"
-#include "ems.h"
-#include "memory.h"
-
-/* PRIVATE VARIABLES **********************************************************/
-
-static RTL_BITMAP AllocBitmap;
-static PULONG BitmapBuffer = NULL;
-static PEMS_PAGE PageTable = NULL;
-static EMS_HANDLE HandleTable[EMS_MAX_HANDLES];
-static PVOID Mapping[EMS_PHYSICAL_PAGES] = { NULL };
-static ULONG EmsTotalPages = 0;
-static PVOID EmsMemory = NULL;
-
-/* PRIVATE FUNCTIONS **********************************************************/
-
-static USHORT EmsFree(USHORT Handle)
-{
-    PLIST_ENTRY Entry;
-    PEMS_HANDLE HandleEntry = &HandleTable[Handle];
-
-    if (Handle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
-    {
-        return EMS_STATUS_INVALID_HANDLE;
-    }
-
-    for (Entry = HandleEntry->PageList.Flink;
-         Entry != &HandleEntry->PageList;
-         Entry = Entry->Flink)
-    {
-        PEMS_PAGE PageEntry = (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
-        ULONG PageNumber = ARRAY_INDEX(PageEntry, PageTable);
-
-        /* Free the page */
-        RtlClearBits(&AllocBitmap, PageNumber, 1);
-    }
-
-    HandleEntry->Allocated = FALSE;
-    HandleEntry->PageCount = 0;
-    InitializeListHead(&HandleEntry->PageList);
-
-    return EMS_STATUS_OK;
-}
-
-static UCHAR EmsAlloc(USHORT NumPages, PUSHORT Handle)
-{
-    ULONG i, CurrentIndex = 0;
-    PEMS_HANDLE HandleEntry;
-
-    if (NumPages == 0) return EMS_STATUS_ZERO_PAGES;
-
-    for (i = 0; i < EMS_MAX_HANDLES; i++)
-    {
-        HandleEntry = &HandleTable[i];
-        if (!HandleEntry->Allocated)
-        {
-            *Handle = i;
-            break;
-        }
-    }
-
-    if (i == EMS_MAX_HANDLES) return EMS_STATUS_NO_MORE_HANDLES;
-    HandleEntry->Allocated = TRUE;
-
-    while (HandleEntry->PageCount < NumPages)
-    {
-        ULONG RunStart;
-        ULONG RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
-
-        if (RunSize == 0)
-        {
-            /* Free what's been allocated already and report failure */
-            EmsFree(*Handle);
-            return EMS_STATUS_INSUFFICIENT_PAGES;
-        }
-        else if ((HandleEntry->PageCount + RunSize) > NumPages)
-        {
-            /* We don't need the entire run */
-            RunSize = NumPages - HandleEntry->PageCount;
-        }
-
-        CurrentIndex = RunStart + RunSize;
-        HandleEntry->PageCount += RunSize;
-        RtlSetBits(&AllocBitmap, RunStart, RunSize);
-
-        for (i = 0; i < RunSize; i++)
-        {
-            PageTable[RunStart + i].Handle = *Handle;
-            InsertTailList(&HandleEntry->PageList, &PageTable[RunStart + i].Entry);
-        }
-    }
-
-    return EMS_STATUS_OK;
-}
-
-static PEMS_PAGE GetLogicalPage(PEMS_HANDLE Handle, USHORT LogicalPage)
-{
-    PLIST_ENTRY Entry = Handle->PageList.Flink;
-
-    while (LogicalPage)
-    {
-        if (Entry == &Handle->PageList) return NULL;
-        LogicalPage--;
-        Entry = Entry->Flink;
-    }
-
-    return (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
-}
-
-static USHORT EmsMap(USHORT Handle, UCHAR PhysicalPage, USHORT LogicalPage)
-{
-    PEMS_PAGE PageEntry;
-    PEMS_HANDLE HandleEntry = &HandleTable[Handle];
-
-    if (PhysicalPage >= EMS_PHYSICAL_PAGES) return EMS_STATUS_INV_PHYSICAL_PAGE;
-    if (LogicalPage == 0xFFFF)
-    {
-        /* Unmap */
-        Mapping[PhysicalPage] = NULL;
-        return EMS_STATUS_OK;
-    }
-
-    if (Handle >= EMS_MAX_HANDLES || !HandleEntry->Allocated) return EMS_STATUS_INVALID_HANDLE;
-
-    PageEntry = GetLogicalPage(HandleEntry, LogicalPage);
-    if (!PageEntry) return EMS_STATUS_INV_LOGICAL_PAGE; 
-
-    Mapping[PhysicalPage] = (PVOID)((ULONG_PTR)EmsMemory
-                            + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE);
-    return EMS_STATUS_OK;
-}
-
-static VOID WINAPI EmsIntHandler(LPWORD Stack)
-{
-    switch (getAH())
-    {
-        /* Get Manager Status */
-        case 0x40:
-        {
-            setAH(EMS_STATUS_OK);
-            break;
-        }
-
-        /* Get Page Frame Segment */
-        case 0x41:
-        {
-            setAH(EMS_STATUS_OK);
-            setBX(EMS_SEGMENT);
-            break;
-        }
-
-        /* Get Number Of Pages */
-        case 0x42:
-        {
-            setAH(EMS_STATUS_OK);
-            setBX(RtlNumberOfClearBits(&AllocBitmap));
-            setDX(EmsTotalPages);
-            break;
-        }
-
-        /* Get Handle And Allocate Memory */
-        case 0x43:
-        {
-            USHORT Handle;
-            UCHAR Status = EmsAlloc(getBX(), &Handle);
-
-            setAH(Status);
-            if (Status == EMS_STATUS_OK) setDX(Handle);
-            break;
-        }
-
-        /* Map Memory */
-        case 0x44:
-        {
-            setAH(EmsMap(getDX(), getAL(), getBX()));
-            break;
-        }
-
-        /* Release Handle And Memory */
-        case 0x45:
-        {
-            setAH(EmsFree(getDX()));
-            break;
-        }
-
-        /* Get EMM Version */
-        case 0x46:
-        {
-            setAH(EMS_STATUS_OK);
-            setAL(EMS_VERSION_NUM);
-            break;
-        }
-
-        /* Move/Exchange Memory */
-        case 0x57:
-        {
-            PUCHAR SourcePtr, DestPtr;
-            PEMS_HANDLE HandleEntry;
-            PEMS_PAGE PageEntry;
-            BOOLEAN Exchange = getAL();
-            PEMS_COPY_DATA Data = (PEMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI());
-
-            if (Data->SourceType)
-            {
-                /* Expanded memory */
-                HandleEntry = &HandleTable[Data->SourceHandle];
-
-                if (Data->SourceHandle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
-                {
-                    setAL(EMS_STATUS_INVALID_HANDLE);
-                    break;
-                }
-
-                PageEntry = GetLogicalPage(HandleEntry, Data->SourceSegment);
-
-                if (!PageEntry)
-                {
-                    setAL(EMS_STATUS_INV_LOGICAL_PAGE);
-                    break;
-                }
-
-                SourcePtr = (PUCHAR)((ULONG_PTR)EmsMemory
-                                     + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE
-                                     + Data->SourceOffset);
-            }
-            else
-            {
-                /* Conventional memory */
-                SourcePtr = (PUCHAR)SEG_OFF_TO_PTR(Data->SourceSegment, Data->SourceOffset);
-            }
-
-            if (Data->DestType)
-            {
-                /* Expanded memory */
-                HandleEntry = &HandleTable[Data->DestHandle];
-
-                if (Data->SourceHandle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
-                {
-                    setAL(EMS_STATUS_INVALID_HANDLE);
-                    break;
-                }
-
-                PageEntry = GetLogicalPage(HandleEntry, Data->DestSegment);
-
-                if (!PageEntry)
-                {
-                    setAL(EMS_STATUS_INV_LOGICAL_PAGE);
-                    break;
-                }
-
-                DestPtr = (PUCHAR)((ULONG_PTR)EmsMemory
-                                   + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE
-                                   + Data->DestOffset);
-            }
-            else
-            {
-                /* Conventional memory */
-                DestPtr = (PUCHAR)SEG_OFF_TO_PTR(Data->DestSegment, Data->DestOffset);
-            }
-
-            if (Exchange)
-            {
-                ULONG i;
-
-                /* Exchange */
-                for (i = 0; i < Data->RegionLength; i++)
-                {
-                    UCHAR Temp = DestPtr[i];
-                    DestPtr[i] = SourcePtr[i];
-                    SourcePtr[i] = Temp;
-                }
-            }
-            else
-            {
-                /* Move */
-                RtlMoveMemory(DestPtr, SourcePtr, Data->RegionLength);
-            }
-
-            setAL(EMS_STATUS_OK);
-            break;
-        }
-
-        default:
-        {
-            DPRINT1("EMS function AH = %02X NOT IMPLEMENTED\n", getAH());
-            setAH(EMS_STATUS_UNKNOWN_FUNCTION);
-            break;
-        }
-    }
-}
-
-static VOID NTAPI EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
-{
-    ULONG i;
-    ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0);
-    ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
-    ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
-    ULONG Offset, Length;
-
-    for (i = FirstPage; i <= LastPage; i++)
-    {
-        Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0;
-        Length = ((i == LastPage)
-                 ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS))
-                 : EMS_PAGE_SIZE) - Offset;
-
-        if (Mapping[i]) RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Mapping[i] + Offset), Length);
-        Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
-    }
-}
-
-static BOOLEAN NTAPI EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
-{
-    ULONG i;
-    ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0);
-    ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
-    ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
-    ULONG Offset, Length;
-
-    for (i = FirstPage; i <= LastPage; i++)
-    {
-        Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0;
-        Length = ((i == LastPage)
-                 ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS))
-                 : EMS_PAGE_SIZE) - Offset;
-
-        if (Mapping[i]) RtlCopyMemory((PVOID)((ULONG_PTR)Mapping[i] + Offset), Buffer, Length);
-        Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
-    }
-
-    return TRUE;
-}
-
-/* PUBLIC FUNCTIONS ***********************************************************/
-
-BOOLEAN EmsInitialize(ULONG TotalPages)
-{
-    ULONG i;
-
-    for (i = 0; i < EMS_MAX_HANDLES; i++)
-    {
-        HandleTable[i].Allocated = FALSE;
-        HandleTable[i].PageCount = 0;
-        InitializeListHead(&HandleTable[i].PageList);
-    }
-
-    EmsTotalPages = TotalPages;
-    BitmapBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
-                                   HEAP_ZERO_MEMORY,
-                                   ((TotalPages + 31) / 32) * sizeof(ULONG));
-    if (BitmapBuffer == NULL) return FALSE;
-
-    RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, TotalPages);
-
-    PageTable = (PEMS_PAGE)RtlAllocateHeap(RtlGetProcessHeap(),
-                                           HEAP_ZERO_MEMORY,
-                                           TotalPages * sizeof(EMS_PAGE));
-    if (PageTable == NULL)
-    {
-        RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
-        BitmapBuffer = NULL;
-
-        return FALSE;
-    }
-
-    EmsMemory = (PVOID)RtlAllocateHeap(RtlGetProcessHeap(), 0, TotalPages * EMS_PAGE_SIZE);
-    if (EmsMemory == NULL)
-    {
-        RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
-        PageTable = NULL;
-        RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
-        BitmapBuffer = NULL;
-
-        return FALSE;
-    }
-
-    MemInstallFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0),
-                             EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE,
-                             EmsReadMemory,
-                             EmsWriteMemory);
-
-    RegisterBiosInt32(EMS_INTERRUPT_NUM, EmsIntHandler);
-    return TRUE;
-}
-
-VOID EmsCleanup(VOID)
-{
-    MemRemoveFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0),
-                            EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE);
-
-    if (EmsMemory)
-    {
-        RtlFreeHeap(RtlGetProcessHeap(), 0, EmsMemory);
-        EmsMemory = NULL;
-    }
-
-    if (PageTable)
-    {
-        RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
-        PageTable = NULL;
-    }
-
-    if (BitmapBuffer)
-    {
-        RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
-        BitmapBuffer = NULL;
-    }
-}
index 10afa12..92ef845 100644 (file)
@@ -19,6 +19,7 @@
 #include "utils.h"
 
 #include "dem.h"
+#include "dos/dos32krnl/device.h"
 #include "cpu/bop.h"
 
 #include "bios/bios.h"
 
 /**/extern BYTE CurrentDrive;/**/
 
-/* DEFINES ********************************************************************/
-
-/* BOP Identifiers */
-#define BOP_LOAD_DOS    0x2B    // DOS Loading and Initializing BOP. In parameter (following bytes) we take a NULL-terminated string indicating the name of the DOS kernel file.
-#define BOP_START_DOS   0x2C    // DOS Starting BOP. In parameter (following bytes) we take a NULL-terminated string indicating the name of the DOS kernel file.
-#define BOP_DOS         0x50    // DOS System BOP (for NTIO.SYS and NTDOS.SYS)
-#define BOP_CMD         0x54    // DOS Command Interpreter BOP (for COMMAND.COM)
-
 /* PRIVATE FUNCTIONS **********************************************************/
 
 static VOID WINAPI DosSystemBop(LPWORD Stack)
@@ -89,6 +82,20 @@ Quit:
             break;
         }
 
+        /* Call 32-bit Driver Strategy Routine */
+        case BOP_DRV_STRATEGY:
+        {
+            DeviceStrategyBop();
+            break;
+        }
+
+        /* Call 32-bit Driver Interrupt Routine */
+        case BOP_DRV_INTERRUPT:
+        {
+            DeviceInterruptBop();
+            break;
+        }
+
         default:
         {
             DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum);
index d32ab46..30e47ab 100644 (file)
 
 #include "dos32krnl/dos.h"
 
+/* DEFINES ********************************************************************/
+
+/* BOP Identifiers */
+#define BOP_LOAD_DOS    0x2B    // DOS Loading and Initializing BOP. In parameter (following bytes) we take a NULL-terminated string indicating the name of the DOS kernel file.
+#define BOP_START_DOS   0x2C    // DOS Starting BOP. In parameter (following bytes) we take a NULL-terminated string indicating the name of the DOS kernel file.
+#define BOP_DOS         0x50    // DOS System BOP (for NTIO.SYS and NTDOS.SYS)
+#define BOP_CMD         0x54    // DOS Command Interpreter BOP (for COMMAND.COM)
+
 /* FUNCTIONS ******************************************************************/
 
 DWORD
index d16e486..71152ef 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "ntvdm.h"
 #include "emulator.h"
+#include "cpu/bop.h"
 #include "device.h"
 
 #include "dos.h"
 
 /* PRIVATE VARIABLES **********************************************************/
 
+static const BYTE StrategyRoutine[] = {
+    LOBYTE(EMULATOR_BOP),
+    HIBYTE(EMULATOR_BOP),
+    BOP_DOS,
+    BOP_DRV_STRATEGY,
+    0xCB // retf
+};
+
+static const BYTE InterruptRoutine[] = {
+    LOBYTE(EMULATOR_BOP),
+    HIBYTE(EMULATOR_BOP),
+    BOP_DOS,
+    BOP_DRV_INTERRUPT,
+    0xCB // retf
+};
+
+C_ASSERT((sizeof(StrategyRoutine) + sizeof(InterruptRoutine)) == DEVICE_CODE_SIZE);
+
 static LIST_ENTRY DeviceList = { &DeviceList, &DeviceList };
+static DWORD FirstDriver = 0xFFFFFFFF;
+static PDOS_REQUEST_HEADER DeviceRequest;
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
 static VOID DosCallDriver(DWORD Driver, PDOS_REQUEST_HEADER Request)
 {
     PDOS_DRIVER DriverBlock = (PDOS_DRIVER)FAR_POINTER(Driver);
-    PDOS_REQUEST_HEADER RemoteRequest;
+    PDOS_REQUEST_HEADER RemoteRequest = FAR_POINTER(REQUEST_LOCATION);
+    WORD ES = getES();
+    WORD BX = getBX();
 
-    /* Call the strategy routine first */
-    Call16(HIWORD(Driver), DriverBlock->StrategyRoutine);
-    RemoteRequest = (PDOS_REQUEST_HEADER)SEG_OFF_TO_PTR(getES(), getBX());
+    /* Set ES:BX to the location of the request */
+    setES(HIWORD(REQUEST_LOCATION));
+    setBX(LOWORD(REQUEST_LOCATION));
 
     /* Copy the request structure to ES:BX */
     RtlMoveMemory(RemoteRequest, Request, Request->RequestLength);
 
-    /* Call the interrupt routine */
+    /* Call the strategy routine, and then the interrupt routine */
+    Call16(HIWORD(Driver), DriverBlock->StrategyRoutine);
     Call16(HIWORD(Driver), DriverBlock->InterruptRoutine);
 
     /* Get the request structure from ES:BX */
-    RtlMoveMemory(Request, RemoteRequest, RemoteRequest->RequestLength);
+    RtlMoveMemory(Request, RemoteRequest, Request->RequestLength);
+
+    /* Restore ES:BX */
+    setES(ES);
+    setBX(BX);
 }
 
 static inline WORD NTAPI DosDriverReadInternal(PDOS_DEVICE_NODE DeviceNode,
@@ -183,64 +211,344 @@ static WORD NTAPI DosDriverDispatchOutputUntilBusy(PDOS_DEVICE_NODE DeviceNode,
     return Request.Header.Status;
 }
 
+static VOID DosAddDriver(DWORD Driver)
+{
+    PDOS_DRIVER LastDriver;
+
+    if (LOWORD(FirstDriver) == 0xFFFF)
+    {
+        /* This is the first driver */
+        FirstDriver = Driver;
+        return;
+    }
+
+    /* The list isn't empty, so find the last driver in it */
+    LastDriver = (PDOS_DRIVER)FAR_POINTER(FirstDriver);
+    while (LOWORD(LastDriver->Link) != 0xFFFF)
+    {
+        LastDriver = (PDOS_DRIVER)FAR_POINTER(LastDriver->Link);
+    }
+
+    /* Add the new driver to the list */
+    LastDriver->Link = Driver;
+}
+
+static VOID DosRemoveDriver(DWORD Driver)
+{
+    DWORD CurrentDriver = FirstDriver;
+
+    if (FirstDriver == Driver)
+    {
+        /* Update the first driver */
+        FirstDriver = ((PDOS_DRIVER)FAR_POINTER(FirstDriver))->Link;
+        return;
+    }
+
+    while (LOWORD(CurrentDriver) != 0xFFFF)
+    {
+        PDOS_DRIVER DriverHeader = (PDOS_DRIVER)FAR_POINTER(CurrentDriver);
+
+        if (DriverHeader->Link == Driver)
+        {
+            /* Remove it from the list */
+            DriverHeader->Link = ((PDOS_DRIVER)FAR_POINTER(DriverHeader->Link))->Link;
+            return;
+        }
+
+        CurrentDriver = DriverHeader->Link;
+    }
+}
+
+static PDOS_DEVICE_NODE DosCreateDeviceNode(DWORD Driver)
+{
+    BYTE i;
+    PDOS_DRIVER DriverHeader = (PDOS_DRIVER)FAR_POINTER(Driver);
+    PDOS_DEVICE_NODE Node = RtlAllocateHeap(RtlGetProcessHeap(),
+                                            HEAP_ZERO_MEMORY,
+                                            sizeof(*Node));
+    if (Node == NULL) return NULL;
+
+    Node->Driver = Driver;
+    Node->DeviceAttributes = DriverHeader->DeviceAttributes;
+
+    /* Initialize the name string */
+    Node->Name.Buffer = Node->NameBuffer;
+    Node->Name.MaximumLength = MAX_DEVICE_NAME;
+
+    for (i = 0; i < MAX_DEVICE_NAME; i++)
+    {
+        if (DriverHeader->DeviceName[i] == ' ') break;
+        Node->Name.Buffer[i] = DriverHeader->DeviceName[i];
+    }
+
+    Node->Name.Length = i;
+
+    InsertTailList(&DeviceList, &Node->Entry);
+    return Node;
+}
+
 /* PUBLIC FUNCTIONS ***********************************************************/
 
 PDOS_DEVICE_NODE DosGetDevice(LPCSTR DeviceName)
 {
     PLIST_ENTRY i;
-    PDOS_DEVICE_NODE Node;
+    DWORD CurrentDriver = FirstDriver;
     ANSI_STRING DeviceNameString;
 
     RtlInitAnsiString(&DeviceNameString, DeviceName);
 
-    for (i = DeviceList.Flink; i != &DeviceList; i = i->Flink)
+    while (LOWORD(CurrentDriver) != 0xFFFF)
     {
-        Node = CONTAINING_RECORD(i, DOS_DEVICE_NODE, Entry);
+        PDOS_DEVICE_NODE Node;
+        PDOS_DRIVER DriverHeader = (PDOS_DRIVER)FAR_POINTER(CurrentDriver);
+
+        /* Get the device node for this driver */
+        for (i = DeviceList.Flink; i != &DeviceList; i = i->Flink)
+        {
+            Node = CONTAINING_RECORD(i, DOS_DEVICE_NODE, Entry);
+            if (Node->Driver == CurrentDriver) break;
+        }
+
+        if (i == &DeviceList)
+        {
+            DPRINT1("The driver at %04X:%04X has no associated device node. "
+                    "Installing automagically.\n",
+                    HIWORD(CurrentDriver),
+                    LOWORD(CurrentDriver));
+
+            /* Create the device node */
+            Node = DosCreateDeviceNode(CurrentDriver);
+            Node->IoctlReadRoutine = DosDriverDispatchIoctlRead;
+            Node->ReadRoutine = DosDriverDispatchRead;
+            Node->PeekRoutine = DosDriverDispatchPeek;
+            Node->InputStatusRoutine = DosDriverDispatchInputStatus;
+            Node->FlushInputRoutine = DosDriverDispatchFlushInput;
+            Node->IoctlWriteRoutine = DosDriverDispatchIoctlWrite;
+            Node->WriteRoutine = DosDriverDispatchWrite;
+            Node->OutputStatusRoutine = DosDriverDispatchOutputStatus;
+            Node->FlushOutputRoutine = DosDriverDispatchFlushOutput;
+            Node->OpenRoutine = DosDriverDispatchOpen;
+            Node->CloseRoutine = DosDriverDispatchClose;
+            Node->OutputUntilBusyRoutine = DosDriverDispatchOutputUntilBusy;
+        }
+
         if (RtlEqualString(&Node->Name, &DeviceNameString, TRUE)) return Node;
+        CurrentDriver = DriverHeader->Link;
     }
 
     return NULL;
 }
 
-PDOS_DEVICE_NODE DosCreateDevice(WORD Attributes, PCHAR DeviceName)
+PDOS_DEVICE_NODE DosCreateDeviceEx(WORD Attributes, PCHAR DeviceName, WORD PrivateDataSize)
 {
     BYTE i;
+    WORD Segment;
+    PDOS_DRIVER DriverHeader;
     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;
+        return NULL;
     }
 
-    Node = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*Node));
-    if (Node == NULL) return NULL;
+    /* Create a driver header for this device */
+    Segment = DosAllocateMemory(sizeof(DOS_DRIVER) + 10 + PrivateDataSize, NULL);
+    if (Segment == 0) return NULL;
 
-    Node->DeviceAttributes = Attributes;
-
-    /* Initialize the name string */
-    Node->Name.Buffer = Node->NameBuffer;
-    Node->Name.MaximumLength = MAX_DEVICE_NAME;
+    /* Fill the header with data */
+    DriverHeader = SEG_OFF_TO_PTR(Segment, 0);
+    DriverHeader->Link = 0xFFFFFFFF;
+    DriverHeader->DeviceAttributes = Attributes;
+    DriverHeader->StrategyRoutine = sizeof(DOS_DRIVER);
+    DriverHeader->InterruptRoutine = sizeof(DOS_DRIVER) + sizeof(StrategyRoutine);
 
+    RtlFillMemory(DriverHeader->DeviceName, MAX_DEVICE_NAME, ' ');
     for (i = 0; i < MAX_DEVICE_NAME; i++)
     {
         if (DeviceName[i] == '\0' || DeviceName[i] == ' ') break;
-        Node->Name.Buffer[i] = DeviceName[i];
+        DriverHeader->DeviceName[i] = DeviceName[i];
     }
 
-    Node->Name.Length = i;
+    /* Write the routines */
+    RtlMoveMemory(SEG_OFF_TO_PTR(Segment, DriverHeader->StrategyRoutine),
+                  StrategyRoutine,
+                  sizeof(StrategyRoutine));
+    RtlMoveMemory(SEG_OFF_TO_PTR(Segment, DriverHeader->StrategyRoutine),
+                  InterruptRoutine,
+                  sizeof(InterruptRoutine));
+
+    /* Create the node */
+    Node = DosCreateDeviceNode(MAKELONG(0, Segment));
+    if (Node == NULL)
+    {
+        DosFreeMemory(Segment);
+        return NULL;
+    }
 
-    InsertTailList(&DeviceList, &Node->Entry);
+    DosAddDriver(Node->Driver);
     return Node;
 }
 
+PDOS_DEVICE_NODE DosCreateDevice(WORD Attributes, PCHAR DeviceName)
+{
+    /* Call the extended API */
+    return DosCreateDeviceEx(Attributes, DeviceName, 0);
+}
+
 VOID DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode)
 {
+    DosRemoveDriver(DeviceNode->Driver);
+
+    ASSERT(LOWORD(DeviceNode->Driver) == 0);
+    DosFreeMemory(HIWORD(DeviceNode->Driver));
+
     RemoveEntryList(&DeviceNode->Entry);
     RtlFreeHeap(RtlGetProcessHeap(), 0, DeviceNode);
 }
 
+VOID DeviceStrategyBop(VOID)
+{
+    /* Save ES:BX */
+    DeviceRequest = (PDOS_REQUEST_HEADER)SEG_OFF_TO_PTR(getES(), getBX());
+}
+
+VOID DeviceInterruptBop(VOID)
+{
+    PLIST_ENTRY i;
+    PDOS_DEVICE_NODE Node;
+    DWORD DriverAddress = (getCS() << 4) + getIP() - sizeof(DOS_DRIVER) - 9;
+
+    /* Get the device node for this driver */
+    for (i = DeviceList.Flink; i != &DeviceList; i = i->Flink)
+    {
+        Node = CONTAINING_RECORD(i, DOS_DEVICE_NODE, Entry);
+        if (TO_LINEAR(HIWORD(Node->Driver), LOWORD(Node->Driver)) == DriverAddress) break;
+    }
+
+    if (i == &DeviceList)
+    {
+        DPRINT1("Device interrupt BOP from an unknown location.\n");
+        return;
+    }
+
+    switch (DeviceRequest->CommandCode)
+    {
+        case DOS_DEVCMD_IOCTL_READ:
+        {
+            PDOS_IOCTL_RW_REQUEST Request = (PDOS_IOCTL_RW_REQUEST)DeviceRequest;
+
+            DeviceRequest->Status = Node->IoctlReadRoutine(
+                Node,
+                Request->BufferPointer,
+                &Request->Length
+            );
+
+            break;
+        }
+
+        case DOS_DEVCMD_READ:
+        {
+            PDOS_RW_REQUEST Request = (PDOS_RW_REQUEST)DeviceRequest;
+
+            DeviceRequest->Status = Node->ReadRoutine(
+                Node,
+                Request->BufferPointer,
+                &Request->Length
+            );
+
+            break;
+        }
+
+        case DOS_DEVCMD_PEEK:
+        {
+            PDOS_PEEK_REQUEST Request = (PDOS_PEEK_REQUEST)DeviceRequest;
+            DeviceRequest->Status = Node->PeekRoutine(Node, &Request->Character);
+            break;
+        }
+
+        case DOS_DEVCMD_INSTAT:
+        {
+            DeviceRequest->Status = Node->InputStatusRoutine(Node);
+            break;
+        }
+
+        case DOS_DEVCMD_FLUSH_INPUT:
+        {
+            DeviceRequest->Status = Node->FlushInputRoutine(Node);
+            break;
+        }
+
+        case DOS_DEVCMD_IOCTL_WRITE:
+        {
+            PDOS_IOCTL_RW_REQUEST Request = (PDOS_IOCTL_RW_REQUEST)DeviceRequest;
+
+            DeviceRequest->Status = Node->IoctlWriteRoutine(
+                Node,
+                Request->BufferPointer,
+                &Request->Length
+            );
+
+            break;
+        }
+
+        case DOS_DEVCMD_WRITE:
+        {
+            PDOS_RW_REQUEST Request = (PDOS_RW_REQUEST)DeviceRequest;
+
+            DeviceRequest->Status = Node->WriteRoutine(Node,
+                Request->BufferPointer,
+                &Request->Length
+            );
+
+            break;
+        }
+
+        case DOS_DEVCMD_OUTSTAT:
+        {
+            DeviceRequest->Status = Node->OutputStatusRoutine(Node);
+            break;
+        }
+
+        case DOS_DEVCMD_FLUSH_OUTPUT:
+        {
+            DeviceRequest->Status = Node->FlushOutputRoutine(Node);
+            break;
+        }
+
+        case DOS_DEVCMD_OPEN:
+        {
+            DeviceRequest->Status = Node->OpenRoutine(Node);
+            break;
+        }
+
+        case DOS_DEVCMD_CLOSE:
+        {
+            DeviceRequest->Status = Node->CloseRoutine(Node);
+            break;
+        }
+
+        case DOS_DEVCMD_OUTPUT_BUSY:
+        {
+            PDOS_OUTPUT_BUSY_REQUEST Request = (PDOS_OUTPUT_BUSY_REQUEST)DeviceRequest;
+
+            DeviceRequest->Status = Node->OutputUntilBusyRoutine(
+                Node,
+                Request->BufferPointer,
+                &Request->Length
+            );
+
+            break;
+        }
+
+        default:
+        {
+            DPRINT1("Unknown device command code: %u\n", DeviceRequest->CommandCode);
+        }
+    }
+}
+
 DWORD DosLoadDriver(LPCSTR DriverFile)
 {
     DWORD Result = ERROR_SUCCESS;
@@ -334,10 +642,8 @@ DWORD DosLoadDriver(LPCSTR DriverFile)
             goto Next;
         }
 
-        /* Create the device */
-        DeviceNode = DosCreateDevice(DriverHeader->DeviceAttributes,
-                                     DriverHeader->DeviceName);
-        DeviceNode->Driver = Driver;
+        /* Create the device node */
+        DeviceNode = DosCreateDeviceNode(Driver);
         DeviceNode->IoctlReadRoutine = DosDriverDispatchIoctlRead;
         DeviceNode->ReadRoutine = DosDriverDispatchRead;
         DeviceNode->PeekRoutine = DosDriverDispatchPeek;
@@ -351,6 +657,7 @@ DWORD DosLoadDriver(LPCSTR DriverFile)
         DeviceNode->CloseRoutine = DosDriverDispatchClose;
         DeviceNode->OutputUntilBusyRoutine = DosDriverDispatchOutputUntilBusy;
 
+        DosAddDriver(Driver);
         DriversLoaded++;
 
 Next:
index 1635c6d..2061f4a 100644 (file)
 /* DEFINITIONS ****************************************************************/
 
 #define MAX_DEVICE_NAME 8
+#define REQUEST_LOCATION 0x00700100 // 0070:0100
+#define DEVICE_CODE_SIZE 10
+#define DEVICE_PRIVATE_AREA(Driver) (Driver + sizeof(DOS_DRIVER) + DEVICE_CODE_SIZE)
+
+#define BOP_DRV_STRATEGY  0x42
+#define BOP_DRV_INTERRUPT 0x43
 
 #define DOS_DEVATTR_STDIN       (1 << 0)
 #define DOS_DEVATTR_STDOUT      (1 << 1)
@@ -80,6 +86,7 @@ typedef WORD (NTAPI *PDOS_DEVICE_PEEK_ROUTINE)
 struct _DOS_DEVICE_NODE
 {
     LIST_ENTRY Entry;
+    DWORD Driver;
     WORD DeviceAttributes;
     ANSI_STRING Name;
     CHAR NameBuffer[MAX_DEVICE_NAME];
@@ -95,7 +102,6 @@ struct _DOS_DEVICE_NODE
     PDOS_DEVICE_GENERIC_ROUTINE OpenRoutine;
     PDOS_DEVICE_GENERIC_ROUTINE CloseRoutine;
     PDOS_DEVICE_IO_ROUTINE OutputUntilBusyRoutine;
-    DWORD Driver;
 };
 
 #pragma pack(push, 1)
@@ -190,7 +196,16 @@ typedef struct _DOS_OUTPUT_BUSY_REQUEST
 
 PDOS_DEVICE_NODE DosGetDevice(LPCSTR DeviceName);
 PDOS_DEVICE_NODE DosCreateDevice(WORD Attributes, PCHAR DeviceName);
+PDOS_DEVICE_NODE DosCreateDeviceEx
+(
+    WORD Attributes,
+    PCHAR DeviceName,
+    WORD PrivateDataSize
+);
 VOID DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode);
+VOID DeviceStrategyBop(VOID);
+VOID DeviceInterruptBop(VOID);
+DWORD DosLoadDriver(LPCSTR DriverFile);
 
 #endif // _DEVICE_H_
 
index 5b11898..973b78c 100644 (file)
 #include "dos/dem.h"
 #include "device.h"
 #include "memory.h"
+#include "himem.h"
 
 #include "bios/bios.h"
 
 #include "io.h"
 #include "hardware/ps2.h"
 
+#include "emsdrv.h"
+
 /* PRIVATE VARIABLES **********************************************************/
 
 CALLBACK16 DosContext;
@@ -2934,9 +2937,39 @@ VOID WINAPI DosFastConOut(LPWORD Stack)
 
 VOID WINAPI DosInt2Fh(LPWORD Stack)
 {
-    DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
-            getAH(), getAL());
-    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+    switch (getAH())
+    {
+        /* Extended Memory Specification */
+        case 0x43:
+        {
+            DWORD DriverEntry;
+            if (!XmsGetDriverEntry(&DriverEntry)) break;
+
+            if (getAL() == 0x00)
+            {
+                /* The driver is loaded */
+                setAL(0x80);
+            }
+            else if (getAL() == 0x10)
+            {
+                setES(HIWORD(DriverEntry));
+                setBX(LOWORD(DriverEntry));
+            }
+            else
+            {
+                DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
+            }
+
+            break;
+        }
+        
+        default:
+        {
+            DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
+                    getAH(), getAL());
+            Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+        }
+    }
 }
 
 BOOLEAN DosKRNLInitialize(VOID)
@@ -3004,12 +3037,6 @@ BOOLEAN DosKRNLInitialize(VOID)
         DosSystemFileTable[i].RefCount = 0;
     }
 
-    /* Load the EMS driver */
-    EmsDrvInitialize();
-
-    /* Load the CON driver */
-    ConDrvInitialize();
-
 #endif
 
     /* Initialize the callback context */
@@ -3024,6 +3051,19 @@ BOOLEAN DosKRNLInitialize(VOID)
     RegisterDosInt32(0x29, DosFastConOut    ); // DOS 2+ Fast Console Output
     RegisterDosInt32(0x2F, DosInt2Fh        );
 
+    /* Load the EMS driver */
+    if (!EmsDrvInitialize(EMS_TOTAL_PAGES))
+    {
+        DPRINT1("Could not initialize EMS. EMS will not be available.\n"
+                "Try reducing the number of EMS pages.\n");
+    }
+
+    /* Load the XMS driver (HIMEM) */
+    XmsInitialize();
+
+    /* Load the CON driver */
+    ConDrvInitialize();
+
     return TRUE;
 }
 
index d262bbf..5214edc 100644 (file)
@@ -50,6 +50,9 @@
 #define DOS_PROGRAM_NAME_TAG 0x0001
 #define DEFAULT_JFT_SIZE 20
 
+/* 16 MB of EMS memory */
+#define EMS_TOTAL_PAGES 1024
+
 typedef enum
 {
     DOS_LOAD_AND_EXECUTE = 0x00,
@@ -198,8 +201,6 @@ BOOLEAN DosCheckInput(VOID);
 VOID DosPrintCharacter(WORD FileHandle, CHAR Character);
 
 BOOLEAN DosBIOSInitialize(VOID);
-VOID EmsDrvInitialize(VOID);
-VOID EmsDrvCleanup(VOID);
 VOID ConDrvInitialize(VOID);
 VOID ConDrvCleanup(VOID);
 
index 1dde4b3..58b6e91 100644 (file)
 #define NDEBUG
 
 #include "ntvdm.h"
+#include "emulator.h"
 
 #include "dos.h"
 #include "dos/dem.h"
 #include "device.h"
 
+#include "../../memory.h"
+#include "emsdrv.h"
+
 #define EMS_DEVICE_NAME "EMMXXXX0"
 
 /* PRIVATE VARIABLES **********************************************************/
 
 static PDOS_DEVICE_NODE Node;
+static RTL_BITMAP AllocBitmap;
+static PULONG BitmapBuffer = NULL;
+static PEMS_PAGE PageTable = NULL;
+static EMS_HANDLE HandleTable[EMS_MAX_HANDLES];
+static PVOID Mapping[EMS_PHYSICAL_PAGES] = { NULL };
+static ULONG EmsTotalPages = 0;
+static PVOID EmsMemory = NULL;
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
+static USHORT EmsFree(USHORT Handle)
+{
+    PLIST_ENTRY Entry;
+    PEMS_HANDLE HandleEntry = &HandleTable[Handle];
+
+    if (Handle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
+    {
+        return EMS_STATUS_INVALID_HANDLE;
+    }
+
+    for (Entry = HandleEntry->PageList.Flink;
+         Entry != &HandleEntry->PageList;
+         Entry = Entry->Flink)
+    {
+        PEMS_PAGE PageEntry = (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
+        ULONG PageNumber = ARRAY_INDEX(PageEntry, PageTable);
+
+        /* Free the page */
+        RtlClearBits(&AllocBitmap, PageNumber, 1);
+    }
+
+    HandleEntry->Allocated = FALSE;
+    HandleEntry->PageCount = 0;
+    InitializeListHead(&HandleEntry->PageList);
+
+    return EMS_STATUS_OK;
+}
+
+static UCHAR EmsAlloc(USHORT NumPages, PUSHORT Handle)
+{
+    ULONG i, CurrentIndex = 0;
+    PEMS_HANDLE HandleEntry;
+
+    if (NumPages == 0) return EMS_STATUS_ZERO_PAGES;
+
+    for (i = 0; i < EMS_MAX_HANDLES; i++)
+    {
+        HandleEntry = &HandleTable[i];
+        if (!HandleEntry->Allocated)
+        {
+            *Handle = i;
+            break;
+        }
+    }
+
+    if (i == EMS_MAX_HANDLES) return EMS_STATUS_NO_MORE_HANDLES;
+    HandleEntry->Allocated = TRUE;
+
+    while (HandleEntry->PageCount < NumPages)
+    {
+        ULONG RunStart;
+        ULONG RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
+
+        if (RunSize == 0)
+        {
+            /* Free what's been allocated already and report failure */
+            EmsFree(*Handle);
+            return EMS_STATUS_INSUFFICIENT_PAGES;
+        }
+        else if ((HandleEntry->PageCount + RunSize) > NumPages)
+        {
+            /* We don't need the entire run */
+            RunSize = NumPages - HandleEntry->PageCount;
+        }
+
+        CurrentIndex = RunStart + RunSize;
+        HandleEntry->PageCount += RunSize;
+        RtlSetBits(&AllocBitmap, RunStart, RunSize);
+
+        for (i = 0; i < RunSize; i++)
+        {
+            PageTable[RunStart + i].Handle = *Handle;
+            InsertTailList(&HandleEntry->PageList, &PageTable[RunStart + i].Entry);
+        }
+    }
+
+    return EMS_STATUS_OK;
+}
+
+static PEMS_PAGE GetLogicalPage(PEMS_HANDLE Handle, USHORT LogicalPage)
+{
+    PLIST_ENTRY Entry = Handle->PageList.Flink;
+
+    while (LogicalPage)
+    {
+        if (Entry == &Handle->PageList) return NULL;
+        LogicalPage--;
+        Entry = Entry->Flink;
+    }
+
+    return (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
+}
+
+static USHORT EmsMap(USHORT Handle, UCHAR PhysicalPage, USHORT LogicalPage)
+{
+    PEMS_PAGE PageEntry;
+    PEMS_HANDLE HandleEntry = &HandleTable[Handle];
+
+    if (PhysicalPage >= EMS_PHYSICAL_PAGES) return EMS_STATUS_INV_PHYSICAL_PAGE;
+    if (LogicalPage == 0xFFFF)
+    {
+        /* Unmap */
+        Mapping[PhysicalPage] = NULL;
+        return EMS_STATUS_OK;
+    }
+
+    if (Handle >= EMS_MAX_HANDLES || !HandleEntry->Allocated) return EMS_STATUS_INVALID_HANDLE;
+
+    PageEntry = GetLogicalPage(HandleEntry, LogicalPage);
+    if (!PageEntry) return EMS_STATUS_INV_LOGICAL_PAGE; 
+
+    Mapping[PhysicalPage] = (PVOID)((ULONG_PTR)EmsMemory
+                            + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE);
+    return EMS_STATUS_OK;
+}
+
+static VOID WINAPI EmsIntHandler(LPWORD Stack)
+{
+    switch (getAH())
+    {
+        /* Get Manager Status */
+        case 0x40:
+        {
+            setAH(EMS_STATUS_OK);
+            break;
+        }
+
+        /* Get Page Frame Segment */
+        case 0x41:
+        {
+            setAH(EMS_STATUS_OK);
+            setBX(EMS_SEGMENT);
+            break;
+        }
+
+        /* Get Number Of Pages */
+        case 0x42:
+        {
+            setAH(EMS_STATUS_OK);
+            setBX(RtlNumberOfClearBits(&AllocBitmap));
+            setDX(EmsTotalPages);
+            break;
+        }
+
+        /* Get Handle And Allocate Memory */
+        case 0x43:
+        {
+            USHORT Handle;
+            UCHAR Status = EmsAlloc(getBX(), &Handle);
+
+            setAH(Status);
+            if (Status == EMS_STATUS_OK) setDX(Handle);
+            break;
+        }
+
+        /* Map Memory */
+        case 0x44:
+        {
+            setAH(EmsMap(getDX(), getAL(), getBX()));
+            break;
+        }
+
+        /* Release Handle And Memory */
+        case 0x45:
+        {
+            setAH(EmsFree(getDX()));
+            break;
+        }
+
+        /* Get EMM Version */
+        case 0x46:
+        {
+            setAH(EMS_STATUS_OK);
+            setAL(EMS_VERSION_NUM);
+            break;
+        }
+
+        /* Move/Exchange Memory */
+        case 0x57:
+        {
+            PUCHAR SourcePtr, DestPtr;
+            PEMS_HANDLE HandleEntry;
+            PEMS_PAGE PageEntry;
+            BOOLEAN Exchange = getAL();
+            PEMS_COPY_DATA Data = (PEMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI());
+
+            if (Data->SourceType)
+            {
+                /* Expanded memory */
+                HandleEntry = &HandleTable[Data->SourceHandle];
+
+                if (Data->SourceHandle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
+                {
+                    setAL(EMS_STATUS_INVALID_HANDLE);
+                    break;
+                }
+
+                PageEntry = GetLogicalPage(HandleEntry, Data->SourceSegment);
+
+                if (!PageEntry)
+                {
+                    setAL(EMS_STATUS_INV_LOGICAL_PAGE);
+                    break;
+                }
+
+                SourcePtr = (PUCHAR)((ULONG_PTR)EmsMemory
+                                     + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE
+                                     + Data->SourceOffset);
+            }
+            else
+            {
+                /* Conventional memory */
+                SourcePtr = (PUCHAR)SEG_OFF_TO_PTR(Data->SourceSegment, Data->SourceOffset);
+            }
+
+            if (Data->DestType)
+            {
+                /* Expanded memory */
+                HandleEntry = &HandleTable[Data->DestHandle];
+
+                if (Data->SourceHandle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
+                {
+                    setAL(EMS_STATUS_INVALID_HANDLE);
+                    break;
+                }
+
+                PageEntry = GetLogicalPage(HandleEntry, Data->DestSegment);
+
+                if (!PageEntry)
+                {
+                    setAL(EMS_STATUS_INV_LOGICAL_PAGE);
+                    break;
+                }
+
+                DestPtr = (PUCHAR)((ULONG_PTR)EmsMemory
+                                   + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE
+                                   + Data->DestOffset);
+            }
+            else
+            {
+                /* Conventional memory */
+                DestPtr = (PUCHAR)SEG_OFF_TO_PTR(Data->DestSegment, Data->DestOffset);
+            }
+
+            if (Exchange)
+            {
+                ULONG i;
+
+                /* Exchange */
+                for (i = 0; i < Data->RegionLength; i++)
+                {
+                    UCHAR Temp = DestPtr[i];
+                    DestPtr[i] = SourcePtr[i];
+                    SourcePtr[i] = Temp;
+                }
+            }
+            else
+            {
+                /* Move */
+                RtlMoveMemory(DestPtr, SourcePtr, Data->RegionLength);
+            }
+
+            setAL(EMS_STATUS_OK);
+            break;
+        }
+
+        default:
+        {
+            DPRINT1("EMS function AH = %02X NOT IMPLEMENTED\n", getAH());
+            setAH(EMS_STATUS_UNKNOWN_FUNCTION);
+            break;
+        }
+    }
+}
+
+static VOID NTAPI EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
+{
+    ULONG i;
+    ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0);
+    ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
+    ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
+    ULONG Offset, Length;
+
+    for (i = FirstPage; i <= LastPage; i++)
+    {
+        Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0;
+        Length = ((i == LastPage)
+                 ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS))
+                 : EMS_PAGE_SIZE) - Offset;
+
+        if (Mapping[i]) RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Mapping[i] + Offset), Length);
+        Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
+    }
+}
+
+static BOOLEAN NTAPI EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
+{
+    ULONG i;
+    ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0);
+    ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
+    ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
+    ULONG Offset, Length;
+
+    for (i = FirstPage; i <= LastPage; i++)
+    {
+        Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0;
+        Length = ((i == LastPage)
+                 ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS))
+                 : EMS_PAGE_SIZE) - Offset;
+
+        if (Mapping[i]) RtlCopyMemory((PVOID)((ULONG_PTR)Mapping[i] + Offset), Buffer, Length);
+        Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
+    }
+
+    return TRUE;
+}
+
+
 WORD NTAPI EmsDrvDispatchIoctlRead(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length)
 {
     // TODO: NOT IMPLEMENTED
@@ -34,17 +363,85 @@ WORD NTAPI EmsDrvDispatchIoctlRead(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD
 
 /* PUBLIC FUNCTIONS ***********************************************************/
 
-VOID EmsDrvInitialize(VOID)
+BOOLEAN EmsDrvInitialize(ULONG TotalPages)
 {
+    ULONG i;
+
+    for (i = 0; i < EMS_MAX_HANDLES; i++)
+    {
+        HandleTable[i].Allocated = FALSE;
+        HandleTable[i].PageCount = 0;
+        InitializeListHead(&HandleTable[i].PageList);
+    }
+
+    EmsTotalPages = TotalPages;
+    BitmapBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
+                                   HEAP_ZERO_MEMORY,
+                                   ((TotalPages + 31) / 32) * sizeof(ULONG));
+    if (BitmapBuffer == NULL) return FALSE;
+
+    RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, TotalPages);
+
+    PageTable = (PEMS_PAGE)RtlAllocateHeap(RtlGetProcessHeap(),
+                                           HEAP_ZERO_MEMORY,
+                                           TotalPages * sizeof(EMS_PAGE));
+    if (PageTable == NULL)
+    {
+        RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
+        BitmapBuffer = NULL;
+
+        return FALSE;
+    }
+
+    EmsMemory = (PVOID)RtlAllocateHeap(RtlGetProcessHeap(), 0, TotalPages * EMS_PAGE_SIZE);
+    if (EmsMemory == NULL)
+    {
+        RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
+        PageTable = NULL;
+        RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
+        BitmapBuffer = NULL;
+
+        return FALSE;
+    }
+
+    MemInstallFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0),
+                             EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE,
+                             EmsReadMemory,
+                             EmsWriteMemory);
+
+    RegisterDosInt32(EMS_INTERRUPT_NUM, EmsIntHandler);
+
     /* Create the device */
-    Node = DosCreateDevice(DOS_DEVATTR_IOCTL
-                           | DOS_DEVATTR_CHARACTER,
+    Node = DosCreateDevice(DOS_DEVATTR_IOCTL | DOS_DEVATTR_CHARACTER,
                            EMS_DEVICE_NAME);
     Node->IoctlReadRoutine = EmsDrvDispatchIoctlRead;
+
+    return TRUE;
 }
 
 VOID EmsDrvCleanup(VOID)
 {
     /* Delete the device */
     DosDeleteDevice(Node);
+
+    MemRemoveFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0),
+                            EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE);
+
+    if (EmsMemory)
+    {
+        RtlFreeHeap(RtlGetProcessHeap(), 0, EmsMemory);
+        EmsMemory = NULL;
+    }
+
+    if (PageTable)
+    {
+        RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
+        PageTable = NULL;
+    }
+
+    if (BitmapBuffer)
+    {
+        RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
+        BitmapBuffer = NULL;
+    }
 }
@@ -1,13 +1,13 @@
 /*
  * COPYRIGHT:       GPLv2+ - See COPYING in the top level directory
  * PROJECT:         ReactOS Virtual DOS Machine
- * FILE:            ems.h
- * PURPOSE:         Expanded Memory Support
+ * FILE:            emsdrv.h
+ * PURPOSE:         DOS EMS Driver
  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  */
 
-#ifndef _EMS_H_
-#define _EMS_H_
+#ifndef _EMSDRV_H_
+#define _EMSDRV_H_
 
 /* DEFINITIONS ****************************************************************/
 
@@ -59,11 +59,9 @@ typedef struct _EMS_COPY_DATA
 
 #pragma pack(pop)
 
-/* FUNCTIONS ******************************************************************/
-
-BOOLEAN EmsInitialize(ULONG TotalPages);
-VOID EmsCleanup(VOID);
+#endif
 
-#endif // _EMS_H_
+/* FUNCTIONS ******************************************************************/
 
-/* EOF */
+BOOLEAN EmsDrvInitialize(ULONG TotalPages);
+VOID EmsDrvCleanup(VOID);
diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c
new file mode 100644 (file)
index 0000000..e40070d
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * COPYRIGHT:       GPLv2+ - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            himem.c
+ * PURPOSE:         DOS XMS Driver
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#define NDEBUG
+
+#include "ntvdm.h"
+#include "emulator.h"
+#include "cpu/bop.h"
+
+#include "dos.h"
+#include "dos/dem.h"
+#include "device.h"
+
+#define XMS_DEVICE_NAME "XMSXXXX0"
+#define XMS_BOP 0x52
+
+/* PRIVATE VARIABLES **********************************************************/
+
+static const BYTE EntryProcedure[] = {
+    0xEB, // jmp short +0x03
+    0x03,
+    0x90, // nop
+    0x90, // nop
+    0x90, // nop
+    LOBYTE(EMULATOR_BOP),
+    HIBYTE(EMULATOR_BOP),
+    XMS_BOP,
+    0xCB // retf
+};
+
+static PDOS_DEVICE_NODE Node = NULL;
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+static VOID WINAPI XmsBopProcedure(LPWORD Stack)
+{
+    switch (getAH())
+    {
+        /* Get XMS Version */
+        case 0x00:
+        {
+            setAX(0x0300); /* XMS version 3.0 */
+            setDX(0x0001); /* HMA present */
+
+            break;
+        }
+
+        default:
+        {
+            DPRINT1("XMS command AH = 0x%02X NOT IMPLEMENTED\n", getAH());
+        }
+    }
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+BOOLEAN XmsGetDriverEntry(PDWORD Pointer)
+{
+    if (Node == NULL) return FALSE;
+    *Pointer = DEVICE_PRIVATE_AREA(Node->Driver);
+    return TRUE;
+}
+
+VOID XmsInitialize(VOID)
+{
+    Node = DosCreateDeviceEx(DOS_DEVATTR_IOCTL | DOS_DEVATTR_CHARACTER,
+                             XMS_DEVICE_NAME,
+                             sizeof(EntryProcedure));
+
+    RegisterBop(XMS_BOP, XmsBopProcedure);
+
+    /* Copy the entry routine to the device private area */
+    RtlMoveMemory(FAR_POINTER(DEVICE_PRIVATE_AREA(Node->Driver)),
+                  EntryProcedure,
+                  sizeof(EntryProcedure));
+}
+
+VOID XmsCleanup(VOID)
+{
+    RegisterBop(XMS_BOP, NULL);
+    DosDeleteDevice(Node);
+}
diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.h b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.h
new file mode 100644 (file)
index 0000000..09a7465
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * COPYRIGHT:       GPLv2+ - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            himem.h
+ * PURPOSE:         DOS XMS Driver
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* FUNCTIONS ******************************************************************/
+
+BOOLEAN XmsGetDriverEntry(PDWORD Pointer);
+VOID XmsInitialize(VOID);
+VOID XmsCleanup(VOID);