-- file ObjectOut.Mesa
-- last modified by Satterthwaite, June 3, 1983 10:27 am
-- last modified by Sweet, September 2, 1980  3:05 PM

DIRECTORY
  Alloc: TYPE USING [Base, Handle, Notifier, AddNotify, DropNotify, Bounds],
  BcdDefs: TYPE USING [SGRecord, VersionStamp, FTNull],
  ComData: TYPE USING [
    compilerVersion, codeSeg, defBodyLimit, fgTable, fixupLoc, globalFrameSize,
    importCtx, interface, mainCtx, moduleCtx, mtRoot, mtRootSize,
    nBodies, nInnerBodies, objectBytes, objectVersion, ownSymbols, source,
    symSeg, typeAtomRecord],
  CompilerUtil: TYPE USING [Address],
  Environment: TYPE USING [bytesPerWord, wordsPerPage],
  FileStream: TYPE USING [FileByteIndex, GetIndex, SetIndex],
  Inline: TYPE USING [LongCOPY],
  Literals: TYPE USING [Base, STNull],
  LiteralOps: TYPE USING [CopyLiteral, ForgetEntries, StringValue, TextType],
  OSMiscOps: TYPE USING [FreeWords, Words],
  PackageSymbols: TYPE USING [OuterPackRecord, InnerPackRecord, IPIndex, IPNull],
  RCMap: TYPE USING [Base],
  RCMapOps: TYPE USING [Acquire, Finalize, GetBase, Initialize],
  RTBcd: TYPE USING [
    RefLitItem, RefLitList, RTHeader, StampIndex, StampList,TypeItem, TypeList,
    UTInfo, AnyStamp],
  Stream: TYPE USING [Handle, PutBlock, PutWord],
  Strings: TYPE USING [String, SubString, SubStringDescriptor, AppendSubString],
  Symbols: TYPE USING [
    Base, HashVector, Name, Type, MDIndex, BodyInfo, BTIndex,
    MDNull, OwnMdi, BTNull, RootBti, lL],
  SymbolSegment: TYPE USING [
    Base, FGHeader, FGTEntry, ExtRecord, ExtIndex, STHeader, WordOffset, VersionID,
    ltType, htType, ssType, seType, ctxType, mdType, bodyType, extType, constType],
  SymbolOps: TYPE USING [
    EnumerateBodies, HashBlock, NameForSe, SiblingBti, SonBti, SubStringForName],
  SymLiteralOps: TYPE USING [
    RefLitItem, DescribeRefLits, DescribeTypes, EnumerateRefLits, EnumerateTypes,
    TypeIndex, UTypeId],
  Table: TYPE USING [IPointer, Selector],
  Tree: TYPE USING [Base, Index, Link, Map, Node, Null, NullIndex, treeType],
  TreeOps: TYPE USING [FreeTree, NodeSize, UpdateLeaves],
  TypeStrings: TYPE USING [Create];

