-- ListCode.mesa; 
--  edited by Bruce; 13-Jan-81 11:00:33
--  edited by Sandman; September 29, 1980  9:21 AM
--  edited by Sweet; 26-Mar-81 12:26:01
--  edited by Satterthwaite; May 10, 1983 1:03 pm

DIRECTORY
  BcdDefs: TYPE USING [Base, MTIndex],
  BcdOps: TYPE USING [BcdBase, MTHandle],
  CommanderOps: TYPE USING [AddCommand, CommandBlockHandle],
  Environment: TYPE USING [Byte, PageCount],
  FileSegment: TYPE USING [Pages],
  FileStream: TYPE USING [Create, EndOf, SetIndex],
  Format: TYPE USING [NumberFormat],
  Heap: TYPE USING [systemZone],
  Inline: TYPE USING [BITOR],
  ListerDefs: TYPE USING [
    IncorrectVersion, Load, LoadFromConfig, MapPages, NoCode, NoFGT, NoFile,
    NoSymbols, SetRoutineSymbols, WriteFileID, WriteString],
  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 [InstAligned, InstLength, InstName, PopStack, PushStack],
  OSMiscOps: TYPE USING [FileError, FindFile],
  OutputDefs: TYPE USING [
    CloseOutput, OpenOutput, PutChar, PutNumber, PutString, PutLongSubString],
  PrincOps: TYPE USING [
    CSegPrefix, EntryVectorItem, FrameHandle, FrameVec, InstWord],
  Space: TYPE USING [Handle, LongPointer, Delete],
  Stream: TYPE USING [Delete, GetChar, Handle],
  Strings: TYPE USING [AppendString, SubStringDescriptor],
  Symbols: TYPE USING [
    BodyInfo, BodyRecord, BTIndex, BTNull, CBTIndex, HTIndex, HTNull, ISEIndex,
    SENull],
  SymbolSegment: TYPE USING [FGTEntry],
  SymbolTable: TYPE USING [Base, Acquire, Release, SetCacheSize];

