(* * * * * * * * * * * * * * * * * * *
 *
 * vmwire.pas -- Wire and unwire VM pages
 *
 *      Defines routines for wiring and unwiring VM pages.
 * wiring a page makes it accessable to a program. Unwiring
 * indicates that the page is will not be needed until it is
 * wired again.
 *
 * * * * * * * * * * * * * * * * * * *)

function VM_VmBlk.wire(areaOffset: DWord;
            areaSize: DWord;
            var areaAddress: Pointer): Boolean;

label
    100;

var

    startPage:          Word;
    endPage:            Word;
    pageNum:            Word;
    offset:             Word;

    blockBuffer:        VM_ConvBuffPtr;

    vmBlock:            VM_VmBlockPtr;
    vmPage:             VM_VmPagePtr;

    error:              Integer;
    contiguous:         Boolean;
    pageWired:          Boolean;
    resident:           Boolean;

    tmpEmsPage:         VM_EmsPagePtr;

begin 

    (*
     *  Start by casting the handle into an appropriate pointer.
     *)
    vmBlock:= VM_VmBlockPtr(handle);

    (*
     *  Let's translate the offset and size into page
     *  values.
     *  The offset is truncated to a page number, and the
     *  size is rounded up to a page number.
     *)
    startPage:= Word(areaOffset shr VM_PAGE_SHIFT);
    endPage:= Word((areaOffset + areaSize - 1) shr VM_PAGE_SHIFT);
    offset:= Word(areaOffset and VM_PAGE_OFFSET_MASK);

    (*
     *  Make sure the pages are within the bounds of the block:
     *)
    if endPage >= vmBlock^.size then begin
        errno:= VMErrBounds;
        wire:= True;
        end;

    (*
     *  We need to see whether the pages are wired or resident already.
     *  If there is an already wired page in the set, it cannot be moved.
     *  Otherwise, we would invalidate the address returned to the caller
     *  who wired it. Since it cannot be moved, we must have room in its
     *  buffer for all of the other pages that we are loading. If there
     *  is room in the buffer, then the other pages we are mapping will
     *  be there, since we do not replace individual pages in a buffer.
     *  This, though, does not hold true for the PFA, so we make an
     *  exception.
     *)

    (*
     *  blockBuffer will hold the convBuff address for the resident
     *  pages. contiguous will flag whether all of the resident pages
     *  are in the same buffer. If there is a wired page, they must
     *  be.
     *)
    contiguous:= True;
    resident:= True;
    pageWired:= False;
    blockBuffer:= vmBlock^.pages^[startPage].convBuff;
    for pageNum:= startPage to endPage do begin
        vmPage:= @vmBlock^.pages^[pageNum];

        if vmPage^.convBuff = Nil then begin
            resident:= False;
            end;

        if vmPage^.wired <> 0 then begin
            if not contiguous then begin
                goto 100;
                end;
            pageWired:= True;
            end;

        if vmPage^.convBuff <> blockBuffer then begin
            if pageWired then begin
                goto 100;
                end;
            contiguous:= False;
            end;
        end;

    (*
     *  OK, we're good. Either we already have the pages in a
     *  contiguous block, or none of them are wired.
     *  blockBuffer tells us which is the case.
     *)
    if not resident or not contiguous then begin
        (*
         *  Oh well, we have to do some work. I hate it when that happens.
         *
         *  Where to begin, where to begin ?  I know, let's call another
         *  routine to do the mapping. This will give the appearance of
         *  progress.
         *)
        if vm_faultInPages(vmBlock^, startPage, endPage) then begin
            wire:= True;
            Exit;
            end;
        end
    else begin
        (*
         *  We need to move stuff from the LRU queues to
         *  the wired queues.
         *)
        if vmBlock^.pages^[startPage].convBuff^.wiredPages = 0 then begin
            vmBlock^.pages^[startPage].convBuff^.deque(
                            @conv.mruBuff,
                            @conv.lruBuff);
            vmBlock^.pages^[startPage].convBuff^.enqueTail(
                            @conv.firstWired,
                            @conv.lastWired);
            end;

        end;

    (*
     *  Everythings resident. Let's go through, upping the
     *  wire counts, and then return the address of the first page
     *)
    for pageNum:= startPage to endPage do begin
        if (vmBlock^.pages^[pageNum].wired = 0) and
                (vmBlock^.pages^[pageNum].convBuff <> PFABUFF) then begin
            vmBlock^.pages^[pageNum].convBuff^.wiredPages:=
                    vmBlock^.pages^[pageNum].convBuff^.wiredPages + 1;
            end;

        vmBlock^.pages^[pageNum].wired:= vmBlock^.pages^[pageNum].wired + 1;

        vm_moveSecPageToWired(vmBlock^.pages^[pageNum]);
        end;

    (*
     *  We're set, let's return the address.
     *)
    vmPage:= @vmBlock^.pages^[startPage];
    if vmPage^.convBuff = PFABUFF then begin
        areaAddress:= Pointer(DWord(em.pageFrame)
                            + Word(vmPage^.offset) + offset);
        end
    else begin
        areaAddress:= Pointer(DWord(vmPage^.convBuff^.address) +
                                    vmPage^.offset + offset);
        end;

    errno:= VMErrOK;
    wire:= False;

    Exit;
100:
    errno:= VMErrBadWire;
    wire:= True;    
end;

function vm_faultInPages(var vmBlock: VM_VMBlock;
                    startPage: Word; endPage: Word): Boolean;

var
    toss:               Boolean;

begin 

    (*
     *  Let's promote these pages to EMS or XMS (if possible and
     *  necessary).
     *  This achieves two purposes. First, we get the pages into
     *  EMS if possible so that we can map them into the PFA, which
     *  is our fastest mapping method. Second, if the pages are on
     *  disk, they are promoted to EMS or XMS so that we maintain
     *  the most recently used pages in EMS or XMS as opposed to disk.
     *
     *  We'll try EMS, if that fails, for example, because the system
     *  has no EMS, we'll try XMS. If that fails, then OK, we'll get
     *  the pages from disk. The only real reason for vm_faultToDisk
     *  is to handle the possibility that the pages are unallocated.
     *)
    if vm_faultToEMS(vmBlock, startPage, endPage) then begin
        if vm_faultToXMS(vmBlock, startPage, endPage) then begin
            toss:= vm_faultToDisk(vmBlock, startPage, endPage);
            end;
        end;

    (*
     *  OK, the pages are in the best secondary memory we have.
     *  Let's see if we can map them into the EMS PFA
     *)
    if not vm_tryMapPFA(vmBlock, startPage, endPage) then begin
        errno:= VMErrOK;
        vm_faultInPages:= False;
        Exit;
        end;

    (*
     *  That didn't work. Let's put it into a normal buffer
     *)
    if not vm_tryMapConv(vmBlock, startPage, endPage) then begin
        errno:= VMErrOK;
        vm_faultInPages:= False;
        Exit;
        end;

    (*
     *  That didn't work either. We're out of luck.
     *)
    vm_faultInPages:= True;

end;


function vm_faultToEMS(var vmBlock: VM_VMBlock;
                    startPage: Word; endPage: Word): Boolean;

label
    100;

