-- file OutCode.mesa
-- last modified by Sandman, Jan 18, 1980 7:04 PM

DIRECTORY
  AltoDefs: FROM "altodefs" USING [Address, BYTE, PageSize],
  Code: FROM "code" USING [CodePassInconsistency, codeptr, dStar],
  CodeDefs: FROM "codedefs" USING [CCIndex, CCItem, CCNull, NULLfileindex],
  ComData: FROM "comdata" USING [
    codeSeg, fgTable, linkCount, mainBody, mtRoot, nBodies, nSigCodes, 
    objectFrameSize, objectStream, stopping, switches],
  CompilerUtil: FROM "compilerutil" USING [nextFilePage],
  ControlDefs: FROM "controldefs" USING [
    AllocationVectorSize, CSegPrefix, EntryVectorItem, EPRange, 
    InstWord],
  FOpCodes: FROM "fopcodes" USING [qBLTC, qGADRB, qLADRB, qNOOP],
  InlineDefs: FROM "inlinedefs" USING [BITOR, BITSHIFT],
  Literals: FROM "literals" USING [MSTIndex, STIndex, stType],
  LiteralOps: FROM "literalops" USING [
    EnumerateLocalStrings, EnumerateMasterStrings],
  Mopcodes: FROM "mopcodes" USING [zJIB, zJIW, zNOOP],
  P5: FROM "p5" USING [C1W, P5Error],
  P5U: FROM "p5u" USING [
    FreeChunk, Out0, Out1, ComputeFrameSize, PushLitVal, WordsForSei],
  PrincOps: FROM "PrincOps" USING [InstWord],
  Stack: FROM "stack" USING [Dump],
  StreamDefs: FROM "streamdefs" USING [
    GetIndex, SetIndex, StreamIndex, WriteBlock],
  StringDefs: FROM "stringdefs" USING [WordsForString],
  Symbols: FROM "symbols" USING [
    BodyInfo, bodyType, BTIndex, CBTIndex, CSEIndex, CTXIndex, HTIndex, 
    ISEIndex, RecordSEIndex, SEIndex, SERecord, seType],
  SymbolOps: FROM "symbolops" USING [UnderType],
  SymbolSegment: FROM "symbolsegment" USING [ByteIndex, FGTEntry],
  SystemDefs: FROM "systemdefs" USING [
    AllocateHeapNode, AllocatePages, AllocateSegment, FreeHeapNode, 
    FreePages, FreeSegment],
  Table: FROM "table" USING [Base, Limit, Notifier],
  Tree: FROM "tree" USING [treeType];

