-- TJaMImpl.mesa
-- Last changed by Bill Paxton, 20-Oct-81 14:21:56 

DIRECTORY
  JaMBasic,
  JaMInternal,
  JaMOps,
  Ascii USING [CR],
  Directory USING [Error, Lookup],
  File USING [Capability],
  Heap USING [systemZone],
  Process USING [Detach],
  Runtime USING [CallDebugger, ConfigError, IsBound, LoadConfig],
  Storage USING [String, FreeString],
  StreamDefs;

TJaMImpl: MONITOR
IMPORTS JaMOps, Directory, Heap, Process, Runtime, Storage, TTY, Exec 
EXPORTS TJaM = {
OPEN StreamDefs, JaMOps, JaMInternal, JaMBasic;

zone: UNCOUNTED ZONE = Heap.systemZone;

prompt,start,badfile,badname,badversion: name Object;

version: STRING ← "Tioga JaM of 16-Oct-81";

line: STRING ← NIL;
index: CARDINAL ← 0;

running: BOOLEAN ← FALSE; -- this is the monitor data
quit: BOOLEAN ← FALSE;

Quit: PROC[frame: Frame] = {
  quit ← TRUE;
  Stop[frame];
  };

-- ***** BCD procs

LoadBCD: PROC[frame: Frame] = { BCDLoader[frame,FALSE] };
-- Expects opstk: (bcdFileName)
-- Loads and STARTS the configuration in bcdFileName

DebugBCD: PROC[frame: Frame] = { BCDLoader[frame,TRUE] };
-- Expects opstk: (bcdFileName)
-- Like LoadBCD, but invokes the debugger before STARTing

BCDLoader: PROC[frame: Frame, debug: BOOLEAN] = {
  Append: PROC[s,t: STRING] = {
    FOR i: CARDINAL IN[0..t.length) WHILE s.length<s.maxlength DO
      s[s.length] ← t[i]; s.length ← s.length + 1 ENDLOOP };
  name: STRING ← [80];
  ext: STRING ← ".bcd"L;
  extended: BOOLEAN ← FALSE;
  file: File.Capability;
  prog: PROGRAM ← NIL;
  string: string Object ← PopString[frame.opstk];
  IF (string.length+ext.length)>name.maxlength THEN GOTO BadName
  ELSE StringText[string,name];
  file ← Directory.Lookup[name !
    Directory.Error => SELECT type FROM
      fileNotFound => IF extended THEN GOTO BadName
        ELSE { Append[name,ext]; extended ← TRUE; RETRY };
      ENDCASE => GOTO BadName];
  prog ← Runtime.LoadConfig[file: file, offset: 1 !
    Runtime.ConfigError => SELECT type FROM
      versionMismatch => GOTO BadVersion;
      ENDCASE => GOTO BadFile];
  IF debug THEN {
    message: STRING ← [100];
    Append[message,"JaM: Just loaded "];
    Append[message,name];
    Runtime.CallDebugger[message];
    };
  IF Runtime.IsBound[prog] THEN START prog;
  EXITS
    BadFile => ERROR Error[badfile];
    BadName => ERROR Error[badname];
    BadVersion => ERROR Error[badversion];
  };
  

-- ***** I/O procs

JPrint: PROC[frame: Frame] = {
  string: string Object ← PopString[frame.opstk];
  Put: PROC[c: CHARACTER] RETURNS[BOOLEAN] = {
    abort: BOOLEAN ← TTY.UserAbort[];
    IF abort THEN { TTY.ResetUserAbort[]; SetAbort[frame,TRUE] }
    ELSE TTY.PutChar[tty,c];
    RETURN[abort] };
  StringForAll[string,Put];
  };


-- ***** Initialization 

InstallJaMPilot: PROC[why: InstallReason, frame: Frame] = { SELECT why FROM
  register => {
    found: BOOLEAN;
    badfile ← MakeName[".badfile"L];
    badname ← MakeName[".badname"L];
    badversion ← MakeName[".badversion"L];
    prompt ← MakeName[".prompt"L];
    start ← MakeName[".start"L];

    RegisterExplicit[frame,".print"L,JPrint];
    RegisterExplicit[frame,".quit"L,Quit];
    RegisterExplicit[frame,".loadbcd"L,LoadBCD];
    RegisterExplicit[frame,".debugbcd"L,DebugBCD];

    Def[frame,prompt,MakeString["(*).print"L,X]];
    Def[frame,MakeName[".version"L],MakeString[version]];
    Def[frame, undefname, MakeString["(Undefined name - .undefname: ).print
(                         ).cvis .print"L,X]];
    [found,] ← TryToLoad[frame,start];
    IF NOT found THEN Def[frame,start,MakeString[".version .print (
).print"L,X]];

    };
  ENDCASE;
  };

LineReset: PROC[s: StreamHandle] = { index ← 0 };
LineGet: PROC[s: StreamHandle] RETURNS[UNSPECIFIED] = {
  i: CARDINAL ← index;
  IF i<line.length THEN { index ← i + 1; RETURN[line[i]] }
  ELSE RETURN[0C];
  };
LinePutback: PROC[s: StreamHandle, x: UNSPECIFIED] = {
  IF index>0 THEN index ← index - 1;
  };
LineEndof: PROC[s: StreamHandle] RETURNS[BOOLEAN] = {
  RETURN[index>=line.length];
  };
NoopPut: PROC[s: StreamHandle, x: UNSPECIFIED] = { };
NoopDestroy: PROC[s: StreamHandle] = { };

EndLine: PROC[c: CHARACTER] RETURNS[BOOLEAN] = {
  IF c=Ascii.CR THEN RETURN[LineComplete[line]] ELSE RETURN[FALSE] };

DoJaM: ENTRY PROC = {
  ENABLE UNWIND => NULL;
  IF running THEN TTY.PutString[Exec.w,"JaM is already running."L]
  ELSE { OPEN Exec;
    TTY.PutString[w,"Starting "]; TTY.PutString[w,version]; TTY.PutString[w,"..."L];
    -- Grab the rest of the command line now, before it goes away
    line ← Storage.String[MAX[200,commandLine.s.length-commandLine.i]];
    WHILE commandLine.i<commandLine.s.length DO
      line[line.length] ← commandLine.s[commandLine.i];
      line.length ← line.length + 1; commandLine.i ← commandLine.i + 1;
      ENDLOOP;
    Process.Detach[FORK RunJaM[]];
    running ← TRUE;
    };
  };

RunJaM: PROC = {
  frame: Frame;
  s: StreamHandle ← zone.NEW[StreamObject ← [
    reset: LineReset, get: LineGet, putback: LinePutback, endof: LineEndof,
      put: NoopPut, destroy: NoopDestroy, data: NIL]];
  stream: stream Object;
  tty ← TTY.Create["JaM.log"L];

  StartJaM[];
  frame ← defaultFrame;

  { ENABLE ABORTED => CONTINUE;
  -- do start macro
  Execute[frame,start];

  -- do command line
  s.reset[s]; stream ← MakeStream[s];
  stream.tag ← X; Execute[frame,stream];

  -- The Main Loop
  quit ← FALSE;
  UNTIL quit DO
    Execute[frame,prompt];
    [] ← TTY.GetEditedString[tty,line,EndLine,TRUE !
      TTY.LineOverflow => IF s=line THEN {
        new: STRING ← Storage.String[s.maxlength+s.maxlength/2];
	FOR i: CARDINAL IN[0..s.length) DO new[i] ← s[i] ENDLOOP;
	new.length ← s.length; line ← new; Storage.FreeString[s];
	RESUME[new] };
      TTY.Rubout => { TTY.PutLine[tty," XXX"L]; LOOP }];
    TTY.PutCR[tty];
    s.reset[s]; stream ← MakeStream[s];
    stream.tag ← X; Execute[frame,stream];
    ENDLOOP;
  };

  StopJaM[];
  TTY.Destroy[tty];
  Storage.FreeString[line];
  running ← FALSE;
  };

-- Initialization

Install[InstallJaMPilot];

}.