mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 03:48:17 +00:00
819 lines
23 KiB
C
819 lines
23 KiB
C
/*
|
|
* COPYRIGHT: GPLv2+ - See COPYING in the top level directory
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
|
* FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c
|
|
* PURPOSE: DOS XMS Driver and UMB Provider
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
|
|
*
|
|
* DOCUMENTATION: Official specifications:
|
|
* XMS v2.0: http://www.phatcode.net/res/219/files/xms20.txt
|
|
* XMS v3.0: http://www.phatcode.net/res/219/files/xms30.txt
|
|
*
|
|
* About the implementation of UMBs in DOS:
|
|
* ----------------------------------------
|
|
* DOS needs a UMB provider to be able to use chunks of RAM in the C000-EFF0
|
|
* memory region. A UMB provider detects regions of memory that do not contain
|
|
* any ROMs or other system mapped area such as video RAM.
|
|
*
|
|
* Where can UMB providers be found?
|
|
*
|
|
* The XMS specification (see himem.c) provides three APIs to create, free, and
|
|
* resize UMB blocks. As such, DOS performs calls into the XMS driver chain to
|
|
* request UMB blocks and include them into the DOS memory arena.
|
|
* However, is it only the HIMEM driver (implementing the XMS specification)
|
|
* which can provide UMBs? It appears that this is not necessarily the case:
|
|
* for example the MS HIMEM versions do not implement the UMB APIs; instead
|
|
* it is the EMS driver (EMM386) which provides them, by hooking into the XMS
|
|
* driver chain (see https://support.microsoft.com/en-us/kb/95555 : "MS-DOS 5.0
|
|
* and later EMM386.EXE can also be configured to provide UMBs according to the
|
|
* XMS. This causes EMM386.EXE to be a provider of the UMB portion of the XMS.").
|
|
*
|
|
* Some alternative replacements of HIMEM/EMM386 (for example in FreeDOS)
|
|
* implement everything inside only one driver (XMS+EMS+UMB provider).
|
|
* Finally there are some UMB providers that exist separately of XMS and EMS
|
|
* drivers (for example, UMBPCI): they use hardware-specific tricks to discover
|
|
* and provide UMBs.
|
|
*
|
|
* For more details, see:
|
|
* http://www.freedos.org/technotes/technote/txt/169.txt
|
|
* http://www.freedos.org/technotes/technote/txt/202.txt
|
|
* http://www.uncreativelabs.net/textfiles/system/UMB.TXT
|
|
*
|
|
* This DOS XMS Driver provides the UMB APIs that are implemented on top
|
|
* of the internal Upper Memory Area Manager, in umamgr.c
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "ntvdm.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#include "emulator.h"
|
|
#include "cpu/bop.h"
|
|
#include "../../memory.h"
|
|
#include "bios/umamgr.h"
|
|
|
|
#include "device.h"
|
|
#include "himem.h"
|
|
|
|
#define XMS_DEVICE_NAME "XMSXXXX0"
|
|
|
|
/* BOP Identifiers */
|
|
#define BOP_XMS 0x52
|
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
static const BYTE EntryProcedure[] =
|
|
{
|
|
0xEB, // jmp short +0x03
|
|
0x03,
|
|
0x90, // nop
|
|
0x90, // nop
|
|
0x90, // nop
|
|
LOBYTE(EMULATOR_BOP),
|
|
HIBYTE(EMULATOR_BOP),
|
|
BOP_XMS,
|
|
0xCB // retf
|
|
};
|
|
|
|
static PDOS_DEVICE_NODE Node = NULL;
|
|
static XMS_HANDLE HandleTable[XMS_MAX_HANDLES];
|
|
static WORD FreeBlocks = XMS_BLOCKS;
|
|
static RTL_BITMAP AllocBitmap;
|
|
static ULONG BitmapBuffer[(XMS_BLOCKS + 31) / 32];
|
|
|
|
/*
|
|
* This value is associated to HIMEM's "/HMAMIN=" switch. It indicates the
|
|
* minimum account of space in the HMA a program can use, and is used in
|
|
* conjunction with the "Request HMA" function.
|
|
*
|
|
* NOTE: The "/HMAMIN=" value is in kilo-bytes, whereas HmaMinSize is in bytes.
|
|
*
|
|
* Default value: 0. This causes the HMA to be allocated on a first come,
|
|
* first served basis.
|
|
*/
|
|
static WORD HmaMinSize = 0;
|
|
/*
|
|
* Flag used by "Request/Release HMA" functions, which indicates
|
|
* whether the HMA was reserved or not.
|
|
*/
|
|
static BOOLEAN IsHmaReserved = FALSE;
|
|
|
|
/*
|
|
* Flag used by "Global Enable/Disable A20" functions, so that they don't
|
|
* need to re-change the state of A20 if it was already enabled/disabled.
|
|
*/
|
|
static BOOLEAN IsA20Enabled = FALSE;
|
|
/*
|
|
* This flag is set to TRUE or FALSE when A20 line was already disabled or
|
|
* enabled when XMS driver was loaded.
|
|
* In case A20 was disabled, we are allowed to modify it. In case A20 was
|
|
* already enabled, we are not allowed to touch it.
|
|
*/
|
|
static BOOLEAN CanChangeA20 = TRUE;
|
|
/*
|
|
* Count for enabling or disabling the A20 line. The A20 line is enabled
|
|
* only if the enabling count is greater than or equal to 0.
|
|
*/
|
|
static LONG A20EnableCount = 0;
|
|
|
|
/* A20 LINE HELPERS ***********************************************************/
|
|
|
|
static VOID XmsLocalEnableA20(VOID)
|
|
{
|
|
/* Enable A20 only if we can do so, otherwise make the caller believe we enabled it */
|
|
if (!CanChangeA20) goto Quit;
|
|
|
|
/* The count is zero so enable A20 */
|
|
if (A20EnableCount == 0) EmulatorSetA20(TRUE);
|
|
|
|
++A20EnableCount;
|
|
|
|
Quit:
|
|
setAX(0x0001); /* Line successfully enabled */
|
|
setBL(XMS_STATUS_SUCCESS);
|
|
return;
|
|
}
|
|
|
|
static VOID XmsLocalDisableA20(VOID)
|
|
{
|
|
UCHAR Result = XMS_STATUS_SUCCESS;
|
|
|
|
/* Disable A20 only if we can do so, otherwise make the caller believe we disabled it */
|
|
if (!CanChangeA20) goto Quit;
|
|
|
|
/* If the count is already zero, fail */
|
|
if (A20EnableCount == 0) goto Fail;
|
|
|
|
--A20EnableCount;
|
|
|
|
/* The count is zero so disable A20 */
|
|
if (A20EnableCount == 0)
|
|
EmulatorSetA20(FALSE); // Result = XMS_STATUS_SUCCESS;
|
|
else
|
|
Result = XMS_STATUS_A20_STILL_ENABLED;
|
|
|
|
Quit:
|
|
setAX(0x0001); /* Line successfully disabled */
|
|
setBL(Result);
|
|
return;
|
|
|
|
Fail:
|
|
setAX(0x0000); /* Line failed to be disabled */
|
|
setBL(XMS_STATUS_A20_ERROR);
|
|
return;
|
|
}
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
static inline PXMS_HANDLE GetXmsHandleRecord(WORD Handle)
|
|
{
|
|
PXMS_HANDLE Entry;
|
|
if (Handle == 0 || Handle >= XMS_MAX_HANDLES) return NULL;
|
|
|
|
Entry = &HandleTable[Handle - 1];
|
|
return Entry->Size ? Entry : NULL;
|
|
}
|
|
|
|
static inline BOOLEAN ValidateXmsHandle(PXMS_HANDLE HandleEntry)
|
|
{
|
|
return (HandleEntry != NULL && HandleEntry->Handle != 0);
|
|
}
|
|
|
|
static WORD XmsGetLargestFreeBlock(VOID)
|
|
{
|
|
WORD Result = 0;
|
|
DWORD CurrentIndex = 0;
|
|
ULONG RunStart;
|
|
ULONG RunSize;
|
|
|
|
while (CurrentIndex < XMS_BLOCKS)
|
|
{
|
|
RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
|
|
if (RunSize == 0) break;
|
|
|
|
/* Update the maximum */
|
|
if (RunSize > Result) Result = RunSize;
|
|
|
|
/* Go to the next run */
|
|
CurrentIndex = RunStart + RunSize;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
static UCHAR XmsAlloc(WORD Size, PWORD Handle)
|
|
{
|
|
BYTE i;
|
|
PXMS_HANDLE HandleEntry;
|
|
DWORD CurrentIndex = 0;
|
|
ULONG RunStart;
|
|
ULONG RunSize;
|
|
|
|
if (Size > FreeBlocks) return XMS_STATUS_OUT_OF_MEMORY;
|
|
|
|
for (i = 0; i < XMS_MAX_HANDLES; i++)
|
|
{
|
|
HandleEntry = &HandleTable[i];
|
|
if (HandleEntry->Handle == 0)
|
|
{
|
|
*Handle = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == XMS_MAX_HANDLES) return XMS_STATUS_OUT_OF_HANDLES;
|
|
|
|
/* Optimize blocks */
|
|
for (i = 0; i < XMS_MAX_HANDLES; i++)
|
|
{
|
|
/* Skip free and locked blocks */
|
|
if (HandleEntry->Handle == 0 || HandleEntry->LockCount > 0) continue;
|
|
|
|
CurrentIndex = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE;
|
|
|
|
/* Check if there is any free space before this block */
|
|
RunSize = RtlFindLastBackwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
|
|
if (RunSize == 0) break;
|
|
|
|
/* Move this block back */
|
|
RtlMoveMemory((PVOID)REAL_TO_PHYS(HandleEntry->Address - RunSize * XMS_BLOCK_SIZE),
|
|
(PVOID)REAL_TO_PHYS(HandleEntry->Address),
|
|
RunSize * XMS_BLOCK_SIZE);
|
|
|
|
/* Update the address */
|
|
HandleEntry->Address -= RunSize * XMS_BLOCK_SIZE;
|
|
}
|
|
|
|
while (CurrentIndex < XMS_BLOCKS)
|
|
{
|
|
RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
|
|
if (RunSize == 0) break;
|
|
|
|
if (RunSize >= HandleEntry->Size)
|
|
{
|
|
/* Allocate it here */
|
|
HandleEntry->Handle = i + 1;
|
|
HandleEntry->LockCount = 0;
|
|
HandleEntry->Size = Size;
|
|
HandleEntry->Address = XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE;
|
|
|
|
FreeBlocks -= Size;
|
|
RtlSetBits(&AllocBitmap, RunStart, HandleEntry->Size);
|
|
|
|
return XMS_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Keep searching */
|
|
CurrentIndex = RunStart + RunSize;
|
|
}
|
|
|
|
return XMS_STATUS_OUT_OF_MEMORY;
|
|
}
|
|
|
|
static UCHAR XmsRealloc(WORD Handle, WORD NewSize)
|
|
{
|
|
DWORD BlockNumber;
|
|
PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
|
|
DWORD CurrentIndex = 0;
|
|
ULONG RunStart;
|
|
ULONG RunSize;
|
|
|
|
if (!ValidateXmsHandle(HandleEntry))
|
|
return XMS_STATUS_INVALID_HANDLE;
|
|
|
|
if (HandleEntry->LockCount)
|
|
return XMS_STATUS_LOCKED;
|
|
|
|
/* Get the block number */
|
|
BlockNumber = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE;
|
|
|
|
if (NewSize < HandleEntry->Size)
|
|
{
|
|
/* Just reduce the size of this block */
|
|
RtlClearBits(&AllocBitmap, BlockNumber + NewSize, HandleEntry->Size - NewSize);
|
|
FreeBlocks += HandleEntry->Size - NewSize;
|
|
HandleEntry->Size = NewSize;
|
|
}
|
|
else if (NewSize > HandleEntry->Size)
|
|
{
|
|
/* Check if we can expand in-place */
|
|
if (RtlAreBitsClear(&AllocBitmap,
|
|
BlockNumber + HandleEntry->Size,
|
|
NewSize - HandleEntry->Size))
|
|
{
|
|
/* Just increase the size of this block */
|
|
RtlSetBits(&AllocBitmap,
|
|
BlockNumber + HandleEntry->Size,
|
|
NewSize - HandleEntry->Size);
|
|
FreeBlocks -= NewSize - HandleEntry->Size;
|
|
HandleEntry->Size = NewSize;
|
|
|
|
/* We're done */
|
|
return XMS_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Deallocate the current block range */
|
|
RtlClearBits(&AllocBitmap, BlockNumber, HandleEntry->Size);
|
|
|
|
/* Find a new place for this block */
|
|
while (CurrentIndex < XMS_BLOCKS)
|
|
{
|
|
RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
|
|
if (RunSize == 0) break;
|
|
|
|
if (RunSize >= NewSize)
|
|
{
|
|
/* Allocate the new range */
|
|
RtlSetBits(&AllocBitmap, RunStart, NewSize);
|
|
|
|
/* Move the data to the new location */
|
|
RtlMoveMemory((PVOID)REAL_TO_PHYS(XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE),
|
|
(PVOID)REAL_TO_PHYS(HandleEntry->Address),
|
|
HandleEntry->Size * XMS_BLOCK_SIZE);
|
|
|
|
/* Update the handle entry */
|
|
HandleEntry->Address = XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE;
|
|
HandleEntry->Size = NewSize;
|
|
|
|
/* Update the free block counter */
|
|
FreeBlocks -= NewSize - HandleEntry->Size;
|
|
|
|
return XMS_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Keep searching */
|
|
CurrentIndex = RunStart + RunSize;
|
|
}
|
|
|
|
/* Restore the old block range */
|
|
RtlSetBits(&AllocBitmap, BlockNumber, HandleEntry->Size);
|
|
return XMS_STATUS_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return XMS_STATUS_SUCCESS;
|
|
}
|
|
|
|
static UCHAR XmsFree(WORD Handle)
|
|
{
|
|
DWORD BlockNumber;
|
|
PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
|
|
|
|
if (!ValidateXmsHandle(HandleEntry))
|
|
return XMS_STATUS_INVALID_HANDLE;
|
|
|
|
if (HandleEntry->LockCount)
|
|
return XMS_STATUS_LOCKED;
|
|
|
|
BlockNumber = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE;
|
|
RtlClearBits(&AllocBitmap, BlockNumber, HandleEntry->Size);
|
|
|
|
HandleEntry->Handle = 0;
|
|
FreeBlocks += HandleEntry->Size;
|
|
|
|
return XMS_STATUS_SUCCESS;
|
|
}
|
|
|
|
static UCHAR XmsLock(WORD Handle, PDWORD Address)
|
|
{
|
|
PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
|
|
|
|
if (!ValidateXmsHandle(HandleEntry))
|
|
return XMS_STATUS_INVALID_HANDLE;
|
|
|
|
if (HandleEntry->LockCount == 0xFF)
|
|
return XMS_STATUS_LOCK_OVERFLOW;
|
|
|
|
/* Increment the lock count */
|
|
HandleEntry->LockCount++;
|
|
*Address = HandleEntry->Address;
|
|
|
|
return XMS_STATUS_SUCCESS;
|
|
}
|
|
|
|
static UCHAR XmsUnlock(WORD Handle)
|
|
{
|
|
PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
|
|
|
|
if (!ValidateXmsHandle(HandleEntry))
|
|
return XMS_STATUS_INVALID_HANDLE;
|
|
|
|
if (!HandleEntry->LockCount)
|
|
return XMS_STATUS_NOT_LOCKED;
|
|
|
|
/* Decrement the lock count */
|
|
HandleEntry->LockCount--;
|
|
|
|
return XMS_STATUS_SUCCESS;
|
|
}
|
|
|
|
static VOID WINAPI XmsBopProcedure(LPWORD Stack)
|
|
{
|
|
switch (getAH())
|
|
{
|
|
/* Get XMS Version */
|
|
case 0x00:
|
|
{
|
|
setAX(0x0300); /* XMS version 3.00 */
|
|
setBX(0x0301); /* Driver version 3.01 */
|
|
setDX(0x0001); /* HMA present */
|
|
break;
|
|
}
|
|
|
|
/* Request HMA */
|
|
case 0x01:
|
|
{
|
|
/* Check whether HMA is already reserved */
|
|
if (IsHmaReserved)
|
|
{
|
|
/* It is, bail out */
|
|
setAX(0x0000);
|
|
setBL(XMS_STATUS_HMA_IN_USE);
|
|
break;
|
|
}
|
|
|
|
// NOTE: We implicitly suppose that we always have HMA.
|
|
// If not, we should fail there with the XMS_STATUS_HMA_DOES_NOT_EXIST
|
|
// error code.
|
|
|
|
/* Check whether the requested size is above the minimal allowed one */
|
|
if (getDX() < HmaMinSize)
|
|
{
|
|
/* It is not, bail out */
|
|
setAX(0x0000);
|
|
setBL(XMS_STATUS_HMA_MIN_SIZE);
|
|
break;
|
|
}
|
|
|
|
/* Reserve it */
|
|
IsHmaReserved = TRUE;
|
|
setAX(0x0001);
|
|
setBL(XMS_STATUS_SUCCESS);
|
|
break;
|
|
}
|
|
|
|
/* Release HMA */
|
|
case 0x02:
|
|
{
|
|
/* Check whether HMA was reserved */
|
|
if (!IsHmaReserved)
|
|
{
|
|
/* It was not, bail out */
|
|
setAX(0x0000);
|
|
setBL(XMS_STATUS_HMA_NOT_ALLOCATED);
|
|
break;
|
|
}
|
|
|
|
/* Release it */
|
|
IsHmaReserved = FALSE;
|
|
setAX(0x0001);
|
|
setBL(XMS_STATUS_SUCCESS);
|
|
break;
|
|
}
|
|
|
|
/* Global Enable A20 */
|
|
case 0x03:
|
|
{
|
|
/* Enable A20 if needed */
|
|
if (!IsA20Enabled)
|
|
{
|
|
XmsLocalEnableA20();
|
|
if (getAX() != 0x0001)
|
|
{
|
|
/* XmsLocalEnableA20 failed and already set AX and BL to their correct values */
|
|
break;
|
|
}
|
|
|
|
IsA20Enabled = TRUE;
|
|
}
|
|
|
|
setAX(0x0001); /* Line successfully enabled */
|
|
setBL(XMS_STATUS_SUCCESS);
|
|
break;
|
|
}
|
|
|
|
/* Global Disable A20 */
|
|
case 0x04:
|
|
{
|
|
UCHAR Result = XMS_STATUS_SUCCESS;
|
|
|
|
/* Disable A20 if needed */
|
|
if (IsA20Enabled)
|
|
{
|
|
XmsLocalDisableA20();
|
|
if (getAX() != 0x0001)
|
|
{
|
|
/* XmsLocalDisableA20 failed and already set AX and BL to their correct values */
|
|
break;
|
|
}
|
|
|
|
IsA20Enabled = FALSE;
|
|
Result = getBL();
|
|
}
|
|
|
|
setAX(0x0001); /* Line successfully disabled */
|
|
setBL(Result);
|
|
break;
|
|
}
|
|
|
|
/* Local Enable A20 */
|
|
case 0x05:
|
|
{
|
|
/* This call sets AX and BL to their correct values */
|
|
XmsLocalEnableA20();
|
|
break;
|
|
}
|
|
|
|
/* Local Disable A20 */
|
|
case 0x06:
|
|
{
|
|
/* This call sets AX and BL to their correct values */
|
|
XmsLocalDisableA20();
|
|
break;
|
|
}
|
|
|
|
/* Query A20 State */
|
|
case 0x07:
|
|
{
|
|
setAX(EmulatorGetA20());
|
|
setBL(XMS_STATUS_SUCCESS);
|
|
break;
|
|
}
|
|
|
|
/* Query Free Extended Memory */
|
|
case 0x08:
|
|
{
|
|
setAX(XmsGetLargestFreeBlock());
|
|
setDX(FreeBlocks);
|
|
|
|
if (FreeBlocks > 0)
|
|
setBL(XMS_STATUS_SUCCESS);
|
|
else
|
|
setBL(XMS_STATUS_OUT_OF_MEMORY);
|
|
|
|
break;
|
|
}
|
|
|
|
/* Allocate Extended Memory Block */
|
|
case 0x09:
|
|
{
|
|
WORD Handle;
|
|
UCHAR Result = XmsAlloc(getDX(), &Handle);
|
|
|
|
if (Result == XMS_STATUS_SUCCESS)
|
|
{
|
|
setAX(1);
|
|
setDX(Handle);
|
|
}
|
|
else
|
|
{
|
|
setAX(0);
|
|
setBL(Result);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Free Extended Memory Block */
|
|
case 0x0A:
|
|
{
|
|
UCHAR Result = XmsFree(getDX());
|
|
|
|
setAX(Result == XMS_STATUS_SUCCESS);
|
|
setBL(Result);
|
|
break;
|
|
}
|
|
|
|
/* Move Extended Memory Block */
|
|
case 0x0B:
|
|
{
|
|
PVOID SourceAddress, DestAddress;
|
|
PXMS_COPY_DATA CopyData = (PXMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI());
|
|
PXMS_HANDLE HandleEntry;
|
|
|
|
if (CopyData->SourceHandle)
|
|
{
|
|
HandleEntry = GetXmsHandleRecord(CopyData->SourceHandle);
|
|
if (!ValidateXmsHandle(HandleEntry))
|
|
{
|
|
setAX(0);
|
|
setBL(XMS_STATUS_BAD_SRC_HANDLE);
|
|
break;
|
|
}
|
|
|
|
if (CopyData->SourceOffset >= HandleEntry->Size * XMS_BLOCK_SIZE)
|
|
{
|
|
setAX(0);
|
|
setBL(XMS_STATUS_BAD_SRC_OFFSET);
|
|
}
|
|
|
|
SourceAddress = (PVOID)REAL_TO_PHYS(HandleEntry->Address + CopyData->SourceOffset);
|
|
}
|
|
else
|
|
{
|
|
/* The offset is actually a 16-bit segment:offset pointer */
|
|
SourceAddress = FAR_POINTER(CopyData->SourceOffset);
|
|
}
|
|
|
|
if (CopyData->DestHandle)
|
|
{
|
|
HandleEntry = GetXmsHandleRecord(CopyData->DestHandle);
|
|
if (!ValidateXmsHandle(HandleEntry))
|
|
{
|
|
setAX(0);
|
|
setBL(XMS_STATUS_BAD_DEST_HANDLE);
|
|
break;
|
|
}
|
|
|
|
if (CopyData->DestOffset >= HandleEntry->Size * XMS_BLOCK_SIZE)
|
|
{
|
|
setAX(0);
|
|
setBL(XMS_STATUS_BAD_DEST_OFFSET);
|
|
}
|
|
|
|
DestAddress = (PVOID)REAL_TO_PHYS(HandleEntry->Address + CopyData->DestOffset);
|
|
}
|
|
else
|
|
{
|
|
/* The offset is actually a 16-bit segment:offset pointer */
|
|
DestAddress = FAR_POINTER(CopyData->DestOffset);
|
|
}
|
|
|
|
/* Perform the move */
|
|
RtlMoveMemory(DestAddress, SourceAddress, CopyData->Count);
|
|
|
|
setAX(1);
|
|
setBL(XMS_STATUS_SUCCESS);
|
|
break;
|
|
}
|
|
|
|
/* Lock Extended Memory Block */
|
|
case 0x0C:
|
|
{
|
|
DWORD Address;
|
|
UCHAR Result = XmsLock(getDX(), &Address);
|
|
|
|
if (Result == XMS_STATUS_SUCCESS)
|
|
{
|
|
setAX(1);
|
|
|
|
/* Store the LINEAR address in DX:BX */
|
|
setDX(HIWORD(Address));
|
|
setBX(LOWORD(Address));
|
|
}
|
|
else
|
|
{
|
|
setAX(0);
|
|
setBL(Result);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Unlock Extended Memory Block */
|
|
case 0x0D:
|
|
{
|
|
UCHAR Result = XmsUnlock(getDX());
|
|
|
|
setAX(Result == XMS_STATUS_SUCCESS);
|
|
setBL(Result);
|
|
break;
|
|
}
|
|
|
|
/* Get Handle Information */
|
|
case 0x0E:
|
|
{
|
|
PXMS_HANDLE HandleEntry = GetXmsHandleRecord(getDX());
|
|
UINT i;
|
|
UCHAR Handles = 0;
|
|
|
|
if (!ValidateXmsHandle(HandleEntry))
|
|
{
|
|
setAX(0);
|
|
setBL(XMS_STATUS_INVALID_HANDLE);
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < XMS_MAX_HANDLES; i++)
|
|
{
|
|
if (HandleTable[i].Handle == 0) Handles++;
|
|
}
|
|
|
|
setAX(1);
|
|
setBH(HandleEntry->LockCount);
|
|
setBL(Handles);
|
|
setDX(HandleEntry->Size);
|
|
break;
|
|
}
|
|
|
|
/* Reallocate Extended Memory Block */
|
|
case 0x0F:
|
|
{
|
|
UCHAR Result = XmsRealloc(getDX(), getBX());
|
|
|
|
setAX(Result == XMS_STATUS_SUCCESS);
|
|
setBL(Result);
|
|
break;
|
|
}
|
|
|
|
/* Request UMB */
|
|
case 0x10:
|
|
{
|
|
BOOLEAN Result;
|
|
USHORT Segment = 0x0000; /* No preferred segment */
|
|
USHORT Size = getDX(); /* Size is in paragraphs */
|
|
|
|
Result = UmaDescReserve(&Segment, &Size);
|
|
if (Result)
|
|
setBX(Segment);
|
|
else
|
|
setBL(Size > 0 ? XMS_STATUS_SMALLER_UMB : XMS_STATUS_OUT_OF_UMBS);
|
|
|
|
setDX(Size);
|
|
setAX(Result);
|
|
break;
|
|
}
|
|
|
|
/* Release UMB */
|
|
case 0x11:
|
|
{
|
|
BOOLEAN Result;
|
|
USHORT Segment = getDX();
|
|
|
|
Result = UmaDescRelease(Segment);
|
|
if (!Result)
|
|
setBL(XMS_STATUS_INVALID_UMB);
|
|
|
|
setAX(Result);
|
|
break;
|
|
}
|
|
|
|
/* Reallocate UMB */
|
|
case 0x12:
|
|
{
|
|
BOOLEAN Result;
|
|
USHORT Segment = getDX();
|
|
USHORT Size = getBX(); /* Size is in paragraphs */
|
|
|
|
Result = UmaDescReallocate(Segment, &Size);
|
|
if (!Result)
|
|
{
|
|
if (Size > 0)
|
|
{
|
|
setBL(XMS_STATUS_SMALLER_UMB);
|
|
setDX(Size);
|
|
}
|
|
else
|
|
{
|
|
setBL(XMS_STATUS_INVALID_UMB);
|
|
}
|
|
}
|
|
|
|
setAX(Result);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPRINT1("XMS command AH = 0x%02X NOT IMPLEMENTED\n", getAH());
|
|
setBL(XMS_STATUS_NOT_IMPLEMENTED);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
BOOLEAN XmsGetDriverEntry(PDWORD Pointer)
|
|
{
|
|
if (Node == NULL) return FALSE;
|
|
*Pointer = DEVICE_PRIVATE_AREA(Node->Driver);
|
|
return TRUE;
|
|
}
|
|
|
|
VOID XmsInitialize(VOID)
|
|
{
|
|
RtlZeroMemory(HandleTable, sizeof(HandleTable));
|
|
RtlZeroMemory(BitmapBuffer, sizeof(BitmapBuffer));
|
|
RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, XMS_BLOCKS);
|
|
|
|
Node = DosCreateDeviceEx(DOS_DEVATTR_IOCTL | DOS_DEVATTR_CHARACTER,
|
|
XMS_DEVICE_NAME,
|
|
sizeof(EntryProcedure));
|
|
|
|
RegisterBop(BOP_XMS, 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(BOP_XMS, NULL);
|
|
DosDeleteDevice(Node);
|
|
}
|