(* * * * * * * * * * * * * * * * * * *
 *
 *  vminit.pas --     VM initialization
 *
 *      Initialization and termination of VM module
 *
 * * * * * * * * * * * * * * * * * * *)

function VM_Vm.init(maxSpace: DWord) : Boolean;

var
    i:                  Integer;
    numHandles:         Word;
    totalPages:         Word;
    freePages:          Word;
    activeHandles:      Word;
    freeKXM:            Word;
    contiguousKXM:      Word;
    xmsHandle:          XMS_XmBlk;
    blockSize:          Word;
    lockCount:          Word;

begin

    (*
     *  First look into initializing EMS
     *)
    em.mruPage:= Nil;
    em.lruPage:= Nil;
    em.firstWired:= Nil;
    em.lastWired:= Nil;
    em.firstFree:= Nil;
    em.lastFree:= Nil;
    em.firstBuff:= Nil;
    em.lastBuff:= Nil;

    emsPresent:= False;
    if (not ems.init) and (not ems.getStatus) then begin
        emsPresent:= True;

        (*
         *  Get the page frame address
         *)
        if ems.getPFA(em.pageFrame) then begin
            errno:= VMErrEMS;
            init:= True;
            Exit;
            end;

        (*
         *  Indicate that the page frame area is empty
         *)
        for i:= 0 to EMS_PAGE_FRAME_SIZE-1 do begin
            em.contents[i]:= Nil;
            end;


        (*
         *  Allocate a one page buffer which we will use for
         *  reading and writing pages between XMS and DISK.
         *
         *  This is to avoid spending valuable conventional memory on
         *  the buffer.
         *)
        if em.pageBuffHandle.allocEM(1) then begin
            em.pageBuffHandle.handle:= 0;
            end;

        (*
         *  Let's figure out how big blocks we should allocate.
         *  We do this by dividing the total available pages by the
         *  total available handles.
         *)
        if ems.getTotalHandles40(numHandles) then begin
            (*
             *  Probably not 4.0 EMS. Just assume 64 handles.
             *)
            numHandles:= 64;
            end;
        if ems.getNumActiveHandles(activeHandles) then begin
            vm_fatal('EMS_Ems.getNumActiveHandles');
            end;
        if ems.getFreeEM(totalPages, freePages) then begin
            vm_fatal('EMS_Ems.getFreeEM');
            end;

        em.emsBlockSize:= (freePages + numHandles - 1) div
                            (numHandles - activeHandles);

        em.emsBlockSize:= max(em.emsBlockSize, 8);
        end;

    (*
     *  Now let's look at XMS.
     *)
    xm.mruPage:= Nil;
    xm.lruPage:= Nil;
    xm.firstWired:= Nil;
    xm.lastWired:= Nil;

    xm.firstFree:= Nil;
    xm.lastFree:= Nil;

    xm.firstBuff:= Nil;
    xm.lastBuff:= Nil;

    xmsPresent:= False;
    if not xms.init then begin
        xmsPresent:= True;

        (*
         *  Let's figure out how big blocks we should allocate.
         *  We do this by dividing the total available pages by the
         *  total available handles.
         *
         *  Start by just getting a handle.
         *)
        if xms.getFreeXM(freeKXM, contiguousKXM) then begin
            if errno = XMSErrNoXMLeft then begin
                freeKXM:= 0;
                end
            else begin
                vm_fatal('XMS_Xms.getFreeXM');
                end;
            end;

        if freeKXM <> 0 then begin
            if xmsHandle.allocXM(1) then begin
                vm_fatal('XMS_XmBlk.allocXM');
                end;
            if xmsHandle.getHandInfo(blockSize, numHandles, lockCount) then begin
                vm_fatal('XMS_Xms.getHandleInfo');
                end;

            freePages:= freeKXM div (VM_PAGE_SIZE div XMS_PAGE_SIZE);
            xm.xmsBlockSize:= (freePages + numHandles - 1) div numHandles;

            xm.xmsBlockSize:= max(xm.xmsBlockSize, 8);

            if xmsHandle.freeXM then begin
                vm_fatal('XMS_XmBlk.freeXM');
                end;
            end
        else begin
            xm.xmsBlockSize:= 8;
            end;
        end;

    (*
     *  Now initialize the disk
     *)
    disk.firstPage:= Nil;
    disk.lastPage:= Nil;
    disk.firstFree:= Nil;
    disk.lastFree:= Nil;
    disk.fileSize:= 0;

    assign(disk.channel, 'TMPFILE');
    rewrite(disk.channel);
