-- file Pass4B.Mesa
-- last modified by Satterthwaite, October 30, 1979  2:59 PM

DIRECTORY
  AltoDefs: FROM "altodefs" USING [PageSize],
  BcdDefs: FROM "bcddefs"
    USING [
      BCD, CodeDesc, ControlLink, EPIndex, EVIndex, EVRecord, EXPRecord,
      FrameFrag, FTIndex, FTRecord, GFTIndex, IMPIndex, IMPRecord,
      MTRecord, Namee, NameRecord, NameString, NTRecord, SGIndex, SGRecord,
      VersionID,
      CTNull, EVNull, FTSelf, NullLink, NullName],
  ComData: FROM "comdata"
    USING [
      codeSeg, compilerVersion, definitionsOnly, fixupLoc, mainBody,
      mainCtx, moduleCtx, mtRoot, nBodies, nSigCodes, objectVersion,
      ownSymbols, sourceFile, sourceVersion, switches, symSeg, textIndex],
  CompilerUtil: FROM "compilerutil"
    USING [
      AppendBCDString, AppendBCDWord, AppendBCDWords, EndBCD,
      ReadBCDIndex, ReadBCDOffset, StartBCD, UpdateBCDWords],
  Copier: FROM "copier" USING [FreeSymbolTable, GetSymbolTable],
  Log: FROM "log" USING [ErrorN, ErrorSei, WarningSei],
  P4: FROM "p4" USING [OperandType],
  Pass4: FROM "pass4" USING [resident],
  StringDefs: FROM "stringdefs"
    USING [
      SubString, SubStringDescriptor,
      AppendString, AppendSubString, EqualSubStrings, EquivalentSubStrings],
  Symbols: FROM "symbols"
    USING [bodyType, ctxType, mdType, seType,
      HTIndex, ISEIndex, CSEIndex, RecordSEIndex, CTXIndex, IncludedCTXIndex,
      BitAddress, Linkage, MDRecord, MDIndex,
      HTNull, SENull, ISENull, CTXNull, OwnMdi],
  SymbolOps: FROM "symbolops"
    USING [
      FindString, NextSe, SearchContext, SubStringForHash,
      UnderType, XferMode],
  SystemDefs: FROM "systemdefs"
    USING [
      AllocateHeapNode, AllocateHeapString, FreeHeapNode, FreeHeapString],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [treeType, Index, Link, Map, Scan],
  TreeOps: FROM "treeops" USING [GetNode, ListLength, ScanList, UpdateList],
  Types: FROM "types" USING [SymbolTableBase, Assignable, Equivalent];

