MDAssignRingImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Willie-Sue, May 8, 1986 5:08:30 pm PDT
taken from MDassign.bcpl
DIRECTORY
Basics,
IO,
Rope,
VM USING [AddressForPageNumber, PagesForWords, SimpleAllocate],
MDADefs,
MDDefs,
MDGlobalVars,
MDOps,
MDUtils;
MDAssignRingImpl: CEDAR PROGRAM
IMPORTS
Basics, IO, VM,
MDGlobalVars, MDOps, MDUtils
EXPORTS
MDOps, MDUtils
= BEGIN OPEN MDADefs, MDDefs, MDGlobalVars;
firstDoradoPage: DoradoPage;   -- keep track of the first one??
pageTab: ARRAY [0..nPages) OF DoradoPage;  -- pointers to pages
aTab: ARRAY [0..DoradoPageSize) OF CARDINAL;
firstPage, lastPage, thisPage: CARDINAL;
fullMsg, failMsg: ROPE;
totalMsg: ROPE = "%6Bb instructions in %g";
wBuf: WorkingBuffer;
wRing: DoradoPage ← NEW[DoradoPageRec ← [0, 0, 0, FALSE, 0, NEW[PbtRec]] ];
NotUnique: SIGNAL = CODE;
DoAssignments: PUBLIC PROC RETURNS[ok: BOOL] = TRUSTED {
groups: ARRAY [0..11) OF CARDINALALL[7777B];
gAbs, gGlobals, gIFUE: INTEGER ← -1; last three entries in groups (8, 9, 10)
MDOps.Report[infoOnly, "Assigning locations...\n"];
IF firstDoradoPage = NIL THEN {
buf: LONG POINTER
VM.AddressForPageNumber[VM.SimpleAllocate[VM.PagesForWords[lWBuf]].page];
wBuf ← LOOPHOLE[buf];
FOR i: CARDINAL IN [0..nPages) DO  -- this won't change
pageTab[i] ← NEW[DoradoPageRec];
pageTab[i].absPage ← i * DoradoPageSize;
pageTab[i].bt ← NEW[PbtRec ← ALL[FALSE]];
ENDLOOP;
firstDoradoPage ← pageTab[0];
buf ← VM.AddressForPageNumber[VM.SimpleAllocate[VM.PagesForWords[IMsize]].page];
realToImagAddrs ← LOOPHOLE[buf];
buf ← VM.AddressForPageNumber[VM.SimpleAllocate[VM.PagesForWords[IMsize]].page];
imagToRealAddrs ← LOOPHOLE[buf];
};
FOR i: CARDINAL IN [0..MDDefs.IMsize) DO
(realToImagAddrs+i)^ ← 177777B;
(imagToRealAddrs+i)^ ← 177777B;
ENDLOOP;
BEGIN  -- mark used pages from IMLocked in pageBase data structure
thisAddr: CARDINAL ← 0;
used: CARDINAL ← 0;
FOR i: CARDINAL IN [0..nPages) DO
dp: DoradoPage = pageTab[i];
FOR j: CARDINAL IN [0..DoradoPageSize) DO
IF (dp.bt[j] ← imLocked[thisAddr]) THEN used ← used + 1;
thisAddr ← thisAddr + 1;
ENDLOOP;
dp.used ← used;
dp.global ← dp.ifuEntry ← 0;
dp.slow← FALSE;
ENDLOOP;
END;
FOR i: CARDINAL DECREASING IN [0.. nInstructions) DO-- so lists are in ascending order
imPtr: IMRecordPtr = MDUtils.IMPtr[i];
groupIndex: CARDINAL;
imPtr.word2.branchesAndMarked ← 0;
SELECT TRUE FROM
imPtr.word0.onPageAndPlaced = 1 => groupIndex ← 8;
imPtr.word0.globalAndIFUSeq = 1 => groupIndex ← 9;
imPtr.word0.brkPAndIFUE = 1 => groupIndex ← 10;
ENDCASE => {
IF imPtr.links.aLinked # 0 THEN groupIndex ← 4 ELSE groupIndex ← 0; -- alists first
IF Basics.BITAND[Basics.BITNOT[calledMask], imPtr.mask] = 0 THEN
groupIndex ← groupIndex + 2;  -- Then subroutine entries
IF imPtr.word2.W2AddrAndbLink # i THEN
groupIndex ← groupIndex + 1;  -- Then non-unit rings
};
imPtr.word1.W1AddrAndGroupLink ← groups[groupIndex];
groups[groupIndex] ← i;
ENDLOOP;
Process pages with known page number
BEGIN
gAbs: CARDINAL ← groups[8];
imPtr: IMRecordPtr;
pAddr: CARDINAL;
failMsg ← "Can't assign absolutely placed ring";
fullMsg ← "Too many instructions on page";
totalCount ← 0;
DO
i, pageNum: CARDINAL;
IF gAbs = 7777B THEN EXIT;
imPtr ← MDUtils.IMPtr[i ← gAbs];
gAbs ← imPtr.word1.W1AddrAndGroupLink;  -- next in list
IF imPtr.word2.branchesAndMarked = 1 THEN LOOP; -- already processed
pageNum ← imPtr.word0.W0Addr/DoradoPageSize;
pAddr ← pageNum * DoradoPageSize;
totalCount ← CollectRing[i, wBuf, wRing] + totalCount;
IF (wRing.global # 0) AND (MDUtils.CardAnd[pAddr, globalZero] # 0) THEN
MDOps.Report[passFatal, "Can't have GLOBAL on page %b\n", IO.card[pAddr]];
IF (wRing.ifuEntry # 0) AND (MDUtils.CardAnd[pAddr, ifuZero] # 0) THEN
MDOps.Report[passFatal, "Can't have IFU on page %b\n", IO.card[pAddr]];
PageLimits[pageNum, pageNum];
AssignPage[wBuf, wRing];
ENDLOOP;
END;
IF totalCount # 0 THEN
MDOps.Report[infoOnly,
totalMsg, IO.card[totalCount], IO.rope["rings involving ONPAGE or AT\n"] ];
Process global and IFU entry rings
BEGIN
gGlobal: CARDINAL ← groups[9];
gIFUE: CARDINAL ← groups[10];
imPtr: IMRecordPtr;
failMsg ← "Can't assign GLOBAL ring";
fullMsg ← NIL;
PageLimits[0, nGlobalPages-1];
totalCount ← 0;
DO
i: CARDINAL;
IF gGlobal = 7777B THEN EXIT;
imPtr ← MDUtils.IMPtr[i ← gGlobal];
gGlobal ← imPtr.word1.W1AddrAndGroupLink;  -- next in list
IF imPtr.word2.branchesAndMarked = 1 THEN LOOP; -- already processed
totalCount ← CollectRing[i, wBuf, wRing] + totalCount;
AssignPage[wBuf, wRing];
ENDLOOP;
IF totalCount # 0 THEN
MDOps.Report[infoOnly,
totalMsg, IO.card[totalCount], IO.rope["rings with a GLOBAL\n"] ];
PageLimits[0, nIfuPages-1];
failMsg ← "Can't assign IFU entry ring";
totalCount ← 0;
DO
i: CARDINAL;
IF gIFUE = 7777B THEN EXIT;
imPtr ← MDUtils.IMPtr[i ← gIFUE];
gIFUE ← imPtr.word1.W1AddrAndGroupLink;  -- next in list
IF imPtr.word2.branchesAndMarked = 1 THEN LOOP; -- already processed
totalCount ← CollectRing[i, wBuf, wRing] + totalCount;
AssignPage[wBuf, wRing];
ENDLOOP;
IF totalCount # 0 THEN
MDOps.Report[infoOnly,
totalMsg, IO.card[totalCount], IO.rope["rings with an IFU entry\n"] ];
END;
Process other rings
PageLimits[0, nPages - 1];
failMsg ← "Ran out of pages trying to assign ring";
FOR a: CARDINAL DECREASING IN [0..8) DO
i: CARDINAL ← groups[a];
thisMsg: ROPE;
totalCount ← 0;
DO
imPtr: IMRecordPtr;
IF i = 7777B THEN EXIT;
imPtr ← MDUtils.IMPtr[i];
IF imPtr.word2.branchesAndMarked = 0 THEN { -- not yet seen
totalCount ← CollectRing[i, wBuf, wRing] + totalCount;
AssignPage[wBuf, wRing];
};
i ← imPtr.word1.W1AddrAndGroupLink;
ENDLOOP;
thisMsg ← SELECT a FROM
7 => "CALLed rings including a CALL/conditional\n",
6 => NIL,    -- Can't have a 1-instruction alist
5 => "other rings including a CALL/conditional\n",
4 => NIL,    -- Can't have a 1-instruction alist
3 => "CALLed multi-instruction rings\n",
2 => "CALLed 1-instruction rings\n",
1 => "other multi-instruction rings\n",
0 => "other 1-instruction rings\n",
ENDCASE => NIL;
IF totalCount # 0 THEN
MDOps.Report[infoOnly,
totalMsg, IO.card[totalCount], IO.rope[thisMsg] ];
ENDLOOP;
Check to make sure assignment was completed (strictly an internal consistency check)
BEGIN
first: BOOLTRUE;
FOR i: CARDINAL IN [0..nInstructions) DO
imPtr: IMRecordPtr = MDUtils.IMPtr[i];
placed: BOOL =
(imPtr.word0.onPageAndPlaced = 1) AND (imPtr.word0.atW0AndatWord = 1);
imPtr.word0.onPageAndPlaced ← IF placed THEN 1 ELSE 0;
IF ~placed THEN {
IF first THEN {
MDOps.Report[passFatal,
"\n\n******The following had no address assigned:\n\t%g", IO.card[i] ];
first ← FALSE;
}
ELSE MDOps.Report[infoOnly, "%6bB", IO.card[i]];
};
ELSE {
w0: CARDINAL = imPtr.word0.W0Addr;
IF (realToImagAddr+w0)^ # 177777B THEN SIGNAL NotUnique;
(realToImagAddr+w0)^ ← i;
};
ENDLOOP;
IF ~first THEN MDOps.Report[infoOnly, "\n"];
END;
RETURN[TRUE];
};
PageLimits: PROC[first, last: CARDINAL] = {
firstPage ← first;
lastPage ← last;
thisPage ← first;
};
aTrue: CARDINAL ← 0;  -- statistics
aFalse: CARDINAL ← 0;
RingStatus: TYPE = {ok, isolatedIns, ptrToSubPage, failure };
AssignPage: PROC[wBuf: WorkingBuffer, wRing: DoradoPage] = TRUSTED {
max: CARDINAL ← DoradoPageSize - wRing.used;
pn: CARDINAL ← thisPage;
af: CARDINAL ← aFalse;
fPtr, fp: LONG POINTER TO CARDINAL;
ringStatus: RingStatus;
fUsed: CARDINAL ← DoradoPageSize;
DO
page: DoradoPage ← pageTab[pn];
IF page.used <= max THEN {
[fp, ringStatus] ← AssignRing[wBuf, wRing, page];
IF ringStatus = ok THEN
{ thisPage ← pn; aTrue ← aTrue + 1; RETURN }
ELSE {
IF page.used < fUsed THEN fUsed ← page.used;
fPtr ← fp;
aFalse ← aFalse + 1;
};
};
IF pn <= firstPage THEN EXIT;
pn ← pn - 1;
ENDLOOP;
pn ← thisPage + 1;
UNTIL pn > lastPage DO
page: DoradoPage = pageTab[pn];
IF page.used <= max THEN {
[fp, ringStatus] ← AssignRing[wBuf, wRing, page];
IF ringStatus = ok THEN
{ thisPage ← pn; aTrue ← aTrue + 1; RETURN }
ELSE {
IF page.used < fUsed THEN fUsed ← page.used;
fPtr ← fp;
aFalse ← aFalse + 1;
}
};
pn ← pn + 1;
ENDLOOP;
if we get here, there was an error of some type
BEGIN
msg: ROPEIF fullMsg # NIL THEN fullMsg ELSE failMsg;
MDOps.Report[infoOnly,
IF ringStatus = isolatedIns THEN "%g:"
ELSE "%g,\n while trying to place instruction(s):",
IO.rope[msg] ];
IF ringStatus = isolatedIns THEN MDUtils.PutAData[fPtr^]
ELSE {
fx: SubPage = LOOPHOLE[fPtr];
FOR j: CARDINAL IN [0..fx.subPageHeader.length) DO
MDUtils.PutAData[fx.data[j]];
ENDLOOP;
};
MDOps.Report[passFatal, " Ring consists of:\n"];
MDUtils.PutRing[FirstIns[wBuf]];
END;
};
FirstIns: PROC[wBuf: WorkingBuffer] RETURNS[CARDINAL] = TRUSTED {
sph: SubPage =
LOOPHOLE[IF wBuf^ = 0 THEN wBuf+SIZE[SubPageHeader] ELSE wBuf];
RETURN[sph.data[0]];
};
CollectRing: PROC[i: CARDINAL, wBuf: WorkingBuffer, wRing: DoradoPage]
RETURNS[CARDINAL] = TRUSTED {
Collect instructions starting at i.
Set bit table and counts in ring (DoradoPage structure)
Put instructions in tab (fake SubPage for isolated instructions, sequence of SubPage structures, 0)
Return # of instructions collected
count: CARDINAL ← 0;
aTab ← ALL[0];
aTab[0] ← 177776B;
clear the wRing for this call
wRing.used ← wRing.global ← wRing.ifuEntry ← 0;
wRing.slow ← FALSE;
BEGIN
xbt: Xbt ← LOOPHOLE[wRing.bt];
FOR i: CARDINAL IN [0..xbtWords) DO
xbt[i] ← 0;
ENDLOOP;
END;
Don't start ring inside subpage sequence
DO
imPtr: IMRecordPtr ← MDUtils.IMPtr[i];
i ← imPtr.word2.W2AddrAndbLink;
IF imPtr.links.jbcLinked = 0 THEN EXIT;
ENDLOOP;
BEGIN
j: CARDINAL ← i;
ni: CARDINAL ← 0;
top: CARDINAL = lWBuf-1;
bot: CARDINAL = SIZE[SubPageHeader];
ptr: CARDINAL ← bot;
end: CARDINAL ← top;
nsp: CARDINAL ← 0;
lastLinked: BOOLFALSE;
DO
imPtr: IMRecordPtr ← MDUtils.IMPtr[j];
imPtr.word2.branchesAndMarked ← 1;
IF imPtr.word0.globalAndIFUSeq = 1 THEN {
wRing.global ← wRing.global + 1;
wRing.slow ← TRUE;
IF wRing.global > pageGlobalMax THEN {
MDOps.Report[passFatal, "***More than 1 GLOBAL on a page:\n"];
MDUtils.PutRing[j];
};
};
IF imPtr.word0.brkPAndIFUE = 1 THEN {
wRing.ifuEntry ← wRing.ifuEntry + 1;
wRing.slow ← TRUE;
IF wRing.ifuEntry > pageIfuMax THEN {
MDOps.Report[passFatal, "***More than 16 IFU entries on a page:\n"];
MDUtils.PutRing[j];
};
};
IF imPtr.word0.atW0AndatWord = 1 THEN {
wn: CARDINAL ← MDUtils.CardAnd[imPtr.word0.W0Addr, WordMask];
IF aTab[0] = 177776B THEN aTab ← ALL[177777B];
IF aTab[wn] = 177777B THEN {
wRing.bt[wn] ← TRUE;
aTab[wn] ← j;
wRing.slow ← TRUE;
}
ELSE MDOps.Report[passFatal,
"%g....assigned to same location as %g\n", IO.card[j], IO.card[aTab[wn]] ];
};
IF ~lastLinked THEN {
IF imPtr.links.jbcLinked = 0 THEN
{ IF end # ptr THEN { (wBuf+ptr)^ ← j; ptr ← ptr + 1 } }
ELSE {
len: CARDINAL = CollectSubpage[wBuf, j, end, end-ptr];
end ← end - len;
IF len # 0 THEN nsp ← nsp + len - SIZE[SubPageHeader];
};
};
j ← imPtr.word2.W2AddrAndbLink;
lastLinked ← imPtr.links.jbcLinked = 1;
ni ← ni + 1;
IF j = i THEN EXIT;
ENDLOOP;
IF ni > DoradoPageSize THEN {
MDOps.Report[passFatal,
"%g instructions had to go on the same page (limit is 64):\n", IO.card[ni]];
MDUtils.PutRing[i];
ni ← ptr - bot + nsp;
};
wRing.used ← ni;
Rearrange the tables
wBuf^ ← ptr - bot;
(wBuf+top)^ ← 0;
FOR m: CARDINAL IN [0..top-end+1) DO
(wBuf+ptr+m)^ ← (wBuf+end+m)^;
ENDLOOP;
RETURN[ni];
END;
};
CollectSubpage: PROC[wBuf: WorkingBuffer, start, end, len: CARDINAL]
  RETURNS[CARDINAL] = TRUSTED {
Start is first instruction of subpage in ring
End is the end of a SubPage structure for the result
Put addresses in the structure with +1 lists first
Set length, spn1, alists
Return amount used if enough room, 0 if not enough
IF len <= SIZE[SubPageHeader] THEN RETURN[0];
BEGIN
i: CARDINAL ← start;
data: ARRAY [0..SubPageSize) OF CARDINAL;
jBot: CARDINAL ← end - len;
jTop: CARDINAL ← end;
dBot: CARDINAL ← 0;
dTop: CARDINAL ← SubPageSize;
spn: CARDINAL ← 177777B;
iAbs: CARDINAL;
DO
imPtr: IMRecordPtr ← MDUtils.IMPtr[i];
IF imPtr.word0.atW0AndatWord = 1 THEN {  -- absolute placement
spa: CARDINAL ← PageShift[imPtr.word0.W0Addr];
IF spn = 177777B THEN {
spn ← spa;
iAbs ← i
}
ELSE {
IF spn # spa THEN
MDOps.Report[passFatal,
"%g....must be in subpage with %g, but has conflicting assignment",
IO.card[i], IO.card[iAbs] ];
};
};
IF imPtr.links.aLink = i THEN {  -- not on a +1 list, put at end
IF jTop = jBot THEN RETURN[0];
jTop ← jTop - 1;
(wBuf+jTop)^ ← i;
}
ELSE {  -- on a +1 list, put the list at the beginning
i0, i1: CARDINAL;
gotIt: BOOLFALSE;
FOR j: CARDINAL IN [0..dBot) DO
IF data[j] = i THEN GOTO ska;  -- already got it
ENDLOOP;
IF gotIt THEN LOOP;
i1 ← i;
WHILE imPtr.links.aLinked = 1 DO  -- find beginning of alist
i1 ← imPtr.links.aLink;
imPtr ← MDUtils.IMPtr[i1];
ENDLOOP;
i0 ← i1;
DO
IF dBot = dTop THEN RETURN[0];
data[dBot] ← i1;
dBot ← dBot + 1;
i1 ← imPtr.links.aLink;
imPtr ← MDUtils.IMPtr[i1];
IF i1 = i0 THEN EXIT;
ENDLOOP;
imPtr ← MDUtils.IMPtr[i];
EXITS
ska => NULL;
};
i ← imPtr.word2.W2AddrAndbLink;
IF imPtr.links.jbcLinked = 0 THEN EXIT;
ENDLOOP;
Join sections of table
BEGIN
v: CARDINAL ← jTop - dBot - SIZE[SubPageHeader];
nw: CARDINAL;
sph: SubPageHeader;
IF v < jBot THEN RETURN[0];
FOR m: CARDINAL IN [0..dBot) DO
(wBuf+v+SIZE[SubPageHeader]+m)^ ← data[m];
ENDLOOP;
nw ← end - v - SIZE[SubPageHeader];
IF nw > 16 THEN
MDOps.Report[passFatal,
"%g....more than 16 instructions in subpage\n", IO.card[start]];
sph.aLists ← IF dBot # 0 THEN 1 ELSE 0;
sph.spn1 ← spn+1;
sph.length ← nw;
(wBuf+v)^ ← LOOPHOLE[sph];
RETURN[end - v];
END;
END;
};
PageShift: PROC[addr: CARDINAL] RETURNS[CARDINAL] = {
x: WORD = Basics.BITAND[LOOPHOLE[addr], DoradoPageSize-20B];
RETURN[LOOPHOLE[Basics.BITSHIFT[x, -4], CARDINAL]];
};
AssignRing: PROC[wBuf: WorkingBuffer, wRing, page: DoradoPage]
RETURNS[fPtr: LONG POINTER TO CARDINAL, status: RingStatus] = TRUSTED {
Assign instructions in table to page
Return status = ok for success (fPtr = NIL); other cases are failure, isolatedIns, ptrToSubPage as values for fPtr (non-NIL)
Don't alter any important data structures on failure
xRing: XDoradoPage = LOOPHOLE[wRing];
xPage: XDoradoPage = LOOPHOLE[page];
fSub: LONG POINTER TO CARDINAL = LOOPHOLE[wBuf + SIZE[SubPageHeader] + wBuf^];
subTab: SubPage = LOOPHOLE[wBuf];
assignList: LIST OF Assignment ← NIL;
xbt: XDoradoPage = NEW[XDoradoPageRec];
dbt: DoradoPage = LOOPHOLE[xbt];
xbt.bt ← NEW[XbtRec ← ALL[0]];
IF wRing.slow THEN {
IF wRing.global + page.global > pageGlobalMax THEN RETURN[NIL, failure];
IF wRing.ifuEntry + page.ifuEntry > pageIfuMax THEN RETURN[NIL, failure];
Check absolutely assigned instructions first
FOR i: CARDINAL IN [0..xbtWords) DO
rBits: WORD = xRing.bt[i];
pBits: WORD = xPage.bt[i];
IF Basics.BITAND[rBits, pBits] # 0 THEN RETURN[NIL, failure];
xbt.bt[i] ← Basics.BITOR[rBits, pBits];
ENDLOOP;
}
ELSE
FOR i: CARDINAL IN [0..xbtWords) DO
xbt.bt[i] ← xPage.bt[i];
ENDLOOP;
Do subpages with +1 lists
BEGIN
xSub: LONG POINTER TO CARDINAL ← fSub;
UNTIL xSub^ = 0 DO
sub: SubPage = LOOPHOLE[xSub];
IF sub.subPageHeader.aLists # 0 THEN
IF ~MDUtils.PlaceSubpage[sub, page.absPage, xbt] THEN
RETURN[xSub, ptrToSubPage];
xSub ← LOOPHOLE[xSub + SIZE[SubPageHeader] + sub.subPageHeader.length];
ENDLOOP;
Next do other subpages
xSub ← fSub;
UNTIL xSub^ = 0 DO
sub: SubPage = LOOPHOLE[xSub];
IF sub.subPageHeader.aLists = 0 THEN
IF ~MDUtils.PlaceSubpage[sub, page.absPage, xbt] THEN
RETURN[xSub, ptrToSubPage];
xSub ← LOOPHOLE[xSub + SIZE[SubPageHeader] + sub.subPageHeader.length];
ENDLOOP;
Finally do rest of ring (isolated instructions)
FOR j: CARDINAL IN [0..subTab.subPageHeader.length) DO
jx: CARDINAL;
w0: CARDINAL;
imPtr: IMRecordPtr = MDUtils.IMPtr[jx ← subTab.data[j]];
IF imPtr.word0.atW0AndatWord = 1 THEN-- absolutely placed
w0 ← page.absPage + MDUtils.CardAnd[imPtr.word0.W0Addr, WordMask]
ELSE {
loc: CARDINAL = MDUtils.Find1Place[imPtr.mask, xbt.bt];
IF loc = 177777B THEN RETURN[wBuf+SIZE[SubPageHeader]+j, isolatedIns];
w0 ← page.absPage + loc;
};
IF (imagToRealAddrs+jx)^ # 177777B THEN SIGNAL NotUnique;
IF (realToImagAddrs+w0)^ # 177777B THEN SIGNAL NotUnique;
imPtr.word0.W0Addr ← w0;
ENDLOOP;
END;
page.global ← page.global + wRing.global;
page.ifuEntry ← page.ifuEntry + wRing.ifuEntry;
page.used ← page.used + wRing.used;
FOR i: CARDINAL IN [0.. xbtWords) DO
xPage.bt[i] ← xbt.bt[i];
ENDLOOP;
BEGIN
xSub: LONG POINTER TO CARDINAL ← wBuf;
DO
sub: SubPage = LOOPHOLE[xSub];
end: LONG POINTER TO CARDINAL =
LOOPHOLE[xSub+SIZE[SubPageHeader]+sub.subPageHeader.length];
xSub ← LOOPHOLE[xSub+SIZE[SubPageHeader]];
UNTIL xSub = end DO
jx: CARDINAL = xSub^;
imPtr: IMRecordPtr = MDUtils.IMPtr[jx];
imPtr.word0.onPageAndPlaced ← 1;
imPtr.word0.atW0AndatWord ← 1;
(imagToRealAddrs+jx)^ ← imPtr.word0.W0Addr;
(realToImagAddrs+imPtr.word0.W0Addr)^ ← jx;
xSub ← LOOPHOLE[xSub + 1];
ENDLOOP;
IF xSub^ = 0 THEN EXIT;
ENDLOOP;
END;
RETURN[NIL, ok];
};
GetPageN: PUBLIC PROC[pageNum: CARDINAL] RETURNS[DoradoPage] =
{ RETURN[pageTab[pageNum]] };
END.