Programming Note: Except for the restriction frames are contained entirely within a 64K bank, the maximum size of a frame is not specified by the architecture.
GlobalFrameHandle: TYPE = LONG POINTER TO GlobalVariables;
Figure 3.4 Global frame
The overhead words contain the flag bits trapxfers and codelinks used during control transfers (§9.3).
GlobalFrameBase: TYPE = LONG POINTER TO GlobalOverhead;
GlobalWord: TYPE = MACHINE DEPENDENT RECORD [
available(0: 0..13): [0..37777B],
trapxfers(0: 14..14): BOOLEAN,
codelinks(0: 15..15}: BOOLEAN];
GlobalOverhead: TYPE = MACHINE DEPENDENT RECORD [
available(0): UNSPECIFIED,
word(1): GlobalWord,
global(2): GlobalVariables];
Local Frames
The gloabllink points to the procedure's global frame index. It is used to gain access to the procedure's global variables.
Figure 5.3 Local frame
LocalOverhead: TYPE = MACHINE DEPENDENT RECORD [
word(0): LocalWord,
returnlink(1): ShortControlLink,
globalLink(2): GFTHandle,
pc(3): CARDINAL,
local(4): LocalVariables];
The global frame index of the current context is contained in the sixteen bit register GFI. Its value is obtained using LocalBase[LF].globallink.
GFI: GFTHandle;The address of the global frame of the current process is contained in the 32 bit register GF. Its value is obtained using GFT[GFI].globalFrame.
GF: GlobalFrameHandle;The address of the code segment of the current context is contained in the register CB (the code base, a long pointer). Its value is obtained using GFT[GFI].codebase.
LGAn: PROCEDURE[n: [0..1]] =
BEGIN
PushLong[GF + n];
END;
LGAB Long Global Address Byte
LGAB: PROCEDURE =
BEGIN
alpha: BYTE = GetCodeByte[];
PushLong[GF + alpha];
END;
LGAW Long Global Address Word
LGAW: PROCEDURE =
BEGIN
word: BYTE = GetCodeWord[];
PushLong[GF + word];
END;
LGn: PROCEDURE [n: [0..2]] =
BEGIN
Push[Fetch[GF + n]
];
END;
LGB Load Global Byte
LGB: PROCEDURE =
BEGIN
alpha: BYTE = GetCoodeByte[];
Push[Fetch[GF + alpha]
];
END;
LGDn Load Global Double n
LGDn: PROCEDURE [n: [0..2]] =
BEGIN
Push[Fetch[GF + n]
];
Push[Fetch[GF + n + 1]
];
END;
LGDB Load Global Double Byte
LGDB: PROCEDURE =
BEGIN
alpha: BYTE = GetCodeByte[];
Push[Fetch[GF + alpha]
];
Push[Fetch[GF + alpha + 1]
];
END;
SGB: PROCEDURE =
BEGIN
alpha: BYTE = GetCodeByte[];
Store[GF + alpha]
Pop[];
END;
SGDB Store Global Double Byte
SGDB: PROCEDURE =
BEGIN
alpha: BYTE = GetCodeByte[];
Store[GF + alpha + 1]
Pop[];
Store[GF + alpha]
Pop[];
END;
RGIP: PROCEDURE =
BEGIN
pair: NibblePair
GetCodeByte[];
ptr: POINTER = Fetch[GF + pair.left]
;
Push[FetchMds[ptr + pair.right]
];
END;
RGILP Read Global Indirect Long Pair
RGILP: PROCEDURE =
BEGIN
pair: NibblePair = GetCodeByte[];
ptr: LONG POINTER = ReadDbl[GF + pair.left];
Push[Fetch[ptr + LONG[pair.right]]
];
END;
LinkType: TYPE = {
frame, oldProcedure, indirect, newProcedure};
ControlLinkType: PROCEDURE[link: ControlLink]
RETURNS[LinkType] =
BEGIN
cl: TaggedControlLink = LOOPHOLE[link];
RETURN[
SELECT cl.tag FROM
0 => frame,
1 => oldProcedure,
2 => indirect,
ENDCASE => newProcedure];
END;
NewProdDesc: TYPE = MACHINE DEPENDENT RECORD [
taggedGFI(0): UNSPECIFIED,
pc(1): CARDINAL];
MakeNewProcDesc: PROCEDURE [link: ControlLink]
RETURNS [NewProdDesc] =
BEGIN
IF ControlLinkType[link] # newProcedure THEN ERROR;
RETURN[LOOPHOLE[LINK]];
END;
The taggedGFI is the global frame index or'ed with 3. The new value of GFI is computed from taggedGFI. The global frame and the code base are then obtained from the global frame table using GFI.
proc: ProcDesc; ... GFIAnd[proc.taggedGF, 177774B]; GF
ReadDbl[@GFT[GFI].global Frame]; CB
ReadDbl[@GFT[GFI].codebase]; ...
GFT: LONG POINTER TO GlobalFrameTable =
LOOPHOLE[mGFT];
GlobalFrameTable: TYPE - LONG BASE POINTER TO
ARRAY GFIndex OF GFTItem;
GFTIndex: TYPE = [0..16348];
GFTHandle: TYPE = GlobalFrameTable RELATIVE POINTER TO GFTItem;
GFTItem: TYPE = MACHINE DEPENDENT ERECORD[
globalFrame(0): GlobalFramehandle,
codebase(2): LONG POINTER TO CodeSegment];
Programming Note: By convention, the first entry of the GFT is not
used. Procedure descriptors with a gfi of zero will always result in an
UnboundTrap.
The GFT can be shorter than the maximum size specified above. In this case, it is the responsibility of the programmer to ensure that no gfi's that exceed the size of the GFT are used; the processor does no dynamic bounds checking on gfi's.
DESC: PROCEDURE =
BEGIN
word: UNSPECIFIED = GetCodeWord[];
Push[Or[GFI, 3]];
Push[word];
END;
SELECT ControlLinkType[nDst] FROM
newProcedure =>
BEGIN
word: BytePair;
proc: NewProcDesc = MakeNewProcDesc[nDst];
GFI
AND[proc.tagedGF, 177774B];
IF GFI = LOOPHOLE[0] THEN UnboundTrap[dst];
GF
ReadDbl[@GFT[GFI].globalFrame];
CB
ReadDbl[@GFT[GFI].codebase];
IF Odd[LowHalf[CB]] THEN CodeTrap[GFI];
nPC
proc.pc;
if nPC = 0 THEN UnboundTrap[dst];
word
ReadCode[nPC/2];
nLF
Alloc[
IF nPC MOD 2 = 0 THEN word.left
ELSE word.right];
nPC
nPC + 1;
StoreMds[@LocalBase[nLF].globallink]
GFI;
StoreMds[@LocalBase[nLF].returnlink]
src;
END
frame =>
BEGIN
frame: FrameLink = MakeFrameLink[nDst];
IF frame = LOOPHOLE[0] THEN ControlTrap[src];
nLF
frame;
GFI
FetchMds[@LocalBase[nLF].globallink]
;
IF GFI = LOOPHOLE[0] THEN UnboundTrap[dst];
GF
ReadDbl[@GFT[GFI].globalFrame];
CB
ReadDbl[@GFT[GFI].codebase];
IF Odd[LowHalf[CB]] THEN CodeTrap[GFI];
...
LFC: PROCEDURE =
BEGIN
...
StoreMds[@LocalBase[nLF].globallink]
GFI;
...
FetchLink: PROCEDURE [offset: BYTE] RETURNS [ControlLink] =
BEGIN
word: GlobalWord = Fetch[@GlobalBase[GF].work]
;
RETURN [
IF word.codelinks THEN ReadDbl[cb-LONG[(offset+1)*2]]
ELSE ReadDbl[GlobalBase[GF]-(offset+1)*2]];
END;
Design Note: If the links are stored in the code segment, they must be contain in the same 64K bank as the code base. This ensures that the calculation of the address of the link will not underflow in the low-order word or cause a borrow from the high-order word. Since frames are always completely contain in a 64K bank, this calculation is also accurate if the links are store in the global frame.
CodeTrap: PROCEDURE [gfi: GFTHandle] = {
TrapOne[@SD[sCodeTrap], gfi]};
mGFT: LONG CARDINAL = 400000B;
LGAB Long Global Address Byte
LGAW Long Global Address Word
DESC Descriptor