-- Signaller.Mesa  Edited by Sandman on May 22, 1980 11:11 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoDefs USING [BYTE],
  ControlDefs USING [
    ControlLink, Frame, FrameHandle, GlobalFrameHandle, InstWord, localbase,
    NullFrame, StateVector, WordPC],
  FrameDefs USING [SwapInCode],
  FrameOps USING [
    Alloc, Free, GetReturnLink, MyGlobalFrame, MyLocalFrame, ReleaseCode],
  ImageDefs USING [PuntMesa],
  Mopcodes USING [zCATCH, zJ2, zJ9, zJB, zJW, zKFCB, zNOOP, zPORTI, zRBL, zSLB],
  NucleusOps USING [],
  SDOps USING [],
  SDDefs USING [SD, sUncaughtSignal],
  TrapDefs USING [];

Signaller: PROGRAM
  IMPORTS FrameDefs, FrameOps, ImageDefs EXPORTS NucleusOps, SDOps, TrapDefs
  =PUBLIC

  BEGIN OPEN ControlDefs;

  BYTE: TYPE = AltoDefs.BYTE;

  CatchPointer: TYPE = POINTER TO catch 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;

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

  SignalHandler: PROCEDURE [signal: SIGNAL, message: UNSPECIFIED] =
    BEGIN
    SignalFrame: TYPE = POINTER TO FRAME[SignalHandler];
    frame, nextFrame: FrameHandle;
    target, nextTarget: FrameHandle;
    self: FrameHandle = FrameOps.MyLocalFrame[];
    start: FrameHandle;
    catchFrame: CatchPointer;
    action: ActionCode;
    unwinding: BOOLEAN;
    catchPhrase: BOOLEAN;
    catchFSIndex: BYTE;
    catchPC, exitPC: WordPC;
    catchState: ControlDefs.StateVector;
    MarkSignalFrame[signalling];
    unwinding ← FALSE;
    start ← GetFrame[self.returnlink];
    target ← NullFrame;
    DO
      nextFrame ← start;
      UNTIL nextFrame = target DO
	frame ← nextFrame;
	IF frame.accesslink = FrameOps.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]];
	      FrameOps.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 ← FrameOps.Alloc[catchFSIndex];
	  catchFrame↑ ← Frame[
	    accesslink: frame.accesslink, pc: catchPC, returnlink: [frame[self]],
	    extensions: catch[
	    unused:, staticlink: frame + 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 ← FrameOps.GetReturnLink[];
		catchState.source ← 0;
		RETURN WITH catchState;
		END;
	    exit =>
	      BEGIN -- catchFrame is waiting to execute its exit jump
	      exitPC ← catchFrame.pc;
	      FrameOps.Free[catchFrame];
	      target ← LOOPHOLE[catchState.stk[0] - ControlDefs.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]];
	  FrameOps.Free[frame];
	  END;
	REPEAT StartUnwind => NULL; FINISHED => EXIT
	ENDLOOP;
      ENDLOOP;
    IF unwinding THEN target.pc ← exitPC
    ELSE
      BEGIN
      IF SDDefs.SD[SDDefs.sUncaughtSignal] = 0 THEN ImageDefs.PuntMesa[];
      UncaughtSignal[message, signal, frame];
      END;
    RETURN
    END;

  ReadWord0: PROCEDURE [LONG POINTER] RETURNS [InstWord] = MACHINE CODE
    BEGIN Mopcodes.zRBL, 0 END;

  ReadWord1: PROCEDURE [LONG POINTER] RETURNS [InstWord] = MACHINE CODE
    BEGIN Mopcodes.zRBL, 1 END;

  ReadWord2: PROCEDURE [LONG POINTER] RETURNS [InstWord] = MACHINE CODE
    BEGIN Mopcodes.zRBL, 2 END;

  CheckCatch: PROCEDURE [frame: FrameHandle]
    RETURNS [catchPhrase: BOOLEAN, fsIndex: BYTE, pc: WordPC] =
    BEGIN OPEN Mopcodes;
    ThreeWords: TYPE = ARRAY [0..3) OF InstWord; -- worst case XFER NOOP CATCH 0
    codeWords: ThreeWords; -- worst case is XFER NOOP CATCH 0
    code: POINTER TO ThreeWords ← @codeWords;
    c: [0..3);
    inst: [0..377B];
    g: GlobalFrameHandle ← frame.accesslink;
    parity: {even, odd};
    MarkSignalFrame[notSignalling];
    FrameDefs.SwapInCode[g];
    pc ← [ABS[frame.pc]];
    IF g.code.highByte = 0 THEN
      BEGIN
      lp: LONG POINTER TO InstWord ← pc + g.code.longbase;
      code[0] ← ReadWord0[lp];
      code[1] ← ReadWord1[lp];
      code[2] ← ReadWord2[lp];
      END
    ELSE code↑ ← LOOPHOLE[pc + g.code.shortbase, POINTER TO ThreeWords]↑;
    c ← 0;
    parity ← IF frame.pc < 0 THEN odd ELSE even;
    DO
      inst ← IF parity = even THEN code[c].evenbyte ELSE code[c].oddbyte;
      SELECT inst FROM
	zCATCH => BEGIN catchPhrase ← parity = even; EXIT END;
	zPORTI, zNOOP =>
	  IF parity = even THEN parity ← odd
	  ELSE BEGIN c ← c + 1; parity ← even; END;
	ENDCASE => BEGIN catchPhrase ← FALSE; EXIT END;
      ENDLOOP;
    IF catchPhrase THEN
      BEGIN -- [c, parity] points at zCatch (note: parity must be even)
      pc ← WordPC[pc + c + 1];
      fsIndex ← code[c].oddbyte;
      SELECT code[c + 1].evenbyte FROM
	zJB => BEGIN pc ← [pc + 1]; GO TO evenPC END;
	IN [zJ2..zJ9] => GO TO oddPC;
	zJW => -- always padded !
	  BEGIN pc ← [pc + 2]; GO TO evenPC END;
	ENDCASE;
      EXITS evenPC => NULL; oddPC => pc ← [-pc];
      END;
    FrameOps.ReleaseCode[g];
    RETURN
    END;

  GetFrame: PROCEDURE [link: ControlLink] RETURNS [FrameHandle] =
    BEGIN -- MarkSignalFrame[notSignalling];
    DO
      WITH cl: link SELECT link.tag FROM
	frame => RETURN[cl.frame];
	indirect => link ← cl.link↑;
	ENDCASE => RETURN[NullFrame];
      ENDLOOP;
    END;

  Signal: PROCEDURE [signal: SIGNAL, message: UNSPECIFIED] = SignalHandler;

  SignalList: PROCEDURE [signal: SIGNAL, message: POINTER TO UNSPECIFIED] =
    BEGIN
    MarkSignalFrame[notSignalling];
    SignalHandler[signal, message ! UNWIND => FrameOps.Free[message]];
    FrameOps.Free[message];
    RETURN
    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 => FrameOps.Free[message]];
    FrameOps.Free[message];
    ERROR ResumeError
    END;

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

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


  END.