Pass4B: PROGRAM
    IMPORTS
	CompilerUtil, Copier, Log, P4, StringDefs, SymbolOps,
	SystemDefs, TreeOps, Types,
	dataPtr: ComData, passPtr: Pass4
    EXPORTS P4 =
  BEGIN
  OPEN SymbolOps, Symbols;

  tb: Table.Base;	-- tree base address (local copy)
  seb: Table.Base;	-- se table base address (local copy)
  ctxb: Table.Base;	-- context table base address (local copy)
  mdb: Table.Base;	-- body table base address (local copy)
  bb: Table.Base;	-- body table base address (local copy)

  BCDNotify: PUBLIC Table.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    tb ← base[Tree.treeType];
    seb ← base[seType];  ctxb ← base[ctxType];  mdb ← base[mdType];
    bb ← base[bodyType];
    END;


 -- shared variables

  bcdHeader: POINTER TO BcdDefs.BCD;
  bcdOffset, mtOffset: CARDINAL;

  nString: BcdDefs.NameString;

  firstPorted: MDIndex = FIRST[MDIndex] + SIZE[MDRecord];
  lastPorted: MDIndex;	-- im/exported files in [firstPorted..lastPorted)

 -- service routines

  GFTIndex: TYPE = BcdDefs.GFTIndex;
  EPIndex: TYPE = BcdDefs.EPIndex;
  EPLimit: CARDINAL = LAST[EPIndex]+1;
  ControlLink: TYPE = BcdDefs.ControlLink;

  OwnGfi: GFTIndex = 1;

  GFSlots: PROCEDURE [epMax: EPIndex] RETURNS [nGfi: [1..4]] =
    BEGIN
    nGfi ← epMax/EPLimit + 1;  RETURN
    END;

  MakeEPLink: PUBLIC PROCEDURE [ep: CARDINAL, gfi: GFTIndex] RETURNS [ControlLink] =
    BEGIN
    RETURN [ControlLink[procedure[
	tag: procedure,
	ep: ep MOD EPLimit,
	gfi: gfi + ep/EPLimit]]]
    END;

  MakeFrameLink: PROCEDURE [ep: CARDINAL, gfi: GFTIndex] RETURNS [ControlLink] =
    BEGIN
    RETURN [ControlLink[procedure[
	tag: frame,
	ep: ep MOD EPLimit,
	gfi: gfi + ep/EPLimit]]]
    END;


  PortedFile: PROCEDURE [ctx: CTXIndex] RETURNS [fti: BcdDefs.FTIndex] =
    BEGIN
    mdi: MDIndex;
    n: CARDINAL;
    mdi ← WITH c: ctxb[ctx] SELECT FROM
      included => c.module,
      imported => ctxb[c.includeLink].module,
      ENDCASE => OwnMdi;
    IF mdi = OwnMdi
      THEN  fti ← BcdDefs.FTSelf
      ELSE
	BEGIN
	IF mdi IN [firstPorted .. lastPorted)
	  THEN  n ← LOOPHOLE[mdi-firstPorted, CARDINAL]/SIZE[MDRecord]
	  ELSE
	    BEGIN
	    n ← LOOPHOLE[lastPorted-firstPorted, CARDINAL]/SIZE[MDRecord];
	    SwapMdi[mdi, lastPorted];
	    lastPorted ← lastPorted + SIZE[MDRecord];
	    END;
	fti ← LOOPHOLE[n*SIZE[BcdDefs.FTRecord]];
	END;
    RETURN
    END;

  SwapMdi: PROCEDURE [mdi1, mdi2: MDIndex] =
    BEGIN
    ctx: IncludedCTXIndex;
    t: MDRecord;
    IF mdi1 # mdi2
      THEN
 	BEGIN
	FOR ctx ← mdb[mdi1].ctx, ctxb[ctx].chain UNTIL ctx = CTXNull
	  DO  ctxb[ctx].module ← mdi2  ENDLOOP;
	FOR ctx ← mdb[mdi2].ctx, ctxb[ctx].chain UNTIL ctx = CTXNull
	  DO  ctxb[ctx].module ← mdi1  ENDLOOP;
	t ← mdb[mdi1];  mdb[mdi1] ← mdb[mdi2];  mdb[mdi2] ← t;
	END;
    END;


  SubString: TYPE = StringDefs.SubString;
  SubStringDescriptor: TYPE = StringDefs.SubStringDescriptor;

  EnterId: PROCEDURE [id: SubString, ignoreCase: BOOLEAN] RETURNS [BcdDefs.NameRecord] =
    BEGIN
    i: CARDINAL;
    desc: SubStringDescriptor;
    s: SubString = @desc;
    t: BcdDefs.NameString;
    i ← 0;  s.base ← @nString.string;
    UNTIL i = nString.string.length
      DO
      s.offset ← i ← i+1;  s.length ← nString.size[i];
      IF (IF ignoreCase
	    THEN StringDefs.EquivalentSubStrings
	    ELSE StringDefs.EqualSubStrings)[id, s]
	THEN EXIT;
      i ← i + s.length;
      REPEAT
	FINISHED =>
	  BEGIN
	  IF nString.string.length + (id.length+1) > nString.string.maxlength
	    THEN
	      BEGIN	-- rewrite if nString is in table area
	      t ← LOOPHOLE[SystemDefs.AllocateHeapString[
		    nString.string.maxlength + MAX[(id.length+1), 32]]];
	      StringDefs.AppendString[@t.string, @nString.string];
	      SystemDefs.FreeHeapString[@nString.string];  nString ← t;
	      END;
	  i ← nString.string.length ← nString.string.length + 1;
	  nString.size[i] ← id.length;
	  StringDefs.AppendSubString[@nString.string, id];
	  END;
      ENDLOOP;
    RETURN [[i]]
    END;

  EnterSymbolId: PROCEDURE [sei: ISEIndex] RETURNS [BcdDefs.NameRecord] =
    BEGIN
    s: SubStringDescriptor;
    SubStringForHash[@s, seb[sei].hash];
    RETURN [EnterId[@s, FALSE]]
    END;

  EnterFileId: PROCEDURE [mdi: MDIndex] RETURNS [BcdDefs.NameRecord] =
    BEGIN
    s, dExt: SubStringDescriptor;
    bcd: STRING = ".bcd.";
    dBcd: SubStringDescriptor ← [base:bcd, offset:0, length:bcd.length];
    SubStringForHash[@s, mdb[mdi].fileId];
    dExt ← [base: s.base, offset: s.offset+s.length-bcd.length, length: bcd.length];
    IF StringDefs.EquivalentSubStrings[@dExt, @dBcd]
      THEN s.length ← s.length - bcd.length;
    RETURN [EnterId[@s, TRUE]]
    END;


  TreeSymbol: PROCEDURE [t: Tree.Link] RETURNS [ISEIndex] =
    BEGIN
    WITH t SELECT FROM
      symbol => RETURN [index];
      ENDCASE => ERROR;
    END;


 -- relocating imported control links

  RelocateImports: PROCEDURE [ctx: CTXIndex, gfi: GFTIndex] RETURNS [epMax: EPIndex] =
    BEGIN
    sei: ISEIndex;
    epN: CARDINAL;
    epMax ← 0;
    IF ctx = CTXNull THEN RETURN;
    FOR sei ← ctxb[ctx].seList, NextSe[sei] UNTIL sei = SENull
      DO
      IF ~seb[sei].constant THEN
	BEGIN
	epN ← seb[sei].idValue;  epMax ← MAX[epN, epMax];
	seb[sei].idValue ← SELECT XferMode[seb[sei].idType] FROM
	  procedure, signal, error => MakeEPLink[epN, gfi],
	  ENDCASE => MakeFrameLink[epN, gfi];
	END;
      ENDLOOP;
    RETURN
    END;

  AssignImports: PUBLIC Tree.Scan =
    BEGIN
    gfi: GFTIndex;
    saveIndex: CARDINAL = dataPtr.textIndex;

    ImportItem: Tree.Scan =
      BEGIN
      node: Tree.Index = TreeOps.GetNode[t];
      sei: ISEIndex = TreeSymbol[tb[node].son[1]];
      type: CSEIndex = UnderType[seb[sei].idType];
      epMax: EPIndex;
      dataPtr.textIndex ← tb[node].info;
      WITH seb[type] SELECT FROM
	definition =>
	  BEGIN
	  IF ctxb[defCtx].seList = SENull
	    THEN
	      WITH c: ctxb[defCtx] SELECT FROM
		imported =>
		  IF tb[node].attr1
		   OR ~mdb[ctxb[c.includeLink].module].exported
		    THEN  Log.WarningSei[unusedImport, sei];
		ENDCASE;
	  epMax ← RelocateImports[defCtx, gfi];
	  seb[sei].idValue ← gfi;
	  gfi ← gfi + (seb[sei].idInfo ← nGfi);
	  -- consider adding GFSlots[epMax] instead --
	  END;
	pointer =>
	  BEGIN
	  seb[sei].idValue ← MakeFrameLink[ep:0, gfi:gfi];
	  gfi ← gfi + 1;
	  END;
	ENDCASE;
      seb[sei].mark4 ← TRUE;
      END;

    dataPtr.mtRoot.gfi ← OwnGfi;
    dataPtr.mtRoot.ngfi ← GFSlots[MAX[dataPtr.nBodies, dataPtr.nSigCodes]-1];
    gfi ← bcdHeader.firstdummy ← OwnGfi + dataPtr.mtRoot.ngfi;
    IF ~dataPtr.definitionsOnly THEN TreeOps.ScanList[t, ImportItem];
    bcdHeader.nDummies ← gfi - bcdHeader.firstdummy;
    dataPtr.textIndex ← saveIndex;
    END;


 -- writing import records

  ProcessImports: PUBLIC Tree.Scan =
    BEGIN
    -- N.B. nextGfi must be regenerated to match AssignImports
    nImports: CARDINAL;
    impi: BcdDefs.IMPIndex;
    nextGfi: GFTIndex;
    anyNamed: BOOLEAN;

    ImportItem: Tree.Scan =
      BEGIN
      node: Tree.Index = TreeOps.GetNode[t];
      sei1: ISEIndex = TreeSymbol[tb[node].son[1]];
      sei2: ISEIndex = TreeSymbol[tb[node].son[2]];
      type: CSEIndex;
      rType: RecordSEIndex;
      entry: BcdDefs.IMPRecord;
      entry ← [
		name: EnterSymbolId[sei2],
		port: interface,
		namedinstance: seb[sei1].hash # seb[sei2].hash,
		file: ,
		gfi: ,
		ngfi: ];
      type ← UnderType[seb[sei1].idType];
      WITH seb[type] SELECT FROM
	definition =>
	  BEGIN
	  entry.file ← PortedFile[defCtx];
	  entry.gfi ← seb[sei1].idValue;
	  entry.ngfi ← seb[sei1].idInfo;
	  nextGfi ← seb[sei1].idValue + seb[sei1].idInfo;
	  END;
	pointer =>
	  BEGIN
	  entry.port ← module;
	  rType ← LOOPHOLE[UnderType[refType]];
	  entry.file ← PortedFile[seb[rType].fieldCtx];
	  entry.gfi ← nextGfi;  entry.ngfi ← 1;
	  nextGfi ← nextGfi + 1;
	  END;
	ENDCASE;
      nImports ← nImports + 1;
      IF entry.namedinstance THEN anyNamed ← TRUE;
      CompilerUtil.AppendBCDWords[@entry, SIZE[BcdDefs.IMPRecord]];
      END;

    NameItem: Tree.Scan =
      BEGIN
      node: Tree.Index = TreeOps.GetNode[t];
      sei1: ISEIndex = TreeSymbol[tb[node].son[1]];
      sei2: ISEIndex = TreeSymbol[tb[node].son[2]];
      entry: BcdDefs.NTRecord;
      IF  seb[sei1].hash # seb[sei2].hash
	THEN
	  BEGIN
	  entry ← [
		name: EnterSymbolId[sei1],
		item: BcdDefs.Namee[import[impi]]];
	  CompilerUtil.AppendBCDWords[@entry, SIZE[BcdDefs.NTRecord]];
	  END;
      impi ← impi + SIZE[BcdDefs.IMPRecord];
      END;

    offset: CARDINAL;
    bcdHeader.impOffset ← offset ← CompilerUtil.ReadBCDOffset[];
    nImports ← 0;  impi ← FIRST[BcdDefs.IMPIndex];
    nextGfi ← bcdHeader.firstdummy;  anyNamed ← FALSE;
    IF ~dataPtr.definitionsOnly THEN TreeOps.ScanList[t, ImportItem];
    bcdHeader.nImports ← nImports;
    bcdHeader.impLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-offset];
    bcdHeader.ntOffset ← offset ← CompilerUtil.ReadBCDOffset[];
    IF anyNamed THEN TreeOps.ScanList[t, NameItem];
    bcdHeader.ntLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-offset];
    END;


 -- writing export records

  maxEVLength: CARDINAL;
  evList: POINTER TO BcdDefs.EVRecord;

  EnterEVOffset: PROCEDURE [offset: CARDINAL] RETURNS [index: CARDINAL] =
    BEGIN
    IF offset = 0
      THEN  index ← 0
      ELSE
	FOR index IN [1 .. evList.length]
	  DO
	  IF offset = evList.offsets[index] THEN EXIT;
	  REPEAT
	    FINISHED =>
	      BEGIN
	      index ← evList.length ← evList.length + 1;
	      IF index <= maxEVLength THEN evList.offsets[index] ← offset;
	      END;
	  ENDLOOP;
    RETURN
    END;


  ExportId: Tree.Map =
    BEGIN
    type: CSEIndex = P4.OperandType[t];
    ctx: IncludedCTXIndex;
    iBase: Types.SymbolTableBase;
    id, sei, iSei: ISEIndex;
    mode: Linkage;
    link: ControlLink;
    public, used: BOOLEAN;
    id ← TreeSymbol[t];
    WITH v: seb[type] SELECT FROM
      definition =>
	BEGIN  ctx ← LOOPHOLE[v.defCtx];
	iBase ← Copier.GetSymbolTable[ctxb[ctx].module];
	IF iBase # NIL
	  THEN
	    BEGIN
	      BEGIN
	      header: BcdDefs.EXPRecord ← [
		  name: EnterSymbolId[id],
		  size: 0,
		  port: interface,
		  namedinstance: FALSE,
		  file: PortedFile[v.defCtx],
		  links: ];
	      FOR iSei ← iBase.FirstCtxSe[ctxb[ctx].map],
	       iBase.NextSe[iSei] UNTIL iSei = SENull
		DO
		IF iBase.LinkMode[iSei] # manifest
		  THEN  header.size ← header.size + 1;
		ENDLOOP;
	      CompilerUtil.AppendBCDWords[@header, SIZE[BcdDefs.EXPRecord]];
	      END;
	    used ← FALSE;
	    FOR iSei ← iBase.FirstCtxSe[ctxb[ctx].map],
	     iBase.NextSe[iSei] UNTIL iSei = SENull
	      DO
	      mode ← iBase.LinkMode[iSei];  link ← BcdDefs.NullLink;
		BEGIN
		ss: SubStringDescriptor;
		hti: HTIndex;
		iBase.SubStringForHash[@ss, iBase.seb[iSei].hash];
		hti ← FindString[@ss];
		IF hti = HTNull
		  THEN  sei ← ISENull
		  ELSE
		    BEGIN
		    sei ← SearchContext[hti, dataPtr.mainCtx];
		    IF sei = SENull
		      THEN sei ← SearchContext[hti, dataPtr.moduleCtx];
		    END;
		END;
	      IF sei # SENull THEN
		BEGIN
		public ← seb[sei].public;
		SELECT mode FROM
		  val =>
		    BEGIN
		    IF ~Types.Assignable[
			[iBase, iBase.UnderType[iBase.seb[iSei].idType]],
			[dataPtr.ownSymbols, UnderType[seb[sei].idType]]]
		      THEN
			BEGIN IF public THEN Log.ErrorSei[exportClash,sei] END
		      ELSE
			IF ~public AND seb[sei].idCtx = dataPtr.mainCtx
			  THEN Log.WarningSei[privateExport, sei];
		    IF public THEN
		      BEGIN
		      IF ~seb[sei].constant OR seb[sei].extended
			THEN Log.ErrorSei[varExport, sei];
		      link ← IF XferMode[seb[sei].idType] = program
			  THEN MakeFrameLink[ep:EnterEVOffset[0], gfi:OwnGfi]
			  ELSE seb[sei].idValue;
		      END;
		    END;
		  ref =>
		    BEGIN
		    IF ~Types.Equivalent[
			[iBase, iBase.UnderType[iBase.seb[iSei].idType]],
			[dataPtr.ownSymbols, UnderType[seb[sei].idType]]]
		      THEN
			BEGIN IF public THEN Log.ErrorSei[exportClash,sei] END
		      ELSE
			IF ~public AND seb[sei].idCtx = dataPtr.mainCtx
			  THEN Log.WarningSei[privateExport, sei];
		    IF public THEN
		      BEGIN
		      SELECT TRUE FROM
			seb[sei].constant => Log.ErrorSei[varExport, sei];
			seb[sei].immutable AND ~iBase.seb[iSei].immutable =>
			  Log.ErrorSei[exportClash, sei];
			ENDCASE;
		      link ← MakeFrameLink[
			      ep: EnterEVOffset[
				   LOOPHOLE[seb[sei].idValue, BitAddress].wd],
			      gfi: OwnGfi];
		      END;
		    END;
		  manifest =>  IF public THEN Log.WarningSei[voidExport, sei];
		  ENDCASE;
		END;
	      IF link # BcdDefs.NullLink THEN used ← TRUE;
	      IF mode # manifest THEN CompilerUtil.AppendBCDWord[link];
	      ENDLOOP;
	    Copier.FreeSymbolTable[iBase];
	    IF ~used THEN Log.WarningSei[unusedExport, id];
	    END;
	END;
      ENDCASE;
    RETURN [t]
    END;

  ProcessExports: PUBLIC Tree.Map =
    BEGIN
    offset: CARDINAL;
    bcdHeader.nExports ← TreeOps.ListLength[t];
    bcdHeader.expOffset ← offset ← CompilerUtil.ReadBCDOffset[];
    maxEVLength ← dataPtr.mtRoot.ngfi*EPLimit - 1;
    evList ← SystemDefs.AllocateHeapNode[SIZE[BcdDefs.EVRecord]+maxEVLength];
    evList↑ ← [length:0, offsets:];
    v ← TreeOps.UpdateList[t, ExportId];
    bcdHeader.expLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-offset];
    bcdHeader.evOffset ← offset ← CompilerUtil.ReadBCDOffset[];
    IF evList.length > maxEVLength
      THEN  Log.ErrorN[exportedVars, evList.length-maxEVLength];
    IF evList.length = 0
      THEN  dataPtr.mtRoot.variables ← BcdDefs.EVNull
      ELSE
	BEGIN
	dataPtr.mtRoot.variables ← FIRST[BcdDefs.EVIndex];
	CompilerUtil.AppendBCDWords[evList,
	    SIZE[BcdDefs.EVRecord] + MIN[evList.length, maxEVLength]];
	END;
    bcdHeader.evLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-offset];
    SystemDefs.FreeHeapNode[evList];
    RETURN
    END;

 -- initialization/finalization

  ProcessFiles: PROCEDURE =
    BEGIN
    ftEntry: BcdDefs.FTRecord;
    mdi: MDIndex;
    offset: CARDINAL = CompilerUtil.ReadBCDOffset[];
    bcdHeader.ftOffset ← offset;
    FOR mdi ← firstPorted, mdi+SIZE[MDRecord] UNTIL mdi = lastPorted
      DO
      ftEntry ← [name: EnterFileId[mdi], version: mdb[mdi].stamp];
      CompilerUtil.AppendBCDWords[@ftEntry, SIZE[BcdDefs.FTRecord]];
      ENDLOOP;
    bcdHeader.ftLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[] - offset];
    END;


  MTRootSize: CARDINAL = SIZE[BcdDefs.MTRecord]-SIZE[BcdDefs.FrameFrag];

  InitBCD: PUBLIC PROCEDURE =
    BEGIN  OPEN BcdDefs;
    lastPorted ← firstPorted;
    nString ← LOOPHOLE[SystemDefs.AllocateHeapString[64]];
    -- allocate the null name
      nString.string.length ← BcdDefs.NullName;
      nString.size[BcdDefs.NullName] ← 0;
    CompilerUtil.StartBCD[];
    bcdHeader ← SystemDefs.AllocateHeapNode[SIZE[BCD]];
      bcdHeader.versionident ← VersionID;
      bcdHeader.version ← dataPtr.objectVersion;
      bcdHeader.sourceVersion ← dataPtr.sourceVersion;
      bcdHeader.creator ← dataPtr.compilerVersion;
      bcdHeader.nConfigs ← 0;
      bcdHeader.nModules ← 1;
      bcdHeader.nImports ← bcdHeader.nExports ← 0;
      bcdHeader.definitions ← dataPtr.definitionsOnly;
      bcdHeader.ctOffset ← 0;  bcdHeader.ctLimit ← LOOPHOLE[0];
      bcdHeader.spOffset ← 0;  bcdHeader.spLimit ← LOOPHOLE[0];
      nString.string.length ← nString.string.length + 1;
      bcdHeader.source ← NameRecord[nString.string.length];
	nString.size[bcdHeader.source] ← dataPtr.sourceFile.length;
	StringDefs.AppendString[@nString.string, dataPtr.sourceFile];
    bcdOffset ← CompilerUtil.ReadBCDOffset[];
    CompilerUtil.AppendBCDWords[bcdHeader, SIZE[BCD]];
    dataPtr.fixupLoc ← CompilerUtil.ReadBCDIndex[];
    bcdHeader.sgOffset ← CompilerUtil.ReadBCDOffset[];
    CompilerUtil.AppendBCDWords[@dataPtr.codeSeg, SIZE[SGRecord]];
    CompilerUtil.AppendBCDWords[@dataPtr.symSeg, SIZE[SGRecord]];
    bcdHeader.mtOffset ← mtOffset ← CompilerUtil.ReadBCDOffset[];
    bcdHeader.sgLimit ← LOOPHOLE[mtOffset - bcdHeader.sgOffset];
    CompilerUtil.AppendBCDWords[@dataPtr.mtRoot, MTRootSize];
    END;

  FinishBCD: PUBLIC PROCEDURE =
    BEGIN  OPEN BcdDefs;
    PageSize: CARDINAL = AltoDefs.PageSize;
    bcdSize: CARDINAL;
    -- fill MTRecord
      dataPtr.mtRoot.name ← EnterSymbolId[bb[dataPtr.mainBody].id];
      dataPtr.mtRoot.namedinstance ← FALSE;
      dataPtr.mtRoot.initial ← FALSE;
      dataPtr.mtRoot.file ← dataPtr.codeSeg.file ← dataPtr.symSeg.file ←
	PortedFile[dataPtr.mainCtx];
      dataPtr.mtRoot.links ← frame;
      dataPtr.mtRoot.config ← CTNull;
      dataPtr.mtRoot.code ←
	CodeDesc[
	    sgi: FIRST[SGIndex],
	    packed: FALSE, linkspace: FALSE,
	    offset: 0, length: ];
      dataPtr.mtRoot.sseg ← FIRST[SGIndex] + SIZE[SGRecord];
      dataPtr.mtRoot.altoCode ← dataPtr.switches['a];
      dataPtr.mtRoot.residentFrame ← passPtr.resident;
      dataPtr.mtRoot.attr3 ← FALSE;
      dataPtr.mtRoot.sseg ← FIRST[SGIndex] + SIZE[SGRecord];
    bcdHeader.mtLimit ← LOOPHOLE[bcdHeader.impOffset-bcdHeader.mtOffset];
    ProcessFiles[];
    bcdHeader.ssOffset ← CompilerUtil.ReadBCDOffset[];
    CompilerUtil.AppendBCDString[@nString.string];
    bcdSize ← CompilerUtil.ReadBCDOffset[];
    bcdHeader.ssLimit ← LOOPHOLE[bcdSize-bcdHeader.ssOffset];
    bcdHeader.nPages ← (bcdSize + (PageSize-1))/PageSize;
    CompilerUtil.UpdateBCDWords[bcdOffset, bcdHeader, SIZE[BCD]];
    CompilerUtil.EndBCD[];
    SystemDefs.FreeHeapString[@nString.string];
    SystemDefs.FreeHeapNode[bcdHeader];
    END;

  END.