var

    emsPage:            VM_EmsPagePtr;
    nextEmsPage:        VM_EmsPagePtr;
    emsPageChain:       VM_EmsPagePtr;

    vmPage:             VM_VmPagePtr;

    pageNum:            Word;
    nonEMSPages:        Integer;

begin 

    if not emsPresent then begin
        (*
         *  We don't even have EMS. Let's give an error
         *)
        vm_faultToEMS:= True;
        Exit;
        end;


    (*
     *  First go through and see how many pages are not in EMS.
     *  Pages that _are_ in EMS must be put onto the wired queue,
     *  lest they be booted out to make way for other pages in this wire.
     *)
    nonEMSPages:= 0;
    for pageNum:= startPage to endPage do begin
        if vmBlock.pages^[pageNum].secondaryKind < VM_SEC_EMS then begin
            nonEMSPages:= nonEMSPages + 1;
            end
        else begin
            vm_moveSecPageToWired(vmBlock.pages^[pageNum]);
            end;
        end;

    (*
     *  Now let's get as many pages as we can. This will include
     *  swapping older pages from EMS to disk.
     *)
    vm_getEMSPages(nonEMSPages, emsPageChain);

    (*
     *  Let's promote as many pages as possible.
     *)
    pageNum:= startPage;
    emsPage:= emsPageChain;
    pageNum:= startPage;
    while pageNum <= endPage do begin

        vmPage:= @vmBlock.pages^[pageNum];

        (*
         *  If this is one of the nonEMS pages, we need to take
         *  an emsPage and promote it.
         *)

        if vmPage^.secondaryKind < VM_SEC_EMS then begin
            (*
             *  Before anything else, let's see if there's a
             *  page left.
             *)

            if emsPage = Nil then begin


                goto 100;
                end;

            (*
             *  Promotion will enque the page, so we need to get
             *  its 'next' pointer now.
             *)
            nextEmsPage:= emsPage^.next;

            vm_promoteToEMS(vmPage^, emsPage^);

            emsPage:= nextEmsPage;
            end;

        pageNum:= pageNum + 1;
        end;

100:
    if pageNum <= endPage then begin
        (*
         *  We weren't able to promote all of the pages. Return
         *  a flag to that effect.
         *)
        vm_faultToEMS:= True;
        Exit;
        end;

    vm_faultToEMS:= False;

end;


function vm_faultToXMS(var vmBlock: VM_VMBlock;
                    startPage: Word; endPage: Word): Boolean;

label
    100;

var
    xmsPage:            VM_XmsPagePtr;
    nextXmsPage:        VM_XmsPagePtr;
    xmsPageChain:       VM_XmsPagePtr;

    vmPage:             VM_VmPagePtr;

    pageNum:            Word;
    nonXMSPages:        Integer;

begin 

    if not xmsPresent then begin
        (*
         *  We don't even have XMS. Let's give an error
         *)
        vm_faultToXMS:= True;
        Exit;
        end;


    (*
     *  First go through and see how many pages are not in XMS
     *)
    nonXMSPages:= 0;
    for pageNum:= startPage to endPage do begin
        if vmBlock.pages^[pageNum].secondaryKind < VM_SEC_XMS then begin
            nonXMSPages:= nonXMSPages + 1;
            end
        else if vmBlock.pages^[pageNum].secondaryKind = VM_SEC_XMS then begin
            vm_moveSecPageToWired(vmBlock.pages^[pageNum]);
            end;
        end;

    (*
     *  Now let's get as many pages as we can. This will include
     *  swapping older pages from XMS to disk.
     *)
    vm_getXMSPages(nonXMSPages, xmsPageChain);

    (*
     *  Let's promote as many pages as possible.
     *)
    pageNum:= startPage;
    xmsPage:= xmsPageChain;
    pageNum:= startPage;
    while pageNum <= endPage do begin

        vmPage:= @vmBlock.pages^[pageNum];

        (*
         *  If this is one of the nonXMS pages, we need to take
         *  an emsPage and promote it.
         *)
        if vmPage^.secondaryKind < VM_SEC_XMS then begin
            (*
             *  Before anything else, let's see if there's a
             *  page left.
             *)
            if xmsPage = Nil then begin
                goto 100;
                end;

            (*
             *  Promotion will enque the page, so we need to get
             *  its 'next' pointer now.
             *)
            nextXmsPage:= xmsPage^.next;

            vm_promoteToXMS(vmPage^, xmsPage^);

            xmsPage:= nextXmsPage;
            end;

        pageNum:= pageNum + 1;
        end;

100:
    if pageNum <= endPage then begin
        (*
         *  We weren't able to promote all of the pages. Return
         *  a flag to that effect.
         *)
        vm_faultToXMS:= True;
        Exit;
        end;

    vm_faultToXMS:= False;

end;

function vm_faultToDisk(var vmBlock: VM_VMBlock;
                    startPage: Word; endPage: Word): Boolean;

label
    100;

var
    diskPage:           VM_DiskPagePtr;
    nextDiskPage:       VM_DiskPagePtr;
    diskPageChain:      VM_DiskPagePtr;

    pageNum:            Word;
    nonDiskPages:       Integer;

begin 

    (*
     *  First go through and see how many pages are not allocated
     *)
    nonDiskPages:= 0;
    for pageNum:= startPage to endPage do begin
        if vmBlock.pages^[pageNum].secondaryKind < VM_SEC_DISK then begin
            nonDiskPages:= nonDiskPages + 1;
            end;
        end;

    (*
     *  Now let's get as many pages as we can. This will include
     *  swapping older pages from Disk to disk.
     *)
    vm_getDiskPages(nonDiskPages, diskPageChain);

    (*
     *  Let's promote as many pages as possible.
     *)
    pageNum:= startPage;
    diskPage:= diskPageChain;
    while diskPage <> Nil do begin
        (*
         *  If this is one of the non Disk pages, we need to take
         *  an diskPage and promote it.
         *)
        if vmBlock.pages^[pageNum].secondaryKind < VM_SEC_DISK then begin
            (*
             *  Promotion will enque the Disk page, so we need to
             *  get the 'next' pointer now.
             *)
            nextDiskPage:= diskPage^.next;

            vm_promoteToDisk(vmBlock.pages^[pageNum], diskPage^);

            diskPage:= nextDiskPage;
            end;

        pageNum:= pageNum + 1;
        end;

    if pageNum <= endPage then begin
        (*
         *  We weren't able to promote all of the pages. Return
         *  a flag to that effect.
         *)
        vm_faultToDisk:= True;
        end;

    vm_faultToDisk:= False;

end;

procedure    vm_getEMSPages(number: Word; var chain: VM_EmsPagePtr);

var

    newEmsPage:         VM_EmsPagePtr;
    tmpEmsPage:         VM_EmsPagePtr;
    newEmsBuff:         VM_EmsBuffPtr;

    freePages:          Word;
    totalPages:         Word;
    requestNum:         Word;
    pageHandle:         EMS_EmBlk;

    tmpFreeArea:        VM_FreeAreaPtr;

