-- file CLList.mesa 
-- last edited by Satterthwaite on May 10, 1983 12:15 pm

DIRECTORY
  BcdDefs: TYPE USING [Base, MTIndex],
  BcdOps: TYPE USING [BcdBase, MTHandle],
  CharIO: TYPE USING [PutChar, PutNumber, PutString, PutSubString],
  Environment: TYPE USING [Byte, PageCount],
  FileSegment: TYPE USING [Pages, nullPages],
  FileStream: TYPE USING [Create, EndOf, SetIndex],
  Format: TYPE USING [NumberFormat],
  Heap: TYPE USING [systemZone],
  Inline: TYPE USING [BITOR],
  ListerOps: TYPE USING [CodeOptions],
  ListerUtil: TYPE USING [
    CreateStream, LoadBcd, LoadModule, MapPages, Message,
    SetFileName, SetRoutineSymbols, PutFileID, UnknownModule],
  Mopcodes: TYPE USING [
    zJ2, zJ9, zJEQ2, zJEQ9, zJIB, zJIW, zJNE2, zJNE9, zLI0, zLI6, zLIB, zLIW, zRF,
    zRFC, zRFL, zRIGP, zRIGPL, zRILP, zRILPL, zRXGPL, zRXLP, zRXLPL, zWF, zWFL,
    zWIGPL, zWILP, zWILPL, zWSF, zWXGPL, zWXLP, zWXLPL],
  OpTableDefs: TYPE USING [InstLength, InstName],
  OSMiscOps: TYPE USING [FileError, FindFile],
  PrincOps: TYPE USING [
    CSegPrefix, EntryVectorItem, FrameHandle, FrameVec, InstWord],
  Space: TYPE USING [Handle, LongPointer, Delete],
  Stream: TYPE USING [Delete, GetChar, Handle],
  Strings: TYPE USING [String, SubStringDescriptor, EqualSubStrings],
  Symbols: TYPE USING [
    Name, ISEIndex, BodyInfo, BodyRecord, BTIndex, CBTIndex,
    nullName, ISENull, BTNull],
  SymbolSegment: TYPE USING [FGTEntry],
  SymbolTable: TYPE USING [Base, Acquire, Release, SetCacheSize];

