-- CTLoadImpl.Mesa, last edit March 14, 1983 2:09 pm
-- Mesa 7.0/ Pilot 6.0
-- procedures to load and start modules in the compiler tool
-- links:
-- IF gfi > firstdummy, then gfi is index into Import table
-- and ep is index into the export record pared with that import
-- binding is simply to copy control link in the export record
-- into this link
-- IF gfi < firstdummy, then gfi in this link is an index into the config's
-- moduletable. Do not alter the ep
DIRECTORY
BcdDefs: TYPE USING [Base, FTIndex, FTSelf,
GFTIndex, Link, MTIndex, NameRecord, NullLink,
UnboundLink, VersionID],
BcdOps: TYPE USING [BcdBase, EXPHandle, FTHandle, MTHandle, NameString,
ProcessModules],
CedarLinkerOps: TYPE USING[FindVariableLink],
CTLoad: TYPE USING [DummyMapSeq, DummyMapSeqRecord, ImpExpSeq, ImpExpSeqRecord,
InterfaceSeq, InterfaceSeqRecord, LoadInfoSeq, LoadInfoSeqRecord, ReplaceResult],
Directory: TYPE USING [UpdateDates],
Environment: TYPE USING [PageCount, PageNumber],
File: TYPE USING [Capability, PageCount, PageNumber, read, write],
Frame: TYPE USING [GetReturnLink],
Inline: TYPE USING [BITAND],
IO: TYPE USING[card, Handle, PutF, rope],
LoaderPrivate: TYPE USING[AssignCodeToFrames, AssignControlModules, CreateGlobalFrames,
FindMappedSpace, NextMultipleOfFour],
PilotLoaderOps: TYPE USING[DestroyMap,
InitializeMap, IthLink, LinkSegmentLength],
PilotLoadStateOps: TYPE USING[ConfigIndex],
PrincOps: TYPE USING [ControlLink, ControlModule, Frame, GFTIndex,
GlobalFrameHandle, NullLink, StateVector, UnboundLink],
PrincOpsRuntime: TYPE USING [GetFrame, GFT],
Rope: TYPE USING[Cat, Flatten, FromChar, ROPE, Text],
RTOS: TYPE USING [CheckForModuleReplacement],
Runtime: TYPE USING [ValidateGlobalFrame],
RuntimeInternal: TYPE USING [Codebase],
SDDefs: TYPE USING [SD, sStart, sSwapTrap],
Space: TYPE USING [Create, Delete, Error,
GetHandle, GetWindow, Handle,
LongPointer, MakeReadOnly, MakeWritable, Map,
PageFromLongPointer, virtualMemory],
TimeStamp: TYPE USING [Stamp],
Trap: TYPE USING [ReadOTP];
CTLoadImpl: MONITOR
IMPORTS BcdOps, CedarLinkerOps, Directory, Frame, Inline, IO, LoaderPrivate, PilotLoaderOps,
PrincOpsRuntime, Rope, RTOS, Runtime, RuntimeInternal, Space, Trap
EXPORTS CTLoad
SHARES File = {
-- MDS Usage!
waitCodeTrapCV: CONDITION;
-- link space data
links: LONG POINTER TO ARRAY[0 .. 0) OF PrincOps.ControlLink ← NIL;
writeable: BOOL ← FALSE;
long: BOOL ← FALSE;
-- end of MDS usage
LoadGlobalFrames: PUBLIC PROC[cap: File.Capability, config: PilotLoadStateOps.ConfigIndex,
oldConfigGfi: PrincOps.GFTIndex, out: IO.Handle]
RETURNS[loadinfoseq: CTLoad.LoadInfoSeq, newConfigGfi: PrincOps.GFTIndex] = {
bcdbase: BcdOps.BcdBase;
mod: CARDINAL ← 0;
dummymapseq: CTLoad.DummyMapSeq;
-- mod, dummymapseq, map is passed in
ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
RETURNS[stop: BOOL] = {
gfi: CARDINAL;
frame: PrincOps.GlobalFrameHandle;
stop ← FALSE;
frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[loadinfoseq.map[mth.gfi]]];
loadinfoseq[mod] ← [frame: frame, framesize: mth.framesize, ngfi: mth.ngfi];
newConfigGfi ← newConfigGfi + mth.ngfi;
-- now store in cgfi to rgfi map
gfi ← loadinfoseq.map[mth.gfi];
FOR i: CARDINAL IN [0 .. mth.ngfi) DO
dummymapseq[mth.gfi + i] ← [ind: gfi, whichone: i];
ENDLOOP;
dummymapseq.size ← dummymapseq.size + mth.ngfi;
mod ← mod + 1;
out.PutF["%s: gfi = %bB, gfh = %bB, framesize = %d, ",
IO.rope[NSToRope[bcdbase, mth.name]], IO.card[gfi],
IO.card[LOOPHOLE[frame, CARDINAL]], IO.card[mth.framesize]];
out.PutF["#%slinks = %d\n",
IO.rope[IF frame.codelinks THEN "code" ELSE "frame"],
IO.card[PilotLoaderOps.LinkSegmentLength[mth, bcdbase]]];
};
{
cap ← Directory.UpdateDates[cap, File.read];
[bcd: bcdbase] ← LoadUpBcd[cap, 1];
loadinfoseq ← AllocateLoadInfoSeq[bcdbase];
loadinfoseq.bcdbase ← bcdbase;
loadinfoseq.size ← 0;
loadinfoseq.configGfi ← oldConfigGfi;
newConfigGfi ← oldConfigGfi;
dummymapseq ← loadinfoseq.dummymapseq;
dummymapseq[0] ← [0, 0];
dummymapseq.size ← 1; -- dummy module indices start at 1, not 0
loadinfoseq.map ← PilotLoaderOps.InitializeMap[bcdbase];
-- map is filled in by CreateGlobalFrames
loadinfoseq.frameList ← LoaderPrivate.CreateGlobalFrames[bcdbase, loadinfoseq.map,
config, FALSE];
[] ← BcdOps.ProcessModules[bcdbase, ForEachModule];
LoaderPrivate.AssignCodeToFrames[bcdbase, cap, 1, loadinfoseq.map];
SetLinksToNull[bcdbase, loadinfoseq];
loadinfoseq.cm ← LoaderPrivate.AssignControlModules[bcdbase, loadinfoseq.map];
loadinfoseq.size ← mod;
}};
WaitForBroadcast: ENTRY PROC[frame: PrincOps.GlobalFrameHandle] = {
WHILE frame.code.out DO
WAIT waitCodeTrapCV;
ENDLOOP;
};
-- can only be called for modules (not configs)
LoadIncremental: PUBLIC ENTRY PROC[bcdcap: File.Capability,
loadinfoseq: CTLoad.LoadInfoSeq, window: IO.Handle]
RETURNS[replaceResult: CTLoad.ReplaceResult] = {
savemodellercode: PROC ← NIL;
codetrapframe: PrincOps.GlobalFrameHandle ← NIL;
{
ENABLE UNWIND => IF savemodellercode ~= NIL THEN
SDDefs.SD[SDDefs.sSwapTrap] ← savemodellercode;
ModellerCodeTrap: PROC = {
start: PROC[PrincOps.ControlModule];
dest: PrincOps.ControlLink;
state: PrincOps.StateVector;
frame: PrincOps.GlobalFrameHandle;
state ← STATE;
dest ← Trap.ReadOTP[];
state.dest ← Frame.GetReturnLink[];
DO
IF dest.proc THEN {
frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[dest.gfi]];
EXIT}
ELSE
IF dest.indirect THEN dest ← dest.link↑
ELSE {frame ← dest.frame.accesslink; EXIT}; -- frame
ENDLOOP;
IF frame = codetrapframe THEN {
-- this halts outside process until my procedure is finished
WaitForBroadcast[frame];
RETURN;
};
IF ~frame.started THEN {
start ← LOOPHOLE[SDDefs.SD[SDDefs.sStart]];
start[[frame[frame]]];
};
frame.code.out ← FALSE;
RETURN WITH state
};
n: CARDINAL;
mname: Rope.Text;
gfi: CARDINAL;
bcdbase: BcdOps.BcdBase;
mth: BcdOps.MTHandle;
frame: PrincOps.GlobalFrameHandle ← loadinfoseq[0].frame;
replaceResult ← ok;
IF loadinfoseq.size ~= 1 THEN RETURN[configNotReplaceable];
[bcdbase] ← LoadUpBcd[bcdcap, 1];
loadinfoseq.bcdbase ← bcdbase;
loadinfoseq.cm ← LOOPHOLE[frame];
-- recompute these since bcdbase.firstdummy, bcdbase.nDummies, and bcdbase.nImports
-- may have changed
ReSizeMaps[loadinfoseq];
mth ← @LOOPHOLE[bcdbase + bcdbase.mtOffset, BcdDefs.Base][FIRST[BcdDefs.MTIndex]];
-- checking
IF LOOPHOLE[frame + mth.framesize, CARDINAL] >
LOOPHOLE[LoaderPrivate.NextMultipleOfFour[frame + loadinfoseq[0].framesize], CARDINAL] THEN
RETURN[frameTooBig];
IF mth.ngfi > loadinfoseq[0].ngfi THEN RETURN[ngfiTooBig];
-- now think of monitor:
-- set lock by setting code trap, then call check procedure
-- then do replacement, then release lock
-- this is to avoid the case where some local frames
-- are created after the call on RTOS.CheckForModuleReplacemeent
-- but before I swap the code
codetrapframe ← frame;
savemodellercode ← SDDefs.SD[SDDefs.sSwapTrap];
SDDefs.SD[SDDefs.sSwapTrap] ← ModellerCodeTrap;
frame.code.out ← TRUE; -- force code trap
IF NOT RTOS.CheckForModuleReplacement[frame] THEN {
SDDefs.SD[SDDefs.sSwapTrap] ← savemodellercode;
RETURN[checkForMRFailed];
};
PilotLoaderOps.DestroyMap[loadinfoseq.map]; -- old map
loadinfoseq.map ← PilotLoaderOps.InitializeMap[bcdbase];
-- the dummy bcd #'s start at 1
FOR i: CARDINAL IN [0 .. bcdbase.firstdummy) DO
loadinfoseq.map[i] ← loadinfoseq.dummymapseq[i].ind;
ENDLOOP;
LoaderPrivate.AssignCodeToFrames[bcdbase, bcdcap, 1, loadinfoseq.map];
-- havinge set the code base, we can now release the lock
frame.code.out ← FALSE;
BROADCAST waitCodeTrapCV;
SDDefs.SD[SDDefs.sSwapTrap] ← savemodellercode;
savemodellercode ← NIL;
SetLinksToNull[bcdbase, loadinfoseq];
mname ← NSToRope[bcdbase, mth.name];
n ← mth.framesize;
gfi ← frame.gfi;
window.PutF["%s: gfi = %bB, gfh = %bB, framesize = %d, ",
IO.rope[mname], IO.card[gfi], IO.card[LOOPHOLE[frame, CARDINAL]], IO.card[n]];
n ← PilotLoaderOps.LinkSegmentLength[mth, bcdbase];
window.PutF["#%slinks = %d\n",
IO.rope[IF frame.codelinks THEN "code" ELSE "frame"], IO.card[n]];
RETURN[ok];
}};
-- old contents are preserved
ReSizeMaps: PROC[loadinfoseq: CTLoad.LoadInfoSeq] =
{
olddummymapseq: CTLoad.DummyMapSeq ← loadinfoseq.dummymapseq;
oldimpexpseq: CTLoad.ImpExpSeq ← loadinfoseq.impexpseq;
minSize: CARDINAL;
loadinfoseq.dummymapseq ← NEW[CTLoad.DummyMapSeqRecord[
loadinfoseq.bcdbase.firstdummy +loadinfoseq.bcdbase.nDummies]];
loadinfoseq.impexpseq ← NEW[
CTLoad.ImpExpSeqRecord[loadinfoseq.bcdbase.nImports]];
minSize ← MIN[olddummymapseq.size, loadinfoseq.dummymapseq.maxsize];
FOR i: CARDINAL IN [0 .. minSize) DO
loadinfoseq.dummymapseq[i] ← olddummymapseq[i];
ENDLOOP;
loadinfoseq.dummymapseq.size ← minSize;
minSize ← MIN[oldimpexpseq.size, loadinfoseq.impexpseq.maxsize];
FOR i: CARDINAL IN [0 .. minSize) DO
loadinfoseq.impexpseq[i] ← oldimpexpseq[i];
ENDLOOP;
loadinfoseq.impexpseq.size ← minSize;
FREE[@olddummymapseq];
FREE[@oldimpexpseq];
};
SetLinksToNull: PROC[bcdbase: BcdOps.BcdBase, loadinfoseq: CTLoad.LoadInfoSeq] = {
ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
RETURNS[stop: BOOL] = {
-- set all the links to null
frame: PrincOps.GlobalFrameHandle;
stop ← FALSE;
frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[
loadinfoseq.dummymapseq[mth.gfi].ind]];
Runtime.ValidateGlobalFrame[frame];
[] ← OpenLinkSpace[frame, mth, bcdbase];
FOR i: CARDINAL IN [0..PilotLoaderOps.LinkSegmentLength[mth, bcdbase]) DO
WriteLink[
offset: i,
link: SELECT PilotLoaderOps.IthLink[mth, i, bcdbase].vtag FROM
var, type => PrincOps.NullLink,
ENDCASE => PrincOps.UnboundLink];
ENDLOOP;
CloseLinkSpace[frame];
};
[] ← BcdOps.ProcessModules[bcdbase, ForEachModule];
loadinfoseq.linksresolved ← FALSE;
};
BuildInterface: PUBLIC PROC[loadinfoseq: CTLoad.LoadInfoSeq,
eth: BcdOps.EXPHandle] RETURNS[interfaceseq: CTLoad.InterfaceSeq] = {
clink: PrincOps.ControlLink;
cgfi: PrincOps.GFTIndex; -- dummy
bcdbase: BcdOps.BcdBase;
name: Rope.Text;
fth: BcdOps.FTHandle;
IF eth.size = 0 THEN RETURN[NIL];
bcdbase ← loadinfoseq.bcdbase;
name ← NSToRope[bcdbase, eth.name];
interfaceseq ← AllocateInterfaceSeq[name, eth.size];
interfaceseq.resolved ← TRUE;
FOR i: CARDINAL IN [0 .. eth.size) DO
SELECT eth.links[i].vtag FROM
var => {
[link: clink] ← CedarLinkerOps.FindVariableLink[bcd: loadinfoseq.bcdbase,
mthLink: eth.links[i], rgfi: loadinfoseq.dummymapseq[eth.links[i].vgfi].ind];
interfaceseq[i] ← [clink: clink, blink: BcdDefs.NullLink]
};
proc0, proc1 => {
realgfi: PrincOps.GFTIndex;
realgfi ← loadinfoseq.dummymapseq[eth.links[i].gfi].ind;
[clink, cgfi] ← ConvertDummyLinkToControlLink[eth.links[i],
realgfi, bcdbase, loadinfoseq];
interfaceseq[i] ← [clink: clink,
blink: [procedure[gfi: cgfi, ep: eth.links[i].ep, tag: eth.links[i].tag]]];
};
type => -- means no checking for exported type mismatches!!!
interfaceseq[i] ← [clink: PrincOps.NullLink, blink: BcdDefs.UnboundLink];
ENDCASE => ERROR;
IF EmptyLink[interfaceseq[i].clink] THEN interfaceseq.resolved ← FALSE;
ENDLOOP;
interfaceseq.size ← eth.size;
fth ← @LOOPHOLE[bcdbase + bcdbase.ftOffset, BcdDefs.Base][eth.file];
interfaceseq.bcdVers ← fth.version;
};
EmptyLink: PROC[link: PrincOps.ControlLink] RETURNS[empty: BOOL] = {
RETURN[link = PrincOps.UnboundLink OR link = PrincOps.NullLink];
};
-- can't be used for configs
BuildFramePtrInterface: PUBLIC PROC[bcdbase: BcdOps.BcdBase,
frame: PrincOps.GlobalFrameHandle]
RETURNS[interfaceseq: CTLoad.InterfaceSeq] = {
name: Rope.Text;
mth: BcdOps.MTHandle;
IF bcdbase.nModules ~= 1 THEN ERROR;
mth ← @LOOPHOLE[bcdbase + bcdbase.mtOffset, BcdDefs.Base][FIRST[BcdDefs.MTIndex]];
name ← NSToRope[bcdbase, mth.name];
interfaceseq ← AllocateInterfaceSeq[name, 1];
IF mth.file = BcdDefs.FTSelf THEN
interfaceseq.bcdVers ← bcdbase.version
ELSE {
fth: BcdOps.FTHandle;
fth ← @LOOPHOLE[bcdbase + bcdbase.ftOffset, BcdDefs.Base][mth.file];
interfaceseq.bcdVers ← fth.version;
};
interfaceseq[0] ← [clink: LOOPHOLE[frame], blink: BcdDefs.NullLink];
interfaceseq.size ← 1;
};
-- only works for exported BcdDefs.Links in the export table
ConvertDummyLinkToControlLink: PROC[bl: BcdDefs.Link, realgfi: CARDINAL,
bcdbase: BcdOps.BcdBase, loadinfoseq: CTLoad.LoadInfoSeq]
RETURNS[cl: PrincOps.ControlLink, newcgfi: PrincOps.GFTIndex] = {
cgfi: PrincOps.GFTIndex;
ForEachModule: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [BOOL] = BEGIN
mgfi: PrincOps.GFTIndex ← mth.gfi;
IF cgfi IN [mth.gfi..mgfi + mth.ngfi) THEN {
newcgfi ← newcgfi + (cgfi - mgfi);
realgfi ← realgfi + (cgfi - mgfi);
RETURN[TRUE];
};
RETURN[FALSE]
END;
newcgfi ← loadinfoseq.configGfi;
IF bl = BcdDefs.UnboundLink THEN RETURN[PrincOps.UnboundLink, newcgfi];
SELECT bl.vtag FROM
var => {
cgfi ← bl.vgfi;
IF BcdOps.ProcessModules[bcdbase, ForEachModule].mth = NIL THEN
RETURN[PrincOps.NullLink, newcgfi];
cl ← [procedure[gfi: realgfi, ep: bl.var, tag: FALSE]];
};
proc0, proc1 => {
cgfi ← bl.gfi;
IF BcdOps.ProcessModules[bcdbase, ForEachModule].mth = NIL THEN
RETURN[PrincOps.UnboundLink, newcgfi];
cl ← [procedure[gfi: realgfi, ep: bl.ep, tag: TRUE]];
};
type => cl ← LOOPHOLE[bl.typeID];
ENDCASE;
};
NSToRope: PUBLIC PROC[bcdbase: BcdOps.BcdBase, name: BcdDefs.NameRecord]
RETURNS[resultstr: Rope.Text] = {
namestring: BcdOps.NameString;
r: Rope.ROPE ← NIL;
namestring ← LOOPHOLE[bcdbase + bcdbase.ssOffset];
FOR i: CARDINAL IN [0 .. namestring.size[name]) DO
r ← r.Cat[Rope.FromChar[namestring.string.text[name + i]]];
ENDLOOP;
resultstr ← Rope.Flatten[r];
};
AllocateLoadInfoSeq: PUBLIC PROC[bcdbase: BcdOps.BcdBase]
RETURNS[loadinfoseq: CTLoad.LoadInfoSeq] = {
loadinfoseq ← NEW[CTLoad.LoadInfoSeqRecord[bcdbase.nModules]];
loadinfoseq.dummymapseq ← NEW[
CTLoad.DummyMapSeqRecord[bcdbase.firstdummy +bcdbase.nDummies]];
loadinfoseq.impexpseq ← NEW[
CTLoad.ImpExpSeqRecord[bcdbase.nImports]];
};
FreeLoadInfoSeq: PUBLIC PROC[loadinfoseq: CTLoad.LoadInfoSeq] RETURNS[CTLoad.LoadInfoSeq]= {
IF loadinfoseq = NIL THEN RETURN[NIL];
IF loadinfoseq.bcdbase ~= NIL THEN
Space.Delete[Space.GetHandle[Space.PageFromLongPointer[loadinfoseq.bcdbase]]
! Space.Error => CONTINUE];
loadinfoseq.impexpseq ← NIL;
loadinfoseq.dummymapseq ← NIL;
RETURN[NIL];
};
AllocateInterfaceSeq: PUBLIC PROC[name: Rope.Text, size: CARDINAL]
RETURNS[interfaceseq: CTLoad.InterfaceSeq] = {
interfaceseq ← NEW[CTLoad.InterfaceSeqRecord[size] ← [body: ]];
interfaceseq.name ← name;
FOR i: CARDINAL IN [0 .. size) DO
interfaceseq[i] ← [PrincOps.NullLink, BcdDefs.NullLink];
ENDLOOP;
};
FreeInterfaceSeq: PUBLIC PROC[interfaceseq: CTLoad.InterfaceSeq] RETURNS[CTLoad.InterfaceSeq] = {
IF interfaceseq = NIL THEN RETURN[NIL];
interfaceseq.name ← NIL;
FREE[@interfaceseq];
RETURN[NIL];
};
InvalidFile: PUBLIC ERROR [File.Capability] = CODE;
LoadUpBcd: PROC [file: File.Capability, offset: File.PageCount]
RETURNS [bcd: BcdOps.BcdBase, pages: CARDINAL] =
BEGIN
bcdSpaceBase: File.PageNumber;
bcdSpace: Space.Handle;
bcdSpaceBase ← offset;
bcdSpace ← Space.Create[size: 1, parent: Space.virtualMemory];
Space.Map[space: bcdSpace, window: [file: file, base: bcdSpaceBase]];
bcd ← Space.LongPointer[bcdSpace];
pages ← bcd.nPages;
IF bcd.versionIdent # BcdDefs.VersionID OR bcd.definitions
OR (NOT bcd.tableCompiled AND ~bcd.spare1) THEN
ERROR InvalidFile[file ! UNWIND => Space.Delete[bcdSpace]];
IF pages > 1 THEN
BEGIN
Space.Delete[bcdSpace];
bcdSpace ← Space.Create[size: pages, parent: Space.virtualMemory];
Space.Map[space: bcdSpace, window: [file: file, base: bcdSpaceBase]];
bcd ← Space.LongPointer[bcdSpace];
END;
Space.MakeReadOnly[bcdSpace]; -- make bcd header spaces ReadOnly, as the code is already
RETURN
END;
-- link space operations
OpenLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle,
mth: BcdOps.MTHandle, bcd: BcdOps.BcdBase ← NIL]
RETURNS[LONG POINTER] =
BEGIN
IF frame.codelinks THEN {
long ← TRUE;
links ← RuntimeInternal.Codebase[LOOPHOLE[frame]];
IF links = NIL THEN ERROR;
}
ELSE {
long ← FALSE;
links ← LOOPHOLE[LONG[frame]]
};
links ← links - PilotLoaderOps.LinkSegmentLength[mth, bcd];
writeable ← FALSE;
RETURN[links];
END;
ReadLink: PUBLIC PROC [offset: CARDINAL]
RETURNS [link: PrincOps.ControlLink] =
BEGIN RETURN[links[offset]]; END;
WriteLink: PUBLIC PROC [offset: CARDINAL, link: PrincOps.ControlLink] = {
IF long AND NOT writeable THEN {
space: Space.Handle;
cap: File.Capability;
writeable ← TRUE;
space ← LoaderPrivate.FindMappedSpace[Space.GetHandle[
Space.PageFromLongPointer[links]]];
cap ← Space.GetWindow[space].file;
-- questionable????
-- IF Inline.BITAND[cap.permissions, File.write] = 0 THEN
cap.permissions ← cap.permissions + File.write;
Space.MakeWritable[space, cap]
};
links[offset] ← link};
CloseLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle] = {
IF long AND writeable THEN
Space.MakeReadOnly[LoaderPrivate.FindMappedSpace[Space.GetHandle[
Space.PageFromLongPointer[links]]]]
};
ConvertLink: PUBLIC PROC [bl: BcdDefs.Link] RETURNS [cl: PrincOps.ControlLink] =
BEGIN
IF bl = BcdDefs.UnboundLink THEN RETURN[PrincOps.UnboundLink];
SELECT bl.vtag FROM
var => cl ← [procedure[gfi: bl.vgfi, ep: bl.var, tag: FALSE]];
proc0, proc1 => cl ← [procedure[gfi: bl.gfi, ep: bl.ep, tag: TRUE]];
type => cl ← LOOPHOLE[bl.typeID];
ENDCASE;
RETURN
END;
}.