-- 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[];
}.