-- MesaRuntime>Signals.mesa  (November 21, 1982 10:47 am by Levin)

DIRECTORY
  Environment USING [Byte],
  Frame USING [
    Alloc, Free, GetReturnFrame,GetReturnLink, MyGlobalFrame,
    MyLocalFrame, SetReturnLink],
  Mopcodes USING [zCATCH, zJ2, zJ9, zJB, zJW, zKFCB, zPORTI, zSLB],
  PrincOps USING [
    ControlLink, Frame, FrameHandle, localbase, NullFrame, NullLink,
    StateVector, BytePC],
  RuntimeInternal USING [Codebase],
  RuntimePrograms USING [],
  SDDefs USING [
    SD, sError, sErrorList, sReturnError, sReturnErrorList, sSignal,
    sSignalList, sUncaughtSignal, sUnnamedError],
  TemporarySignals USING [];

Signals: PROGRAM
  IMPORTS Frame, RuntimeInternal EXPORTS RuntimeInternal, RuntimePrograms, TemporarySignals =

BEGIN

BYTE: TYPE = Environment.Byte;

CatchPointer: TYPE = POINTER TO catch PrincOps.Frame;
CatchCall: TYPE = PROCEDURE [SIGNAL] RETURNS [ActionCode];
CatchContinue: TYPE = PROCEDURE;

ActionCode: TYPE = INTEGER;
  reject: ActionCode = 0;
  resume: ActionCode = 1;
  exit:   ActionCode = -1;

SendMsgSignal: PUBLIC SIGNAL RETURNS [UNSPECIFIED, UNSPECIFIED] = CODE;

signalling: CARDINAL = 177777B;
notSignalling: CARDINAL = 0;

--RuntimePrograms.--InitializeSignals: PUBLIC PROCEDURE [] =
  BEGIN OPEN SDDefs;
  pSD: POINTER TO ARRAY [0..0) OF UNSPECIFIED ← SD;
  pSD[sSignalList] ← SignalList;
  pSD[sSignal] ← Signal;
  pSD[sErrorList] ← ErrorList;
  pSD[sError] ← Error;
  pSD[sReturnErrorList] ← ReturnErrorList;
  pSD[sReturnError] ← ReturnError;
  pSD[sUnnamedError] ← UnnamedError;
  END;

MarkSignalFrame: PROCEDURE [value: CARDINAL] =
  MACHINE CODE BEGIN Mopcodes.zSLB, 3 --OFFSET[mark]-- END;

