-- Loader.mesa
-- Last Modified by Levin,  September 15, 1978  10:57 AM 

DIRECTORY
  AltoDefs: FROM "altodefs" USING [PageCount],
  BcdDefs: FROM "bcddefs" USING [
    ControlLink, EPIndex, EPLimit, EXPHandle, EXPIndex, EXPNull, FTHandle,
    FTIndex, FTSelf, GFTIndex, IMPHandle, IMPIndex, MTHandle, MTIndex, MTNull,
    NameRecord, NameString, NullLink, PackedString, UnboundLink, VersionID,
    VersionStamp],
  CodeDefs: FROM "codedefs" USING [CodeHandle],
  ControlDefs: FROM "controldefs" USING [
    FrameCodeBase, Free, GFT, GlobalFrameHandle, NullGlobalFrame],
  FrameDefs: FROM "framedefs" USING [EnterGlobalFrame, EnumerateGlobalFrames],
  InlineDefs: FROM "inlinedefs" USING [BITAND, COPY],
  LoaderBcdUtilDefs: FROM "loaderbcdutildefs" USING [
    BcdBase, EnumerateExportTable, EnumerateImportTable, EnumerateModuleTable,
    ReleaseBcdSeg, SetUpBcd],
  LoaderDefs: FROM "loaderdefs" USING [FileSegmentHandle, LoaderErrorType],
  LoaderUtilityDefs: FROM "loaderutilitydefs" USING [
    AllocateSingleModule, AssignControlModules, Binding, ControlModuleFrame,
    EnterCodeFileNames, FinalizeUtilities, FindCodeSegment, FindFrameIndex,
    InitializeUtilities, InitImportBinding, LookupFileTable,
    RequiredFrameSpace],
  LoadStateDefs: FROM "loadstatedefs" USING [
    BcdHasExports, BcdHasUnresolvedImports, BcdSegFromLoadState, ConfigIndex,
    EnterGfi, InitializeRelocation, InputLoadState, MapConfigToReal,
    ReleaseLoadState, ReleaseRelocation, Relocation, SetUnresolvedImports,
    UpdateLoadState],
  SDDefs: FROM "sddefs" USING [SD, sNew],
  SegmentDefs: FROM "segmentdefs" USING [
    AddressFromPage, FileHandle, FileSegmentHandle, MoveFileSegment, NewFile,
    NewFileSegment, OldFileOnly, Read, SwapIn, SwapUp, Unlock],
  StringDefs: FROM "stringdefs" USING [
    AppendSubString, EqualSubStrings, EquivalentSubStrings,
    SubStringDescriptor],
  SystemDefs: FROM "systemdefs" USING [
    AllocateHeapNode, AllocateHeapString, AllocateResidentSegment,
    FreeHeapNode, FreeHeapString, FreePages],
  XMesaDefs: FROM "XMesaDefs" USING [XCOPY, XFileSegmentAddress];				--XM

DEFINITIONS FROM SegmentDefs, BcdDefs, LoaderDefs;