begin 

    (*
     *  Start by making the return chain empty.
     *)
    chain:= Nil;

    (*
     *  Now try to get pages as we can up to the number requested.
     *)
    while number <> 0 do begin

        if em.firstFree <> Nil then begin
            (*
             *  We've got some free pages.
             *  Create an EmsPage and fill it in.
             *)
            New(newEmsPage);
            newEmsPage^.emsBuff:= VM_EmsBuffPtr(em.firstFree^.handle);
            newEmsPage^.pageNum:= Word(em.firstFree^.start);
            newEmsPage^.secondaryQueue:= VM_Q_FREE;

            (*
             *  Bump the buffer's use count.
             *)
            newEmsPage^.emsBuff^.useCount:= newEmsPage^.emsBuff^.useCount + 1;

            (*
             *  Remove the page from the free chain.
             *)
            em.firstFree^.size:= em.firstFree^.size - 1;
            em.firstFree^.start:= em.firstFree^.start + 1;
            if em.firstFree^.size = 0 then begin
                tmpFreeArea:= em.firstFree;
                tmpFreeArea^.deque(@em.firstFree, @em.lastFree);
                Dispose(tmpFreeArea);
                end;

            (*
             *  Add our new page to the return list.
             *)
            newEmsPage^.next:= chain;
            chain:= newEmsPage;

            (*
             *  We've added a page. Decrement 'number';
             *)
            number:= number - 1;
            end
        else begin
            (*
             *  Nothing on the free chain. Look into EMS from the
             *  EMS manager.
             *)
            if ems.getFreeEM(totalPages, freePages) then begin
                ems_demoError('EMS_Ems.getNumPages');
                end;

            if freePages <> 0 then begin
                (*
                 *  We've got some EMS left.
                 *  Allocate a block of EMS. We've computed this size
                 *  so that we can use all of EMS with the available
                 *  handles.
                 *)
                requestNum:= min(em.emsBlockSize, freePages);
                if pageHandle.allocEM(requestNum) then begin
                    ems_demoError('EMS_Ems.allocEM');
                    end;
                New(newEmsBuff);
                newEmsBuff^.buffSize:= requestNum;
                newEmsBuff^.handle:= pageHandle;
                newEmsBuff^.useCount:= 0;
                newEmsBuff^.enqueHead(@em.firstBuff, @em.lastBuff);

                (*
                 *  Put the pages on the free chain.
                 *)
                vm_addFree(DWord(newEmsBuff), 0,
                                requestNum, em.firstFree, em.lastFree);

                (*
                 *  We haven't actually returned a page, so
                 *  we don't decrement 'number'
                 *)
                end
            else begin

                (*
                 *  We've got no EMS. Let's start throwing stuff out.
                 *)
                if em.lruPage <> Nil then begin

                    (*
                     *  Pull the least recently used page.
                     *)
                    tmpEmsPage:= em.lruPage;
                    tmpEmsPage^.deque(@em.mruPage, @em.lruPage);
                    tmpEmsPage^.secondaryQueue:= VM_Q_FREE;

                    (*
                     *  Demote it to XMS or disk.
                     *)
                    vm_demoteFromEMS(tmpEmsPage^);

                    (*
                     *  Enque the newly available page to the return chain.
                     *)
                    tmpEmsPage^.next:= chain;
                    chain:= tmpEmsPage;

                    number:= number - 1;
                    end
                else begin

                    (*
                     *  We're totally out of EMS. Let's just return.
                     *  We've done the best we could.
                     *)
                    Exit;
                    end;
                end;
            end;
        end;

end;

procedure vm_getXMSPages(number: Word; var chain: VM_XmsPagePtr);

var

    newXmsPage:         VM_XmsPagePtr;
    tmpXmsPage:         VM_XmsPagePtr;
    newXmsBuff:         VM_XmsBuffPtr;

    freePages:          Word;
    contiguousPages:    Word;
    requestNum:         Word;
    pageHandle:         XMS_XmBlk;

    tmpFreeArea:        VM_FreeAreaPtr;

begin 

    (*
     *  Start by making the return chain empty.
     *)
    chain:= Nil;

    (*
     *  Now try to get pages as we can up to the number requested.
     *)
    while number <> 0 do begin
        if xm.firstFree <> Nil then begin
            (*
             *  We've got some free pages.
             *  Create an XmsPage and fill it in.
             *)
            New(newXmsPage);
            newXmsPage^.xmsBuff:= VM_XmsBuffPtr(xm.firstFree^.handle);
            newXmsPage^.pageNum:= Word(xm.firstFree^.start);
            newXmsPage^.secondaryQueue:= VM_Q_FREE;

            (*
             *  Bump the buffer's use count.
             *)
            newXmsPage^.xmsBuff^.useCount:= newXmsPage^.xmsBuff^.useCount + 1;

            (*
             *  Remove the page from the free chain.
             *)
            xm.firstFree^.size:= xm.firstFree^.size - 1;
            xm.firstFree^.start:= xm.firstFree^.start + 1;
            if xm.firstFree^.size = 0 then begin
                tmpFreeArea:= xm.firstFree;
                tmpFreeArea^.deque(@xm.firstFree, @xm.lastFree);
                Dispose(tmpFreeArea);
                end;

            (*
             *  Add our new page to the return list.
             *)
            newXmsPage^.next:= chain;
            chain:= newXmsPage;

            (*
             *  We've added a page. Decrement 'number';
             *)
            number:= number - 1;
            end
        else begin
            (*
             *  Nothing on the free chain. Look into XMS from the
             *  XMS manager.
             *)
            if xms.getFreeXM(freePages, contiguousPages) then begin
                if errno = XMSErrNoXMLeft then begin
                    contiguousPages:= 0;
                    end
                else begin
                    xms_demoError('XMS_Xms.getFreeXM');
                    end;
                end;
            contiguousPages:= contiguousPages
                        div (VM_PAGE_SIZE div XMS_PAGE_SIZE);

            if contiguousPages <> 0 then begin
                (*
                 *  We've got some XMS left.
                 *  Try to allocate as much as we can to satisfy
                 *  our needs.
                 *)
                requestNum:= min(xm.xmsBlockSize, contiguousPages);
                if pageHandle.allocXM(requestNum * 
                            (VM_PAGE_SIZE div XMS_PAGE_SIZE)) then begin
                    xms_demoError('xms_allocXM');
                    end;

                New(newXmsBuff);
                newXmsBuff^.buffSize:= requestNum;
                newXmsBuff^.handle:= pageHandle;
                newXmsBuff^.useCount:= 0;
                newXmsBuff^.enqueHead(@xm.firstBuff, @xm.lastBuff);

                (*
                 *  Put the pages on the free chain.
                 *)
                vm_addFree(DWord(newXmsBuff), 0,
                                requestNum, xm.firstFree, xm.lastFree);

                (*
                 *  We haven't actually returned a page, so
                 *  we don't decrement 'number'
                 *)
                end
            else begin
                (*
                 *  We've got no XMS. Let's start throwing stuff out.
                 *)
                if xm.lruPage <> Nil then begin
                    (*
                     *  Pull the least recently used page.
                     *)
                    tmpXmsPage:= xm.lruPage;
                    tmpXmsPage^.deque(@xm.mruPage, @xm.lruPage);
                    tmpXmsPage^.secondaryQueue:= VM_Q_FREE;

                    (*
                     *  Demote it to disk.
                     *)
                    vm_demoteFromXMS(tmpXmsPage^);

                    (*
                     *  Enque the newly available page to the return chain.
                     *)
                    tmpXmsPage^.next:= chain;
                    chain:= tmpXmsPage;

                    number:= number - 1;
                    end
                else begin
                    (*
                     *  We're totally out of XMS. Let's just return.
                     *  We've done the best we could.
                     *)
                    Exit;
                    end;
                end;
            end;
        end;

