:TITLE[MesaOP3];*Opcodes 300b - 377b
%
Ed Fiala 21 December 1982: Make opcodes 372-375 undefined only when not Cedar.
Ed Fiala 13 October 1982: Add SavPCinFrame1 label for MesaESC; fix bug in PC
backup at xfTrap; add tasking at LFC and RET.
Ed Fiala 23 August 1982: Fix xfer allocate to not overwrite frame’s FSI word
with the FSI of the chain because sometimes ths FSI contains 37b rather than
the chain’s FSI.
Ed Fiala 26 May 1982: Remove local cache refill at BLTint; eliminate
MemStat← at StorePCTrap for frame faults; add MemStat←Normal at P7Tailx;
do Xfer local cache refill only on type 0.
Ed Fiala 20 May 1982: Replace NoPushSD by PushSD in MemStat; fix bug at
xfT0e+1; improve tasking at xfT0e return; cause PushSD only on indirect
xfers saving 10 cycles in ordinary case of EFC; interchange at
BLTdonex for CacheLocals=1.
Ed Fiala 3 May 1982: Merge xfLM1toT into SavPCinFrame saving 1 mi.
Ed Fiala 30 April 1982: Improve tasking in Xfer type 0 and in FreeSub.
Ed Fiala 16 April 1982: add masking of xfFSI in FreeSub as reported by
Jim Sandman; avoid PushSD in LFC.
Ed Fiala 16 March 1982: bugfix at InitEnd, xfT0+1 (ControlTrap), StorePCTrap;
move BrkByte check out of Xfer and into BRK opcode; remove PCB odd check in
SavePCXfer; eliminate xfNoStorePC label; fix trap parm for sUnboundTrap;
bum 1 mi in calls to xfMemEarly.
Ed Fiala 26 February 1982: Fix page fault restart for XferFixup case; fix bug
in FreeSub; another PCB odd check in Xfer trap code; more LPChecking
conditional.
Ed Fiala 15 December 1981: Create from old Pilot MesaX and other opcodes;
substantial rework of page placement and registers; numerous Xfer changes
for new Pilot.

NOTES:
1) The PCB odd kludge to indicate "context invalid" seems expensive; can we
arrange something else?
2) Use of TrapParm copied into prData at FrameFault1 is redundant.
3) Check for 0 PC in Xfer?
%
*LoadRAM jumps here at end of initialization to start the emulator.
*StkP points at FFault; set "trap on page fault" bit.
InitEnd:
Stack ← (Stack) or (1C), At[InitEndLoc];
LoadPage[xfPage1];
StkP ← RZero, GoToP[Xfer];


PFetch4[PCB,IBuf,4], GoToP[MesaRefill], At[LShift[xfPage1,10],377];

xfPushTR:
Stack&+1 ← T, Return;

xfMemEarly:
MNBR ← LOCAL;
xfMemEarly1:
MemStat ← (MemStat) or (EarlyXfer), Return;

xfTtoLOCAL:
LOCAL ← T, Return;

xfRet:
Return;


%Save CODE-relative PC in frame using xfTemp1 as a temporary.
CODEhi .eq. PCBhi and |PCB-CODE| < 32K, so the result fits in one word.
FrameFault, among others, makes PCB odd to prevent the call on SavPCinFrame
in MesaP from storing in the Frame.
Called by @PO, @POR, @AF, MesaP, and
xfTrap.
NOTE: Saved byte PC must be left in xfTemp1 for
xfTrap.
%
SavPCinFrame:
T ← LSh[CODE,1];
SavPCinFrame1:
T ← (LSh[PCB,1]) - T, GoTo[xfRet,R Odd];
T ← (PCFreg) + T;
xfTemp1 ← T;
xfLM1toT:
T ← (LOCAL) - 1;
PStore1[MDS,xfTemp1], Return;*Can’t page fault

%Loadgc loads GLOBAL, xfGFIWord, CODE, and CODEhi given a pointer to a
global frame.

input registers
xfTempglobal frame address
output registers
xfGFIWord
CODE,CODEhicode base register
GLOBALglobal base register
xfTempmodified for Xfer type 1 during memory wait

