-- JaMVMImpl.mesa
-- Pilot version
-- Original version by Martin Newell, February 1979
-- Updated June 12, 1979  4:42 PM by MN
-- Last changed by Bill Paxton, 14-Jan-82 14:03:03

-- Virtual memory is mapped onto a file, using the Pilot Space machinery.
-- The maximum size of the file is fixed at the time the VM is started,
-- but the actual file will grow in increments as necessary.
-- 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.

DIRECTORY
  JaMBasic,
  JaMVM,
  DCSFileTypes USING [tLeaderPage],
  Directory,
  File,
  Inline,
  Space;

JaMVMImpl: MONITOR
IMPORTS JaMVM, Directory, File, Inline, Space
EXPORTS JaMVM = {
OPEN JaMBasic;

-- Types and constants

AllocInfo: TYPE = MACHINE DEPENDENT RECORD[
  password: CARDINAL,
  words: LONG CARDINAL,
  offset: CARDINAL];

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

abase: CARDINAL = 1; -- base page in file for alloc info
vmbase: CARDINAL = 2; -- base page in file for vm
initialSize: CARDINAL = 256;
sizeIncrement: CARDINAL = 16;
maximumPages: CARDINAL = 4096;

-- Globals

vmfile: File.Capability ← File.nullCapability;
aspace: Space.Handle ← Space.nullHandle; -- space for alloc info (1 page)
vmspace: Space.Handle ← Space.nullHandle; -- space for all of vm
vmpages: CARDINAL; -- number of pages currently mapped
vmwords: LONG CARDINAL; -- number of words currently mapped

alloc: LONG POINTER TO AllocInfo ← NIL;
vm: PUBLIC Base ← NIL;

-- Errors and Signals

VMError: PUBLIC ERROR = CODE;
BadVMPassword: PUBLIC SIGNAL = CODE;

-- Procedures

WordsForPages: PROC[p: CARDINAL] RETURNS[LONG CARDINAL] = INLINE {
  RETURN[Inline.LongMult[p,Space.wordsPerPage]] };
PagesForWords: PROC[w: LONG CARDINAL] RETURNS[CARDINAL] = INLINE {
  RETURN[Inline.LongDiv[w + Space.wordsPerPage - 1, Space.wordsPerPage]] };

OpenFile: PROC[name: LONG STRING] RETURNS[file: File.Capability, old: BOOLEAN] = {
  -- Open a file with the given name.
  -- If no file with that name exists, create one with initialSize pages.
  permissions: File.Permissions = File.read + File.write + File.grow;
  new: BOOLEAN ← FALSE;
  -- Look up the file
  file ← Directory.Lookup[fileName: name,
    permissions: permissions ! Directory.Error => SELECT type FROM
      fileNotFound => { new ← TRUE; CONTINUE }; ENDCASE];
  IF new THEN {
    file ← Directory.CreateFile[fileName: name,
      fileType: DCSFileTypes.tLeaderPage, size: vmbase + initialSize];
    file ← File.LimitPermissions[file,permissions];
    }
  ELSE {
    type: File.Type ← File.GetAttributes[file].type;
    IF type#DCSFileTypes.tLeaderPage THEN ERROR VMError;
    IF File.GetSize[file]<vmbase THEN File.SetSize[file,vmbase];
    };
  RETURN[file,~new];
  };

Start: PUBLIC ENTRY PROC[name: LONG STRING, maxPages: CARDINAL]
  RETURNS[restarted: BOOLEAN] = {
  size: CARDINAL;
  IF vm#NIL THEN ERROR VMError;
  [vmfile,restarted] ← OpenFile[name];
  aspace ← Space.Create[size: 1, parent: Space.virtualMemory];
  Space.Map[space: aspace, window: [file: vmfile, base: abase]];
  alloc ← Space.LongPointer[aspace];
  IF restarted THEN {
    IF alloc.password#vmPassword THEN { -- file is bad
    	--SIGNAL BadVMPassword;-- --don't raise this now since nobody in good position to catch it!
    	restarted ← FALSE }; -- pretend the file didn't exist before
    };
  IF ~restarted THEN {
    alloc.password ← vmPassword;
    alloc.words ← SIZE[Root];
    alloc.offset ← 0;
    };
  maxPages ← MIN[maxPages, maximumPages];
  vmspace ← Space.Create[size: maxPages, parent: Space.virtualMemory];
  vm ← Space.LongPointer[vmspace]; vmpages ← 0;
  size ← PagesForWords[alloc.words + (IF alloc.offset>0 THEN 1 ELSE 0)];
  MapUpTo[MAX[size,initialSize]];
  };

Expand: PROC = {
  want: CARDINAL ← vmpages + sizeIncrement; -- want to add at least sizeIncrement
  need: CARDINAL ← PagesForWords[alloc.words + (IF alloc.offset>0 THEN 1 ELSE 0)];
  pages: CARDINAL ← MAX[want,need];
  MapUpTo[pages];
  };

MapUpTo: PROC[pages: CARDINAL] = {
  filesize: CARDINAL ← vmbase + pages; -- required file size
  h: Space.Handle;
  IF pages>maximumPages THEN ERROR VMError; -- too many pages
  IF File.GetSize[vmfile]<filesize THEN File.SetSize[vmfile,filesize];
  h ← Space.Create[size: pages - vmpages, parent: vmspace, base: vmpages];
  Space.CreateUniformSwapUnits[size: 1, parent: h];
  Space.Map[space: h, window: [file: vmfile, base: vmbase + vmpages]];
  vmwords ← WordsForPages[vmpages ← pages];
  };