end;

procedure vm_getDiskPages(number: Word; var chain: VM_DiskPagePtr);

var
    newDiskPage:        VM_DiskPagePtr;
    lastDiskPage:       VM_DiskPagePtr;

    tmpFreeArea:        VM_FreeAreaPtr;

begin 

    (*
     *  Start by making the return chain empty.
     *)
    chain:= Nil;
    lastDiskPage:= Nil;

    (*
     *  Now try to get pages as we can up to the number requested.
     *  We enque pages in order rather than in reverse order as in
     *  the getXMS and getEMS functions because we want to write
     *  pages in order to minimize disk access if we're extending EOF.
     *)
    while number <> 0 do begin
        if disk.firstFree <> Nil then begin
            (*
             *  We've got some free pages.
             *  Create an DiskPage and fill it in.
             *)
            New(newDiskPage);
            newDiskPage^.pageNum:= Word(disk.firstFree^.start);

            (*
             *  Remove the page from the free chain.
             *)
            disk.firstFree^.size:= disk.firstFree^.size - 1;
            disk.firstFree^.start:= disk.firstFree^.start + 1;
            if disk.firstFree^.size = 0 then begin
                tmpFreeArea:= disk.firstFree;
                tmpFreeArea^.deque(@disk.firstFree, @disk.lastFree);
                Dispose(tmpFreeArea);
                end;

            (*
             *  Add our new page to the return list.
             *)
            newDiskPage^.next:= Nil;
            if lastDiskPage <> Nil then begin
                lastDiskPage^.next:= newDiskPage;
                end
            else begin
                chain:= newDiskPage;
                end;
            lastDiskPage:= newDiskPage;

            (*
             *  We've added a page. Decrement 'number';
             *)
            number:= number - 1;
            end
        else begin
            (*
             *  Put an appropriate number of pages on the free chain
             *  starting at the current end of file.
             *)
            vm_addFree(0, disk.fileSize,
                                number, disk.firstFree, disk.lastFree);

            (*
             *  Advance the EOF marker.
             *)
            disk.fileSize:= disk.fileSize + number;

            (*
             *  We haven't actually returned a page, so
             *  we don't decrement 'number'
             *)
            end;
        end;

end;

procedure vm_promoteToEMS(var vmPage: VM_VmPage; var emsPage: VM_EmsPage);

var
    xmsMovePk:          XMS_MovePacket;

begin 

    (*
     *  First off, let's see if the page is already in EMS. If so
     *  we're done.
     *)
    if vmPage.secondaryKind = VM_SEC_EMS then begin
        Exit;
        end;

    (*
     *  We need to save the page map (pages in PFA), map the page, and then
     *  xfer the data to the mapped page, then restore the page map.
     *)

    (*
     *  Save the current state of the PFA
     *)
    if emsPage.emsBuff^.handle.savePageMap then begin
        ems_demoError('EMS_Ems.savePageMap');
        end;

    (*
     *  Map the EMS page into the first page of the PFA
     *)
    if emsPage.emsBuff^.handle.mapPage(0, emsPage.pageNum) then begin
        ems_demoError('EMS_EmBlk.mapPage');
        end;

    (*
     *  Let's do the xfer:
     *)
    case vmPage.secondaryKind of
    VM_SEC_UNALLOCATED:
        (*
         *  The page is as yet unallocated. Zero it out.
         *)
        memset(@em.pageFrame^[0], 0, VM_PAGE_SIZE);

    VM_SEC_DISK: begin
        (*
         *  The page is on disk. Read it in.
         *)
        Seek(disk.channel, DWord(vmPage.sec.disk^.pageNum));
        Read(disk.channel, VM_Page(em.pageFrame^[0]));

        (*
         *  Free the disk page.
         *)
        vm_addFree(0, vmPage.sec.disk^.pageNum, 1,
                        disk.firstFree, disk.lastFree);

        (*
         *  Release the disk page descriptor.
         *)
        vm_dequeSecPage(vmPage);
        Dispose(vmPage.sec.disk);
        end;

    VM_SEC_XMS: begin
        (*
         *  The page is in XMS. Let's xfer it in.
         *)
        xmsMovePk.length:= VM_PAGE_SIZE;
        xmsMovePk.srcHandle:= vmPage.sec.xms^.xmsBuff^.handle.handle;
        xmsMovePk.srcOffset:= DWord(vmPage.sec.xms^.pageNum) * VM_PAGE_SIZE;
        xmsMovePk.destHandle:= 0;
        xmsMovePk.destOffset:= DWord(@em.pageFrame^[0]);
        if xms.moveXM(xmsMovePk) then begin
            xms_demoError('XMS_Xms.moveXM');
            end;

        (*
         *  Free the xms page.
         *)
        vm_addFree(DWord(vmPage.sec.xms^.xmsBuff),
                        vmPage.sec.xms^.pageNum, 1,
                        xm.firstFree, xm.lastFree);

        (*
         *  Release the xms page descriptor.
         *)
        vm_dequeSecPage(vmPage);
        Dispose(vmPage.sec.xms);
        end;

    else
        vm_fatal('Bogus vmPage.secondaryKind');
        end;

    (*
     *  Set up the EMS page descriptor.
     *)
    emsPage.vmPage:= @vmPage;
    vmPage.secondaryKind:= VM_SEC_EMS;
    vmPage.sec.ems:= @emsPage;

    (*
     *  Let's just restore the PFA map.
     *)
    if emsPage.emsBuff^.handle.restorePageMap then begin
        ems_demoError('EMS_Ems.restorePageMap');
        end;


end;

procedure vm_promoteToXMS(var vmPage: VM_VmPage; var xmsPage: VM_XmsPage);

var

    pageBuff:           VM_PagePtr;
    xmsMovePk:          XMS_MovePacket;