CallExternal to Loadgc from Fault.Mc and from xfControlTrap, to Loadgc0 from
Xfer type 1, and to Loadgc1 from Xfer type 0.
Fill xfGFIWord, CODE, and CODEhi with new global overhead; the register
preceding xfGFIWord (xfGarb0) is smashed; modifications to xfTemp
are for the call from Xfer type 1.
%
Loadgc:
T ← Form-4[xfTemp], At[LoadGCLoc];*xfPage1
Loadgc0:
GLOBAL ← T;
Loadgc1:
T ← (GLOBAL) - (4C), Skip[ALU#0];
RTemp ← sUnboundTrap, GoTo[xfDestParmTrap];
PFetch4[MDS,xfGarb0];
xfTemp ← LdF[xfTemp,16,2];
T ← (LdF[xfCount,12,5]) + 1;
xfTemp ← (LSh[xfTemp,5]) + T + 1;*EVI + 2
LU ← CODE, Skip[R Even];
RTemp ← sCodeTrap, GoTo[xfDestParmTrap];
:IF[LPChecking]; *************************************
*Test for bad pointer
LU ← RSh[CODEhi,6];
CODEhi ← T ← LdF[CODEhi,12,6], Skip[ALU=0];
T ← CODEhi ← (Zero) - 1, Skip;
:ELSE; ***********************************************
T ← RHMask[CODEhi];
:ENDIF; **********************************************
*CODE doesn’t cross 64K boundary
T ← CODEhi ← (LSh[CODEhi,10]) or T;
PCBhi ← T, Return;

%FreeSub free’s a frame except for the final PStore1[MDS,xfFrame] which
is not done for tasking reasons. None of FreeSub’s memory references can
page fault because AV must be resident and L[-4] to L[+3] are on the same page
and not swapped out. L[0] is quadaligned. @FF opcode enters at FreeSub0,
Xfer at FreeSub0 and FreeSub1.

input registers
xfFramepointer to frame - 4
Tpointer to frame - 4
xfFSIdisplacement to head-of-list in AV
temps
xBufframe chain link
%
FreeSub0:
xfFSI ← T ← (xfFSI) + (AVOffset);
FreeSub1:
PFetch1[MDS,xBuf];*get head of list from AV
T ← xfFrame ← (xfFrame) + (4C);
PStore4[MDS,xBuf];*link ← head
T ← xfFSI, Return;

%Xfer transfers control from one context to another. Affected are the
LOCAL, GLOBAL, CODE/CODEhi, PCB/PCF, xfGFIWord, and xfBrkByte registers
and the values in the overhead words of the local frame (return link,
global link, and return pc).

Enter at SavePCXfer from @LSTE,
EFC, @SFC, @KFCB, and BackTrap.
Enter at Xfer from InitEnd, @LSTF, @RET, @PO, @POR, StorePCTrap, and
RSXfer (in MesaP).
Enter at xfLFC from LFC.

Permanent registers used
xfBrkByte
40400b + break bytecode to execute (Known bugs!!).
xfXTSReg
Xfer trap status register--right-shifted
1 bit each Xfer; an odd value means trap at exit
from the Xfer just before entering the new context.
Input registers
xfMX
destination frame link (type 0), procedure link (type 1),
or indirect link (type 2).
xfMY
source link--may become trap parameter or return link.
MemStat
possible FreeFrame or Trap bits set.
xfFSI
frame size index if freeing frame.
xfFrame
pointer to frame if freeing it.
Temporary registers
xfCount, xfTemp, xfTemp1, RTemp, RTemp1, MNBR, xBuf, TrapParm

Various value restrictions:
CODE
quadaligned.
PCB
quadaligned (PCB*2 + PCF = byte pc)
PCBhi = CODEhi
0 <= PCB-CODE <= 32k
GLOBAL
quadaligned; overhead words at GLOBAL-3 to GLOBAL-1.
LOCAL
quadaligned; overhead words at LOCAL-4 to LOCAL-1; LOCAL-4 and
LOCAL+3 are on the same page.
GLOBALhi = LOCALhi = MDShi
AV
page-aligned, locked in storage.
GFT
page-aligned.

Page faults are dealt with in three steps:
(1) Bytecodes that page fault prior to setting EarlyXfer in MemStat
will be restarted after fault service.
(2) A page fault with EarlyXfer will rebuild LOCAL, xfGFIWord, CODE/CODEhi,
and GLOBAL from the frame pointed to by MNBR; then as in (1) above.
(3) XferFixup in MemStat indicates that all references which might page fault
in the old context are complete; if the PFetch4 which refills IBuf faults,
IBuf will be filled with 377 bytecodes and the fault handler will reenter
Xfer immediately after the PFetch4; bytecode 377 microcode (illegal in normal
programs) then deals with the trap. Note that the fault handler smashes
SALUF and xBuf to xBuf3, so these cannot be used in the part of Xfer which
might continue from an XferFixup fault.

Destination (xfMX) and Source (xfMY) links will be pushed onto the stack if
PushSD=1 in MemStat, but this must not happen until EarlyXfer page faults
(step 2 above) are impossible because some Xfer entries depend upon stack
arguments which would be overwritten, making restart impossible (PO and POR).
The stack is popped twice to its original level after these links are pushed.

If the Trap bit in MemStat is 1, TrapParm is stored in Local 0. A type 0
Xfer will also store the source link (xfMY) in the return link word of the
frame being entered.

The following fault conditions are detected by Xfer:

1) If after following all indirect links the final destination link is 0,
a control trap occurs. This will backup and save the PC in the old
frame, then reenter Xfer with the Trap bit set in MemStat, the destination
link changed to the sControlTrap entry in SD, and the original source link
in TrapParm. This occurs legitimately when initializing port linkages;
otherwise, it indicates a bug.

2) If the global frame address for the new context is 0, an unbound trap
occurs--like (1) using the sUnboundTrap entry in SD with the destination link
in TrapParm. This indicates that the procedure doesn’t exist (i.e., a bug).

3) If the new code base is uneven, a code trap occurs--like (1) with the
destination link (xfMX) in TrapParm. This is used for start traps.

