-- listFGT.mesa; modified by Sweet, July 8, 1980  9:31 AM

DIRECTORY
  CommanderDefs USING [AddCommand, CommandBlockHandle],
  IODefs USING [NumberFormat, SP, WriteString],
  ListerDefs USING [
    FileSegmentHandle, IncorrectVersion, Load, MultipleModules, NoCode, NoFGT,
    NoSymbols, PrintSei, SetRoutineSymbols, WriteFileID],
  OutputDefs USING [
    CloseOutput, OpenOutput, PutChar, PutCR, PutDecimal, PutNumber, PutOctal,
    PutString],
  PrincOps USING [CSegPrefix],
  SegmentDefs USING [
    DeleteFileSegment, FileNameError, FileSegmentAddress, SwapError, SwapIn,
    Unlock],
  String USING [AppendString],
  Symbols USING [BTIndex, BTNull],
  SymbolTable USING [Acquire, Base, Release, TableForSegment],
  Storage USING [Node, Free];

ListFGT: PROGRAM
  IMPORTS
    CommanderDefs, IODefs, ListerDefs, OutputDefs, SegmentDefs, String,
    SymbolTable, Storage
  EXPORTS ListerDefs =
  BEGIN OPEN ListerDefs, OutputDefs, Symbols;
  
  SP: CHARACTER = ' ;
  
  symbols: SymbolTable.Base;
  code: POINTER TO PrincOps.CSegPrefix;
  
  SortByFirstFG: PROCEDURE [na: DESCRIPTOR FOR ARRAY OF BodyData] =
    BEGIN
    i: CARDINAL;
    j: INTEGER;
    key: BodyData;
    FOR i IN [1..LENGTH[na]) DO
      key ← na[i];
      j ← i - 1;
      WHILE j >= 0 AND na[j].firstFG > key.firstFG DO
	na[j + 1] ← na[j]; j ← j - 1; ENDLOOP;
      na[j + 1] ← key;
      ENDLOOP;
    END;
    
  GenBT: PROCEDURE [p: PROCEDURE [Symbols.BTIndex]] =
    BEGIN OPEN symbols;
    bti, prev: BTIndex;
    bti ← FIRST[BTIndex];
    DO
      p[bti];
      IF bb[bti].firstSon # BTNull THEN bti ← bb[bti].firstSon
      ELSE
	DO
	  prev ← bti;
	  bti ← bb[bti].link.index;
	  IF bti = BTNull THEN GO TO Done;
	  IF bb[prev].link.which # parent THEN EXIT;
	  ENDLOOP;
      REPEAT Done => NULL;
      ENDLOOP;
    END;
    
  PrintByColumns: PROCEDURE [
    PrintOne: PROCEDURE [item: CARDINAL, lastOnLine: BOOLEAN],
    firstItem, nItems, nColumns, spaceBetween: CARDINAL] =
    BEGIN
    i, j, nc: CARDINAL;
    delta: CARDINAL ← (nItems + nColumns - 1)/nColumns;
    last: BOOLEAN;
    FOR i IN [0..delta) DO
      nc ← 0;
      last ← FALSE;
      FOR j ← i, j + delta WHILE ~last AND j < nItems DO
	nc ← nc + 1;
	last ← nc = nColumns;
	PrintOne[firstItem + j, last];
	IF ~last THEN THROUGH [0..spaceBetween) DO PutChar[SP]; ENDLOOP;
	ENDLOOP;
      PutCR[];
      ENDLOOP;
    END;
    
  BodyData: TYPE = RECORD [firstFG, lastFG: CARDINAL, bti: Symbols.BTIndex];
  
  lastSource, lastObject, bodyObject: CARDINAL;
  
  PrintFGT: PROCEDURE =
    BEGIN OPEN Symbols, symbols;
    cbti: BTIndex;
    i, n, cfirst, clast: CARDINAL;
    na: DESCRIPTOR FOR ARRAY OF BodyData;
    
    countBti: PROCEDURE [bti: BTIndex] =
      BEGIN
      WITH bb[bti] SELECT FROM Callable => IF inline THEN RETURN; ENDCASE;
      n ← n + 1;
      END;
      
    insertBti: PROCEDURE [bti: BTIndex] =
      BEGIN OPEN symbols;
      WITH bb[bti] SELECT FROM Callable => IF inline THEN RETURN; ENDCASE;
      WITH bb[bti].info SELECT FROM
	External => na[i] ← [startIndex, startIndex + indexLength - 1, bti];
	ENDCASE;
      i ← i + 1;
      END;
      
    PrintBodyLine: PROCEDURE [depth: CARDINAL] =
      BEGIN
      first, last: CARDINAL;
      origin: CARDINAL;
      bti: BTIndex;
      [first, last, bti] ← na[i];
      THROUGH [0..depth*2) DO PutChar[SP]; ENDLOOP;
      PutString["  ["L];
      PutDecimal[LOOPHOLE[bti]];
      PutString["] fg: ["L];
      PutDecimal[first];
      PutString[".."L];
      PutDecimal[last];
      PutString["], pc: ["L];
      WITH br: bb[bti] SELECT FROM
	Callable =>
	  BEGIN
	  bodyObject ← origin ← code.entry[br.entryIndex].initialpc*2;
	  lastObject ← 0;
	  lastSource ← br.sourceIndex;
	  END;
	Other => origin ← bodyObject + br.relOffset;
	ENDCASE;
      WITH bi: bb[bti].info SELECT FROM
	External =>
	  BEGIN
	  PutOctal[origin];
	  PutString[".."L];
	  PutOctal[origin + bi.bytes - 1];
	  PutChar[']];
	  END;
	ENDCASE;
      PutString[", source: "L];
      PutDecimal[bb[bti].sourceIndex];
      WITH br: bb[bti] SELECT FROM
	Callable => BEGIN PutString[", ep: "L]; PutDecimal[br.entryIndex]; END;
	Other => BEGIN PutString[", relO: "L]; PutOctal[br.relOffset]; END;
	ENDCASE;
      PutCR[];
      i ← i + 1;
      END;
      
    PrintBodyStuff: PROCEDURE [depth: CARDINAL] =
      BEGIN
      myLast: CARDINAL = na[i].lastFG;
      PrintBodyLine[depth];
      WHILE i < n AND na[i].firstFG <= myLast DO
	PrintBodyStuff[depth + 1]; ENDLOOP;
      END;
      
    PrintFGEntry: PROCEDURE [item: CARDINAL, lastOnLine: BOOLEAN] =
      BEGIN OPEN symbols;
      PutNumber[item, decimal5];
      PutString[": "L];
      WITH ff: fgTable[item] SELECT FROM
	normal =>
	  BEGIN -- 34 chars wide?
	  PutNumber[ff.deltaObject, octal3];
	  PutString["B, "L];
	  PutNumber[ff.deltaSource, decimal3];
	  PutString[" = "L];
	  PutNumber[absFGT[item - cfirst].object, octal5];
	  PutString["B, "L];
	  PutNumber[absFGT[item - cfirst].source, decimal5];
	  PutString[" ("L];
	  PutNumber[absFGT[item - cfirst].object + bodyObject, octal6];
	  PutString["B)"];
	  END;
	step =>
	  BEGIN
	  PutString["Step "];
	  IF ff.which = source THEN
	    BEGIN
	    PutString["source:"L];
	    PutNumber[ff.delta, decimal5];
	    PutChar[IODefs.SP];
	    END
	  ELSE
	    BEGIN
	    PutString["object:"L];
	    PutNumber[ff.delta, octal5];
	    PutChar['B];
	    END;
	  IF ~lastOnLine THEN THROUGH [17..34) DO PutChar[IODefs.SP]; ENDLOOP;
	  END;
	ENDCASE;
      END;
      
    AbsFGTEntry: TYPE = RECORD [object, source: CARDINAL];
    absFGT: POINTER TO ARRAY [0..0) OF AbsFGTEntry;
    
    GenAbsFGT: PROCEDURE =
      BEGIN OPEN s: symbols;
      i: CARDINAL;
      absFGT ← Storage.Node[(clast - cfirst + 1)*SIZE[AbsFGTEntry]];
      FOR i IN [cfirst..clast] DO
	WITH ff: s.fgTable[i] SELECT FROM
	  normal =>
	    BEGIN
	    lastSource ← lastSource + ff.deltaSource;
	    lastObject ← lastObject + ff.deltaObject;
	    END;
	  step =>
	    IF ff.which = source THEN lastSource ← lastSource + ff.delta
	    ELSE lastObject ← lastObject + ff.delta;
	  ENDCASE;
	absFGT[i - cfirst] ← [source: lastSource, object: lastObject];
	ENDLOOP;
      END;
      
    n ← 0;
    GenBT[countBti];
    na ← DESCRIPTOR[Storage.Node[SIZE[BodyData]*n], n];
    i ← 0;
    GenBT[insertBti];
    SortByFirstFG[na];
    i ← 0;
    WHILE i < n DO
      [cfirst, clast, cbti] ← na[i];
      WITH br: bb[cbti] SELECT FROM
	Callable =>
	  IF ~br.inline THEN
	    BEGIN
	    PrintSei[br.id];
	    PutCR[];
	    PrintBodyStuff[0];
	    GenAbsFGT[];
	    PrintByColumns[
	      PrintOne: PrintFGEntry, firstItem: cfirst,
	      nItems: clast - cfirst + 1, nColumns: 2, spaceBetween: 2];
	    Storage.Free[absFGT];
	    PutCR[];
	    END;
	ENDCASE => ERROR;
      ENDLOOP;
    Storage.Free[BASE[na]];
    RETURN
    END;
    
  octal3: IODefs.NumberFormat =
    [base: 8, columns: 3, zerofill: FALSE, unsigned: TRUE];
  decimal3: IODefs.NumberFormat =
    [base: 10, columns: 3, zerofill: FALSE, unsigned: TRUE];
  octal4: IODefs.NumberFormat =
    [base: 8, columns: 4, zerofill: FALSE, unsigned: TRUE];
  octal5: IODefs.NumberFormat =
    [base: 8, columns: 5, zerofill: FALSE, unsigned: TRUE];
  decimal5: IODefs.NumberFormat =
    [base: 10, columns: 5, zerofill: FALSE, unsigned: TRUE];
  octal6: IODefs.NumberFormat =
    [base: 8, columns: 6, zerofill: FALSE, unsigned: TRUE];
  
  PrintFGEntry: PROCEDURE [item: CARDINAL, lastOnLine: BOOLEAN] =
    BEGIN OPEN symbols;
    PutNumber[item, decimal5];
    PutString[": "L];
    WITH ff: fgTable[item] SELECT FROM
      normal =>
	BEGIN -- 34 chars wide?
	PutNumber[ff.deltaObject, octal3];
	PutString["B, "L];
	PutNumber[ff.deltaSource, decimal3];
	lastSource ← lastSource + ff.deltaSource;
	lastObject ← lastObject + ff.deltaObject;
	PutString[" = "L];
	PutNumber[lastObject, octal5];
	PutString["B, "L];
	PutNumber[lastSource, decimal5];
	PutString[" ("L];
	PutNumber[lastObject + bodyObject, octal6];
	PutString["B)"];
	END;
      step =>
	BEGIN
	PutString["Step "];
	IF ff.which = source THEN
	  BEGIN
	  PutString["source:"L];
	  lastSource ← lastSource + ff.delta;
	  PutNumber[ff.delta, decimal5];
	  PutChar[IODefs.SP];
	  END
	ELSE
	  BEGIN
	  PutString["object:"L];
	  lastObject ← lastObject + ff.delta;
	  PutNumber[ff.delta, octal5];
	  PutChar['B];
	  END;
	IF ~lastOnLine THEN THROUGH [17..34) DO PutChar[IODefs.SP]; ENDLOOP;
	END;
      ENDCASE;
    END;
    
  FGTable: PROCEDURE [root: STRING] =
    BEGIN OPEN String, SegmentDefs;
    i: CARDINAL;
    defs: BOOLEAN ← FALSE;
    bcdFile: STRING ← [40];
    sseg, cseg: FileSegmentHandle;
    AppendString[bcdFile, root];
    FOR i IN [0..bcdFile.length) DO
      IF bcdFile[i] = '. THEN EXIT;
      REPEAT FINISHED => AppendString[bcdFile, ".bcd"];
      ENDLOOP;
    BEGIN
    [code: cseg, symbols: sseg] ← Load[
      bcdFile ! NoFGT => GOTO badformat; NoCode => GO TO defsFile;
      NoSymbols, IncorrectVersion, MultipleModules => GOTO badformat;
      SegmentDefs.FileNameError => GOTO badname];
    symbols ← SymbolTable.Acquire[SymbolTable.TableForSegment[sseg]];
    SegmentDefs.SwapIn[cseg];
    code ← SegmentDefs.FileSegmentAddress[cseg];
    ListerDefs.SetRoutineSymbols[symbols];
    OpenOutput[root, ".fl"];
    WriteFileID[];
    IF symbols.sourceFile # NIL THEN
      BEGIN PutString["  Source: "]; PutString[symbols.sourceFile]; PutCR[]; END;
    PrintFGT[];
    SegmentDefs.Unlock[cseg];
    SymbolTable.Release[symbols];
    SegmentDefs.DeleteFileSegment[sseg ! SegmentDefs.SwapError => CONTINUE];
    SegmentDefs.DeleteFileSegment[cseg ! SegmentDefs.SwapError => CONTINUE];
    CloseOutput[];
    EXITS
      defsFile => IODefs.WriteString["Definitions File!"];
      badformat => IODefs.WriteString["Bad Format!"];
      badname => IODefs.WriteString["File Not Found!"];
    END;
    END;
    
  command: CommanderDefs.CommandBlockHandle;
  
  command ← CommanderDefs.AddCommand["FGTable", LOOPHOLE[FGTable], 1];
  command.params[0] ← [type: string, prompt: "Filename"];
  
  END...