-- LaurelUnNewConfig.mesa; edited by Levin on April 8, 1983  10:50 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  BcdDefs USING [Base, MTIndex],
  BcdOps USING [BcdBase, MTHandle, ProcessModules],
  ControlDefs USING [
    ControlLink, FrameCodeBase, GFT, GFTIndex, GFTItem, GFTNull,
    GlobalFrameHandle, NullGlobalFrame, PrefixHandle, PrefixInfo, TrapLink,
    UnboundLink],
  FrameDefs USING [EnumerateGlobalFrames, GlobalFrame, SwapInCode, UnNew],
  FrameOps USING [CodeHandle, Free],
  InlineDefs USING [BITAND],
  LoaderOps USING [CloseLinkSpace, OpenLinkSpace, ReadLink, WriteLink],
  LoadStateFormat USING [ModuleInfo],
  LoadStateOps USING [
    AcquireBcd, ConfigIndex, EnterModule, EnumerateBcds, GetMap, GetModule,
    InputLoadState, MapRealToConfig, ReleaseBcd, ReleaseLoadState, ReleaseMap,
    Map, RemoveConfig],
  Mopcodes USING [zRBL],
  SegmentDefs USING [
    DataSegmentAddress, DeleteFileSegment, FileSegmentHandle, SwapError, Unlock,
    VMtoDataSegment],
  SystemDefs USING [FreeSegment, SegmentSize];

