-- Traps.mesa  (August 26, 1982 11:26 am by Levin)

DIRECTORY
  Environment USING [Byte],
  Frame USING [Free, GetReturnFrame, GetReturnLink,
        MyLocalFrame, SetReturnFrame, SetReturnLink],
  Mopcodes USING [zDESCBS, zLDIV, zPORTI, zPORTO, zSFC],
  ProcessorFace USING [SetMP],
  PilotMP USING [cEarlyTrap, Code],
  PrincOps USING [
    AV, AVHandle, ControlLink,
    ControlModule, Frame, FrameCodeBase, FrameHandle,
    GlobalFrameHandle, LargeReturnSlot, MainBodyIndex,
    NullControl, NullFrame, NullGlobalFrame, Port,
    PortHandle, PrefixHandle, StateVector, SVPointer],
  PrincOpsRuntime USING [GetFrame, GFT],
  Runtime USING [ValidateGlobalFrame],
  RuntimeInternal USING [Codebase],
  RuntimePrograms USING [],
  SDDefs USING [
    sBoundsFault, sControlFault, SD, sDivideCheck, sError, sHardwareError, sPointerFault,
    sRestart, sSignal, sStackError, sStart, sSwapTrap, sUnbound, sWakeupError, sZeroDivisor],
  Trap USING [ReadOTP];