Check: PUBLIC ENTRY PROC = {
  IF vm=NIL THEN ERROR VMError;
  Space.ForceOut[aspace];
  Space.ForceOut[vmspace];
  };

Flush: PUBLIC ENTRY PROC = {
  IF vm=NIL THEN ERROR VMError;
  vm ← NIL; alloc ← NIL;
  Space.Delete[aspace];
  Space.Delete[vmspace];
  aspace ← vmspace ← Space.nullHandle;
  };

Rptr: TYPE = Base RELATIVE LONG POINTER;
Offs: TYPE = [0..1];

AllocWords: ENTRY PROC[n: LONG CARDINAL] RETURNS[Rptr] = {
  -- Increment allocation pointer by n words
  -- AllocWords[0] aligns on a word boundary
  -- Generates VMError (from Expand) if no more room:
  rptr: Rptr;
  IF alloc.offset > 0 THEN { alloc.words ← alloc.words + 1; alloc.offset ← 0 };
  rptr ← LOOPHOLE[alloc.words];
  alloc.words ← alloc.words + n;
  IF alloc.words > vmwords THEN Expand[];
  RETURN[rptr];
  };

AllocChars: ENTRY PROC[n: CARDINAL] RETURNS[Rptr,Offs] = {
  -- Increment allocataion pointer by length chars
  -- Generates VMError (from Expand) if no more room:
  rptr: Rptr ← LOOPHOLE[alloc.words];
  offs: Offs ← alloc.offset;
  frac: CARDINAL ← offs + n MOD 2;
  alloc.words ← alloc.words + n/2 + frac/2; -- mind overflow!
  alloc.offset ← frac MOD 2;
  IF alloc.words >= vmwords THEN Expand[];
  RETURN[rptr,offs];
  };

AllocString: PUBLIC PROC[length: StringLength] RETURNS[string Object] = {
  string: string Object ← [L,string[length: length, text: , offset: ]];
  [string.text,string.offset] ← AllocChars[length];
  RETURN[string];
  };

AllocArray: PUBLIC PROC[length: CARDINAL] RETURNS[array Object] = {
  array: array Object ← [L,array[length: length, base: ]];
  array.base ← AllocWords[Inline.LongMult[length,SIZE[Object]]];
  RETURN[array];
  };

AllocTuples: PROC[size: CARDINAL] RETURNS[beg,end: TupleRptr] = {
  words: LONG CARDINAL ← Inline.LongMult[size,SIZE[Tuple]];
  beg ← AllocWords[words]; end ← beg + words;
  RETURN[beg,end];
  };

AllocDict: PUBLIC PROC[size: CARDINAL] RETURNS[dict Object] = {
  dict: dict Object ← [L,dict[AllocWords[SIZE[DictBody]]]];
  dd: DictBody ← [curlen: 0, maxlen: 0, size: size,
    beg: , end: , curatt: 0, attach: [L,array[0,vmNIL]]];
  [dd.beg,dd.end] ← AllocTuples[size];
  JaMVM.PutDict[dict,dd];
  RETURN[dict];
  };

CopyArray: PUBLIC PROC[src,dst: array Object] = {
  count: CARDINAL ← MIN[src.length,dst.length]; -- number of objects to copy
  sptr: LONG POINTER ← @vm[src.base];
  dptr: LONG POINTER ← @vm[dst.base];
  words: LONG CARDINAL ← Inline.LongMult[count,SIZE[Object]];
  chunk: CARDINAL = (LAST[CARDINAL]/SIZE[Object])*SIZE[Object];
  WHILE words>0 DO
    w: CARDINAL ← Inline.LowHalf[MIN[words,chunk]];
    Inline.LongCOPY[from: sptr, nwords: w, to: dptr];
    sptr ← sptr + w; dptr ← dptr + w; words ← words - w;
    ENDLOOP;
  };

GetText: PUBLIC PROC[string: string Object, text: LONG STRING] = {
  count: CARDINAL ← MIN[string.length,text.maxlength];
  stext: LONG POINTER TO TextBody ← @vm[string.text];
  soffs: CARDINAL ← string.offset;
  FOR i: CARDINAL IN[0..count) DO text[i] ← stext[soffs+i] ENDLOOP;
  text.length ← count;
  };

PutText: PUBLIC PROC[string: string Object, text: LONG STRING] = {
  count: CARDINAL ← MIN[string.length,text.length];
  stext: LONG POINTER TO TextBody ← @vm[string.text];
  soffs: CARDINAL ← string.offset;
  FOR i: CARDINAL IN[0..count) DO stext[soffs+i] ← text[i] ENDLOOP;
  };

CopyString: PUBLIC PROC[src,dst: string Object] = {
  count: CARDINAL ← MIN[src.length,dst.length]; -- number of chars to copy
  stext: LONG POINTER TO TextBody ← @vm[src.text];
  dtext: LONG POINTER TO TextBody ← @vm[dst.text];
  soffs: CARDINAL ← src.offset;
  doffs: CARDINAL ← dst.offset;
  FOR i: CARDINAL IN[0..count) DO dtext[doffs+i] ← stext[soffs+i] ENDLOOP;
  };

}.

Wyatt  24-Oct-81 14:42:23
  add maxPages parameter to Start