--- /dev/null
+/*
+ * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
+ * PROJECT: ReactOS Virtual DOS Machine
+ * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c
+ * PURPOSE: DOS EMS Driver
+ * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ *
+ * DOCUMENTATION: Official specification:
+ * LIM EMS v4.0: http://www.phatcode.net/res/218/files/limems40.txt
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+
+#define NDEBUG
+#include <debug.h>
+
+#include "emulator.h"
+#include "../../memory.h"
+#include "bios/umamgr.h"
+
+#include "dos.h"
+#include "dos/dem.h"
+#include "device.h"
+
+#include "emsdrv.h"
+
+#define EMS_DEVICE_NAME "EMMXXXX0"
+
+#define EMS_SEGMENT_SIZE ((EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE) >> 4)
+#define EMS_SYSTEM_HANDLE 0
+
+/* 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 PVOID MappingBackup[EMS_PHYSICAL_PAGES] = { NULL };
+static ULONG EmsTotalPages = 0;
+static PVOID EmsMemory = NULL;
+static USHORT EmsSegment = EMS_SEGMENT;
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+static VOID InitHandlesTable(VOID)
+{
+ USHORT i;
+
+ for (i = 0; i < ARRAYSIZE(HandleTable); i++)
+ {
+ HandleTable[i].Allocated = FALSE;
+ HandleTable[i].PageCount = 0;
+ RtlZeroMemory(HandleTable[i].Name, sizeof(HandleTable[i].Name));
+ InitializeListHead(&HandleTable[i].PageList);
+ }
+}
+
+static PEMS_HANDLE CreateHandle(PUSHORT Handle)
+{
+ PEMS_HANDLE HandleEntry;
+ USHORT i;
+
+ /* Handle 0 is reserved (system handle) */
+ for (i = 1; i < ARRAYSIZE(HandleTable); i++)
+ {
+ HandleEntry = &HandleTable[i];
+ if (!HandleEntry->Allocated)
+ {
+ *Handle = i;
+ HandleEntry->Allocated = TRUE;
+ return HandleEntry;
+ }
+ }
+
+ return NULL;
+}
+
+static VOID FreeHandle(PEMS_HANDLE HandleEntry)
+{
+ HandleEntry->Allocated = FALSE;
+ HandleEntry->PageCount = 0;
+ RtlZeroMemory(HandleEntry->Name, sizeof(HandleEntry->Name));
+ // InitializeListHead(&HandleEntry->PageList);
+}
+
+static inline PEMS_HANDLE GetHandleRecord(USHORT Handle)
+{
+ if (Handle >= ARRAYSIZE(HandleTable)) return NULL;
+ return &HandleTable[Handle];
+}
+
+static inline BOOLEAN ValidateHandle(PEMS_HANDLE HandleEntry)
+{
+ return (HandleEntry != NULL && HandleEntry->Allocated);
+}
+
+static UCHAR EmsFree(USHORT Handle)
+{
+ PLIST_ENTRY Entry;
+ PEMS_HANDLE HandleEntry = GetHandleRecord(Handle);
+
+ if (!ValidateHandle(HandleEntry))
+ 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);
+ }
+
+ InitializeListHead(&HandleEntry->PageList);
+
+ if (Handle != EMS_SYSTEM_HANDLE)
+ FreeHandle(HandleEntry);
+
+ return EMS_STATUS_SUCCESS;
+}
+
+static UCHAR EmsAlloc(USHORT NumPages, PUSHORT Handle)
+{
+ ULONG i, CurrentIndex = 0;
+ PEMS_HANDLE HandleEntry;
+
+ if (NumPages == 0) return EMS_STATUS_ZERO_PAGES;
+
+ HandleEntry = CreateHandle(Handle);
+ if (!HandleEntry) return EMS_STATUS_NO_MORE_HANDLES;
+
+ 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_SUCCESS;
+}
+
+static UCHAR InitSystemHandle(USHORT NumPages)
+{
+ //
+ // FIXME: This is an adapted copy of EmsAlloc!!
+ //
+
+ ULONG i, CurrentIndex = 0;
+ PEMS_HANDLE HandleEntry = &HandleTable[EMS_SYSTEM_HANDLE];
+
+ /* The system handle must never have been initialized before */
+ ASSERT(!HandleEntry->Allocated);
+
+ /* Now allocate it */
+ 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(EMS_SYSTEM_HANDLE);
+ // FIXME: For this function (and EmsAlloc as well),
+ // use instead an internal function that just uses
+ // PEMS_HANDLE pointers instead. It's only in the
+ // EMS interrupt handler that we should do the
+ // unfolding.
+ 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 = EMS_SYSTEM_HANDLE;
+ InsertTailList(&HandleEntry->PageList, &PageTable[RunStart + i].Entry);
+ }
+ }
+
+ return EMS_STATUS_SUCCESS;
+}
+
+static PEMS_PAGE GetLogicalPage(PEMS_HANDLE HandleEntry, USHORT LogicalPage)
+{
+ PLIST_ENTRY Entry = HandleEntry->PageList.Flink;
+
+ while (LogicalPage)
+ {
+ if (Entry == &HandleEntry->PageList) return NULL;
+ LogicalPage--;
+ Entry = Entry->Flink;
+ }
+
+ return (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
+}
+
+static UCHAR EmsMap(USHORT Handle, UCHAR PhysicalPage, USHORT LogicalPage)
+{
+ PEMS_PAGE PageEntry;
+ PEMS_HANDLE HandleEntry = GetHandleRecord(Handle);
+
+ if (!ValidateHandle(HandleEntry))
+ return EMS_STATUS_INVALID_HANDLE;
+
+ if (PhysicalPage >= EMS_PHYSICAL_PAGES)
+ return EMS_STATUS_INV_PHYSICAL_PAGE;
+
+ if (LogicalPage == 0xFFFF)
+ {
+ /* Unmap */
+ Mapping[PhysicalPage] = NULL;
+ return EMS_STATUS_SUCCESS;
+ }
+
+ 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_SUCCESS;
+}
+
+static VOID WINAPI EmsIntHandler(LPWORD Stack)
+{
+ switch (getAH())
+ {
+ /* Get Manager Status */
+ case 0x40:
+ {
+ setAH(EMS_STATUS_SUCCESS);
+ break;
+ }
+
+ /* Get Page Frame Segment */
+ case 0x41:
+ {
+ setAH(EMS_STATUS_SUCCESS);
+ setBX(EmsSegment);
+ break;
+ }
+
+ /* Get Number of Unallocated Pages */
+ case 0x42:
+ {
+ setAH(EMS_STATUS_SUCCESS);
+ setBX(RtlNumberOfClearBits(&AllocBitmap));
+ setDX(EmsTotalPages);
+ break;
+ }
+
+ /* Get Handle and Allocate Memory */
+ case 0x43:
+ {
+ USHORT Handle;
+ UCHAR Status = EmsAlloc(getBX(), &Handle);
+
+ if (Status == EMS_STATUS_SUCCESS)
+ setDX(Handle);
+
+ setAH(Status);
+ 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_SUCCESS);
+ setAL(EMS_VERSION_NUM);
+ break;
+ }
+
+ /* Save Page Map */
+ case 0x47:
+ {
+ // FIXME: This depends on an EMS handle given in DX
+ RtlCopyMemory(MappingBackup, Mapping, sizeof(Mapping));
+ setAH(EMS_STATUS_SUCCESS);
+ break;
+ }
+
+ /* Restore Page Map */
+ case 0x48:
+ {
+ // FIXME: This depends on an EMS handle given in DX
+ RtlCopyMemory(Mapping, MappingBackup, sizeof(Mapping));
+ setAH(EMS_STATUS_SUCCESS);
+ break;
+ }
+
+ /* Get Number of Opened Handles */
+ case 0x4B:
+ {
+ USHORT NumOpenHandles = 0;
+ USHORT i;
+
+ for (i = 0; i < ARRAYSIZE(HandleTable); i++)
+ {
+ if (HandleTable[i].Allocated)
+ ++NumOpenHandles;
+ }
+
+ setAH(EMS_STATUS_SUCCESS);
+ setBX(NumOpenHandles);
+ break;
+ }
+
+ /* Get Handle Number of Pages */
+ case 0x4C:
+ {
+ PEMS_HANDLE HandleEntry = GetHandleRecord(getDX());
+
+ if (!ValidateHandle(HandleEntry))
+ {
+ setAH(EMS_STATUS_INVALID_HANDLE);
+ break;
+ }
+
+ setAH(EMS_STATUS_SUCCESS);
+ setBX(HandleEntry->PageCount);
+ break;
+ }
+
+ /* Get All Handles Number of Pages */
+ case 0x4D:
+ {
+ PEMS_HANDLE_PAGE_INFO HandlePageInfo = (PEMS_HANDLE_PAGE_INFO)SEG_OFF_TO_PTR(getES(), getDI());
+ USHORT NumOpenHandles = 0;
+ USHORT i;
+
+ for (i = 0; i < ARRAYSIZE(HandleTable); i++)
+ {
+ if (HandleTable[i].Allocated)
+ {
+ HandlePageInfo->Handle = i;
+ HandlePageInfo->PageCount = HandleTable[i].PageCount;
+ ++HandlePageInfo;
+ ++NumOpenHandles;
+ }
+ }
+
+ setAH(EMS_STATUS_SUCCESS);
+ setBX(NumOpenHandles);
+ break;
+ }
+
+ /* Get or Set Page Map */
+ case 0x4E:
+ {
+ switch (getAL())
+ {
+ /* Get Mapping Registers */
+ // case 0x00: // TODO: NOT IMPLEMENTED
+
+ /* Set Mapping Registers */
+ // case 0x01: // TODO: NOT IMPLEMENTED
+
+ /* Get and Set Mapping Registers At Once */
+ // case 0x02: // TODO: NOT IMPLEMENTED
+
+ /* Get Size of Page-Mapping Array */
+ case 0x03:
+ {
+ setAH(EMS_STATUS_SUCCESS);
+ setAL(sizeof(Mapping));
+ break;
+ }
+
+ default:
+ {
+ DPRINT1("EMS function AH = 0x4E, subfunction AL = %02X NOT IMPLEMENTED\n", getAL());
+ setAH(EMS_STATUS_UNKNOWN_FUNCTION);
+ break;
+ }
+ }
+
+ break;
+ }
+
+ /* Get/Set Handle Name */
+ case 0x53:
+ {
+ PEMS_HANDLE HandleEntry = GetHandleRecord(getDX());
+
+ if (!ValidateHandle(HandleEntry))
+ {
+ setAH(EMS_STATUS_INVALID_HANDLE);
+ break;
+ }
+
+ if (getAL() == 0x00)
+ {
+ /* Retrieve the name */
+ RtlCopyMemory(SEG_OFF_TO_PTR(getES(), getDI()),
+ HandleEntry->Name,
+ sizeof(HandleEntry->Name));
+ setAH(EMS_STATUS_SUCCESS);
+ }
+ else if (getAL() == 0x01)
+ {
+ /* Store the name */
+ RtlCopyMemory(HandleEntry->Name,
+ SEG_OFF_TO_PTR(getDS(), getSI()),
+ sizeof(HandleEntry->Name));
+ setAH(EMS_STATUS_SUCCESS);
+ }
+ else
+ {
+ DPRINT1("Invalid subfunction %02X for EMS function AH = 53h\n", getAL());
+ setAH(EMS_STATUS_INVALID_SUBFUNCTION);
+ }
+
+ break;
+ }
+
+ /* Handle Directory functions */
+ case 0x54:
+ {
+ if (getAL() == 0x00)
+ {
+ /* Get Handle Directory */
+
+ PEMS_HANDLE_DIR_ENTRY HandleDir = (PEMS_HANDLE_DIR_ENTRY)SEG_OFF_TO_PTR(getES(), getDI());
+ USHORT NumOpenHandles = 0;
+ USHORT i;
+
+ for (i = 0; i < ARRAYSIZE(HandleTable); i++)
+ {
+ if (HandleTable[i].Allocated)
+ {
+ HandleDir->Handle = i;
+ RtlCopyMemory(HandleDir->Name,
+ HandleTable[i].Name,
+ sizeof(HandleDir->Name));
+ ++HandleDir;
+ ++NumOpenHandles;
+ }
+ }
+
+ setAH(EMS_STATUS_SUCCESS);
+ setAL((UCHAR)NumOpenHandles);
+ }
+ else if (getAL() == 0x01)
+ {
+ /* Search for Named Handle */
+
+ PUCHAR HandleName = (PUCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
+ PEMS_HANDLE HandleFound = NULL;
+ USHORT i;
+
+ for (i = 0; i < ARRAYSIZE(HandleTable); i++)
+ {
+ if (HandleTable[i].Allocated &&
+ RtlCompareMemory(HandleName,
+ HandleTable[i].Name,
+ sizeof(HandleTable[i].Name)) == sizeof(HandleTable[i].Name))
+ {
+ HandleFound = &HandleTable[i];
+ break;
+ }
+ }
+
+ /* Bail out if no handle was found */
+ if (i >= ARRAYSIZE(HandleTable)) // HandleFound == NULL
+ {
+ setAH(EMS_STATUS_HANDLE_NOT_FOUND);
+ break;
+ }
+
+ /* Return the handle number */
+ setDX(i);
+
+ /* Sanity check: Check whether the handle was unnamed */
+ i = 0;
+ while ((i < sizeof(HandleFound->Name)) && (HandleFound->Name[i] == '\0'))
+ ++i;
+
+ if (i >= sizeof(HandleFound->Name))
+ {
+ setAH(EMS_STATUS_UNNAMED_HANDLE);
+ }
+ else
+ {
+ setAH(EMS_STATUS_SUCCESS);
+ }
+ }
+ else if (getAL() == 0x02)
+ {
+ /*
+ * Get Total Number of Handles
+ *
+ * This function retrieves the maximum number of handles
+ * (allocated or not) the memory manager supports, which
+ * a program may request.
+ */
+ setAH(EMS_STATUS_SUCCESS);
+ setBX(ARRAYSIZE(HandleTable));
+ }
+ else
+ {
+ DPRINT1("Invalid subfunction %02X for EMS function AH = 54h\n", getAL());
+ setAH(EMS_STATUS_INVALID_SUBFUNCTION);
+ }
+
+ 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 = GetHandleRecord(Data->SourceHandle);
+ if (!ValidateHandle(HandleEntry))
+ {
+ setAH(EMS_STATUS_INVALID_HANDLE);
+ break;
+ }
+
+ PageEntry = GetLogicalPage(HandleEntry, Data->SourceSegment);
+ if (!PageEntry)
+ {
+ setAH(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 = GetHandleRecord(Data->DestHandle);
+ if (!ValidateHandle(HandleEntry))
+ {
+ setAH(EMS_STATUS_INVALID_HANDLE);
+ break;
+ }
+
+ PageEntry = GetLogicalPage(HandleEntry, Data->DestSegment);
+ if (!PageEntry)
+ {
+ setAH(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);
+ }
+
+ setAH(EMS_STATUS_SUCCESS);
+ break;
+ }
+
+ /* Get Mappable Physical Address Array */
+ case 0x58:
+ {
+ if (getAL() == 0x00)
+ {
+ PEMS_MAPPABLE_PHYS_PAGE PageArray = (PEMS_MAPPABLE_PHYS_PAGE)SEG_OFF_TO_PTR(getES(), getDI());
+ ULONG i;
+
+ for (i = 0; i < EMS_PHYSICAL_PAGES; i++)
+ {
+ PageArray->PageSegment = EMS_SEGMENT + i * (EMS_PAGE_SIZE >> 4);
+ PageArray->PageNumber = i;
+ ++PageArray;
+ }
+
+ setAH(EMS_STATUS_SUCCESS);
+ setCX(EMS_PHYSICAL_PAGES);
+ }
+ else if (getAL() == 0x01)
+ {
+ setAH(EMS_STATUS_SUCCESS);
+ setCX(EMS_PHYSICAL_PAGES);
+ }
+ else
+ {
+ DPRINT1("Invalid subfunction %02X for EMS function AH = 58h\n", getAL());
+ setAH(EMS_STATUS_INVALID_SUBFUNCTION);
+ }
+
+ break;
+ }
+
+ /* Get Expanded Memory Hardware Information */
+ case 0x59:
+ {
+ if (getAL() == 0x00)
+ {
+ PEMS_HARDWARE_INFO HardwareInfo = (PEMS_HARDWARE_INFO)SEG_OFF_TO_PTR(getES(), getDI());
+
+ /* Return the hardware information */
+ HardwareInfo->RawPageSize = EMS_PAGE_SIZE >> 4;
+ HardwareInfo->AlternateRegSets = 0;
+ HardwareInfo->ContextAreaSize = sizeof(Mapping);
+ HardwareInfo->DmaRegisterSets = 0;
+ HardwareInfo->DmaChannelOperation = 0;
+
+ setAH(EMS_STATUS_SUCCESS);
+ }
+ else if (getAL() == 0x01)
+ {
+ /* Same as function AH = 42h */
+ setAH(EMS_STATUS_SUCCESS);
+ setBX(RtlNumberOfClearBits(&AllocBitmap));
+ setDX(EmsTotalPages);
+ }
+ else
+ {
+ DPRINT1("Invalid subfunction %02X for EMS function AH = 59h\n", getAL());
+ setAH(EMS_STATUS_INVALID_SUBFUNCTION);
+ }
+
+ break;
+ }
+
+ default:
+ {
+ DPRINT1("EMS function AH = %02X NOT IMPLEMENTED\n", getAH());
+ setAH(EMS_STATUS_UNKNOWN_FUNCTION);
+ break;
+ }
+ }
+}
+
+static VOID FASTCALL EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
+{
+ ULONG i;
+ ULONG RelativeAddress = Address - TO_LINEAR(EmsSegment, 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 FASTCALL EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
+{
+ ULONG i;
+ ULONG RelativeAddress = Address - TO_LINEAR(EmsSegment, 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;
+}
+
+static WORD NTAPI EmsDrvDispatchIoctlRead(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length)
+{
+ // TODO: NOT IMPLEMENTED
+ UNIMPLEMENTED;
+ return DOS_DEVSTAT_DONE;
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+BOOLEAN EmsDrvInitialize(USHORT Segment, ULONG TotalPages)
+{
+ USHORT Size;
+
+ /* Try to allocate our page table in UMA at the given segment */
+ EmsSegment = (Segment != 0 ? Segment : EMS_SEGMENT);
+ Size = EMS_SEGMENT_SIZE; // Size in paragraphs
+ if (!UmaDescReserve(&EmsSegment, &Size)) return FALSE;
+
+ EmsTotalPages = TotalPages;
+ BitmapBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ ((TotalPages + 31) / 32) * sizeof(ULONG));
+ if (BitmapBuffer == NULL)
+ {
+ UmaDescRelease(EmsSegment);
+ 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;
+
+ UmaDescRelease(EmsSegment);
+ 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;
+
+ UmaDescRelease(EmsSegment);
+ return FALSE;
+ }
+
+ InitHandlesTable();
+ /*
+ * FIXME: We should ensure that the system handle is associated
+ * with mapped pages from conventional memory. DosEmu seems to do
+ * it correctly. 384kB of memory mapped.
+ */
+ if (InitSystemHandle(384/16) != EMS_STATUS_SUCCESS)
+ {
+ DPRINT1("Impossible to allocate pages for the system handle!\n");
+
+ RtlFreeHeap(RtlGetProcessHeap(), 0, EmsMemory);
+ EmsMemory = NULL;
+ RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
+ PageTable = NULL;
+ RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
+ BitmapBuffer = NULL;
+
+ UmaDescRelease(EmsSegment);
+ return FALSE;
+ }
+
+ MemInstallFastMemoryHook((PVOID)TO_LINEAR(EmsSegment, 0),
+ EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE,
+ EmsReadMemory,
+ EmsWriteMemory);
+
+ /* Create the device */
+ Node = DosCreateDeviceEx(DOS_DEVATTR_IOCTL | DOS_DEVATTR_CHARACTER,
+ EMS_DEVICE_NAME,
+ Int16To32StubSize);
+ Node->IoctlReadRoutine = EmsDrvDispatchIoctlRead;
+
+ RegisterInt32(DEVICE_PRIVATE_AREA(Node->Driver),
+ EMS_INTERRUPT_NUM, EmsIntHandler, NULL);
+
+ return TRUE;
+}
+
+VOID EmsDrvCleanup(VOID)
+{
+ /* Delete the device */
+ DosDeleteDevice(Node);
+
+ MemRemoveFastMemoryHook((PVOID)TO_LINEAR(EmsSegment, 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;
+ }
+
+ UmaDescRelease(EmsSegment);
+}