-- MMModules.mesa; edited by Forrest, July 15, 1980  2:45 PM

DIRECTORY
  BcdDefs USING [NullLink, UnboundLink],
  ControlDefs USING [
    ControlLink, EPRange, FrameCodeBase, FrameHandle, GFT, GFTIndex,
    GFTItem, GlobalFrameHandle, MainBodyIndex, LastAVSlot, NullEpBase,
    NullFrame, NullGlobalFrame, PrefixHandle],
  FrameDefs USING [
    EnterGlobalFrame, EnumerateGlobalFrames, RemoveGlobalFrame, SwapInCode],
  FrameOps USING [
    Alloc, FrameSize, Free, GetReturnFrame, SetReturnLink],
  InlineDefs USING [BITAND, COPY],
  MiscDefs,
  MMOps,
  MMSDEntries,
  SDDefs USING [SD, sGFTLength],
  SystemDefs USING [FreePages],
  TrapDefs USING [UnboundProcedure];

MMModules: PROGRAM
  IMPORTS FrameDefs, FrameOps, InlineDefs, SystemDefs, TrapDefs
  EXPORTS FrameDefs, MiscDefs, MMOps, MMSDEntries = PUBLIC BEGIN

  OPEN ControlDefs;

  -- Global Frame Table management

  gftrover: CARDINAL ← 0;  -- okay to start at 0 because incremented before used

  NoGlobalFrameSlots: PUBLIC SIGNAL [CARDINAL] = CODE;

  EnumerateGlobalFrames: PUBLIC PROCEDURE [
    proc: PROCEDURE [GlobalFrameHandle] RETURNS [BOOLEAN]]
    RETURNS [GlobalFrameHandle] =
    BEGIN
    i: GFTIndex;
    frame: GlobalFrameHandle;
    gft: POINTER TO ARRAY [0..0) OF GFTItem ← GFT;
    FOR i IN [0..SDDefs.SD[SDDefs.sGFTLength]) DO
      frame ← gft[i].frame;
      IF frame # NullGlobalFrame AND gft[i].epbase = 0
	AND proc[frame] THEN RETURN[frame];
      ENDLOOP;
    RETURN[NullGlobalFrame]
    END;

  EnterGlobalFrame: PUBLIC PROCEDURE [
    frame: GlobalFrameHandle, nslots: CARDINAL]
    RETURNS [entryindex: GFTIndex] =
    BEGIN
    gft: POINTER TO ARRAY [0..0) OF GFTItem ← GFT;
    i, imax, n, epoffset: CARDINAL;
    i ← gftrover;  imax ← SDDefs.SD[SDDefs.sGFTLength] - nslots;  n ← 0;
    DO
      IF (i ← IF i>=imax THEN 1 ELSE i+1) = gftrover
	THEN SIGNAL NoGlobalFrameSlots[nslots];
      IF gft[i].frame # NullGlobalFrame THEN n ← 0
      ELSE IF gft[i].epbase = NullEpBase THEN n ← 0
      ELSE IF (n ← n+1) = nslots THEN EXIT;
      ENDLOOP;
    entryindex ← (gftrover←i)-nslots+1;  epoffset ← 0;
    FOR i IN [entryindex..gftrover] DO
      gft[i] ← GFTItem[frame, epoffset];
      epoffset ← epoffset + EPRange;
      ENDLOOP;
    RETURN
    END;

  RemoveGlobalFrame: PUBLIC PROCEDURE [frame: GlobalFrameHandle] =
    BEGIN
    gft: POINTER TO ARRAY [0..0) OF GFTItem ← GFT;
    sd: POINTER TO ARRAY [0..0) OF CARDINAL ← SDDefs.SD;
    i: CARDINAL;
    FOR i ← frame.gfi, i+1
    WHILE i<sd[SDDefs.sGFTLength] AND gft[i].frame=frame DO
      gft[i] ← IF frame.copied THEN
	GFTItem[NullGlobalFrame,0] ELSE GFTItem[NullGlobalFrame,NullEpBase];
      ENDLOOP;
    RETURN
    END;

  -- Frame manipulation

  InvalidGlobalFrame: PUBLIC SIGNAL [frame: GlobalFrameHandle] = CODE;

  ValidateGlobalFrame: PUBLIC PROCEDURE [g: GlobalFrameHandle] =
    BEGIN
    IF ~ValidGlobalFrame[g] THEN SIGNAL InvalidGlobalFrame[g];
    END;

  ValidGlobalFrame: PROCEDURE [g: GlobalFrameHandle]
    RETURNS[BOOLEAN] =
    BEGIN
    i: CARDINAL;
    gft: POINTER TO ARRAY [0..0) OF GFTItem ← GFT;
    IF LOOPHOLE[g, ControlLink].tag # frame THEN RETURN[FALSE];
    FOR i IN [1..SDDefs.SD[SDDefs.sGFTLength]) DO
      IF gft[i].frame = g THEN EXIT;
      REPEAT FINISHED => RETURN[FALSE];
      ENDLOOP;
    RETURN[gft[g.gfi].frame = g]
    END;

  InvalidFrame: PUBLIC SIGNAL [frame: FrameHandle] = CODE;

  ValidateFrame: PUBLIC PROCEDURE [f: FrameHandle] =
    BEGIN
    IF ~ValidFrame[f] THEN SIGNAL InvalidFrame[f];
    END;

  ValidFrame: PROCEDURE [f: FrameHandle]
    RETURNS[BOOLEAN] =
    BEGIN
    RETURN[LOOPHOLE[f, ControlLink].tag = frame
      AND ValidGlobalFrame[f.accesslink]]
    END;

  GlobalFrame: PUBLIC PROCEDURE [link: UNSPECIFIED]
    RETURNS [GlobalFrameHandle] =
    BEGIN OPEN l: LOOPHOLE[link, ControlLink];
    DO SELECT l.tag FROM
      frame =>
	BEGIN
	IF link = 0 THEN RETURN[NullGlobalFrame];
	IF ValidGlobalFrame[link] THEN RETURN[link];
	IF ValidGlobalFrame[l.frame.accesslink] THEN
	  RETURN[l.frame.accesslink];
	RETURN[NullGlobalFrame]
	END;
      procedure => RETURN[GFT[l.gfi].frame];
      indirect => link ← l.link↑;
      unbound => link ← SIGNAL TrapDefs.UnboundProcedure[link];
      ENDCASE ENDLOOP
    END;

  Copy: PUBLIC PROCEDURE [old: GlobalFrameHandle]
    RETURNS [new: GlobalFrameHandle] =
    BEGIN
    linkspace, ngfi: CARDINAL ← 0;
    FrameDefs.SwapInCode[old];
    [new, linkspace, ngfi] ← AllocGlobalFrame[old];
    new ← new + linkspace;
    new↑ ← [gfi:, unused: 0, alloced: TRUE, shared: TRUE, copied: TRUE,
      started: FALSE, trapxfers: FALSE, codelinks: old.codelinks,
      code: old.code, global:];
    new.gfi ← FrameDefs.EnterGlobalFrame[new, ngfi];
    new.code.offset ← old.code.shortbase - old.code.handle;
    new.code.out ← TRUE;
    new.global[0] ← NullGlobalFrame;
    old.shared ← TRUE;
    IF linkspace # 0 THEN InlineDefs.COPY[
      from: old-linkspace, to: new-linkspace, nwords: linkspace];
    RETURN
    END;

  MakeFsi: PUBLIC PROCEDURE [words: CARDINAL] RETURNS [fsi: CARDINAL] =
    BEGIN
    FOR fsi IN [0..LastAVSlot] DO
      IF FrameOps.FrameSize[fsi] >= words THEN RETURN;
      ENDLOOP;
    RETURN[words]
    END;

  AllocGlobalFrame: PROCEDURE [old: GlobalFrameHandle]
    RETURNS [frame: GlobalFrameHandle, linkspace, ngfi: CARDINAL] =
    BEGIN
    pbody: POINTER;
    cp: PrefixHandle ← old.code.shortbase;
    pbody ← cp+CARDINAL[cp.entry[MainBodyIndex].initialpc];
    linkspace ← IF ~old.codelinks THEN cp.header.info.nlinks +
      InlineDefs.BITAND[-LOOPHOLE[cp.header.info.nlinks, INTEGER], 3B]
      ELSE 0;
    frame ← FrameOps.Alloc[MakeFsi[(pbody-1)↑+linkspace]];
    ngfi ← cp.header.info.ngfi;
    RETURN
    END;

  UnNew: PUBLIC PROCEDURE [frame: GlobalFrameHandle] =
    BEGIN
    nlinks: CARDINAL;
    sharer: GlobalFrameHandle ← NullGlobalFrame;
    original: GlobalFrameHandle ← NullGlobalFrame;
    copy: GlobalFrameHandle ← NullGlobalFrame;
    nothers: CARDINAL ← 0;
    RemoveAllTraces: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
      BEGIN
      cm: CodeMatch;
      IF f#frame THEN
	BEGIN
	IF f.global[0] = frame AND ~f.started THEN f.global[0] ← NullFrame;
	cm ← SameCode[f, frame];
	IF cm # different THEN
	  BEGIN
	  nothers ← nothers + 1;  sharer ← f;
	  IF cm = identical THEN
	    IF f.copied THEN copy ← f ELSE original ← f;
	  END;
	END;
      RETURN[FALSE];
      END;
    FrameDefs.SwapInCode[frame];
    [] ← FrameDefs.EnumerateGlobalFrames[RemoveAllTraces];
    FrameDefs.RemoveGlobalFrame[frame];
    IF frame.shared THEN
      BEGIN
      IF nothers = 1 THEN sharer.shared ← FALSE
      END
    ELSE SystemDefs.FreePages[frame.code.handle];
    IF frame.alloced THEN
      BEGIN
      Align: PROCEDURE [POINTER, WORD] RETURNS [POINTER] =
	LOOPHOLE[InlineDefs.BITAND];
      IF frame.codelinks THEN FrameOps.Free[frame]
      ELSE FrameOps.Free[Align[frame - nlinks, 177774B]]
      END;
    END;

  CodeMatch: TYPE = {identical, same, different};

  SameCode: PROCEDURE [f1, f2: GlobalFrameHandle] RETURNS [cm: CodeMatch] =
    BEGIN
    fcb1, fcb2: ControlDefs.FrameCodeBase;
    fcb1 ← f1.code;
    fcb2 ← f2.code;
    IF f1.code.handle # f2.code.handle THEN RETURN[different];
    FrameDefs.SwapInCode[f1];
    FrameDefs.SwapInCode[f2];
    cm ← IF f1.code = f2.code THEN identical ELSE same;
    IF ~f1.started THEN f1.code ← fcb1;
    IF ~f2.started THEN f2.code ← fcb2;
    RETURN
    END;

  LockCode, UnlockCode: PROCEDURE [link: UNSPECIFIED] = BEGIN END;

  MakeCodeResident, SwapOutCode: PROCEDURE [f: GlobalFrameHandle] = LockCode;

  GetCaller: PROCEDURE RETURNS [PROGRAM] =
    BEGIN
    RETURN[LOOPHOLE[FrameOps.GetReturnFrame[].returnlink.frame.accesslink]];
    END;

  IsBound: PROCEDURE [link: UNSPECIFIED] RETURNS [BOOLEAN] =
    BEGIN
    RETURN[link # BcdDefs.UnboundLink AND link # BcdDefs.NullLink];
    END;

  SelfDestruct: PROCEDURE =
    BEGIN
    destructee: FrameHandle ← FrameOps.GetReturnFrame[];
    FrameOps.SetReturnLink[destructee.returnlink];
    UnNew[GlobalFrame[destructee]];
    FrameOps.Free[destructee];
    RETURN
    END;

  SetBlock: PROCEDURE [p:POINTER, v:UNSPECIFIED, l:CARDINAL] =
    BEGIN
    IF l=0 THEN RETURN;  p↑ ← v;
    InlineDefs.COPY[from:p, to:p+1, nwords:l-1];
    END;

  Zero: PROCEDURE [p:POINTER, l:CARDINAL] =
    BEGIN
    IF l=0 THEN RETURN;  p↑ ← 0;
    InlineDefs.COPY[from:p, to:p+1, nwords:l-1];
    END;

  FakeModulesCode: PROCEDURE [p: PROGRAM] RETURNS [POINTER] =
    BEGIN
    frame: GlobalFrameHandle;
    FrameDefs.SwapInCode[frame ← GlobalFrame[p]];
    RETURN[frame.code.shortbase];
    END;

END...