-- JaMControlImpl.mesa
-- Original version by John Warnock, Feb., 1979.
-- Last changed by Paxton, 14-Jan-82 14:12:03

DIRECTORY
  JaMBasic USING [Object, Root, Tag],
  JaMInternal USING [Frame, FrameRecord],
  JaMOps USING [ACopy, AFind, AGet, APut, Array, Assert, Begin, Def, Dict,
    Error, ForEachInstalled, FreeCache, FreeLocals, FreeStack, Install,
    InstallReason, MakeName, MakeString, NewCache, NewLocals, NewStack, Pop,
    PopCommand, Push, Text, TryToLoad, undefname],
  JaMStorage USING [Prune, Zone],
  JaMVM USING [Check, Flush, GetRoot, PutRoot, Start];

JaMControlImpl: MONITOR
LOCKS lock USING lock: POINTER TO MONITORLOCK
IMPORTS JaMOps, JaMStorage, JaMVM
EXPORTS JaMOps = {
OPEN VM:JaMVM, JaMOps, JaMInternal, JaMBasic;

-- Types and Constants

initCommandSize: CARDINAL = 512;
initNameSize: CARDINAL = 512;

CommandEntry: TYPE = RECORD[SELECT plicit: * FROM
  ex => [proc: PROC[Frame]], im => [proc: PROC[]], ENDCASE];
CommandTable: TYPE = RECORD [len: CARDINAL,
  array: SEQUENCE max: CARDINAL OF CommandEntry];

-- Globals

zone: UNCOUNTED ZONE = JaMStorage.Zone[];

root: PUBLIC LONG POINTER TO Root ← NIL;

defaultFrame: PUBLIC Frame ← NIL; -- the default frame

framelock: MONITORLOCK;
framelist: PUBLIC Frame ← NIL; -- head of list of all frames

commandlock: MONITORLOCK;
commands: LONG POINTER TO CommandTable ← NIL;

unregistered: name Object;

-- Procedures

DoCommand: PUBLIC PROC[frame: Frame, ob: command Object] = {
  entry: CommandEntry ← GetCommand[@commandlock,ob.index];
  WITH e:entry SELECT FROM
    ex => e.proc[frame];
    im => IF frame=defaultFrame THEN e.proc[] ELSE Unregistered[frame];
    ENDCASE;
  };

RegisterExplicit: PUBLIC PROC[frame: Frame, text: Text, proc: PROC[Frame]] = {
  RegisterEntry[frame,MakeName[text],[ex[proc]]] };

RegisterImplicit: PUBLIC PROC[frame: Frame, text: Text, proc: PROC[]] = {
  RegisterEntry[frame,MakeName[text],[im[proc]]] };

RegisterEntry: PROC[frame: Frame, name: name Object, entry: CommandEntry] = {
  known: BOOLEAN; ob: Object; index: CARDINAL;
  [known,ob] ← TryToLoad[frame,name]; -- look up the name
  IF known THEN WITH ob:ob SELECT FROM
    command => index ← ob.index; -- use old command index
    ENDCASE => known ← FALSE; -- not defined as a command
  IF NOT known THEN { index ← NewCommand[@commandlock]; -- acquire a new index
    Def[frame,name,[X,command[index]]] }; -- define in current dictionary context
  PutCommand[@commandlock,index,name,entry]; -- enter in command table and commandArray
  };

NewCommand: ENTRY PROC[lock: POINTER TO MONITORLOCK]
  RETURNS[CARDINAL] = {
  ENABLE UNWIND => NULL;
  index: CARDINAL ← root.commandCount; -- new command index
  array: array Object ← root.commandArray;
  Assert[index=commands.len AND array.length=commands.max]; -- check consistency
  IF NOT index<array.length THEN {
    -- must grow command table and command array
    oldcommands: LONG POINTER TO CommandTable ← commands;
    oldmax: CARDINAL ← array.length;
    newmax: CARDINAL ← oldmax + MIN[oldmax/2,LAST[CARDINAL]-oldmax];
    Assert[index<newmax]; -- 2↑16 commands?!
    -- grow the command table
    commands ← zone.NEW[CommandTable[newmax]];
    FOR i: CARDINAL IN[0..oldmax) DO commands[i] ← oldcommands[i] ENDLOOP;
    FOR i: CARDINAL IN[oldmax..newmax) DO commands[i] ← [ex[Unregistered]] ENDLOOP;
    zone.FREE[@oldcommands];
    -- also grow commandArray
    array ← root.commandArray ← ACopy[array,newmax];
    };
  root.commandCount ← commands.len ← index + 1;
  VM.PutRoot[root]; -- update root information in VM
  RETURN[index];
  };

GetCommand: ENTRY PROC[lock: POINTER TO MONITORLOCK,
  i: CARDINAL] RETURNS[CommandEntry] = INLINE {
  RETURN[IF i<commands.len THEN commands[i] ELSE [ex[Unregistered]]] };

PutCommand: ENTRY PROC[lock: POINTER TO MONITORLOCK,
  index: CARDINAL, name: name Object, entry: CommandEntry] = {
  ENABLE UNWIND => NULL;
  Assert[index<root.commandCount];
  commands[index] ← entry; -- put entry in command table
  APut[root.commandArray,index,name]; -- record command name in array
  };

FindCommand: ENTRY PROC[lock: POINTER TO MONITORLOCK,
  name: name Object]
  RETURNS[known: BOOLEAN, index: CARDINAL] = {
  ENABLE UNWIND => NULL;
  [known,index] ← AFind[root.commandArray,name];
  };

CommandName: ENTRY PROC[lock: POINTER TO MONITORLOCK,
  command: command Object] RETURNS[Object] = {
  ENABLE UNWIND => NULL;
  array: array Object ← root.commandArray;
  count: CARDINAL ← root.commandCount;
  i: CARDINAL ← command.index;
  IF i<count AND i<array.length THEN RETURN[AGet[array,i]]
  ELSE RETURN[unregistered];
  };

RegisterInternal: PUBLIC PROC[text: Text, proc: PROC[Frame]]
  RETURNS[command Object] = {
  name: name Object ← MakeName[text];
  known: BOOLEAN; index: CARDINAL;
  [known,index] ← FindCommand[@commandlock,name];
  IF NOT known THEN index ← NewCommand[@commandlock];
  PutCommand[@commandlock,index,name,[ex[proc]]];
  RETURN[[X,command[index]]];
  };

RegisterString: PROC[frame: Frame, name: Text, string: Text] = {
  key: Object ← MakeName[name];
  val: Object ← MakeString[string,X];
  Def[frame,key,val];
  };

RegisterObject: PROC[frame: Frame, name: Text, val: Object] = {
  key: Object ← MakeName[name];
  Def[frame,key,val];
  };

-- Intrinsics

Unregistered: PUBLIC PROC[frame: Frame] = {
  ERROR Error[unregistered];
  };

JCommandName: PROC[frame: Frame] = {
  command: command Object ← PopCommand[frame.opstk];
  name: Object ← CommandName[@commandlock,command];
  Push[frame.opstk, name];
  };

JReRegister: PROC[frame: Frame] = {
  ForEachInstalled[register,frame];
  };

JUndef: PROC[frame: Frame] = {
  ob: Object ← Pop[frame.opstk];
  };

-- Initialization

StartJaM: PUBLIC PROC[vmname: LONG STRING, maxPages: CARDINAL] = {
  -- init everybody
  ForEachInstalled[init,NIL];
  StartVM[vmname,maxPages]; -- fire up the VM
  commands ← zone.NEW[CommandTable[root.commandArray.length] ← [len: 0, array: ]];
  FOR i: CARDINAL IN[0..commands.max) DO commands[i] ← [ex[Unregistered]] ENDLOOP;
  commands.len ← root.commandCount;
  defaultFrame ← NewFrame[]; -- make a new frame **** fix this ****
  Begin[defaultFrame,root.sysDict]; -- push the system dictionary
  -- Register everyone (including Control)
  ForEachInstalled[register,defaultFrame];
  VM.Check[];
  };

StopJaM: PUBLIC PROC = {
  VM.Flush[];
  FreeFrame[defaultFrame]; -- **** fix this ****
  zone.FREE[@commands];
  -- free everybody
  ForEachInstalled[free,NIL];
  JaMStorage.Prune[];
  };

StartVM: PROC[vmname: LONG STRING, maxPages: CARDINAL] = {
  old: BOOLEAN ← VM.Start[vmname,maxPages]; -- fire up the VM
  IF old THEN {
    VM.GetRoot[root];
    root.stamp ← root.stamp + 1;
    }
  ELSE {
    -- initialize root information
    root.stamp ← 0;
    root.sysDict ← Dict[256];
    root.commandCount ← 0;
    root.commandArray ← Array[initCommandSize];
    root.nameCount ← 0;
    root.nameArray ← Array[initNameSize];
    root.nameDict ← Dict[initNameSize];
    };
  VM.PutRoot[root];
  };

NewFrame: PUBLIC PROC RETURNS[Frame] = {
  Link: ENTRY PROC[lock: POINTER TO MONITORLOCK,
    f: Frame] = INLINE { f.link ← framelist; framelist ← f };
  frame: Frame ← zone.NEW[FrameRecord ← [link: NIL, opstk: NIL, execstk: NIL, dictstk: NIL,
    cache: NIL, locals: NIL, abort: FALSE, stepflag: FALSE, astepflag: FALSE]];
  frame.opstk ← NewStack[500];
  frame.execstk ← NewStack[250];
  frame.dictstk ← NewStack[20];
  frame.cache ← NewCache[];
  frame.locals ← NewLocals[];
  Link[@framelock,frame];
  RETURN[frame];
  };

FreeFrame: PUBLIC PROC[frame: Frame] = {
  Unlink: ENTRY PROC[lock: POINTER TO MONITORLOCK,
    f: Frame] = INLINE {
    FOR p: LONG POINTER TO Frame ← @framelist, @p↑.link UNTIL p↑ = NIL DO
      IF p↑=f THEN { p↑ ← f.link; EXIT }; ENDLOOP; };
  Unlink[@framelock,frame];
  FreeLocals[frame.locals];
  FreeCache[frame.cache];
  FreeStack[frame.dictstk];
  FreeStack[frame.execstk];
  FreeStack[frame.opstk];
  zone.FREE[@frame];
  };

ForEachFrame: PUBLIC PROC[proc: PROC[Frame]] = {
  ForEach: ENTRY PROC[lock: POINTER TO MONITORLOCK,
    proc: PROC[Frame]] = INLINE {
    FOR f: Frame ← framelist, f.link UNTIL f=NIL DO proc[f] ENDLOOP;
    };
  ForEach[@framelock,proc];
  };

ForEachFrameExcept: PUBLIC PROC[frame: Frame, proc: PROC[Frame]] = {
  ForEachExcept: ENTRY PROC[lock: POINTER TO MONITORLOCK,
    frame: Frame, proc: PROC[Frame]] = INLINE {
    FOR f: Frame ← framelist, f.link UNTIL f=NIL DO IF f#frame THEN proc[f] ENDLOOP;
    };
  ForEachExcept[@framelock,frame,proc];
  };

-- Initialization

InstallControl: PROC[why: InstallReason, frame: Frame] = { SELECT why FROM
  init => {
    root ← zone.NEW[Root];
    };
  free => {
    zone.FREE[@root];
    };
  register => {
    unregistered ← MakeName[".unregistered"L];
    RegisterObject[frame, ".sysdict"L, root.sysDict]; -- the system dictionary
    RegisterObject[frame, ".true"L, [L,boolean[TRUE]]];
    RegisterObject[frame, ".false"L, [L,boolean[FALSE]]];
    RegisterExplicit[frame, ".commandname"L, JCommandName];
    RegisterExplicit[frame, ".reregister"L, JReRegister];
    -- Register a default error handler for .undefname
    -- Better be sure that undefname has already been registered.
    RegisterEntry[frame, undefname, [ex[JUndef]]];
    };
  ENDCASE;
  };

Install[InstallControl];

}.


Paxton  20-Oct-81 10:25:38
  made NewFrame and FreeFrame public

Wyatt  22-Oct-81 10:34:08
  add vmname parameter to StartJaM

Wyatt  24-Oct-81 15:18:54
  add maxPages parameter to StartJaM