4) If a type 1 Xfer’s frame allocation fails, a frame fault occurs.
This backs up and saves the PC in the old frame, then enters prFault
in MesaP with the new frame’s FSI in prData, qFrameFaultOs in T, and PCB odd.
A process switch will occur to the frame fault process; PCB odd indicates
"context invalid" and prevents both StkP←SStkP upon a page fault and storing
PC in the frame. Completion of an Xfer will turn off "context invalid."

5) If xfXTSReg is odd, an Xfer trap occurs provided xfGFIWord bit 14 is also
1. This trap occurs in the new context with the sXferTrap entry in SD as the
destination link and the new frame in xfMY as the source link.

To get total time for an Xfer opcode, add the values in the tables below;
the first table below includes 2.25 cycles/opcode byte for buffer refill.

Timing from beginning of opcode to the label Xfer:
27.25 cycles EFC code link, 28.25 cycles if frame link;
(+3 cycles if type 0, +6 if type 2) + 4.25 cycles if EFCB.
18.25 cycles on RET (Same timing for types 0, 1, or 2)
22.25 cycles on SFC type 1 (+3 cycles type 0, +6 cycles type 2)
29.5 cycles on KFCB type 1 (+3 cycles type 0, +6 cycles type 2)
46.5 cycles on PO or POR type 1 (+3 cycles type 0, +6 cycles type 2)
68.75 cycles + 7*(StkP+2) on LSTF
65.75 cycles + 7*(StkP+2) on LSTE

Timing from @LFCn to the label xfLFC:
22.25 cycles on @LFC1 to @LFC5;
28.5 cycles on @LFCB.

Timing from Xfer to xfGo:
19 cycles/indirection (type 2 destination link) +
25 cycles for type 0 Local return (+30 cycles if non-Local)
(+24 cycles if Trap)
133 cycles for type 1 (+14 cycles if Trap=1 in MemStat)

Timing from xfGo to exit:
36 cycles if FreeFrame=0 and PushSD=1;
26 cycles if FreeFrame=0 and PushSD=0;
58 cycles if FreeFrame=1 (PushSD=1 is almost free).

Total time for typical procedure calls:
27.25+133+26 + 18.25+25+30+58 = 186.25+131.25 = 317.5 cycles for an
EFC type 1 and its associated RET;
22.25+133-71+26 + 18.25+25+58 = 110.25+101.25 = 211.5 cycles for a LFC and
its associated RET;
%

SavePCXfer:
T ← LSh[CODE,1], Task;*Odd placement; paired with Xfer
T ← (LSh[PCB,1]) - T;
T ← (PCFreg) + T;
xfTemp1 ← T, Call[xfLM1toT];
xfMY ← (Zero) + T + 1;*xfMY ← LOCAL
Xfer:
LU ← Dispatch[xfMX,16,2];*Even placement; paired with SavePCXfer
T ← xfMX, Disp[xfT0];

xfT2:
PFetch1[MDS,xfTemp], At[xftype,2];*Indirect link
MemStat ← (MemStat) or (PushSD), Call[xfRet];
LU ← Dispatch[xfTemp,16,2];
T ← xfTemp, Disp[xfT0];