begin 

    (*
     *  If EMS is initialized, we have a page of EMS to use as
     *  a buffer. Otherwise we need to use conventional memory.
     *)
    if emsPresent and (em.pageBuffHandle.handle <> 0) then begin
        (*
         *  Save the current page frame map.
         *)
        if em.pageBuffHandle.savePageMap then begin
            ems_demoError('EMS_EmBlk.savePageMap');
            end;

        (*
         *  Map the EMS page into the first page of the PFA
         *)
        if em.pageBuffHandle.mapPage(0, 0) then begin
            ems_demoError('EMS_EmBlk.mapPage');
            end;

        pageBuff:= @em.pageFrame^[0];
        end
    else begin
        (*
         *  No EMS, we need to use conventional memory.
         *)
        New(pageBuff);
        end;

    (*
     *  Let's do the xfer:
     *)
    case vmPage.secondaryKind of
    VM_SEC_UNALLOCATED: begin
        (*
         *  The page is as yet unallocated. Zero it out.
         *)
        memset(pageBuff, 0, VM_PAGE_SIZE);
        end;

    VM_SEC_DISK: begin
        (*
         *  The page is on disk. Read it in.
         *)
        Seek(disk.channel, vmPage.sec.disk^.pageNum);
        Read(disk.channel, pageBuff^);

        (*
         *  Free the disk page.
         *)
        vm_addFree(0, vmPage.sec.disk^.pageNum, 1,
                        disk.firstFree, disk.lastFree);

        (*
         *  Release the disk page descriptor.
         *)
        vm_dequeSecPage(vmPage);
        Dispose(vmPage.sec.disk);

        end;

    else
        vm_fatal('Bogus vmPage.secondaryKind');
        end;

    (*
     *  We have the page in memory. Let's transfer it to
     *  XMS now.
     *)
    xmsMovePk.length:= VM_PAGE_SIZE;
    xmsMovePk.srcHandle:= 0;
    xmsMovePk.srcOffset:= DWord(pageBuff);
    xmsMovePk.destHandle:= xmsPage.xmsBuff^.handle.handle;
    xmsMovePk.destOffset:= DWord(xmsPage.pageNum) * VM_PAGE_SIZE;
    if xms.moveXM(xmsMovePk) then begin
        xms_demoError('XMS_Xms.moveXM');
        end;

    (*
     *  Set up the XMS page descriptor.
     *)
    xmsPage.vmPage:= @vmPage;
    vmPage.secondaryKind:= VM_SEC_XMS;
    vmPage.sec.xms:= @xmsPage;

    (*
     *  Now release the buffer
     *)
    if emsPresent and (em.pageBuffHandle.handle <> 0) then begin
        (*
         *  Let's just restore the PFA map.
         *)
        if em.pageBuffHandle.restorePageMap then begin
            ems_demoError('EMS_EmBlk.restorePageMap');
            end;
        end
     else begin
        (*
         *  Free the conventional memory buffer
         *)
        Dispose(pageBuff);
        end;


end;

procedure vm_promoteToDisk(var vmPage: VM_VmPage; var diskPage: VM_DiskPage);

var
    pageBuff:           VM_PagePtr;

begin 

    (*
     *  If EMS is initialized, we have a page of EMS to use as
     *  a buffer. Otherwise we need to use conventional memory.
     *)
    if emsPresent and (em.pageBuffHandle.handle <> 0) then begin
        (*
         *  Save the current page frame map.
         *)
        if em.pageBuffHandle.savePageMap then begin
            ems_demoError('EMS_EmBlk.savePageMap');
            end;

        (*
         *  Map the EMS page into the first page of the PFA
         *)
        if em.pageBuffHandle.mapPage(0, 0) then begin
            ems_demoError('EMS_EmBlk.mapPage');
            end;

        pageBuff:= @em.pageFrame^[0];
        end
    else begin
        (*
         *  No EMS, we need to use conventional memory.
         *)
        New(pageBuff);
        end;

    (*
     *  Let's do the xfer:
     *)
    case vmPage.secondaryKind of
    VM_SEC_UNALLOCATED: begin
        (*
         *  The page is as yet unallocated. Zero it out.
         *)
        memset(pageBuff, 0, VM_PAGE_SIZE);
        end;

    else
        vm_fatal('Bogus vmPage.secondaryKind');
        end;

    (*
     *  Let's write the thing to disk now.
     *)
    Seek(disk.channel, diskPage.pageNum);
    Write(disk.channel, pageBuff^);

    (*
     *  Set up the Disk page descriptor.
     *)
    diskPage.vmPage:= @vmPage;
    vmPage.secondaryKind:= VM_SEC_DISK;
    vmPage.sec.disk:= @diskPage;

    (*
     *  Enque to the list of pages
     *)
    diskPage.enqueTail(@disk.firstPage, @disk.lastPage);

    (*
     *  Now release the buffer
     *)
    if emsPresent and (em.pageBuffHandle.handle <> 0) then begin
        (*
         *  Let's just restore the PFA map.
         *)
        if em.pageBuffHandle.restorePageMap then begin
            ems_demoError('EMS_EmBlk.restorePageMap');
            end;
        end
     else begin
        (*
         *  Free the conventional memory buffer
         *)
        Dispose(pageBuff);
        end;


end;

procedure vm_demoteFromEMS(var emsPage: VM_EmsPage);

var
    vmPage:             VM_VmPagePtr;
    xmsPage:            VM_XmsPagePtr;
    diskPage:           VM_DiskPagePtr;

    pageType:           VM_SecondaryKind;
    xmsMovePk:          XMS_MovePacket;

begin 

    (*
     *  Just get a handy pointer to the VM page
     *)
    vmPage:= emsPage.vmPage;

    (*
     *  This page could possibly be in the PFA. If so we need to
     *  remove it.
     *)
    if vmPage^.convBuff = PFABUFF then begin
        (*
         *  OK. It's in the PFA. It's OK to leave the EMS page there,
         *  because we're going to need it anyway. However, we want
         *  to indicate that the virtual page is no longer there, since
         *  it is being disconnected from the EMS page.
         *)
        vmPage^.convBuff:= Nil;
        vmPage^.offset:= 0;
        end;


    (*
     *  The first thing we need to do is to try to find a
     *  page to go to. We first try XMS, if that fails, we
     *  go to Disk.
     *)
    vm_getXMSPages(1, xmsPage);
    pageType:= VM_SEC_XMS;
    if xmsPage = Nil then begin
        vm_getDiskPages(1, diskPage);
        pageType:= VM_SEC_DISK;
        end;


    (*
     *  We need to map the page. We'll save the current page map
     *  so we can restore it later.
     *)

    (*
     *  Save the current page frame map.
     *)
    if emsPage.emsBuff^.handle.savePageMap then begin
        ems_demoError('EMS_EmBlk.savePageMap');
        end;

    (*
     *  Map the EMS page into the first page of the PFA
     *)
    if emsPage.emsBuff^.handle.mapPage(0, emsPage.pageNum) then begin
        ems_demoError('EMS_EmBlk.mapPage');
        end;

    (*
     *  Let's do the xfer:
     *)
    case pageType of
    VM_SEC_DISK: begin
        (*
         *  We're demoting to disk. Write the page out.
         *)
        Seek(disk.channel, diskPage^.pageNum);
        Write(disk.channel, VM_Page(em.pageFrame^[0]));

        (*
         *  Update the VM page, etc.
         *)
        diskPage^.vmPage:= vmPage;
        vmPage^.secondaryKind:= VM_SEC_DISK;
        vmPage^.sec.disk:= diskPage;

        (*
         *  Enque the disk page. Note that is will be the most
         *  recently used on disk.
         *)
        diskPage^.enqueHead(@disk.firstPage, @disk.lastPage);

        end;

    VM_SEC_XMS: begin
        (*
         *  We're demoting to XMS, let's move it on out.
         *)
        xmsMovePk.length:= VM_PAGE_SIZE;
        xmsMovePk.srcHandle:= 0;
        xmsMovePk.srcOffset:= DWord(@em.pageFrame^[0]);
        xmsMovePk.destHandle:= xmsPage^.xmsBuff^.handle.handle;
        xmsMovePk.destOffset:= DWord(xmsPage^.pageNum) * VM_PAGE_SIZE;
        if xms.moveXM(xmsMovePk) then begin
            xms_demoError('XMS_Xms.moveXM');
            end;

        (*
         *  Update the VM page, etc.
         *)
        xmsPage^.vmPage:= vmPage;
        vmPage^.secondaryKind:= VM_SEC_XMS;
        vmPage^.sec.xms:= xmsPage;

        (*
         *  Enque the XMS page. Note that is will be the most
         *  recently used on XMS.
         *)
        xmsPage^.enqueHead(@xm.mruPage, @xm.lruPage);
        xmsPage^.secondaryQueue:= VM_Q_LRU;

        end;

    else
        vm_fatal('Bogus vmPage.secondaryKind');
        end;

    (*
     *  Let's just restore the PFA map.
     *)
    if emsPage.emsBuff^.handle.restorePageMap then begin
        ems_demoError('EMS_EmBlk.savePageMap');
        end;