LaurelUnNewConfig: PROGRAM
  IMPORTS
    BcdOps, FrameDefs, FrameOps, InlineDefs, LoaderOps, LoadStateOps, SegmentDefs,
    SystemDefs
  EXPORTS FrameDefs =
  BEGIN OPEN ControlDefs;

  ConfigIndex: TYPE = LoadStateOps.ConfigIndex;
  Map: TYPE = LoadStateOps.Map;
  BcdBase: TYPE = BcdOps.BcdBase;
  GlobalFrameHandle: TYPE = ControlDefs.GlobalFrameHandle;
  NullGlobalFrame: GlobalFrameHandle = ControlDefs.NullGlobalFrame;
  FileSegmentHandle: TYPE = SegmentDefs.FileSegmentHandle;

  UnNewConfig: PUBLIC PROCEDURE [link: UNSPECIFIED] =
    BEGIN OPEN LoadStateOps;
    config: ConfigIndex;
    map: Map;
    bcd: BcdBase;
    frame: GlobalFrameHandle ← FrameDefs.GlobalFrame[link];

    FindOriginal: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
      BEGIN
      IF f # frame THEN
	BEGIN
	IF SameCode[frame, f] = identical AND ~f.copied THEN
	  BEGIN frame ← f; RETURN[TRUE] END;
	END;
      RETURN[FALSE];
      END;

      IF frame.copied THEN [] ← FrameDefs.EnumerateGlobalFrames[FindOriginal];
    [] ← InputLoadState[];
    [config: config] ← MapRealToConfig[frame.gfi];
    bcd ← AcquireBcd[config];
    map ← GetMap[config];
    --UnBindConfig[config, bcd, map];
    CleanupFrames[map];
    CleanupGFT[map];
    RemoveConfig[map, config];
    ReleaseMap[map];
    ReleaseBcd[bcd];
    ReleaseLoadState[];
    RETURN
    END;

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

  SameCode: PROCEDURE [f1, f2: GlobalFrameHandle] RETURNS [cm: CodeMatch] =
    BEGIN
    o1, o2: BOOLEAN;
    seg1, seg2: 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;

  DestroyCopy: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
    BEGIN FrameDefs.UnNew[f]; RETURN[FALSE]; END;

  CleanupFrames: PROCEDURE [map: Map] =
    BEGIN
    seg: FileSegmentHandle;
    frameSegment: POINTER ← NIL;
    frame: GlobalFrameHandle;
    i, ep: CARDINAL;
    nlinks: [0..256);
    FOR i IN [1..LENGTH[map]) DO
      deleteSeg: BOOLEAN ← FALSE;
      [frame: frame, epbase: ep] ← ControlDefs.GFT[map[i]];
      IF frame = NullGlobalFrame OR ep # 0 THEN LOOP;
      seg ← FrameOps.CodeHandle[frame];
      IF ~EnumerateCopies[frame, DestroyCopy].shared AND seg # NIL THEN
	deleteSeg ← TRUE;
      IF frame.alloced THEN
	BEGIN
	Align: PROCEDURE [POINTER, WORD] RETURNS [POINTER] =
	  LOOPHOLE[InlineDefs.BITAND];
	IF frame.codelinks THEN FrameOps.Free[frame]
	ELSE
	  BEGIN
	  nlinks ← FindNLinks[frame, seg];
	  FrameOps.Free[Align[frame - nlinks, 177774B]];
	  END;
	END
      ELSE frameSegment ← frame;
      IF deleteSeg THEN
	SegmentDefs.DeleteFileSegment[seg ! SegmentDefs.SwapError => CONTINUE];
      ENDLOOP;
    IF frameSegment # NIL THEN SystemDefs.FreeSegment[frameSegment];
    RETURN
    END;

  FindNLinks: PROCEDURE [frame: GlobalFrameHandle, seg: FileSegmentHandle]
    RETURNS [nlinks: CARDINAL] =
    BEGIN
    FrameDefs.SwapInCode[frame];
    IF frame.code.highByte = 0 THEN
      BEGIN
      GetPrefixInfo: PROCEDURE [LONG POINTER] RETURNS [PrefixInfo] = MACHINE CODE
	BEGIN Mopcodes.zRBL, 1 END;
      prefix: PrefixInfo ← GetPrefixInfo[frame.code.longbase];
      nlinks ← prefix.nlinks;
      END
    ELSE nlinks ← LOOPHOLE[frame.code.shortbase, PrefixHandle].header.info.nlinks;
    SegmentDefs.Unlock[seg];
    RETURN;
    END;

  NextMultipleOfFour: PROCEDURE [n: UNSPECIFIED] RETURNS [UNSPECIFIED] =
    BEGIN RETURN[n + InlineDefs.BITAND[-n, 3B]] END;

  OP: TYPE = ORDERED POINTER;

  UnBindConfig: PROCEDURE [config: ConfigIndex, bcd: BcdBase, map: Map] =
    BEGIN OPEN LoadStateOps;
    bmap: Map;
    frameStart, frameEnd: OP;
    UnBind: PROCEDURE [c: ConfigIndex] RETURNS [BOOLEAN] =
      BEGIN
      bindee: BcdBase;
      IF c = config THEN RETURN[FALSE];
      bindee ← AcquireBcd[c];
      IF bindee.nImports # 0 THEN
	BEGIN
	bmap ← GetMap[c];
	[] ← BcdOps.ProcessModules[bindee, AdjustLinks];
	ReleaseMap[bmap];
	END;
      ReleaseBcd[bindee];
      RETURN[FALSE]
      END;
    AdjustLinks: PROCEDURE [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
      RETURNS [BOOLEAN] =
      BEGIN
      gfi: ControlDefs.GFTIndex = bmap[mth.gfi];
      frame: GlobalFrameHandle = ControlDefs.GFT[gfi].frame;
      changed: BOOLEAN;
      AdjustCopies: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
	BEGIN [] ← AdjustFrame[f, mth]; RETURN[FALSE]; END;
	IF frame = NullGlobalFrame OR mth.frame.length = 0 THEN RETURN[FALSE];
      changed ← AdjustFrame[frame, mth];
      IF changed AND frame.shared AND ~frame.codelinks THEN
	[] ← EnumerateCopies[frame, AdjustCopies];
      IF changed THEN
	BEGIN
	info: LoadStateFormat.ModuleInfo ← GetModule[gfi];
	info.resolved ← FALSE;
	EnterModule[rgfi: gfi, module: info];
	END;
      RETURN[FALSE]
      END;
    AdjustFrame: PROCEDURE [frame: GlobalFrameHandle, mth: BcdOps.MTHandle]
      RETURNS [changed: BOOLEAN] =
      BEGIN
      i: CARDINAL;
      link: ControlDefs.ControlLink;
      changed ← FALSE;
      LoaderOps.OpenLinkSpace[frame, mth];
      FOR i IN [0..mth.frame.length) DO
	link ← LoaderOps.ReadLink[i];
	IF BoundHere[link, mth.frame.frag[i].vtag = var] THEN
	  BEGIN
	  link ← (SELECT link.tag FROM frame => TrapLink, ENDCASE => UnboundLink);
	  LoaderOps.WriteLink[i, link];
	  changed ← TRUE;
	  END;
	ENDLOOP;
      LoaderOps.CloseLinkSpace[frame];
      RETURN
      END;
    BoundHere: PROCEDURE [link: ControlLink, var: BOOLEAN] RETURNS [BOOLEAN] =
      BEGIN OPEN FrameDefs;
      i: CARDINAL;
      gfi: GFTIndex ← GFTNull;
      IF var THEN RETURN[LOOPHOLE[link, OP] IN [frameStart..frameEnd)];
      SELECT link.tag FROM procedure => gfi ← link.gfi; ENDCASE => RETURN[FALSE];
      FOR i IN [1..LENGTH[map]) DO IF map[i] = gfi THEN RETURN[TRUE]; ENDLOOP;
      RETURN[FALSE];
      END;
    IF bcd.nExports = 0 AND bcd.nModules # 1 THEN RETURN;
    frameStart ← LOOPHOLE[GFT[map[1]].frame];
    IF bcd.nModules = 1 THEN
      BEGIN OPEN BcdDefs;
      mth: BcdOps.MTHandle = @LOOPHOLE[bcd + bcd.mtOffset, Base][FIRST[MTIndex]];
      frameEnd ← LOOPHOLE[frameStart + mth.framesize]
      END
    ELSE
      BEGIN OPEN SegmentDefs;
      frameStart ← LOOPHOLE[DataSegmentAddress[VMtoDataSegment[frameStart]]];
      frameEnd ← LOOPHOLE[frameStart + SystemDefs.SegmentSize[frameStart]];
      END;
    [] ← EnumerateBcds[recentfirst, UnBind];
    RETURN
    END;

  CleanupGFT: PROCEDURE [map: Map] =
    BEGIN OPEN ControlDefs;
    i: CARDINAL;
    FOR i IN [1..LENGTH[map]) DO
      GFT[map[i]] ← GFTItem[NullGlobalFrame, 0]; ENDLOOP;
    END;

  EnumerateCopies: PROCEDURE [
    frame: GlobalFrameHandle,
    proc: PROCEDURE [GlobalFrameHandle] RETURNS [BOOLEAN]]
    RETURNS [result: GlobalFrameHandle, shared: BOOLEAN] =
    BEGIN
    FindCopies: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
      BEGIN
      IF f = frame THEN RETURN[FALSE];
      SELECT SameCode[frame, f] FROM
	identical =>
	  BEGIN shared ← TRUE; IF f.copied AND proc[f] THEN RETURN[TRUE]; END;
	same => shared ← TRUE;
	ENDCASE;
      RETURN[FALSE];
      END;
    shared ← FALSE;
    IF ~frame.shared THEN RETURN[NIL, FALSE];
    RETURN[FrameDefs.EnumerateGlobalFrames[FindCopies], shared];
    END;


  END...