-- File: PackCodeImpl.mesa
-- Last edited by Sweet on 26-Feb-81 16:44:02
-- Last edited by Lewis on 15-May-81 18:08:49
-- Last edited by Levin on September 8, 1982 4:33 pm
DIRECTORY
Alloc USING [AddNotify, Bounds, DropNotify, Handle, Notifier, Top, Words],
Ascii,
BcdDefs,
BcdOps USING [
CTHandle, EXPHandle, IMPHandle, MTHandle, NTHandle, ProcessConfigs,
ProcessExports, ProcessImports, ProcessModules, ProcessNames],
BcdUtilDefs,
CharIO,
CodePackProcs USING [
EnumerateCodePacks, EnumerateModules, EnumerateProcs, EnumerateSegments,
IsDiscardCodePack, ModuleIndex, SubStringForCodePackNode,
SubStringForSegmentNode, TreeIndex],
Error USING [ErrorName, SegmentTooLarge],
FileTable USING [HandleForFile],
FramePackModules USING [
EnumerateFramePacks, EnumerateModules, SubStringForFramePackNode],
Inline USING [LongCOPY, LongDiv, LongDivMod, LongMult, LowHalf],
LongStorage USING [Free, FreePages, Node, Pages, PagesForWords],
ModuleSymbols USING [constArray, innerPackArray, outerPackArray],
Mopcodes USING [op, zJIB, zJIW, zLCO, zLI0, zLI6, zLIB, zLIW],
OpTableDefs USING [InstLength],
PackagerDefs USING [packctreetype, globalData, PackagerDataRecord],
PackageSymbols USING [
ConstRecord, IPIndex, IPNull, MaxEntries, OPIndex, WordIndex],
PackCode USING [
Address, FinalizeBcdTab, InitBcdTab, NullWordIndex, Problem, SeenModuleRecord, WordIndex],
PackEnviron USING [
BcdStringHandle, Byte, BytesPerPage, Copy, PageSize, SetBlock, StreamPosition],
PackHeap USING [FreeSpace, GetSpace],
PieceTable USING [
Append, AppendPage, AppendQuadWord, AppendWord, CopyFromFile, Delete,
GetByte, GetPlace, GetVPos, GetWord, Initialize, Length, Place,
Position, PutWord, PutZeros, SetVPos, Store],
PrincOps USING [CSegPrefix, EntryVectorItem],
Segments USING [
FHandle, GetFileTimes, LockFile, NewFile, UnlockFile, Write],
SourceBcd USING [
bcdBases, bcdHeader, CTreeIndex, LookupSS, moduleCount, ModuleNum,
ModuleNumForMti, NullCTreeIndex],
Streams USING [
CreateStream, Destroy, GetIndex, Handle, PutByte,
PutBlock, Write],
Strings,
SymbolOps,
Symbols,
Table USING [Base, Limit, OrderedIndex],
Time USING [Append, Current, Packed, Unpack];
PackCodeImpl: PROGRAM
IMPORTS
Alloc, BcdOps, BcdUtilDefs, CharIO, CodePackProcs, Error, FileTable,
FramePackModules, Inline, LongStorage, ModuleSymbols, OpTableDefs,
PackagerDefs, PackCode, PackEnviron, PackHeap, PieceTable, Segments, SourceBcd,
Streams, Strings, SymbolOps, Time
EXPORTS PackCode =
BEGIN
gd: LONG POINTER TO PackagerDefs.PackagerDataRecord; -- PackagerDefs.globalData
spb, sgb, fpb, ctreeb: Table.Base;
itb, etb, ctb, mtb, ntb: Table.Base;
ssb: PackEnviron.BcdStringHandle;
table: Alloc.Handle ← NIL;
Notify: Alloc.Notifier =
BEGIN
ctreeb ← base[PackagerDefs.packctreetype];
sgb ← base[BcdDefs.sgtype];
spb ← base[BcdDefs.sptype];
fpb ← base[BcdDefs.fptype];
ssb ← base[BcdDefs.sstype];
itb ← base[BcdDefs.imptype];
etb ← base[BcdDefs.exptype];
ctb ← base[BcdDefs.cttype];
mtb ← base[BcdDefs.mttype];
ntb ← base[BcdDefs.nttype];
END;
EntryIndex: TYPE = [0..PackageSymbols.MaxEntries);
PackError: PUBLIC SIGNAL [reason: PackCode.Problem] = CODE;
cstb: LONG DESCRIPTOR FOR ARRAY OF PackageSymbols.ConstRecord;
seenModules: LONG DESCRIPTOR FOR ARRAY OF PackCode.SeenModuleRecord;
newConstants: PUBLIC LONG POINTER TO ARRAY [0..0) OF PackCode.WordIndex;
oldCodeFile: PUBLIC Segments.FHandle;
oldCodeBasePosition: PackEnviron.StreamPosition;
currentModule: BcdDefs.MTIndex;
firstCodeSgi: BcdDefs.SGIndex;
currentCodeSegment: PUBLIC BcdDefs.SGIndex;
currentSpaceIndex: PUBLIC BcdDefs.SPIndex;
segmentPosition: PUBLIC PieceTable.Position;
codePackPosition: PUBLIC PieceTable.Position;
codeBasePosition: PUBLIC PieceTable.Position;
codeBaseOffset: PUBLIC PackCode.Address; -- from start of code segment
procOffset: PUBLIC PackCode.Address; -- from codeBase
procPosition: PUBLIC PieceTable.Position;
lastProcEnd: PieceTable.Position;
firstCodePack, currentCodePackResident: BOOLEAN;
outStream: Streams.Handle;
WriteChar: PROC [c: CHARACTER] =
{IF gd.mapStream # NIL THEN CharIO.PutChar[gd.mapStream, c]};
WriteString: PROC [s: Strings.String] =
{IF gd.mapStream # NIL THEN CharIO.PutString[gd.mapStream, s]};
WriteSubString: PROC [ss: Strings.SubString] =
BEGIN
FOR i: CARDINAL IN [ss.offset..ss.offset+ss.length) DO
WriteChar[ss.base[i]];
ENDLOOP;
END;
WriteDecimal: PROC [n: CARDINAL] =
{IF gd.mapStream # NIL THEN CharIO.PutDecimal[gd.mapStream, n]};
WriteNumber: PROC [n: CARDINAL, fmt: CharIO.NumberFormat] =
{IF gd.mapStream # NIL THEN CharIO.PutNumber[gd.mapStream, n, fmt]};
WriteCR: PROC = INLINE {WriteChar[Ascii.CR]};
WriteTime: PROC [t: Time.Packed] =
BEGIN
s: STRING ← [20];
Time.Append[s, Time.Unpack[t]];
WriteString[s];
END;
Initialize: PROCEDURE [nModules: CARDINAL] =
BEGIN
gd ← PackagerDefs.globalData;
table ← gd.ownTable;
table.AddNotify[Notify];
PackCode.InitBcdTab[];
seenModules ← DESCRIPTOR [
LongStorage.Pages[
LongStorage.PagesForWords[nModules * SIZE[PackCode.SeenModuleRecord]]],
nModules];
FOR i: CARDINAL IN [0..nModules) DO seenModules[i] ← [] ENDLOOP;
nMods ← nModules;
END;
nMods: CARDINAL;
Finalize: PUBLIC PROCEDURE =
BEGIN
IF table ~= NIL THEN table.DropNotify[Notify];
FOR i: CARDINAL IN [0..nMods) DO
IF seenModules[i].newConstants # NIL THEN PackHeap.FreeSpace[seenModules[i].newConstants];
ENDLOOP;
PackCode.FinalizeBcdTab[];
LongStorage.FreePages[BASE[seenModules]];
seenModules ← DESCRIPTOR [NIL, 0];
table ← NIL;
END;
GetNewConstants: PROCEDURE [
mNum: SourceBcd.ModuleNum] RETURNS [new: BOOLEAN] =
BEGIN
new ← ~seenModules[mNum].seen;
cstb ← ModuleSymbols.constArray;
IF LENGTH[cstb] # 0 AND new THEN
BEGIN
seenModules[mNum].newConstants ← PackHeap.GetSpace[LENGTH[cstb]*SIZE[PackCode.WordIndex]];
PackEnviron.SetBlock[
p: seenModules[mNum].newConstants,
v: PackCode.NullWordIndex,
n: (LENGTH[cstb]) * SIZE[PackCode.WordIndex]];
END;
newConstants ← seenModules[mNum].newConstants;
RETURN
END;
NewOffset: PUBLIC PROCEDURE [old: PackCode.WordIndex]
RETURNS [PackCode.WordIndex] =
BEGIN -- address in new segment of multiword constant a "old" in old
l, u, i: INTEGER;
delta: CARDINAL;
l ← 0; u ← LENGTH[cstb];
UNTIL l > u DO
i ← (l+u)/2;
SELECT cstb[i].offset FROM
< old => l ← i+1;
> old => u ← i-1;
ENDCASE => EXIT;
REPEAT
FINISHED => i ← u;
ENDLOOP;
IF i < 0 THEN PackError[InvalidCodeOffset];
delta ← old - cstb[i].offset;
IF delta > cstb[i].length THEN PackError[InvalidCodeOffset];
IF newConstants[i] = PackCode.NullWordIndex THEN
BEGIN
savePos: PieceTable.Position = PieceTable.GetVPos[];
newConstants[i] ← CodeOffset[PieceTable.AppendWord[]];
PieceTable.CopyFromFile[
file: oldCodeFile,
position: oldCodeBasePosition + cstb[i].offset*2,
length: cstb[i].length*2];
PieceTable.SetVPos[savePos];
END;
RETURN[newConstants[i] + delta];
END;
CopyBodies: PUBLIC PROCEDURE [root: PackageSymbols.OPIndex]
RETURNS [stop: BOOLEAN] =
BEGIN -- copy procedure (and any nested below unless main body)
i: PackageSymbols.IPIndex;
IF gd.printMap THEN DisplayNumbers[
ep: ModuleSymbols.outerPackArray[root].entryIndex,
length: (ModuleSymbols.outerPackArray[root].length+1)/2,
hti: ModuleSymbols.outerPackArray[root].hti];
CopyOneBody[
ModuleSymbols.outerPackArray[root].entryIndex,
ModuleSymbols.outerPackArray[root].length];
i ← ModuleSymbols.outerPackArray[root].firstSon;
IF i # PackageSymbols.IPNull THEN
DO
IF gd.printMap THEN DisplayNumbers[
ep: ModuleSymbols.innerPackArray[i].entryIndex,
length: (ModuleSymbols.innerPackArray[i].length+1)/2,
hti: Symbols.HTNull];
CopyOneBody[
ModuleSymbols.innerPackArray[i].entryIndex,
ModuleSymbols.innerPackArray[i].length];
IF ModuleSymbols.innerPackArray[i].lastSon THEN EXIT;
i ← i+1;
ENDLOOP;
RETURN[FALSE];
END;
FullWordBytes: PROCEDURE [n: CARDINAL] RETURNS [CARDINAL] = INLINE
BEGIN
RETURN [n MOD 2 + n];
END;
SegmentOffset: PROCEDURE [pos: PieceTable.Position]
RETURNS [PackCode.WordIndex] =
BEGIN
new: LONG CARDINAL = pos - segmentPosition;
IF new > LAST[CARDINAL] THEN PackError[SegmentTooBig];
RETURN [Inline.LowHalf[new]/2];
END;
CodeOffset: PROCEDURE [pos: PieceTable.Position]
RETURNS [PackCode.WordIndex] =
BEGIN
new: LONG CARDINAL = pos - codeBasePosition;
IF new > LAST[CARDINAL] THEN PackError[SegmentTooBig];
RETURN [Inline.LowHalf[new]/2];
END;
CopyOneBody: PROCEDURE [ep: EntryIndex, length: CARDINAL] =
BEGIN
eviOffset: POINTER;
oldProcOffset: PackCode.WordIndex;
codeLength: CARDINAL ← length;
vicinity: PieceTable.Place;
-- copy code into output file
procPosition ← PieceTable.AppendWord[];
procOffset ← CodeOffset[procPosition];
vicinity ← PieceTable.GetPlace[];
-- for main body, word -1 is global frame size, used by CopyNew
IF ep = 0 THEN procOffset ← procOffset + 1;
-- fix up entry vector for module
eviOffset ←
@(LOOPHOLE[1, POINTER TO PrincOps.CSegPrefix].entry[ep].initialpc) - 1;
PieceTable.SetVPos[
codeBasePosition + 2*LOOPHOLE[eviOffset, CARDINAL], @evPlace];
oldProcOffset ← PieceTable.GetWord[];
PieceTable.Delete[-2];
PieceTable.PutWord[procOffset];
PieceTable.SetVPos[procPosition, @vicinity];
IF ep = 0 THEN {oldProcOffset ← oldProcOffset-1; length ← length + 2};
PieceTable.CopyFromFile[
file: oldCodeFile,
position: oldProcOffset*2 + oldCodeBasePosition,
length: FullWordBytes[length]];
IF ep = 0 THEN procPosition ← procPosition + 2;
-- now get ready to look for multiword constants
PieceTable.SetVPos[procPosition];
BEGIN OPEN Mopcodes; -- constant search
op: PackEnviron.Byte;
lastConstant: CARDINAL ← 0; -- negative constants need not apply
il: CARDINAL;
WHILE codeLength > 0 DO
op ← PieceTable.GetByte[];
il ← OpTableDefs.InstLength[op];
BEGIN -- to set up vanilla label
SELECT op FROM
IN [zLI0..zLI6] => {lastConstant ← op - zLI0; GO TO vanilla};
zLIB => lastConstant ← PieceTable.GetByte[];
zLIW => lastConstant ← PieceTable.GetWord[];
-- zLINB, zLINI, zLIN1 not interesting for JIB/JIW case
zJIB, zJIW =>
BEGIN
newTableOffset: PackCode.WordIndex;
oldTableOffset: PackCode.WordIndex = PieceTable.GetWord[];
savePos: PieceTable.Position = PieceTable.GetVPos[];
IF lastConstant = 0 THEN PackError[StrangeJI];
IF op = zJIB THEN lastConstant ← (lastConstant+1)/2;
-- copy table to output file
newTableOffset ← CodeOffset[PieceTable.AppendWord[]];
PieceTable.CopyFromFile[
file: oldCodeFile,
position: oldCodeBasePosition+oldTableOffset*2,
length: lastConstant*2];
PieceTable.SetVPos[savePos];
PieceTable.Delete[-2];
PieceTable.PutWord[newTableOffset];
lastConstant ← 0;
END;
zLCO =>
BEGIN
old: CARDINAL = PieceTable.GetWord[];
new: CARDINAL = NewOffset[old];
PieceTable.Delete[-2];
PieceTable.PutWord[new];
END;
ENDCASE => GO TO vanilla;
EXITS vanilla => THROUGH (1..il] DO [] ← PieceTable.GetByte[]; ENDLOOP;
END;
codeLength ← codeLength - il;
ENDLOOP;
END; -- of constant search
END;
CreateNewSegment: PROC [segNode: CodePackProcs.TreeIndex] RETURNS [BOOLEAN] =
BEGIN
endPosition: PieceTable.Position;
base, pages: CARDINAL;
desc: Strings.SubStringDescriptor;
CodePackProcs.SubStringForSegmentNode[@desc, segNode];
IF gd.printMap THEN
BEGIN
WriteChar[Ascii.CR];
WriteString["Segment: "L]; WriteSubString[@desc];
WriteChar[Ascii.CR];
WriteChar[Ascii.CR];
END;
currentCodeSegment ←
table.Words[BcdDefs.sgtype, SIZE[BcdDefs.SGRecord]];
currentSpaceIndex ←
table.Words[BcdDefs.sptype, SIZE[BcdDefs.SPRecord]];
spb[currentSpaceIndex] ← [seg: currentCodeSegment, length: 0, spaces: NULL];
segmentPosition ← PieceTable.AppendPage[];
firstCodePack ← TRUE;
CodePackProcs.EnumerateCodePacks[segNode, CreateCodePack
! PackError =>
IF reason = SegmentTooBig THEN Error.SegmentTooLarge[error, @desc]];
IF ~firstCodePack THEN FinishCodePack[];
endPosition ← PieceTable.Length[];
base ← Inline.LongDiv[segmentPosition, PackEnviron.BytesPerPage];
pages ← LongStorage.PagesForWords[
(CARDINAL[Inline.LowHalf[endPosition - segmentPosition]]+1)/2];
sgb[currentCodeSegment] ← [
class: code,
file: BcdDefs.FTSelf,
base: base,
pages: pages,
extraPages: 0];
RETURN[FALSE]
END;
CreateFramePack: PROC [fpNode: CodePackProcs.TreeIndex] RETURNS [BOOLEAN] =
BEGIN
fpi: BcdDefs.FPIndex = table.Words[
BcdDefs.fptype, SIZE[BcdDefs.FPRecord]];
desc: Strings.SubStringDescriptor;
nameCopy: STRING ← [80];
name: BcdDefs.NameRecord;
totalFrameWords, inLastPage: CARDINAL ← 0;
AddModToPack: PROC [mti: BcdDefs.MTIndex] RETURNS [BOOLEAN] =
BEGIN
n: CARDINAL = fpb[fpi].length;
offset: CARDINAL ← 0;
[] ← table.Words[BcdDefs.fptype, SIZE[BcdDefs.MTIndex]];
fpb[fpi].modules[n] ← mti;
fpb[fpi].length ← n+1;
IF gd.printMap THEN
BEGIN
mth: BcdOps.MTHandle = @mtb[mti];
offset ← ((totalFrameWords+3)/4)*4;
WriteNumber[mth.framesize, Decimal6];
WriteNumber[offset, Octal7]; WriteChar['B];
totalFrameWords ← totalFrameWords + mth.framesize;
WriteString[" "];
[] ← WriteName[mth.name];
WriteChar[Ascii.CR];
END;
RETURN[FALSE]
END;
FramePackModules.SubStringForFramePackNode[@desc, fpNode];
Strings.AppendSubString[nameCopy, @desc];
desc ← [base: nameCopy, offset: 0, length: nameCopy.length];
fpb[fpi].name ← name ← BcdUtilDefs.EnterName[@desc];
IF gd.printMap THEN
BEGIN
WriteChar[Ascii.CR];
WriteString["Frame Pack: "L];
[] ← WriteName[name];
WriteChar[Ascii.CR];
WriteString["length offset Module"L];
WriteChar[Ascii.CR];
END;
fpb[fpi].length ← 0;
FramePackModules.EnumerateModules[fpNode, AddModToPack];
IF gd.printMap THEN
BEGIN
inLastPage ← totalFrameWords MOD PackEnviron.PageSize;
IF inLastPage # 0 THEN
BEGIN
WriteNumber[PackEnviron.PageSize - inLastPage, Decimal6];
WriteString[" unused"L];
WriteChar[Ascii.CR];
END;
WriteString["Frame pack pages: "L];
WriteDecimal[LongStorage.PagesForWords[totalFrameWords]];
WriteChar[Ascii.CR]; WriteChar[Ascii.CR];
END;
RETURN[FALSE]
END;
StartModule: PUBLIC PROCEDURE [mti: BcdDefs.MTIndex] =
BEGIN
mNum: SourceBcd.ModuleNum = SourceBcd.ModuleNumForMti[mti];
IF GetNewConstants[mNum].new THEN
BEGIN
mth: BcdOps.MTHandle = @mtb[mti];
cd: BcdDefs.CodeDesc ← mth.code;
file: BcdDefs.FTIndex = mth.file;
name: BcdDefs.NameRecord = mth.name;
sgr: BcdDefs.SGRecord = SourceBcd.bcdBases.sgb[cd.sgi];
nEntries: CARDINAL = LENGTH[ModuleSymbols.outerPackArray] +
LENGTH[ModuleSymbols.innerPackArray];
evWords: CARDINAL = SIZE[PrincOps.CSegPrefix] +
nEntries * SIZE[PrincOps.EntryVectorItem];
oldCodeFile ← FileTable.HandleForFile[sgr.file];
oldCodeBasePosition ← 2 *
(Inline.LongMult[sgr.base-1, PackEnviron.PageSize] + LONG[cd.offset]);
IF mth.linkLoc = code THEN
BEGIN
pos: LONG CARDINAL ← PieceTable.AppendWord[];
fLength: CARDINAL = NLinks[mth];
delta: CARDINAL ← (CARDINAL[Inline.LowHalf[pos]] + fLength) MOD 4;
IF delta # 0 THEN delta ← 4 - delta;
PieceTable.PutZeros[(fLength + delta)*2];
cd.linkspace ← TRUE;
END;
codeBasePosition ← PieceTable.AppendQuadWord[];
codeBaseOffset ← SegmentOffset[codeBasePosition];
IF gd.printMap THEN
DisplayNumbers[ep: -1, length: evWords, hti: Symbols.HTNull];
PieceTable.CopyFromFile[
file: oldCodeFile,
position: oldCodeBasePosition,
length: evWords*2];
-- update seenModules array entry
evPlace ← PieceTable.GetPlace[];
seenModules[mNum] ← [
seen: TRUE,
newOffset: codeBaseOffset,
newPiece: evPlace.pi,
oldCodeFile: oldCodeFile,
oldCodePosition: oldCodeBasePosition,
newConstants: newConstants];
-- update module table in bcd
cd.offset ← codeBaseOffset;
cd.sgi ← currentCodeSegment;
cd.length ← 0;
BEGIN -- look for all prototypes of this name
desc: Strings.SubStringDescriptor ← [
base: @ssb.string,
offset: name,
length: ssb.size[name]];
cTreeNode: SourceBcd.CTreeIndex ← SourceBcd.LookupSS[@desc, prototype];
WHILE cTreeNode # SourceBcd.NullCTreeIndex DO
WITH ctr: ctreeb[cTreeNode].index SELECT FROM
module =>
BEGIN
pmth: BcdOps.MTHandle = @mtb[ctr.mti];
IF pmth.file = file THEN pmth.code ← cd;
END;
ENDCASE;
cTreeNode ← ctreeb[cTreeNode].prototypePrev;
ENDLOOP;
END;
END
ELSE
BEGIN
[newOffset: codeBaseOffset,
newPiece: evPlace.pi,
oldCodeFile: oldCodeFile,
oldCodePosition: oldCodeBasePosition] ← seenModules[mNum];
codeBasePosition ← segmentPosition + 2*codeBaseOffset;
evPlace.pos ← codeBasePosition;
END;
END;
NLinks: PROCEDURE [mth: BcdOps.MTHandle] RETURNS [nLinks: [0..Table.Limit)] =
BEGIN
WITH mth: mth SELECT FROM
direct => RETURN[mth.length];
indirect => RETURN[SourceBcd.bcdBases.lfb[mth.links].length];
multiple => RETURN[SourceBcd.bcdBases.lfb[mth.links].length];
ENDCASE;
END;
evPlace: PieceTable.Place;
CopyFakeModule: PROCEDURE [mti: BcdDefs.MTIndex] =
BEGIN
mth: BcdOps.MTHandle = @mtb[mti];
cd: BcdDefs.CodeDesc ← mth.code;
file: BcdDefs.FTIndex = mth.file;
name: BcdDefs.NameRecord = mth.name;
sgr: BcdDefs.SGRecord = SourceBcd.bcdBases.sgb[cd.sgi];
oldCodeFile ← FileTable.HandleForFile[sgr.file];
oldCodeBasePosition ← 2 *
(Inline.LongMult[sgr.base-1, PackEnviron.PageSize] + LONG[cd.offset]);
codeBasePosition ← PieceTable.AppendQuadWord[];
codeBaseOffset ← SegmentOffset[codeBasePosition];
IF gd.printMap THEN
DisplayNumbers[ep: -1, length: (cd.length+1)/2, hti: Symbols.HTNull];
PieceTable.CopyFromFile[
file: oldCodeFile,
position: oldCodeBasePosition,
length: cd.length];
-- update module table in bcd
cd.offset ← codeBaseOffset;
cd.sgi ← currentCodeSegment;
cd.length ← 0;
BEGIN -- look for all prototypes of this name
desc: Strings.SubStringDescriptor ← [
base: @ssb.string,
offset: name,
length: ssb.size[name]];
cTreeNode: SourceBcd.CTreeIndex ← SourceBcd.LookupSS[@desc, prototype];
WHILE cTreeNode # SourceBcd.NullCTreeIndex DO
WITH ctr: ctreeb[cTreeNode].index SELECT FROM
module =>
BEGIN
pmth: BcdOps.MTHandle = @mtb[ctr.mti];
IF pmth.file = file THEN pmth.code ← cd;
END;
ENDCASE;
cTreeNode ← ctreeb[cTreeNode].prototypePrev;
ENDLOOP;
END;
END;
DiscardAllInPack: PROC [cpNode: CodePackProcs.TreeIndex] =
BEGIN
needEntryVector: BOOLEAN ← FALSE;
offset, pages: CARDINAL;
spii: Table.Base RELATIVE POINTER [0..Table.Limit) TO BcdDefs.SpaceID;
name: BcdDefs.NameRecord;
nameCopy: STRING ← [80];
desc: Strings.SubStringDescriptor;
endPosition: PieceTable.Position;
CheckModule: PROC [
mti: BcdDefs.MTIndex, module: CodePackProcs.ModuleIndex]
RETURNS [BOOLEAN] =
BEGIN
mNum: SourceBcd.ModuleNum = SourceBcd.ModuleNumForMti[mti];
RETURN[needEntryVector ← ~seenModules[mNum].seen];
END;
CodePackProcs.EnumerateModules[cpNode, CheckModule];
IF needEntryVector THEN
BEGIN
CodePackProcs.SubStringForCodePackNode[@desc, cpNode];
Strings.AppendSubString[nameCopy, @desc];
desc ← [base: nameCopy, offset: 0, length: nameCopy.length];
name ← BcdUtilDefs.EnterName[@desc];
codePackPosition ← PieceTable.AppendPage[];
END;
CodePackProcs.EnumerateModules[cpNode, DiscardThisModule];
IF needEntryVector THEN
BEGIN
endPosition ← PieceTable.Length[];
offset ← Inline.LongDiv[
codePackPosition - segmentPosition, PackEnviron.BytesPerPage];
pages ← LongStorage.PagesForWords[
(CARDINAL[Inline.LowHalf[endPosition - codePackPosition]]+1)/2];
spii ← table.Words[BcdDefs.sptype, SIZE[BcdDefs.SpaceID]];
spb[spii] ← [name: name, resident: FALSE, offset: offset, pages: pages];
spb[currentSpaceIndex].length ← spb[currentSpaceIndex].length + 1;
END;
END;
DiscardThisModule: PROCEDURE [
mti: BcdDefs.MTIndex, module: CodePackProcs.ModuleIndex]
RETURNS [BOOLEAN] =
BEGIN
StartModule[mti];
CodePackProcs.EnumerateProcs[module, DiscardThisProc];
newConstants ← NIL;
RETURN[FALSE]
END;
DiscardThisProc: PUBLIC PROCEDURE [root: PackageSymbols.OPIndex]
RETURNS [stop: BOOLEAN] =
BEGIN -- copy procedure (and any nested below unless main body)
i: PackageSymbols.IPIndex;
DiscardOneBody[ModuleSymbols.outerPackArray[root].entryIndex];
i ← ModuleSymbols.outerPackArray[root].firstSon;
IF i # PackageSymbols.IPNull THEN
DO
DiscardOneBody[ModuleSymbols.innerPackArray[i].entryIndex];
IF ModuleSymbols.innerPackArray[i].lastSon THEN EXIT;
i ← i+1;
ENDLOOP;
RETURN[FALSE];
END;
DiscardOneBody: PROCEDURE [ep: EntryIndex] =
BEGIN
eviOffset: POINTER;
-- fix up entry vector for module
eviOffset ←
@(LOOPHOLE[1, POINTER TO PrincOps.CSegPrefix].entry[ep].initialpc) - 1;
PieceTable.SetVPos[codeBasePosition + 2*LOOPHOLE[eviOffset, CARDINAL]];
PieceTable.Delete[2];
PieceTable.PutWord[0];
END;
CreateCodePack: PROCEDURE [cpNode: CodePackProcs.TreeIndex]
RETURNS [BOOLEAN] =
BEGIN
offset, pages: CARDINAL;
spii: Table.Base RELATIVE POINTER [0..Table.Limit) TO BcdDefs.SpaceID;
name: BcdDefs.NameRecord;
nameCopy: STRING ← [80];
desc: Strings.SubStringDescriptor;
endPosition: PieceTable.Position;
discard: BOOLEAN = CodePackProcs.IsDiscardCodePack[cpNode];
CodePackProcs.SubStringForCodePackNode[@desc, cpNode];
Strings.AppendSubString[nameCopy, @desc];
desc ← [base: nameCopy, offset: 0, length: nameCopy.length];
name ← BcdUtilDefs.EnterName[@desc];
IF gd.printMap THEN
BEGIN
IF firstCodePack THEN firstCodePack ← FALSE
ELSE FinishCodePack[];
WriteString["Code Pack: "L];
[] ← WriteName[name];
WriteChar[Ascii.CR];
PrintHeader[];
END;
IF discard THEN {DiscardAllInPack[cpNode]; RETURN [FALSE]};
currentCodePackResident ← FALSE; -- set TRUE if any modules resident
lastProcEnd ← codePackPosition ← PieceTable.AppendPage[];
CodePackProcs.EnumerateModules[cpNode, CopyModuleToPack];
endPosition ← PieceTable.Length[];
offset ← Inline.LongDiv[
codePackPosition - segmentPosition, PackEnviron.BytesPerPage];
pages ← LongStorage.PagesForWords[
(CARDINAL[Inline.LowHalf[endPosition - codePackPosition]]+1)/2];
spii ← table.Words[BcdDefs.sptype, SIZE[BcdDefs.SpaceID]];
spb[spii] ← [
name: name, resident: currentCodePackResident,
offset: offset, pages: pages];
spb[currentSpaceIndex].length ← spb[currentSpaceIndex].length + 1;
RETURN[FALSE]
END;
FinishCodePack: PROC =
BEGIN
endPosition: PieceTable.Position = PieceTable.AppendWord[];
totalBytes: CARDINAL ← Inline.LowHalf[endPosition - codePackPosition];
gap: CARDINAL;
delta: CARDINAL = CARDINAL[Inline.LowHalf[endPosition]] MOD PackEnviron.BytesPerPage;
IF lastProcEnd # 0 AND endPosition > lastProcEnd THEN {
IF gd.printMap THEN NoteData[
offset: SegmentOffset[lastProcEnd],
length: (Inline.LowHalf[endPosition-lastProcEnd])/2]};
IF delta # 0 AND gd.printMap THEN {
gap ← (PackEnviron.BytesPerPage - delta)/2;
WriteNumber[gap, Decimal5];
WriteString[" unused"L];
WriteChar[Ascii.CR]};
IF gd.printMap THEN {
WriteString["Code pack pages: "L];
WriteDecimal[
(totalBytes + PackEnviron.BytesPerPage - 1) / PackEnviron.BytesPerPage];
WriteChar[Ascii.CR]; WriteChar[Ascii.CR]};
firstCodePack ← FALSE;
END;
CopyModuleToPack: PROCEDURE [
mti: BcdDefs.MTIndex, module: CodePackProcs.ModuleIndex]
RETURNS [BOOLEAN] =
BEGIN
currentModule ← mti;
IF mtb[mti].tableCompiled THEN
{CopyFakeModule[mti]; RETURN[FALSE]};
IF mtb[mti].residentFrame THEN
currentCodePackResident ← TRUE;
StartModule[mti];
CodePackProcs.EnumerateProcs[module, CopyBodies];
newConstants ← NIL;
RETURN[FALSE]
END;
ComputeCodePlacement: PUBLIC PROC =
BEGIN
Initialize[SourceBcd.moduleCount];
RemapOldBcd[];
IF gd.errors THEN RETURN;
firstCodeSgi ← LOOPHOLE[table.Top[BcdDefs.sgtype]];
PieceTable.Initialize[];
firstCodePack ← TRUE;
lastProcEnd ← 0;
IF gd.printMap THEN {
WriteCR[];
WriteString["File "L]; WriteString[gd.mapFileName];
WriteString[" created by Packager from "L]; WriteString[gd.packName];
WriteString[" on "L]; WriteTime[Time.Current[]]; WriteCR[]};
CodePackProcs.EnumerateSegments[CreateNewSegment];
FramePackModules.EnumerateFramePacks[CreateFramePack];
IF gd.printMap THEN
{Streams.Destroy[gd.mapStream]; gd.mapStream ← NIL};
END;
WriteBcdToFile: PUBLIC PROC =
BEGIN
limitSgi: BcdDefs.SGIndex;
bcdPages, bcdPos, size: CARDINAL;
desc: Strings.SubStringDescriptor;
newHeader: LONG POINTER TO BcdDefs.BCD;
FillToPageBoundary: PROCEDURE =
BEGIN
byte: CARDINAL ← Inline.LongDivMod[
num: Streams.GetIndex[outStream], den: PackEnviron.BytesPerPage].remainder;
IF byte # 0 THEN
THROUGH (byte..PackEnviron.BytesPerPage] DO
Streams.PutByte[outStream, 0];
ENDLOOP;
END;
-- open output stream as a byte stream
IF gd.errors THEN RETURN;
IF PackagerDefs.globalData.outputBcdFile = NIL THEN
BEGIN
nameCopy: STRING ← [40];
Strings.AppendString[to: nameCopy, from: PackagerDefs.globalData.outputBcdName];
PackagerDefs.globalData.outputBcdFile ← Segments.NewFile[nameCopy, Segments.Write];
END;
outStream ← Streams.CreateStream[
PackagerDefs.globalData.outputBcdFile,
Streams.Write];
-- compute size of new bcd
bcdPos ← SIZE[BcdDefs.BCD];
newHeader ← LongStorage.Node[bcdPos];
newHeader↑ ← SourceBcd.bcdHeader↑;
desc ← [base: gd.packName, offset: 0, length: gd.packName.length];
newHeader.source ← BcdUtilDefs.EnterName[@desc];
newHeader.creator ← gd.packagerVersion;
newHeader.sourceVersion ← gd.packVersion;
newHeader.version ← [
time: Segments.GetFileTimes[
PackagerDefs.globalData.outputBcdFile].create,
net: gd.network,
host: gd.host];
newHeader.repackaged ← TRUE;
size ← table.Bounds[BcdDefs.sstype].size;
newHeader.ssOffset ← bcdPos;
newHeader.ssLimit ← LOOPHOLE[size];
bcdPos ← bcdPos + size;
newHeader.ctOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.ctLimit, CARDINAL];
newHeader.mtOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.mtLimit, CARDINAL];
newHeader.impOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.impLimit, CARDINAL];
newHeader.expOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.expLimit, CARDINAL];
newHeader.evOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.evLimit, CARDINAL];
size ← table.Bounds[BcdDefs.sgtype].size;
newHeader.sgOffset ← bcdPos;
newHeader.sgLimit ← LOOPHOLE[size];
bcdPos ← bcdPos + size;
size ← table.Bounds[BcdDefs.fttype].size;
newHeader.ftOffset ← bcdPos;
newHeader.ftLimit ← LOOPHOLE[size];
bcdPos ← bcdPos + size;
size ← table.Bounds[BcdDefs.sptype].size;
newHeader.spOffset ← bcdPos;
newHeader.spLimit ← LOOPHOLE[size];
bcdPos ← bcdPos + size;
newHeader.ntOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.ntLimit, CARDINAL];
newHeader.typOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.typLimit, CARDINAL];
newHeader.tmOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.tmLimit, CARDINAL];
size ← table.Bounds[BcdDefs.fptype].size;
newHeader.fpOffset ← bcdPos;
newHeader.fpLimit ← LOOPHOLE[size];
bcdPos ← bcdPos + size;
IF SourceBcd.bcdHeader.extended THEN
BEGIN
newHeader.lfOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.lfLimit, CARDINAL];
newHeader.rfOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.rfLimit, CARDINAL];
newHeader.tfOffset ← bcdPos;
bcdPos ← bcdPos + LOOPHOLE[newHeader.tfLimit, CARDINAL];
END;
bcdPages ← LongStorage.PagesForWords[bcdPos];
IF SourceBcd.bcdHeader.extended THEN
BEGIN
newHeader.rtPages.relPageBase ← bcdPages;
bcdPages ← bcdPages + newHeader.rtPages.pages;
END;
newHeader.nPages ← bcdPages;
limitSgi ← LOOPHOLE[table.Bounds[BcdDefs.sgtype].size];
FOR sgi: BcdDefs.SGIndex ← firstCodeSgi, sgi+SIZE[BcdDefs.SGRecord]
UNTIL sgi = limitSgi DO
sgb[sgi].base ← sgb[sgi].base + bcdPages + 1;
ENDLOOP;
-- write bcd to stream
[] ← Streams.PutBlock[
outStream,
newHeader,
SIZE[BcdDefs.BCD]];
[] ← Streams.PutBlock[
outStream,
table.Bounds[BcdDefs.sstype].base,
LOOPHOLE[newHeader.ssLimit]];
[] ← Streams.PutBlock[
outStream,
table.Bounds[BcdDefs.cttype].base,
LOOPHOLE[newHeader.ctLimit]];
[] ← Streams.PutBlock[
outStream,
table.Bounds[BcdDefs.mttype].base,
LOOPHOLE[newHeader.mtLimit]];
[] ← Streams.PutBlock[
outStream,
table.Bounds[BcdDefs.imptype].base,
LOOPHOLE[newHeader.impLimit]];
[] ← Streams.PutBlock[
outStream,
table.Bounds[BcdDefs.exptype].base,
LOOPHOLE[newHeader.expLimit]];
[] ← Streams.PutBlock[
outStream,
SourceBcd.bcdHeader + SourceBcd.bcdHeader.evOffset,
LOOPHOLE[newHeader.evLimit]];
[] ← Streams.PutBlock[
outStream,
table.Bounds[BcdDefs.sgtype].base,
LOOPHOLE[newHeader.sgLimit]];
[] ← Streams.PutBlock[
outStream,
table.Bounds[BcdDefs.fttype].base,
LOOPHOLE[newHeader.ftLimit]];
[] ← Streams.PutBlock[
outStream,
table.Bounds[BcdDefs.sptype].base,
LOOPHOLE[newHeader.spLimit]];
[] ← Streams.PutBlock[
outStream,
table.Bounds[BcdDefs.nttype].base,
LOOPHOLE[newHeader.ntLimit]];
[] ← Streams.PutBlock[
outStream,
SourceBcd.bcdHeader + SourceBcd.bcdHeader.typOffset,
LOOPHOLE[newHeader.typLimit]];
[] ← Streams.PutBlock[
outStream,
SourceBcd.bcdHeader + SourceBcd.bcdHeader.tmOffset,
LOOPHOLE[newHeader.tmLimit]];
[] ← Streams.PutBlock[
outStream,
table.Bounds[BcdDefs.fptype].base,
LOOPHOLE[newHeader.fpLimit]];
IF SourceBcd.bcdHeader.extended THEN
BEGIN
[] ← Streams.PutBlock[
outStream,
SourceBcd.bcdHeader + SourceBcd.bcdHeader.lfOffset,
LOOPHOLE[newHeader.lfLimit]];
[] ← Streams.PutBlock[
outStream,
SourceBcd.bcdHeader + SourceBcd.bcdHeader.rfOffset,
LOOPHOLE[newHeader.rfLimit]];
[] ← Streams.PutBlock[
outStream,
SourceBcd.bcdHeader + SourceBcd.bcdHeader.tfOffset,
LOOPHOLE[newHeader.tfLimit]];
FillToPageBoundary[];
[] ← Streams.PutBlock[
outStream,
SourceBcd.bcdHeader +
SourceBcd.bcdHeader.rtPages.relPageBase*PackEnviron.PageSize,
LOOPHOLE[SourceBcd.bcdHeader.rtPages.pages*PackEnviron.PageSize]];
END
ELSE FillToPageBoundary[];
LongStorage.Free[newHeader];
-- throw out allocator space and source bcd
END;
WriteCodeToBcdFile: PUBLIC PROC =
BEGIN
IF gd.errors THEN RETURN;
-- close piece table
IF gd.nErrors # 0 THEN Segments.LockFile[gd.outputBcdFile];
PieceTable.Store[outStream];
IF gd.nErrors # 0 THEN Segments.UnlockFile[gd.outputBcdFile];
Finalize[];
END;
-- procedures to create new name, file, and segment tables for output bcd
-- update source bcd in place, creating new tables:
-- name table (ssb), file table, and segment table
-- after this update, the following is true:
-- All "name" fields refer to new NameRecords
-- In module table,
-- "sseg" refers to new segment table
-- "code.sgi" refers to old segment table
-- In new segment table, "file" refers to new file table
-- In old segment table, "file" refers to old file table
RemapOldBcd: PUBLIC PROC =
BEGIN
NullIndex: Table.OrderedIndex = LOOPHOLE[0];
BcdUtilDefs.Init[table];
IF table.Words[
table: BcdDefs.imptype,
size: LOOPHOLE[SourceBcd.bcdHeader.impLimit]] # NullIndex THEN
SIGNAL PackError [nonZeroBase];
PackEnviron.Copy[
from: SourceBcd.bcdBases.itb,
nwords: LOOPHOLE[SourceBcd.bcdHeader.impLimit],
to: itb];
IF table.Words[
table: BcdDefs.exptype,
size: LOOPHOLE[SourceBcd.bcdHeader.expLimit]] # NullIndex THEN
SIGNAL PackError [nonZeroBase];
PackEnviron.Copy[
from: SourceBcd.bcdBases.etb,
nwords: LOOPHOLE[SourceBcd.bcdHeader.expLimit],
to: etb];
IF table.Words[
table: BcdDefs.cttype,
size: LOOPHOLE[SourceBcd.bcdHeader.ctLimit]] # NullIndex THEN
SIGNAL PackError [nonZeroBase];
PackEnviron.Copy[
from: SourceBcd.bcdBases.ctb,
nwords: LOOPHOLE[SourceBcd.bcdHeader.ctLimit],
to: ctb];
IF table.Words[
table: BcdDefs.mttype,
size: LOOPHOLE[SourceBcd.bcdHeader.mtLimit]] # NullIndex THEN
SIGNAL PackError [nonZeroBase];
PackEnviron.Copy[
from: SourceBcd.bcdBases.mtb,
nwords: LOOPHOLE[SourceBcd.bcdHeader.mtLimit],
to: mtb];
IF table.Words[
table: BcdDefs.nttype,
size: LOOPHOLE[SourceBcd.bcdHeader.ntLimit]] # NullIndex THEN
SIGNAL PackError [nonZeroBase];
PackEnviron.Copy[
from: SourceBcd.bcdBases.ntb,
nwords: LOOPHOLE[SourceBcd.bcdHeader.ntLimit],
to: ntb];
[] ← BcdOps.ProcessImports[SourceBcd.bcdHeader, RemapImports];
[] ← BcdOps.ProcessExports[SourceBcd.bcdHeader, RemapExports];
[] ← BcdOps.ProcessConfigs[SourceBcd.bcdHeader, RemapConfigs];
[] ← BcdOps.ProcessModules[SourceBcd.bcdHeader, RemapModules];
[] ← BcdOps.ProcessNames[SourceBcd.bcdHeader, RemapInstances];
END;
RemapInstances: PROC [nth: BcdOps.NTHandle, nti: BcdDefs.NTIndex]
RETURNS [BOOLEAN] =
BEGIN OPEN nte: ntb[nti];
nte.name ← BcdUtilDefs.MapName[SourceBcd.bcdBases, nth.name];
RETURN [FALSE];
END;
MapFile: PROC [fti: BcdDefs.FTIndex] RETURNS [BcdDefs.FTIndex] =
BEGIN
SELECT fti FROM
BcdDefs.FTSelf =>
BEGIN
new: BcdDefs.FTIndex ← BcdUtilDefs.EnterFile[gd.sourceBcdName];
BcdUtilDefs.SetFileVersion[new, gd.sourceBcdVersion];
RETURN[new];
END;
BcdDefs.FTNull => RETURN[fti];
ENDCASE => RETURN[BcdUtilDefs.MergeFile[SourceBcd.bcdBases, fti]];
END;
RemapImports: PROC [imph: BcdOps.IMPHandle, impi: BcdDefs.IMPIndex]
RETURNS [BOOLEAN] =
BEGIN OPEN impe: itb[impi];
impe.name ← BcdUtilDefs.MapName[SourceBcd.bcdBases, imph.name];
impe.file ← MapFile[imph.file];
RETURN [FALSE]
END;
RemapExports: PROC [exph: BcdOps.EXPHandle, expi: BcdDefs.EXPIndex]
RETURNS [BOOLEAN] =
BEGIN OPEN expe: etb[expi];
expe.name ← BcdUtilDefs.MapName[SourceBcd.bcdBases, exph.name];
expe.file ← MapFile[exph.file];
RETURN [FALSE]
END;
RemapConfigs: PROC [cth: BcdOps.CTHandle, cti: BcdDefs.CTIndex]
RETURNS [BOOLEAN] =
BEGIN OPEN cte: ctb[cti];
cte.name ← BcdUtilDefs.MapName[SourceBcd.bcdBases, cth.name];
-- Note: we carry through FTSelf in order to make the config
-- point to the packaged BCD, not the unpackaged one. (RL)
IF cth.file ~= BcdDefs.FTSelf THEN cte.file ← MapFile[cth.file];
RETURN [FALSE]
END;
RemapModules: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [BOOLEAN] =
BEGIN OPEN mte: mtb[mti];
sgr: BcdDefs.SGRecord ← SourceBcd.bcdBases.sgb[mth.sseg];
IF ~mth.packageable THEN
Error.ErrorName[error, "has already been packaged!"L, mth.name];
mte.name ← BcdUtilDefs.MapName[SourceBcd.bcdBases, mth.name];
mte.file ← MapFile[mth.file];
sgr.file ← MapFile[sgr.file];
mte.sseg ← BcdUtilDefs.EnterSegment[sgr];
mte.packageable ← FALSE;
-- mtb[mti].code will be fixed up later
RETURN [FALSE]
END;
PrintHeader: PROC =
BEGIN
-- should print bcd version in file
WriteString["Words EVI Offset IPC Module"L];
THROUGH [("Module"L).length..modCols] DO WriteChar[Ascii.SP] ENDLOOP;
WriteString["Procedure"L];
WriteChar[Ascii.CR];
WriteChar[Ascii.CR];
END;
-- ** Loadmap stuff
modCols: CARDINAL ← 20;
Decimal4: CharIO.NumberFormat =
[base: 10, zerofill: FALSE, unsigned: TRUE, columns: 4];
Decimal5: CharIO.NumberFormat =
[base: 10, zerofill: FALSE, unsigned: TRUE, columns: 5];
Decimal6: CharIO.NumberFormat =
[base: 10, zerofill: FALSE, unsigned: TRUE, columns: 6];
Octal5: CharIO.NumberFormat =
[base: 8, zerofill: FALSE, unsigned: TRUE, columns: 5];
Octal7: CharIO.NumberFormat =
[base: 8, zerofill: FALSE, unsigned: TRUE, columns: 7];
NoteData: PROC [offset, length: CARDINAL] =
BEGIN
WriteNumber[length, Decimal5];
WriteString[" -"L];
WriteNumber[offset, Octal7];
WriteChar['B];
WriteString[" <data>"L];
WriteChar[Ascii.CR];
END;
DisplayNumbers: PROC [ep: INTEGER, length: CARDINAL, hti: Symbols.HTIndex] =
BEGIN
-- write out module, entry, segOffset, codeOffset
-- called when codeBasePosition and segmentPosition are valid
pos: PieceTable.Position ← PieceTable.Append[];
offset, cols: CARDINAL;
IF ep = 0 THEN pos ← pos + 2;
IF lastProcEnd # 0 AND pos > lastProcEnd THEN
NoteData[
offset: SegmentOffset[lastProcEnd],
length: Inline.LowHalf[(pos-lastProcEnd)/2]];
lastProcEnd ← pos + length*2;
WriteNumber[length, Decimal5];
IF ep = -1 THEN WriteString[" EV"L]
ELSE WriteNumber[ep, Decimal4];
offset ← SegmentOffset[pos];
WriteNumber[offset, Octal7];
WriteChar['B];
IF ep = -1 THEN
WriteString[" "L]
ELSE
BEGIN
offset ← CodeOffset[pos];
WriteNumber[offset*2, Octal7];
WriteChar['B];
END;
WriteString[" "L];
cols ← WriteName[mtb[currentModule].name];
IF ep # -1 THEN
BEGIN
THROUGH [cols..modCols) DO WriteChar[Ascii.SP] ENDLOOP;
WriteChar[Ascii.SP];
IF ep = 0 THEN WriteString["MAIN"L]
ELSE IF hti = Symbols.HTNull THEN
WriteString[" <nested>"L]
ELSE [] ← WriteProcName[hti]
END;
WriteChar[Ascii.CR];
END;
WriteName: PROC [name: BcdDefs.NameRecord] RETURNS [length: CARDINAL] =
BEGIN
desc: Strings.SubStringDescriptor;
desc ← [base: @ssb.string, offset: name, length: ssb.size[name]];
WriteSubString[@desc];
RETURN [desc.length];
END;
WriteProcName: PROC [hti: Symbols.HTIndex] RETURNS [length: CARDINAL] =
BEGIN
desc: Strings.SubStringDescriptor;
IF hti = Symbols.HTNull THEN RETURN[0];
SymbolOps.SubStringForHash[@desc, hti];
WriteSubString[@desc];
RETURN [desc.length];
END;
END.