end;


procedure vm_demoteFromXMS(var xmsPage: VM_XmsPage);

var
    vmPage:             VM_VmPagePtr;
    diskPage:           VM_DiskPagePtr;

    xmsMovePk:          XMS_MovePacket;

    pageBuff:           VM_PagePtr;

begin 

    (*
     *  Just get a handy pointer to the VM page
     *)
    vmPage:= xmsPage.vmPage;

    (*
     *  The first thing we need to do is to try to find a
     *  page to go to.
     *)
    vm_getDiskPages(1, diskPage);


    (*
     *  If EMS is initialized, we have a page of EMS to use as
     *  a buffer. Otherwise we need to use conventional memory.
     *)
    if emsPresent and (em.pageBuffHandle.handle <> 0) then begin
        (*
         *  Save the current page frame map.
         *)
        if em.pageBuffHandle.savePageMap then begin
            ems_demoError('EMS_EmBlk.savePageMap');
            end;

        (*
         *  Map the EMS page into the first page of the PFA
         *)
        if em.pageBuffHandle.mapPage(0, 0) then begin
            ems_demoError('EMS_EmBlk.mapPage');
            end;

        pageBuff:= @em.pageFrame^[0];
        end
    else begin
        (*
         *  No EMS, we need to use conventional memory.
         *)
        New(pageBuff);
        end;

    (*
     *  First let's transfer from the XMS page to the
     *  buffer.
     *)
    xmsMovePk.length:= VM_PAGE_SIZE;
    xmsMovePk.srcHandle:= xmsPage.xmsBuff^.handle.handle;
    xmsMovePk.srcOffset:= DWord(xmsPage.pageNum) * VM_PAGE_SIZE;
    xmsMovePk.destHandle:= 0;
    xmsMovePk.destOffset:= DWord(pageBuff);
    if xms.moveXM(xmsMovePk) then begin
        xms_demoError('XMS_Xms.moveXM');
        end;

    (*
     *  Now transfer the page to disk
     *)
    Seek(disk.channel, diskPage^.pageNum);
    Write(disk.channel, pageBuff^);

    (*
     *  Update the VM page, etc.
     *)
    diskPage^.vmPage:= vmPage;
    vmPage^.secondaryKind:= VM_SEC_DISK;
    vmPage^.sec.disk:= diskPage;

    (*
     *  Enque the disk page. Note that is will be the most
     *  recently used on disk.
     *)
    diskPage^.enqueHead(@disk.firstPage, @disk.lastPage);


    (*
     *  Now release the buffer
     *)
    if emsPresent and (em.pageBuffHandle.handle <> 0) then begin
        (*
         *  Let's just restore the PFA map.
         *)
        if em.pageBuffHandle.restorePageMap then begin
            ems_demoError('EMS_EmBlk.restorePageMap');
            end;
        end
     else begin
        (*
         *  Free the conventional memory buffer
         *)
        Dispose(pageBuff);
        end;


end;

function vm_tryMapPFA(
        var vmBlock:    VM_VmBlock;
        startPage:      Word;
        endPage:        Word)
        : Boolean;

label
    100;

var
    emsPage:            VM_EmsPagePtr;

    pageCount:          Word;
    pageNum:            Word;

    freeString:         Word;
    offset:             Word;
    i:                  Word;

begin 
    (*
     *  First, let's make a couple of quick checks:
     *      Can the pages fit in the PFA ?
     *      Are the page all in EMS ?
     *)
    pageCount:= endPage - startPage + 1;
    if pageCount > EMS_PAGE_FRAME_SIZE then begin
        
        errno:= VMErrNoConv;
        vm_tryMapPFA:= True;
        Exit;
        end;

    for pageNum:= startPage to endPage do begin
        if vmBlock.pages^[pageNum].secondaryKind <> VM_SEC_EMS then begin
            
            errno:= VMErrNoConv;
            vm_tryMapPFA:= True;
            Exit;
            end;
        end;

    (*
     *  OK, the initial checks have passed. Let's see whether we
     *  actually have a block of pages in the PFA which we can use.
     *
     *  Note that we do not concern ourselves with lru-ness. Mapping
     *  pages is too fast to worry about it, since there is no
     *  data movement.
     *)
    freeString:= 0;
    offset:= 0;
    while offset < EMS_PAGE_FRAME_SIZE do begin
        if (em.contents[offset] = Nil) or
                (em.contents[offset]^.vmPage^.wired = 0) then begin
            freeString:= freeString + 1;
            end
        else begin
            freeString:= 0;
            end;

        if freeString >= pageCount then begin
            offset:= offset - freeString + 1;
            goto 100;
            end;

        offset:= offset + 1;
        end;

100:
    (*
     *  If offset isn't in the PFA, we can't do anything.
     *)
    if offset >= EMS_PAGE_FRAME_SIZE then begin
        
        errno:= VMErrNoConv;
        vm_tryMapPFA:= False;
        Exit;
        end;

    (*
     *  Now we need to take any pages out of conventional memory
     *  buffers and flush them back to EMS.
     *)
    for pageNum:= startPage to endPage do begin
        vm_flushVmPage(vmBlock.pages^[pageNum]);
        end;

    (*
     *  Now let's mark the pages we're replacing as history.
     *)
    pageNum:= offset;
    for i:= 0 to pageCount-1 do begin
        if em.contents[pageNum] <> Nil then begin
            em.contents[pageNum]^.vmPage^.convBuff:= Nil;
            em.contents[pageNum]^.vmPage^.offset:= 0;
            end;

        pageNum:= pageNum + 1;
        end;

    (*
     *  We're finally ready to map the pages.
     *)
    for pageNum:= startPage to endPage do begin
        emsPage:= vmBlock.pages^[pageNum].sec.ems;
        if emsPage^.emsBuff^.handle.mapPage(
                        offset+(pageNum-startPage),
                        emsPage^.pageNum) then begin
            ems_demoError('EMS_EmBlk.mapPage');
            end;

        (*
         *  Note where we've mapped them.
         *)
        em.contents[offset+(pageNum-startPage)]:= emsPage;
        emsPage^.vmPage^.convBuff:= PFABUFF;
        emsPage^.vmPage^.offset:= (offset+(pageNum-startPage)) * VM_PAGE_SIZE;
        end;

    errno:= VMErrOK;
    vm_tryMapPFA:= False;