ObjectOut: PROGRAM
    IMPORTS
      Alloc, FileStream, Inline, OSMiscOps, LiteralOps, RCMapOps,
      Stream, Strings, SymbolOps, SymLiteralOps, TreeOps, TypeStrings,
      dataPtr: ComData
    EXPORTS CompilerUtil = {

  StreamIndex: TYPE = FileStream.FileByteIndex;
  Address: TYPE = CompilerUtil.Address;
  
  GetShortIndex: PROC [stream: Stream.Handle] RETURNS [CARDINAL] = INLINE {
    RETURN [FileStream.GetIndex[stream]]};

  stream: Stream.Handle ← NIL;
  zone: UNCOUNTED ZONE ← NIL;
  
  PageSize: CARDINAL = Environment.wordsPerPage;
  BytesPerWord: CARDINAL = Environment.bytesPerWord;
  BytesPerPage: CARDINAL = PageSize*BytesPerWord;

  NextFilePage: PUBLIC PROC RETURNS [CARDINAL] = {
    fill: ARRAY [0..8) OF WORD ← ALL[0];
    r: INTEGER = (GetShortIndex[stream] MOD BytesPerPage)/BytesPerWord;
    m: INTEGER;
    IF r # 0 THEN
      FOR n: INTEGER ← PageSize-r, n-m WHILE n > 0 DO
	m ← MIN[n, fill.LENGTH];
	stream.PutBlock[[fill.BASE, 0, m*BytesPerWord]];
	ENDLOOP;
    RETURN [GetShortIndex[stream]/BytesPerPage + 1]};

  WriteObjectWords: PROC [addr: Address, n: CARDINAL] = {
    stream.PutBlock[[addr, 0, n*BytesPerWord]]};

  RewriteObjectWords: PROC [index: StreamIndex, addr: Address, n: CARDINAL] = {
    saveIndex: StreamIndex = FileStream.GetIndex[stream];
    FileStream.SetIndex[stream, index];
    stream.PutBlock[[addr, 0, n*BytesPerWord]];
    FileStream.SetIndex[stream, saveIndex]};

  WriteTableBlock: PROC [p: Table.IPointer, size: CARDINAL] = {
    stream.PutBlock[[p, 0, size*BytesPerWord]]};

 -- bcd i/o

  bcdOffset: CARDINAL;
  bcdIndex: StreamIndex;
  
  BCDIndex: PROC [offset: CARDINAL] RETURNS [StreamIndex] = INLINE {
    RETURN [bcdIndex + offset*BytesPerWord]};

  StartBCD: PUBLIC PROC = {
    [] ← NextFilePage[];
    bcdIndex ← FileStream.GetIndex[stream];
    bcdOffset ← 0};

  ReadBCDOffset: PUBLIC PROC RETURNS [CARDINAL] = {RETURN [bcdOffset]};

  ReadBCDIndex: PUBLIC PROC RETURNS [StreamIndex] = {
    RETURN [BCDIndex[bcdOffset]]};

  AppendBCDWord: PUBLIC PROC [word: UNSPECIFIED] = {
    stream.PutWord[word]; bcdOffset ← bcdOffset + 1};

  AppendBCDWords: PUBLIC PROC [addr: Address, n: CARDINAL] = {
    WriteObjectWords[addr, n]; bcdOffset ← bcdOffset + n};

  AppendBCDString: PUBLIC PROC [s: Strings.String] = {
    header: StringBody ← [length: s.length, maxlength: s.length, text:];
    AppendBCDWords[@header, StringBody[0].SIZE];
    AppendBCDWords[@s.text, StringBody[s.length].SIZE - StringBody[0].SIZE]};

  FillBCDPage: PUBLIC PROC = {
    IF bcdOffset MOD PageSize # 0 THEN {
      [] ← NextFilePage[]; bcdOffset ← bcdOffset + (PageSize - bcdOffset MOD PageSize)}};

  UpdateBCDWords: PUBLIC PROC [offset: CARDINAL, addr: Address, n: CARDINAL] = {
    RewriteObjectWords[BCDIndex[offset], addr, n]};

  EndBCD: PUBLIC PROC = {[] ← NextFilePage[]};


 -- symbol table i/o 

  PageCount: PROC [words: CARDINAL] RETURNS [CARDINAL] = {
    RETURN [(words+(PageSize-1))/PageSize]};

  SetFgt: PROC [d: SymbolSegment.WordOffset, sourceFile: Strings.SubString]
      RETURNS [fgBase, fgPages: CARDINAL] = {
    np: CARDINAL = PageCount[d];
    dataPtr.symSeg.pages ← np;
    IF dataPtr.interface THEN {
      fgBase ← 0;
      dataPtr.symSeg.extraPages ← fgPages ← 0;
      dataPtr.codeSeg.file ← BcdDefs.FTNull;
      dataPtr.codeSeg.base ← dataPtr.codeSeg.pages ← 0;
      dataPtr.objectBytes ← 0;
      dataPtr.mtRoot.framesize ← dataPtr.globalFrameSize ← 0}
    ELSE { 
      fgBase ← np;
      dataPtr.symSeg.extraPages ← fgPages ← PageCount[
	 (StringBody[sourceFile.length].SIZE-StringBody[0].SIZE) +
	   dataPtr.fgTable.LENGTH*SymbolSegment.FGTEntry.SIZE +
	   SymbolSegment.FGHeader.SIZE]};
    dataPtr.codeSeg.class ← code;  dataPtr.codeSeg.extraPages ← 0;
    RETURN};


 -- tree i/o

  litBias: CARDINAL;

  WriteExtension: PROC [table: Alloc.Handle] RETURNS [size: CARDINAL] = {
    OPEN SymbolSegment;
    tb: Tree.Base;
    ltb: Literals.Base;
    treeLoc: Tree.Index;

    OutputNotify: Alloc.Notifier = {
      tb ← base[Tree.treeType];  ltb ← base[ltType];
      seb ← base[seType];   ctxb ← base[ctxType];
      extb ← base[extType]};

    OutputLiteral: PROC [t: Tree.Link.literal] RETURNS [Tree.Link] = {
      OPEN LiteralOps;
      WITH lit: t.index SELECT FROM
	word => lit.lti ← CopyLiteral[[baseP:@ltb, index:lit]].lti-litBias;
	string => lit.sti ← Literals.STNull;	-- temporary
	ENDCASE => ERROR;
      RETURN [t]};

    SetEmpty: Tree.Map = {RETURN [Tree.Null]};

    OutputTree: Tree.Map = {
      WITH link: t SELECT FROM
	literal => v ← OutputLiteral[link];
	subtree => {
	  s: Tree.Link = TreeOps.UpdateLeaves[link, OutputTree];
	  IF s = Tree.Null THEN v ← Tree.Null
	  ELSE
	    WITH s SELECT FROM
	      subtree => {
		node: Tree.Index = index;
		nw: CARDINAL = TreeOps.NodeSize[@tb, node];
		WriteTableBlock[@tb[node], nw];
		[] ← TreeOps.FreeTree[TreeOps.UpdateLeaves[s, SetEmpty]];
		v ← [subtree[index: treeLoc]];  treeLoc ← treeLoc + nw};
	      ENDCASE => v ← s};
	ENDCASE => v ← link;
      RETURN};

    extb: SymbolSegment.Base;
    extLimit: ExtIndex;
    seb, ctxb: Symbols.Base;
    table.AddNotify[OutputNotify];
    WriteTableBlock[@tb[Tree.NullIndex], Tree.Node.SIZE];
    treeLoc ← Tree.Index.FIRST + Tree.Node.SIZE;
    [extb, LOOPHOLE[extLimit, CARDINAL]] ← table.Bounds[extType];
    FOR exti: ExtIndex ← ExtIndex.FIRST, exti + ExtRecord.SIZE UNTIL exti = extLimit DO
      extb[exti].tree ←
        IF dataPtr.interface OR extb[exti].type = value OR extb[exti].type = default
	  THEN OutputTree[extb[exti].tree]
	  ELSE Tree.Null;
      ENDLOOP;
    table.DropNotify[OutputNotify];
    RETURN [treeLoc-Tree.Index.FIRST]};


 -- package table i/o

  WritePackTables: PROC [table: Alloc.Handle] = {
    OPEN Symbols, PackageSymbols;
    bb: Symbols.Base;

    OutputNotify: Alloc.Notifier = {bb ← base[SymbolSegment.bodyType]};

    BodyLength: PROC [info: Symbols.BodyInfo] RETURNS [CARDINAL] = INLINE {
      RETURN [WITH info SELECT FROM External => bytes, ENDCASE => 0]};

    nOuter: CARDINAL = dataPtr.nBodies - dataPtr.nInnerBodies;
    outer: LONG DESCRIPTOR FOR ARRAY OF OuterPackRecord ←
      DESCRIPTOR[OSMiscOps.Words[nOuter*OuterPackRecord.SIZE], nOuter];
    next: CARDINAL ← 0;
    nextIP: IPIndex ← IPIndex.FIRST;

    OuterBody: PROC [bti: BTIndex] = {
      WITH body: bb[bti] SELECT FROM
	Callable =>
	  IF ~body.inline THEN {
	    outer[next] ← OuterPackRecord[
		hti: SymbolOps.NameForSe[body.id],
		entryIndex: body.entryIndex,
		length: BodyLength[body.info],
		firstSon: InnerBodies[bti],
		resident: body.resident];
	    next ← next + 1};
	ENDCASE};

    InnerBodies: PROC [root: BTIndex] RETURNS [origin: IPIndex] = {
      buffer: InnerPackRecord;

      ProcessBody: PROC [bti: BTIndex] RETURNS [BOOL] = {
	WITH body: bb[bti] SELECT FROM
	  Callable =>
	    IF ~body.inline AND body.level > Symbols.lL THEN {
	      IF origin # IPNull THEN WriteObjectWords[@buffer, InnerPackRecord.SIZE];
	      buffer ← InnerPackRecord[
			  entryIndex: body.entryIndex,
			  length:  BodyLength[body.info],
			  lastSon: FALSE];
	      IF origin = IPNull THEN origin ← nextIP;
	      nextIP ← nextIP + 1};
	  ENDCASE => NULL;
	RETURN [FALSE]};

      origin ← IPNull;
      IF root # Symbols.RootBti THEN [] ← SymbolOps.EnumerateBodies[root, ProcessBody]
      ELSE
	FOR sonBti: BTIndex ← SymbolOps.SonBti[root], SymbolOps.SiblingBti[sonBti]
	 UNTIL sonBti = BTNull DO
	  WITH body: bb[sonBti] SELECT FROM
	    Callable => NULL;	-- processed as an outer body
	    ENDCASE => [] ← SymbolOps.EnumerateBodies[sonBti, ProcessBody];
	  ENDLOOP;
      IF origin # IPNull THEN {
        buffer.lastSon ← TRUE; WriteObjectWords[@buffer, InnerPackRecord.SIZE]};
      RETURN};

    table.AddNotify[OutputNotify];
    OuterBody[Symbols.RootBti];
    FOR bti: BTIndex ← SymbolOps.SonBti[Symbols.RootBti], SymbolOps.SiblingBti[bti]
     UNTIL bti = BTNull DO
      OuterBody[bti] ENDLOOP;
    table.DropNotify[OutputNotify];
    IF next # outer.LENGTH OR nextIP # dataPtr.nInnerBodies THEN ERROR;
    SortPackInfo[outer, 1, outer.LENGTH];
    WriteObjectWords[outer.BASE, nOuter*OuterPackRecord.SIZE];
    OSMiscOps.FreeWords[outer.BASE]};

  SortPackInfo: PROC [
      a: LONG DESCRIPTOR FOR ARRAY OF PackageSymbols.OuterPackRecord,
      l, u: CARDINAL] = {
    -- Shell sort of a[l..u)
    h, i, j, k: CARDINAL;
    key: Symbols.Name;
    t: PackageSymbols.OuterPackRecord;
    h ← u - l;
    DO
      h ← h/2;
      FOR k IN [l+h .. u) DO
	i ← k;  j ← k-h;  key ← a[k].hti;  t ← a[k];
	WHILE key < a[j].hti DO
	  a[i] ← a[j];  i ← j;
	  IF j < l+h THEN EXIT;  j ← j-h;
	  ENDLOOP;
	a[i] ← t;
	ENDLOOP;
      IF h <= 1 THEN EXIT;
      ENDLOOP};


 -- main drivers

  StartObjectFile: PUBLIC PROC [
      objectStream: Stream.Handle, scratchZone: UNCOUNTED ZONE] = {
    stream ← objectStream; zone ← scratchZone};


  TableOut: PUBLIC PROC [table: Alloc.Handle] = {
    OPEN SymbolSegment;
    h: STHeader;
    fixupLoc: StreamIndex;
    d: WordOffset;
    nw: CARDINAL;

    WriteSubTable: PROC [selector: Table.Selector] = {
      base: Alloc.Base;
      size: CARDINAL;
      [base, size] ← table.Bounds[selector];
      WriteTableBlock[base, size]};

    dataPtr.symSeg.class ← symbols;
    dataPtr.symSeg.base ← NextFilePage[];
    h.versionIdent ← SymbolSegment.VersionID;
    h.version ← dataPtr.objectVersion;
    h.sourceVersion ← dataPtr.source.version;
    h.creator ← dataPtr.compilerVersion;
    h.definitionsFile ← dataPtr.interface;
    h.extended ← TRUE;
    h.directoryCtx ← dataPtr.moduleCtx;
    h.importCtx ← dataPtr.importCtx;
    h.outerCtx ← dataPtr.mainCtx;
    d ← STHeader.SIZE;
    h.hvBlock.offset ← d;
      d ← d + (h.hvBlock.size ← Symbols.HashVector.SIZE);
    h.htBlock.offset ← d;  d ← d + (h.htBlock.size ← table.Bounds[htType].size);
    h.ssBlock.offset ← d;  d ← d + (h.ssBlock.size ← table.Bounds[ssType].size);
    IF dataPtr.interface THEN h.innerPackBlock ← h.outerPackBlock ← [d, 0]
    ELSE {
      h.innerPackBlock.offset ← d;
      d ← d + (h.innerPackBlock.size ← dataPtr.nInnerBodies*PackageSymbols.InnerPackRecord.SIZE);
      h.outerPackBlock.offset ← d;
      d ← d + (h.outerPackBlock.size ←
	    (dataPtr.nBodies-dataPtr.nInnerBodies)*PackageSymbols.OuterPackRecord.SIZE)};
    h.constBlock.offset ← d;  d ← d + (h.constBlock.size ← table.Bounds[constType].size);
    h.seBlock.offset ← d;  d ← d + (h.seBlock.size ← table.Bounds[seType].size);
    h.ctxBlock.offset ← d;  d ← d + (h.ctxBlock.size ← table.Bounds[ctxType].size);  
    h.mdBlock.offset ← d;  d ← d + (h.mdBlock.size ← table.Bounds[mdType].size);
    h.bodyBlock.offset ← d;  d ← d + table.Bounds[bodyType].size;
    h.bodyBlock.size ← dataPtr.defBodyLimit;
    h.epMapBlock ← h.spareBlock ← [d, 0];
    IF table.Bounds[extType].size # 0 THEN fixupLoc ← FileStream.GetIndex[stream]
    ELSE {
      h.treeBlock ← h.litBlock ← h.sLitBlock ← h.extBlock ← [d, 0];
      [h.fgRelPgBase, h.fgPgCount] ← SetFgt[d, @dataPtr.source.locator]};
    WriteObjectWords[@h, STHeader.SIZE];
    WriteObjectWords[SymbolOps.HashBlock[], h.hvBlock.size];
    WriteSubTable[htType];
    WriteSubTable[ssType];
    IF ~dataPtr.interface THEN WritePackTables[table];
    WriteSubTable[constType];
    WriteSubTable[seType];
    WriteSubTable[ctxType];
    WriteSubTable[mdType];
    WriteSubTable[bodyType];
    IF table.Bounds[extType].size # 0 THEN {
      litBias ← LiteralOps.ForgetEntries[];
      h.treeBlock.offset ← d;
      h.treeBlock.size ← WriteExtension[table];
      d ← d + h.treeBlock.size;
      h.litBlock.offset ← d;
      nw ← table.Bounds[ltType].size - litBias;
      WriteTableBlock[table.Bounds[ltType].base+litBias, nw];
      d ← d + (h.litBlock.size ← nw);
      h.extBlock.offset ← d;
      h.sLitBlock ← [d, 0];
      WriteSubTable[extType];
      d ← d + (h.extBlock.size ← table.Bounds[extType].size);
      [h.fgRelPgBase, h.fgPgCount] ← SetFgt[d, @dataPtr.source.locator];
      RewriteObjectWords[fixupLoc, @h, STHeader.SIZE]};
    IF ~dataPtr.interface THEN {
      fg: FGHeader;
      s: Strings.String ← zone.NEW[StringBody[dataPtr.source.locator.length]];
      Strings.AppendSubString[s, @dataPtr.source.locator];
      [] ← NextFilePage[];
      nw ← StringBody[s.length].SIZE-StringBody[0].SIZE;
      fg.offset ← FGHeader.SIZE + nw;
      fg.length ← dataPtr.fgTable.LENGTH;
      fg.sourceFile ← StringBody[
			length: s.length,
			maxlength: s.length,
			text: -- written separately -- ];
      WriteObjectWords[@fg, FGHeader.SIZE];
      WriteObjectWords[@s.text, nw];
      WriteObjectWords[dataPtr.fgTable.BASE, dataPtr.fgTable.LENGTH*FGTEntry.SIZE];
      zone.FREE[@s];
      OSMiscOps.FreeWords[dataPtr.fgTable.BASE]}};



  RTTableOut: PUBLIC PROC [table: Alloc.Handle] = {
    nLits: CARDINAL = SymLiteralOps.DescribeRefLits[].length;
    nTypes: CARDINAL = SymLiteralOps.DescribeTypes[].length;
    IF nLits + nTypes # 0 THEN {
      OPEN RTBcd;
      rtOffset: CARDINAL ← RTHeader.SIZE;
      header: RTHeader ← [
	refLitTable: LOOPHOLE[rtOffset],
	litBase: NULL,
	litLength: NULL,
	rcMapBase: NULL,
	rcMapLength: NULL,
	stampTable: NULL,
	typeTable: LOOPHOLE[rtOffset + RefLitList[nLits].SIZE]];
      fixupOffset: CARDINAL = ReadBCDOffset[];


      textBase: LONG POINTER ← NIL;  -- to a sequence of StringBody's
      textLimit: CARDINAL ← 0;
      textLoc: CARDINAL ← 0;

      EqText: PROC [t1, t2: Strings.String] RETURNS [BOOL] = INLINE {
	IF t1.length # t2.length THEN RETURN [FALSE];
	FOR i: CARDINAL IN [0..t1.length) DO
	  IF t1[i] # t2[i] THEN RETURN [FALSE] ENDLOOP;
	RETURN [TRUE]};
     
      EnterText: PROC [s: Strings.String] RETURNS [loc: CARDINAL] = {
        t: Strings.String;
	nw: CARDINAL;
	FOR loc ← 0, loc + StringBody[t.length].SIZE UNTIL loc >= textLoc DO
	  t ← textBase + loc;
	  IF EqText[s, t] THEN RETURN;
	  ENDLOOP;
	nw ← StringBody[s.length].SIZE;
	WHILE textLoc + nw > textLimit DO
	  newLimit: CARDINAL = PageCount[textLimit+MAX[MIN[textLimit/2, 512], 64]]*PageSize;
	  newBase: LONG POINTER = OSMiscOps.Words[newLimit];
	  IF textBase # NIL THEN {
	    Inline.LongCOPY[from: textBase, to: newBase, nwords: textLoc];
	    OSMiscOps.FreeWords[textBase]};
	  textBase ← newBase;  textLimit ← newLimit;
	  ENDLOOP;
	loc ← textLoc;
	Inline.LongCOPY[from: s, to: textBase+loc, nwords: nw];
	textLoc ← textLoc + nw;
	RETURN};
      

      stampList: LONG POINTER TO RTBcd.StampList ← NIL;
      nextStamp: NAT ← 1;

      EnterStamp: PROC [mdi: Symbols.MDIndex] RETURNS [index: RTBcd.StampIndex] = {
	IF mdi = Symbols.MDNull THEN index ← RTBcd.AnyStamp
	ELSE {
	  stamp: BcdDefs.VersionStamp = table.Bounds[SymbolSegment.mdType].base[mdi].stamp;
	  FOR i: NAT IN [1 .. nextStamp) DO
	    IF stamp = stampList[i] THEN RETURN [[i]];
	    ENDLOOP;  
	  IF stampList = NIL OR nextStamp >= stampList.limit THEN ExpandStampList[];
	  index ← [nextStamp];  stampList[nextStamp] ← stamp;  nextStamp ← nextStamp + 1};
	RETURN};
     
      ExpandStampList: PROC = INLINE {
	oldSize: NAT = nextStamp - 1;
	AdjustStampList[oldSize + MAX[MIN[oldSize/2, 128], 32]]};

      AdjustStampList: PROC [newSize: NAT] = {
	oldSize: NAT = nextStamp - 1;
	newList: LONG POINTER TO RTBcd.StampList = 
	  zone.NEW[RTBcd.StampList[newSize]];
	FOR i: NAT IN [1 .. MIN[oldSize, newSize]] DO newList[i] ← stampList[i] ENDLOOP;
	IF stampList # NIL THEN zone.FREE[@stampList];
	stampList ← newList};


      AppendBCDWords[@header, RTHeader.SIZE];
      AppendBCDWord[nLits];
      IF nLits # 0 THEN {

	WriteLitItem: PROC [item: SymLiteralOps.RefLitItem] = {
          info: RefLitItem;
	  loc, chars: CARDINAL;
	  type: Symbols.Type;
	  WITH v: item SELECT FROM
	    atom => {
	      desc: Strings.SubStringDescriptor;
	      s: Strings.String;
	      n: CARDINAL;
	      SymbolOps.SubStringForName[@desc, v.pName];
	      n ← desc.length + (desc.length MOD 2);
	      s ← zone.NEW[StringBody[n]];
	      Strings.AppendSubString[s, @desc];  IF s.length < n THEN s[n-1] ← 0c;
	      loc ← EnterText[s];  chars ← s.length;
	      type ← dataPtr.typeAtomRecord;
	      zone.FREE[@s]};
	    text => {
	      s: Strings.String = LiteralOps.StringValue[v.value];
	      loc ← EnterText[s];  chars ← s.length;
	      type ← LiteralOps.TextType[v.value]};
	    ENDCASE;
	  info ← [
	    referentType: SymLiteralOps.TypeIndex[type, FALSE],
	    offset: loc, length: TEXT[chars].SIZE];
	  AppendBCDWords[@info, RefLitItem.SIZE]};
	
	SymLiteralOps.EnumerateRefLits[WriteLitItem]};

      AppendBCDWord[nTypes];
      rtOffset ← rtOffset + RefLitList[nLits].SIZE + TypeList[nTypes].SIZE;
      header.rcMapBase ← LOOPHOLE[LONG[rtOffset]];
      IF nTypes = 0 THEN header.rcMapLength ← 0
      ELSE {
	
	EnterUT: PROC [type: Symbols.Type] RETURNS [RTBcd.UTInfo] = {
	  mdi: Symbols.MDIndex;
	  sei: Symbols.Type;
	  [mdi, sei] ← SymLiteralOps.UTypeId[type];
	  RETURN [[version: EnterStamp[mdi], sei: sei]]};

  	WriteTypeItem: PROC [canonical: BOOL, type: Symbols.Type] = {
	  s: Strings.String ← TypeStrings.Create[dataPtr.ownSymbols, type, zone];
	  info: TypeItem ← [
	    table: dataPtr.mtRoot.sseg,
	    sei: type,
	    canonical: canonical,
	    rcMap: RCMapOps.Acquire[dataPtr.ownSymbols, type],
	    ct: [EnterText[s]],
	    ut: EnterUT[type]];
	  zone.FREE[@s];
	  AppendBCDWords[@info, TypeItem.SIZE]};
	
	RCMapOps.Initialize[ptr: NIL, nPages: 0, expansionZone: zone];
	[] ← EnterStamp[Symbols.OwnMdi];
	
	SymLiteralOps.EnumerateTypes[WriteTypeItem];

	header.rcMapLength ← RCMapOps.GetBase[].nWords;
	AppendBCDWords[RCMapOps.GetBase[].base, header.rcMapLength];
	rtOffset ← rtOffset + header.rcMapLength;
	RCMapOps.Finalize[]};

      header.stampTable ← LOOPHOLE[rtOffset];
      AdjustStampList[nextStamp-1];
      AppendBCDWords[stampList, StampList[nextStamp-1].SIZE];
      rtOffset ← rtOffset + StampList[nextStamp-1].SIZE;
      zone.FREE[@stampList];
      
      header.litBase ← LOOPHOLE[rtOffset];
      header.litLength ← textLoc;
      IF textBase # NIL THEN {
        AppendBCDWords[textBase, textLoc]; OSMiscOps.FreeWords[textBase]};
      UpdateBCDWords[fixupOffset, @header, RTHeader.SIZE]}};
    

  EndObjectFile: PUBLIC PROC [update: BOOL] = {
    IF stream # NIL AND update THEN {
      saveIndex: StreamIndex = FileStream.GetIndex[stream];
      FileStream.SetIndex[stream, dataPtr.fixupLoc];
      stream.PutBlock[[@dataPtr.codeSeg, 0, BcdDefs.SGRecord.SIZE*BytesPerWord]];
      stream.PutBlock[[@dataPtr.symSeg, 0, BcdDefs.SGRecord.SIZE*BytesPerWord]];
      stream.PutBlock[[dataPtr.mtRoot, 0, dataPtr.mtRootSize*BytesPerWord]];
      FileStream.SetIndex[stream, saveIndex]};
    IF dataPtr.mtRoot # NIL THEN zone.FREE[@dataPtr.mtRoot];
    stream ← NIL;  zone ← NIL};

  }.