*LOCAL is offset by -2 here; skip if this xfer is servicing a trap.
xfT0e:
LOCAL ← T, Skip[ALU#0];
xfT0c:
T ← GLOBAL, Return;
Nop;
PStore1[LOCAL,TrapParm,2];*Save trap parameter
xfWDC ← (xfWDC) + 1;*Disable ints if trapping to a frame.
T ← (LOCAL) - 1;
PStore1[MDS,xfMY], GoTo[xfT0c];*Save return link at LOCAL-3

%Frame link. T points at frame being reentered. That frame has FSI in
word -4, return link (just dispatched on) in word -3, GLOBAL in word -2,
and return PC in word -1.
%
xfT0:
T ← (Form-2[AllOnes]) + T, GoTo[xfT0d,ALU#0], At[xftype,0];
T ← xfMY;
TrapParm ← T;
RTemp ← sControlTrap, GoTo[xfControlTrap];
*xfTemp,,xfTemp1 ← new GLOBAL,,return PC (byte PC relative to CODE).
*We could do "MNBR ← LOCAL, LOCAL ← T, NoRegILockOK" at xfMemEarly, but if a
*fault intervened before MemStat←EarlyXfer, we would be screwed. The fault
*would ordinarily abort the 4th mi after the PFetch2, so we would be safe,
*but "PFetchx, Return" by an io task right before the Dispatch above would
*have transport immediately after the PFetchx, and disturb things.
xfT0d:
PFetch2[MDS,xfTemp], Call[xfMemEarly];
*LOCAL is offset by -2 here.
LU ← (MemStat) and (Trap), Call[xfT0e];
T ← (Form-4[xfTemp]) xor T;
*Equal old and new GLOBAL implies equal CODE, CODEhi, PCBhi, and xfGFIWord,
*so Loadgc need not be called. Type 1 Xfers don’t need this improvement
*because local function calls are used.
LOCAL ← (LOCAL) + (2C), Skip[ALU#0];
:IF[CacheLocals]; ********************************
PFetch4[LOCAL,LocalCache0,0], GoTo[xfGo];
PFetch4[LOCAL,LocalCache0,0];
:ELSE; *******************************************
LU ← Dispatch[MemStat,10,2], GoTo[xfGoa];
:ENDIF; ******************************************
T ← GLOBAL ← (GLOBAL) xor T, Call[Loadgc1];
%Free the old frame if FreeFrame=1 in MemStat; new byte pc in xfTemp
PushSD=1 on indirect xfers, PushSD=0 on traps, LSTE, and LSTF, FreeFrame=1
on RET and LSTF. Since CODE is quadword-aligned, the PCF value depends only
on the relative byte PC here--don’t have to subtract CODE; also, no faults
can occur between here and MemStat←XferFixup because FreeSub’s memory
references can’t fault, so it is safe to do PCF← here.
%
xfGo:
LU ← Dispatch[MemStat,10,2];*FreeFrame & PushSD bits
xfGoa:
PCF ← xfTemp1, Disp[.+1];
xfFSI ← RHMasK[xfFSI], At[xfMSTab,2];
T ← xfFSI ← (xfFSI) + (AVOffset), Call[FreeSub1];
PStore1[MDS,xfFrame], GoTo[xfGo1];

xfFSI ← RHMasK[xfFSI], Call[FreeSub0], At[xfMSTab,3];
PStore1[MDS,xfFrame];
T ← xfMX, At[xfMSTab,1];
Stack&+1 ← T;
T ← xfMY, Call[xfPushTR];
Stack&-2;
xfGo1:
MemStat ← XferFixup, At[xfMSTab,0];
%xfBrkByte is offset by 2001b rcy 2, so when it contains 40400b no break
is pending. An MC1 fault will abort the 4th mi after the PFetch4[CODE,IBuf]
below. If this happens, Fault.Mc will reenter at the mi after the PFetch4
with CODE+RSh[xfTemp1,1] in T, simulating the bypass kludge and with -1 in
PCF[IBuf]. This won’t work if faults can happen on any of the references by
FreeSub or on the local cache refill reference below, but FreeSub’s
references won’t fault and the local cache refill won’t fault because
LOCAL+3 must be on the same page with LOCAL-4.
%
T ← RSh[xfTemp1,1];
PFetch4[CODE,IBuf];
*Important to task; cycles here add to those of the next opcode.
PCB ← T, Task, At[xfFaultGoLoc];
PCB ← Form-4[PCB];
xfXTSReg ← RSh[xfXTSReg,1], GoTo[XferTrap,R Odd];
LU ← NextInst[IBuf];
*Jump here from XferTrap, MesaESC.Mc.
xfTailx:
MemStat ← Normal, NIRet;


xfT3:
xfCount ← T, Skip, At[xftype,3];
xfT1:
xfCount ← T, At[xftype,1];*save descriptor
T ← GFTOffset;
T ← (LdF[xfCount,0,12]) + T;*T ← GFT index + GFT offset
PFetch1[MDS,xfTemp], Call[xfMemEarly];
T ← Form-4[xfTemp], Call[Loadgc0];
*Loadgc did xfTemp ← (LdF[xfCount,12,5]) + ((xfTemp) & 3) lshift 5) + 2
T ← xfTemp, Task;
PFetch1[CODE,xfTemp1];*xfTemp1 ← Byte PC; might page fault
%Enter here from LFC with CODE-relative byte PC in xfTemp.
Allocate new frame and patch links.

First byte of code is the frame size index (FSI) for the new frame. The
word at MDS[AVOffset+FSI] is the header for a chain of frames in which the
low two bits of each pointer are interpreted as follows:
00Word[0:15d] points to a quadaligned frame; the overhead
words are -4 to -1 with respect to this pointer; the link to
the next frame is word 0; overhead words and words 0 to 3
(which become Locals 0-3) are guaranteed on the same page.
x1No frames available--trap with original FSI as parameter.
10No more frames of the exact size wanted, but Word[0:13d]
are an alternate FSI.
References to the FSI table can’t page or write protect fault; the first
reference to any frame might page fault, but since the four overhead words
and the first four locals are guaranteed to be on the same page, no faults
will happen after that.

The original FSI from the 1st code byte is preserved in CycleControl and
becomes the trap parameter, if frame allocation fails; the current FSI
is kept in xfRSAV--if indirection occurs, this becomes different from
the original FSI. A coding trick used below uses a PStore4 to initialize
words 1 (return link) and 2 (GLOBAL) of the frame’s overhead words; word 0
is known to be the current FSI, and word 3 can be smashed.

Timing from Xfer to here = 71 cycles + 14 cycles if Trap=1 in MemStat.
Timing from @LFCn to here = 22.25 cycles;
Timng from @LFCB to here = 28.5 cycles.
%
xfLFC:T ← RSh[xfTemp1,1];
PFetch1[CODE,IBuf], Task;*Read word containing FSI
xfTemp1 ← (xfTemp1) + 1;*Advance to 1st code byte
T ← AVOffset, xfTemp1, Skip[R Odd];
IBuf ← RHMask[IBuf], Skip;
IBuf ← RSh[IBuf,10];
T ← (CycleControl ← IBuf) + T, Call[xfGF1];
*Have original FSI in CycleControl, current FSI + AVOffset in T--return
*here on indirection.
LU ← LdF[xfTemp,16,1], GoTo[xfGF0,R Even];
*Alloc failed--cause FrameFault with original FSI as parameter. To share
*backup code, 1st part of FrameFault is treated as trap. RTemp is set
*negative to separate FrameFaults at StorePCTrap.
T ← RSh[Cycle&PCXF,10];
RTemp ← 100000C, GoTo[xfSetTrapParm];
xfGF0:
T ← xfTemp, GoTo[xfGF2,ALU=0];
xfTemp ← RSh[xfTemp,2];*Indirect
AllocInd:
*Here from @AF in MesaESC.
T ← (xfTemp) + (AVOffset);
xfGF1:
PFetch1[MDS,xfTemp];
TtoxfCount:
*Here from @AF in MesaESC.
xfCount ← T, Return;

*Alloc succeeded--fetch word 0, pointer to next free frame in chain
*This is the last reference which can page fault.
*Timing from xfLFC to here = 38 + 19/indirect allocate cycles.
xfGF2:
PFetch1[MDS,RTemp];*Word 0 of frame = link to next frame.
LOCAL ← T, Task;
T ← (LOCAL) - (4C);
%PFetch1-PStore4 into the overhead words here (of xfRSAV (frame’s FSI),
xfMY (return link), GLOBAL, and GLOBALhi) is substantially faster than two
PStore1’s (PStore2 can’t be used because the two words are unaligned).
Only xfMY and GLOBAL need to be stored. The FSI in word -4 of the frame is
not always the same as the FSI of the chain--sometimes it is 37b, if the
frame handler wants the frame to be freed after execution.
%
PFetch1[MDS,xfRSAV], Call[xfRet];
PStore4[MDS,xfRSAV];
*Complete frame allocation by storing the link into MDS[FSI+AVOffset].
T ← xfCount, Task;
PStore1[MDS,RTemp];
LU ← (MemStat) and (Trap);
LU ← Dispatch[MemStat,10,2], GoTo[xfGoa,ALU=0];
*PStore4 is ok because frame is quadaligned and extends to at least LOCAL+3.
*Time between tasks on this path is about 44 cycles.
PStore4[LOCAL,TrapParm,0], GoTo[xfGo];

TrapRestStkP:
RTemp1 ← T;
StkP ← RTemp1, Return;


*Jump here from Loadgc with sCodeTrap or sUnboundTrap in RTemp.
xfDestParmTrap:
T ← xfMX;
*Xfer Alloc trap jumps here.
xfSetTrapParm:
TrapParm ← T;
T ← MNBR, Call[xfTtoLOCAL];
xfControlTrap:
T ← (LOCAL) - (2C);
LU ← (MemStat) and (FreeFrame);
*xfTemp,xfTemp1 ← old GLOBAL, saved byte PC
PFetch2[MDS,xfTemp], GoTo[xfTrap1,ALU=0];
Call[Loadgc];
MemStat ← (MemStat) and not (FreeFrame), Call[SavPCinFrame];
Nop;
%Get here with PC in xfTemp1. It is possible (?) to get here on a CodeTrap
(start trap) with PCB odd; in other words, the Xfer call from MesaP is
always type 0 to an existing frame, so Xfer alloc and control traps are
impossible, but conceivably a start trap could happen, so must check for
PCB odd.
%
xfTrap1:
T ← (PCXreg) - 1;*Backup PC by PCF - [(PCX-1) mod 10b]
T ← (PCFreg) - T, Skip[ALU>=0];
T ← 7C, GoTo[.-1];
xfTemp1 ← (xfTemp1) - T, Skip[ALU>=0];
*Crossed quadword boundary in opcode.
xfTemp1 ← (xfTemp1) - (10C);
PCB, Skip[R Odd];
*Restore StkP to beginning of opcode (@SFC, @PO, and @POR)
T ← SStkP, Call[TrapRestStkP];
*Fall through here with backed up byte PC in xfTemp1; jump here from XferTrap
*with new byte PC in xfTemp1. StkP is correct for the original context of
*the Xfer.
*RTemp is < 0 here only on Xfer Allocate failure.
StorePCTrap:
T ← (RTemp) + (SDOffset), Skip[R<0];
PFetch1[MDS,xfMX], FreezeResult;
*The source linke for traps is always the current frame’s LOCAL.
T ← (LOCAL) - 1, FreezeResult;
PCB, FreezeResult, Skip[R Odd];
PStore1[MDS,xfTemp1], FreezeResult;*Save backed up PC
MemStat ← Trap, Skip[ALU<0];
xfMY ← (Zero) + T + 1, GoTo[Xfer];
*Frame faults are handled by Fault Notification
T ← TrapParm;
FrameFault1:
*Enter here on @AF failure
prData ← T, LoadPage[opPage0];
T ← qFrameFaultOs, GoToP[.+1];
OnPage[opPage0];
*PCB is made odd so that when ReSchedule calls SavPCinFrame, the PC
*won’t be saved.
PCB ← 1C, GoTo[prFault];*In MesaP

OnPage[xfPage1];

*Enter from xfGo1 if xfXTSReg was odd with new CODE-relative PC in xfTemp1.
XferTrap:
LU ← LdF[xfGFIWord,16,1];
T ← xfMX, Skip[ALU#0];
LU ← NextInst[IBuf], CallX[xfTailx];
TrapParm ← T;
RTemp ← sXferTrap, GoTo[StorePCTrap];
PFetch4[PCB,IBuf,4], GoToP[MesaRefill], At[LShift[opPage3,10],377];

*Timing: 6.25 cycles
@LI0:
LU ← NextInst[IBuf], Opcode[300];*LIn for n = 0-10d
Stack&+1 ← 0C, NIRet;
@LI1:
LU ← NextInst[IBuf], Opcode[301];
Stack&+1 ← 1C, NIRet;
@LI2:
LU ← NextInst[IBuf], Opcode[302];
Stack&+1 ← 2C, NIRet;
@LI3:
LU ← NextInst[IBuf], Opcode[303];
Stack&+1 ← 3C, NIRet;
@LI4:
LU ← NextInst[IBuf], Opcode[304];
Stack&+1 ← 4C, NIRet;
@LI5:
LU ← NextInst[IBuf], Opcode[305];
Stack&+1 ← 5C, NIRet;
@LI6:
LU ← NextInst[IBuf], Opcode[306];
Stack&+1 ← 6C, NIRet;
@LI7:
LU ← NextInst[IBuf], Opcode[307];
Stack&+1 ← 7C, NIRet;
@LI8:
LU ← NextInst[IBuf], Opcode[310];
Stack&+1 ← 10C, NIRet;
@LI9:
LU ← NextInst[IBuf], Opcode[311];
Stack&+1 ← 11C, NIRet;
@LI10:
LU ← NextInst[IBuf], Opcode[312];
Stack&+1 ← 12C, NIRet;

*Load -1 (8.25 cycles).
*LU ← NextInst[IBuf]; Stack&+1 ← (Stack&+1) or not (0C), NIRet;
*is faster here, but illegal to read stack, which might be empty.
@LIN1:
T ← (Zero) - 1, GoTo[P7PushT], Opcode[313];

*Load Immediate Negative Infinity (6.25 cycles).
@LINI:
LU ← NextInst[IBuf], Opcode[314];
Stack&+1 ← 100000C, NIRet;

*Load Immediate Byte (10.5 cycles).
@LIB:
T ← NextData[IBuf], CallX[P7PushT], Opcode[315];

*Load Immediate Word (16.75 cycles).
@LIW:
LU ← CycleControl ← NextData[IBuf], Opcode[316];
T ← LHMask[Cycle&PCXF];
LADRBx:
T ← (NextData[IBuf]) + T;
P7PushT:
LU ← NextInst[IBuf];
Stack&+1 ← T, NIRet;

*Load Immediate Negative Byte (12.5 cycles).
@LINB:
T ← 177400C, GoTo[LADRBx], Opcode[317];

*Load Immediate High Byte (12.5 cycles).
@LIHB:
LU ← CycleControl ← NextData[IBuf], Opcode[320];
T ← LHMask[Cycle&PCXF], GoTo[P7PushT];

*Load Immediate Double Zero (8.25 cycles).
@LID0:
T ← Stack&+1 ← 0C, GoTo[P7PushT], Opcode[321];

*Local Address 0 to 3, 6, 8, Byte, and Word (8.25, 12.5, and 18.75 cycles).
@LA0:
T ← (LOCAL) + (0C), GoTo[P7PushT], Opcode[322];
@LA1:
T ← (LOCAL) + (1C), GoTo[P7PushT], Opcode[323];
@LA2:
T ← (LOCAL) + (2C), GoTo[P7PushT], Opcode[324];
@LA3:
T ← (LOCAL) + (3C), GoTo[P7PushT], Opcode[325];
@LA6:
T ← (LOCAL) + (6C), GoTo[P7PushT], Opcode[326];
@LA8:
T ← (LOCAL) + (10C), GoTo[P7PushT], Opcode[327];
@LAB:
T ← LOCAL, GoTo[LADRBx], Opcode[330];*Local Address Byte
@LAW:
T ← LOCAL, Opcode[331];*Local Address Word
GLWx:
LU ← CycleControl ← NextData[IBuf];
T ← (LHMask[Cycle&PCXF]) + T, GoTo[LADRBx];

*Global Address 0, Byte, and Word (8.25, 12.5, and 18.75 cycles).
@GA0:
T ← GLOBAL, GoTo[P7PushT], Opcode[332];*Global Address 0
@GAB:
T ← GLOBAL, GoTo[LADRBx], Opcode[333];*Global Address Byte
@GAW:
T ← GLOBAL, GoTo[GLWx], Opcode[334];*Global Address Word

*External Function Call 0 to 12, Byte
@EFC0:
T ← (RZero) or not (0C), GoTo[EFC], Opcode[335];
T ← (RZero) or not (1C), GoTo[EFC], Opcode[336];
T ← (RZero) or not (2C), GoTo[EFC], Opcode[337];
T ← (RZero) or not (3C), GoTo[EFC], Opcode[340];
T ← (RZero) or not (4C), GoTo[EFC], Opcode[341];
T ← (RZero) or not (5C), GoTo[EFC], Opcode[342];
T ← (RZero) or not (6C), GoTo[EFC], Opcode[343];
T ← (RZero) or not (7C), GoTo[EFC], Opcode[344];
T ← (RZero) or not (10C), GoTo[EFC], Opcode[345];
T ← (RZero) or not (11C), GoTo[EFC], Opcode[346];
T ← (RZero) or not (12C), GoTo[EFC], Opcode[347];
T ← (RZero) or not (13C), GoTo[EFC], Opcode[350];
T ← (RZero) or not (14C), GoTo[EFC], Opcode[351];
@EFCB:
T ← NextData[IBuf], Opcode[352];
T ← (RZero) or not T;
EFC:xfGFIWord, LoadPage[xfPage1], Skip[R Even];
PFetch1[CODE,xfMX], GoToP[SavePCXfer];*Code link
*Know that GFlagsOffset .eq. -3
T ← (Form-4[AllOnes]) + T + 1, GoToP[.+1];*Frame link
OnPage[xfPage1];
PFetch1[GLOBAL,xfMX], GoTo[SavePCXfer];


OnPage[opPage3];
LFC:T ← LSh[CODE,1], Task;
T ← (LSh[PCB,1]) - T;
T ← (PCFreg) + T;
RTemp1 ← T, LoadPage[xfPage1];
T ← (MNBR ← LOCAL) - 1;
OnPage[xfPage1];
PStore1[MDS,RTemp1];
xfMY ← (Zero) + T + 1, Call[xfMemEarly1];
xfMX ← Zero, GoTo[xfLFC];

*LFC1 - LFC5, Byte
@LFC1:
PFetch1[CODE,xfTemp1,3], GoTo[LFC], Opcode[353];
PFetch1[CODE,xfTemp1,4], GoTo[LFC], Opcode[354];
PFetch1[CODE,xfTemp1,5], GoTo[LFC], Opcode[355];
PFetch1[CODE,xfTemp1,6], GoTo[LFC], Opcode[356];
PFetch1[CODE,xfTemp1,7], GoTo[LFC], Opcode[357];
@LFCB:
T ← 2C, Opcode[360];
T ← (NextData[IBuf]) + T;
PFetch1[CODE,xfTemp1], GoTo[LFC];

*Stack Function Call
@SFC:
T ← Stack&-1, LoadPage[xfPage1], Opcode[361];
xfMX ← T, GoToP[SavePCXfer];

*Return. Fetch frame FSI into xfFSI and return link into xfMX.
@RET:
T ← (LOCAL) - (4C), Opcode[362];
PFetch2[MDS,xfFSI], Task;*Can’t fault
xfMY ← 0C;
xfFrame ← T, LoadPage[xfPage1];
MemStat ← FreeFrame, GoToP[Xfer];

@KFCB:
T ← NextData[IBuf], Opcode[363];*Kernel Function Call Byte
*Entries here from MesaP, Fault on stack errors, ?
kfcr:
RTemp ← SDOffset, At[KFCRLoc];
T ← (RTemp) + T, LoadPage[xfPage1];
PFetch1[MDS,xfMX], GoTo[SavePCXfer];

@D0:
T ← (xfGFIWord) and not (77C), Opcode[364];*Descriptor Zero
T ← (Zero) + T + 1, GoTo[P7PushT];

@DB:
T ← (xfGFIWord) and not (77C), Opcode[365];*Descriptor Byte
descbcom:
T ← (NextData[IBuf]) + T + 1, CallX[P7PushT];

P7Tail:
LU ← NextInst[IBuf], At[P7TailLoc];
*MemStat←Normal is relied upon by the BLT opcodes. This works only because
*Fault.Mc ALWAYS continues the opcode without dispatching on MemStat when
*a NextInst page or write protect fault occurs.
P7Tailx:
MemStat ← Normal, NIRet;

@BLT:
LP ← 0C, Opcode[366];*Block Transfer
T ← MDShi;
BLTcom:
LPhi ← T;
*fixup: fetch => count + 1; store => source-1, dest-1, count+1
MemStat ← BltFixup, Call[P7Ret];*Setup loop
*Loop here
Stack&-1;*Point to count
LU ← Stack&-1;
T ← Stack&+1, Skip[ALU#0];*Get source, point to count
Stack&-2, GoTo[BLTdonex];
Stack ← (Stack) - 1, LoadPage[bltPage1];*Decrement count
PFetch1[LP,RTemp];
OnPage[bltPage1];
Stack&+1;*Point to dest
T ← Stack&-2, LoadPage[opPage3];*Get dest, point to source
PStore1[MDS,RTemp];
OnPage[opPage3];
Stack ← (Stack) + 1;*Increment source
Stack&+2;
Stack ← (Stack) + 1, GoTo[BLTint,IntPending];*Increment destination
P7Ret:
Return;*Loop

:IF[CacheLocals]; ********************************
*Jump here from BLTcom, @BLTL, and @DSK and BLTLR1 in MesaESC.Mc.
BLTdonex:
PFetch4[LOCAL,LocalCache0,0], GoTo[P7Tail];
:ELSE; *******************************************
BLTdonex:
LU ← NextInst[IBuf], CallX[P7Tailx];
:ENDIF; ******************************************

*Local cache refill not needed here because none of the BLT opcodes jumping
*here depends upon any locals. Jump here from BLTcom, BLTLR1 in MesaESC.Mc.
BLTint:
LoadPage[opPage0];
*Jump here from BLTLint.
BLTstop:
MemStat ← Normal, GoToP[NopInt];

*StackLPDest is called by @BLTL, StackLPDest0 by @BLTCL, StackLPDest1 or
*StackLPDest2 by @BLECL in MesaESC.
:IF[LPChecking]; *********************************
StackLPDest:
T ← Stack&-1;
StackLPDest0:
LPDestHi ← T;
LU ← LdF[LPDestHi,0,12];*Test for long pointer too big
StackLPDest1:
LPDestHi ← (LSh[LPDestHi,10]) + T + 1, Skip[ALU=0];
LPDestHi ← (Zero) - 1;*Cause memory out of bounds
:ELSE; *******************************************
StackLPDest:
T ← RHMask[Stack&-1];
StackLPDest0:
LPDestHi ← T;
LPDestHi ← (LSh[LPDestHi,10]) + T + 1;
:ENDIF; ******************************************
T ← Stack&-2;*Skip over the count word.
StackLPDest2:
LPDest ← T, Return;

@BLTL:
RTemp1 ← Zero, Call[StackLPDest], Opcode[367];*Block Transfer Long
:IF[LPChecking]; *********************************
T ← Stack&-1, LoadPage[opPage1];
*Fixup: source+T, dest+T, count+1
MemStat ← BltLFixup, Call[StackLPx];
:ELSE; *******************************************
T ← RHMask[Stack&-1];
LPhi ← T, LoadPage[opPage1];
*Fixup: source+T, dest+T, count+1
MemStat ← BltLFixup, Call[StackLPy];
:ENDIF; ******************************************
Stack&+3, Call[.+1];*Point to count
*Loop here
LU ← Stack;*Read count, point to source lo
T ← RTemp1, Skip[ALU#0];
Stack&-3, GoTo[BLTdonex];
PFetch1[LP,RTemp];
Stack ← (Stack) - 1;*Decrement count
RTemp1 ← (RTemp1) + 1;*Increment offset
PStore1[LPdest,RTemp];
GoTo[BLTLint,IntPending];
Return;*Goes to BLTLloop

BLTLint:
Stack&-2;
Call[BLTLbump];*wait for page fault before updating Stack
Stack&+2, Call[BLTLbump];
LoadPage[opPage0], GoTo[BLTstop];

BLTLbump:
Stack ← (Stack) + T + 1;
Stack&+1, FreezeResult;
Stack ← (Stack) + 1, UseCOutAsCIn, Return;

@BLTC:
T ← CODE, Opcode[370];*Block Transfer Code
LP ← T;
T ← CODEhi, GoTo[BLTcom];

*Block Transfer Code Long
*TOS,,2OS/
Long pointer to destination
*3OS/
Word count
*4OS/
Source offset from CODE (cardinal)
***Implement BLTCLR (reversed) here
*Point LPDest base reg at destination and StkP at source offset.
:IF[LPChecking]; *********************************
@BLTCL:
T ← Stack&-1, Call[StackLPDest0], Opcode[371];
:ELSE; *******************************************
@BLTCL:
T ← RHMask[Stack&-1], Call[StackLPDest0], Opcode[371];
:ENDIF; ******************************************
T ← (Stack&+1) - 1;*Get source offset, point StkP at count
LU ← Stack;
*Loop here with StkP pointing at Count after LU ← Count.
BLTCL1:
T ← (Stack) + T, Skip[ALU#0];*T ← count + source offset - 1.
Stack&-2, GoTo[BLTdonex];
PFetch1[CODE,RTemp];
T ← (Stack&-1) - 1;
*Page or write protect fault aborts 4th mi after PStore1; do not update count
*until the 4th mi, so that no fixup action is needed in Fault.Mc.
PStore1[LPDest,RTemp], Call[P7Ret];***Nop better than Call here
T ← (Stack&+1) - 1, Call[P7Ret];
Stack ← (Stack) - 1, GoTo[BLTCL1,IntPending’];*Decrement count
Stack&+2, GoTo[BLTint];

P7Undef:
LoadPage[opPage0];
RTemp ← sOpcodeTrap, GoToP[SDTrap];

:UNLESS[WithCedar]; ******************************
@OP372:
TrapParm ← 372C, GoTo[P7Undef], Opcode[372];
@OP373:
TrapParm ← 373C, GoTo[P7Undef], Opcode[373];
@OP374:
TrapParm ← 374C, GoTo[P7Undef], Opcode[374];
@OP375:
TrapParm ← 375C, GoTo[P7Undef], Opcode[375];
:ENDIF; ******************************************
@OP376:
TrapParm ← 376C, GoTo[P7Undef], Opcode[376];

*Cause pagefault trap--should not occur as a bytecode. Fault.Mc fills IBuf
*with 377b bytes when it wants to continue the opcode which faulted but trap
*at entry to the next opcode.
***What if PCF .eq. 0 here--does PCB have to be backed up?
TrapFlap:
T ← (PCFReg) - 1, Opcode[377];*back up PCF by one
RTemp ← T, LoadPageExternal[FaultPage];
PCF ← RTemp, GoToExternal[StartMemTrapLoc];

:END[MesaOP3];