end;

function vm_tryMapConv(
    var vmBlock:        VM_VmBlock;
    startPage:          Word;
    endPage:            Word): Boolean;

var

    newConvBuff:        VM_ConvBuffPtr;

    pageNum:            Word;
    pageCount:          Word;
    memNeeded:          DWord;

    newBuffSpace:       Pointer;

begin 

    (*
     *  Get the number of pages.
     *)
    pageCount:= endPage - startPage + 1;

    (*
     *  Free up buffers till we can get the buffer space.
     *)
    memNeeded:= DWord(pageCount) * VM_PAGE_SIZE;
    while memNeeded > conv.spaceAvail do begin
        if vm_freeLRUConvBuff then begin
            errno:= VMErrNoConv;
            vm_tryMapConv:= True;
            Exit;
            end;
        end;
    GetBigMem(newBuffSpace, memNeeded);
    conv.spaceAvail:= conv.spaceAvail - memNeeded;

    (*
     *  We've got memory for the buffer. Let's create a buffer header.
     *)
    New(newConvBuff);
    newConvBuff^.buffSize:= pageCount;
    newConvBuff^.address:= newBuffSpace;
    newConvBuff^.vmBlock:= @vmBlock;
    newConvBuff^.startPage:= startPage;
    newConvBuff^.wiredPages:= 0;
    newConvBuff^.enqueTail(@conv.firstWired, @conv.lastWired);

    (*
     *  OK, we've got a buffer. Let's bring in the pages.
     *)
    for pageNum:= startPage to endPage do begin
        vm_loadVmPage(vmBlock.pages^[pageNum], newConvBuff^);
        end;

    errno:= VMErrOK;
    vm_tryMapConv:= False;
    

end;

procedure vm_flushVmPage(var vmPage: VM_VmPage);

var
    xmsMovePk:          XMS_MovePacket;
    emsPage:            VM_EmsPagePtr;

    pageAddr:           Pointer;

begin 

    (*
     *  If the page is dirty, and resident, write it to secondary
     *)
    if vmPage.dirty and (vmPage.convBuff <> nil) and
                (vmPage.convBuff <> PFABUFF) then begin

        pageAddr:= Pointer(DWord(vmPage.convBuff^.address) + vmPage.offset);

        case vmPage.secondaryKind of
        VM_SEC_UNALLOCATED: begin
            (*
             *  The page is as yet unallocated. This is a NOP
             *)
            end;

        VM_SEC_DISK: begin
            (*
             *  The page is on disk. Write it out
             *)
            Seek(disk.channel, vmPage.sec.disk^.pageNum);
            Write(disk.channel, VM_Page(pageAddr^));
            end;

        VM_SEC_XMS: begin
            (*
             *  The page is in XMS. Let's xfer it out.
             *)
            xmsMovePk.length:= VM_PAGE_SIZE;
            xmsMovePk.srcHandle:= 0;
            xmsMovePk.srcOffset:= DWord(pageAddr);
            xmsMovePk.destHandle:= vmPage.sec.xms^.xmsBuff^.handle.handle;
            xmsMovePk.destOffset:= DWord(vmPage.sec.xms^.pageNum) * VM_PAGE_SIZE;
            if xms.moveXM(xmsMovePk) then begin
                xms_demoError('XMS_Xms.moveXM');
                end;

            end;

        VM_SEC_EMS: begin
            (*
             *  Get and convenient handle on the emsPage.
             *)
            emsPage:= vmPage.sec.ems;

            (*
             *  We need to save the PFA for a bit.
             *)
            if emsPage^.emsBuff^.handle.savePageMap then begin
                ems_demoError('EMS_EmBlk.savePageMap');
                end;

            (*
             *  Map the EMS page in.
             *)
            if emsPage^.emsBuff^.handle.mapPage(0, emsPage^.pageNum) then begin
                ems_demoError('EMS_EmBlk.mapPage');
                end;

            (*
             *  Copy the memory.
             *)
            VM_Page(em.pageFrame^[0]):= VM_Page(pageAddr^);

            (*
             *  Restore the page map.
             *)
            if emsPage^.emsBuff^.handle.restorePageMap then begin
                ems_demoError('EMS_EmBlk.restorePageMap');
                end;

            end;

        else
            vm_fatal('Bogus vmPage.secondaryKind');
            end;
        end;


end;

procedure vm_loadVmPage(
    var vmPage:     VM_VmPage;
    var convBuff:   VM_ConvBuff);

var

    xmsMovePk:      XMS_MovePacket;
    emsPage:        VM_EmsPagePtr;

    pageAddr:       Pointer;

begin 

    (*
     *  First figure out the address of the buffer.
     *)
    vmPage.convBuff:= @convBuff;
    vmPage.offset:= DWord(vmPage.pageNum - convBuff.startPage) * VM_PAGE_SIZE;
    pageAddr:= Pointer(DWord(vmPage.convBuff^.address) + vmPage.offset);

    case vmPage.secondaryKind of
    VM_SEC_DISK: begin
        (*
         *  The page is on disk. Read it in.
         *)

        Seek(disk.channel, vmPage.sec.disk^.pageNum);
        Read(disk.channel, VM_Page(pageAddr^));
        end;

    VM_SEC_XMS: begin
        (*
         *  The page is in XMS. Let's xfer it in.
         *)
        xmsMovePk.length:= VM_PAGE_SIZE;
        xmsMovePk.srcHandle:= vmPage.sec.xms^.xmsBuff^.handle.handle;
        xmsMovePk.srcOffset:= DWord(vmPage.sec.xms^.pageNum) * VM_PAGE_SIZE;
        xmsMovePk.destHandle:= 0;
        xmsMovePk.destOffset:= DWord(pageAddr);
        if xms.moveXM(xmsMovePk) then begin
            xms_demoError('XMS_Xms.moveXM');
            end;
        end;

    VM_SEC_EMS: begin
        (*
         *  Get and convenient handle on the emsPage.
         *)
        emsPage:= vmPage.sec.ems;

        (*
         *  We need to save the PFA for a bit.
         *)
        if emsPage^.emsBuff^.handle.savePageMap then begin
            ems_demoError('EMS_EmBlk.savePageMap');
            end;

        (*
         *  Map the EMS page in.
         *)
        if emsPage^.emsBuff^.handle.mapPage(0, emsPage^.pageNum) then begin
            ems_demoError('EMS_EmBlk.mapPage');
            end;

        (*
         *  Copy the memory.
         *)

        VM_Page(pageAddr^):= VM_Page(em.pageFrame^[0]);

        (*
         *  Restore the page map.
         *)
        if emsPage^.emsBuff^.handle.restorePageMap then begin
            ems_demoError('EMS_EmBlk.restorePageMap');
            end;

        end;

    else
        vm_fatal('Bogus vmPage.secondaryKind');
        end;

end;


function vm_freeLRUConvBuff: Boolean;