OutCode: PROGRAM
    IMPORTS MPtr: ComData, CPtr: Code, CodeDefs, CompilerUtil, InlineDefs, LiteralOps, P5, P5U, Stack, StreamDefs, StringDefs, 
    SymbolOps, SystemDefs 
    EXPORTS CodeDefs, P5
    SHARES Literals =
  BEGIN
  OPEN CodeDefs;


  -- imported definitions

  Address: TYPE = AltoDefs.Address;
  BYTE: TYPE = AltoDefs.BYTE;
  PageSize: INTEGER = AltoDefs.PageSize;

  MyInstWord: TYPE = RECORD [SELECT COMPUTED BOOLEAN FROM
    FALSE => [w: ControlDefs.InstWord],
    TRUE => [w: PrincOps.InstWord],
    ENDCASE];

  BodyInfo: TYPE = Symbols.BodyInfo;
  BTIndex: TYPE = Symbols.BTIndex;
  CBTIndex: TYPE = Symbols.CBTIndex;
  ByteIndex: TYPE = SymbolSegment.ByteIndex;
  CSEIndex: TYPE = Symbols.CSEIndex;
  CTXIndex: TYPE = Symbols.CTXIndex;
  FGTEntry: TYPE = SymbolSegment.FGTEntry;
  HTIndex: TYPE = Symbols.HTIndex;
  ISEIndex: TYPE = Symbols.ISEIndex;
  RecordSEIndex: TYPE = Symbols.RecordSEIndex;
  SEIndex: TYPE = Symbols.SEIndex;
  SERecord: TYPE = Symbols.SERecord;

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


  cb: Table.Base;		-- code base (local copy)
  seb: Table.Base;
  bb: Table.Base;
  stb: Table.Base;

  OutCodeNotify: PUBLIC Table.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    cb ← base[Tree.treeType];
    seb ← base[Symbols.seType];  bb ← base[Symbols.bodyType];
    stb ← base[Literals.stType];
    RETURN
    END;

  FileSequenceError: SIGNAL = CODE;

  fgt: DESCRIPTOR FOR ARRAY OF FGTEntry;
  fgti: INTEGER;
  fgtPages: CARDINAL;
  codeBase, entryBase: StreamDefs.StreamIndex;
  entryVector: DESCRIPTOR FOR ARRAY OF ControlDefs.EntryVectorItem;

  parity: {even, odd};
  codeIndex: CARDINAL;
  buffer: MyInstWord;
  dStarSwitch: BOOLEAN;


  StartCodeFile: PUBLIC PROCEDURE =
    BEGIN -- called to set up bodytable and init binary file header
    OPEN MPtr, ControlDefs, SystemDefs, StreamDefs;
    prefix: CSegPrefix;
    ngfi: CARDINAL = (MAX[nBodies, nSigCodes] + (EPRange-1))/EPRange;
    IF ngfi ~IN [1..4] THEN P5.P5Error[833];
    -- should be 256 (fix ControlDefs)
    IF linkCount > 377B THEN P5.P5Error[834];
    dStarSwitch ← CPtr.dStar;
    prefix ← [header:[
	swapinfo: 0,
	info: [stops: MPtr.stopping, fill: 0, altoCode: ~dStarSwitch,
	  ngfi: ngfi, nlinks: linkCount]],
	entry: ];
    codeSeg.base ← CompilerUtil.nextFilePage[];
    fgti ← -1; fgtPages ← 1;
    codeBase ← GetIndex[objectStream];
    [] ← WriteBlock[objectStream, @prefix, SIZE[CSegPrefix]];
    entryBase ← GetIndex[objectStream];
    codeIndex ← SIZE[CSegPrefix]+nBodies*SIZE[EntryVectorItem];
    parity ← even;
    SetIndex[objectStream, StreamIndex[page: codeBase.page, byte: 2*codeIndex]];
    fgt ← DESCRIPTOR[AllocatePages[fgtPages], (fgtPages*PageSize)/SIZE[FGTEntry]];
    entryVector ← DESCRIPTOR[AllocateSegment[nBodies*SIZE[EntryVectorItem]], nBodies];
    RETURN
    END;

  MoveToCodeWord: PUBLIC PROCEDURE RETURNS [CARDINAL] = 
    BEGIN
    IF parity = odd THEN
      BEGIN
      WITH buffer SELECT dStarSwitch FROM
	FALSE => w.oddbyte ← 377B;
	TRUE => w.oddbyte ← 377B;
	ENDCASE; 
      MPtr.objectStream.put[MPtr.objectStream, buffer];
      parity ← even; codeIndex ← codeIndex+1;
      END;
    RETURN [codeIndex]
    END;


  WriteCodeWord: PUBLIC PROCEDURE [w: WORD] =
    BEGIN
    IF parity # even THEN P5.P5Error[835];
    MPtr.objectStream.put[MPtr.objectStream, w];
    codeIndex ← codeIndex+1;
    RETURN
    END;


  WriteCodeByte: PROCEDURE [b: BYTE] = 
    BEGIN
    IF parity = odd THEN
      BEGIN
      WITH buffer SELECT dStarSwitch FROM
	FALSE => w.oddbyte ← b;
	TRUE => w.oddbyte ← b;
	ENDCASE; 
      MPtr.objectStream.put[MPtr.objectStream, buffer];
      parity ← even; codeIndex ← codeIndex+1;
      END
    ELSE 
      BEGIN
      WITH buffer SELECT dStarSwitch FROM
	FALSE => w.evenbyte ← b;
	TRUE => w.evenbyte ← b;
	ENDCASE; 
      parity ← odd; END;
    RETURN
    END;


  NewFgtEntry: PROCEDURE [fi, ci: ByteIndex] =
    BEGIN -- enters new value into fgt
    i: INTEGER;
    oldfgt: DESCRIPTOR FOR ARRAY OF FGTEntry;

    IF (fgti ← fgti+1) >= LENGTH[fgt] THEN
      BEGIN
      OPEN SystemDefs;
      oldfgt ← fgt;  fgtPages ← fgtPages+1;
      fgt ← DESCRIPTOR[
	    AllocatePages[fgtPages],
	    (fgtPages*PageSize)/SIZE[FGTEntry]];
      FOR i IN [0..LENGTH[oldfgt]) DO fgt[i] ← oldfgt[i] ENDLOOP;
      FreePages[BASE[oldfgt]];
      END;
    fgt[fgti] ← FGTEntry[fIndex: fi, cIndex: ci];
    RETURN
    END;


  OutBinary: PUBLIC PROCEDURE [bti: CBTIndex, start: CCIndex] =
    BEGIN -- outputs binary bytes for body bti starting at start
    cfi: ByteIndex;
    c, cj, nextc: CCIndex;
    bodystart: Address;
    offset, e, fs, nw: CARDINAL;
    bytetable, even: BOOLEAN;
    leftbyte: WORD;
    bodysei: Table.Base RELATIVE POINTER [0..Table.Limit) TO transfer cons SERecord;
    sei: RecordSEIndex;

    bodystart ← MoveToCodeWord[];
    offset ← bodystart * 2;
    FOR c ← start, cb[c].flink UNTIL c = CCNull DO
      WITH  cc:cb[c] SELECT FROM
	code => offset ← offset + cc.isize + cc.pad;
	other => WITH cc SELECT FROM
	  table =>
	    BEGIN
	    OPEN InlineDefs;
	    offset ← offset + tablecodebytes + pad;
	    taboffset ← bodystart;
	    bytetable ← btab ← dStarSwitch AND ByteableJumps[flink];
	    even ← TRUE;
	    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;
		    IF dStarSwitch THEN jbytes ← jbytes+2;
		    IF bytetable THEN
		      BEGIN
		      IF even THEN
			leftbyte ← BITSHIFT[jbytes, 8]
		      ELSE
			WriteCodeWord[BITOR[leftbyte, jbytes]];
		      even ← ~even;
		      END
		    ELSE WriteCodeWord[jbytes];
		    END
		  ELSE EXIT;
		ENDCASE => EXIT;
	      ENDLOOP;
	    IF bytetable AND ~even THEN
	      WriteCodeWord[BITOR[leftbyte,377B]];
	    bodystart ← codeIndex;
	    END;
	  ENDCASE;
	  ENDCASE;
      ENDLOOP;
    e ← bb[bti].entryIndex;
    WITH bb[bti].info SELECT FROM
      Internal =>
	BEGIN
	IF bti = MPtr.mainBody THEN
	  BEGIN
	  WriteCodeWord[MPtr.objectFrameSize];
	  bodystart ← bodystart+1;
	  END;
	fs ← P5U.ComputeFrameSize[frameSize];
	IF bb[bti].resident THEN fs ← fs+ControlDefs.AllocationVectorSize;
	offset ← bodystart*2;
	entryVector[e].info.framesize ← fs;
	NewFgtEntry[cfi ← sourceIndex, offset];
	END;
      ENDCASE => P5.P5Error[836];
    bodysei ← LOOPHOLE[SymbolOps.UnderType[bb[bti].ioType]];
    sei ← seb[bodysei].inRecord;
    entryVector[e].info.nparams ← P5U.WordsForSei[sei];
    entryVector[e].info.defaults ← FALSE;
    entryVector[e].initialpc ← [bodystart];
    bb[bti].info ←
	BodyInfo[External[origin: offset, bytes: , startIndex: fgti, indexLength: ]];
    FOR c ← start, nextc UNTIL c = CCNull DO
      WITH cc:cb[c] SELECT FROM
	code =>
	  BEGIN
	  IF cc.sourcefileindex # NULLfileindex THEN
	    BEGIN
	    IF cfi < cc.sourcefileindex THEN
	      NewFgtEntry[cfi ← cc.sourcefileindex, offset];
	    IF cfi > cc.sourcefileindex THEN
	      BEGIN SIGNAL FileSequenceError; cfi ← cc.sourcefileindex; END;
	    END;
	  IF ~cc.realinst AND cc.inst # FOpCodes.qNOOP THEN ERROR;
	  SELECT cc.isize FROM
	    0 => IF cc.realinst OR cc.inst#FOpCodes.qNOOP THEN ERROR;
	    1 =>
	      BEGIN
	      WriteCodeByte[cc.inst];
	      IF cc.pad # 0 THEN [] ← MoveToCodeWord[];
	      END;
	    2 =>
	      BEGIN
	      IF cc.pad # 0 THEN 
		BEGIN
		IF parity = even THEN SIGNAL CPtr.CodePassInconsistency;
		WriteCodeByte[Mopcodes.zNOOP];
		END;
	      WriteCodeByte[cc.inst];
	      WriteCodeByte[cc.parameters[1]];
	      END;
	    3 =>
	      BEGIN
	      WriteCodeByte[cc.inst];
	      IF cc.pad # 0 THEN 
		BEGIN
		IF parity = even THEN SIGNAL CPtr.CodePassInconsistency;
		[] ← MoveToCodeWord[];
		END;
	      IF dStarSwitch THEN
		BEGIN
		WriteCodeByte[cc.parameters[1]];
		WriteCodeByte[cc.parameters[2]];
		END
	      ELSE
		BEGIN
		WriteCodeByte[cc.parameters[2]];
		WriteCodeByte[cc.parameters[1]];
		END;
	      END;
	    ENDCASE => P5.P5Error[837];
	  offset ← offset+cc.isize+cc.pad;
	  END;
	other => WITH cc SELECT FROM
	  table =>
	  BEGIN
	  CPtr.codeptr ← c;
	  P5.C1W[IF btab THEN Mopcodes.zJIB ELSE Mopcodes.zJIW, taboffset];
	  cb[CPtr.codeptr].pad ← pad;
	  END;
	  startbody =>
	    BEGIN
	    WITH bb[index].info SELECT FROM
	      Internal =>
		NewFgtEntry[cfi ← sourceIndex, offset];
	      ENDCASE => P5.P5Error[838];
	    bb[index].info ← BodyInfo[External[origin: offset, bytes: ,
		startIndex: fgti, indexLength: ]];
	    END;
	  endbody =>
	    BEGIN
	    WITH bb[index].info SELECT FROM
	      External =>
		BEGIN
		indexLength ← fgti-startIndex+1;
		bytes ← offset - origin;
		END;
	      ENDCASE;
	    END;
	  ENDCASE;
	ENDCASE;
      nextc ← cb[c].flink;
      WITH cb[c] SELECT FROM
	code => nw ← MAX[isize, 1]-1+SIZE[code CCItem];
	label => nw ← SIZE[label CCItem];
	jump => nw ← SIZE[jump CCItem];
	other => nw ← SIZE[other CCItem];
	ENDCASE;
      P5U.FreeChunk[c, nw];
      WITH bb[bti].info SELECT FROM
	External =>
	  BEGIN
	  indexLength ← fgti-startIndex+1;
	  bytes ← offset - (bodystart*2);
	  END;
	ENDCASE;
      ENDLOOP;
    RETURN
    END;


  ByteableJumps: PROCEDURE [j: CCIndex] RETURNS [BOOLEAN] =
    BEGIN -- called only when dStarSwitch = TRUE
    DO
    WITH cb[j] SELECT FROM
      jump =>
	IF jtype = JumpC THEN
	  BEGIN
	  jbytes: INTEGER ← cb[destlabel].pc - pc + 3;
	  IF ~forward THEN
	    RETURN[FALSE];
	  IF jbytes > LAST[BYTE] THEN RETURN[FALSE];
	  j ← cb[j].flink;
	  END
	ELSE RETURN[TRUE];
      ENDCASE => RETURN[TRUE]
    ENDLOOP
    END;


  ProcessGlobalStrings: PUBLIC PROCEDURE [framestart: CARDINAL] RETURNS [nextnewframe: CARDINAL] =
    BEGIN
    firstnewcode, nextnewcode: CARDINAL ← MoveToCodeWord[];
    stsize: CARDINAL;
    
    dostring: PROCEDURE [msti: MSTIndex] =
      BEGIN
      nw: CARDINAL;
      IF stb[msti].info = 0 THEN
	BEGIN stb[msti].local ← TRUE; RETURN END;
      nw ← StringDefs.WordsForString[stb[msti].string.length];
      stb[msti].info ← nextnewframe; 
      nextnewframe ← nextnewframe+nw;
      stb[msti].codeIndex ← nextnewcode;
      nextnewcode ← nextnewcode + nw;
      [] ← StreamDefs.WriteBlock[MPtr.objectStream, @stb[msti].string, nw];
      codeIndex ← codeIndex+nw;
      END; -- of dostring

    nextnewframe ← framestart;
    LiteralOps.EnumerateMasterStrings[dostring];
    stsize ← nextnewframe - framestart;
    IF stsize > 0 THEN BLTStrings[firstnewcode, stsize, framestart, FALSE];
    END;


  ProcessLocalStrings: PUBLIC PROCEDURE [framestart: CARDINAL, first: STIndex] RETURNS [nextnewframe: CARDINAL] =
    BEGIN
    nstrings: CARDINAL ← 0;
    countstrings: PROCEDURE [msti: MSTIndex] =
      BEGIN
      IF stb[msti].local AND stb[msti].codeIndex # 0 THEN 
	nstrings ← nstrings+1;
      END;
    firstnewcode, nextnewcode: CARDINAL ← MoveToCodeWord[];
    stsize, i, nw: CARDINAL;
    
    cursize: CARDINAL ← 0;
    StringInfo: TYPE = RECORD [offset: CARDINAL, sti: MSTIndex];
    star: DESCRIPTOR FOR ARRAY OF StringInfo;
    insertstrings: PROCEDURE [msti: MSTIndex] =
      BEGIN
      i, co, nw: CARDINAL;
      IF stb[msti].local THEN
	BEGIN 
	co ← 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 ← StringDefs.WordsForString[stb[msti].string.length];
	  stb[msti].info ← nextnewframe; 
	  nextnewframe ← nextnewframe+nw;
	  stb[msti].codeIndex ← nextnewcode;
	  nextnewcode ← nextnewcode + nw;
	  [] ← StreamDefs.WriteBlock[MPtr.objectStream, @stb[msti].string, nw];
	  codeIndex ← codeIndex+nw;
	  END;
	END;
      END; -- of insertstrings

    nextnewframe ← framestart;
    LiteralOps.EnumerateLocalStrings[first, countstrings];
    IF nstrings # 0 THEN 
      star ← DESCRIPTOR[
	SystemDefs.AllocateHeapNode[nstrings*SIZE[StringInfo]], 
	nstrings];
    LiteralOps.EnumerateLocalStrings[first, insertstrings];
    stsize ← nextnewframe - framestart;
    IF stsize > 0 THEN BLTStrings[firstnewcode, stsize, framestart, TRUE];
    i ← 0;
    WHILE i < nstrings DO
      framestart ← nextnewframe;
      nextnewcode ← firstnewcode ← star[i].offset;
      WHILE i < nstrings AND star[i].offset = nextnewcode DO
	nw ← StringDefs.WordsForString[stb[star[i].sti].string.length];
	nextnewcode ← nextnewcode + nw;
	stb[star[i].sti].info ← nextnewframe;
	nextnewframe ← nextnewframe+nw;
	i ← i+1;
	ENDLOOP;
      stsize ← nextnewframe - framestart;
      BLTStrings[firstnewcode, stsize, framestart, TRUE];
      ENDLOOP;
    IF nstrings # 0 THEN SystemDefs.FreeHeapNode[BASE[star]];
    END;

  BLTStrings: PROCEDURE [coffset, length, foffset: CARDINAL, local: BOOLEAN] =
    BEGIN OPEN FOpCodes;
    Stack.Dump[];
    P5U.PushLitVal[coffset];
    P5U.PushLitVal[length];
    P5U.Out1[IF local THEN qLADRB ELSE qGADRB, foffset];
    P5U.Out0[qBLTC];
    END;


  EndCodeFile: PUBLIC PROCEDURE RETURNS [nbytes: CARDINAL] =
    BEGIN
    OPEN SystemDefs, StreamDefs;
    saveindex: StreamIndex;
    [] ← MoveToCodeWord[];
    MPtr.fgTable ← DESCRIPTOR[BASE[fgt], fgti+1];
    MPtr.codeSeg.pages ← (codeIndex+(PageSize-1))/PageSize;
    saveindex ← GetIndex[MPtr.objectStream];
    SetIndex[MPtr.objectStream, entryBase];
    [] ← WriteBlock[MPtr.objectStream,
	BASE[entryVector],
	LENGTH[entryVector]*SIZE[ControlDefs.EntryVectorItem]];
    FreeSegment[BASE[entryVector]];
    MPtr.mtRoot.framesize ← MPtr.objectFrameSize;
    MPtr.mtRoot.code.length ← codeIndex*2;
    MPtr.mtRoot.crossJumped ← MPtr.switches['j];
    SetIndex[MPtr.objectStream, saveindex];
    RETURN [codeIndex*2]
    END;

END...