-- XDIDebug.Mesa  Edited by: Bruce on September 2, 1980  11:22 AM

DIRECTORY
  BcplOps: FROM "BcplOps" USING [BcplJSR],
  CommandList: FROM "commandlist",
  Commands: FROM "commands" USING [WriteError, Prompt],
  ControlDefs: FROM "controldefs" USING [
    ControlLink, FieldDescriptor, FrameHandle, GlobalFrameHandle,
    NullFrame],
  CoreSwapDefs: FROM "coreswapdefs" USING [
    BBHandle, CallDP, DebugParameter, ExternalStateVector, StartDP,
    SwapReason, SVPointer, UBBPointer, UserBreakBlock, VersionID],
  DebugOps: FROM "debugops" USING [Abort, Proceed, Quit],
  DOutput: FROM "doutput" USING [Char, EOL, Line, Octal, Text],
  FrameDefs: FROM "framedefs" USING [LockCode, UnlockCode],
  FrameOps: FROM "frameops" USING [
    GetReturnFrame, GetReturnLink, MyLocalFrame, SetReturnFrame,
    SetReturnLink],
  ImageDefs: FROM "imagedefs" USING [AbortMesa, StopMesa, UserCleanupProc],
  Internal: FROM "internal",
  KeyDefs: FROM "keydefs" USING [Keys],
  LoadStateOps: FROM "loadstateops" USING [state],
  MachineDefs: FROM "machinedefs" USING [BYTE, FHandle, FSHandle, GFHandle, StateVector],
  Mopcodes: FROM "mopcodes" USING [zRFS],
  Nub: FROM "nub" USING [BadFile, BadVersion],
  ProcessDefs: FROM "processdefs" USING [DisableInterrupts, EnableInterrupts],
  SDDefs: FROM "sddefs" USING [sBreakBlock, sCallDebugger, sCoreSwap, SD],
  SegmentDefs: FROM "segmentdefs" USING [GetFileSegmentDA],
  State: FROM "state" USING [GetGS, GSHandle],
  SwapperOps: FROM "SwapperOps" USING [systemTable],
  TajoOps: FROM "TajoOps" USING [Notifier],
  TrapDefs: FROM "trapdefs" USING [ParityError, PhantomParityError],
  TajoMisc USING [SetState];