var

    convBuff:           VM_ConvBuffPtr;
    vmBlock:            VM_VmBlockPtr;

    pageNum:            Word;
    i:                  Word;

begin

    (*
     *  First, see if there is an unwired buffer, otherwise we're
     *  out of luck.
     *)
    if conv.lruBuff = Nil then begin
        errno:= VMErrNoConv;
        vm_freeLRUConvBuff:= True;
        Exit;
        end;

    (*
     *  O.K, let's get rid of the LRU buffer.
     *)
    convBuff:= conv.lruBuff;
    convBuff^.deque(@conv.mruBuff, @conv.lruBuff);

    (*
     *  Flush, and mark the virtual pages as gone.
     *)
    vmBlock:= convBuff^.vmBlock;
    pageNum:= convBuff^.startPage;
    for i:= 0 to convBuff^.buffSize-1 do begin
        vm_flushVmPage(vmBlock^.pages^[pageNum]);

        vmBlock^.pages^[pageNum].convBuff:= Nil;
        vmBlock^.pages^[pageNum].offset:= 0;

        pageNum:= pageNum + 1;
        end;

    (*
     *  Free up the memory.
     *)
    FreeBigMem(convBuff^.address, DWord(convBuff^.buffSize) * VM_PAGE_SIZE);

    conv.spaceAvail:= conv.spaceAvail +
            DWord(convBuff^.buffSize) * VM_PAGE_SIZE;
    Dispose(convBuff);

    errno:= VMErrOK;
    vm_freeLRUConvBuff:= False;
end;

function VM_VmBlk.unwire(
    areaOffset:         DWord;
    areaSize:           DWord;
    dirty:              Boolean): Boolean;

var

    startPage:          Word;
    endPage:            Word;
    pageNum:            Word;

    vmBlock:            VM_VmBlockPtr;
    vmPage:             VM_VmPagePtr;

    tmpEmsPage:         VM_EmsPagePtr;

begin

    (*
     *  Start by casting the handle into an appropriate pointer.
     *)
    vmBlock:= VM_VmBlockPtr(handle);

    (*
     *  Let's translate the offset and size into page
     *  values.
     *  The offset is truncated to a page number, and the
     *  size is rounded up to a page number.
     *)
    startPage:= Word(areaOffset shr VM_PAGE_SHIFT);
    endPage:= Word((areaOffset + areaSize - 1) shr VM_PAGE_SHIFT);

    (*
     *  Make sure the pages are within the bounds of the block:
     *)
    if endPage >= vmBlock^.size then begin
        errno:= VMErrBounds;
        unwire:= True;
        Exit;
        end;

    (*
     *  Now let's go through and make sure they're wired.
     *)
    for pageNum:= startPage to endPage do begin
        if not (vmBlock^.pages^[pageNum].wired <> 0) then begin
            errno:= VMErrNotWired;
            unwire:= True;
            Exit;
            end;
        end;

    (*
     *  OK, all of the pages are wired. Let's unwire the pages and
     *  mark them dirty as necessary.
     *  If a buffer becomes totally unwired, we move it to the LRU
     *  queue.
     *)
    for pageNum:= startPage to endPage do begin
        vmPage:= @vmBlock^.pages^[pageNum];

        vmPage^.dirty:= vmPage^.dirty or dirty;

        vmPage^.wired:= vmPage^.wired - 1;
        if (vmPage^.wired = 0) and (vmPage^.convBuff <> PFABUFF) then begin
            vmPage^.convBuff^.wiredPages:= vmPage^.convBuff^.wiredPages - 1;
            if vmPage^.convBuff^.wiredPages = 0 then begin
                (*
                 *  Move the buffer to the LRU.
                 *)
                vmPage^.convBuff^.deque(@conv.firstWired, @conv.lastWired);
                vmPage^.convBuff^.enqueHead(@conv.mruBuff, @conv.lruBuff);
                end;
            end;

        (*
         *  If the page is in EMS or XMS, move it from the wired
         *  queue to the LRU chain.
         *)
        case vmPage^.secondaryKind of
        VM_SEC_EMS: begin
            vmPage^.sec.ems^.deque(@em.firstWired, @em.lastWired);
            vmPage^.sec.ems^.enqueHead(@em.mruPage, @em.lruPage);
            vmPage^.sec.ems^.secondaryQueue:= VM_Q_LRU;
            end;
        VM_SEC_XMS: begin
            vmPage^.sec.xms^.deque(@xm.firstWired, @xm.lastWired);
            vmPage^.sec.xms^.enqueHead(@xm.mruPage, @xm.lruPage);
            vmPage^.sec.xms^.secondaryQueue:= VM_Q_LRU;
            end;
        else
            ;
            end;
        end;

    errno:= VMErrOK;
    unwire:= False;

end;

procedure vm_moveSecPageToWired(
    var vmPage:         VM_VmPage);

begin 

    case vmPage.secondaryKind of
    VM_SEC_EMS: begin
        if vmPage.sec.ems^.secondaryQueue = VM_Q_LRU then begin
            vmPage.sec.ems^.deque(@em.mruPage, @em.lruPage);
            end;
        if vmPage.sec.ems^.secondaryQueue <> VM_Q_WIRED then begin
            vmPage.sec.ems^.enqueTail(@em.firstWired, @em.lastWired);
            end;
        vmPage.sec.ems^.secondaryQueue:= VM_Q_WIRED;
        end;
    VM_SEC_XMS: begin
        if vmPage.sec.xms^.secondaryQueue = VM_Q_LRU then begin
            vmPage.sec.xms^.deque(@xm.mruPage, @xm.lruPage);
            end;
        if vmPage.sec.xms^.secondaryQueue <> VM_Q_WIRED then begin
            vmPage.sec.xms^.enqueTail(@xm.firstWired, @xm.lastWired);
            end;
        vmPage.sec.xms^.secondaryQueue:= VM_Q_WIRED;
        end;
    else
        ;
        end;

end;

procedure vm_dequeSecPage(
    var vmPage:         VM_VmPage);

begin 

    case vmPage.secondaryKind of
    VM_SEC_EMS: begin
        if vmPage.sec.ems^.secondaryQueue = VM_Q_LRU then begin
            vmPage.sec.ems^.deque(@em.mruPage, @em.lruPage);
            end
        else if vmPage.sec.ems^.secondaryQueue = VM_Q_WIRED then begin
            vmPage.sec.ems^.deque(@em.firstWired, @em.lastWired);
            end;
        vmPage.sec.ems^.secondaryQueue:= VM_Q_FREE;
        end;
    VM_SEC_XMS: begin
        if vmPage.sec.xms^.secondaryQueue = VM_Q_LRU then begin
            vmPage.sec.xms^.deque(@xm.mruPage, @xm.lruPage);
            end
        else if vmPage.sec.xms^.secondaryQueue = VM_Q_WIRED then begin
            vmPage.sec.xms^.deque(@xm.firstWired, @xm.lastWired);
            end;
        vmPage.sec.xms^.secondaryQueue:= VM_Q_FREE;
        end;
    VM_SEC_DISK: begin
        vmPage.sec.disk^.deque(@disk.firstPage, @disk.lastPage);
        end;
    else
        ;
        end;

end;