SignalHandler: PROCEDURE [signal: SIGNAL, message: UNSPECIFIED, informational: BOOL ← FALSE] =
  BEGIN

  SignalFrame: TYPE = POINTER TO FRAME[SignalHandler];

  frame, nextFrame: PrincOps.FrameHandle;
  target, nextTarget: PrincOps.FrameHandle;
  self: PrincOps.FrameHandle = Frame.MyLocalFrame[];
  start: PrincOps.FrameHandle;
  catchFrame: CatchPointer;
  action: ActionCode;
  unwinding: BOOLEAN;
  catchPhrase: BOOLEAN;
  catchFSIndex: BYTE;
  catchPC, exitPC: PrincOps.BytePC;
  catchState: PrincOps.StateVector;

  MarkSignalFrame[signalling];  unwinding ← FALSE;
  start ← GetFrame[self.returnlink];  target ← PrincOps.NullFrame;  
  DO
    nextFrame ← start;
    UNTIL nextFrame = target DO
    frame ← nextFrame;
    IF frame.accesslink = Frame.MyGlobalFrame[] AND frame.mark THEN
      BEGIN
      OPEN thisSignaller: LOOPHOLE[frame, SignalFrame];
      IF unwinding THEN
        BEGIN
        IF signal = thisSignaller.signal THEN
          nextTarget ← IF thisSignaller.unwinding
            THEN thisSignaller.nextTarget
            ELSE thisSignaller.nextFrame;
        IF thisSignaller.unwinding THEN
          BEGIN
          IF thisSignaller.frame = LOOPHOLE[frame.returnlink] THEN
            frame.returnlink ← [frame[thisSignaller.nextFrame]];
          Frame.Free[thisSignaller.frame];
          END;
        nextFrame ← GetFrame[frame.returnlink];
        END
      ELSE
        nextFrame ← IF signal # thisSignaller.signal THEN
            IF thisSignaller.unwinding
              THEN thisSignaller.nextFrame
              ELSE GetFrame[frame.returnlink]
          ELSE
            IF thisSignaller.unwinding
              THEN thisSignaller.nextTarget
              ELSE thisSignaller.nextFrame;
      END
    ELSE nextFrame ← GetFrame[frame.returnlink];
    IF unwinding AND nextTarget = frame THEN  nextTarget ← nextFrame;
    [catchPhrase, catchFSIndex, catchPC] ← CheckCatch[frame];
    IF catchPhrase THEN
      BEGIN
      catchFrame ← Frame.Alloc[catchFSIndex];
      catchFrame↑ ← PrincOps.Frame[
        accesslink: frame.accesslink,
        pc: catchPC,
        returnlink: [frame[self]],
        extensions: catch[unused: ,
          staticlink: frame+PrincOps.localbase, messageval: message]];
      action ← LOOPHOLE[catchFrame, CatchCall]
       [IF unwinding THEN LOOPHOLE[UNWIND] ELSE signal
        ! SendMsgSignal => RESUME[message, signal]];
      catchState ← STATE;
      SELECT action FROM
        reject =>  NULL;
        resume =>
          IF unwinding
            THEN ERROR ResumeError
            ELSE
              BEGIN
              catchState.dest ← Frame.GetReturnLink[];
              catchState.source ← PrincOps.NullLink;
              RETURN WITH catchState;
              END;
        exit =>
          IF ~informational THEN
            BEGIN
            -- catchFrame is waiting to execute its exit jump
            exitPC ← catchFrame.pc;
            Frame.Free[catchFrame];
            target ← LOOPHOLE[catchState.stk[0]-PrincOps.localbase];
            nextTarget ← nextFrame;
            unwinding ← TRUE;  message ← NIL;
            GO TO StartUnwind;
            END;
        ENDCASE;
      END;
    IF unwinding THEN
      BEGIN
      IF frame = start THEN start ← nextFrame;
      IF frame = LOOPHOLE[self.returnlink]
        THEN self.returnlink ← [frame[nextFrame]];
      Frame.Free[frame];
      END;
    REPEAT
      StartUnwind =>  NULL;
      FINISHED =>  EXIT
    ENDLOOP;
    ENDLOOP;
  SELECT TRUE FROM
    unwinding => target.pc ← exitPC;
    informational => NULL;
    ENDCASE => UncaughtSignal[message, signal, frame]; -- formerly Punted if no uncaught signal catcher
  RETURN
  END;

CheckCatch: PROCEDURE [frame: PrincOps.FrameHandle]
  RETURNS [catchPhrase: BOOLEAN, fsIndex: BYTE, pc: PrincOps.BytePC] =
  BEGIN  OPEN Mopcodes;
  code: LONG POINTER TO PACKED ARRAY [0..0) OF BYTE;
  MarkSignalFrame[notSignalling];
  code ← RuntimeInternal.Codebase[LOOPHOLE[frame.accesslink, PROGRAM]];
  pc ← [frame.pc];
  DO
    SELECT code[pc] FROM
      zCATCH => BEGIN catchPhrase ← TRUE; EXIT END;
      zPORTI => pc ← [pc + 1];
      ENDCASE => BEGIN catchPhrase ← FALSE; EXIT END
    ENDLOOP;
  IF catchPhrase THEN
    BEGIN  -- code[pc] points at zCatch
    fsIndex ← code[pc+1];
    SELECT code[pc ← [pc+2]] FROM
      zJB => pc ← [pc + 2];
      IN [zJ2..zJ9] => pc ← [pc + 1];
      zJW => pc ← [pc + 3];
      ENDCASE
    END;
  RETURN
  END;