XDIDebug: PROGRAM
  IMPORTS BcplOps, Commands, DebugOps, DOutput, FrameDefs, FrameOps,
    ImageDefs, LoadStateOps, Nub, ProcessDefs, SegmentDefs,
    State, SwapperOps, TajoOps, TrapDefs, TajoMisc
  EXPORTS CoreSwapDefs, Internal, Nub =
  BEGIN OPEN CoreSwapDefs;
  
  Error: PUBLIC TYPE = CommandList.Error;
  FHandle: TYPE = MachineDefs.FHandle;

  badFrame: PUBLIC FHandle ← NIL;
  level: CARDINAL ← 1;
  data: State.GSHandle ← State.GetGS[];

  IncLevel: PROCEDURE =
    BEGIN
    data.nubLevel ← level ← level + 1;
    data.inNub ← level > 1;
    END;

  DecLevel: PROCEDURE =
    BEGIN
    data.nubLevel ← level ← level - 1;
    data.inNub ← level > 1;
    END;

  DebugCommand: PUBLIC PROCEDURE =
    BEGIN OPEN Commands;
    IncLevel[];
    DO
      BEGIN
      Commands.Prompt[];
      TajoOps.Notifier[FALSE !
	UNWIND => DecLevel[];
	ABORTED => CONTINUE;
	DebugOps.Abort => CONTINUE;
	Nub.BadFile =>
	  BEGIN OPEN DOutput;
	  WriteError[file]; Text[badname]; Text[": "L]; Text[reason]; CONTINUE
	  END;
	Nub.BadVersion --[badname: STRING] --=> 
	  BEGIN
	  WriteError[file];
	  DOutput.Text[badname];
	  WriteError[diffver,FALSE];
	  RESUME
	  END;
	DebugOps.Quit => 
	  BEGIN
	  IF level # 1 THEN GOTO abort;
	  WriteError[exit, FALSE];
	  --fix this to get confirm[];
	  ImageDefs.StopMesa[]
	  END;
	DebugOps.Proceed => EXIT];
      EXITS
	abort =>
	  BEGIN
	  DecLevel[];
	  SIGNAL DebugOps.Abort;
	  END;
      END;
      ENDLOOP;
    DecLevel[];
    RETURN
    END;

  ProcessBreakpoint: PUBLIC PROCEDURE [s: SVPointer] =
    BEGIN -- called by BRK trap handler in resident code
    inst: MachineDefs.BYTE;
    swap: BOOLEAN;
    [inst, swap] ← DoBreakpoint[s];
    IF swap THEN
      BEGIN
      FrameDefs.LockCode[s.dest];
      CoreSwap[breakpoint, s];
      FrameDefs.UnlockCode[s.dest];
      END
    ELSE s.instbyte ← inst;  --replant the instruction and go on
    RETURN
    END;

  DoBreakpoint: PROCEDURE [s: SVPointer] RETURNS [MachineDefs.BYTE, BOOLEAN] =
    BEGIN OPEN SDDefs;
    ubb: UBBPointer;
    bba: BBHandle = SD[sBreakBlock];
    i: CARDINAL;
    l: FHandle ← s.dest;
    FOR i IN [0..bba.length) DO
      ubb ← @bba.blocks[i];
      IF ubb.frame = l.accesslink AND ubb.pc = l.pc THEN
	IF TrueCondition[ubb, s.source] THEN EXIT
	ELSE RETURN[ubb.inst, FALSE];
      ENDLOOP;
    RETURN[0, TRUE];
    END;

  TrueCondition: PROCEDURE [ubb: UBBPointer, base: FHandle] RETURNS [BOOLEAN] =
    BEGIN --decide whether to take the breakpoint
    fd: ControlDefs.FieldDescriptor;
    left, right: UNSPECIFIED;
    IF ubb.counterL THEN
      IF (ubb.ptrL ← ubb.ptrL + 1) = ubb.ptrR THEN
	BEGIN ubb.ptrL ← LOOPHOLE[0]; RETURN[TRUE]; END
      ELSE RETURN[FALSE];
    fd ← [offset: 0, posn: ubb.posnL, size: ubb.sizeL];
    left ← IF ~ubb.localL THEN ReadField[ubb.ptrL, fd]
    ELSE ReadField[(base + LOOPHOLE[ubb.ptrL, CARDINAL]), fd];
    IF ~ubb.immediateR THEN 
      BEGIN
      fd ← [offset: 0, posn: ubb.posnR, size: ubb.sizeR];
      right ← IF ~ubb.localR THEN ReadField[ubb.ptrR, fd] 
      ELSE ReadField[(base + LOOPHOLE[ubb.ptrR, CARDINAL]), fd];
      END
    ELSE right ← ubb.ptrR;
    RETURN[SELECT ubb.relation FROM
      lt  => left < right,
      gt  => left > right,
      eq  => left = right,
      ne  => left # right,
      le  => left <= right,
      ge  => left >= right,
      ENDCASE => FALSE]
    END;

  ReadField: PROCEDURE [POINTER, ControlDefs.FieldDescriptor] RETURNS [UNSPECIFIED] =
    MACHINE CODE BEGIN Mopcodes.zRFS END;

  NumberBlocks: CARDINAL = 5;

  SwatBreak: PUBLIC PROCEDURE [s: SVPointer] =
    BEGIN OPEN ControlDefs, BcplOps;
    break: RECORD[a,b: WORD];
    break ← [77400B, 1400B];
    s.instbyte ← BcplJSR[JSR, @break, 0];
    RETURN
    END;

  Interrupt: PUBLIC PROCEDURE =
    BEGIN -- called by BRK trap handler in resident code
    baz: RECORD [a,b: CARDINAL, state: MachineDefs.StateVector];
    baz.state ← STATE;
    baz.state.dest ← FrameOps.MyLocalFrame[];
    CoreSwap[breakpoint, @baz.state];
    END;

  Catcher: PUBLIC PROCEDURE [msg, signal: UNSPECIFIED, f: FHandle] =
    BEGIN OPEN ControlDefs;
    SignallerGF: GlobalFrameHandle;
    state: MachineDefs.StateVector;
    frame: FrameHandle;
    SignallerGF ← FrameOps.GetReturnFrame[].accesslink;
    state.stk[0] ← msg;
    state.stk[1] ← signal;
    state.stkptr ← 0;
    -- the call stack below here is: Signaller, [Signaller,] offender
    state.dest ← frame ← FrameOps.GetReturnFrame[].returnlink.frame;
    IF frame.accesslink = SignallerGF THEN state.dest ← frame.returnlink;
    BEGIN
      CoreSwap[uncaughtsignal, @state
	! DebugOps.Abort => GOTO Abort];
    EXITS
      Abort =>
	IF signal = ABORTED THEN {BackStop[f]; ERROR KillThisTurkey} ELSE ERROR ABORTED;
    END;
    RETURN
    END;
  
  BackStop: PUBLIC PROCEDURE [root: FHandle] =
    BEGIN OPEN FrameOps;
    endProcess: ControlDefs.ControlLink ← root.returnlink;
    caller: PROCEDURE = LOOPHOLE[GetReturnLink[]];
    root.returnlink ← LOOPHOLE[MyLocalFrame[]];
    SetReturnFrame[ControlDefs.NullFrame];
    caller[ ! KillThisTurkey => CONTINUE];
    SetReturnLink[endProcess];
    RETURN
    END;
  
  KillThisTurkey: SIGNAL = CODE;

