-- RTTypesRemotePrivateImpl.Mesa
-- last modified on December 21, 1982 9:25 am by Paul Rovner

DIRECTORY
AMBridge USING[WordSequence, WordSequenceRecord, RemotePD, RemoteGlobalFrameHandle,
RemoteFrameHandle, RemoteRef, RemoteSED, RemotePointer,
TVForRemoteFrame, nilRemotePD, nilRemoteGlobalFrameHandle,
nilRemoteFrameHandle],
AMTypes USING[Error, TypedVariable, TVType],
BcdDefs USING[MTIndex, BCD, FTSelf, SGIndex, SGNull, FTIndex, NameRecord],
BcdOps USING[BcdBase, MTHandle, ProcessModules, NameString, FTHandle,
ProcessFiles],
ConvertUnsafe USING[ToRope],
Environment USING[wordsPerPage],
LongString USING[AppendChar, AppendOctal, SubStringDescriptor, AppendSubString],
PilotLoadStateOps USING[InputLoadState, ReleaseLoadState, GetModule, AcquireBcd],
PilotLoadStateFormat USING[LoadState, LoadStateObject, ModuleInfo],
PilotLoadStatePrivate USING[InstallLoadState],
PrincOps USING[BytePC, ControlLink, CSegPrefix, EntryVectorItem, EPRange, Frame,
FrameCodeBase, FrameHandle, GFTIndex, GlobalFrame, GlobalFrameHandle,
localbase, NullLink, ProcDesc, SignalDesc, UnboundLink, NullGlobalFrame],
PrincOpsRuntime USING[GFT, GFTItem, GetFrame],
Rope USING[ROPE, Concat],
RTBasic USING[Type, nullType],
RTCommon USING[Field, StoreFieldLong],
RTQuanta USING[QuantumIndex],
RTRefCounts USING[AGCState],
RTSD USING[sRTState, sFirstCedarFree],
RTSymbols USING[ReleaseSTB, AcquireType, EnumerateRecordIseis, AcquireRope, AcquireSTB,
SymbolTableBase, SymbolTableHandle, nullHandle, CallableBodyIndex,
BodyIndex, rootBodyIndex, SymbolIdIndex, SymbolRecordIndex,
SymbolIndex, nullBodyIndex, nullSymbolIndex],
RTSymbolsPrivate USING[AcquireSTHFromSTX, GetSTHForModule],
RTTypesBasicPrivate USING[TypeDesc, UniqueTypeFinger, STDesc, RMapTiTd,
PTypeDesc, FindSTI, PSTDesc, RMapStiStd, SymbolTableIndex,
MapStiStd],
RTTypesPrivate USING[TypedVariableRec, GetCBTI, ConvertCbti],
RTTypesRemotePrivate USING[RemoteErrorType, EVRange],
RTZones USING[ZoneFinger, SubZone, SubZoneIndex, TMapQZf, MapPtrQ, NodeHeader,
sizeNd, SubZoneRec, RMapQZf, RMapSziSz],
SDDefs USING[SD, sSignal, sGFTLength],
SafeStorage USING[NewZone],
Strings USING[SubStringDescriptor, AppendSubString],
Table USING[Base],
UnsafeStorage USING[NewUObject, GetSystemUZone],
WorldVM USING[Address, CopyRead, CopyWrite, Long, LongRead, LongWrite,
Read, ShortAddress, World, Write, Loadstate, Lock, Unlock,
CurrentIncarnation];

RTTypesRemotePrivateImpl: MONITOR -- protects the cache of remote BCDs
IMPORTS AMBridge, AMTypes, BcdOps, ConvertUnsafe, LongString, PilotLoadStateOps,
PilotLoadStatePrivate,
PrincOpsRuntime, Rope, RTCommon, RTSymbols, RTSymbolsPrivate,
RTTypesBasicPrivate, RTTypesPrivate, RTZones,
SafeStorage, Strings, UnsafeStorage, WorldVM
EXPORTS RTTypesRemotePrivate

= BEGIN OPEN AMBridge, AMTypes, RTBasic, RTSymbols, RTTypesPrivate, RTTypesRemotePrivate,
WorldVM;

z: ZONE = SafeStorage.NewZone[prefixed];

-- TYPES
RemoteBCDCache: TYPE = LIST OF BCDCacheRec;
BCDCacheRec: TYPE = RECORD[world: World ← ,
remoteBCD: Address ← ,
localBCD: BcdOps.BcdBase ← ,
useCount: INT ← ];
-- VARIABLES
remoteBCDCache: RemoteBCDCache ← NIL;
remoteBCDCacheLength: NAT ← 0;
maxRemoteBCDCacheLength: NAT ← 1;

-- CONSTANTS
SignallerFrameMessageOffset: NAT = 9;
SignallerFrameSignalOffset: NAT = 7;

-- S I G N A L S

RemoteError: PUBLIC ERROR[type: RemoteErrorType,
message: LONG POINTER TO TEXTNIL] = CODE;

-- P R O C E D U R E S

--XXX
-- raises notImplemented
ValidateRemoteRef: PUBLIC PROC[ref: RemoteRef] =
{IF FALSE THEN ERROR AMTypes.Error[reason: notImplemented, msg: "ValidateRemoteRef"];
};

SameCode: PUBLIC PROC [f1, f2: RemoteGlobalFrameHandle] RETURNS [BOOL] =
{ fcb1, fcb2: PrincOps.FrameCodeBase;
fcb1 ← GetRemoteGFHeader[f1].code;
fcb2 ← GetRemoteGFHeader[f2].code;
fcb1.out ← fcb2.out ← FALSE;
RETURN[fcb1 = fcb2]};