CLList: PROGRAM
  IMPORTS
    CharIO, FileStream, Heap, Inline, ListerUtil, OpTableDefs,
    OSMiscOps, Space, Stream, Strings, SymbolTable
  EXPORTS ListerOps = {
  
  CodeOptions: TYPE = ListerOps.CodeOptions;
  
  MTIndex: TYPE = BcdDefs.MTIndex;
  FrameHandle: TYPE = PrincOps.FrameHandle;
  NumberFormat: TYPE = Format.NumberFormat;
  PageCount: TYPE = Environment.PageCount;
  BYTE: TYPE = Environment.Byte;
  OpCode: TYPE = BYTE;
  
  JumpOp: TYPE = [Mopcodes.zJ2..Mopcodes.zJIW];

  FineGrainInfo: TYPE = RECORD [
    firstSource, lastSource: CARDINAL ← nullSource,
    pc: CARDINAL,
    procEnd: BOOL,
    bti: Symbols.CBTIndex];
  FGT: TYPE = RECORD [
    length: NAT,
    info: SEQUENCE maxLength: NAT OF FineGrainInfo];
  FGHandle: TYPE = LONG POINTER TO FGT;

  nullSource: CARDINAL = CARDINAL.LAST; -- if lastSource, causes to EOF
  
  myFGT: FGHandle;
  
  DigestFGT: PROC = {
    OPEN s: symbols;
    bti, prev: Symbols.BTIndex;
    cspp: LONG POINTER TO PrincOps.CSegPrefix = codebase;
    
    AddMyEntry: PROC [
       source: CARDINAL←nullSource, object: CARDINAL, procEnd: BOOL←FALSE] = {
      IF n = myFGTSize THEN {
	oldFGT: FGHandle ← myFGT;
	myFGTSize ← myFGTSize + 10;
	SetupMyFGT[];
	FOR i: NAT IN [0..oldFGT.maxLength) DO
	  myFGT[i] ← oldFGT[i] ENDLOOP;
	(Heap.systemZone).FREE[@oldFGT]};
      myFGT[n] ←
	[firstSource: source, pc: object, procEnd: procEnd, bti: LOOPHOLE[bti]];
      myFGT.length ← n ← n + 1};
      
    AddBodyFGT: PROC [bti: Symbols.CBTIndex] = {
      OPEN s: symbols;
      procstart: CARDINAL = cspp.entry[s.bb[bti].entryIndex].initialpc*2;
      info: Symbols.BodyInfo[External] = NARROW[s.bb[bti].info, Symbols.BodyInfo[External]];
      fgLast: CARDINAL = info.startIndex + info.indexLength - 1;
      lastSource: CARDINAL ← s.bb[bti].sourceIndex;
      lastObject: CARDINAL ← procstart;
      FOR i: CARDINAL IN [info.startIndex..fgLast] DO
	f: SymbolSegment.FGTEntry = s.fgTable[i];
	WITH f SELECT FROM
	  normal => {
	    lastSource ← lastSource + deltaSource;
	    lastObject ← lastObject + deltaObject;
	    AddMyEntry[source: lastSource, object: lastObject]};
	  step =>
	    IF which = $source THEN lastSource ← lastSource + delta
	    ELSE lastObject ← lastObject + delta;
	  ENDCASE;
	ENDLOOP;
      AddMyEntry[object: procstart + info.bytes, procEnd: TRUE]};
      
    SetupMyFGT: PROC = INLINE {
      myFGT ← (Heap.systemZone).NEW[FGT[myFGTSize] ← [length: 0, info: TRASH]]};
      
    BySource: PROC [r1, r2: LONG POINTER TO FineGrainInfo] RETURNS [BOOL] = {
      IF r1.firstSource > r2.firstSource THEN RETURN [TRUE];
      IF r1.firstSource = r2.firstSource THEN RETURN [r1.pc > r2.pc];
      RETURN [FALSE]};
      
    ByPC: PROC [r1, r2: LONG POINTER TO FineGrainInfo] RETURNS [BOOL] = {
      IF r1.pc > r2.pc THEN RETURN [TRUE];
      IF r1.pc < r2.pc THEN RETURN [FALSE];
      IF r1.procEnd THEN RETURN [FALSE];
      IF r2.procEnd THEN RETURN [TRUE];
      RETURN [r1.firstSource > r2.firstSource]};
      
    Sort: PROC [
       n: CARDINAL,
       greater: PROC [r1, r2: LONG POINTER TO FineGrainInfo] RETURNS [BOOL]] = {
      i: CARDINAL;
      temp: FineGrainInfo;

      SiftUp: PROC [l, u: CARDINAL] = {
	s: CARDINAL;
	key: FineGrainInfo ← myFGT[l-1];
	DO
	  s ← l*2;
	  IF s > u THEN EXIT;
	  IF s < u AND greater[@myFGT[s+1-1], @myFGT[s-1]] THEN s ← s+1;
	  IF greater[@key, @myFGT[s-1]] THEN EXIT;
	  myFGT[l-1] ← myFGT[s-1];
	  l ← s;
	  ENDLOOP;
	myFGT[l-1] ← key};

      FOR i DECREASING IN [2..n/2] DO SiftUp[i, n]; ENDLOOP;
      FOR i DECREASING IN [2..n] DO
	SiftUp[1, i];
	temp ← myFGT[1-1];
	myFGT[1-1] ← myFGT[i-1];
	myFGT[i-1] ← temp;
	ENDLOOP};
      
    n: CARDINAL ← 0;
    myFGTSize: CARDINAL ← (3*s.fgTable.LENGTH)/2;
    SetupMyFGT[];
    bti ← Symbols.BTIndex.FIRST;
    IF s.bb[bti].sourceIndex # 0 THEN 
      AddMyEntry[source: 0, object: cspp.entry[0].initialpc*2];
    DO
      WITH s.bb[bti] SELECT FROM
	Callable => IF ~inline THEN AddBodyFGT[LOOPHOLE[bti]];
	ENDCASE;
      IF s.bb[bti].firstSon # Symbols.BTNull THEN bti ← s.bb[bti].firstSon
      ELSE
	DO
	  prev ← bti;
	  bti ← s.bb[bti].link.index;
	  IF bti = Symbols.BTNull THEN GO TO Done;
	  IF s.bb[prev].link.which # $parent THEN EXIT;
	  ENDLOOP;
      REPEAT Done => NULL;
      ENDLOOP;
    Sort[n, BySource];
    FOR i: CARDINAL DECREASING IN [0..n - 1) DO
      IF myFGT[i].firstSource = nullSource THEN LOOP;
      IF myFGT[i].firstSource = myFGT[i+1].firstSource THEN {
        myFGT[i].lastSource ← myFGT[i+1].lastSource; 
	myFGT[i+1].firstSource ← myFGT[i+1].lastSource}
      ELSE myFGT[i].lastSource ← myFGT[i + 1].firstSource;
      ENDLOOP;
    Sort[n, ByPC]};
    
  offset: CARDINAL;
  codebase: LONG POINTER;
  codepages: PageCount;
  symbols: SymbolTable.Base;
  Tinst, Tbytes, Pinst, Pbytes: CARDINAL ← 0;
  
  -- number formats (initialized by Octify)
  
  decimal: NumberFormat = NumberFormat[
    base: 10, columns: 1, zerofill: FALSE, unsigned: TRUE];
  decimal3: NumberFormat = NumberFormat[
    base: 10, columns: 3, zerofill: FALSE, unsigned: TRUE];

  hoctal3: NumberFormat;
  hoctal3z: NumberFormat;
  hoctal5: NumberFormat;
  hoctal6: NumberFormat;
  hoctal1: NumberFormat;
  
  -- set base for listings
  
  
  Hexify: PROC = {
    hoctal3 ← NumberFormat[base: 16, columns: 3, zerofill: FALSE, unsigned: TRUE];
    hoctal3z ← NumberFormat[
      base: 16, columns: 3, zerofill: FALSE, unsigned: TRUE];
    hoctal5 ← NumberFormat[base: 16, columns: 5, zerofill: FALSE, unsigned: TRUE];
    hoctal6 ← NumberFormat[base: 16, columns: 6, zerofill: FALSE, unsigned: TRUE];
    hoctal1 ← NumberFormat[base: 16, columns: 1, zerofill: FALSE, unsigned: TRUE]};
    
  Octify: PROC = {
    hoctal3 ← NumberFormat[base: 8, columns: 3, zerofill: FALSE, unsigned: TRUE];
    hoctal3z ← NumberFormat[base: 8, columns: 3, zerofill: TRUE, unsigned: TRUE];
    hoctal5 ← NumberFormat[base: 8, columns: 5, zerofill: FALSE, unsigned: TRUE];
    hoctal6 ← NumberFormat[base: 8, columns: 6, zerofill: FALSE, unsigned: TRUE];
    hoctal1 ← NumberFormat[base: 8, columns: 1, zerofill: FALSE, unsigned: TRUE]};
    
  -- source file procedures
  
  source: Stream.Handle;
  sourceAvailable: BOOL;
  
  out: Stream.Handle ← NIL;
  
  OpenOutput: PROC [root: Strings.String] = {
    outName: STRING ← [40];
    ListerUtil.SetFileName[outName, root, "cl"L];
    out ← ListerUtil.CreateStream[outName]};
    
  CloseOutput: PROC = {
    Stream.Delete[out];  out ← NIL};
    

  OutCheck: PROC [xfirst: CARDINAL, xlast: CARDINAL] = {
    nextchar: CHAR;
    lastcr: CARDINAL;
    IF ~sourceAvailable THEN RETURN;
    FOR lastcr ← xfirst, lastcr - 1 UNTIL lastcr = 0 DO
      FileStream.SetIndex[source, lastcr];
      IF source.GetChar[] = '\n THEN EXIT;
      ENDLOOP;
    THROUGH (lastcr..xfirst) DO CharIO.PutChar[out, ' ] ENDLOOP;
    FileStream.SetIndex[source, xfirst];
    WHILE xfirst # xlast DO
      IF FileStream.EndOf[source] THEN GOTO eof;
      nextchar ← source.GetChar[];
      xfirst ← xfirst + 1;
      IF nextchar = '\032 THEN	-- Bravo trailer
	WHILE nextchar # '\n DO
	  IF FileStream.EndOf[source] THEN GOTO eof;
	  nextchar ← source.GetChar[];
	  xfirst ← xfirst + 1;
	  ENDLOOP;
      CharIO.PutChar[out, nextchar];
      REPEAT eof => NULL;
      ENDLOOP;
    IF nextchar # '\n THEN CharIO.PutChar[out, '\n]};
    
  SetUpSource: PROC = {
    sourceAvailable ← TRUE;
    source ← FileStream.Create[
      OSMiscOps.FindFile[symbols.sourceFile
        ! OSMiscOps.FileError => {sourceAvailable ← FALSE; CONTINUE}]]};
    
  CloseSource: PROC = {IF sourceAvailable THEN Stream.Delete[source]};
    

  FilterBody: PROC [bti: Symbols.CBTIndex, key: Strings.String] RETURNS [BOOL←TRUE] = {
    IF key # NIL THEN {
      sei: Symbols.ISEIndex = symbols.bb[bti].id;
      hti: Symbols.Name;
      d1: Strings.SubStringDescriptor;
      d2: Strings.SubStringDescriptor ← [base: key, offset: 0, length: key.length];
      IF sei = Symbols.ISENull OR (hti ← symbols.seb[sei].hash) = Symbols.nullName THEN
        RETURN [FALSE];
      symbols.SubStringForName[@d1, hti];
      RETURN [Strings.EqualSubStrings[@d1, @d2]]}};
 
  PrintBodyName: PROC [bti: Symbols.CBTIndex] = {
    IF ~sourceAvailable THEN {
      sei: Symbols.ISEIndex = symbols.bb[bti].id;
      hti: Symbols.Name;
      IF sei # Symbols.ISENull AND (hti ← symbols.seb[sei].hash) # Symbols.nullName THEN {
	ss: Strings.SubStringDescriptor;
	symbols.SubStringForName[@ss, hti];
	CharIO.PutSubString[out, @ss]; CharIO.PutString[out, ":\n"L]}}};
    
  EvenUp: PROC [n: CARDINAL] RETURNS [CARDINAL] =  INLINE { 
    -- Round up to an even number
    RETURN [n + n MOD 2]};
    
  GetByte: PROC [pc: CARDINAL] RETURNS [BYTE] = { 
    -- pc is a byte address
    w: LONG POINTER TO PrincOps.InstWord = codebase + pc/2;
    RETURN [IF pc MOD 2 = 0 THEN w.evenbyte ELSE w.oddbyte]};
    
  GetWord: PROC [pc: CARDINAL] RETURNS [WORD] = INLINE { 
    -- pc is a word address
    RETURN [(codebase + pc)↑]};
    
  JumpAddress: PROC [jop: OpCode, arg: INTEGER] RETURNS [CARDINAL] = {
    -- given a jump operator and its argument, return its target address
    OPEN Mopcodes;
    SELECT OpTableDefs.InstLength[
      jop] FROM
      1 =>
	SELECT jop FROM
	  IN [zJ2..zJ9] => arg ← jop - zJ2 + 2;
	  IN [zJEQ2..zJEQ9] => arg ← jop - zJEQ2 + 2;
	  IN [zJNE2..zJNE9] => arg ← jop - zJNE2 + 2;
	  ENDCASE => ERROR;
      2 => {
	IF arg > 177B THEN arg ← Inline.BITOR[arg, 177400B];
	arg ← arg - 1};
      ENDCASE => arg ← arg - 2;
    RETURN [INTEGER[offset] + arg]};
    
  OutWJTab: PROC [tabstart, tablength: CARDINAL, options: CodeOptions] = {
    Pbytes ← Pbytes + tablength*2;
    FOR pc: CARDINAL IN [tabstart..tabstart + tablength) DO
      w: INTEGER = GetWord[pc];
      CharIO.PutString[out, "\n\t\t"L];
      IF options.stripped THEN {CharIO.PutNumber[out, w, hoctal5]; LOOP};
      IF options.full THEN CharIO.PutString[out, "\t\t"L];
      CharIO.PutString[out, " ("L];
      CharIO.PutNumber[out, JumpAddress[Mopcodes.zJIW, w], hoctal5];
      CharIO.PutChar[out, ')];
      ENDLOOP};
    
  OutBJTab: PROC [tabstart, tablength: CARDINAL, options: CodeOptions] = {
    Pbytes ← Pbytes + EvenUp[tablength];
    FOR pc: CARDINAL IN [tabstart*2..tabstart*2 + tablength) DO
      b: BYTE = GetByte[pc];
      CharIO.PutString[out, "\n\t\t"L];
      IF options.stripped THEN {CharIO.PutNumber[out, b, hoctal5]; LOOP};
      IF options.full THEN CharIO.PutString[out, "\t\t"L];
      CharIO.PutString[out, " ("L];
      CharIO.PutNumber[out, JumpAddress[Mopcodes.zJIB, b], hoctal5];
      CharIO.PutChar[out, ')];
      ENDLOOP};
    
  PutPair: PROC [byte: CARDINAL] = {
    a: CARDINAL = byte/16;
    b: CARDINAL = byte MOD 16;
    IF a < 8 AND b < 8 THEN CharIO.PutChar[out, ' ];
    CharIO.PutChar[out, '[];
    CharIO.PutNumber[out, a, hoctal1];
    CharIO.PutChar[out, ',];
    CharIO.PutNumber[out, b, hoctal1];
    CharIO.PutChar[out, ']]};
    
  PrintCode: PROC [
     startcode, endcode: CARDINAL, options: CodeOptions] = {
    -- list opcodes for indicated segment of code
    OPEN Mopcodes;
    lastconstant: INTEGER;
    FOR offset IN [startcode..endcode) DO
      inst: BYTE = GetByte[offset];
      il: [0..3] = OpTableDefs.InstLength[inst];
      -- loginst[inst];
      Pinst ← Pinst + 1;
      CharIO.PutChar[out, '\t];
      IF ~options.stripped THEN {
	IF options.full THEN {
	  CharIO.PutNumber[out, offset/2, hoctal5];
	  CharIO.PutString[out, (IF offset MOD 2 = 0 THEN ",E "L ELSE ",O "L)]};
	CharIO.PutNumber[out, offset, hoctal5];
	CharIO.PutChar[out, ':]};
      IF options.full THEN {
	CharIO.PutString[out, "\t["L]; CharIO.PutNumber[out, inst, hoctal3z]; CharIO.PutChar[out, ']]};
      CharIO.PutChar[out, '\t];
      CharIO.PutString[out, OpTableDefs.InstName[inst]];
      SELECT il FROM
	0, 1 => {
	  Pbytes ← Pbytes + 1;
	  IF inst IN [zLI0..zLI6] THEN lastconstant ← inst - zLI0
	  ELSE IF inst IN JumpOp AND ~options.stripped THEN {
	    CharIO.PutString[out, "\t       ("L];
	    CharIO.PutNumber[out, JumpAddress[inst, 0], hoctal1];
	    CharIO.PutChar[out, ')]}};
	2 => {
	  byte: BYTE = GetByte[(offset ← offset + 1)];
	  Pbytes ← Pbytes + 2;
	  CharIO.PutChar[out, '\t];
	  SELECT inst FROM
	    zRILP, zWILP, zRXLP, zWXLP, zRIGP, zRXLPL, zWXLPL, zRXGPL, zWXGPL,
	      zRILPL, zWILPL, zRIGPL, zWIGPL => PutPair[byte];
	    ENDCASE => CharIO.PutNumber[out, byte, hoctal6];
	  IF inst = zLIB THEN lastconstant ← byte
	  ELSE IF inst IN JumpOp AND ~options.stripped THEN {
	    CharIO.PutString[out, " ("L];
	    CharIO.PutNumber[out, JumpAddress[inst, byte], hoctal1];
	    CharIO.PutChar[out, ')]}};
	3 => {
	  ab: RECORD [first, second: BYTE];
	  Pbytes ← Pbytes + 3;
	  ab.first ← GetByte[(offset ← offset + 1)];
	  ab.second ← GetByte[(offset ← offset + 1)];
	  CharIO.PutChar[out, '\t];
	  SELECT inst FROM
	    zRF, zWF, zWSF, zRFC, zRFL, zWFL => {
	      CharIO.PutNumber[out, ab.first, hoctal6];
	      CharIO.PutString[out, ", "L];
	      PutPair[ab.second]};
	    ENDCASE => {
	      v: INTEGER = ab.first*256 + ab.second;
	      CharIO.PutNumber[out, v, hoctal6];
	      SELECT inst FROM
		zJIB => OutBJTab[v, lastconstant, options];
		zJIW => OutWJTab[v, lastconstant, options];
		zLIW => lastconstant ← v;
		IN JumpOp =>
		  IF ~options.stripped THEN {
		    CharIO.PutString[out, " ("L];
		    CharIO.PutNumber[out, JumpAddress[inst, v], hoctal1];
		    CharIO.PutChar[out, ')]};
		ENDCASE}};
	ENDCASE;
      CharIO.PutChar[out, '\n];
      ENDLOOP};
    
  ListModule: PROC [
      file, module, proc: Strings.String, output: Strings.String, options: CodeOptions] = {
    bcdFile: Strings.String ← [100];
    bcdSeg, cSeg, sSeg: FileSegment.Pages;
    mti: BcdDefs.MTIndex;
    ListerUtil.SetFileName[bcdFile, file, "bcd"L];
    bcdSeg ← ListerUtil.LoadBcd[bcdFile];
    IF bcdSeg = FileSegment.nullPages THEN GO TO NoFile;
    [mti, cSeg, sSeg] ← ListerUtil.LoadModule[bcdSeg, module
      ! ListerUtil.UnknownModule => {GO TO NoModule}];
    DoCodeListing[cSeg, sSeg, bcdSeg, mti, proc, output, options]
    EXITS
      NoFile => ListerUtil.Message["File cannot be opened"L];
      NoModule => {
	ListerUtil.Message["File does not contain module "L];
	ListerUtil.Message[module]}};
    
  ShowTotals: PROC = {
    CharIO.PutString[out, "Instructions: "L];
    CharIO.PutNumber[out, Pinst, decimal];
    CharIO.PutString[out, ", Bytes: "L];
    CharIO.PutNumber[out, Pbytes ← EvenUp[Pbytes], decimal];
    CharIO.PutString[out, "\n\n"L];
    Tinst ← Tinst + Pinst;
    Pinst ← 0;
    Tbytes ← Tbytes + Pbytes;
    Pbytes ← 0};
    
  DoCodeListing: PROC [
      cseg, sseg, bcdseg: FileSegment.Pages,
      mti: MTIndex, proc: Strings.String,
      output: Strings.String, options: CodeOptions] = {
    OPEN BcdDefs, Symbols;
    codeSpace: Space.Handle;
    crossJumped: BOOL;
    codeOffset, framesize: CARDINAL;
    prevBti: BTIndex ← BTNull;
      BEGIN
      bcdSpace: Space.Handle ← ListerUtil.MapPages[bcdseg];
      bcd: BcdOps.BcdBase ← bcdSpace.LongPointer;
      mth: BcdOps.MTHandle ← @LOOPHOLE[bcd + bcd.mtOffset, Base][mti];
      codeOffset ← mth.code.offset;
      framesize ← mth.framesize;
      crossJumped ← mth.crossJumped;
      Space.Delete[bcdSpace];
      END;
    IF cseg = FileSegment.nullPages THEN
      ListerUtil.Message["Code not available"L]
    ELSE IF sseg = FileSegment.nullPages THEN
      ListerUtil.Message["Symbols not available"L]
    ELSE {
      print: BOOL ← FALSE;
      cspp: LONG POINTER TO PrincOps.CSegPrefix;
      codeSpace ← ListerUtil.MapPages[cseg];
      codebase ← codeSpace.LongPointer + codeOffset;
      codepages ← cseg.span.pages;
      cspp ← codebase;
      SymbolTable.SetCacheSize[0];	-- clear cache
      symbols ← SymbolTable.Acquire[sseg];
      IF cspp.header.info.altoCode THEN {
	ListerUtil.Message["Cannot list Alto code"L]; GO TO Fail};
      IF symbols.fgTable = NIL THEN {
	ListerUtil.Message["Bad bcd format"L]; GO TO Fail};
      ListerUtil.SetRoutineSymbols[symbols];
      SetUpSource[];
      OpenOutput[output];
      ListerUtil.PutFileID[out];
      IF crossJumped THEN CharIO.PutString[out, "Cross jumped\n"L];
      CharIO.PutString[out, "Global frame size:  "L];
      CharIO.PutNumber[out, framesize, decimal];
      CharIO.PutString[out, "\n\n"L];
      IF options.radix = $hex THEN Hexify[] ELSE Octify[];
      Tbytes ← Tinst ← 0;
      DigestFGT[];
      FOR i: CARDINAL IN [0..myFGT.length) DO
	ff: FineGrainInfo = myFGT[i];
	IF ff.bti # prevBti THEN {
	  IF prevBti # BTNull AND print THEN ShowTotals[];
	  print ← FilterBody[ff.bti, proc]};
	IF ff.firstSource # nullSource AND print THEN
	  IF ff.lastSource = ff.firstSource THEN CharIO.PutChar[out, '\n]
	  ELSE OutCheck[ff.firstSource, ff.lastSource];
	IF ff.bti # prevBti THEN {
	  ep: CARDINAL = symbols.bb[ff.bti].entryIndex;
	  evi: LONG POINTER TO PrincOps.EntryVectorItem = @cspp.entry[ep];
	  fsize: CARDINAL = PrincOps.FrameVec[evi.info.framesize];
	  IF print THEN {
	    PrintBodyName[ff.bti];
	    IF options.full THEN CharIO.PutChar[out, '\t];
            CharIO.PutString[out, "   Entry point: "L];
	    CharIO.PutNumber[out, ep, decimal];
	    CharIO.PutString[out, ",   Frame size:  "L];
	    CharIO.PutNumber[out, fsize, decimal];
	    CharIO.PutChar[out, '\n]}};
	IF print THEN {
	  IF ~ff.procEnd THEN PrintCode[ff.pc, myFGT[i + 1].pc, options];
	  CharIO.PutChar[out, '\n]};
	prevBti ← ff.bti;
	ENDLOOP;
      IF prevBti # Symbols.BTNull AND print THEN ShowTotals[];
      (Heap.systemZone).FREE[@myFGT];
      SymbolTable.Release[symbols];
      Space.Delete[codeSpace];
      CloseSource[];
      CharIO.PutChar[out, '\n];
      IF proc = NIL THEN {
	IF options.full THEN CharIO.PutChar[out, '\t];
	CharIO.PutString[out, "Total instructions: "L];
	CharIO.PutNumber[out, Tinst, decimal];
	CharIO.PutString[out, ", Bytes: "L];
	CharIO.PutNumber[out, Tbytes, decimal];
	CharIO.PutChar[out, '\n]};
      CloseOutput[]
      EXITS
	Fail => {SymbolTable.Release[symbols]; Space.Delete[codeSpace]}}};
    
  ListProc: PUBLIC PROC [
      input, proc: Strings.String, output: Strings.String, options: CodeOptions] = {
    ListModule[input, input, proc, output, options]};
      
  ListCode: PUBLIC PROC [root: Strings.String, options: CodeOptions] = {
    ListModule[root, root, NIL, root, options]};
    
  ListCodeInConfig: PUBLIC PROC [config, name: Strings.String, options: CodeOptions] = {
    ListModule[config, name, NIL, name, options]};
    
 -- initialization
   Octify[];

  }.