-- Modules.mesa  Last edited by Sandman, July 1, 1980  8:22 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  ControlDefs USING [
    ControlLink, ControlModule, EntryInfo, EPRange, FrameCodeBase, FrameHandle,
    GFT, GFTIndex, GFTItem, GlobalFrameHandle, MainBodyIndex, LastAVSlot,
    NullControl, NullEpBase, NullFrame, NullGlobalFrame, PrefixHandle, PrefixInfo,
    StateVector, SVPointer, WordPC],
  FrameDefs USING [
    EnterGlobalFrame, EnumerateGlobalFrames, RemoveGlobalFrame, SwapInCode,
    ValidateGlobalFrame],
  FrameOps USING [
    Alloc, CodeHandle, FrameSize, Free, GetReturnLink, ReleaseCode,
    SetReturnFrame, ValidGlobalFrame],
  InlineDefs USING [BITAND, COPY],
  LoadStateFormat USING [NullModule],
  LoadStateOps USING [EnterModule, GetModule, InputLoadState, ReleaseLoadState],
  Mopcodes USING [zDESCBS, zRBL, zSFC],
  NucleusOps USING [],
  Runtime USING [],
  SDDefs USING [sCopy, SD, sGFTLength, sUnNew],
  SDOps USING [],
  SegmentDefs USING [
    AddressFromPage, DeleteFileSegment, FileSegmentHandle, SwapError, Unlock],
  TrapDefs USING [UnboundProcedure];