ListCode: PROGRAM
  IMPORTS
    CommanderOps, FileStream, Heap, Inline, ListerDefs, OpTableDefs,
    OSMiscOps, OutputDefs, Space, Stream, Strings, SymbolTable = {
  OPEN OutputDefs;
  
  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: BOOLEAN,
    bti: Symbols.CBTIndex];
  FGT: TYPE = RECORD [
    length: NAT,
    info: SEQUENCE maxLength: NAT OF FineGrainInfo];
  FGHandle: TYPE = LONG POINTER TO FGT;

  nullSource: CARDINAL = LAST[CARDINAL]; -- 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: BOOLEAN←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: NULL]]};
      
    BySource: PROC [r1, r2: LONG POINTER TO FineGrainInfo] RETURNS [BOOLEAN] = {
      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 [BOOLEAN] = {
      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 [BOOLEAN]] = {
      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*LENGTH[s.fgTable])/2;
    SetupMyFGT[];
    bti ← FIRST[Symbols.BTIndex];
    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]};
    
  -- generate list of opcode lengths
  
  OpcodeLengths: PROC [root: STRING] = {
    OPEN OpTableDefs;
    digit: STRING = "0123456789"L;
    OpenOutput[root, ".list"L];
    PutString["  OpcodeLengths: PACKED ARRAY [0..255] OF [0..3] = ["L];
    FOR i: opcode IN opcode DO
      IF i MOD 32 = 0 THEN PutString["\n    "L];
      PutChar[digit[InstLength[i]]];
      IF i # LAST[opcode] THEN PutChar[',];
      ENDLOOP;
    PutString["];\n"L];
    CloseOutput[]};
    
  -- generate list of opcodes
  
  OpcodeList: PROC [root: STRING] = {
    OPEN OpTableDefs;
    digit: STRING = "0123456789"L;
    OpenOutput[root, ".list"L];
    PutString["-- Mesa Opcodes\n"L];
    PutString["-- Format: name hoctal(decimal)push,pop,length,aligned\n\n"L];
    FOR i: opcode IN opcode DO
      op: STRING ← InstName[i];
      length: [0..3] = InstLength[i];
      IF length = 0 THEN op.length ← 0;
      PutString[op];
      THROUGH (op.length..8] DO PutChar[' ] ENDLOOP;
      PutNumber[i, hoctal3];
      PutChar['(]; PutNumber[i, decimal3]; PutChar[')];
      PutChar[digit[PushStack[i]]]; PutChar[',];
      PutChar[digit[PopStack[i]]]; PutChar[',];
      PutChar[digit[length]]; PutChar[',];
      PutChar[IF InstAligned[i] THEN 'T ELSE 'F];
      PutString[IF i MOD 4 = 3 THEN ";\n"L ELSE ";  "L];
      ENDLOOP;
    CloseOutput[]};
    
  -- source file procedures
  
  source: Stream.Handle;
  sourceAvailable: BOOLEAN;
  
  outcheck: PROC [xfirst: CARDINAL, xlast: CARDINAL] = {
    nextchar: CHARACTER;
    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 PutChar[' ] 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;
      PutChar[nextchar];
      REPEAT eof => NULL;
      ENDLOOP;
    IF nextchar # '\n THEN PutChar['\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]};
    
  PrintBodyName: PROC [bti: Symbols.BTIndex] = {
    OPEN symbols;
    sei: Symbols.ISEIndex;
    hti: Symbols.HTIndex;
    ss: Strings.SubStringDescriptor;
    IF sourceAvailable THEN RETURN;
    WITH bb[bti] SELECT FROM
      Callable =>
	IF (sei ← id) = Symbols.SENull OR (hti ← seb[sei].hash) = Symbols.HTNull THEN RETURN;
      ENDCASE => RETURN;
    SubStringForName[@ss, hti];
    PutLongSubString[@ss];
    PutString[":\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, octal: BOOLEAN, stripped: BOOLEAN] = {
    Pbytes ← Pbytes + tablength*2;
    FOR pc: CARDINAL IN [tabstart..tabstart + tablength) DO
      w: INTEGER = getword[pc];
      PutString["\n\t\t"L];
      IF stripped THEN {PutNumber[w, hoctal5]; LOOP};
      IF octal THEN PutString["\t\t"L];
      PutString[" ("L];
      PutNumber[jumpaddress[Mopcodes.zJIW, w], hoctal5];
      PutChar[')];
      ENDLOOP};
    
  outbjtab: PROC [
      tabstart, tablength: CARDINAL, octal: BOOLEAN, stripped: BOOLEAN] = {
    Pbytes ← Pbytes + EvenUp[tablength];
    FOR pc: CARDINAL IN [tabstart*2..tabstart*2 + tablength) DO
      b: BYTE = getbyte[pc];
      PutString["\n\t\t"L];
      IF stripped THEN {PutNumber[b, hoctal5]; LOOP};
      IF octal THEN PutString["\t\t"L];
      PutString[" ("L];
      PutNumber[jumpaddress[Mopcodes.zJIB, b], hoctal5];
      PutChar[')];
      ENDLOOP};
    
  PutPair: PROC [byte: CARDINAL] = {
    a: CARDINAL = byte/16;
    b: CARDINAL = byte MOD 16;
    IF a < 8 AND b < 8 THEN PutChar[' ];
    PutChar['[];
    PutNumber[a, hoctal1];
    PutChar[',];
    PutNumber[b, hoctal1];
    PutChar[']]};
    
  printcode: PROC [
     startcode, endcode: CARDINAL, octal: BOOLEAN, stripped: BOOLEAN] = {
    -- 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;
      PutChar['\t];
      IF ~stripped THEN {
	IF octal THEN {
	  PutNumber[offset/2, hoctal5];
	  PutString[(IF offset MOD 2 = 0 THEN ",E "L ELSE ",O "L)]};
	PutNumber[offset, hoctal5];
	PutChar[':]};
      IF octal THEN {
	PutString["\t["L]; PutNumber[inst, hoctal3z]; PutChar[']]};
      PutChar['\t];
      PutString[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 ~stripped THEN {
	    PutString["\t       ("L];
	    PutNumber[jumpaddress[inst, 0], hoctal1];
	    PutChar[')]}};
	2 => {
	  byte: BYTE = getbyte[(offset ← offset + 1)];
	  Pbytes ← Pbytes + 2;
	  PutChar['\t];
	  SELECT inst FROM
	    zRILP, zWILP, zRXLP, zWXLP, zRIGP, zRXLPL, zWXLPL, zRXGPL, zWXGPL,
	      zRILPL, zWILPL, zRIGPL, zWIGPL => PutPair[byte];
	    ENDCASE => PutNumber[byte, hoctal6];
	  IF inst = zLIB THEN lastconstant ← byte
	  ELSE IF inst IN JumpOp AND ~stripped THEN {
	    PutString[" ("L];
	    PutNumber[jumpaddress[inst, byte], hoctal1];
	    PutChar[')]}};
	3 => {
	  ab: RECORD [first, second: BYTE];
	  Pbytes ← Pbytes + 3;
	  ab.first ← getbyte[(offset ← offset + 1)];
	  ab.second ← getbyte[(offset ← offset + 1)];
	  PutChar['\t];
	  SELECT inst FROM
	    zRF, zWF, zWSF, zRFC, zRFL, zWFL => {
	      PutNumber[ab.first, hoctal6];
	      PutString[", "L];
	      PutPair[ab.second]};
	    ENDCASE => {
	      v: INTEGER = ab.first*256 + ab.second;
	      PutNumber[v, hoctal6];
	      SELECT inst FROM
		zJIB => outbjtab[v, lastconstant, octal, stripped];
		zJIW => outwjtab[v, lastconstant, octal, stripped];
		zLIW => lastconstant ← v;
		IN JumpOp =>
		  IF ~stripped THEN {
		    PutString[" ("L];
		    PutNumber[jumpaddress[inst, v], hoctal1];
		    PutChar[')]};
		ENDCASE}};
	ENDCASE;
      PutChar['\n];
      ENDLOOP};
    
  ListFile: PROC [root: STRING, octal, stripped: BOOLEAN] = {
    OPEN symbols;
    cseg, sseg, bcdseg: FileSegment.Pages;
    bcdFile: STRING ← [40];
    Strings.AppendString[bcdFile, root];
    FOR i: CARDINAL IN [0..root.length) DO
      IF root[i] = '. THEN EXIT;
      REPEAT FINISHED => Strings.AppendString[bcdFile, ".bcd"L];
      ENDLOOP;
    [cseg, sseg, bcdseg] ← ListerDefs.Load[bcdFile];
    DoCodeListing[root, cseg, sseg, bcdseg, FIRST[MTIndex], octal, stripped]};
    
  ListModInConfig: PROC [config, module: STRING, octal, stripped: BOOLEAN] = {
    OPEN symbols;
    cseg, sseg, bcdseg: FileSegment.Pages;
    bcdFile: STRING ← [40];
    mti: BcdDefs.MTIndex;
    Strings.AppendString[bcdFile, config];
    FOR i: CARDINAL IN [0..config.length) DO
      IF config[i] = '. THEN EXIT;
      REPEAT FINISHED => Strings.AppendString[bcdFile, ".bcd"L];
      ENDLOOP;
    [cseg, sseg, bcdseg, mti] ← ListerDefs.LoadFromConfig[bcdFile, module];
    DoCodeListing[module, cseg, sseg, bcdseg, mti, octal, stripped]};
    
  ShowTotals: PROC = {
    PutString["Instructions: "L];
    PutNumber[Pinst, decimal];
    PutString[", Bytes: "L];
    PutNumber[Pbytes ← EvenUp[Pbytes], decimal];
    PutString["\n\n"L];
    Tinst ← Tinst + Pinst;
    Pinst ← 0;
    Tbytes ← Tbytes + Pbytes;
    Pbytes ← 0};
    
  DoCodeListing: PROC [
      root: STRING, cseg, sseg, bcdseg: FileSegment.Pages, mti: MTIndex,
      octal, stripped: BOOLEAN] = {
    OPEN BcdDefs, Symbols;
    cspp: LONG POINTER TO PrincOps.CSegPrefix;
    ff: FineGrainInfo;
    bcdSpace, codeSpace: Space.Handle;
    bcd: BcdOps.BcdBase;
    mth: BcdOps.MTHandle;
    prevBti: BTIndex ← BTNull;
    bcdSpace ← ListerDefs.MapPages[bcdseg];
    bcd ← bcdSpace.LongPointer;
    mth ← @LOOPHOLE[bcd + bcd.mtOffset, Base][mti];
    codeSpace ← ListerDefs.MapPages[cseg];
    codebase ← codeSpace.LongPointer + mth.code.offset;
    codepages ← cseg.span.pages;
    cspp ← codebase;
    IF cspp.header.info.altoCode THEN {
      ListerDefs.WriteString["Cannot list Alto code"L]; RETURN};
    SymbolTable.SetCacheSize[0];	-- clear cache
    symbols ← SymbolTable.Acquire[sseg];
    ListerDefs.SetRoutineSymbols[symbols];
    setupsource[];
    OpenOutput[root, ".cl"L];
    ListerDefs.WriteFileID[];
    IF mth.crossJumped THEN PutString["Cross jumped\n"L];
    PutString["Global frame size:  "L];
    PutNumber[mth.framesize, decimal];
    PutString["\n\n"L];
    Space.Delete[bcdSpace];
    Tbytes ← Tinst ← 0;
    DigestFGT[];
    FOR i: CARDINAL IN [0..myFGT.length) DO
      ff ← myFGT[i];
      IF ff.bti # prevBti AND prevBti # BTNull THEN ShowTotals[];
      IF ff.firstSource # nullSource THEN
	IF ff.lastSource = ff.firstSource THEN PutChar['\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];
	PrintBodyName[ff.bti];
	IF octal THEN PutChar['\t];
        PutString["   Entry point: "L];
	PutNumber[ep, decimal];
	PutString[",   Frame size:  "L];
	PutNumber[fsize, decimal];
	PutChar['\n]};
      IF ~ff.procEnd THEN printcode[ff.pc, myFGT[i + 1].pc, octal, stripped];
      PutChar['\n];
      prevBti ← ff.bti;
      ENDLOOP;
    IF prevBti # Symbols.BTNull THEN ShowTotals[];
    (Heap.systemZone).FREE[@myFGT];
    SymbolTable.Release[symbols];
    Space.Delete[codeSpace];
    closesource[];
    PutChar['\n];
    IF octal THEN PutChar['\t];
    PutString["Total instructions: "L];
    PutNumber[Tinst, decimal];
    PutString[", Bytes: "L];
    PutNumber[Tbytes, decimal];
    PutChar['\n];
    CloseOutput[]};
    
  LCode: PROC [name: STRING, octal, stripped: BOOLEAN] = {
    OPEN ListerDefs;
    ListFile[name, octal, stripped
      ! NoCode => {ListerDefs.WriteString["Code not available"L]; CONTINUE};
        NoSymbols => {ListerDefs.WriteString["Symbols not available"L]; CONTINUE};
        NoFGT, IncorrectVersion => {ListerDefs.WriteString["Bad format"L]; CONTINUE};
        NoFile => {ListerDefs.WriteString["File not found"L]; CONTINUE}]};
    
  Code: PROC [name: STRING] = {LCode[name, FALSE, FALSE]};
    
  OctalCode: PROC [name: STRING] = {LCode[name, TRUE, FALSE]};
    
  StrippedCode: PROC [name: STRING] = {LCode[name, FALSE, TRUE]};
    
  LCodeInConfig: PROC [config, name: STRING, octal, stripped: BOOLEAN] = {
    OPEN ListerDefs;
    ListModInConfig[config, name, octal, stripped
      ! NoCode => {ListerDefs.WriteString["Code not available"L]; CONTINUE};
        NoSymbols => {ListerDefs.WriteString["Symbols not available"L]; CONTINUE};
        NoFGT, IncorrectVersion => {ListerDefs.WriteString["Bad format"L]; CONTINUE};
        NoFile => {ListerDefs.WriteString["File not found"L]; CONTINUE}]};
    
  CodeInConfig: PROC [config, name: STRING] = {
    LCodeInConfig[config, name, FALSE, FALSE]};
    
  OctalCodeInConfig: PROC [config, name: STRING] = {
    LCodeInConfig[config, name, TRUE, FALSE]};
    
  StrippedCodeInConfig: PROC [config, name: STRING] = {
    LCodeInConfig[config, name, FALSE, TRUE]};
    
  Init: PROC = {
    command: CommanderOps.CommandBlockHandle;
    Octify[];
    command ← CommanderOps.AddCommand["Hexify", LOOPHOLE[Hexify], 0];
    command ← CommanderOps.AddCommand["Octify", LOOPHOLE[Octify], 0];
    command ←
      CommanderOps.AddCommand["OpcodeLengths", LOOPHOLE[OpcodeLengths], 1];
    command.params[0] ← [type: string, prompt: "Filename"];
    command ← CommanderOps.AddCommand["OpcodeList", LOOPHOLE[OpcodeList], 1];
    command.params[0] ← [type: string, prompt: "Filename"];
    command ← CommanderOps.AddCommand["OctalCode", LOOPHOLE[OctalCode], 1];
    command.params[0] ← [type: string, prompt: "Filename"];
    command ← CommanderOps.AddCommand["Code", LOOPHOLE[Code], 1];
    command.params[0] ← [type: string, prompt: "Filename"];
    command ← CommanderOps.AddCommand["StrippedCode", LOOPHOLE[StrippedCode], 1];
    command.params[0] ← [type: string, prompt: "Filename"];
    command ← CommanderOps.AddCommand[
      "OctalCodeInConfig", LOOPHOLE[OctalCodeInConfig], 2];
    command.params[0] ← [type: string, prompt: "ConfigName"];
    command.params[1] ← [type: string, prompt: "ModName"];
    command ← CommanderOps.AddCommand["CodeInConfig", LOOPHOLE[CodeInConfig], 2];
    command.params[0] ← [type: string, prompt: "ConfigName"];
    command.params[1] ← [type: string, prompt: "ModName"];
    command ← CommanderOps.AddCommand[
      "StrippedCodeInConfig", LOOPHOLE[StrippedCodeInConfig], 2];
    command.params[0] ← [type: string, prompt: "ConfigName"];
    command.params[1] ← [type: string, prompt: "ModName"]};
    
  Init[];
  }.