-- File: TroubleAltoLocked.mesa,  Last Edit: HGM  March 10, 1981  4:52 AM

DIRECTORY
  Ascii USING [CR],
  ControlDefs USING [StateVector, ControlLink],
  FrameDefs USING [MakeCodeResident, GlobalFrame],
  FrameOps USING [GetReturnLink],
  ImageDefs USING [PuntMesa],
  Mopcodes USING [zSTARTIO],
  Process USING [Detach, GetPriority, Priority, SetPriority, SetTimeout, SecondsToTicks],
  ProcessDefs USING [DisableInterrupts, EnableInterrupts],
  ProcessOps USING [CurrentPSB],
  PSBDefs,
  StreamDefs USING [StreamHandle, CleanupDiskStream],
  String USING [AppendChar, AppendString, AppendOctal],
  SDDefs USING [SD, sIOResetBits, sSwapTrap],
  FileSW USING [GetFile],
  Put USING [Line],
  TextSW USING [GetEOF, SetEOF],
  Time USING [AppendCurrent],
  TrapOps USING [ReadOTP],
  Window USING [Handle],
  Trouble USING [Bug];

TroubleAltoLocked: MONITOR
  IMPORTS
    FrameDefs, FrameOps, ImageDefs, Process, ProcessDefs, StreamDefs, String,
    Time, TrapOps, FileSW, Put, TextSW, Trouble
  EXPORTS Trouble
  SHARES ProcessDefs =  -- CurrentPSB
  BEGIN

  -- This module needs to be locked in core.

  realTrapRoutine: PROCEDURE = SDDefs.SD[SDDefs.sSwapTrap];
  okToSwap: Process.Priority ← 1;

  SetCodeTrap: PUBLIC PROCEDURE [new: Process.Priority] =
    BEGIN
    Process.SetPriority[Process.GetPriority[]];  -- be sure it is in
    Process.SetPriority[okToSwap ← new];
    END;

  -- See <Mesa>Resident.CodeTrap

  TrapCodeNotIn: PROCEDURE =
    BEGIN
    -- Breakpoints in this routine won't work.
    state: ControlDefs.StateVector;
    dest: ControlDefs.ControlLink;
    badGuy: PROCEDURE; -- so the debugger will print it for us
    ProcessDefs.DisableInterrupts[];
    state ← STATE;
    dest ← TrapOps.ReadOTP[]; -- real destination
    badGuy ← LOOPHOLE[dest];
    state.dest ← realTrapRoutine;
    state.source ← FrameOps.GetReturnLink[];
    -- The keyboard process runs at priority 5, Control-Swat and clock updater run at priority 6.  Control-Swat will generate CodeTraps because it calls various cleanup procs on the way to the debugger if nothing else is running.
    IF ProcessOps.CurrentPSB.priority # okToSwap
    AND ProcessOps.CurrentPSB.priority # 5
    AND ProcessOps.CurrentPSB.priority # 6 THEN
      BEGIN
      CheckForRecursion[];
      CodeFaultFromInterruptRoutine[badGuy];
      END;
    ProcessDefs.EnableInterrupts[];
    -- This actually transfers to the real CodeTrap in Resident routine, which uses OTPreg.  Since there is no way to reload OTPreg, you can't continue from here if you have generated another CodeTrap.
    RETURN WITH state;
    END;

  CodeFaultFromInterruptRoutine: PROCEDURE [dest: UNSPECIFIED] =
    BEGIN
    e: STRING = [100];
    String.AppendString[e, "CodeTrap from Interrupt routine, dest="L];
    String.AppendOctal[e, dest];
    Trouble.Bug[e];
    END;

  recursion: BOOLEAN ← FALSE;
  CheckForRecursion: PUBLIC PROCEDURE =
    BEGIN
    ProcessDefs.DisableInterrupts[];
    IF recursion THEN
      BEGIN
      StartIO: PROCEDURE [WORD] = MACHINE CODE BEGIN Mopcodes.zSTARTIO END;
      StartIO[SDDefs.SD[SDDefs.sIOResetBits]];  -- Kill Ethernets
      turnOffSLA[];  -- Kill SLA and PR
      ImageDefs.PuntMesa[];
      END;
    SDDefs.SD[SDDefs.sSwapTrap] ← realTrapRoutine;
    recursion ← TRUE;
    END;

  PupGlitchTrap: PUBLIC PROCEDURE [why: ERROR] =
    BEGIN
    e: STRING = [100];
    CheckForRecursion[];
    String.AppendString[e, "PupPackage Troubles, arg="L];
    String.AppendOctal[e, why];
    Trouble.Bug[e];
    END;

  turnOffSLA: PROCEDURE ← {};
  SetRamUnBooter: PUBLIC PROCEDURE [new: PROCEDURE] =
    BEGIN
    turnOffSLA ← new;
    END;

  -- This stuff lives here because it is hot and fits on the page.

  window: Window.Handle ← NIL;
  stream: StreamDefs.StreamHandle ← NIL;

  WrapTypescriptFile: PUBLIC PROCEDURE [wh: Window.Handle, pages: CARDINAL] =
    BEGIN
    window ← wh;
    stream ← FileSW.GetFile[window].s;
    Process.Detach[FORK Watcher[LONG[pages]*512]];
    END;

  Watcher: ENTRY PROCEDURE [limit: LONG CARDINAL] =
    BEGIN
    pause: CONDITION;
    afterStartup, recent, now: LONG CARDINAL;
    Process.SetTimeout[@pause, Process.SecondsToTicks[15]];
    THROUGH [0..3*4) DO WAIT pause; ENDLOOP;
    afterStartup ← TextSW.GetEOF[window];
    DO  -- forever
      WAIT pause;
      now ← TextSW.GetEOF[window];
      IF recent = now THEN LOOP;
      ForceOutTypescriptFile[];
      recent ← now;
      IF now < limit THEN LOOP;
      ResetTypescriptFile[afterStartup];
      ENDLOOP;
    END;

  ForceOutTypescriptFile: PUBLIC PROCEDURE =
    BEGIN IF stream # NIL THEN StreamDefs.CleanupDiskStream[stream]; END;

  ResetTypescriptFile: PROCEDURE [where: LONG CARDINAL] =
    BEGIN
    text: STRING = [100];
    TextSW.SetEOF[window, where];
    THROUGH [0..5) DO String.AppendChar[text, Ascii.CR]; ENDLOOP;
    Time.AppendCurrent[text];
    String.AppendString[text, "  Typescript file reset."L];
    Put.Line[window, text];
    END;

  FrameDefs.MakeCodeResident[FrameDefs.GlobalFrame[TrapCodeNotIn]];
  SDDefs.SD[SDDefs.sSwapTrap] ← TrapCodeNotIn;
  END.