Modules: PROGRAM
  IMPORTS FrameDefs, FrameOps, InlineDefs, LoadStateOps, SegmentDefs, TrapDefs
  EXPORTS FrameDefs, FrameOps, NucleusOps, Runtime, SDOps, TrapDefs
  SHARES ControlDefs =
  BEGIN OPEN ControlDefs;

  StartFault: PUBLIC SIGNAL [dest: UNSPECIFIED] = CODE;

  MainBody: PROCEDURE [GlobalFrameHandle] RETURNS [ControlLink] = MACHINE CODE
    BEGIN Mopcodes.zDESCBS, 0 END;

  Call: PROCEDURE [ControlLink] = MACHINE CODE BEGIN Mopcodes.zSFC END;

  Start: PUBLIC PROCEDURE [cm: ControlModule] =
    BEGIN
    state: StateVector;
    state ← STATE;
    IF ~cm.multiple THEN
      BEGIN
      IF cm.frame = NullGlobalFrame OR cm.frame.started THEN
	ERROR StartFault[cm.frame];
      FrameDefs.ValidateGlobalFrame[cm.frame];
      StartCM[cm.frame.global[0], cm.frame, @state];
      IF ~cm.frame.started THEN
	BEGIN cm.frame.started ← TRUE; StartWithState[cm.frame, @state]; END
      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: ControlModule, frame: GlobalFrameHandle, state: ControlDefs.SVPointer] =
    BEGIN
    SELECT TRUE FROM
      cm = NullControl => RETURN;
      cm.multiple =>
	BEGIN
	i, length: CARDINAL;
	cm.multiple ← FALSE;
	IF (length ← cm.list.nModules) = 0 THEN RETURN;
	cm.list.nModules ← 0;
	FOR i IN [0..length) DO
	  StartCM[[frame[cm.list.frames[i]]], frame, state]; ENDLOOP;
	FrameOps.Free[cm.list];
	END;
      cm.frame.started => RETURN;
      ENDCASE =>
	BEGIN
	control: 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: PROCEDURE [
    frame: GlobalFrameHandle, state: ControlDefs.SVPointer] =
    BEGIN OPEN ControlDefs;
    s: StateVector ← state↑;
    retFrame: FrameHandle ← FrameOps.GetReturnLink[].frame;
    s.dest ← MainBody[frame];
    s.source ← retFrame.returnlink;
    FrameOps.Free[retFrame];
    RETURN WITH s;
    END;

  Restart: PUBLIC PROCEDURE [dest: GlobalFrameHandle] =
    BEGIN
    stops: BOOLEAN;
    frame: FrameHandle;
    IF dest = NullGlobalFrame THEN ERROR StartFault[dest];
    FrameDefs.ValidateGlobalFrame[dest];
    IF ~dest.started THEN Start[[frame[dest]]];
    FrameDefs.SwapInCode[dest];
    IF dest.code.highByte = 0 THEN
      BEGIN

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

      stops ← GetPrefixInfo[dest.code.longbase].stops;
      END
    ELSE stops ← LOOPHOLE[dest.code.shortbase, PrefixHandle].header.info.stops;
    FrameOps.ReleaseCode[dest];
    IF ~stops THEN ERROR StartFault[dest];
    IF (frame ← dest.global[0]) # NullFrame THEN
      BEGIN
      frame.returnlink ← FrameOps.GetReturnLink[];
      FrameOps.SetReturnFrame[frame];
      END;
    RETURN
    END;

  -- 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


  GlobalFrame: PUBLIC PROCEDURE [link: UNSPECIFIED] RETURNS [UNSPECIFIED] =
    BEGIN OPEN l: LOOPHOLE[link, ControlLink];
    DO
      SELECT l.tag FROM
	frame =>
	  BEGIN
	  IF link = 0 THEN RETURN[NullGlobalFrame];
	  IF FrameOps.ValidGlobalFrame[link] THEN RETURN[link];
	  IF FrameOps.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;
    long: BOOLEAN;
    seg: SegmentDefs.FileSegmentHandle;
    FrameDefs.ValidateGlobalFrame[old];
    seg ← FrameOps.CodeHandle[old];
    FrameDefs.SwapInCode[old];
    IF (long ← old.code.highByte = 0) THEN
      [new, linkspace, ngfi] ← AllocLongGlobalFrame[old]
    ELSE [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];
    IF seg # NIL THEN
      BEGIN
      new.code.offset ←
	old.code.shortbase - SegmentDefs.AddressFromPage[seg.VMpage];
      new.code.handle ← seg;
      END;
    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];
    FrameOps.ReleaseCode[old];
    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;

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

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

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

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

  AllocLongGlobalFrame: PROCEDURE [old: GlobalFrameHandle]
    RETURNS [frame: GlobalFrameHandle, linkspace, ngfi: CARDINAL] =
    BEGIN
    p: LONG POINTER;
    fsi: CARDINAL;
    initialpc: WordPC;
    cp: LONG PrefixHandle ← old.code.longbase;
    prefix: PrefixInfo ← GetPrefixInfo[cp];
    entryInfo: EntryInfo;
    p ← @cp.entry[MainBodyIndex];
    entryInfo ← GetEntryInfo[p];
    initialpc ← GetInitialPC[p];
    p ← cp + initialpc - 1;
    linkspace ←
      IF ~old.codelinks THEN
      prefix.nlinks + InlineDefs.BITAND[-LOOPHOLE[prefix.nlinks, INTEGER], 3B]
      ELSE 0;
    fsi ← GetFSize[p];
    frame ← FrameOps.Alloc[MakeFsi[fsi + linkspace]];
    ngfi ← prefix.ngfi;
    RETURN
    END;

  UnNewModule: PROCEDURE [frame: GlobalFrameHandle] =
    BEGIN
    cseg: SegmentDefs.FileSegmentHandle;
    sharer: GlobalFrameHandle ← NullGlobalFrame;
    original: GlobalFrameHandle ← NullGlobalFrame;
    copy: GlobalFrameHandle ← NullGlobalFrame;
    fcb: FrameCodeBase;
    nothers: CARDINAL ← 0;
    nlinks: CARDINAL;
    long: BOOLEAN;

    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.ValidateGlobalFrame[frame];
    cseg ← FrameOps.CodeHandle[frame];
    FrameDefs.SwapInCode[frame];
    fcb ← frame.code;
    IF (long ← frame.code.highByte = 0) THEN
      BEGIN
      prefix: PrefixInfo ← GetPrefixInfo[frame.code.longbase];
      nlinks ← prefix.nlinks
      END
    ELSE
      BEGIN
      nlinks ← LOOPHOLE[frame.code.shortbase, PrefixHandle].header.info.nlinks;
      fcb.offset ←
	frame.code.shortbase - SegmentDefs.AddressFromPage[cseg.VMpage];
      fcb.out ← TRUE;
      END;
    [] ← FrameDefs.EnumerateGlobalFrames[RemoveAllTraces];
    IF cseg # NIL THEN SegmentDefs.Unlock[cseg];
    IF original = NullGlobalFrame AND ~frame.copied AND copy # NullGlobalFrame
      THEN
      BEGIN OPEN LoadStateOps;
      copy.copied ← FALSE;
      [] ← InputLoadState[];
      EnterModule[rgfi: copy.gfi, module: GetModule[frame.gfi]];
      EnterModule[rgfi: frame.gfi, module: LoadStateFormat.NullModule];
      ReleaseLoadState[];
      END;
    IF frame.shared THEN BEGIN IF nothers = 1 THEN sharer.shared ← FALSE END
    ELSE
      IF cseg # NIL THEN
	BEGIN OPEN SegmentDefs;
	DeleteFileSegment[cseg ! SwapError => CONTINUE];
	END;
    FrameDefs.RemoveGlobalFrame[frame];
    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
    o1, o2: BOOLEAN;
    seg1, seg2: SegmentDefs.FileSegmentHandle;
    fcb1, fcb2: ControlDefs.FrameCodeBase;
    seg1 ← FrameOps.CodeHandle[f1];
    seg2 ← FrameOps.CodeHandle[f2];
    fcb1 ← f1.code;
    fcb2 ← f2.code;
    IF seg1 = NIL AND seg2 = NIL THEN
      BEGIN
      fcb1.out ← fcb2.out ← FALSE;
      RETURN[IF fcb1 = fcb2 THEN identical ELSE different];
      END;
    IF seg1 # seg2 THEN RETURN[different];
    IF (o1 ← f1.code.out) AND (o2 ← f2.code.out) THEN
      RETURN[IF f1.code = f2.code THEN identical ELSE same];
    FrameDefs.SwapInCode[f1];
    FrameDefs.SwapInCode[f2];
    cm ← IF f1.code = f2.code THEN identical ELSE same;
    SegmentDefs.Unlock[seg1];
    SegmentDefs.Unlock[seg2];
    IF ~f1.started THEN f1.code ← fcb1;
    IF ~f2.started THEN f2.code ← fcb2;
    RETURN
    END;

  Init: PROCEDURE =
    BEGIN OPEN SDDefs;
    sd: POINTER TO ARRAY [0..0) OF UNSPECIFIED ← SD;
    sd[sCopy] ← Copy;
    sd[sUnNew] ← UnNewModule;
    END;

  -- Main Body;

  Init[];

  END...