-- PilotUnLoaderImpl.mesa; edited by Sandman on August 28, 1980  2:02 PM
-- edited by Paul Rovner on 23-Sep-81 13:02:30

DIRECTORY
  BcdDefs USING [Base, MTIndex],
  BcdOps USING [BcdBase, MTHandle, ProcessModules],
  Inline USING [BITAND],
  PilotLoaderOps USING [
    CloseLinkSpace, FreeSpace, GetSpace, OpenLinkSpace, ReadLink, WriteLink, LinkSegmentLength, IthLink],
  PilotLoadStateFormat USING [ModuleInfo],
  PilotLoadStateOps USING [
    AcquireBcd, ConfigIndex, EnterModule, EnumerateBcds, GetMap, GetModule,
    InputLoadState, MapRealToConfig, ReleaseBcd, ReleaseLoadState, ReleaseMap,
    Map, RemoveConfig],
  Mopcodes USING [zFREE],
  PrincOps USING [
    ControlLink, FrameCodeBase, GFTIndex, GFTNull, GlobalFrameHandle,
    NullGlobalFrame, PrefixHandle, TrapLink, UnboundLink],
  PrincOpsRuntime USING [EmptyGFTItem, GetFrame, GFT],
  Runtime USING [GlobalFrame, UnNew],
  RuntimeInternal USING [GetNextGlobalFrame],
  Space USING [
    Delete, GetAttributes, GetHandle, Handle, PageFromLongPointer, Pointer,
    wordsPerPage],
  SubSys USING [];