Loader: PROGRAM
  IMPORTS FrameDefs, LoaderBcdUtilDefs, LoaderUtilityDefs, LoadStateDefs,
    CodeDefs, SegmentDefs, StringDefs, SystemDefs, XMesaDefs					--XM
  EXPORTS LoaderDefs =										--XM

  BEGIN

  SSD: TYPE = StringDefs.SubStringDescriptor;
  Relocation: TYPE = LoadStateDefs.Relocation;
  BcdBase: TYPE = LoaderBcdUtilDefs.BcdBase;
  ConfigIndex: TYPE = LoadStateDefs.ConfigIndex;
  Binding: TYPE = LoaderUtilityDefs.Binding;
  GlobalFrameHandle: TYPE = ControlDefs.GlobalFrameHandle;

  LoaderError: PUBLIC SIGNAL [error: LoaderErrorType] = CODE;
  InvalidBcd: PUBLIC ERROR [bcdfile: FileHandle] = CODE;
  InvalidFile: PUBLIC ERROR [name: STRING] = CODE;
  VersionMismatch: PUBLIC SIGNAL [name: STRING] = CODE;

  NewNew: PROCEDURE [name: STRING] RETURNS [frame: GlobalFrameHandle] =
    BEGIN
    RETURN[New[Load[name], TRUE, FALSE]];
    END;

  Load: PUBLIC PROCEDURE [name: STRING] RETURNS [FileSegmentHandle] =
    BEGIN
    RETURN [LoadBcd[SegmentDefs.NewFile[name, Read, OldFileOnly]
      ! InvalidBcd => ERROR InvalidFile[name]]];
    END;

  LoadBcd: PUBLIC PROCEDURE [bcdfile: FileHandle] RETURNS [bcdseg: FileSegmentHandle] =
    BEGIN OPEN SegmentDefs;
    pages: AltoDefs.PageCount;
    bcd: BcdBase;
    bcdseg ← NewFileSegment[bcdfile, 1, 1, Read];
    bcd ← LoaderBcdUtilDefs.SetUpBcd[bcdseg];
    pages ← bcd.nPages;
    IF pages > 1 THEN
      BEGIN
      Unlock[bcdseg];
      MoveFileSegment[bcdseg, 1, pages];
      bcd ← LoaderBcdUtilDefs.SetUpBcd[bcdseg];
      END;
    BEGIN
      ENABLE UNWIND => LoaderBcdUtilDefs.ReleaseBcdSeg[bcdseg];
      IF bcd.versionident # BcdDefs.VersionID OR bcd.definitions THEN
	ERROR InvalidBcd[bcdfile];
    END; -- OF OPEN
    Unlock[bcdseg];
    END;

  New: PUBLIC PROCEDURE [bcdseg: FileSegmentHandle, framelinks, alloc: BOOLEAN]
    RETURNS [frame: GlobalFrameHandle] =
    BEGIN OPEN SegmentDefs, SystemDefs;
    NullRel: Relocation = DESCRIPTOR[NIL, 0];
    NullBind: Binding = DESCRIPTOR[NIL, 0];
    loadee, system: BcdBase ← NIL;
    LReloc: Relocation ← NullRel;
    SReloc: Relocation ← NullRel;
    LBind: Binding ← NullBind;
    SBind: Binding ← NullBind;
    frames: POINTER ← NIL;
    nbcds, i: CARDINAL;
    systemseg: FileSegmentHandle ← NIL;
    single, unresolved, sysunres, initial: BOOLEAN;
    CleanUpNew: PROCEDURE =
      BEGIN
      LoadStateDefs.ReleaseLoadState[];
      IF loadee # NIL THEN LoaderBcdUtilDefs.ReleaseBcdSeg[bcdseg];
      IF LReloc # NullRel THEN SystemDefs.FreeHeapNode[BASE[LReloc]];
      IF LBind # NullBind THEN SystemDefs.FreeHeapNode[BASE[LBind]];
      LoaderUtilityDefs.FinalizeUtilities[];
      END;
  
    BEGIN ENABLE UNWIND =>
      BEGIN
      CleanUpNew[];
      IF frames # NIL THEN
	IF single THEN ControlDefs.Free[frames] ELSE FreePages[frames];
      END;
    loadee ← LoaderBcdUtilDefs.SetUpBcd[bcdseg];
    LoaderUtilityDefs.InitializeUtilities[loadee];
    LoaderUtilityDefs.EnterCodeFileNames[loadee];
    LoaderUtilityDefs.LookupFileTable[];
    single ← loadee.nModules = 1;
    frames ← AllocateFrames[loadee, single, alloc, framelinks];
    LReloc ←
      DESCRIPTOR[AllocateHeapNode[loadee.firstdummy], loadee.firstdummy];
    nbcds ← LoadStateDefs.InputLoadState[];
    AssignFrameAddresses[frames, loadee, LReloc, nbcds, single, alloc, framelinks];
    LBind ← LoaderUtilityDefs.InitImportBinding[loadee.nDummies];
    unresolved ← loadee.nImports # 0;
    initial ← TRUE;
    IF ~unresolved THEN RelocateOnly[loadee, LReloc];
    FOR i DECREASING IN [0..nbcds) DO
      IF unresolved AND LoadStateDefs.BcdHasExports[i] THEN
	BEGIN
	  ENABLE UNWIND =>
	    IF systemseg # NIL THEN LoaderBcdUtilDefs.ReleaseBcdSeg[systemseg];
	  systemseg ← LoadStateDefs.BcdSegFromLoadState[i];
	  system ← LoaderBcdUtilDefs.SetUpBcd[systemseg];
	BindImports[loadee, system, LBind];
	unresolved ← ProcessControlLinks[loadee, system, LReloc, LBind, i, initial];
	initial ← FALSE;
	END;
      IF LoadStateDefs.BcdHasUnresolvedImports[i] AND loadee.nExports # 0 THEN
	BEGIN
	  ENABLE UNWIND =>
	    BEGIN
	    IF systemseg # NIL THEN LoaderBcdUtilDefs.ReleaseBcdSeg[systemseg];
	    IF SReloc # NullRel THEN LoadStateDefs.ReleaseRelocation[SReloc];
	    IF SBind # NullBind THEN SystemDefs.FreeHeapNode[BASE[SBind]];
	    END;
	  IF systemseg = NIL THEN
	    BEGIN
	    systemseg ← LoadStateDefs.BcdSegFromLoadState[i];
	    system ← LoaderBcdUtilDefs.SetUpBcd[systemseg];
	    END;
	SReloc ← LoadStateDefs.InitializeRelocation[i];
	SBind ← LoaderUtilityDefs.InitImportBinding[system.nDummies];
	BindImports[system, loadee, SBind];
	sysunres ←
	  ProcessControlLinks[system, loadee, SReloc, SBind, nbcds, FALSE];
	LoadStateDefs.SetUnresolvedImports[i, sysunres];
	LoadStateDefs.ReleaseRelocation[SReloc];  SReloc ← NullRel;
	SystemDefs.FreeHeapNode[BASE[SBind]];  SBind ← NullBind;
	END;
      IF systemseg # NIL THEN
	  BEGIN
	  LoaderBcdUtilDefs.ReleaseBcdSeg[systemseg];
	  systemseg ← NIL;
	  END;
      ENDLOOP;
    LoadStateDefs.UpdateLoadState[
      nbcds, bcdseg, unresolved, (loadee.nExports # 0 OR single)];
    frame ← IF single THEN LOOPHOLE[frames]
      ELSE LoaderUtilityDefs.ControlModuleFrame[loadee, LReloc];
    CleanUpNew[];
    END;
    END;

  AllocateFrames: PROCEDURE [loadee: BcdBase, single, alloc, framelinks: BOOLEAN]
    RETURNS [POINTER] =
    BEGIN OPEN SegmentDefs;
    RETURN[IF single
      THEN LoaderUtilityDefs.AllocateSingleModule[loadee, framelinks]
      ELSE SystemDefs.AllocateResidentSegment[
	LoaderUtilityDefs.RequiredFrameSpace[loadee, alloc, framelinks]]];
    END;

  AssignFrameAddresses: PROCEDURE [p: POINTER, loadee: BcdBase, Reloc: Relocation,
  config: ConfigIndex, single, alloc, allframelinks: BOOLEAN] =
    BEGIN
    frame: GlobalFrameHandle ← p;

    ModuleSearch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
      BEGIN
      seg: SegmentDefs.FileSegmentHandle;
      gfi: GFTIndex;
      i: CARDINAL;
      framelinks: BOOLEAN;

      framelinks ← allframelinks OR mth.links = frame OR ~mth.code.linkspace;
      IF ~single AND alloc THEN
	BEGIN
	p ← NextMultipleOfFour[p+1];
	(p-1)↑ ← LoaderUtilityDefs.FindFrameIndex[mth, framelinks];
	END;
      IF ~single AND framelinks THEN p ← p + mth.frame.length;
      frame ← NextMultipleOfFour[p];
      p ← frame + mth.framesize;
      gfi ← FrameDefs.EnterGlobalFrame[frame, mth.ngfi];
      FOR i IN [0..mth.ngfi) DO
	Reloc[mth.gfi+i] ← gfi + i;
	LoadStateDefs.EnterGfi[mth.gfi+i, gfi+i, config];
	ENDLOOP;
      seg ← LoaderUtilityDefs.FindCodeSegment[loadee, mth, frame];
      seg.class ← code;
      frame↑ ← [gfi: gfi, unused: 0, alloced: alloc OR single, shared: FALSE,
	copied: FALSE, started: FALSE, trapxfers: FALSE, codelinks: ~framelinks,
	code: [out[mth.code.offset]], codesegment: seg, global:];
      frame.code.swappedout ← TRUE;
      RETURN[FALSE];
      END;
    FindSharedModules: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
      BEGIN
      Search: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
	BEGIN
	frame: GlobalFrameHandle ← ControlDefs.GFT[Reloc[mth.gfi]].frame;
	IF EqualCodeSegments[f, frame] THEN
	  BEGIN f.shared ← TRUE; frame.shared ← TRUE; END;
	RETURN[FALSE];
	END;
      IF f.shared THEN RETURN[FALSE];
      [] ← LoaderBcdUtilDefs.EnumerateModuleTable[loadee, Search];
      RETURN[FALSE];
      END;
    gfi: GFTIndex;
    Reloc[0] ← 0;
    [] ← LoaderBcdUtilDefs.EnumerateModuleTable[loadee, ModuleSearch];
    FOR gfi IN [1..LENGTH[Reloc]) DO
      LoadStateDefs.EnterGfi[gfi, Reloc[gfi], config];
      ENDLOOP;
    LoaderUtilityDefs.AssignControlModules[loadee, Reloc];
    [] ← FrameDefs.EnumerateGlobalFrames[FindSharedModules];
    END;

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

  EqualCodeSegments: PROCEDURE [f1, f2: GlobalFrameHandle] RETURNS [BOOLEAN] =
    BEGIN OPEN SegmentDefs;
    fcb: ControlDefs.FrameCodeBase;
    s1, s2: FileSegmentHandle;
    o1, o2: CARDINAL;
    s1 ← CodeDefs.CodeHandle[f1];
    s2 ← CodeDefs.CodeHandle[f2];
    IF s1 = s2 THEN
      BEGIN
      IF ~f1.code.swappedout THEN
	o1 ← f1.code.codebase - AddressFromPage[s1.VMpage]
      ELSE
	BEGIN
	fcb ← f1.code;
	fcb.swappedout ← FALSE;
	o1 ← fcb.offset;
	END;
      IF ~f2.code.swappedout THEN
	o2 ← f2.code.codebase - AddressFromPage[s2.VMpage]
      ELSE
	BEGIN
	fcb ← f2.code;
	fcb.swappedout ← FALSE;
	o2 ← fcb.offset;
	END;
      RETURN[o1 = o2]
      END;
    RETURN[FALSE]
    END;

  BindImports: PROCEDURE [loadee, system: BcdBase, ImportBinding: Binding] =
    BEGIN
    ForEachImport: PROCEDURE [ith: IMPHandle, iti: IMPIndex] RETURNS [BOOLEAN] =
      BEGIN
      i: CARDINAL;
      iname, sysname: SSD;
      issb, sysssb: POINTER TO BcdDefs.PackedString;
      module: MTIndex;    export: EXPIndex;
      ExportMatch: PROCEDURE [eth: EXPHandle, eti: EXPIndex] RETURNS [BOOLEAN] =
	BEGIN OPEN StringDefs;
	sysname.offset ← eth.name;  sysname.length ← sysssb.size[eth.name]; 
	RETURN[eth.port = ith.port AND EqualSubStrings[@iname, @sysname] AND
	  EqualFiles[loadee, system, ith.file, eth.file]]
	END;
      ModuleMatch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
	BEGIN OPEN StringDefs;
	sysname.offset ← mth.name;  sysname.length ← sysssb.size[mth.name]; 
	RETURN[EqualSubStrings[@iname, @sysname] AND
	  EqualFiles[loadee, system, ith.file, mth.file]]
	END;

      issb ← LOOPHOLE[loadee+loadee.ssOffset];
      iname ← SSD[base: @issb.string, offset: ith.name, length: issb.size[ith.name]];
      sysssb ← LOOPHOLE[system+system.ssOffset];
      sysname.base ← @sysssb.string;
      export ← LoaderBcdUtilDefs.EnumerateExportTable[system, ExportMatch].eti;
      IF  export # EXPNull THEN
	FOR i IN [0..ith.ngfi) DO
	  ImportBinding[ith.gfi-loadee.firstdummy+i] ←
	    [whichgfi: i, body: interface[export]];
	  ENDLOOP
      ELSE
	BEGIN
	module ←
	  LoaderBcdUtilDefs.EnumerateModuleTable[system, ModuleMatch].mti;
	FOR i IN [0..ith.ngfi) DO
	  IF module = MTNull
	    THEN ImportBinding[ith.gfi-loadee.firstdummy+i] ←
	      [whichgfi: i, body: notbound[]]
	    ELSE ImportBinding[ith.gfi-loadee.firstdummy+i] ←
	      [whichgfi: i, body: module[module]];
	  ENDLOOP;
	END;
      RETURN[FALSE];
      END;
  
    [] ← LoaderBcdUtilDefs.EnumerateImportTable[loadee, ForEachImport];
    END;

  EqualFiles: PROCEDURE [bcd1, bcd2: BcdBase, file1, file2: FTIndex] RETURNS [BOOLEAN] =
    BEGIN
    name1, name2: SSD;
    v1, v2: POINTER TO BcdDefs.VersionStamp;
    ps1: BcdDefs.NameString ← LOOPHOLE[bcd1+bcd1.ssOffset];
    ps2: BcdDefs.NameString ← LOOPHOLE[bcd2+bcd2.ssOffset];
    name1.base ← @ps1.string;
    name2.base ← @ps2.string;
    IF file1 = FTSelf THEN
      BEGIN
      name: NameRecord =
	(LOOPHOLE[bcd1+bcd1.mtOffset, CARDINAL] + FIRST[MTIndex]).name;
      name1.offset ← name;
      name1.length ← ps1.size[name];
      v1 ← @bcd1.version;
      END
    ELSE
      BEGIN
      file: FTHandle ← LOOPHOLE[bcd1+bcd1.ftOffset, CARDINAL] + file1;
      name1.offset ← file.name;
      name1.length ← ps1.size[file.name];
      v1 ← @file.version;
      END;
    IF file2 = FTSelf THEN
      BEGIN
      name: NameRecord =
	(LOOPHOLE[bcd2+bcd2.mtOffset, CARDINAL] + FIRST[MTIndex]).name;
      name2.offset ← name;
      name2.length ← ps2.size[name];
      v2 ← @bcd2.version;
      END
    ELSE
      BEGIN
      file: FTHandle ← LOOPHOLE[bcd2+bcd2.ftOffset, CARDINAL] + file2;
      name2.offset ← file.name;
      name2.length ← ps2.size[file.name];
      v2 ← @file.version;
      END;
    IF StringDefs.EquivalentSubStrings[@name1, @name2] THEN
      IF EqVer[v1, v2] THEN RETURN[TRUE]
      ELSE
	BEGIN OPEN SystemDefs;
	filename: STRING ← AllocateHeapString[name1.length];
	StringDefs.AppendSubString[filename, @name1];
	SIGNAL VersionMismatch[filename ! UNWIND => FreeHeapString[filename]];
	FreeHeapString[filename];
	END;
    RETURN[FALSE];
    END;

  EqVer: PROCEDURE [v1, v2: POINTER TO BcdDefs.VersionStamp] RETURNS [BOOLEAN] =
    BEGIN
    RETURN [v1.zapped OR v2.zapped OR v1↑ = v2↑]
    END;


  ProcessControlLinks: PROCEDURE [loadee, system: BcdBase, Reloc: Relocation,
  ImportBinding: Binding, config: ConfigIndex, initial: BOOLEAN]
  RETURNS [BOOLEAN]=
    BEGIN
    smtb: CARDINAL = LOOPHOLE[system+system.mtOffset];
    setb: CARDINAL = LOOPHOLE[system+system.expOffset];
    unresolved: BOOLEAN ← FALSE;

    MungeControlLink: PROCEDURE [link: ControlLink, addr: LONG POINTER TO ControlLink]
      RETURNS [changedLink: BOOLEAN] =
      BEGIN
      gfi: GFTIndex;
      ep: EPIndex;
      acl: ControlLink;										--XM
      XMesaDefs.XCOPY[from: addr, to: LONG[@acl], nwords: SIZE[ControlLink]];			--XM
      changedLink ← FALSE;									--XM
      SELECT link.tag FROM
	procedure =>
	  IF acl = UnboundLink THEN								--XM
	    BEGIN
	    IF link.gfi >= loadee.firstdummy THEN
	      WITH ImportBinding[link.gfi-loadee.firstdummy] SELECT FROM
		module => SIGNAL LoaderError[Impossible];  -- Shouldn't Happen
		interface =>
		  BEGIN OPEN e: setb+eti;
		  SELECT e.port FROM
		    interface =>
		      BEGIN
		      ep ← link.ep+(whichgfi*EPLimit);
		      gfi ← LoadStateDefs.MapConfigToReal[e.links[ep].gfi, config];
		      IF gfi = 0 THEN unresolved ← TRUE
		      ELSE
			BEGIN
			changedLink ← TRUE;
			SELECT e.links[ep].tag FROM
			  procedure =>
			    BEGIN
			    acl.gfi ← gfi;							--XM
			    acl.ep ← e.links[ep].ep;						--XM
			    acl.tag ← procedure;						--XM
			    END;
			  frame => acl ← LOOPHOLE[ControlDefs.GFT[gfi].frame];			--XM
			  ENDCASE;
			END;
		      END;
		    module => SIGNAL LoaderError[Impossible];
		    ENDCASE;
		  END;
		notbound => unresolved ← TRUE;
		ENDCASE
	    ELSE
	      BEGIN
	      acl ← link;									--XM
	      acl.gfi ← Reloc[link.gfi];							--XM
	      changedLink ← TRUE;
	      END;
	    END;
	frame =>
	  IF acl = NullLink THEN								--XM
	    BEGIN
	    IF link.gfi >= loadee.firstdummy THEN
	      BEGIN
	      WITH ImportBinding[link.gfi-loadee.firstdummy] SELECT FROM
		module => gfi ← LoadStateDefs.MapConfigToReal[(smtb+mti).gfi, config];
		interface =>
		  BEGIN OPEN e: setb+eti;
		  SELECT e.port FROM
		    interface => ep ← link.ep+(whichgfi*EPLimit);
		    module => ep ← 0;
		    ENDCASE;
		  gfi ← LoadStateDefs.MapConfigToReal[e.links[ep].gfi, config];
		  END;
		notbound => gfi ← 0;
		ENDCASE;
	      IF gfi = 0 THEN unresolved ← TRUE
	      ELSE
		BEGIN
		changedLink ← TRUE;
		acl ← LOOPHOLE[ControlDefs.GFT[gfi].frame];					--XM
		END;
	      END
	    ELSE
	      BEGIN
	      changedLink ← TRUE;
	      acl ← LOOPHOLE[ControlDefs.GFT[Reloc[link.gfi]].frame];				--XM
	      END;
	    END;
	ENDCASE;
      IF changedLink THEN XMesaDefs.XCOPY[from: LONG[@acl], to: addr, nwords: SIZE[ControlLink]];	--XM
      END;

    ModuleSearch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
      BEGIN
      n: CARDINAL = 10;										--XM
      imax, j: CARDINAL;									--XM
      linkVec: ARRAY [0..n) OF ControlLink;							--XM
      i: CARDINAL;
      frame: GlobalFrameHandle ← ControlDefs.GFT[Reloc[mth.gfi]].frame;
      codesegment: SegmentDefs.FileSegmentHandle;
      linkbase: LONG POINTER TO ControlLink;
      changed: BOOLEAN;
      IF frame = ControlDefs.NullGlobalFrame THEN RETURN[FALSE];
      codesegment ← CodeDefs.CodeHandle[frame];
      IF frame.codelinks THEN
	BEGIN
	SwapIn[codesegment];
	linkbase ← LOOPHOLE[XMesaDefs.XFileSegmentAddress[codesegment]+mth.code.offset]		--XM
	END
      ELSE linkbase ← LOOPHOLE[LONG[frame]];
      linkbase ← linkbase - mth.frame.length;
      IF initial THEN
	FOR j ← 0, j+n UNTIL j >= mth.frame.length						--XM 
	  DO											--XM
	  imax ← MIN [n, mth.frame.length-j];							--XM
	  InlineDefs.COPY[from: @mth.frame.frag[j], to: @linkVec[0], nwords: imax];		--XM
	  FOR i IN [0..imax)									--XM
	    DO											--XM
	    SELECT linkVec[i].tag FROM								--XM
		procedure => linkVec[i] ← UnboundLink;						--XM
		frame => linkVec[i] ← NullLink;							--XM
		ENDCASE;									--XM
	    ENDLOOP;										--XM
	  XMesaDefs.XCOPY[from: LONG[@linkVec[0]], to: linkbase+j, nwords: imax];		--XM
	  ENDLOOP;										--XM
      changed ← FALSE;
      FOR i IN [0..mth.frame.length) DO
	changed ← MungeControlLink[mth.frame.frag[i], linkbase+i] OR changed;
	ENDLOOP;
      IF frame.codelinks THEN
	BEGIN											--XM
	codesegment.write ← changed OR initial;							--XM
	KickOutCode[codesegment];								--XM
	END;											--XM
      RETURN[FALSE];
      END;

    [] ← LoaderBcdUtilDefs.EnumerateModuleTable[loadee, ModuleSearch];
    RETURN[unresolved];
    END;

  KickOutCode: PROCEDURE[seg: FileSegmentHandle] =						--XM
    BEGIN
    Unlock[seg];
    IF seg.write THEN BEGIN SwapUp[seg]; seg.write ← FALSE; END
    END;

  RelocateOnly: PROCEDURE [loadee: BcdBase, Reloc: Relocation]=
    BEGIN
    ModuleSearch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
      BEGIN
      i: CARDINAL;
      frame: GlobalFrameHandle ← ControlDefs.GFT[Reloc[mth.gfi]].frame;
      codesegment: SegmentDefs.FileSegmentHandle;
      codelinks: BOOLEAN ← frame.codelinks;
      linkbase: LONG POINTER TO ControlLink;
      IF mth.frame.length = 0 THEN RETURN[FALSE];
      codesegment ← CodeDefs.CodeHandle[frame];
      IF frame.codelinks THEN
	BEGIN
	SwapIn[codesegment];
	linkbase ← LOOPHOLE[XMesaDefs.XFileSegmentAddress[codesegment]+mth.code.offset]		--XM
	END
      ELSE linkbase ← LOOPHOLE[LONG[frame]];
      linkbase ← linkbase - mth.frame.length;
      FOR i IN [0..mth.frame.length) DO
	OPEN link: mth.frame.frag[i];
	SELECT link.tag FROM
	  procedure =>
	    BEGIN
	    (linkbase+i)↑ ← link;
	    (linkbase+i).gfi ← Reloc[link.gfi];
	    END;
	  frame =>
	    (linkbase+i)↑ ← LOOPHOLE[ControlDefs.GFT[Reloc[link.gfi]].frame];
	  ENDCASE;
	ENDLOOP;
      IF codelinks THEN
	BEGIN
	codesegment.write ← TRUE;
	KickOutCode[codesegment];								--XM
	END;
      RETURN[FALSE];
      END;

    [] ← LoaderBcdUtilDefs.EnumerateModuleTable[loadee, ModuleSearch];
    RETURN
    END;

  SDDefs.SD[SDDefs.sNew] ← NewNew;

  END...