-- file OutCode.mesa
-- last modified by Sweet, July 29, 1982 3:54 pm
-- last modified by Satterthwaite, January 11, 1983 4:59 pm

DIRECTORY
  Alloc: TYPE USING [Notifier],
  CatchFormat: TYPE USING [EnableItem],
  Code: TYPE USING [codeptr, enableList],
  CodeDefs: TYPE USING [
    Base, BYTE, CCIndex, CCItem, CCNull, codeType, EINull, 
    EnableIndex, LabelCCIndex, NULLfileindex, TableCodeBytes],
  ComData: TYPE USING [
    catchBytes, catchIndex, codeByteOffsetList, codeOffsetList, 
    codeSeg, fgTable, globalFrameSize, jumpIndirectList,
    linkCount, mtRoot, nBodies, nSigCodes, 
    stopping, switches, zone],
  CompilerUtil: TYPE USING [AcquireStream, NextFilePage, ReleaseStream],
  Environment: TYPE USING [bytesPerPage, wordsPerPage],
  FileStream: TYPE USING [FileByteIndex, GetIndex, SetIndex],
  Fixup: TYPE USING [JIHandle, JIRec, PCHandle, PCRec],
  FOpCodes: TYPE USING [qBLTC, qLCO, qGA, qLA],
  Literals: TYPE USING [Base, MSTIndex, STIndex, stType],
  LiteralOps: TYPE USING [EnumerateLocalStrings, EnumerateMasterStrings],
  Log: TYPE USING [ErrorTree],
  Mopcodes: TYPE USING [zJIB, zJIW, zLIW],
  OSMiscOps: TYPE USING [FreePages, FreeWords, Pages, Words],
  P5: TYPE USING [C1W, P5Error],
  P5U: TYPE USING [
    FreeChunk, Out0, Out1, ComputeFrameSize, PushLitVal, RecordConstant,
    WordsForString],
  PrincOps: TYPE USING [
    AVHeapSize, BytePC, CSegPrefix, EntryVectorItem, EPRange, GlobalOverhead,
    MaxFrameSize],
  Stack: TYPE USING [Dump],
  Stream: TYPE USING [Block, Handle, PutBlock, PutByte, PutWord],
  Symbols: TYPE USING [Base, BodyInfo, bodyType, CBTIndex, RootBti],
  SymbolSegment: TYPE USING [FGTEntry, ObjectStep, SourceStep, Stride];