EnumerateRemoteGlobalFrames: PUBLIC PROC[world: World,
proc: PROC[RemoteGlobalFrameHandle]
RETURNS[BOOLEAN]]
RETURNS[RemoteGlobalFrameHandle] =
{ gftLength: CARDINAL;

{ ENABLE UNWIND => Unlock[world];
Lock[world];
gftLength ← Read[world: world,
addr: Long[world: world,
addr: LOOPHOLE[SDDefs.SD + SDDefs.sGFTLength,
ShortAddress]]];
FOR i: CARDINAL IN [1..gftLength) DO
item: PrincOpsRuntime.GFTItem;
frame: PrincOps.GlobalFrameHandle;
CopyRead[world: world,
from: Long[world: world,
addr: LOOPHOLE[PrincOpsRuntime.GFT
+ i * SIZE[PrincOpsRuntime.GFTItem],
ShortAddress]],
nwords: SIZE[PrincOpsRuntime.GFTItem],
to: @item];
frame ← PrincOpsRuntime.GetFrame[item];
IF frame # PrincOps.NullGlobalFrame AND item.epbias = 0 THEN
{IF proc[[world: world,
worldIncarnation: CurrentIncarnation[world],
gfh: LOOPHOLE[frame, ShortAddress]]]
THEN {Unlock[world]; RETURN[[world: world,
worldIncarnation: CurrentIncarnation[world],
gfh: LOOPHOLE[frame, ShortAddress]]]}};
ENDLOOP;
Unlock[world];
RETURN[nilRemoteGlobalFrameHandle]}};

-- MOVE NOTE PROBLEM!
AcquireSTBFromRemoteGFH: PUBLIC PROC[gfh: RemoteGlobalFrameHandle]
RETURNS[SymbolTableBase] =
{ mappedBCD: BcdOps.BcdBase;
module: PilotLoadStateFormat.ModuleInfo; -- a loadstate.gft entry
std: RTTypesBasicPrivate.STDesc;
sth: SymbolTableHandle;
mth: BcdOps.MTHandle;
ftb: Table.Base;
sti: RTTypesBasicPrivate.SymbolTableIndex;

FindModule: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] RETURNS [BOOLEAN] =
{RETURN[module.gfi IN [mth.gfi .. mth.gfi + mth.ngfi)]};

[mappedBCD, module] ← AcquireRemoteBCDAndModule[gfh];

-- here with the copied remote BCD. Now poke around in the remote bcd to find
-- the version stamp, then get the symboltable
ftb ← LOOPHOLE[LOOPHOLE[mappedBCD, LONG POINTER] + mappedBCD.ftOffset, Table.Base];

[mth, ] ← BcdOps.ProcessModules[LOOPHOLE[mappedBCD], FindModule
! UNWIND => ReleaseRemoteBCD[mappedBCD]];
IF mth = NIL THEN ERROR;

std ← [symbolsStamp: IF mth.file = BcdDefs.FTSelf
THEN mappedBCD.version
ELSE ftb[mth.file].version];
ReleaseRemoteBCD[mappedBCD];

sti ← RTTypesBasicPrivate.FindSTI[std];
sth ← RTSymbolsPrivate.AcquireSTHFromSTX[sti].sth;
IF sth = nullHandle
THEN {moduleName: Rope.ROPE = RemoteGFHToName[gfh: gfh, moduleNameOnly: TRUE];
sth ← RTSymbolsPrivate.GetSTHForModule[stamp: std.symbolsStamp,
moduleName: moduleName,
fileName: Rope.Concat[moduleName,
".bcd"]];
IF sth # nullHandle
THEN RTTypesBasicPrivate.MapStiStd[sti].sth ← sth;
};
IF sth = nullHandle
THEN ERROR AMTypes.Error[reason: noSymbols,
msg: RemoteGFHToName[gfh]]
ELSE RETURN[RTSymbols.AcquireSTB[sth]];
};

