-- XDNubImpl.Mesa  Edited by: Bruce on September 24, 1980  3:28 PM
--				Sandman on July 21, 1980  11:02 AM

DIRECTORY
  Actions USING [Read, Write],
  Ascii USING [ControlS, ControlT, CR, NUL, SP],
  BcdOps USING [BcdBase],
  CommandList USING [Command],
  Commands USING [Command, Confirm, GetString, GetToken, GetTwoStrings, WriteCommand, WriteError],
  ControlDefs USING [ControlModule, GFT, NullFrame],
  DebugOps USING [Abort, Proceed, Quit, StringExpToOctal],
  DOutput USING [Char, Decimal, EOL, Octal, Text],
  LoaderOps USING [BadCode, FileNotFound, Load, New, VersionMismatch],
  MachineDefs USING [FHandle, GFHandle, NullGF, RealToBytePC],
  Runtime USING [CallDebugger],
  Mopcodes USING [zEXCH, zPOP, zW0, zWBL],
  Nub USING [badFrame, Sob, Switches],
  SegmentDefs USING [memConfig],
  State USING [GetGS, GetString, GSHandle, SetString, WritePrompt],
  Storage USING [String],
  String USING [AppendChar, AppendOctal, AppendString, LowerCase];

XDNubImpl: PROGRAM
  IMPORTS Actions, Commands, DebugOps, DOutput, LoaderOps, 
    MachineDefs, Runtime, Nub, SegmentDefs, State, Storage, String
  EXPORTS Commands, Nub =
  BEGIN
  
  -- System Signals are converted to these to prevent NubCommand
  -- from catching user generated signals
  BadFile: PUBLIC ERROR [badname: STRING, reason: STRING] = CODE;	-- nee FileNameError
  BadVersion: PUBLIC SIGNAL [badname: STRING] = CODE;	-- nee VersionMismatch

  data: State.GSHandle ← State.GetGS[];

  DoCommand: PUBLIC PROCEDURE [char: CHARACTER] =
    BEGIN OPEN Commands;
    SELECT String.LowerCase[char] FROM
      'n => GetString[[prompt: name, sId: nubModule], LoadIt, nubNew];
      's => GetString[[prompt: nubFrame, sId: nubFrame], StartIt, nubStart];
      't => DisplayNubStack[Nub.badFrame];
      'd => Confirm[nubDebug, CallIDebug];
      'q => Confirm[quit, SignalQuit];
      'p => Confirm[proceed, SignalProceed];
      'r => GetTwoStrings[[prompt: at, sId: nubRAddr, resetPrompt: FALSE], [
	prompt: n10, sId: nubRCount, resetPrompt: FALSE], ReadIt, read];
      'w => GetTwoStrings[[prompt: at, sId: nubWAddr, resetPrompt: FALSE], [
	prompt: gets, sId: nubRhs, colon: FALSE, resetPrompt: FALSE],
	WriteIt, write];
      '? =>
	BEGIN
	DOutput.Text["Commands are: "L];
	FOR nubIndex: CommandList.Command IN [nubNew..nubStack) DO
	  WriteCommand[nubIndex]; DOutput.Text[", "L] ENDLOOP;
	WriteCommand[nubStack];
	State.WritePrompt[];
	END;
      Ascii.CR, Ascii.SP => State.WritePrompt[];
      ENDCASE => BEGIN DOutput.Char['?]; State.WritePrompt[]; END;
    RETURN
    END;
  
  CallIDebug: PROCEDURE = {Runtime.CallDebugger["What's up?"L]};

  LoadIt: PROCEDURE [file: STRING] = BEGIN [] ← LoadFile[file] END;

  StartIt: PROCEDURE [frame: STRING] =
    BEGIN
    gframe: MachineDefs.GFHandle ← LOOPHOLE[DebugOps.StringExpToOctal[frame]];
    StartProg[gframe];
    END;

  ReadIt: PROCEDURE [start, cnt: STRING] = {Actions.Read[start,cnt,ReadXD]};

  ReadXD: PUBLIC PROCEDURE [p: LONG POINTER] RETURNS [UNSPECIFIED] = {RETURN[p↑]};

  DisplayNubStack: PROCEDURE [f: MachineDefs.FHandle]  =
    BEGIN
    Commands.WriteCommand[nubStack]; 
    IF f#ControlDefs.NullFrame THEN f ← f.returnlink.frame;
    THROUGH [0..25) WHILE f#ControlDefs.NullFrame DO
      DOutput.EOL[];
      DOutput.Text["L: "L];
      DOutput.Octal[f];
      DOutput.Text[", G: "L];
      DOutput.Octal[f.accesslink];
      DOutput.Text[", PC: "L];
      DOutput.Octal[MachineDefs.RealToBytePC[f.pc]];
      DOutput.Text[", Locals: "L];
      FOR i: CARDINAL IN [0..3) DO
        DOutput.Octal[f.local[i]];
        DOutput.Text[", "L];
        ENDLOOP;
      DOutput.Octal[f.local[3]];
      f ← f.returnlink.frame;
      ENDLOOP;
    IF f#ControlDefs.NullFrame THEN
      BEGIN
      DOutput.EOL[];
      FOR i: INTEGER IN [0..1000) DO
	IF f=ControlDefs.NullFrame THEN {DOutput.Decimal[i]; EXIT};
        f ← f.returnlink.frame;
        REPEAT
	  FINISHED => DOutput.Text["> 1000"L];
        ENDLOOP;
      DOutput.Text[" more..."L];
      END;
    DOutput.EOL[];
    State.WritePrompt[];
    END;

  WriteXD: PUBLIC PROC [p: LONG POINTER, v: UNSPECIFIED] =
    BEGIN
    WBL: PROC [UNSPECIFIED, LONG POINTER] = 
      MACHINE CODE BEGIN Mopcodes.zWBL, 0 END;
    WriteMem: PROCEDURE [LONG POINTER, UNSPECIFIED] =
      MACHINE CODE BEGIN Mopcodes.zPOP; Mopcodes.zEXCH; Mopcodes.zW0 END;
    IF SegmentDefs.memConfig.xmMicroCode THEN WBL[v, p] ELSE WriteMem[p,v]
    END;

  WriteIt: PROCEDURE [loc, rhs: STRING] = {Actions.Write[loc,rhs,WriteXD]};

  SignalProceed: PUBLIC PROCEDURE =
    BEGIN SIGNAL DebugOps.Proceed; RETURN END;
  
  SignalQuit: PUBLIC PROCEDURE =
    BEGIN SIGNAL DebugOps.Quit; RETURN END;
  
  LoadFile: PUBLIC PROCEDURE [file: STRING] RETURNS [gf: MachineDefs.GFHandle] =
    BEGIN
    ext: STRING ← [10];
    sw: STRING ← [10];
    name: STRING ← [40];
    i: CARDINAL ← 0;
    get: PROCEDURE RETURNS [c:CHARACTER] =
      BEGIN
      IF i = file.length THEN RETURN [Ascii.NUL];
      c ← file[i];
      i ← i+1;
      RETURN[c];
      END;
    gf ← MachineDefs.NullGF;
    Commands.GetToken[get, name, ext, sw];
    IF ext.length = 0 THEN String.AppendString[ext, "bcd"L];
    String.AppendChar[name, '.];
    String.AppendString[name, ext];
    ProcessSwitches[sw];
    gf ← LoadNew[name, switches.framelinks ! BadFile => {
      Commands.WriteError[badFile]; IF ~data.debugging THEN CONTINUE}];
    IF gf # MachineDefs.NullGF THEN 
      BEGIN frame: STRING ← State.GetString[nubFrame];
      data.nubFrame ← gf;
      IF frame = NIL THEN
	BEGIN frame ← Storage.String[7]; State.SetString[nubFrame, frame] END
      ELSE frame.length ← 0;
      String.AppendOctal[frame, gf];
      END;
    RETURN
    END;
  
  LoadNew: PUBLIC PROCEDURE [file: STRING, framelinks: BOOLEAN ← TRUE]
    RETURNS [MachineDefs.GFHandle] =
    BEGIN OPEN LoaderOps;
    bcd: BcdOps.BcdBase;
    cm: ControlDefs.ControlModule;
    bcd ← Load[file
      ! BadFile, UNWIND => NULL;
	ANY => ERROR BadFile[file, "unknown Load error!"L]];
    cm ← New[bcd, switches.framelinks, FALSE
      ! BadFile, BadVersion, UNWIND => NULL;
	VersionMismatch => BEGIN SIGNAL BadVersion[name]; RESUME END;
	FileNotFound => ERROR BadFile[name, "not found!"L];
	BadCode => ERROR BadFile[name, "bad code!"L];
	ANY => ERROR BadFile[file, "unknown New error!"L]];
    DOutput.Text[" -- "L];
    DOutput.Octal[cm];
    DOutput.EOL[];
    RETURN[cm.frame]
    END;
  
  StartProg: PUBLIC PROCEDURE [gf: MachineDefs.GFHandle] =
    BEGIN
    p: PROCESS;
    IF ControlDefs.GFT[gf.gfi].frame # gf THEN
      BEGIN
      DOutput.Text[" not a global frame: "L];
      SIGNAL DebugOps.Abort
      END;
    IF gf # MachineDefs.NullGF THEN
      BEGIN 
      data.nubFrame ← gf;
      p ← FORK StartModule[LOOPHOLE[gf]];
      JOIN p;
      END;
    RETURN
    END;

  StartModule: PROCEDURE [f: PROGRAM] =
    BEGIN ENABLE ABORTED, DebugOps.Abort => CONTINUE;
    IF ~LOOPHOLE[f, MachineDefs.GFHandle].started THEN START f ELSE RESTART f;
    END;
  
  switches: Nub.Sob;
  
  InitSwitches: PUBLIC PROCEDURE RETURNS [Nub.Switches] =
    BEGIN
    switches ← [command: FALSE, internalInstall: FALSE, install: FALSE,
      framelinks: TRUE, start: TRUE, search: FALSE, trees: FALSE,
      display: FALSE, spare1: FALSE, spare2: FALSE];
    RETURN [@switches]
    END;

  ProcessSwitches: PUBLIC PROCEDURE [s: STRING] =
    BEGIN OPEN switches;
    i: CARDINAL;
    inverse: BOOLEAN ← FALSE;
    FOR i IN [0..s.length) DO
      SELECT s[i] FROM
	'c, 'C => BEGIN inverse ← FALSE; command ← TRUE END;
	'i, 'I => BEGIN inverse ← FALSE; install ← TRUE END;
	'x, 'X => BEGIN inverse ← FALSE; internalInstall ← TRUE END;
	's, 'S => IF inverse THEN inverse ← start ← FALSE ELSE start ← TRUE;
	'l, 'L => BEGIN inverse ← FALSE; framelinks ← FALSE END;
	'b, 'B =>
	  IF inverse THEN inverse ← display ← FALSE ELSE display ← TRUE;
	Ascii.ControlS => 
	  IF inverse THEN inverse ← display ← FALSE ELSE search ← TRUE;
	Ascii.ControlT =>
	  IF inverse THEN inverse ← display ← FALSE ELSE trees ← TRUE;
	'- => inverse ← TRUE;
	ENDCASE => inverse ← FALSE;
      ENDLOOP;
    END;
  
  GetToken: PUBLIC PROCEDURE [
    get: PROCEDURE RETURNS [CHARACTER], token, ext, switches: STRING] =
    BEGIN OPEN Ascii;
    s: STRING;
    c: CHARACTER;
    token.length ← ext.length ← switches.length ← 0;
    s ← token;
    WHILE (c ← get[]) # NUL DO
      SELECT c FROM
	SP, CR =>
	  IF token.length # 0 OR ext.length # 0 OR switches.length # 0 THEN RETURN;
	'. => s ← ext;
	'/ => s ← switches;
	ENDCASE => String.AppendChar[s, c];
      ENDLOOP;
    RETURN
    END;
  
END.