OutCode: PROGRAM
    IMPORTS MPtr: ComData, CPtr: Code, CompilerUtil, FileStream, LiteralOps, Log,
      OSMiscOps, P5, P5U, Stack, Stream 
    EXPORTS CodeDefs, P5 =
  BEGIN
  OPEN CodeDefs;

  -- imported definitions

  Address: TYPE = CARDINAL;
  PageSize: CARDINAL = Environment.wordsPerPage;

  BodyInfo: TYPE = Symbols.BodyInfo;
  CBTIndex: TYPE = Symbols.CBTIndex;
  FGTEntry: TYPE = SymbolSegment.FGTEntry;

  STIndex: TYPE = Literals.STIndex;
  MSTIndex: TYPE = Literals.MSTIndex;


  cb: CodeDefs.Base;		-- code base (local copy)
  bb: Symbols.Base;
  stb: Literals.Base;

  OutCodeNotify: PUBLIC Alloc.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    cb ← base[codeType];
    bb ← base[Symbols.bodyType];
    stb ← base[Literals.stType];
    END;

  FileSequenceError: SIGNAL = CODE;

  ByteBlock: PROC [base: LONG POINTER, nw: CARDINAL] RETURNS [Stream.Block] = INLINE {
    RETURN [[LOOPHOLE[base], 0, 2*nw]]};
    
  StreamIndex: TYPE = FileStream.FileByteIndex;

  fgt: LONG DESCRIPTOR FOR ARRAY OF FGTEntry;
  fgti: INTEGER;
  fgtPages: CARDINAL;
  
  LabelPcInfo: TYPE = RECORD [label: LabelCCIndex, pc: Address];
  LabelPcSeq: TYPE = RECORD [
    count: NAT, 
    data: SEQUENCE max: [0 .. NAT.LAST/LabelPcInfo.SIZE) OF LabelPcInfo];
  LabelPcList: TYPE = LONG POINTER TO LabelPcSeq;
  labelPcList: LabelPcList ← NIL;
  
  SetLabelPc: PROC [label: LabelCCIndex, pc: Address] =
    BEGIN
    count: NAT ← IF labelPcList = NIL THEN 0 ELSE labelPcList.count;
    FOR i: NAT IN [0..count) DO
      IF labelPcList[i].label = label THEN {labelPcList[i].pc ← pc; RETURN};
      ENDLOOP;
    IF labelPcList = NIL OR count = labelPcList.max THEN {
      new: LabelPcList ← MPtr.zone.NEW[LabelPcSeq[count+20] ← 
        [count: count, data:]];
      FOR j: NAT IN [0..count) DO new[j] ← labelPcList[j] ENDLOOP;
      IF labelPcList # NIL THEN MPtr.zone.FREE[@labelPcList];
      labelPcList ← new};
    labelPcList[count] ← [label: label, pc: pc];
    labelPcList.count ← count+1;
    END;
    
  GetLabelPc: PROC [label: LabelCCIndex] RETURNS [Address] =
    BEGIN
    IF labelPcList = NIL THEN ERROR;
    FOR i: NAT IN [0..labelPcList.count) DO
      IF labelPcList[i].label = label THEN RETURN [labelPcList[i].pc];
      ENDLOOP;
    ERROR;
    END;

  objectStream: Stream.Handle ← NIL;
  codeBase: StreamIndex;
  entryVector: LONG DESCRIPTOR FOR ARRAY OF PrincOps.EntryVectorItem;
  
  catchEntry: LONG DESCRIPTOR FOR ARRAY OF PrincOps.BytePC;

  codeByteIndex: CARDINAL;

  lastObject, lastSource: CARDINAL;

  StartCodeFile: PUBLIC PROC =
    BEGIN -- called to set up bodytable and init binary file header
    OPEN MPtr, PrincOps;
    nGfi: CARDINAL = (MAX[nBodies, nSigCodes] + (PrincOps.EPRange-1))/PrincOps.EPRange;
    IF ~(nGfi IN [1..4]) THEN P5.P5Error[833];
    IF linkCount > 377B THEN P5.P5Error[834];
    objectStream ← CompilerUtil.AcquireStream[object];
    codeSeg.base ← CompilerUtil.NextFilePage[];
    fgti ← -1; fgtPages ← 1;
    IF mtRoot.code.offset # 0 THEN
      BEGIN
      objectStream.PutWord[mtRoot.code.offset];
      THROUGH (1..mtRoot.code.offset] DO objectStream.PutWord[0] ENDLOOP;
      END;
    codeBase ← FileStream.GetIndex[objectStream];
    codeByteIndex ← (CSegPrefix.SIZE+nBodies*EntryVectorItem.SIZE+1)*2;
    FileStream.SetIndex[objectStream, codeBase + codeByteIndex];
    fgt ← DESCRIPTOR[OSMiscOps.Pages[fgtPages], (fgtPages*PageSize)/FGTEntry.SIZE];
    entryVector ← DESCRIPTOR[
      OSMiscOps.Words[nBodies*EntryVectorItem.SIZE], 
      nBodies];
    catchEntry ← DESCRIPTOR [
      OSMiscOps.Words[MPtr.catchIndex*PrincOps.BytePC.SIZE], MPtr.catchIndex];
    firstCatch ← lastCatch ← CCNull;
    END;

  MoveToCodeWord: PUBLIC PROC RETURNS [CARDINAL] = 
    BEGIN
    IF codeByteIndex MOD 2 = 1 THEN {
      objectStream.PutByte[377B]; codeByteIndex ← codeByteIndex+1};
    RETURN [codeByteIndex/2]
    END;


  WriteCodeWord: PUBLIC PROC [w: WORD] =
    BEGIN
    IF codeByteIndex MOD 2 = 1 THEN P5.P5Error[835];
    objectStream.PutWord[w];
    codeByteIndex ← codeByteIndex+2;
    END;


  WriteCodeByte: PROC [b: BYTE] = {
    objectStream.PutByte[b]; codeByteIndex ← codeByteIndex+1};


  NewFgtEntry: PROC [source, object: CARDINAL] =
    BEGIN -- enters new value into fgt

    AddEntry: PROC [e: SymbolSegment.FGTEntry] =
      BEGIN
      IF (fgti ← fgti+1) >= fgt.LENGTH THEN
        BEGIN
        oldfgt: LONG DESCRIPTOR FOR ARRAY OF FGTEntry ← fgt;  
        fgtPages ← fgtPages+1;
        fgt ← DESCRIPTOR[
            OSMiscOps.Pages[fgtPages],
            (fgtPages*PageSize)/FGTEntry.SIZE];
        FOR i: CARDINAL IN [0..oldfgt.LENGTH) DO fgt[i] ← oldfgt[i] ENDLOOP;
        OSMiscOps.FreePages[oldfgt.BASE];
        END;
      fgt[fgti] ← e;
      END;

    t: CARDINAL;
    dSource: CARDINAL ← source - lastSource;
    dObject: CARDINAL ← object - lastObject;
    WHILE dSource > SymbolSegment.SourceStep DO
      t ← MIN[dSource, SymbolSegment.Stride];
      AddEntry[[step[which: source, delta: t]]];
      dSource ← dSource - t;
      ENDLOOP;
    WHILE dObject > SymbolSegment.ObjectStep DO
      t ← MIN[dObject, SymbolSegment.Stride];
      AddEntry[[step[which: object, delta: t]]];
      dObject ← dObject - t;
      ENDLOOP;
    AddEntry[[normal[deltaObject: dObject, deltaSource: dSource]]];
    lastSource ← source; lastObject ← object;
    END;


  OutJumpTables: PUBLIC PROC [start: CCIndex] RETURNS [bodyStart: Address] =
    BEGIN -- outputs binary bytes starting at start
    c, cj: CCIndex;
    offset: CARDINAL;
    byteTable: BOOL;
    bodyStart ← codeByteIndex;
    offset ← bodyStart;
    FOR c ← start, cb[c].flink UNTIL c = CCNull DO
      WITH  cc:cb[c] SELECT FROM
	code => offset ← offset + cc.isize;
	other => WITH cc SELECT FROM
	  table =>
	    BEGIN
	    offset ← offset + TableCodeBytes;
	    taboffset ← MoveToCodeWord[];
	    byteTable ← btab ← ByteableJumps[flink];
	    FOR cj ← flink, cb[cj].flink DO
	      WITH cb[cj] SELECT FROM
		jump =>
		  IF jtype = JumpC THEN
		    BEGIN
		    -- jBytes is surprisingly correct for both forward
		    --   and backward jumps.
		    jBytes: INTEGER ← cb[destlabel].pc - pc+1;
		    jBytes ← jBytes+2;
		    IF byteTable THEN WriteCodeByte[jBytes]
		    ELSE WriteCodeWord[jBytes];
		    END
		  ELSE EXIT;
		ENDCASE => EXIT;
	      ENDLOOP;
	    IF byteTable THEN [] ← MoveToCodeWord[];
	    bodyStart ← codeByteIndex;
	    END;
	  ENDCASE;
	ENDCASE;
      ENDLOOP;
    END;


  OutChunks: PUBLIC PROC [start: CCIndex] =
    BEGIN -- outputs binary bytes for body bti starting at start
    c, nextC: CCIndex;
    offset, nw: CARDINAL;
    bodyStart: Address ← codeByteIndex;
    labelToKeep: BOOL;
    offset ← bodyStart;
    FOR c ← start, nextC UNTIL c = CCNull DO
      labelToKeep ← FALSE;
      WITH cc:cb[c] SELECT FROM
	code =>
	  BEGIN
	  IF ~cc.realinst THEN ERROR;
	  SELECT cc.isize FROM
	    0 => IF cc.realinst THEN ERROR;
	    1 =>
	      BEGIN
	      WriteCodeByte[cc.inst];
	      END;
	    2 =>
	      BEGIN
	      WriteCodeByte[cc.inst];
	      WriteCodeByte[cc.parameters[1]];
	      END;
	    3 => 
	      BEGIN
	      IF cc.inst = Mopcodes.zLIW AND cc.lco THEN {
		new: Fixup.PCHandle = MPtr.zone.NEW [Fixup.PCRec ← [
		  pc: [offset], next: ]];
		IF MPtr.codeOffsetList = NIL THEN new.next ← new
		ELSE {
		  new.next ← MPtr.codeOffsetList.next;
		  MPtr.codeOffsetList.next ← new};
		MPtr.codeOffsetList ← new;
		fixupThisProc ← TRUE}; 
	      WriteCodeByte[cc.inst];
	      WriteCodeByte[cc.parameters[1]]; WriteCodeByte[cc.parameters[2]];
	      END;
	    ENDCASE =>	-- only from MACHINE CODE inlines
	      BEGIN
	      WriteCodeByte[cc.inst];
	      FOR i: CARDINAL IN [1..cc.isize) DO WriteCodeByte[cc.parameters[i]] ENDLOOP;
	      END;
	  offset ← offset+cc.isize;
	  END;
	label => IF cc.offsetLoaded THEN 
	  {SetLabelPc[label: LOOPHOLE[c], pc: offset]; labelToKeep ← TRUE};
	jump => IF cc.jtype = JumpLIO THEN
	  BEGIN
	  val: RECORD [SELECT OVERLAID * FROM
	    card => [c: CARDINAL],
	    pair => [b1, b2: [0..256)],
	    ENDCASE];
	  new: Fixup.PCHandle = MPtr.zone.NEW [Fixup.PCRec ← [
	    pc: [offset], next: ]];
	  IF MPtr.codeByteOffsetList = NIL THEN new.next ← new 
	  ELSE {
	    new.next ← MPtr.codeByteOffsetList.next;
	    MPtr.codeByteOffsetList.next ← new};
	  MPtr.codeByteOffsetList ← new;
	  val.c ← GetLabelPc[cc.destlabel];
	  WriteCodeByte[Mopcodes.zLIW];
	  WriteCodeByte[val.b1];
	  WriteCodeByte[val.b2];
	  offset ← offset + 3;
	  fixupThisProc ← TRUE;
	  END;
	other => WITH cc SELECT FROM
	  table =>
	    BEGIN
	    new: Fixup.JIHandle = MPtr.zone.NEW [Fixup.JIRec ← [
	      pc: [offset], tableSize: tableSize, next: ]];
	    IF MPtr.jumpIndirectList = NIL THEN new.next ← new
	    ELSE {
	      new.next ← MPtr.jumpIndirectList.next;
	      MPtr.jumpIndirectList.next ← new};
	    MPtr.jumpIndirectList ← new;
	    CPtr.codeptr ← c;
	    P5.C1W[IF btab THEN Mopcodes.zJIB ELSE Mopcodes.zJIW, taboffset];
	    fixupThisProc ← TRUE;
	    END;
	  markbody =>
	    IF start THEN
	      BEGIN -- immediately prior chunk was source unless catch
	      WITH br: bb[index] SELECT FROM
		Other => br.relOffset ← offset - bodyStart;
		Callable => WITH brc: br SELECT FROM
		  Catch => {
		    catchEntry[brc.index] ← [offset];
		    lastSource ← brc.sourceIndex;
		    lastObject ← bodyStart ← offset;
		    NewFgtEntry[lastSource, lastObject]}; -- a [0,0] for this body
		  ENDCASE => ERROR;
		ENDCASE => ERROR;
	      bb[index].info ← BodyInfo[External[bytes: , startIndex: fgti, indexLength: ]];
	      END
	    ELSE
	      BEGIN
	      WITH bi: bb[index].info SELECT FROM
		External =>
		  BEGIN
		  bi.indexLength ← fgti-bi.startIndex+1;
		  WITH br: bb[index] SELECT FROM
		    Other => bi.bytes ← offset - br.relOffset - bodyStart;
		    Callable => WITH brc: br SELECT FROM
		      Catch => bi.bytes ← offset - catchEntry[brc.index];
		      ENDCASE => ERROR;
		    ENDCASE => ERROR;
		  END;
		ENDCASE;
	      END;
	  markCatch =>
	    IF start THEN cb[index].startPC ← offset
	    ELSE cb[index].bytes ← offset - cb[index].startPC;
	  source => IF index # NULLfileindex THEN
	    BEGIN
	    IF index > lastSource OR
	     (index = lastSource AND offset # lastObject) THEN 
	     NewFgtEntry[index, offset];
	    END;
	  ENDCASE;
	ENDCASE;
      nextC ← cb[c].flink;
      IF ~labelToKeep THEN {
        nw ← WITH cc: cb[c] SELECT FROM
	  code => MAX[cc.isize, 1]-1 + CCItem.code.SIZE,
	  label => CCItem.label.SIZE,
	  jump => CCItem.jump.SIZE,
	  other => CCItem.other.SIZE,
	  ENDCASE => ERROR;
        P5U.FreeChunk[c, nw]};
      ENDLOOP;
    END;


  fixupThisProc: BOOL;
  
  OutBinary: PUBLIC PROC [bti: CBTIndex, start: CCIndex] =
    BEGIN -- outputs binary bytes for body bti starting at start
    e, fs: CARDINAL;
    bodyStart: Address;
    fixupThisProc ← FALSE;
    bodyStart ← OutJumpTables[start];
    RemoveCatchCode[start];
    e ← bb[bti].entryIndex;
    lastSource ← bb[bti].sourceIndex;
    lastObject ← bodyStart;
    WITH bi: bb[bti].info SELECT FROM
      Internal =>
	BEGIN
	fs ← P5U.ComputeFrameSize[bi.frameSize];
	IF bb[bti].resident THEN fs ← fs+PrincOps.AVHeapSize;
	END;
      ENDCASE => P5.P5Error[836];
    NewFgtEntry[source: lastSource, object: lastObject]; -- put out [0,0]
    entryVector[e].pc ← [bodyStart];
    bb[bti].info ← BodyInfo[External[bytes: , startIndex: fgti, indexLength: ]];
    WriteCodeByte[fs];
    OutChunks[start]; 
    WITH bb[bti].info SELECT FROM
      External => {
	indexLength ← fgti - startIndex+1; 
	bytes ← codeByteIndex - bodyStart};
      ENDCASE;
--  bb[bti].hints.needsFixup ← fixupThisProc;
    END;



  RemoveCatchCode: PROC [start: CCIndex] =
    BEGIN
    c: CCIndex;
    FOR c ← start, cb[c].flink UNTIL c = CCNull DO
      WITH cc:cb[c] SELECT FROM
	other => WITH cc SELECT FROM
	  markbody =>
	    IF start THEN
	      BEGIN
	      WITH br: bb[index] SELECT FROM
		Other => NULL;
		Callable => WITH brc: br SELECT FROM
		  Catch => {DelinkCatch[c]; RETURN};
		  ENDCASE;
		ENDCASE;
	      END;
	  ENDCASE;
	ENDCASE;
      ENDLOOP;
    END;
    
  firstCatch, lastCatch: CCIndex;
  
  DelinkCatch: PROC [c: CCIndex] =
    BEGIN
    prev: CCIndex = cb[c].blink;
    IF firstCatch = CCNull THEN firstCatch ← c
    ELSE cb[lastCatch].flink ← c; 
    cb[c].blink ← lastCatch;
    cb[prev].flink ← CCNull;
    lastCatch ← c;
    WHILE cb[lastCatch].flink # CCNull DO 
      lastCatch ← cb[lastCatch].flink;
      ENDLOOP;
    END;

  ByteableJumps: PROC [j: CCIndex] RETURNS [BOOL] =
    BEGIN
    DO
    WITH cb[j] SELECT FROM
      jump =>
	IF jtype = JumpC THEN
	  BEGIN
	  jBytes: INTEGER = cb[destlabel].pc - pc + 3;
	  IF ~forward OR jBytes > BYTE.LAST THEN RETURN [FALSE];
	  j ← cb[j].flink;
	  END
	ELSE RETURN [TRUE];
      ENDCASE => RETURN [TRUE]
    ENDLOOP
    END;


  WriteCodeString: PROC [s: LONG POINTER, nw: CARDINAL] =
    BEGIN
    objectStream.PutBlock[ByteBlock[s, nw]];
    END;

  ProcessGlobalStrings: PUBLIC PROC [framestart: CARDINAL]
      RETURNS [nextnewframe: CARDINAL] =
    BEGIN
    firstNewCode, nextNewCode: CARDINAL ← MoveToCodeWord[];
    stSize, litSize: CARDINAL;
    
    DoString: PROC [msti: MSTIndex] =
      BEGIN
      nw: CARDINAL;
      IF stb[msti].info = 0 THEN {stb[msti].local ← TRUE; RETURN};
      nw ← P5U.WordsForString[stb[msti].string.length];
      stb[msti].info ← nextnewframe; 
      nextnewframe ← nextnewframe+nw;
      IF nextnewframe > PrincOps.MaxFrameSize THEN
	Log.ErrorTree[addressOverflow, [literal[[string[msti]]]]];
      stb[msti].codeIndex ← nextNewCode;
      nextNewCode ← nextNewCode + nw;
      WriteCodeString[@stb[msti].string, nw];
      codeByteIndex ← codeByteIndex + 2*nw;
      END; -- of doglobal

    nextnewframe ← framestart;
    LiteralOps.EnumerateMasterStrings[DoString];
    litSize ← nextNewCode - firstNewCode;  stSize ← nextnewframe - framestart;
    IF litSize > 0 THEN 
      BEGIN
      P5U.RecordConstant[firstNewCode, litSize];
      IF stSize > 0 THEN BLTStrings[firstNewCode, stSize, framestart, FALSE];
      END;
    END;


  ProcessLocalStrings: PUBLIC PROC [framestart: CARDINAL, first: STIndex]
      RETURNS [nextnewframe: CARDINAL] =
    BEGIN
    nStrings: CARDINAL ← 0;

    CountStrings: PROC [msti: MSTIndex] =
      BEGIN
      IF stb[msti].local AND stb[msti].codeIndex # 0 THEN nStrings ← nStrings+1;
      END;

    firstNewCode, nextNewCode: CARDINAL;
    firstCode: BOOL ← TRUE;
    stSize, i, nw: CARDINAL;
    curSize: CARDINAL ← 0;
    StringInfo: TYPE = RECORD [offset: CARDINAL, sti: MSTIndex];
    star: LONG DESCRIPTOR FOR ARRAY OF StringInfo;

    InsertStrings: PROC [msti: MSTIndex] =
      BEGIN
      IF stb[msti].local THEN
	BEGIN 
	co: CARDINAL = stb[msti].codeIndex; 
	IF co # 0 THEN
	  BEGIN
	  FOR i ← curSize, i-1 WHILE i>0 AND co < star[i-1].offset DO
	    star[i] ← star[i-1];
	    ENDLOOP; 
	  star[i] ← [co, msti]; 
	  curSize ← curSize+1;
	  END
	ELSE
	  BEGIN
	  nw: CARDINAL = P5U.WordsForString[stb[msti].string.length];
	  stb[msti].info ← nextnewframe; 
	  nextnewframe ← nextnewframe+nw;
	  IF nextnewframe > PrincOps.MaxFrameSize THEN
	    Log.ErrorTree[addressOverflow, [literal[[string[msti]]]]];
	  IF firstCode THEN {
	    firstCode ← FALSE; firstNewCode ← nextNewCode ← MoveToCodeWord[]};
	  stb[msti].codeIndex ← nextNewCode;
	  nextNewCode ← nextNewCode + nw;
	  WriteCodeString[@stb[msti].string, nw];
	  codeByteIndex ← codeByteIndex + nw*2;
	  END;
	END;
      END; -- of InsertStrings

    nextnewframe ← framestart;
    LiteralOps.EnumerateLocalStrings[first, CountStrings];
    IF nStrings # 0 THEN 
      star ← DESCRIPTOR[OSMiscOps.Words[nStrings*StringInfo.SIZE], nStrings];
    LiteralOps.EnumerateLocalStrings[first, InsertStrings];
    stSize ← nextnewframe - framestart;
    IF stSize > 0 THEN 
      BEGIN
      BLTStrings[firstNewCode, stSize, framestart, TRUE];
      P5U.RecordConstant[firstNewCode, stSize];
      END;
    i ← 0;
    WHILE i < nStrings DO
      framestart ← nextnewframe;
      nextNewCode ← firstNewCode ← star[i].offset;
      WHILE i < nStrings AND star[i].offset = nextNewCode DO
	nw ← P5U.WordsForString[stb[star[i].sti].string.length];
	nextNewCode ← nextNewCode + nw;
	stb[star[i].sti].info ← nextnewframe;
	nextnewframe ← nextnewframe+nw;
	IF nextnewframe > PrincOps.MaxFrameSize THEN
	  Log.ErrorTree[addressOverflow, [literal[[string[star[i].sti]]]]];
	i ← i+1;
	ENDLOOP;
      stSize ← nextnewframe - framestart;
      BLTStrings[firstNewCode, stSize, framestart, TRUE];
      ENDLOOP;
    IF nStrings # 0 THEN OSMiscOps.FreeWords[star.BASE];
    END;

  BLTStrings: PROC [coffset, length, foffset: CARDINAL, local: BOOL] =
    BEGIN OPEN FOpCodes;
    Stack.Dump[]; -- though I don't see how it could be non-empty now
    P5U.Out1[qLCO, coffset];
    P5U.PushLitVal[length];
    P5U.Out1[IF local THEN qLA ELSE qGA, foffset];
    P5U.Out0[qBLTC];
    END;
    
  OutputCatchBodies: PROC =
    BEGIN
    IF firstCatch # CCNull THEN OutChunks[firstCatch];
    MPtr.catchBytes ← codeByteIndex - 2*catchOffset; -- count CEV, etc
    END;
    
  OutputCatchTables: PROC =
    BEGIN
    maxLevel: CARDINAL = CPtr.enableList.LENGTH-1;
    OutEnableLevel: PROC [i: CARDINAL] =
      BEGIN
      n: CARDINAL ← 0;
      ei: EnableIndex;
      ee: CatchFormat.EnableItem;
      lei: EnableIndex = CPtr.enableList[i];
      lnei: EnableIndex;
      FOR ei ← lei, cb[ei].next UNTIL ei = EINull DO 
        n ← n+1;
	ENDLOOP;
      WriteCodeWord[n];
      FOR ei ← lei, cb[ei].next UNTIL ei = EINull DO 
        ee ← [
	  start: [cb[ei].startPC], length: cb[ei].bytes,
	  index: bb[cb[ei].bti].index, alsoNested: FALSE];
	IF i < maxLevel AND (lnei ← CPtr.enableList[i+1]) # EINull THEN
	  BEGIN -- look for nexted catch phrases
	  FOR nei: EnableIndex ← lnei, cb[nei].next UNTIL nei = EINull DO
	    ns: CARDINAL = cb[nei].startPC;
	    IF ns >= ee.start THEN {
	      IF ns < ee.start + ee.length THEN ee.alsoNested ← TRUE;
	      EXIT};
	    ENDLOOP;
	  END;
	[] ← objectStream.PutBlock[ByteBlock[@ee, CatchFormat.EnableItem.SIZE]];
	ENDLOOP;
      END;
    
    SortEnableLists[];
    WriteCodeWord[catchEntry.LENGTH];
    IF catchEntry.LENGTH # 0 THEN
      [] ← objectStream.PutBlock[
	    ByteBlock[catchEntry.BASE, catchEntry.LENGTH*PrincOps.BytePC.SIZE]];
    IF CPtr.enableList[0] = EINull THEN WriteCodeWord[0];
    FOR l: CARDINAL IN [0..maxLevel] WHILE CPtr.enableList[l] # EINull DO
      OutEnableLevel[l];
      ENDLOOP;
    END;
  
  SortEnableLists: PROC = {
    FOR l: CARDINAL IN [0..CPtr.enableList.LENGTH) WHILE CPtr.enableList[l] # EINull DO
      new: EnableIndex ← EINull;
      next: EnableIndex;
      -- do simple insertion sort of each list
      FOR ei: EnableIndex ← CPtr.enableList[l], next UNTIL ei = EINull DO
        next ← cb[ei].next;
        IF new = EINull OR cb[ei].startPC < cb[new].startPC THEN {cb[ei].next ← new; new ← ei}
        ELSE {
          prevEi: EnableIndex ← new;
          WHILE cb[prevEi].next # EINull AND cb[cb[prevEi].next].startPC < cb[ei].startPC DO
            prevEi ← cb[prevEi].next;
            ENDLOOP;
          cb[ei].next ← cb[prevEi].next;
          cb[prevEi].next ← ei};
        CPtr.enableList[l] ← new;
        ENDLOOP;
      ENDLOOP};
    
    
  catchOffset: CARDINAL;

  EndCodeFile: PUBLIC PROC RETURNS [nbytes: CARDINAL] =
    BEGIN OPEN PrincOps;
    maxLevel: CARDINAL = CPtr.enableList.LENGTH-1;
    saveindex, catchIndex: StreamIndex;
    noCatch: BOOL = (catchEntry.LENGTH = 0);
    ctSize: CARDINAL ← 1 + catchEntry.LENGTH*PrincOps.BytePC.SIZE + 1;
    prefix: CSegPrefix;
    nGfi: CARDINAL = (MAX[MPtr.nBodies, MPtr.nSigCodes] + (EPRange-1))/EPRange;
    fs: CARDINAL;
    IF nGfi NOT IN [1..4] THEN P5.P5Error[833];
    IF noCatch THEN {catchOffset ← 0; MPtr.catchBytes ← 0}
    ELSE {
      lei: EnableIndex;
      catchOffset ← MoveToCodeWord[];
      catchIndex ← FileStream.GetIndex[objectStream];
      FOR l: CARDINAL IN [0..maxLevel] WHILE (lei ← CPtr.enableList[l]) # EINull DO
        IF l # 0 THEN ctSize ← ctSize + 1;
        FOR ei: EnableIndex ← lei, cb[ei].next WHILE ei # EINull DO 
          ctSize ← ctSize + CatchFormat.EnableItem.SIZE;
	  ENDLOOP;
        ENDLOOP;
      codeByteIndex ← codeByteIndex + ctSize*2; -- make room for catch entry, enables
      FileStream.SetIndex[objectStream, codeBase + codeByteIndex];
      OutputCatchBodies[]};
    MPtr.mtRoot.code.length ← codeByteIndex;
    [] ← MoveToCodeWord[];
    saveindex ← FileStream.GetIndex[objectStream];
    IF ~noCatch THEN {
      FileStream.SetIndex[objectStream, catchIndex]; codeByteIndex ← catchOffset*2;
      OutputCatchTables[]};
    MPtr.fgTable ← DESCRIPTOR[fgt.BASE, fgti+1];
    MPtr.codeSeg.pages ← 
      (2*MPtr.mtRoot.code.offset + MPtr.mtRoot.code.length + (Environment.bytesPerPage-1))/
         Environment.bytesPerPage;
    FileStream.SetIndex[objectStream, codeBase];
    fs ← P5U.ComputeFrameSize[MPtr.globalFrameSize];
    IF bb[Symbols.RootBti].resident THEN fs ← fs+PrincOps.AVHeapSize;
    prefix ← [
      header: [
	globalFsi: fs,
	nEntries: MPtr.nBodies,
	info: [
	  available: 0, 
	  stops: MPtr.stopping, ngfi: nGfi, nlinks: MPtr.linkCount]],
      entry: NULL];
    [] ← objectStream.PutBlock[ByteBlock[@prefix, CSegPrefix.SIZE]];
    [] ← objectStream.PutBlock[
	ByteBlock[entryVector.BASE, entryVector.LENGTH*PrincOps.EntryVectorItem.SIZE]];
    objectStream.PutWord[catchOffset*2];
    OSMiscOps.FreeWords[entryVector.BASE];
    OSMiscOps.FreeWords[catchEntry.BASE];
    MPtr.mtRoot.framesize ← MPtr.globalFrameSize + PrincOps.GlobalOverhead.SIZE;
    MPtr.mtRoot.crossJumped ← MPtr.switches['j];
    FileStream.SetIndex[objectStream, saveindex];
    CompilerUtil.ReleaseStream[object];  objectStream ← NIL;
    IF labelPcList # NIL THEN {
      FOR i: NAT IN [0..labelPcList.count) DO
        P5U.FreeChunk[labelPcList[i].label, CCItem.label.SIZE];
        ENDLOOP;
      MPtr.zone.FREE[@labelPcList]};
    IF MPtr.codeOffsetList # NIL THEN {
      p: Fixup.PCHandle = MPtr.codeOffsetList.next;
      MPtr.codeOffsetList.next ← NIL; MPtr.codeOffsetList ← p};
    IF MPtr.codeByteOffsetList # NIL THEN {
      p: Fixup.PCHandle = MPtr.codeByteOffsetList.next;
      MPtr.codeByteOffsetList.next ← NIL; MPtr.codeByteOffsetList ← p};
    IF MPtr.jumpIndirectList # NIL THEN {
      j: Fixup.JIHandle = MPtr.jumpIndirectList.next;
      MPtr.jumpIndirectList.next ← NIL; MPtr.jumpIndirectList ← j};
    RETURN [MPtr.mtRoot.code.length]
    END;

  END.