Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
static UCHAR XmsUnlock(WORD Handle)
{
    PXMS_HANDLE HandleEntry = GetHandleRecord(Handle);

    if (!ValidateHandle(HandleEntry))
        return XMS_STATUS_INVALID_HANDLE;

    if (!HandleEntry->LockCount)
        return XMS_STATUS_NOT_LOCKED;

    /* Decrement the lock count */
    HandleEntry->LockCount--;

    return XMS_STATUS_SUCCESS;
}
Beispiel #4
0
static UCHAR XmsLock(WORD Handle, PDWORD Address)
{
    PXMS_HANDLE HandleEntry = GetHandleRecord(Handle);

    if (!ValidateHandle(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;
}
Beispiel #5
0
static UCHAR XmsFree(WORD Handle)
{
    DWORD BlockNumber;
    PXMS_HANDLE HandleEntry = GetHandleRecord(Handle);

    if (!ValidateHandle(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;
}
Beispiel #6
0
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 implicitely 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 = GetHandleRecord(CopyData->SourceHandle);
                if (!ValidateHandle(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 = GetHandleRecord(CopyData->DestHandle);
                if (!ValidateHandle(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 = GetHandleRecord(getDX());
            UINT i;
            UCHAR Handles = 0;

            if (!ValidateHandle(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);
        }
    }
}
Beispiel #7
0
static UCHAR XmsRealloc(WORD Handle, WORD NewSize)
{
    DWORD BlockNumber;
    PXMS_HANDLE HandleEntry = GetHandleRecord(Handle);
    DWORD CurrentIndex = 0;
    ULONG RunStart;
    ULONG RunSize;

    if (!ValidateHandle(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;
}
Beispiel #8
0
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;
        }