GetFrame: PROCEDURE [link: PrincOps.ControlLink] RETURNS [PrincOps.FrameHandle] =  
  BEGIN
  -- MarkSignalFrame[notSignalling];
  DO
    IF ~link.proc THEN
      IF link.indirect THEN link ← link.link↑
      ELSE --frame link-- RETURN[link.frame]
    ELSE RETURN[PrincOps.NullFrame];
    ENDLOOP;
  END;


Signal: PROCEDURE [signal: SIGNAL, message: UNSPECIFIED] =
  BEGIN
  MarkSignalFrame[notSignalling];
  SignalHandler[signal, message];
  END;

SignalList: PROCEDURE [signal: SIGNAL, message: POINTER TO UNSPECIFIED] =
  BEGIN
  MarkSignalFrame[notSignalling];
  SignalHandler[signal, message ! UNWIND => Frame.Free[message]];
  Frame.Free[message];
  RETURN
  END;

-- Provisional informational signal logic --

InformationalSignal: PUBLIC PROCEDURE [signal: SIGNAL] =
  BEGIN
  MarkSignalFrame[notSignalling];
  SignalHandler[signal, -1, TRUE];
  END;


ResumeError: PUBLIC SIGNAL = CODE;

Error: PROCEDURE [signal: SIGNAL, message: UNSPECIFIED] =
  BEGIN
  MarkSignalFrame[notSignalling];
  SignalHandler[signal, message];
  ERROR ResumeError
  END;

ErrorList: PROCEDURE [signal: SIGNAL, message: POINTER TO UNSPECIFIED] =
  BEGIN
  MarkSignalFrame[notSignalling];
  SignalHandler[signal, message !
    UNWIND => Frame.Free[message]];
  Frame.Free[message];  
  ERROR ResumeError
  END;

ReturnError: PROCEDURE [signal: SIGNAL, message: UNSPECIFIED] =
  BEGIN
  caller: PrincOps.FrameHandle = Frame.GetReturnFrame[];
  Frame.SetReturnLink[caller.returnlink];
  MarkSignalFrame[notSignalling];
  SignalHandler[signal, message !
    UNWIND => Frame.Free[caller]];
  Frame.Free[caller];  
  ERROR ResumeError
  END;

ReturnErrorList: PROCEDURE [signal: SIGNAL, message: POINTER TO UNSPECIFIED] =
  BEGIN
  caller: PrincOps.FrameHandle = Frame.GetReturnFrame[];
  Frame.SetReturnLink[caller.returnlink];
  MarkSignalFrame[notSignalling];
  SignalHandler[signal, message !
    UNWIND => { Frame.Free[caller]; Frame.Free[message]; }];
  Frame.Free[caller];  
  Frame.Free[message];  
  ERROR ResumeError
  END;

UnnamedError: PROCEDURE =
  BEGIN
  MarkSignalFrame[notSignalling];
  SignalHandler[LOOPHOLE[-1], -1];
  ERROR ResumeError
  END;

UncaughtSignal: PROCEDURE [msg, signal: UNSPECIFIED, frame: PrincOps.FrameHandle] =
  MACHINE CODE BEGIN Mopcodes.zKFCB, SDDefs.sUncaughtSignal END;

END.


LOG

August 7, 1978  8:57 AM   Sandman   Got Codebase from RuntimeInternal instead of CodebaseDefs.
August 30, 1978  2:06 PM   Sandman   Removed TrapDefs and PuntMesa.
March 13, 1979  3:15 PM   McJones   Mesa 5.
April 4, 1979  11:02 AM   McJones   Added (conditional) BytePC logic.
September 21, 1979  2:37 PM   McJones   Removed (conditional) BytePC logic.
May 3, 1980  11:07 AM   Forrest   Mesa 6.0 Conversion.
January 15, 1981  4:09 PM   Knutsen   InitizeSignals[].
February 4, 1981  1:05 PM   Knutsen   PrincOps fields changed names.
November 21, 1982 10:47 am	Levin	Add provisional informational signal stuff.