-- raises Error
AcquireRemoteBCDAndModule: PUBLIC PROC[gfh: RemoteGlobalFrameHandle]
RETURNS[BcdOps.BcdBase, PilotLoadStateFormat.ModuleInfo] =
{ bcd: BcdOps.BcdBase; -- remote bcd
module: PilotLoadStateFormat.ModuleInfo; -- a loadstate.gft entry
world: World = gfh.world;
mappedBCD: BcdOps.BcdBase;

FindOriginal: PROCEDURE [f: RemoteGlobalFrameHandle] RETURNS [BOOLEAN] =
{RETURN[(f.gfh # gfh.gfh)
AND (SameCode[gfh, f])
AND (~GetRemoteGFHeader[f].copied)]};

IF gfh.gfh = 0 THEN ERROR AMTypes.Error[reason: noSymbols];

{ ENABLE UNWIND => Unlock[world];
loadStateHeld: BOOLEANFALSE;
oldState: PilotLoadStateFormat.LoadState;
newState: REF PilotLoadStateFormat.LoadStateObject;

Lock[world];

{ ENABLE ANY => IF loadStateHeld
THEN {PilotLoadStateOps.ReleaseLoadState[];
loadStateHeld ← FALSE};

IF GetRemoteGFHeader[gfh].copied
THEN gfh ← EnumerateRemoteGlobalFrames[world, FindOriginal];

newState ← Loadstate[gfh.world];
loadStateHeld ← TRUE;
[] ← PilotLoadStateOps.InputLoadState[];
oldState ← PilotLoadStatePrivate.InstallLoadState[LOOPHOLE[newState]]; -- no error raised

-- now find the bcd and module of interest

module ← PilotLoadStateOps.GetModule[GetRemoteGFHeader[gfh].gfi];

IF NOT loadStateHeld
THEN {loadStateHeld ← TRUE;
[] ← PilotLoadStateOps.InputLoadState[];
[] ← PilotLoadStatePrivate.InstallLoadState[LOOPHOLE[newState]];-- no error raised--};

bcd ← PilotLoadStateOps.AcquireBcd -- NOTE assume that it doesn't need to be released
[config: module.config];

IF NOT loadStateHeld
THEN {loadStateHeld ← TRUE;
[] ← PilotLoadStateOps.InputLoadState[];
[] ← PilotLoadStatePrivate.InstallLoadState[LOOPHOLE[newState]];-- no error raised--};

[] ← PilotLoadStatePrivate.InstallLoadState[oldState];
PilotLoadStateOps.ReleaseLoadState[];
loadStateHeld ← FALSE}; -- end ENABLE ANY

mappedBCD ← AcquireRemoteBCD[world: gfh.world, bcd: bcd];
Unlock[world];
}; -- end ENABLE UNWIND

RETURN[mappedBCD, module];
}; -- end AcquireRemoteBCDAndModule

ReleaseRemoteBCD: PUBLIC ENTRY PROC[bcd: BcdOps.BcdBase] =
{ENABLE UNWIND => NULL;
FOR l: RemoteBCDCache ← remoteBCDCache, l.rest UNTIL l = NIL
DO IF l.first.localBCD = bcd
THEN {l.first.useCount ← l.first.useCount - 1;
IF remoteBCDCacheLength > maxRemoteBCDCacheLength
THEN {prev: RemoteBCDCache ← NIL;
FOR s: RemoteBCDCache ← remoteBCDCache, s.rest UNTIL s = NIL
DO IF s.first.useCount = 0
THEN {nBCD: BcdOps.BcdBase ← s.first.localBCD;
UnsafeStorage.GetSystemUZone[].FREE[@nBCD];
IF prev = NIL
THEN remoteBCDCache ← s.rest
ELSE prev.rest ← s.rest;
EXIT};
prev ← s;
ENDLOOP};
RETURN};
ENDLOOP;
ERROR;
};

FindCachedRemoteBCD: ENTRY PROC[world: World, remoteBCD: Address]
RETURNS[localBCD: BcdOps.BcdBase ← NIL] =
{ENABLE UNWIND => NULL;
prev: RemoteBCDCache ← NIL;
FOR l: RemoteBCDCache ← remoteBCDCache, l.rest UNTIL l = NIL
DO IF l.first.world = world AND l.first.remoteBCD = remoteBCD
THEN {l.first.useCount ← l.first.useCount + 1;
IF prev = NIL THEN remoteBCDCache ← l.rest ELSE prev.rest ← l.rest; -- unhook it
l.rest ← remoteBCDCache; -- stick it at the beginning (most recent)
remoteBCDCache ← l;
RETURN[l.first.localBCD];
};
prev ← l;
ENDLOOP;
};

EnterRemoteBCDInCache: ENTRY PROC[world: World,
remoteBCD: Address,
localBCD: BcdOps.BcdBase] =
{ENABLE UNWIND => NULL;
FOR l: RemoteBCDCache ← remoteBCDCache, l.rest UNTIL l = NIL
DO IF l.rest = NIL
THEN {l.rest ← CONS[[world: world,
remoteBCD: remoteBCD,
localBCD: localBCD,
useCount: 1],
NIL];
RETURN;
};
ENDLOOP;
remoteBCDCache ← CONS[[world: world,
remoteBCD: remoteBCD,
localBCD: localBCD,
useCount: 1],
NIL];
};

-- assumes world is locked
AcquireRemoteBCD: PUBLIC PROC[world: World, bcd: BcdOps.BcdBase--remote--]
RETURNS[mappedBCD: BcdOps.BcdBase ← NIL--local--] =
{ pages: NAT;
mappedBCD ← FindCachedRemoteBCD[world, LOOPHOLE[bcd, Address]];
IF mappedBCD # NIL THEN RETURN[mappedBCD];
mappedBCD ← UnsafeStorage.NewUObject
[size: Environment.wordsPerPage,
zone: UnsafeStorage.GetSystemUZone[]];
CopyRead[world: world,
from: LOOPHOLE[bcd, Address],
nwords: SIZE[BcdDefs.BCD],
to: LOOPHOLE[mappedBCD, LONG POINTER]
! UNWIND => UnsafeStorage.GetSystemUZone[].FREE[@mappedBCD]];

IF mappedBCD.extended
THEN pages ← mappedBCD.nPages - mappedBCD.rtPages.pages
ELSE pages ← mappedBCD.nPages;

UnsafeStorage.GetSystemUZone[].FREE[@mappedBCD];
mappedBCD ← UnsafeStorage.NewUObject[size: pages*Environment.wordsPerPage,
zone: UnsafeStorage.GetSystemUZone[]];
CopyRead[world: world,
from: LOOPHOLE[bcd, Address],
nwords: pages*Environment.wordsPerPage,
to: LOOPHOLE[mappedBCD, LONG POINTER]
! UNWIND => UnsafeStorage.GetSystemUZone[].FREE[@mappedBCD]];
EnterRemoteBCDInCache[world: world,
remoteBCD: LOOPHOLE[bcd, Address],
localBCD: mappedBCD];
};

RemoteGFHToName: PUBLIC PROC[gfh: RemoteGlobalFrameHandle,
moduleNameOnly: BOOLFALSE]
RETURNS[ans: Rope.ROPENIL] =
{ s: STRING ← [80];
bcd: BcdOps.BcdBase;
module: PilotLoadStateFormat.ModuleInfo;
ssb: BcdOps.NameString;

AppendBracketed: PROC [s: STRING, value: CARDINAL] = INLINE
{IF moduleNameOnly THEN RETURN;
LongString.AppendChar[s, '[];
LongString.AppendOctal[s, value];
LongString.AppendChar[s, ']]};

FindModuleString: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [stop: BOOLEAN] =
{ ssd: LongString.SubStringDescriptor;
IF module.gfi IN [mth.gfi..mth.gfi + mth.ngfi)
THEN {ssd ← [base: @ssb.string, offset: mth.name, length: ssb.size[mth.name]];
s.length ← 0;
LongString.AppendSubString[s, @ssd];
IF GetRemoteGFHeader[gfh].copied
THEN AppendBracketed[s, GetRemoteGFHeader[gfh].gfi];
RETURN[TRUE]}
ELSE RETURN[FALSE]};

[bcd, module] ← AcquireRemoteBCDAndModule[gfh];
ssb ← LOOPHOLE[bcd + bcd.ssOffset, BcdOps.NameString];

[] ← BcdOps.ProcessModules[bcd, FindModuleString
! ANY => {s.length ← 0;
AppendBracketed[s, GetRemoteGFHeader[gfh].gfi];
CONTINUE}];
ReleaseRemoteBCD[bcd];
RETURN[ConvertUnsafe.ToRope[LONG[s]]]
};

-- MOVE NOTE PROBLEM!
RemoteTypeToLocal: PUBLIC PROC[world: World, remoteType: CARDINAL]
RETURNS[type: Type] =
{stb: SymbolTableBase;
td: RTTypesBasicPrivate.TypeDesc;
utf: RTTypesBasicPrivate.UniqueTypeFinger;
std: RTTypesBasicPrivate.STDesc;
sth: SymbolTableHandle;
sti: RTTypesBasicPrivate.SymbolTableIndex;

CopyRead[world: world,
from: LongRead[world: world,
addr: LOOPHOLE[WorldRoot[world].GCStateBasic.mapTiTd
+ SIZE[RTTypesBasicPrivate.RMapTiTd[0]]
+ LOOPHOLE[remoteType, CARDINAL]
*SIZE[RTTypesBasicPrivate.PTypeDesc],
Address]],
nwords: SIZE[RTTypesBasicPrivate.TypeDesc],
to: @td];
utf ← td.utf;
std ← [symbolsStamp: utf.umid];
sti ← RTTypesBasicPrivate.FindSTI[std];
sth ← RTSymbolsPrivate.AcquireSTHFromSTX[sti].sth;

IF sth = nullHandle
THEN {refRemoteSTD: REF RTTypesBasicPrivate.STDesc
= WorldMapStiStdEntry[world: world, sti: td.symbolAccess.sti];
IF refRemoteSTD.bcd # NIL
THEN {moduleName: Rope.ROPE;
mappedBCD: BcdOps.BcdBase ← AcquireRemoteBCD[world: world,
bcd: refRemoteSTD.bcd];
{ ENABLE UNWIND => ReleaseRemoteBCD[mappedBCD];
ssb: BcdOps.NameString = LOOPHOLE[mappedBCD + mappedBCD.ssOffset, BcdOps.NameString];
sgi: BcdDefs.SGIndex = refRemoteSTD.sgi; -- maybe SGNull

IF sgi = BcdDefs.SGNull -- search the BCD's file table (find a DEFs)
THEN { findSymbolFTI: PROC[ffth: BcdOps.FTHandle,
ffti: BcdDefs.FTIndex]
RETURNS[stop: BOOLEAN] =
{ IF ffth.version # utf.umid THEN RETURN[FALSE];
moduleName ← GetModuleName[ssb, ffth.name];
RETURN[TRUE]};

[] ← BcdOps.ProcessFiles[mappedBCD, findSymbolFTI];
IF moduleName = NIL THEN ERROR}
ELSE {sgb: Table.Base = LOOPHOLE[mappedBCD + mappedBCD.sgOffset, Table.Base];
ftb: Table.Base = LOOPHOLE[mappedBCD + mappedBCD.ftOffset, Table.Base];
fti: BcdDefs.FTIndex ← sgb[sgi].file;
IF fti = BcdDefs.FTSelf
THEN moduleName ← GetModuleName[ssb, mappedBCD.source]
-- NOTE module name vs file name
ELSE moduleName ← GetModuleName[ssb, ftb[fti].name]};
}; -- end ENABLE UNWIND

ReleaseRemoteBCD[mappedBCD];
sth ← RTSymbolsPrivate.GetSTHForModule
[stamp: refRemoteSTD.symbolsStamp,
moduleName: moduleName,
fileName: Rope.Concat[moduleName, ".bcd"]];
IF sth # nullHandle
THEN RTTypesBasicPrivate.MapStiStd[sti].sth ← sth;
}; -- end refRemoteSTD.bcd # NIL
}; -- end sth = nullHandle

IF sth = nullHandle THEN ERROR AMTypes.Error[reason: noSymbols];
stb ← RTSymbols.AcquireSTB[sth];
type ← RTSymbols.AcquireType[stb: stb, seIndex: utf.seIndex
! UNWIND => RTSymbols.ReleaseSTB[stb]];
RTSymbols.ReleaseSTB[stb];
}; -- end RemoteTypeToLocal

GetModuleName: PROC[ssb: BcdOps.NameString, n: BcdDefs.NameRecord] RETURNS[Rope.ROPE] =
{nameString: STRING = [100];
ssd: Strings.SubStringDescriptor ← [base: @ssb.string, offset: n, length: MIN[ssb.size[n], 100]];
nameString.length ← 0;
Strings.AppendSubString[nameString, @ssd];
FOR i: CARDINAL IN [0..nameString.length) DO
IF nameString[i] = '. THEN {nameString.length ← i; EXIT};
ENDLOOP;
RETURN[ConvertUnsafe.ToRope[LONG[nameString]]]};

-- raises RemoteError
ValidateRemoteFrame: PUBLIC PROC[remoteFH: RemoteFrameHandle] =
{frame: rep PrincOps.ControlLink = LOOPHOLE[remoteFH.fh, rep PrincOps.ControlLink];
IF frame.proc
OR frame.indirect
OR ~ValidRemoteGlobalFrame[[world: remoteFH.world,
worldIncarnation: CurrentIncarnation[remoteFH.world],
gfh: LOOPHOLE[GetRemoteFrameHeader[remoteFH].accesslink,
WorldVM.ShortAddress]]]
THEN ERROR RemoteError[invalidFrame]};

ValidRemoteGlobalFrame: PROC[gfh: RemoteGlobalFrameHandle]
RETURNS[ans: BOOLEANFALSE] =
{h: rep PrincOps.ControlLink = LOOPHOLE[gfh.gfh, rep PrincOps.ControlLink];
ans ← NOT h.proc AND NOT h.indirect AND InRemoteGFT[gfh ! ANY => CONTINUE]};

InRemoteGFT: PROC[gfh: RemoteGlobalFrameHandle] RETURNS[BOOLEAN] =
{gftLength: CARDINAL;
world: World = gfh.world;

{ ENABLE UNWIND => Unlock[world];
Lock[world];

gftLength ← Read[world: world,
addr: Long[world: world,
addr: LOOPHOLE[SDDefs.SD + SDDefs.sGFTLength,
ShortAddress]]];
FOR k: CARDINAL IN [1..gftLength) DO
IF GetRemoteGFHandle[world: world, gfi: k] = gfh
AND GetRemoteGFHeader[gfh].gfi = k
THEN {Unlock[world]; RETURN[TRUE]};
ENDLOOP;
Unlock[world];
RETURN[FALSE];
}};

-- NOTE copies the AGCState (but not MapPiRce and MapOiOe)
WorldRoot: PROC[world: World] RETURNS[ans: REF RTRefCounts.AGCState] =
{ans ← z.NEW[RTRefCounts.AGCState];
CopyRead[world: world,
from: LOOPHOLE[LongRead[world: world,
addr: Long[world: world,
addr: LOOPHOLE[SDDefs.SD + RTSD.sRTState,
ShortAddress]]],
Address],
nwords: SIZE[RTRefCounts.AGCState],
to: LOOPHOLE[ans, LONG POINTER]
];
};

-- NOTE
WorldMapStiStdEntry: PROC[world: World, sti: RTTypesBasicPrivate.SymbolTableIndex]
RETURNS[ans: REF RTTypesBasicPrivate.STDesc] =
{ans ← z.NEW[RTTypesBasicPrivate.STDesc];
CopyRead[ world: world,
from: LOOPHOLE
[LongRead
[world: world,
addr: LOOPHOLE[LongRead[world: world,
addr: Long[world: world,
addr: LOOPHOLE
[SDDefs.SD
+ RTSD.sFirstCedarFree,
ShortAddress]]],
Address] -- MapStiStd
+ SIZE[RTTypesBasicPrivate.RMapStiStd[0]]
+ SIZE[RTTypesBasicPrivate.PSTDesc] * sti],
Address],
nwords: SIZE[RTTypesBasicPrivate.STDesc],
to: LOOPHOLE[ans, LONG POINTER]
];
};

GetRemotePc: PUBLIC PROC[gf: RemoteGlobalFrameHandle, i: EVRange]
RETURNS [PrincOps.BytePC] =
{codeBase: RemotePointer -- LONG POINTER TO PrincOps.CSegPrefix
= GetRemoteEntryTableBase[gf];
wpc: CARDINAL ← Read[world: gf.world,
addr: LOOPHOLE[codeBase.ptr, Address]
+ SIZE[PrincOps.CSegPrefix]
+ i*SIZE[PrincOps.EntryVectorItem]];
RETURN[LOOPHOLE[wpc*2, PrincOps.BytePC]]};

GetRemoteEntryTableBase: PROC [gfh: RemoteGlobalFrameHandle]
RETURNS [RemotePointer] =
{c: PrincOps.FrameCodeBase ← GetRemoteGFHeader[gfh].code;
c.out ← FALSE;
RETURN[[world: gfh.world,
worldIncarnation: CurrentIncarnation[gfh.world],
ptr: LOOPHOLE[c, Address]]];
};

-- raises typeFault
UnwindRemoteIndirectProcDesc: PUBLIC PROC[pd: RemotePD] RETURNS[RemotePD] =
{icl: PrincOps.ControlLink ← LOOPHOLE[pd.pd, PrincOps.ControlLink];
world: World = pd.world;

IF icl = PrincOps.NullLink OR icl = PrincOps.UnboundLink
THEN RETURN[nilRemotePD];

{ ENABLE UNWIND => Unlock[world];
Lock[world];

UNTIL icl.proc DO
IF icl.indirect
THEN icl ← LOOPHOLE[Read[world: world,
addr: Long[world: world,
addr: LOOPHOLE[icl.link, ShortAddress]]],
PrincOps.ControlLink]
ELSE ERROR Error[reason: typeFault, type: nullType]
ENDLOOP;
Unlock[world];
RETURN[[world: world, worldIncarnation: CurrentIncarnation[world], pd: icl]]}};

-- break up and MOVE
AcquireCBTHandleFromRemotePD
: PUBLIC PROC[pd: RemotePD]
RETURNS[stb: SymbolTableBase, cbti: CallableBodyIndex] =
{ item: PrincOpsRuntime.GFTItem;
world: World = pd.world;

{ ENABLE UNWIND => Unlock[world];
Lock[world];

pd ← UnwindRemoteIndirectProcDesc[pd];
CopyRead[world: world,
from: Long[world: world,
addr: LOOPHOLE[PrincOpsRuntime.GFT
+ (LOOPHOLE[pd.pd, PrincOps.ProcDesc].gfi
* SIZE[PrincOpsRuntime.GFTItem]),
ShortAddress]],
nwords: SIZE[PrincOpsRuntime.GFTItem],
to: @item
];
stb ← AcquireSTBFromRemoteGFH
[GetRemoteGFHandle[world: world,
gfi: LOOPHOLE[pd.pd, PrincOps.ProcDesc].gfi]];
cbti ← RTTypesPrivate.GetCBTI
[stb,
item.epbias * PrincOps.EPRange
+ LOOPHOLE[pd.pd, PrincOps.ProcDesc].ep];
Unlock[world]}};

GetRemoteGFHandle: PUBLIC PROC[world: World, gfi: PrincOps.GFTIndex]
RETURNS[RemoteGlobalFrameHandle] =
{item: PrincOpsRuntime.GFTItem;

{ ENABLE UNWIND => Unlock[world];
Lock[world];

CopyRead[world: world,
from: Long[world: world,
addr: LOOPHOLE[PrincOpsRuntime.GFT
+ gfi * SIZE[PrincOpsRuntime.GFTItem],
ShortAddress]],
nwords: SIZE[PrincOpsRuntime.GFTItem],
to: @item
];
Unlock[world];
RETURN[[world: world,
worldIncarnation: CurrentIncarnation[world],
gfh: LOOPHOLE[PrincOpsRuntime.GetFrame[item], ShortAddress]]]};
};

GetRemoteGFHeader: PUBLIC PROC[gfh: RemoteGlobalFrameHandle]
RETURNS[ans: REF PrincOps.GlobalFrame] =
{world: World = gfh.world;

ans ← z.NEW[PrincOps.GlobalFrame];
{ ENABLE UNWIND => Unlock[world];
Lock[world];

CopyRead[world: world,
from: Long[world: world,
addr: LOOPHOLE[gfh.gfh, ShortAddress]],
nwords: SIZE[PrincOps.GlobalFrame],
to: LOOPHOLE[ans, LONG POINTER]];
Unlock[world];
}};

GetRemoteFrameHeader: PUBLIC PROC[fh: RemoteFrameHandle]
RETURNS[ans: REF PrincOps.Frame] =
{world: World = fh.world;
ans ← z.NEW[PrincOps.Frame];

{ ENABLE UNWIND => Unlock[world];
Lock[world];

CopyRead[world: world,
from: Long[world: world,
addr: LOOPHOLE[fh.fh, ShortAddress]],
nwords: SIZE[PrincOps.GlobalFrame],
to: LOOPHOLE[ans, LONG POINTER]];
Unlock[world];
}};

GetRemoteReferentType: PUBLIC PROC[remoteRef: RemoteRef]
RETURNS[type: Type--valid locally--] =
{ mz: RTZones.ZoneFinger;
world: World = remoteRef.world;

IF remoteRef.ref = 0 THEN RETURN[nullType];

{ ENABLE UNWIND => Unlock[world];
Lock[world];

mz ← RemoteMapRefZf[remoteRef];
WITH mz: mz SELECT FROM
prefixed => type ← GetRemotePrefixedType[remoteRef];
sub => type ← GetRemoteSzType[world: world, szi: mz.szi];
ENDCASE => ERROR;
Unlock[world]}};

RemoteMapRefZf: PROC[remoteRef: RemoteRef]
RETURNS[zf: RTZones.ZoneFinger] =
{qi: RTQuanta.QuantumIndex = RTZones.MapPtrQ[LOOPHOLE[remoteRef.ref, LONG POINTER]];
remoteMapQZf: RTZones.TMapQZf = WorldRoot[remoteRef.world].GCStateBasic.mapQZf;
CopyRead[world: remoteRef.world,
from: LOOPHOLE[remoteMapQZf
+ SIZE[RTZones.RMapQZf[0]]
+ qi*SIZE[RTZones.ZoneFinger],
Address],
nwords: SIZE[RTZones.ZoneFinger],
to: @zf];
};

GetRemotePrefixedType: PROC[remoteRef: RemoteRef]
RETURNS[Type--valid locally--] =
{remoteType: CARDINAL;
NHdr: inuse RTZones.NodeHeader;
CopyRead[world: remoteRef.world,
from: remoteRef.ref - RTZones.sizeNd,
nwords: SIZE[inuse RTZones.NodeHeader],
to: @NHdr];
remoteType ← NHdr.type;
RETURN[RemoteTypeToLocal[world: remoteRef.world, remoteType: remoteType]];
};

GetRemoteSzType: PROC[world: World, szi: RTZones.SubZoneIndex]
RETURNS[Type--valid locally--] =
{remoteType: CARDINAL;
szrp: Address; -- SubZone
szRec: RTZones.SubZoneRec;
szrp ← LongRead
[world: world,
addr: LOOPHOLE[WorldRoot[world].GCStateBasic.mapSziSz
+ SIZE[RTZones.RMapSziSz[0]]
+ szi*SIZE[RTZones.SubZone],
Address]];
CopyRead[world: world,
from: szrp,
nwords: SIZE[RTZones.SubZoneRec],
to: @szRec];
remoteType ← szRec.type;
RETURN[RemoteTypeToLocal[world: world, remoteType: remoteType]];
};

-- MOVE
GetRemoteProcedureType
: PUBLIC PROC[remotePD: RemotePD]
RETURNS[type: Type--valid locally--] =
{ stb: SymbolTableBase;
cbti: CallableBodyIndex;
[stb, cbti] ← AcquireCBTHandleFromRemotePD[remotePD];
type ← RTSymbols.AcquireType[stb, stb.bb[cbti].ioType ! UNWIND => ReleaseSTB[stb]];
ReleaseSTB[stb]};

-- break up and MOVE
GetRemoteSignalType
: PUBLIC PROC[remoteSED: RemoteSED]
RETURNS[type: Type--valid locally--] =
{ gfh: RemoteGlobalFrameHandle ← nilRemoteGlobalFrameHandle;
item: PrincOpsRuntime.GFTItem;
stb: SymbolTableBase;
world: World = remoteSED.world;

proc: PROC[stb: SymbolTableBase, isei: SymbolIdIndex]
RETURNS[stop: BOOLEAN] =
{ tsei: SymbolIndex;
IF stb.seb[isei].constant
AND stb.seb[tsei ← stb.UnderType[stb.seb[isei].idType]].typeTag = transfer
AND (SELECT stb.XferMode[tsei] FROM
error, signal => TRUE,
ENDCASE => FALSE)
AND CARDINAL[item.epbias * PrincOps.EPRange
+ LOOPHOLE[remoteSED.sed, PrincOps.SignalDesc].ep] =
STInfoToEPN[LOOPHOLE[stb.seb[isei].idValue, PrincOps.SignalDesc]]
THEN {type ← RTSymbols.AcquireType[stb, stb.seb[isei].idType];
RETURN[TRUE]}
ELSE RETURN[FALSE]};

-- START GetRemoteSignalType HERE

IF LOOPHOLE[remoteSED.sed, CARDINAL] = 177777B -- ERROR
OR LOOPHOLE[remoteSED.sed, CARDINAL] = LOOPHOLE[UNWIND, CARDINAL]
OR LOOPHOLE[remoteSED.sed, CARDINAL] = LOOPHOLE[ABORTED, CARDINAL]
THEN RETURN[CODE[ERROR]];

{ ENABLE UNWIND => Unlock[world];
Lock[world];

gfh ← GetRemoteGFHandle[world: world,
gfi: LOOPHOLE[remoteSED.sed,
PrincOps.SignalDesc].gfi];
stb ← AcquireSTBFromRemoteGFH[gfh];
CopyRead[world: world,
from: Long[world: world,
addr: LOOPHOLE[PrincOpsRuntime.GFT
+ LOOPHOLE[remoteSED.sed,
PrincOps.SignalDesc].gfi
* SIZE[PrincOpsRuntime.GFTItem],
ShortAddress]],
nwords: SIZE[PrincOpsRuntime.GFTItem],
to: @item
];
type ← nullType;
[] ← RTSymbols.EnumerateRecordIseis[stb, stb.bb[rootBodyIndex].type, proc
! UNWIND => ReleaseSTB[stb]];
ReleaseSTB[stb];
Unlock[world]}};

-- break up and MOVE
IsRemoteCatchFrame
: PUBLIC PROC[remoteFrameHandle: RemoteFrameHandle,
bti: BodyIndex]
RETURNS [isCatchFrame: BOOLEANTRUE,
revBti: BodyIndex] =
{world: World = remoteFrameHandle.world;

{ ENABLE UNWIND => Unlock[world];
Lock[world];

revBti ← bti;
ValidateRemoteFrame[remoteFrameHandle ! ANY => GOTO notCatch];
{ -- return FALSE if input invalid
nextFrame: RemoteFrameHandle
← [world: remoteFrameHandle.world,
worldIncarnation: CurrentIncarnation[remoteFrameHandle.world],
fh: LOOPHOLE[GetRemoteFrameHeader[remoteFrameHandle].returnlink.frame,
ShortAddress]];
nextFHdr: REF PrincOps.Frame;

ValidateRemoteFrame[nextFrame ! ANY => GOTO notCatch];
-- return FALSE if no valid calling frame
nextFHdr ← GetRemoteFrameHeader[nextFrame];
IF nextFHdr.accesslink # RemoteSigGF[remoteFrameHandle.world]
THEN GOTO notCatch;

{ -- return FALSE if caller not signaller
L0Frame: RemoteFrameHandle
← [world: remoteFrameHandle.world,
worldIncarnation: CurrentIncarnation[remoteFrameHandle.world],
fh: LOOPHOLE[GetRemoteFrameHeader[remoteFrameHandle].staticlink
- PrincOps.localbase, ShortAddress]];
-- L0Frame is the frame that encloses the catch frame
ValidateRemoteFrame[L0Frame ! ANY => GOTO notCatch];
-- return FALSE if static frame not valid (??)
IF GetRemoteFrameHeader[remoteFrameHandle].accesslink
# GetRemoteFrameHeader[L0Frame].accesslink
THEN GOTO notCatch;
IF ~nextFHdr.mark THEN GOTO notCatch; -- calling frame not the signaller
{ -- detect situation where catch frame has no locals, thus no body table.
tr: REF TypedVariableRec = NARROW[TVForRemoteFrame[L0Frame],
REF TypedVariableRec];
WITH hd: tr.head SELECT FROM
remoteFH => IF bti = hd.bti THEN revBti ← nullBodyIndex;
ENDCASE => ERROR
};
Unlock[world];
}
};
EXITS
notCatch => {Unlock[world];
isCatchFrame ← FALSE}}};

RemoteSigGF: PROC[world: World] RETURNS[PrincOps.GlobalFrameHandle] =
{ item: PrincOpsRuntime.GFTItem;
pd: PrincOps.ProcDesc
= LOOPHOLE[Read[world: world,
addr: Long[world: world,
addr: LOOPHOLE[SDDefs.SD + SDDefs.sSignal,
ShortAddress]]],
PrincOps.ProcDesc];
CopyRead[world: world,
from: Long[world: world,
addr: LOOPHOLE[PrincOpsRuntime.GFT
+ pd.gfi * SIZE[PrincOpsRuntime.GFTItem],
ShortAddress]],
nwords: SIZE[PrincOpsRuntime.GFTItem],
to: @item
];
RETURN[PrincOpsRuntime.GetFrame[item]]};


-- MOVE
AcquireBTIFromRemoteFH
: PUBLIC PROC[remoteFrameHandle: RemoteFrameHandle,
contextPC: BOOL]
RETURNS[bti: BodyIndex] =
{stb: SymbolTableBase;
world: World = remoteFrameHandle.world;
Lock[world];
[stb, bti] ← AcquireBTHandleFromRemoteFH[remoteFrameHandle, contextPC
! ANY => GOTO nope];
Unlock[world];
ReleaseSTB[stb];
EXITS nope => {Unlock[remoteFrameHandle.world]; RETURN[nullBodyIndex]}};

-- MOVE
AcquireBTHandleFromRemoteFH
: PROC[fh: RemoteFrameHandle, contextPC: BOOL]
RETURNS[stb: SymbolTableBase, bti: BodyIndex] =
{ start: PrincOps.BytePC;
epn: CARDINAL;
remoteFrame: REF PrincOps.Frame = GetRemoteFrameHeader[fh];
framePC: PrincOps.BytePC = [(remoteFrame.pc - (IF contextPC THEN 0 ELSE 1))];

stb ← AcquireSTBFromRemoteGFH[[world: fh.world,
worldIncarnation: CurrentIncarnation[fh.world],
gfh: LOOPHOLE[remoteFrame.accesslink, ShortAddress]]];

[epn, start] ← GetRemoteEp[pc: framePC,
gf: [world: fh.world,
worldIncarnation: CurrentIncarnation[fh.world],
gfh: LOOPHOLE[remoteFrame.accesslink, ShortAddress]],
stb: stb
! UNWIND => ReleaseSTB[stb]];
bti ← RTTypesPrivate.ConvertCbti[base: stb,
pc: framePC,
start: start,
lastBti: RTTypesPrivate.GetCBTI[stb, epn]
! UNWIND => ReleaseSTB[stb]];
IF bti = rootBodyIndex THEN bti ← nullBodyIndex};

-- MOVE
GetRemoteEp
: PUBLIC PROC [pc: PrincOps.BytePC,
gf: RemoteGlobalFrameHandle,
stb: SymbolTableBase]
RETURNS [ep: EVRange, start: PrincOps.BytePC] =
{ FindMaxEI: PROC RETURNS [max: EVRange] =
{ GetMax: PROC [bti: BodyIndex] RETURNS [stop: BOOLEAN] =
{WITH stb.bb[bti] SELECT FROM
Callable => IF ~inline THEN max ← MAX[max, entryIndex];
ENDCASE;
RETURN[FALSE]};
max ← 0;
[] ← stb.EnumerateBodies[rootBodyIndex, GetMax]};

-- body of GetEp begins here
diff: CARDINALLAST[CARDINAL];
anyProcedure: BOOLEANFALSE;
FOR i: EVRange IN [0..FindMaxEI[]] DO
last: PrincOps.BytePC ← GetRemotePc[gf, i];
IF Card[last] > Card[pc] THEN LOOP;
IF Card[pc] - Card[last] > diff THEN LOOP;
diff ← Card[pc] - Card[last];
ep ← i; start ← last;
anyProcedure ← TRUE;
ENDLOOP;
IF ~anyProcedure THEN ERROR; -- SIGNAL NotInAnyProcedure;
RETURN};

-- MOVE
RemotePDToName
: PUBLIC PROC[pd: RemotePD] RETURNS[ans: Rope.ROPE] =
{ stb: SymbolTableBase;
cbti: CallableBodyIndex;
pd ← UnwindRemoteIndirectProcDesc[pd];
IF pd.pd = 0 THEN RETURN[NIL];
[stb, cbti] ← AcquireCBTHandleFromRemotePD[pd];
ans ← RTSymbols.AcquireRope[stb, stb.seb[stb.bb[cbti].id].hash ! UNWIND => ReleaseSTB[stb]];
ReleaseSTB[stb];
RETURN[ans]};

-- MOVE
RemoteSEDToName
: PUBLIC PROC[sed: RemoteSED] RETURNS[ans: Rope.ROPENIL] =
{ item: PrincOpsRuntime.GFTItem;
gfh: RemoteGlobalFrameHandle ← nilRemoteGlobalFrameHandle;
stb: SymbolTableBase;
world: World = sed.world;

proc: PROC[stb: SymbolTableBase, isei: SymbolIdIndex]
RETURNS[stop: BOOLEAN] =
{ tsei: SymbolIndex;
IF stb.seb[isei].constant
AND stb.seb[tsei ← stb.UnderType[stb.seb[isei].idType]].typeTag = transfer
AND (SELECT stb.XferMode[tsei] FROM
error, signal => TRUE,
ENDCASE => FALSE)
AND CARDINAL[item.epbias * PrincOps.EPRange
+ LOOPHOLE[sed.sed, PrincOps.SignalDesc].ep] =
STInfoToEPN[LOOPHOLE[stb.seb[isei].idValue, PrincOps.SignalDesc]]
THEN {ans ← RTSymbols.AcquireRope[stb, stb.seb[isei].hash]; RETURN[TRUE]}
ELSE RETURN[FALSE]};

IF sed.sed = PrincOps.NullLink OR sed.sed = PrincOps.UnboundLink
THEN RETURN[NIL];

{ ENABLE UNWIND => Unlock[world];
sei: SymbolRecordIndex;
Lock[world];
gfh ← GetRemoteGFHandle[world: world,
gfi: LOOPHOLE[sed.sed, PrincOps.SignalDesc].gfi];
CopyRead[world: world,
from: Long[world: world,
addr: LOOPHOLE[PrincOpsRuntime.GFT
+ LOOPHOLE[sed.sed, PrincOps.SignalDesc].gfi
* SIZE[PrincOpsRuntime.GFTItem],
ShortAddress]],
nwords: SIZE[PrincOpsRuntime.GFTItem],
to: @item];
stb ← AcquireSTBFromRemoteGFH[gfh];
sei ← stb.bb[rootBodyIndex].type;
IF sei # nullSymbolIndex THEN
[] ← RTSymbols.EnumerateRecordIseis[stb, sei, proc ! UNWIND => ReleaseSTB[stb]];
ReleaseSTB[stb];
Unlock[world]}};

RemoteStoreWords: PUBLIC PROC[from: LONG POINTER,
to: RemotePointer,
nWords: NAT] =
{ENABLE UNWIND => Unlock[to.world];
Lock[to.world];
CopyWrite[world: to.world, from: from, nwords: nWords, to: to.ptr];
Unlock[to.world]};

RemoteStoreWord: PUBLIC PROC[to: RemotePointer, value: CARDINAL] =
{ENABLE UNWIND => Unlock[to.world];
Lock[to.world];
Write[world: to.world, addr: to.ptr, value: value];
Unlock[to.world]};

RemoteStoreDoubleWord: PUBLIC PROC[to: RemotePointer, value: LONG CARDINAL] =
{ENABLE UNWIND => Unlock[to.world];
Lock[to.world];
LongWrite[world: to.world, addr: to.ptr, value: value];
Unlock[to.world]};

RemoteStoreFieldLong: PUBLIC PROC[ptr: RemotePointer,
field: RTCommon.Field,
value: LONG CARDINAL] =
{ENABLE UNWIND => Unlock[ptr.world];
val: LONG CARDINAL;
Lock[ptr.world];
val ← LongRead[world: ptr.world, addr: LOOPHOLE[ptr.ptr, Address]];
RTCommon.StoreFieldLong[ptr: @val, field: field, newValue: value];
LongWrite[world: ptr.world, addr: LOOPHOLE[ptr.ptr, Address], value: val];
Unlock[ptr.world];
};

GetRemoteWord: PUBLIC PROC[remotePointer: RemotePointer] RETURNS[ans: WORD] =
{ENABLE UNWIND => Unlock[remotePointer.world];
Lock[remotePointer.world];
ans ← Read[world: remotePointer.world, addr: remotePointer.ptr];
Unlock[remotePointer.world];
};

GetRemoteLC: PUBLIC PROC[remotePointer: RemotePointer] RETURNS[ans: LONG CARDINAL] =
{ENABLE UNWIND => Unlock[remotePointer.world];
Lock[remotePointer.world];
ans ← LongRead[world: remotePointer.world, addr: remotePointer.ptr];
Unlock[remotePointer.world];
};

GetRemoteWords: PUBLIC PROC[remotePointer: RemotePointer, nWords: NAT]
RETURNS[ans: WordSequence] =
{ENABLE UNWIND => Unlock[remotePointer.world];
Lock[remotePointer.world];
ans ← z.NEW[WordSequenceRecord[nWords]];
CopyRead[world: remotePointer.world,
from: remotePointer.ptr,
nwords: nWords,
to: LOOPHOLE[@ans[0], LONG POINTER]];
Unlock[remotePointer.world];
};

-- NOTE
RemoteSignalValues: PUBLIC PROC[tv: TypedVariable--catch Frame--]
RETURNS [message: CARDINAL, signal: PrincOps.SignalDesc, world: World] =
{rtr: REF TypedVariableRec ← NARROW[tv];
WITH th: rtr.head SELECT FROM
remoteFH =>
IF th.isCatchFrame
THEN {ENABLE UNWIND => Unlock[th.remoteFrameHandle.world];
remoteSignallerFrameHandle: RemoteFrameHandle ← nilRemoteFrameHandle;
Lock[th.remoteFrameHandle.world];

ValidateRemoteFrame[th.remoteFrameHandle];
remoteSignallerFrameHandle
← [world: th.remoteFrameHandle.world,
worldIncarnation: CurrentIncarnation[th.remoteFrameHandle.world],
fh: LOOPHOLE[GetRemoteFrameHeader
[th.remoteFrameHandle].returnlink,
ShortAddress]];
message ← Read[world: th.remoteFrameHandle.world,
addr: Long[world: th.remoteFrameHandle.world,
addr: remoteSignallerFrameHandle.fh
+ SignallerFrameMessageOffset]];
signal ← LOOPHOLE
[Read[world: th.remoteFrameHandle.world,
addr: Long[world: th.remoteFrameHandle.world,
addr: remoteSignallerFrameHandle.fh
+ SignallerFrameSignalOffset]],
PrincOps.SignalDesc];
Unlock[th.remoteFrameHandle.world];
RETURN}
ELSE ERROR Error[reason: typeFault, type: TVType[tv]];
ENDCASE => ERROR Error[reason: typeFault, type: TVType[tv]]};

STInfoToEPN: PROC[cl: PrincOps.SignalDesc] RETURNS[CARDINAL] =
{RETURN[(cl.gfi - 1) * PrincOps.EPRange + cl.ep]};

Card: PROC[pc: PrincOps.BytePC] RETURNS[CARDINAL] =
{RETURN[LOOPHOLE[pc, CARDINAL]]};

END.