Traps: PROGRAM
  IMPORTS Frame, PrincOpsRuntime, ProcessorFace, RuntimeInternal, Trap, Runtime
  EXPORTS Runtime, RuntimePrograms =

  BEGIN


  ControlLink: TYPE = PrincOps.ControlLink;
  FrameHandle: TYPE = PrincOps.FrameHandle;
  GlobalFrameHandle: TYPE = PrincOps.GlobalFrameHandle;
  StateVector: TYPE = PrincOps.StateVector;

  AV: PrincOps.AVHandle = PrincOps.AV;

  LargeReturnSlot: CARDINAL = PrincOps.LargeReturnSlot;


  BoundsFault: PUBLIC ERROR = CODE;
  ControlFault: PUBLIC SIGNAL [source: FrameHandle] RETURNS [ControlLink] = CODE;
  DivideCheck: PUBLIC SAFE SIGNAL = CODE;
  HardwareError: PRIVATE ERROR = CODE;
  LinkageFault: PUBLIC ERROR = CODE;
  PointerFault: PUBLIC ERROR = CODE;
  PortFault: PUBLIC ERROR = CODE;
  StartFault: PUBLIC SIGNAL [dest: PROGRAM] = CODE;
  StackError: PUBLIC ERROR = CODE;
  UnboundProcedure: PUBLIC SIGNAL [dest: ControlLink] RETURNS [ControlLink] =
    CODE;
  WakeupError: ERROR = CODE;
  ZeroDivisor: PUBLIC SAFE SIGNAL = CODE;

  CodeBytesPtr: TYPE = LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte;


  -- Non-trap type procedures:


  BumpPC: PROCEDURE [f: FrameHandle, i: INTEGER] = INLINE {f.pc ← [f.pc + i]};


  GetCodeBytes: PROC [frame: FrameHandle] RETURNS [CodeBytesPtr] = INLINE {
    RETURN[LOOPHOLE[frame.accesslink.code.longbase]]};


  MainBody: PROC [GlobalFrameHandle] RETURNS [ControlLink] = MACHINE CODE
    BEGIN Mopcodes.zDESCBS, PrincOps.MainBodyIndex END;



  --RuntimePrograms.--Restart: PUBLIC PROC [dest: GlobalFrameHandle] =
    BEGIN
    stops: BOOLEAN;
    frame: FrameHandle;
    IF dest = PrincOps.NullGlobalFrame THEN ERROR StartFault[LOOPHOLE[dest, PROGRAM]];
    IF ~dest.started THEN Start[[frame[dest]]];
    stops ← LOOPHOLE[RuntimeInternal.Codebase[LOOPHOLE[dest, PROGRAM]],
                           LONG PrincOps.PrefixHandle].header.info.stops;
    IF ~stops THEN ERROR StartFault[LOOPHOLE[dest, PROGRAM]];
    IF (frame ← dest.global[0]) # PrincOps.NullFrame THEN
      BEGIN
      frame.returnlink ← Frame.GetReturnLink[];
      Frame.SetReturnFrame[frame]
      END
    END;



  --RuntimePrograms.--Start: PUBLIC PROCEDURE [cm: PrincOps.ControlModule] =
    BEGIN
    CM: PROGRAM;
    FramesStarted: PROCEDURE RETURNS [BOOLEAN] = INLINE
      BEGIN OPEN PrincOpsRuntime;
      dest: ControlLink = LOOPHOLE[Runtime.ValidateGlobalFrame];
      RETURN[GetFrame[GFT[dest.gfi]].started];
      END;
    state: StateVector;
    state ← STATE;
    CM ← LOOPHOLE[cm];
    IF ~cm.multiple THEN
      BEGIN OPEN f: cm.frame;
      IF @f = PrincOps.NullGlobalFrame OR f.started THEN ERROR StartFault[CM];
      -- don't start trap module Frames early!!
      IF FramesStarted[] THEN Runtime.ValidateGlobalFrame[@f];
      StartCM[f.global[0], @f, @state];
      IF ~f.started THEN {f.started ← TRUE; StartWithState[@f, @state]; }
      ELSE IF state.stkptr # 0 THEN SIGNAL StartFault[CM];
      END
    ELSE
      BEGIN
      StartCM[cm, NIL, NIL];
      IF state.stkptr # 0 THEN SIGNAL StartFault[CM];
      END;
    RETURN
    END;



  StartCM: PROCEDURE [
    cm: PrincOps.ControlModule, frame: GlobalFrameHandle, state: PrincOps.SVPointer] =
    BEGIN
    Call: PROCEDURE [ControlLink] = MACHINE CODE BEGIN Mopcodes.zSFC END;
    SELECT TRUE FROM
      cm = PrincOps.NullControl => RETURN;
      cm.multiple =>
        BEGIN
        length: CARDINAL;
        cm.multiple ← FALSE;
        IF (length ← cm.list.nModules) = 0 THEN RETURN;
        cm.list.nModules ← 0;
        FOR i: CARDINAL IN [0..length) DO
          StartCM[[frame[cm.list.frames[i]]], frame, state]; ENDLOOP;
        Frame.Free[cm.list];
        END;
      cm.frame.started => RETURN;
      ENDCASE =>
        BEGIN
        control: PrincOps.ControlModule ← cm.frame.global[0];
        IF control # cm THEN StartCM[control, frame, state];
        IF ~cm.frame.started THEN
          BEGIN
          cm.frame.started ← TRUE;
          IF frame # cm.frame THEN Call[MainBody[cm.frame]]
          ELSE StartWithState[frame, state];
          END;
        END;
    RETURN
    END;



  StartWithState: PROC [frame: GlobalFrameHandle, state: PrincOps.SVPointer] =
    BEGIN
    s: StateVector ← state↑;
    retFrame: FrameHandle ← Frame.GetReturnLink[].frame;
    s.dest ← MainBody[frame];
    Frame.SetReturnLink[s.source ← retFrame.returnlink];  -- allows stack trace if RETURN WITH s faults.
    Frame.Free[retFrame];
    RETURN WITH s;
    END;



  -- Trap Handlers:



  BoundsFaultTrap: PROC =
    BEGIN
    frame: PrincOps.FrameHandle;
    state: RECORD [a: UNSPECIFIED, v: StateVector];
    state.v ← STATE;
    state.v.dest ← ControlLink[frame[frame ← Frame.GetReturnFrame[]]];
    BumpPC[frame, 1];
    state.v.stkptr ← state.v.stkptr - 1;
    ERROR BoundsFault;  -- pc is advanced on this trap
    -- RETURN WITH state.v
    END;



  CodeTrap: PROC =
    BEGIN OPEN PrincOpsRuntime;
    dest: ControlLink;
    state: StateVector;
    frame: GlobalFrameHandle;
    state ← STATE;
    dest ← Trap.ReadOTP[];
    state.dest ← Frame.GetReturnLink[];
    DO
      IF dest.proc THEN {frame ← GetFrame[GFT[dest.gfi]]; EXIT}
      ELSE
        IF dest.indirect THEN dest ← dest.link↑
        ELSE {frame ← dest.frame.accesslink; EXIT}; -- frame

      ENDLOOP;
    IF ~frame.started THEN Start[[frame[frame]]];
    frame.code.out ← FALSE;
    RETURN WITH state
    END;



  DivideCheckTrap: PROC =
    BEGIN
    frame: PrincOps.FrameHandle;
    state: RECORD [a: UNSPECIFIED, v: StateVector];
    state.v ← STATE;
    state.v.dest ← ControlLink[frame[frame ← Frame.GetReturnFrame[]]];
    BumpPC[frame, 1];
    state.v.stkptr ← state.v.stkptr - 2;
    SIGNAL DivideCheck; -- pc is advanced on this trap
    RETURN WITH state.v
    END;



  HardwareErrorTrap: PROC =
    BEGIN
    state: RECORD [a, b: UNSPECIFIED, v: StateVector];
    state.v ← STATE;
    ERROR HardwareError
    END;



  PointerFaultTrap: PROC =
    BEGIN
    frame: PrincOps.FrameHandle;
    state: RECORD [a: UNSPECIFIED, v: StateVector];
    state.v ← STATE;
    state.v.dest ← ControlLink[frame[frame ← Frame.GetReturnFrame[]]];
    BumpPC[frame, 1];
    ERROR PointerFault; -- pc is advanced on this trap
    --RETURN WITH state.v
    END;



  StackErrorTrap: PROC =
    BEGIN
    state: RECORD [a: UNSPECIFIED, v: StateVector];
    foo: BOOLEAN;
    state.v ← STATE;
    foo ← TRUE; -- The garbage with foo is so all the code for the next statement
    -- looks like it is in this procedure
    -- to the debugger (when it looks in the fine grain table)
    IF foo THEN ERROR StackError
    END;



  OpcodeLengths: PACKED ARRAY [0..255] OF [0..3] =
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1,
      2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1,
      1, 1, 1, 1, 2, 3, 2, 2, 2, 3, 0, 0, 0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 3, 3,
      2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 3, 2, 3, 1, 1, 2, 2, 2, 2, 2, 2,
      2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 0, 0,
      0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1,
      1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1,
      1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
      1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
      1, 1, 2, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 2, 2,
      0, 2, 2, 1, 0];



  UnboundProcedureTrap: PROC =
    BEGIN
    sourceOp: Environment.Byte;
    frame: PrincOps.FrameHandle;
    dest: ControlLink;
    state: StateVector;
    state ← STATE;
    dest ← Trap.ReadOTP[];
    state.dest ← ControlLink[frame[frame ← Frame.GetReturnFrame[]]];
    BumpPC[frame, OpcodeLengths[sourceOp ← GetCodeBytes[frame][frame.pc]]];
    IF sourceOp = Mopcodes.zSFC OR sourceOp = Mopcodes.zPORTO THEN
      state.stkptr ← state.stkptr - 1;
    [] ← SIGNAL UnboundProcedure[dest];
    RETURN WITH state
    END;



  WakeupErrorTrap: PROC =
    BEGIN
    state: RECORD [a, b: UNSPECIFIED, v: StateVector];
    state.v ← STATE;
    ERROR WakeupError
    END;



  ZeroDivisorTrap: PROC =
    BEGIN
    frame: PrincOps.FrameHandle;
    state: RECORD [a: UNSPECIFIED, v: StateVector];
    state.v ← STATE;
    state.v.dest ← ControlLink[frame[frame ← Frame.GetReturnFrame[]]];
    state.v.stkptr ←
      state.v.stkptr -
        (IF GetCodeBytes[frame][frame.pc] = Mopcodes.zLDIV THEN 2 ELSE 1);
    BumpPC[frame, 1];
    SIGNAL ZeroDivisor; -- pc is advanced on this trap
    RETURN WITH state.v
    END;



  ControlFaultTrap: PROC =
    BEGIN
    errorStart, savedState: StateVector;
    sourceOp: Environment.Byte;
    PORTI: PROC = MACHINE CODE BEGIN Mopcodes.zPORTI END;
    p, q: PrincOps.PortHandle;
    sourceFrame, self: FrameHandle;
    source: ControlLink;

    savedState ← STATE;
    self ← Frame.MyLocalFrame[];
    sourceFrame ← self.returnlink.frame;
    source ← Trap.ReadOTP[];
    sourceOp ← GetCodeBytes[sourceFrame][sourceFrame.pc];
    BumpPC[sourceFrame, OpcodeLengths[sourceOp]];
    IF sourceOp = Mopcodes.zPORTO THEN
      BEGIN
      savedState.stkptr ← savedState.stkptr - 1;
      p ← source.port;
      q ← p.dest.port;
      IF q = NIL THEN errorStart.stk[0] ← LinkageFault
      ELSE
        BEGIN
        q↑ ← PrincOps.Port[links[PrincOps.NullFrame, [indirect[port[p]]]]];
        errorStart.stk[0] ← PortFault
        END;
      errorStart.stk[1] ← 0; -- message
      errorStart.instbyte ← 0;
      errorStart.stkptr ← 2;
      errorStart.source ← sourceFrame.returnlink;
      -- lets UNWIND skip trapping frame
      errorStart.dest ← SDDefs.SD[SDDefs.sError];
      IF savedState.stkptr = 0 THEN RETURN WITH errorStart --  RESPONDING port

      ELSE
        BEGIN
        p.frame ← self;
        TRANSFER WITH errorStart;
        PORTI[];
        p.frame ← sourceFrame;
        savedState.stk[savedState.stkptr + 1] ←
            savedState.source ← ControlLink[indirect[port[p]]];
        savedState.stk[savedState.stkptr] ← savedState.dest ← p.dest;
        RETURN WITH savedState
        END
      END
    ELSE -- not a port call
      BEGIN
      Punt: PROC [c: PilotMP.Code] = INLINE
        BEGIN ProcessorFace.SetMP[c]; DO ENDLOOP END;
      IF sourceOp = Mopcodes.zSFC THEN savedState.stkptr ← savedState.stkptr - 1;
      IF SDDefs.SD[SDDefs.sSignal] = 0 THEN Punt[PilotMP.cEarlyTrap];
      -- avoid loop
      savedState.dest ← ControlLink[frame[sourceFrame]];
      [] ← SIGNAL ControlFault[sourceFrame];
      RETURN WITH savedState -- to press on

      END
    END;



  -- XFER break tracing implementation:

  -- The following code is thought not to work.  Furthermore, we do not want it permanently resident.  Thus it is retired, at least for now.

  -- continueTracing: BOOLEAN;



  -- ++RuntimeInternal.++StartTrace: PUBLIC PROC [
  --    loc: POINTER, val: UNSPECIFIED, mask: WORD, equal: BOOLEAN] =
  --    BEGIN
  --    state: PrincOps.StateVector;
  --    lval: UNSPECIFIED;
  --    continueTracing ← TRUE;
  --    state ← STATE;
  --    state.dest ← Frame.GetReturnLink[];
  --    SDDefs.SD[SDDefs.sXferTrap] ← state.source ← Frame.MyLocalFrame[];
  --    ProcessInternal.DisableInterrupts[];
  --    DO
  --      lval ← Inline.BITAND[loc↑, mask];
  --      IF (IF equal THEN val = lval ELSE val # lval) THEN
  --        RuntimeInternal.WorryCallDebugger["TraceTrap"L];
  --      IF ~continueTracing THEN {
  --        SDDefs.SD[SDDefs.sXferTrap] ← 0;
  --        ProcessInternal.EnableInterrupts[];
  --        RETURN WITH state}
  --      ELSE {
  --        XferTrap.WriteXTS[skip1];
  --        ProcessInternal.EnableInterrupts[];
  --        TRANSFER WITH state};
  --      ProcessInternal.DisableInterrupts[];
  --      state ← STATE;
  --      XferTrap.WriteXTS[off];
  --      state.dest ← Frame.GetReturnLink[];
  --      state.source ← 0;
  --      ENDLOOP
  --    END;

  --   ++RuntimeInternal.++StopTrace: PUBLIC PROC = {continueTracing ← FALSE};



  Initialize: PROC =
    -- (This code only needs to be initially resident.)
    BEGIN OPEN SDDefs;
    pSD: POINTER TO ARRAY [0..0) OF UNSPECIFIED ← SD;
    pSD[sBoundsFault] ← BoundsFaultTrap;
    pSD[sControlFault] ← ControlFaultTrap;
    pSD[sDivideCheck] ← DivideCheckTrap;
    pSD[sHardwareError] ← HardwareErrorTrap;
    pSD[sPointerFault] ← PointerFaultTrap;
    pSD[sRestart] ← Restart;
    pSD[sStackError] ← StackErrorTrap;
    pSD[sStart] ← Start;
    pSD[sSwapTrap] ← CodeTrap;
    pSD[sUnbound] ← UnboundProcedureTrap;
    pSD[sWakeupError] ← WakeupErrorTrap;
    pSD[sZeroDivisor] ← ZeroDivisorTrap;
    END;



  -- Main body

  Initialize[];



  END.





LOG

(For earlier entries, please see version archived with Pilot 4.0.)

April 10, 1980  5:16 PM	Knutsen	Made compatible with changed ResidentMemory procedure names; set mds in MemorySwap.

April 29, 1980  9:38 PM	Forrest	Moved MemorySwap and callers to Traps, MiscDeviceCleanups to OISProcessorHeadD0.  Rearrange and eliminate MONITOR.

May 3, 1980  1:28 PM	Forrest	Mesa 6.0 (non-Dandelion) conversion

June 23, 1980  5:46 PM	McJones	OISProcessorFace=>ProcessorFace; no longer disable interrupts when getting PrincOps trap parameters.

September 2, 1980  1:56 PM	Johnsson	New XferTrap semantics.

September 29, 1980  9:30 AM	Johnsson	all princops traps occur with state restored to beginning of instruction.

October 3, 1980  1:54 PM	Forrest	Moved Unimplemented Trap to Processor Head.

December 2, 1980  10:43 AM	Knutsen	Retired AllocTrap backstop allocator and its storage, replaced with bear trap.  Fix return link in StartWithState.

January 19, 1981  9:32 AM	Knutsen	Frame fault handler, FrameSize, Codebase, GetTableBase moved to FrameImpl.  Retired StartTrace, StopTrace.

February 5, 1981  6:01 PM	Knutsen	PrincOps fields changed names.

August 26, 1982 11:26 am	Levin	Make BoundsFault and PointerFault be ERRORs.