CallTheDebugger: PROCEDURE [s: STRING] =
  BEGIN -- user's entry point to debugger
  state: MachineDefs.StateVector;
  filler0, filler1: CARDINAL;
  state ← STATE;
  state.stk[0] ← s;
  state.stkptr ← 1;
  state.dest ← FrameOps.GetReturnLink[];
  CoreSwap[explicitcall, @state];
  IF FALSE THEN filler0 ← filler1 ← 0;
  RETURN
  END;

  -- The non-Swapping information handlers

  ShowBreak: PUBLIC PROCEDURE [s: SVPointer] =
    BEGIN
    DOutput.EOL[];
    DOutput.Text["*** interrupt *** "L];
    DOutput.Char['[];
    DOutput.Octal[badFrame ← FrameOps.MyLocalFrame[]];
    DOutput.Char[']];
    [] ← TajoMisc.SetState[on];
    DebugCommand[];
    END;

  ShowInterrupt: PUBLIC PROCEDURE =
    BEGIN
    RETURN
    END;

  ShowSignal: PUBLIC PROCEDURE [msg, signal: UNSPECIFIED, f: FHandle] =
    BEGIN OPEN DOutput;
    DOutput.EOL[];
    SELECT signal FROM
      TrapDefs.PhantomParityError, TrapDefs.ParityError =>
	BEGIN
	IF signal = TrapDefs.PhantomParityError THEN Text["Phantom "L];
	Text["Parity Error"L];
	IF signal = TrapDefs.ParityError THEN
	  BEGIN
	  Text[" at "L];
	  Octal[msg];
	  END;
	Line[" ... Proceeding! "L];
	RETURN;
	END;
      ENDCASE;
    Text["*** uncaught SIGNAL "L]; Octal[signal];
    Text[", msg = "L]; Octal[msg];
    Char[' ]; Char['[];
    Octal[badFrame ← FrameOps.MyLocalFrame[]];
    Char[']];
    [] ← TajoMisc.SetState[on];
    DebugCommand[];
    END;

  GetDebuggerNub: PUBLIC PROC [STRING] = {badFrame ← NIL; DebugCommand[]};

  PauseAtDebuggerNub: PUBLIC PROCEDURE [STRING] =
    BEGIN OPEN SDDefs;
    BEGIN ENABLE UNWIND => SD[sCallDebugger] ← PauseAtDebuggerNub;
    SD[sCallDebugger] ← CallTheDebugger;
    DebugCommand[];
    SD[sCallDebugger] ← PauseAtDebuggerNub;
    END;
    END;

  -- The core swapper

  Quit: SIGNAL = CODE;

  parmstring: STRING ← [40];

  CoreSwap: PUBLIC PROCEDURE [why: SwapReason, sp: SVPointer] =
    BEGIN OPEN BcplOps;
      e: ExternalStateVector;
      seg: MachineDefs.FSHandle ← LoadStateOps.state;
      DP: DebugParameter;
      decode: PROCEDURE RETURNS [BOOLEAN] =
	BEGIN OPEN ControlDefs; -- decode the SwapReason
	f: MachineDefs.GFHandle;
	lsv: MachineDefs.StateVector;
	SELECT e.reason FROM
	  proceed, resume => RETURN[TRUE];
	  call =>
	    BEGIN 
	    lsv ← LOOPHOLE[e.parameter, CallDP].sv;
	    lsv.source ← FrameOps.MyLocalFrame[];
	    TRANSFER WITH lsv;
	    lsv ← STATE;
	    LOOPHOLE[e.parameter, CallDP].sv ← lsv;
	    why ← return;
	    END;
	  start =>
	    BEGIN
	    f ← LOOPHOLE[e.parameter, StartDP].frame;
	    IF ~f.started THEN START LOOPHOLE[f, PROGRAM] ELSE RESTART f;
	    why ← return;
	    END;
	  quit => SIGNAL Quit;
	  kill => ImageDefs.AbortMesa[];
	  showscreen =>
	    BEGIN OPEN KeyDefs;
	    UNTIL Keys.Spare3 = down OR Keys.FR5 = down DO NULL ENDLOOP;
	    why ← return;
	    END;
	  ENDCASE =>
	    BEGIN
	    RETURN [TRUE];
	    END;
	RETURN [FALSE]
	END;

      -- Body of CoreSwap

      e.state ← sp;
      e.drumFile ← data.selfFH; -- filehandle for State.selfFP

      DP.string ← parmstring;
      e.versionident ← CoreSwapDefs.VersionID;
      e.parameter ← @DP;
      e.tables ← @SwapperOps.systemTable;
      e.loadstateCFA.fp ← seg.file.fp;
      e.loadstateCFA.fa ← [
	page: seg.base, byte: 0, da: SegmentDefs.GetFileSegmentDA[seg]];
      e.lspages ← seg.pages;
      e.bitmap ← NIL;
      e.bitmapPages ← 0;
      e.fill ← ALL[0];

      DO
	e.reason ← why;
	ImageDefs.UserCleanupProc[OutLd];
	ProcessDefs.DisableInterrupts[];
	DoSwap[@e];
	ProcessDefs.EnableInterrupts[];
	ImageDefs.UserCleanupProc[InLd];
	IF decode[ !
	  ABORTED, DebugOps.Abort => IF e.level>0 THEN {why ← return; CONTINUE};
	  Quit => GOTO abort] THEN EXIT
	REPEAT abort => SIGNAL DebugOps.Abort;
	ENDLOOP;

      RETURN
      END;

  Stop: PROCEDURE = BEGIN ImageDefs.StopMesa[] END;

  DoSwap: PORT [POINTER TO CoreSwapDefs.ExternalStateVector];
  P: TYPE = MACHINE DEPENDENT RECORD [in, out: UNSPECIFIED]; -- PORT

  LOOPHOLE[DoSwap,P] ← [in: 0, out: SDDefs.SD[SDDefs.sCoreSwap]];

  END.