-- FramesHot.mesa  last edit, Bruce  September 22, 1980  5:47 PM

DIRECTORY
  DebugOps USING [GFHandle, ReadCodeByte, ShortCopyREAD, ShortREAD, UserAborted],
  DOutput USING [Line, Octal],
  Frames USING [LG],
  Gf USING [],
  Inline USING [DIVMOD],
  Lf USING [],
  MachineDefs USING [
    BytePC, ControlLink, FHandle, Frame, GFHandle, GfiToFrame, GfiToOffset, GFTIndex,
    GlobalFrame, NullGF, NullOffset, RealToBytePC, TypeOfLink],
  Mopcodes USING [zBRK, zNOOP],
  PrincOps USING [ControlLink, EPIndex, EPRange, FrameCodeBase, ProcDesc],
  State USING [GetGS, GSHandle],
  Storage USING [Free, Node],
  UserInput USING [userAbort];

FramesHot: PROGRAM 
  IMPORTS DebugOps, DOutput, Inline, MachineDefs, State, Storage, UserInput
  EXPORTS Frames, Gf, Lf =
  BEGIN OPEN MachineDefs;

  NoPrevious: PUBLIC SIGNAL [f: FHandle] = CODE;
  Invalid: PUBLIC SIGNAL [f: POINTER] = CODE;
  Clobbered: PUBLIC SIGNAL [f: FHandle] = CODE;
  NoAccessLink: PUBLIC SIGNAL [f: FHandle] = CODE;

  FramePointer: TYPE = MACHINE DEPENDENT RECORD [SELECT OVERLAID * FROM
    pointer => [fill: [0..77777B], lg: Frames.LG],
    link => [cl: PrincOps.ControlLink],
    fullPointer => [frame: POINTER],
    ENDCASE];

  Item: TYPE = POINTER TO ItemObject;
  ItemObject: TYPE = MACHINE DEPENDENT RECORD [
    link: Item,
    fp: FramePointer,
    frame: SELECT OVERLAID * FROM
      global => [globalFrame: GlobalFrame],
      local => [localFrame: local Frame],
      ENDCASE];

  data: State.GSHandle ← State.GetGS[];
  Head: Item ← NIL;

  Cache: PUBLIC PROC [f: POINTER, lg: Frames.LG] RETURNS [copy: POINTER] = 
    BEGIN
    p,last: Item;
    fp: FramePointer;
    IF f = NIL THEN ERROR Invalid[f];
    FOR p ← Head, p.link UNTIL p = NIL DO
      IF p.fp.lg = lg THEN
	BEGIN
	fp ← p.fp; fp.cl.tag ← FALSE;  -- zero out tag
	IF fp.frame = f THEN
	  BEGIN
	  copy ← @p.frame;
	  IF p = Head OR last = Head THEN RETURN;
	  last.link ← p.link; p.link ← Head; Head ← p;
	  RETURN;
	  END;
	END;
      last ← p;
      ENDLOOP;
    p ← Storage.Node[IF lg = global THEN SIZE[global ItemObject]
      ELSE SIZE[local ItemObject]];
    fp.cl ← LOOPHOLE[f,PrincOps.ControlLink];
    IF fp.cl.tag THEN ERROR Invalid[f];
    fp.lg ← lg;
    copy ← @p.frame;
    p↑ ← [link: Head, fp: fp, frame:];
    Head ← p;
    DebugOps.ShortCopyREAD[from: f, to: copy,
      nwords: IF lg = global THEN SIZE[GlobalFrame] ELSE SIZE[local Frame]];
    END;

  FlushFrameCache: PUBLIC PROCEDURE =
    BEGIN
    p, next: Item;
    FOR p ← Head, next UNTIL p = NIL DO
      next ← p.link;
      Storage.Free[p];
      ENDLOOP;
    Head ← NIL;
    END;

  Type: PUBLIC PROC [p: POINTER] RETURNS [Frames.LG] = {
    RETURN[IF ValidateGF[p] THEN global ELSE local]};

  CheckF: PUBLIC PROCEDURE [f: FHandle] =
    BEGIN IF ~ValidateF[f] THEN SIGNAL Invalid[f] END;

  ValidateF: PUBLIC PROCEDURE [f: FHandle] RETURNS [BOOLEAN] =
    BEGIN
    gf: GFHandle;
    IF f = NIL THEN RETURN[FALSE];
    IF (LOOPHOLE[f,CARDINAL] MOD 4) # 0 THEN RETURN[FALSE];
    f ← Cache[f,local];
    gf ← f.accesslink;
    IF gf = MachineDefs.NullGF THEN RETURN[FALSE];
    RETURN [ValidateGF[gf]];
    END;

  GF: PUBLIC PROCEDURE [f: FHandle] RETURNS [gf: GFHandle] =
    BEGIN
    CheckF[f];
    f ← Cache[f,local];
    gf ← f.accesslink;
    IF gf = MachineDefs.NullGF THEN SIGNAL NoAccessLink[f];
    CheckGF[gf];
    END;

  PC: PUBLIC PROCEDURE [f: FHandle] RETURNS [pc: BytePC] =
    BEGIN
    lf: FHandle ← Cache[f,local];
    CheckF[f];
    pc ← MachineDefs.RealToBytePC[LOOPHOLE[lf.pc]];
    IF data.breakFrame = f THEN RETURN;
    IF pc # 0 THEN
      BEGIN OPEN Mopcodes, DebugOps;
      IF ReadCodeByte[lf.accesslink,pc] # zBRK THEN pc ← [pc-1];
      IF ReadCodeByte[lf.accesslink,pc] = zNOOP THEN pc ← [pc-1];
      END;
    END;

  Previous: PUBLIC PROCEDURE [f: FHandle] RETURNS [prev: FHandle] =
    BEGIN
    link: MachineDefs.ControlLink;
    CheckF[f];
    f ← Cache[f,local];
    IF UserInput.userAbort THEN SIGNAL DebugOps.UserAborted;
    link ← LOOPHOLE[f.returnlink];
    THROUGH [0..100] DO 
      SELECT MachineDefs.TypeOfLink[link] FROM
      frame =>
	IF link.frame = NIL THEN GOTO noPrev
	ELSE IF ~ValidateF[link.frame] THEN GOTO clobbered
	ELSE RETURN[link.frame];
      procedure => GOTO noPrev;
      indirect => link ← DebugOps.ShortREAD[LOOPHOLE[link]];
      ENDCASE => GOTO clobbered;
    REPEAT
      noPrev => ERROR NoPrevious[f];
      clobbered => ERROR Clobbered[f];
      FINISHED => ERROR Clobbered[f];
    ENDLOOP
    END;

  ValidateGF: PUBLIC PROC [gf: GFHandle] RETURNS [BOOLEAN] =
    BEGIN
    cl: PrincOps.ControlLink ← LOOPHOLE[gf];
    gfi: MachineDefs.GFTIndex;
    IF gf = NIL THEN RETURN[FALSE];
    IF cl.proc OR cl.indirect THEN RETURN[FALSE];
    gfi ← GFI[gf];
    RETURN[MachineDefs.GfiToFrame[gfi] = gf]
    END;

  CheckGF: PUBLIC PROC[gf: GFHandle] = 
    BEGIN IF ~ValidateGF[gf] THEN SIGNAL Invalid[gf] END;
  
  Copied: PUBLIC PROCEDURE [gf: GFHandle] RETURNS [BOOLEAN] =
    BEGIN gf ← Cache[gf,global]; RETURN[gf.copied] END;

  Started: PUBLIC PROCEDURE [gf: GFHandle] RETURNS [BOOLEAN] =
    BEGIN gf ← Cache[gf,global]; RETURN[gf.started] END;

  Shared: PUBLIC PROCEDURE [gf: GFHandle] RETURNS [BOOLEAN] =
    BEGIN gf ← Cache[gf,global]; RETURN[gf.shared] END;

  CheckStarted: PUBLIC PROC [gf: GFHandle] RETURNS [running: BOOLEAN] =
    BEGIN
    running ← Started[gf];
    IF running THEN RETURN;
    DOutput.Octal[gf]; DOutput.Line[" is not started!"L];
    END;

  GFI: PUBLIC PROCEDURE [gf: GFHandle] RETURNS [MachineDefs.GFTIndex] =
    BEGIN gf ← Cache[gf,global]; RETURN[gf.gfi] END;

  Links: PUBLIC PROCEDURE [gf: GFHandle] RETURNS [BOOLEAN] =
    BEGIN gf ← Cache[gf,global]; RETURN[gf.codelinks] END;

  CodeBase: PUBLIC PROC [gf: GFHandle] RETURNS [PrincOps.FrameCodeBase] =
    BEGIN gf ← Cache[gf,global]; RETURN[gf.code] END;

  Deleted: PUBLIC PROC [gfi: MachineDefs.GFTIndex] RETURNS [BOOLEAN] = {
    RETURN[MachineDefs.GfiToFrame[gfi] = NullGF AND
      MachineDefs.GfiToOffset[gfi] = MachineDefs.NullOffset]};

  MakeDesc: PUBLIC PROC [gf: GFHandle, ep: CARDINAL] RETURNS [pd: PrincOps.ProcDesc] =
    BEGIN
    base: CARDINAL;
    offset: PrincOps.EPIndex;
    [base, offset] ← Inline.DIVMOD[ep, PrincOps.EPRange];
    pd.gfi ← GFI[gf] + base;
    pd.ep ← offset;
    pd.tag ← TRUE;
    END;

  PreDeclared: PUBLIC PROC [u: UNSPECIFIED] RETURNS [BOOLEAN] =
    BEGIN
    Signals: ARRAY [0..4) OF INTEGER = [-1,1,3,5];
    FOR i: CARDINAL IN [0..4) DO
      IF u = Signals[i] THEN RETURN[TRUE]
      ENDLOOP;
    RETURN[FALSE]
    END;

  NewLink: PUBLIC PROC [ocl: ControlLink]
    RETURNS [ncl: PrincOps.ControlLink] =
    BEGIN
    IF PreDeclared[ocl] THEN RETURN[LOOPHOLE[ocl]];
    SELECT TypeOfLink[ocl] FROM
      indirect, frame => ncl ← LOOPHOLE[ocl];
      procedure => {ncl.gfi ← ocl.gfi; ncl.ep ← ocl.ep; ncl.tag ← TRUE};
      ENDCASE => ncl ← LOOPHOLE[1];
    END;

  AddGfi: PUBLIC PROC [gf: GFHandle, cl: PrincOps.ControlLink]
    RETURNS [pd: PrincOps.ProcDesc] = {
      pd ← LOOPHOLE[cl]; pd.gfi ← pd.gfi + GFI[gf] - 1};

  FrameGfi: PUBLIC PROC [gfi: GFTIndex] RETURNS [GFHandle] = {
    RETURN[MachineDefs.GfiToFrame[gfi]]};

  END.