-- IntVM.mesa
-- Derived from IntVM
-- Last changed by MN,  3-Aug-81 17:24:04


-- Virtual memory is mapped onto a file, using the Pilot Space machinery.
-- The size of the file is fixed at the time the VM is started.
-- VM address 0 maps onto the first word of page 2 (counting from 0) of the
-- file, because page 0 is reserved for the leader page, and page 1 is used
-- by the VM package for restart information. A simple marching allocator
-- is provided, but it is logically separate from the VM package itself
-- (i.e. the first command can be GetWordVM[...]).

DIRECTORY
  IntVMDefs USING [VMBase, VMAddr],
  DCSFileTypes USING [tLeaderPage],
  Directory USING [CreateFile, Error, Lookup],
  File USING [Capability, Error, GetAttributes, GetSize,
    nullCapability, PageCount, read, write],
  Inline USING [LowHalf],
  Space USING [Create, CreateUniformSwapUnits, Delete, Handle,
    LongPointer, Map, nullHandle, Unmap, virtualMemory, wordsPerPage],
  Transaction USING [Begin, Commit, Handle, nullHandle];

IntVM: PROGRAM
IMPORTS Directory, File, Inline, Space, Transaction
EXPORTS IntVMDefs = {
OPEN IntVMDefs;

--*** GLOBALS ***

vmpages: CARDINAL; -- number of pages available in VM
maxWords: LONG CARDINAL; -- maximum number of words in VM

minFileSize: File.PageCount = 12; -- don't allow VM file smaller than this
maxFileSize: File.PageCount = 4002; -- don't allow VM file larger than this

-- Paging file
vmfile: File.Capability ← File.nullCapability;
vmspace: Space.Handle ← Space.nullHandle;
vmtrans: Transaction.Handle ← Transaction.nullHandle;
vmbase: PUBLIC VMBase ← NIL;

vmPassword: CARDINAL = 48249; -- a more-or-less random number

-- Allocation info
AllocInfo: TYPE = MACHINE DEPENDENT RECORD[
  password: CARDINAL,
  words: LONG CARDINAL,
  offset: CARDINAL];
alloc: LONG POINTER TO AllocInfo ← NIL;

--*** PROCEDURES ***

OpenVMFile: PROC[name: STRING, nPages: CARDINAL]
  RETURNS[file: File.Capability, pages: CARDINAL, old: BOOLEAN] = {
  ENABLE {
    File.Error => GOTO Punt;
    Directory.Error => GOTO Punt;
    };
  old ← TRUE;
  file ← Directory.Lookup[fileName: name,
    permissions: File.read+File.write
    !Directory.Error => SELECT type FROM
      fileNotFound => { old ← FALSE; CONTINUE };
      ENDCASE];
  IF old THEN {
    size: File.PageCount ← File.GetSize[file];
    IF size IN[minFileSize..maxFileSize] THEN pages ← Inline.LowHalf[size]-2
    ELSE GOTO Punt;
    IF File.GetAttributes[file].type#DCSFileTypes.tLeaderPage THEN GOTO Punt;
    }
  ELSE file ← Directory.CreateFile[fileName: name,
    fileType: DCSFileTypes.tLeaderPage, size: (pages ← nPages)+2];
  RETURN[file,pages,old];
  EXITS Punt => ERROR VMFileProblem
  };

InitVM: PUBLIC PROCEDURE RETURNS [BOOLEAN] = {
  filename: STRING ← "IntScratch$";
  nPages: CARDINAL ← 4000;
  restarted: BOOLEAN;
  -- Restart VM to page from file filename, not to exceed nPages pages
  [vmfile,vmpages,restarted] ← OpenVMFile[filename,nPages];
  vmspace ← Space.Create[size: vmpages+1, parent: Space.virtualMemory];
  Space.CreateUniformSwapUnits[size: 1, parent: vmspace];
  vmtrans ← Transaction.Begin[];
  Space.Map[space: vmspace, window: [file: vmfile, base: 1],
    transaction: vmtrans];
  alloc ← Space.LongPointer[vmspace]; -- first data page holds allocation info
  vmbase ← alloc + Space.wordsPerPage;
  maxWords ← LONG[vmpages]*Space.wordsPerPage;
 -- IF restarted THEN {
 --   IF alloc.password#vmPassword THEN ERROR VMFileProblem;
 --   IF alloc.words > maxWords THEN ERROR VMFull;
 --   }
 -- ELSE {
    alloc.password ← vmPassword;
    alloc.words ← 0; alloc.offset ← 0;
 --   };
  RETURN[TRUE];
  };

FinishVM: PUBLIC PROCEDURE RETURNS [BOOLEAN] = {
  -- Write up page buffers and deallocate them
  vmbase ← NIL; alloc ← NIL;
  Space.Unmap[vmspace];
  Transaction.Commit[vmtrans];
  vmtrans ← Transaction.nullHandle;
  Space.Delete[vmspace];
  vmspace ← Space.nullHandle;
  RETURN[TRUE];
  };

AllocateBlock: PUBLIC PROCEDURE [size: CARDINAL] RETURNS [VMAddr] = {
  -- Increment allocation pointer by length words
  -- Generates VMFull:
  location: VMAddr;
  IF alloc.offset > 0 THEN { alloc.words ← alloc.words + 1; alloc.offset ← 0 };
  location ← LOOPHOLE[alloc.words];
  alloc.words ← alloc.words + size;
  IF alloc.words > maxWords THEN ERROR VMFull;
  RETURN[location];
  };

VMBadPointer: PUBLIC ERROR = CODE;
VMFull: PUBLIC ERROR = CODE;
VMFileProblem: PUBLIC ERROR = CODE;

}.