(*    if disk.channel = Nil then begin
        return VMErrDisk;
        end;
 *)

    (*
     *  Initialize conventional memory descriptor:
     *)
    conv.mruBuff:= Nil;
    conv.lruBuff:= Nil;
    conv.firstWired:= Nil;
    conv.lastWired:= Nil;
    conv.spaceAvail:= maxSpace;

    (*
     *  Now initialize the VM block queue
     *)
    firstVmBlock:= Nil;
    lastVmBlock:= Nil;

    errno:= VMErrOK;
    init:= False;
end;

function VM_Vm.shutdown : Boolean;

var
    i:                  Word;

    currFreeArea:       VM_FreeAreaPtr;
    nextFreeArea:       VM_FreeAreaPtr;

    currConvBuff:       VM_ConvBuffPtr;
    nextConvBuff:       VM_ConvBuffPtr;

    currEmsBuff:        VM_EmsBuffPtr;
    nextEmsBuff:        VM_EmsBuffPtr;

    currXmsBuff:        VM_XmsBuffPtr;
    nextXmsBuff:        VM_XmsBuffPtr;

    currVmPage:         VM_VmPagePtr;

    currVmBlock:        VM_VmBlockPtr;
    nextVmBlock:        VM_VmBlockPtr;

begin

    (*
     *  Start by going through the VM blocks.
     *)
    currVmBlock:= firstVmBlock;
    while currVmBlock <> Nil do begin
        nextVmBlock:= currVmBlock^.next;

        (*
         *  Now go through the pages for the block
         *)
        for i:= 0 to currVmBlock^.size - 1 do begin
            currVmPage:= @currVmBlock^.pages^[i];

            if currVmPage^.secondaryKind <> VM_SEC_UNALLOCATED then begin
                Dispose(currVmPage^.sec.disk);
                end;
            end;
        FreeMem(currVmBlock^.pages,
                    sizeof(VM_VmPage) * currVmBlock^.size);
        Dispose(currVmBlock);

        currVmBlock:= nextVmBlock;
        end;


    (*
     *  Now free up the various buffers and buffer descriptors
     *)
    currConvBuff:= conv.mruBuff;
    while currConvBuff <> Nil do begin
        nextConvBuff:= currConvBuff^.next;

        (*
         *  Free the actual buffer
         *)
        FreeMem(currConvBuff^.address,
                currConvBuff^.buffSize * VM_PAGE_SIZE);

        (*
         *  Free the buffer descriptor
         *)
        Dispose(currConvBuff);

        currConvBuff:= nextConvBuff;
        end;

    currEmsBuff:= em.firstBuff;
    while currEmsBuff <> Nil do begin
        nextEmsBuff:= currEmsBuff^.next;

        (*
         *  Free the actual buffer
         *)
        if currEmsBuff^.handle.freeEM then begin
            writeln('VM: Fatal Error: ',
                        ems.errorText(errno), ' EMS Handle #', 
                                            currEmsBuff^.handle.handle);
            Halt;
            end;

        (*
         *  Free the buffer descriptor
         *)
        Dispose(currEmsBuff);

        currEmsBuff:= nextEmsBuff;
        end;

    currXmsBuff:= xm.firstBuff;
    while currXmsBuff <> Nil do begin
        nextXmsBuff:= currXmsBuff^.next;

        (*
         *  Free the actual buffer
         *)
        if currXmsBuff^.handle.freeXM then begin
            writeln('VM: Fatal Error: ',
                        xms.errorText(errno), ' XMS Handle #',
                                            currXmsBuff^.handle.handle);
            Halt;
            end;

        (*
         *  Free the buffer descriptor
         *)
        Dispose(currXmsBuff);

        currXmsBuff:= nextXmsBuff;
        end;

    (*
     *  Now free various free chains.
     *)
    currFreeArea:= em.firstFree;
    while currFreeArea <> Nil do begin
        nextFreeArea:= currFreeArea^.next;
        Dispose(currFreeArea);

        currFreeArea:= nextFreeArea;
        end;

    currFreeArea:= xm.firstFree;
    while currFreeArea <> Nil do begin
        nextFreeArea:= currFreeArea^.next;
        Dispose(currFreeArea);

        currFreeArea:= nextFreeArea;
        end;

    currFreeArea:= disk.firstFree;
    while currFreeArea <> Nil do begin
        nextFreeArea:= currFreeArea^.next;
        Dispose(currFreeArea);

        currFreeArea:= nextFreeArea;
        end;

    (*
     *  Free up the one page EMS buffer
     *)
    if emsPresent then begin
        if em.pageBuffHandle.freeEM then begin
            vm_fatal('EMS_EmBlock.freeEM');
            end;
        end;

    (*
     *  Now close the temp file and delete it.
     *)
    Close(disk.channel);
    Erase(disk.channel);

    (*
     *  All done.
     *)
    shutdown:= False;
end;