PilotUnLoaderImpl: PROGRAM
  IMPORTS
    BcdOps, Inline, PilotLoaderOps, PilotLoadStateOps, PrincOpsRuntime, Runtime,
    RuntimeInternal, Space
  EXPORTS Runtime, SubSys =
  BEGIN OPEN PrincOps, RuntimeInternal;

  ConfigIndex: TYPE = PilotLoadStateOps.ConfigIndex;
  Map: TYPE = PilotLoadStateOps.Map;
  BcdBase: TYPE = BcdOps.BcdBase;
  GlobalFrameHandle: TYPE = PrincOps.GlobalFrameHandle;
  NullGlobalFrame: GlobalFrameHandle = PrincOps.NullGlobalFrame;

  Item: TYPE = RECORD [next: List, space: Space.Handle, pages: CARDINAL];
  List: TYPE = POINTER TO Item;

  fList, cList: List;

  UnNewConfig, UnLoad: PUBLIC PROC [link: UNSPECIFIED] =
    BEGIN OPEN PilotLoadStateOps;
    config: ConfigIndex;
    map: Map;
    bcd: BcdBase;
    frame, f: GlobalFrameHandle;
    frame ← LOOPHOLE[Runtime.GlobalFrame[link]];
    IF frame.copied THEN
      FOR f ← GetNextGlobalFrame[NullGlobalFrame], GetNextGlobalFrame[f] UNTIL f =
	NullGlobalFrame DO
	IF f # frame AND SameCode[frame, f] AND ~f.copied THEN
	  BEGIN frame ← f; EXIT END;
	ENDLOOP;
    [] ← InputLoadState[];
    BEGIN
    ENABLE UNWIND => ReleaseLoadState[];
    [config: config] ← MapRealToConfig[frame.gfi];
    bcd ← AcquireBcd[config];
    map ← GetMap[config];
    CollectSpaces[map];
    UnBindConfig[config, bcd, map];
    CleanupFrames[map];
    RemoveConfig[map, config];
    ReleaseMap[map];
    ReleaseBcd[bcd];
    END; -- ELBANE
    ReleaseLoadState[];
    RETURN
    END;

  CollectSpaces: PROC [map: Map] =
    BEGIN OPEN PrincOpsRuntime, Space;
    frame: GlobalFrameHandle;
    i: CARDINAL;
    fList ← cList ← NIL;
    FOR i IN [1..LENGTH[map]) DO
      frame ← GetFrame[GFT[map[i]]];
      IF frame = NullGlobalFrame OR GFT[map[i]].epbias # 0 THEN LOOP;
      IF LENGTH[map] > 2 THEN
	fList ← AddSpaceToList[list: fList, space: FindMappedSpace[frame]];
      cList ← AddSpaceToList[
	list: cList, space: FindMappedSpace[frame.code.longbase]];
      ENDLOOP;
    RETURN
    END;

  FindMappedSpace: PROCEDURE [lp: LONG POINTER] RETURNS [space: Space.Handle] =
    BEGIN
    mapped: BOOLEAN;
    parent: Space.Handle;
    space ← Space.GetHandle[Space.PageFromLongPointer[lp]];
    DO
      [mapped: mapped, parent: parent] ← Space.GetAttributes[space];
      IF mapped THEN RETURN[space];
      space ← parent;
      ENDLOOP;
    END;

  AddSpaceToList: PROCEDURE [list: List, space: Space.Handle] RETURNS [List] =
    BEGIN
    l: List;
    FOR l ← list, l.next UNTIL l = NIL DO
      IF l.space = space THEN RETURN[list]; ENDLOOP;
    l ← PilotLoaderOps.GetSpace[SIZE[Item]];
    l↑ ← [next: list, space: space, pages: Space.GetAttributes[space].size];
    RETURN[l];
    END;

  SameCode: PROC [f1, f2: GlobalFrameHandle] RETURNS [BOOLEAN] =
    BEGIN
    fcb1, fcb2: PrincOps.FrameCodeBase;
    fcb1 ← f1.code;
    fcb2 ← f2.code;
    fcb1.out ← fcb2.out ← FALSE;
    RETURN[fcb1 = fcb2];
    END;

  DestroyCopy: PROC [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
    BEGIN Runtime.UnNew[LOOPHOLE[f]]; RETURN[FALSE]; END;

  Free: PROC [POINTER] = MACHINE CODE BEGIN Mopcodes.zFREE END;

  CleanupFrames: PROC [map: Map] =
    BEGIN OPEN PrincOpsRuntime, Space;
    frame: GlobalFrameHandle;
    f, c: List;
    i, ep: CARDINAL;
    FOR i IN [1..LENGTH[map]) DO
      frame ← GetFrame[GFT[map[i]]];
      ep ← GFT[map[i]].epbias;
      IF frame = NullGlobalFrame OR ep # 0 THEN LOOP;
      [] ← EnumerateCopies[frame, DestroyCopy];
      IF frame.alloced THEN
	BEGIN
	Align: PROC [POINTER, WORD] RETURNS [POINTER] = LOOPHOLE[Inline.BITAND];
	IF frame.codelinks THEN Free[frame]
	ELSE
	  BEGIN
	  nlinks: [0..256) ← FindNLinks[frame];
	  Free[Align[frame - nlinks, 177774B]];
	  END;
	END;
      ENDLOOP;
    FOR f ← fList, fList UNTIL f = NIL DO
      Space.Delete[f.space]; fList ← f.next; PilotLoaderOps.FreeSpace[f]; ENDLOOP;
    FOR i IN [1..LENGTH[map]) DO GFT[map[i]] ← EmptyGFTItem ENDLOOP;
    FOR frame ← GetNextGlobalFrame[NullGlobalFrame], GetNextGlobalFrame[frame]
      UNTIL frame = NullGlobalFrame DO
      space: Space.Handle ← FindMappedSpace[frame.code.longbase];
      FOR c ← cList, c.next UNTIL c = NIL DO
	IF space = c.space THEN {c.pages ← 0; EXIT}; ENDLOOP;
      ENDLOOP;
    FOR c ← cList, cList UNTIL c = NIL DO
      IF c.pages # 0 THEN Space.Delete[c.space];
      cList ← c.next;
      PilotLoaderOps.FreeSpace[c];
      ENDLOOP;
    RETURN
    END;

  FindNLinks: PROC [frame: GlobalFrameHandle] RETURNS [nlinks: CARDINAL] = INLINE
    BEGIN
    RETURN[LOOPHOLE[frame.code.longbase, LONG PrefixHandle].header.info.nlinks]
    END;

  NextMultipleOfFour: PROC [n: UNSPECIFIED] RETURNS [UNSPECIFIED] = INLINE {
    RETURN[n + Inline.BITAND[-n, 3B]]};

  UnBindConfig: PROC [config: ConfigIndex, bcd: BcdBase, map: Map] =
    BEGIN OPEN PilotLoadStateOps;
    bmap: Map;
    UnBind: PROC [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: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
      RETURNS [BOOLEAN] =
      BEGIN OPEN PrincOpsRuntime;
      gfi: PrincOps.GFTIndex = bmap[mth.gfi];
      frame: GlobalFrameHandle = GetFrame[GFT[gfi]];
      changed: BOOLEAN;
      AdjustCopies: PROC [f: GlobalFrameHandle] RETURNS [BOOLEAN] = {
	[] ← AdjustFrame[f, mth]; RETURN[FALSE]};
      IF frame = NullGlobalFrame OR PilotLoaderOps.LinkSegmentLength[mth, bcd] = 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: PilotLoadStateFormat.ModuleInfo ← GetModule[gfi];
	info.resolved ← FALSE;
	EnterModule[rgfi: gfi, module: info];
	END;
      RETURN[FALSE]
      END;
    AdjustFrame: PROC [frame: GlobalFrameHandle, mth: BcdOps.MTHandle]
      RETURNS [changed: BOOLEAN] =
      BEGIN
      i: CARDINAL;
      link: PrincOps.ControlLink;
      changed ← FALSE;
      PilotLoaderOps.OpenLinkSpace[frame, mth, bcd];
      FOR i IN [0..PilotLoaderOps.LinkSegmentLength[mth, bcd]) DO
	link ← PilotLoaderOps.ReadLink[i];
	IF BoundHere[link, PilotLoaderOps.IthLink[mth,i, bcd].vtag = var] THEN
	  BEGIN
	  link ← IF link.proc OR link.indirect THEN UnboundLink ELSE TrapLink;
	  PilotLoaderOps.WriteLink[i, link];
	  changed ← TRUE;
	  END;
	ENDLOOP;
      PilotLoaderOps.CloseLinkSpace[frame];
      RETURN
      END;
    BoundHere: PROC [link: ControlLink, var: BOOLEAN] RETURNS [BOOLEAN] =
      BEGIN
      i: CARDINAL;
      gfi: GFTIndex ← GFTNull;
      SELECT TRUE FROM
	var => {
	  op: ORDERED POINTER = LOOPHOLE[link];
	  f: List;
	  IF LENGTH[map] = 2 THEN RETURN[op IN [frameStart..frameEnd)]
	  ELSE
	    FOR f ← fList, f.next UNTIL f = NIL DO
	      frameStart ← LOOPHOLE[Space.Pointer[f.space]];
	      frameEnd ← frameStart + Space.wordsPerPage*f.pages;
	      IF op IN [frameStart..frameEnd) THEN RETURN[TRUE];
	      ENDLOOP;
	  RETURN[FALSE]};
	link.proc => gfi ← link.gfi;
	ENDCASE => RETURN[FALSE];
      FOR i IN [1..LENGTH[map]) DO IF map[i] = gfi THEN RETURN[TRUE]; ENDLOOP;
      RETURN[FALSE];
      END;
    frameStart, frameEnd: ORDERED POINTER;
    IF bcd.nExports = 0 AND bcd.nModules # 1 THEN RETURN;
    frameStart ← LOOPHOLE[PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[map[1]]]];
    IF bcd.nModules = 1 THEN
      BEGIN OPEN BcdDefs;
      mth: BcdOps.MTHandle = @LOOPHOLE[bcd + bcd.mtOffset, BcdDefs.Base][
	FIRST[BcdDefs.MTIndex]];
      frameEnd ← LOOPHOLE[frameStart + mth.framesize]
      END;
    [] ← EnumerateBcds[recentfirst, UnBind];
    RETURN
    END;

  EnumerateCopies: PROC [
    frame: GlobalFrameHandle, proc: PROC [GlobalFrameHandle] RETURNS [BOOLEAN]]
    RETURNS [result: GlobalFrameHandle] =
    BEGIN
    f: GlobalFrameHandle;
    IF ~frame.shared THEN RETURN[NullGlobalFrame];
    FOR f ← GetNextGlobalFrame[NullGlobalFrame], GetNextGlobalFrame[f] UNTIL f =
      NullGlobalFrame DO
      IF f # frame AND f.copied AND SameCode[frame, f] AND proc[f] THEN EXIT;
      ENDLOOP;
    RETURN[f];
    END;

  END...