:TITLE[MesaESC];*ESC and ESCL opcodes

%Ed Fiala 29 November 1982: Add LoadStack opcode = Esc 177b as a comment;
needs more work to combine code with LSTE and LSTF--note: can save 4b mi on
moPage in LSTE with redundant Call[SavPCinFrame] in LSTF.
Ed Fiala 3 November 1982: Add UserTimer opcode = Esc 214b; so far, only
used by PathFinder configuration; adds 2b mi on moPage.
Ed Fiala 13 October 1982: Fix bug in LSTE causing source link from LoadState
table to be smashed with a link for the enclosing frame.
Ed Fiala 23 August 1982: Fix @AF to reflect change to xfer allocate
subroutine.
Ed Fiala 27 May 1982: Fix @WRWP to set or clear IntPendingBit.
Ed Fiala 24 May 1982: Move @LONGBLKZ and @LOCALBLKZ to Cedar1.Mc; replace
NoPushSD by PushSD in Xfer MemStat of @LSTE and @LSTF; make csPage;
fix CacheLocals=1 bug in @DSK; change in @PI:.
Ed Fiala 13 May 1982: Change @VERSION to days since 1 Jan 1901; improve
tasking in @PI.
Ed Fiala 5 May 1982: Improve tasking at @SLOB and @WOB.
Ed Fiala 30 April 1982: Change to @FF improving tasking.
Ed Fiala 28 April 1982: Fix long tasking in @LUDIV, @SMF
Ed Fiala 27 April 1982: Fix long tasking in @PI, @FF, @SMF, @GMF, @DSK,
@DBS.

ESC and ESCL are distinct opcodes because the Dorado IFU must determine
opcode length without reference to alpha. However, this implementation uses
the same dispatch table for both opcodes to save microcode space.

ESC
and ESCL are defined to trap at MDS[EscTrapOffset+alpha] with parameter
alpha when undefined. CedarGC opcodes presently trap with parameter 1 in the
no-more-free-list-cells case or 2 in the trace-and-sweep-running case
.

Alpha dispatch is to moPage where possible because this results in 2 or 4
cycles faster execution than is possible when the Dispatch at EscDFirst is
abandoned to change pages. Timing for the alpha dispatch when the first
mi is on moPage is 4.5 (buffer refill) + 10 = 14.5 cycles for alpha < 200b
and 4.5 + 14 = 18.5 cycles for alpha >= 200b.

Placement is as follows:
253b mi on moPage
75b mi on xfPage1
27b mi on csPage
22b mi on opPage0 (+2b mi if CacheLocals=1)
7b mi on opPage1 (+2b mi if CacheLocals=1)
11b mi on opPage2
20b mi on opPage3
25b mi on LSPage
3b mi on wrMDSPage
1b mi on ncPage
1b mi on piPage
1b mi on dvPage1
+2b mi on moPage if WithLInt=1
+2b mi on moPage if WithJS=1
+2b mi on opPage2 if WithJS=1
+2b mi on moPage if WithRotate=1
+1b mi on opPage1 if WithRotate=1
+2b mi on moPage if WithBLEL=1
+25b mi on opPage3 if WithBLEL=1
+5b mi on opPage0 if WithBLEL=1
+5b mi on moPage if WithTMS1000=1
+3b mi on opPage1 if WithTMS1000=1
+6b mi on moPage if WithDLogic=1
+2b mi on ppPage if WithDLogic=1
-1b mi on moPage if WithFloatingPoint=1
-1b mi on moPage if WithCedar=1
+1b mi on moPage if WithBitBlt=1
+1b mi on moPage if WithTextBlt=1
+13b mi on opPage3 if WithBLTLR=1
+1b mi on moPage if WithBLTLR=1
+34b mi on opPage3 if WithDShift=1
+1b mi on moPage if WithDShift=1
+2b mi on dsPage1 if WithDShift=1

POSSIBLE CHANGES:
1) Minimal stack error checks for (?) BitBlt, CKSUM, ?
2) Error checks for real page number and virtual page number in @SM, etc.
3) Implement @BYTBLT, @BYTBLTR?
4) Error check xfWDC decrement in @LSTE.
5) Check @BLTLR, @DSHIFT, @ROTATE.
6) PStore1[MDS,RTemp] could be subr shared with MesaOP3.
7) Assign distinct trap location for AssignRef opcode.
8) Is LocalCache reload needed at @WOB exit?
%

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

moUnimp
:
TrapParm ← T, LoadPage[opPage0];*Even placement
moUnimpx:
T ← RTemp ← (RTemp) + (EscTrapOffset), GoToP[BackTrap];

moStkPSave:
T ← (SStkP&NStkP) xor (377C);
moStkPSwap:
RTemp ← T, StkP ← RTemp, NoRegILockOK, Return;

moPsh:
LU ← NextInst[IBuf];
Stack&+1, NIRet;

moPshRRTemp:
T ← (SStkP&NStkP) xor (377C), Call[moStkPSwap];
T ← Stack, Call[moStkPSwap];*T ← RM register, restore StkP
moPushT:
LU ← NextInst[IBuf];
Stack&+1 ← T, NIRet;

moRet:
Return;


MC[EscLast,216];

*ESC--extended Opcodes accessed by dispatching on alpha.
@ESC:
T ← MNBR ← CNextData[IBuf], Call[EscDispatch], Opcode[273];
LU ← NextInst[IBuf], CallX[P6Tailx];

*ESCL--extended opcodes dispatching on alpha using beta as operand.
@ESCL:
T ← MNBR ← CNextData[IBuf], Call[EscDispatch], Opcode[274];
LU ← NextInst[IBuf], CallX[P6Tailx];
EscDispatch:
RTemp ← T, LoadPage[moPage], Skip[H2Bit8];
Dispatch[RTemp,10,4], GoToP[EscDFirst];
LU ← (RTemp) - (EscLast) - 1, GoToP[.+1];
OnPage[moPage];
Dispatch[RTemp,10,4], GoTo[moUnimp,ALU>=0];
EscDFirst:
Dispatch[RTemp,14,4], Disp[.+1];*Dispatch on 2nd byte
TrapParm ← T, LoadPage[xfPage1], Disp[Esc00], At[EscTop,0];* 0b-17b
Disp[Esc20], At[EscTop,1];*20b-37b
Disp[Esc40], At[EscTop,2];*40b-57b
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscTop,3];
:UNLESS[WithFloatingPoint]; **************************** MesaFP
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscTop,4];
:ENDIF; ************************************************
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscTop,5];
:UNLESS[WithCedar]; ************************************ CedarGC
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscTop,6];
:ENDIF; ************************************************
Disp[Esc160], At[EscTop,7];*160b-177b
Disp[Esc200], At[EscTop,10];*200b-217b

*0 to 6 (@ME, @MX, @MW, @MR, @NC, @BC, and @REQ) are in MesaP.Mc.

%Map opcodes

XMap[LP,xBuf,0] writes the map from xBuf and dumps the old contents into
xBuf1, xBuf2, and xBuf3. Timing is as follows: 2 cycles for the XMap mi,
7 cycles in MC1, 1 cycle of RM transport, 2 more cycles in MC1, and 3 final
cycles of RM transport. These opcodes deal with 32-bit virtual and real
page numbers for machine independence, but only 12d bits of real page number
and 14d bits of virtual page number are legal here. Higher bits of real
page number and bits of virtual page number above 16d are thrown away
without any error checks. Since pages are 256d words long, LP/LPhi are
loaded from the virtual page number lshift 8. Since displacement from LP/LPhi
is 0, only the left-half of LPhi need be valid. Since a map entry may be in
use by an io task while being changed, no tasking may occur when the map is
invalid.

Set Map (Timing: 42.5 cycles) accepts:
TOS/
Flags (12d=LogSE, 13d=protected, 14d=dirty, 15d=referenced)
2OS,,3OS/
Real page number (***high part and top 4 bits of low part
are assumed 0)
4OS,,5OS/
Virtual page number (***high part assumed 0)
Writes the map and returns with stack popped five times.
%
Esc00:
@SM:
T ← LSh[Stack&-2,14], GoToP[.+1], At[EscD0,7];
OnPage[xfPage1];
*Get RPlo and pop StkP across VPhi.
T ← (LdF[Stack&-2,4,14]) or T, Task;
xBuf ← T;
***Clear LogSE because of LoadPage problems when single-errors are logged.
xBuf ← (xBuf) and not (100000C), Call[MapLP];
SM1:
XMap[LP,xBuf,0], Call[xfRet];*Need this extra task here
xfTail:LU ← NextInst[IBuf], CallX[xfTailx];

*Subr MapLP creates a BR pair from a virtual page number for the Map opcodes.
MapLP:
T ← LSh[Stack,10];
MapLPx:
LP ← T;*Set low Base
*Only the left half of the high base register matters.
T ← Stack&-1, Skip[R>=0];
LPhi ← (LPhi) or not (0C), GoTo[xfRet];*Set bit 1 if 0 is set
*1 extra mi needed after loading LPhi before ref.
LPhi ← T, GoTo[xfRet];

*Subroutine MapPushOld pushes old flags; returns value for restoring map in T.
MapPushOld:
T ← LSh[xBuf3,10];*Put flags,,card,blk0 in left byte
*T ← value for restoring map entry (complement all the bits to high true)
T ← xBuf1 ← (RHMask[xBuf1]) xnor T;*blk1,rowaddr in low byte
*NOTE: must be non-tasking return because map invalid here.
Stack&+1 ← T, UseCTask;
Stack ← RSh[Stack,14], Return;*Old flags right-justified

%Set Map Flags (Timing ~ 77.5 to 85.5 cycles) accepts:
TOS/
New map flags
2OS,,3OS/
Virtual page number
Unless entry is empty, writes the map with the new flags and returns:
TOS,,2OS/
Real page number
3OS/
Old map flags
%
@SMF:
T ← LSh[Stack&-2,14], GoToP[.+1], At[EscD0,10];
OnPage[xfPage1];
xBuf ← T, Call[MapLP];
**Non-tasking time is about 51 cycles here.
XMap[LP,xBuf,0], Call[MapPushOld];
LU ← (LdF[xBuf3,11,3]) - 1; *=0 if map entry = VACANT
*Rewrite the map entry with the old flags and page if it was vacant
*(defined as protected, dirty, and referenced); otherwise, rewrite with new
*flags and old page.
Stack&+1 ← T, GoTo[SM0,ALU=0];
T ← Stack ← (Stack) and not (170000C);*Low part of old real page
**LogSE ignored here because of LoadPage gotcha problems.
xBuf ← (LdF[xBuf,1,17]) or T, GoTo[SM2];*New flags, old page

%Get Map Flags (Timing: 69.5 to 76.5 cycles) accepts:
TOS,,2OS/
Virtual page number
Reads the map and returns:
TOS,,2OS/
Real page number
3OS/
Map flags
%
@GMF:
LU ← Stack&-1, GoToP[.+1], At[EscD0,11];
OnPage[xfPage1];
T ← LSh[Stack,10], Call[MapLPx];
**Non-tasking time is about 48 cycles here.
XMap[LP,xBuf,0], Call[MapPushOld];
Stack&+1 ← T;
SM0:
Stack ← (Stack) and not (170000C);*Low part of real page
xBuf ← T;*Restore old flags and page
SM2:
Stack&+1 ← 0C, GoTo[SM1];


*Allocate Frame (Timing: 40.5 + WriteTail + 23 cycles/indirect)
@AF:
T ← (Stack&-1) + (AVOffset), GoToP[.+1], At[EscD0,12];*T ← FSI+xfAV
OnPage[xfPage1];
PFetch1[MDS,xfTemp], Call[TtoxfCount];*Head-of-list (can’t fault)
CycleControl ← xfCount, Call[.+1];
*Indirect allocate returns here
LU ← LdF[xfTemp,16,1], GoTo[AllocFail,R Odd];
T ← xfTemp, Skip[ALU=0];
xfTemp ← RSh[xfTemp,2], GoTo[AllocInd];*In MesaOP3
*Allocate succeeded; complete allocate and done.
PFetch1[MDS,RTemp];*This is last reference that can fault.
Stack&+1 ← T, Task;
T ← xfCount;
PStore1[MDS,RTemp], GoTo[xfTail];

*Allocate failed. Cause FrameFault with destination as parameter.
AllocFail:
Stack&+1, LoadPage[opPage0];
*These two subroutines serve merely to do xfTemp1 ← backed up PC.
T ← (PCXreg) - 1, CallP[BackPC];
Call[SavPCinFrame];
*Original frame size index (FSI) is trap parameter.
T ← RSh[Cycle&PCXF,10], GoTo[FrameFault1];

*FreeFrame (Timing: 60.5 to 67.5 cycles)
@FF:
T ← (Stack&-1) - (4C), GoToP[.+1], At[EscD0,13];
OnPage[xfPage1];
PFetch1[MDS,xfFSI], Task;
xfFrame ← T;
xfFSI ← RHMask[xfFSI], Call[FreeSub0];
PStore1[MDS,xfFrame], GoTo[xfTail];

*Port In
*[-1OS] ← 0; if -2OS .ne. 0 then [-1OS+1] ← -2OS
*Timing: 50.5 to 57.5 cycles if CacheLocals=0 and both PStore1’s are done.
@PI:
Stack&+2, GoToP[.+1], At[EscD0,14];
OnPage[xfPage1];
T ← Stack&-1, LoadPage[piPage];*-2OS
RTemp ← T, LoadPage[opPage1];
OnPage[piPage];
:IF[CacheLocals]; ********************************
T ← Stack&-1, GoToP[portinz,ALU=0];*-1OS
OnPage[opPage1];
PStore1[MDS,RZero], Call[P5Ret];
T ← (RZero) + T + 1;
portinz:
PStore1[MDS,RTemp];
T ← (LOCAL) - T - 1, Call[P5Ret];
*Reload store-through LOCAL cache if port was in active frame.
LU ← (Form-4[AllOnes]) and T, GoTo[CLCR2];
:ELSE; *******************************************
T ← Stack&-1, SkipP[ALU#0];*-1OS
OnPage[opPage1];
PStore1[MDS,RTemp], Return;
PStore1[MDS,RZero], Task;
T ← (RZero) + T + 1;
PStore1[MDS,RTemp], GoTo[P5Tail];
:ENDIF; ******************************************

*Port Out
*xfMX,xfMY ← [TOS+1],TOS; [TOS] ← LOCAL; Xfer
@PO:
T ← (Stack&-1) + 1, GoToP[.+1], At[EscD0,15];
OnPage[xfPage1];
POR1:
PFetch1[MDS,xfMX];
xfMY ← T, Task;
xfMY ← T ← (xfMY) - 1;
PStore1[MDS,LOCAL], Call[SavPCinFrame];
GoTo[Xfer];

*Port Out Responding
@POR:
T ← (Stack&-1) + 1, GoToP[POR1], At[EscD0,16];

*Esc 17b (@SPP) is in MesaP.Mc

Esc20:
*Disable Interrupts (Timing: 23.5 cycles) (checked).
@DI:
xfWDC ← (xfWDC) + 1, At[EscD1,0];
Skip[ALU=0];
Iok:
LU ← NextInst[IBuf], CallX[moTailx];
IErr:
LoadPage[opPage0];
RTemp ← sInterruptError, GoToP[P4Trap];

*Enable Interrupts--also jump here from @WRWDC.
*Timing: 14.5+17 cycles if NWW .eq. 0 else 14.5+20 cycles.
EI:
RTemp ← IP[NWW]C, At[EscD1,1];
T ← (SStkP&NStkP) xor (377C), Call[moStkPSwap];
LU ← Stack&-1;*Test NWW, point StkP at RSImage
xfWDC ← (xfWDC) - 1, Skip[ALU#0];
*Carry’ implies that the old value was 0 and is going negative.
StkP ← RTemp, DblGoTo[IErr,Iok,Carry’];
*Set IntPending in RS232 and in RSImage
T ← Stack ← (Stack) or (IntPendingBit), Skip[Carry];
StkP ← RTemp, RS232 ← T, GoTo[IErr];
StkP ← RTemp, RS232 ← T, GoTo[moTail];

*Exclusive Or (Timing: 20.5 cycles) (checked).
@XOR:
T ← Stack&-1, At[EscD1,2];
LU ← NextInst[IBuf];
Stack ← (Stack) xor T, NIRet;

:IF[WithDLogic]; ***************************************
OnPage[ppPage];
PshPop2:
Stack&+1;
T ← Stack&-2, Return;

*Double And--have trap support (Timing: 28.5 cycles) (checked).
@DAND:
T ← Stack&-2, LoadPage[ppPage], At[EscD1,3];
Stack ← (Stack) and T, CallP[PshPop2];
Stack ← (Stack) and T, GoTo[moPsh];

*Double Inclusive Or--have trap support (Timing: 28.5 cycles) (checked).
@DIOR:
T ← Stack&-2, LoadPage[ppPage], At[EscD1,4];
Stack ← (Stack) or T, CallP[PshPop2];
Stack ← (Stack) or T, GoTo[moPsh];

*Double Exclusive Or--have trap support (Timing: 28.5 cycles) (checked).
@DXOR:
T ← Stack&-2, LoadPage[ppPage], At[EscD1,5];
Stack ← (Stack) xor T, CallP[PshPop2];
Stack ← (Stack) xor T, GoTo[moPsh];
:ELSE; *************************************************
@DAND:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD1,3];
@DIOR:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD1,4];
@DXOR:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD1,5];
:ENDIF; ************************************************

%CycleControl controls the action of the RF, WFA, and WFB functions; bits
0:3 of CycleControl are D, bits 4:7 are M.

RF right-justifies a register and masks out all but M bits of a field:
A ← RCy[Reg,15d-D-M] & Mask1[M]
where Mask1[M] contains 1’s in bits 15d-M through 15d.

WFA shifts a source register to its correct position in the destination and
masks out all bits except those in the field:
A ← LCy[Reg,15-D-M] & Mask2[D,M] -or-
A ← RCy[Reg,D+M+1] & Mask2[D,M]
where Mask2[D,M] contains 1’s in bits D through D+M.

WFB inserts a field in the destination word:
A ← (Reg) and not Mask2[D,M].


Rotate--have trap support (Timing: 26.5 cycles) (checked).
Left cycles 2OS by TOS bits?? To do this CycleControl must be loaded
with the left-cycle count in the 1st nibble and 17b in the 2nd nibble;
then RF is used to cycle the stack.
%
:IF[WithRotate]; ***************************************
@ROTATE:
T ← LSh[Stack&-1,4], At[EscD1,6];
RTemp ← T, LoadPage[opPage1];
RTemp ← (RTemp) or (17C);
OnPage[opPage1];
CycleControl ← RTemp, GoTo[RFLx];*RFLx in MesaOP1.Mc.
:ELSE; *************************************************
@ROTATE:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD1,6];
:ENDIF; ************************************************


%Double Shift--have trap support
TOS = shift count (+ .eq. left, - .eq. right), 2OS,,3OS = doubleword shifted.
Timing: 28.5 cycles if S .ge. 40b; 30.5 cycles if S .le. -40b;
34.5 cycles if S .eq. -20b;
45.5 or 48.5 cycles if 0 .le. S .le 37b
44.5 or 45.5 cycles if -1 .ge. S .ge. -37b and S .ne. -20b.
%
:IF[WithDShift]; ***************************************
@DSHIFT:
T ← Stack&-1, LoadPage[opPage3], At[EscD1,7];
*For a left shift by S, load CycleControl with D,,M = 0,,17b-S and use WFA.
*For a right shift by S, load CycleControl with D,,M = 17b,,20b-S and use RF.
RTemp ← T, GoToP[DShiftRight,ALU<0];
OnPage[opPage3];
DShiftLeft:
LU ← (RTemp) - (40C);
*For rightshift by 20b-S, control is 17b,,20b-(20b-S) = 17b,,S.
T ← (RTemp) or (360C), Skip[ALU<0];
T ← Stack&-1 ← 0C, GoTo[P7PushT];*Shift .ge. 40b gives zero
Stack&-1, LoadPage[dsPage1];
RTemp1 ← T;
OnPage[dsPage1];
*For leftshift by S, the control 0,,17b-S is generated as 377b - (17b,,S).
T ← (R400) - T - 1, LoadPage[opPage3];*377 - T.
CycleControl ← RTemp1, RTemp1 ← T, NoRegILockOK;
OnPage[opPage3];
T ← RF[Stack];
LU ← LdF[RTemp,13,1];
CycleControl ← RTemp1, Skip[ALU#0];
*Left-shift of 0 .le. S .le. 17b: left-shift low word 1st.
Stack ← WFA[Stack], GoTo[DSL1];
*Left-shift of 20b .le. S .le. 37b.
T ← WFA[Stack];
Stack ← 0C, GoTo[P7PushT];

DSL1:
Stack&+1, LoadPage[opPage3];
*Left-shift high word and OR bits shifted out of low word.
**Could save 2 cycles here with NextInst; Stack ← (WFA[Stack]) or T, NIRet;
T ← (WFA[Stack&-1]) or T, GoToP[P7PushT];

DShiftRight:
LU ← (RTemp) + (40C);
T ← (LdF[RTemp,14,4]) - 1, Skip[ALU>=0];
T ← Stack&-1 ← 0C, GoTo[P7PushT];*Shift .le. -40b gives zero
*For leftshift by S, control is 0,,17b-S .eq. S-1 for S .ne. 0.
RTemp1 ← T, GoTo[.+3,ALU>=0];
Stack&-1 ← Stack&-1;*Shift of exactly 20b
DSRBig:
T ← 0C, GoTo[P7PushT];
*For rightshift by -S, control is 17b,,S
T ← (RTemp) or (360C);
CycleControl ← RTemp1, RTemp1 ← T, NoRegILockOK;
T ← WFA[Stack];
LU ← LdF[RTemp,13,1];
CycleControl ← RTemp1, Skip[ALU#0];
*21b .le. S .le. 37b: copy shifted high word to low and zero high word.
Stack&-1 ← RF[Stack&-1], GoTo[DSRBig];
Stack ← RF[Stack];*Right shift high word
Stack&-1, LoadPage[moPage];
*Right shift low word, ORing bits shifted left from high word
Stack ← (RF[Stack]) or T, GoToP[moPsh];
:ELSE; *************************************************
@DSHIFT:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD1,7];
:ENDIF; ************************************************

*Lengthen Integer--have trap support (Timing: 23.5 or 24.5 cycles) (checked).
:IF[WithLInt]; *****************************************
@LINT:
T ← Stack&-1, LoadPage[opPage2], Skip[R<0], At[EscD1,10];
T ← 0C, GoToP[SkipPushT];
T ← AllOnes, GoToP[SkipPushT];*SkipPushT in MesaOP2.Mc
:ELSE; *************************************************
@LINT:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD1,10];
:ENDIF; ************************************************

*Jump Stack--have trap support (Timing: 38.5 cycles).
:IF[WithJS]; *******************************************
@JS:
LU ← PCF ← Stack&-1, At[EscD1,11];
Stack&+1, LoadPage[opPage2];
T ← RSh[Stack&-1,1];
OnPage[opPage2];
*PFetch4 on same page as jumps for Fault.Mc
PFetch4[CODE,IBuf];
PCB ← T, GoTo[P6Tail];*Bypass kludge
:ELSE; *************************************************
@JS:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD1,11];
:ENDIF; ************************************************

*Read Code Field Stack (Timing: 38.5 cycles).
@RCFS:
T ← RSh[Stack&-1,10], At[EscD1,12];*T ← Desc.Offset
T ← (Stack&+1) + T, LoadPage[opPage1];*Offset + Desc.Offset
CycleControl ← Stack&-2;*Desc.Field
OnPage[opPage1];
PFetch1[CODE,Stack], GoTo[RFLx];

*Read Code (Timing: 29.75 cycles to 34.75 cycles).
@RC:
T ← NextData[IBuf], At[EscD1,13];
T ← (Stack&-1) + T;
PFetch1[CODE,Stack], GoTo[moTail];

*Load Local Overhead Byte. User must ensure alpha in 1..4.
*To add error check do LU ← (Form-4[AllOnes]) and T; Skip[ALU=0].
*Timing: 29.75 to 34.75 cycles).
@LLOB:
T ← (NextData[IBuf]) - 1, At[EscD1,14];
T ← (RZero) - T - 1;*T ← - alpha.
PFetch1[LOCAL,Stack], GoTo[moTail];

*Read Overhead Byte. Error check alpha in 1..4
*(Timing: 29.75 to 34.75 cycles).
@ROB:
T ← (NextData[IBuf]) - 1, At[EscD1,15];
T ← (Stack&-1) - T - 1;
PFetch1[MDS,Stack], GoTo[moTail];

*Store Local Overhead Byte. Error check alpha in 1..4
*(Timing: 30.75 to 37.75 cycles).
@SLOB:
T ← (NextData[IBuf]) - 1, At[EscD1,16];
T ← (RZero) - T - 1, Call[moRet];**Avoid long refill path
PStore1[LOCAL,Stack], GoTo[moTail];

*Write Overhead Byte. Error check alpha in 1..4
*(Timing: 30.75 to 37.75 cycles).
@WOB:
T ← (NextData[IBuf]) - 1, At[EscD1,17];
T ← (Stack&-1) - T - 1, Call[moRet];**Avoid long refill path
***Local cache ok here?
PStore1[MDS,Stack], GoTo[moTail];

Esc40:
%Dump Stack.
Save state and set StkP to 0. LOCAL+alpha points at the MDS block where
state is saved; the stack is saved at +0 to +MaxStack-1; BrkByte,,StkP is
saved at +MaxStack.
Timing: 47.75 + 17/word cycles.
%
@DSK:
T ← SStkP, At[EscD2,0];*SStkP .eq. (NStkP xor 377) here
T ← (LSh[xfBrkByte,10]) + T;
RTemp ← T, LoadPage[opPage3];
xfTemp1 ← T ← MaxStack;
OnPage[opPage3];
*This can be any page with Tail
T ← (MNBR ← CNextData[IBuf]) + T, Task;
PStore1[LOCAL,RTemp];
T ← (RTemp) - (Sub[MaxStack!,1]C);
*xfTemp1 ← StkP ← Min(StkP+2,MaxStack)
Skip[Carry];*Skip if SStkP .gr. MaxStack
xfTemp1 ← (xfTemp1) + T + 1;
T ← (StkP ← xfTemp1) - 1, Call[svpop];
*Loop here with 377b in T
T ← (NStkP) xor T;
T ← (AllOnes) + T, Skip[ALU#0];*T ← offset in saved Stack
:IF[CacheLocals]; **************************************
xfBrkByte ← 40400C, GoTo[BLTdonex];
:ELSE; *************************************************
xfBrkByte ← 40400C, GoTo[P7Tail];
:ENDIF; ************************************************
svpop:
T ← (MNBR) + T;
PStore1[LOCAL,Stack];*Write stack at alpha+StkP-1
sv377:
T ← 377C, Return;


%Load State and Enable
NOTE: The programmer must ensure that LSTE doesn’t cause a trap or fault
because xfWDC would be wrong. LSTE is used by fixed frame trap handlers
which must run with interrupts disabled.
***No error check on xfWDC decrement.
Timing: 16.75+35 cycles to the PFetch1 at LSTgo
%
*NextData must precede SavePC call
@LSTE:
T ← NextData[IBuf], At[EscD2,1];
xfTemp ← T, LoadPage[xfPage1];
T ← LSh[CODE,1], CallP[SavPCinFrame1];
xfWDC ← (xfWDC) - 1, LoadPage[LSPage];
T ← (xfTemp) + (MaxStack), GoToP[LSTgo];

%Load State and Free
Timing: 16.75+15 cycles to LSTgo
%
@LSTF:
T ← (LOCAL) - (4C), At[EscD2,2];
PFetch1[MDS,xfFSI];
xfFrame ← T, LoadPage[LSPage];
MemStat ← FreeFrame, GoToP[.+1];
OnPage[LSPage];
T ← NextData[IBuf];
*xfTemp is pointer to saved state.
xfTemp ← T;
T ← (xfTemp) + (MaxStack);
*Timing from here: (34 or 35)+14/word cycles
LSTgo:
PFetch1[LOCAL,IBuf];*IBuf ← break byte,,StkP
xfBrkByte ← 40400C, Task;
T ← (xfTemp) + (Add[MaxStack!,1]C);
PFetch1[LOCAL,xfMX];*xfMX ← destination link
T ← (xfTemp) + (Add[MaxStack!,2]C);
StkP ← RZero;
PFetch1[LOCAL,xfMY], Task;*xfMY ← source link
*Fetch Min[StkP+2,MaxStack] words to the Stack; load xfTemp1 with
*Min[StkP+1,MaxStack-1] (i.e., word count - 1).
T ← xfTemp1 ← Sub[MaxStack!,1]C;
T ← (LdF[IBuf,14,4]) - T;
*NewStkP - (MaxStack-1) will carry when NewStkP .gr. MaxStack-2.
IBuf ← (IBuf) and not (360C), Skip[Carry];
xfTemp1 ← (xfTemp1) + T + 1;*xfTemp1 ← NewStkP+1
T ← xfTemp, Call[.+2];
*Loop here
T ← (Zero) + T + 1, xfTemp1, GoTo[.+3,R<0];
PFetch1[LOCAL,Stack];
xfTemp1 ← (xfTemp1) - 1, Return;*Loop
T ← RSh[IBuf,10];
xfBrkByte ← (xfBrkByte) or T, LoadPage[xfPage1];
StkP ← IBuf, GoToP[Xfer];

*Descriptor Byte Stack (Timing: 16.75+20+6 = 42.75 cycles)
@DBS:
T ← (Stack&-1) - (GFlagsOffset), At[EscD2,3];
PFetch1[MDS,RTemp], Call[moRet];
T ← (RTemp) and not (77C);
T ← (NextData[IBuf]) + T + 1, CallX[moPushT];

*Divide--(0,,TOS-1)/TOS. Single word dividend and divisor, no overflow check.
*The remainder is left above the Stack.
@UDIV:
MNBR ← Stack&-1, At[EscD2,4];
T ← RZero, LoadPage[dvPage1], GoTo[UDiv1];

%Long Divide--(TOS-1,,TOS-2)/TOS. Double-word dividend, single-word divisor,
no overflow check. The remainder is left above the Stack.
dividend low in Stack; hi in RTemp
divisor in T
quotient appears in Stack; remainder in RTemp
Timing: ~ 42.5 + (9/quotient 1) + (12/quotient 0) cycles
%
@LUDIV:
MNBR ← Stack&-1, At[EscD2,5];
T ← Stack&-1, LoadPage[dvPage1];
UDiv1:
RTemp ← T, LoadPage[opPage0], GoToP[.+1];
OnPage[dvPage1];
T ← MNBR, GoToP[.+1];*T ← divisor
OnPage[opPage0];
LU ← (RTemp) - T, Skip[ALU#0];
RTemp ← sDivZeroTrap, GoTo[P4Trap];
xfTemp ← 16C, Skip[Carry’];
RTemp ← sDivCheckTrap, GoTo[P4Trap];
RTemp1 ← (Zero) - T;*Save minusdivisor
T ← 31C, Task;*SALUFOP = A+A+1
SALUF ← T;
*The 1st bit shifted into stack is "Don’t care."
Stack ← (Stack) SALUFOP T, Call[DvStart];
*Shift the high dividend while simultaneously subtracting the divisor and
*adding in the bit shifted out of the low word in the previous step.
T ← RTemp ← (LSh[RTemp,1]) + T + 1, Skip[R>=0];
*Shift the low dividend while bringing in the quotient bit
Stack ← (Stack) SALUFOP T, GoTo[DvSub];*q bit 1
*Subtract ok?
Stack ← (Stack) SALUFOP T, UseCOutAsCIn, GoTo[DvSub,Carry];
*No--undo by adding back the divisor. Would like to simply add in the next
*step (i.e., ((X+divs) lshift 1) - divs .eq. (X lshift 1) + divs), but the
*carry gets screwed up.
T ← RTemp1, FreezeResult;
T ← RTemp ← (RTemp) - T, FreezeResult;
DvSub:
xfTemp ← (xfTemp) - 1, FreezeResult, GoTo[DvStart,R>=0];
Stack&+1 ← T;
P4Pop:
LU ← NextInst[IBuf];
Stack&-1, NIRet;
*Get minusdivisor + bit shifted out of low word
DvStart:
T ← (RTemp1) - 1, UseCOutAsCIn, Return;

*Nil Check Long (Timing: 25.5 cycles)
@NILCKL:
T ← Stack&-1, LoadPage[ncPage], At[EscD2,6];
LU ← (Stack&+1) or T, LoadPage[opPage0];
OnPage[ncPage];
RTemp ← sPointerTrap, DblGoToP[P4Trap,P4NoTrap,ALU=0];


OnPage[opPage3];
:IF[LPChecking]; ***************************************
BLTSetup:
LPDestHi ← (LSh[LPDestHi,10]) + T + 1, Skip[ALU=0];
LPDestHi ← (Zero) - 1;
T ← Stack&-2, LoadPage[opPage1];
LPDest ← T, GoToP[StackLP];
:ELSE; *************************************************
BLTSetup:
LPDest ← T, LoadPage[opPage1];
T ← RHMask[Stack&-1], GoToP[StackLPx];
:ENDIF; ************************************************

*Block Transfer Long Reversed--have trap support.
*TOS,,2OS is a long pointer to a destination block, 3OS a cardinal count,
*and 4OS,,5OS a long pointer to a source block. Transfer the block.
*Timing = 45.5 + 23/word cycles.
:IF[WithBLTLR]; ****************************************
:IF[LPChecking]; ***************************************
@BLTLR:
T ← Stack&-1, LoadPage[opPage3], At[EscD2,7];
LPDestHi ← T;
OnPage[opPage3];
LU ← LdF[LPDestHi,0,12], Call[BLTSetup];
:ELSE; *************************************************
@BLTLR:
T ← RHMask[Stack&-1], At[EscD2,7];
LPDestHi ← T, LoadPage[opPage3];
LPDestHi ← (LSh[LPDestHi,10]) + T + 1;
OnPage[opPage3];
T ← Stack&-2, Call[BLTSetup];
:ENDIF; ************************************************
Stack&+2;
LU ← Stack;
BLTLR1:
T ← (Stack) - 1, Skip[ALU#0];
Stack&-3, GoTo[BLTdonex];
PFetch1[LP,RTemp];
Nop;
PStore1[LPDest,RTemp], Call[P7Ret];
*3 mi to allow for page fault before changing count on stack.
Call[P7Ret];
Stack ← (Stack) - 1, GoTo[BLTLR1,IntPending’];*Decrement count
Stack&+2, GoTo[BLTint];
:ELSE; *************************************************
@BLTLR:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,7];
:ENDIF; ************************************************


:IF[WithBLEL]; *****************************************
%Block Equal Long--have trap support.
TOS,,2OS and 4OS,,5OS are long pointers to two blocks; 3OS is a cardinal
count of words to compare. If any words are different, push 0, else 1.
Timing: ? + 27/true word cycles.
%
:IF[LPChecking]; ***************************************
@BLEL:
T ← Stack&-1, LoadPage[opPage3], At[EscD2,10];
LPDestHi ← T;
OnPage[opPage3];
LU ← LdF[LPDestHi,0,12], Call[BLTSetup];
:ELSE; *************************************************
@BLEL:
T ← RHMask[Stack&-1], At[EscD2,10];
LPDestHi ← T, LoadPage[opPage3];
LPDestHi ← (LSh[LPDestHi,10]) + T + 1;
OnPage[opPage3];
T ← Stack&-2, Call[BLTSetup];
:ENDIF; ************************************************
Stack&+3;
LU ← Stack, Call[BLEL0];*Point at word count
*Loop returns here to compare words from last iteration.
T ← RTemp;
LU ← (RTemp1) xor T;
Stack ← (Stack) - 1, Skip[ALU=0];
*Some word is different--return 0 (false).
Stack&-2 ← 0C, GoTo[P7Tail];
BLEL0:
T ← (Stack) - 1, LoadPage[opPage0], Skip[ALU#0];
*All words the same--return 1 (true).
Stack&-2 ← 1C, GoToP[P4Tail];
PFetch1[LP,RTemp], SkipP[IntPending’];
OnPage[opPage0];
Stack&+2, GoTo[NopInt];
Nop;*Let T← finish
BLECL1:
PFetch1[LPDest,RTemp1], Return;


%Block Equal Code Long--have trap support.
TOS,,2OS is long pointer, 3OS a cardinal word count, and 4OS a cardinal
offset relative to CODE. If any words of the two blocks are different,
push 0, else 1.
Timing: 14.5+6+?+4+(8 to true exit, 34 to false exit) + 29/true word cycles.
%
:IF[LPChecking]; ***************************************
@BLECL:
T ← Stack&-1, LoadPage[opPage3], At[EscD2,11];
LPDestHi ← T;
OnPage[opPage3];
LU ← LdF[LPDestHi,0,12], Call[StackLPDest1];
:ELSE; *************************************************
@BLECL:
T ← RHMask[Stack&-1], At[EscD2,11];
LPDestHi ← T, LoadPage[opPage3];
LPDestHi ← (LSh[LPDestHi,10]) + T + 1;
OnPage[opPage3];
T ← Stack&-2, Call[StackLPDest2];
:ENDIF; ************************************************
T ← (Stack&+1) - 1;*T ← CODE offset - 1
LU ← Stack, Call[BLECL0];*Test word count
*Loop returns here to compare words from last iteration.
T ← RTemp;
LU ← (RTemp1) xor T;
T ← (Stack&+1) - 1, Skip[ALU=0];*T ← CODE offset - 1
*Some word is different--return 0 (false).
Stack&-1 ← 0C, GoTo[P7Tail];
Stack ← (Stack) - 1;
*T ← word count + CODE offset - 1.
BLECL0:
T ← (Stack) + T, LoadPage[opPage0], Skip[ALU#0];
*All words the same--return 1 (true).
Stack&-1 ← 1C, GoToP[P4Tail];
PFetch1[CODE,RTemp], SkipP[IntPending’];
OnPage[opPage0];
Stack&+2, GoTo[NopInt];
T ← (Stack&-1) - 1, GoTo[BLECL1];*T ← word count - 1
:ELSE; *************************************************
@BLEL:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,10];
@BLECL:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,11];
:ENDIF; ************************************************

%Software (Pup) Checksum Opcode
FOR i IN [0..size) DO sum ← LeftCycle[OnesAdd[sum,x[i]]]; ENDLOOP;

TOS-0, Stack3: high half of pointer
TOS-1, Stack2: low half of pointer
TOS-2, Stack1: count (hickup if cross 64k boundary)
TOS-3, Stack0: sum (must be initialized to 0)
Rest of Stack must be empty
%
:IF[LPChecking]; ***************************************
@CKSUM:
T ← Stack&-1, LoadPage[opPage1], At[EscD2,12];
LPhi ← T, CallP[StackLPy];
:ELSE; *************************************************
@CKSUM:
T ← RHMask[Stack&-1], At[EscD2,12];
LPhi ← T, LoadPage[opPage1];
LPhi ← (LSh[LPhi,10]) + T + 1, CallP[StackLPz];
:ENDIF; ************************************************
*StackLPy Returns after LP,,LPhi ← Stack2,,Stack3 with Stack popped twice,
*LPhi bounds-checked and in base register format.
LU ← Stack&+1, LoadPage[csPage];
GoToP[CSBeg,ALU#0];
OnPage[csPage];
CSEnd:
LU ← (Stack0) xnor (0C);
LoadPage[moPage], Skip[ALU#0];
Stack&-2 ← 0C, GoToP[moTail];
Stack&-2, GoToP[moTail];

*Possible page fault after next mi, so no Stack changes until it is completed.
CSLoop:
Dispatch[Stack2,16,2], GoTo[.+3,Carry’];
LPhi ← (LPhi) + (400C) + 1;*64k boundary crossing
Stack3 ← (Stack3) + 1, GoTo[.-2];
Stack2 ← T, Disp[.+1];
T ← LCy[xBuf,1], Call[CSWord], DispTable[4];
T ← LCy[xBuf1,1], Call[CSWord];
T ← LCy[xBuf2,1], Call[CSWord];
T ← LCy[xBuf3,1], Call[CSWord];
PFetch4[LP,xBuf,0], Skip;
CSBeg:
PFetch4[LP,xBuf,0];
LP ← (LP) or (3C);*Compute address of next quadword
T ← LP ← (LP) + 1, GoTo[CSLoop,IntPending’];
Stack&+1;
LoadPage[opPage0];*Earliest mi for LoadPage
GoToP[NopInt];

CSWord:
Stack1 ← (Stack1) - 1;
Stack0 ← (LCy[Stack0,1]) + T, Skip[ALU=0];
Stack0 ← (Stack0) + 1, UseCOutAsCIn, Return;
Stack0 ← (Stack0) + 1, UseCOutAsCIn, GoTo[CSEnd];

*Bit Block Transfer--code in BitBlt.Mc
:IF[WithBitBlt]; ***************************************
@BITBLT:
T ← MDShi, LoadPage[bbp1], At[EscD2,13];
bbArgHi ← T, GoTo[MesaBitBLT];
:ELSE; *************************************************
@BITBLT:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,13];
:ENDIF; ************************************************

*Text Block Transfer
:IF[WithTextBlt]; **************************************
@TXTBLT:
T ← txrArgSPtr, LoadPage[TxP1], At[EscD2,14];
txrArgLo ← T, GoTo[TextBlt];
:ELSE; *************************************************
@TXTBLT:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,14];
:ENDIF; ************************************************

*Byte Block Transfer--have trap support.
@BYTBLT:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,15];

*Byte Block Transfer Reversed--have trap support.
@BYTBLTR:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,16];

%Returns 32d-bit result with information about the microcode.

TOS:
Day of release encoded as number of days since 1 January 1901

2OS:
Bits 0:3
Engineering number (1 = Alto I, 2 = Alto II without extended
memory, 3 = Alto II with extended memory, 4 = Dolphin,
5 = Dorado, 6 = Dandelion, 7 = Dicentra; these assignments
are historical from the Alto VERS opcode).
Bits 4:7
Version number mod 16d
Bits 8:15
Flags:
Bit 141 = have floating point microcode
Bit 151 = have Cedar microcode

Pilot time is a long cardinal equal to the number of seconds since
midnight 1 January 1901. This opcode pushes the number of days since 1 Jan
1901; to convert to Pilot time, lengthen the result and multiply by 86,400.

For the macros below, 1904, 1908, ..., 1980, 1984, etc. are leap years.
So 1 January 1982 is 81*365 + 19 days = 29584 days (=71620b) after
1 January 1901; 365d = 555b.
%
Macro[MakeYear,Set[YearOffset,
Add[71620,Select[Sub[#1,122],0,555,1332,2110,2665,3442,4217,4775]]]
Set[LeapOffset,Select[Sub[#1,122],0,0,1,0,0,0,1,0]]
];

Macro[MakeDay,Set[DayOffset,Sub[#1,1]]];

Macro[MakeMonth,Set[MonthOffset,
IFG[#1,6,Select[Sub[#1,7],265,324,363,421,460,516],
Select[Sub[#1,1],0,37,73,132,170,227]]]
IFG[MonthOffset,72,Set[MonthOffset,Add[LeapOffset,MonthOffset]]]
];

Set[VersionNumber,0];
MakeYear[122];
*82
MakeMonth[12];
*October
MakeDay[15];
*13

Set[VersionFlags,Add[40000,
LShift[VersionNumber,10],
WithCedar,
LShift[WithFLoatingPoint,1]
]];
Set[ReleaseDate,Add[YearOffset,MonthOffset,DayOffset]];

@VERSION:
Stack&+1 ← HiA[VersionFlags], At[EscD2,17];
Stack ← (Stack) or (LoA[VersionFlags]);
Stack&+1 ← HiA[ReleaseDate];
Stack ← (Stack) or (LoA[ReleaseDate]), GoTo[moTail];

%Esc60:
*Double Multiply. Have trap support.
*DMUL multiplies two 32-bit integers and returns a 32-bit integer. If the
*quotient exceeds 32 bits, the high-order 32 bits are discarded. (???)
@DMUL:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,0];

*Signed Divide--have trap support.
@SDIV:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,1];

*Signed Double Divide. Have trap support.
*SDDIV divides two 32-bit integers and returns the 32-bit quotient; the
*32-bit remainder is left above TOS. Remainder has same sign as dividend?
@SDDIV:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,2];

*Unsigned Double Divide. Have trap support.
*UDDIV is same as SDDIV using cardinals instead of integers.
@UDDIV:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,3];
@ESC64:TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,4];
@ESC65:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,5];
@ESC66:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,6];
@ESC67:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,7];
@ESC70:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,10];
@ESC71:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,11];
@ESC72:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,12];
@ESC73:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,13];
@ESC74:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,14];
@ESC75:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,15];
@ESC76:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,16];
@ESC77:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,17];
%

*Alpha = 100b-117b are single precision floating point in MesaFP.Mc
*Alpha = 120b-137b are reserved for other floating point (?)
*Alpha = 140b-157b are Cedar opcodes in CedarGC.Mc.

Esc160:
*Write 16-bit current process
@WRPSB:
T ← Stack&-1, At[EscD7,0];
prCurrentPsb ← T, Return;

*Write MDS
@WRMDS:
T ← Stack&-1, LoadPage[wrMDSPage], At[EscD7,1];
MDShi ← T, GoToP[.+1];
OnPage[wrMDSPage];
*Call here from MesaP.
WRMDS1:
T ← MDShi ← (LSh[MDShi,10]) + T;
LOCALhi ← T;
GLOBALhi ← T, Return;

*Write Wakeups Pending; know that RSImage and NWW are consecutive locations.
@WRWP:
RTemp ← IP[RSImage]C, At[EscD7,2];
LU ← MNBR ← Stack&-1, Call[moStkPSave];
LU ← MNBR;
Skip[ALU#0];
T ← Stack ← (Stack) and not (IntPendingBit), Skip;
T ← Stack ← (Stack) or (IntPendingBit);
RS232 ← T, GoTo[moMNBRWRx];

*Write Wakeup Disable Counter (writing with 177777b doesn’t work)
@WRWDC:
T ← (Stack&-1) + 1, At[EscD7,3];
xfWDC ← T, GoTo[EI];

*Write Process Tick Count; writes prTicks register and initializes prTime to
*3 field interrupts/tick.
@WRPTC:
RTemp ← IP[prTime]C, At[EscD7,4];
LU ← MNBR ← Stack&-1, Call[moStkPSave];
Stack ← 3C, GoTo[moMNBRWRx];*prTime init

DblPopWRTemp:
MNBR ← Stack&-1, Task;
T ← Stack&-1;*New ClockHi,,ClockLo into MNBR,,RTemp1
RTemp1 ← T, Call[moStkPSave];
T ← RTemp1;
Stack ← T, GoTo[moMNBRWRx];*Write ClockLo

*Write Interval Timer
@WRIT:
RTemp ← IP[ClockLo]C, GoTo[DblPopWRTemp], At[EscD7,5];

*Write Xfer Trap Status
@WRXTS:
T ← Stack&-1, At[EscD7,6];
xfXTSReg ← T, Return;

*Write Maintenance Panel
@WRMP:
T ← Stack&-1, LoadPageExternal[0], At[EscD7,7];
GoToExternal[PNIPStart];

*Push 16-bit current process
@RRPSB:
T ← prCurrentPsb, GoTo[moPushT], At[EscD7,10];

*Read MDS
@RRMDS:
T ← RSh[MDShi,10], GoTo[moPushT], At[EscD7,11];

*Read Wakeups Pending
@RRWP:
RTemp ← IP[NWW]C, GoTo[moPshRRTemp], At[EscD7,12];

*Read Wakeup Disable Counter
@RRWDC:
T ← xfWDC, GoTo[moPushT], At[EscD7,13];

***Double precision here?
*Read Process Tick Count
@RRPTC:
RTemp ← IP[prTicks]C, GoTo[moPshRRTemp], At[EscD7,14];

moDblPshRRTemp:
T ← (SStkP&NStkP) xor (377C), Call[moStkPSwap];
*No tasking between reading ClockLo and ClockHi
T ← Stack&+1;
MNBR ← Stack, Call[moStkPSwap];
Stack&+1 ← T;
T ← MNBR, GoTo[moPushT];

*Read Interval Timer
@RRIT:
RTemp ← IP[ClockLo]C, GoTo[moDblPshRRTemp], At[EscD7,15];

*Read Xfer Trap Status
@RRXTS:
T ← xfXTSReg, GoTo[moPushT], At[EscD7,16];

@LOADSTACK:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD7,17];
%
T ← NextData[IBuf], At[EscD7,17];
*xfTemp is pointer to saved state.
xfTemp ← T;
T ← (xfTemp) + (MaxStack);
*Timing from here: (34 or 35)+14/word cycles
PFetch1[LOCAL,IBuf];*IBuf ← break byte,,StkP
xfBrkByte ← 40400C, Task;
StkP ← RZero;
*Fetch Min[StkP+2,MaxStack] words to the Stack; load xfTemp1 with
*Min[StkP+1,MaxStack-1] (i.e., word count - 1).
T ← xfTemp1 ← Sub[MaxStack!,1]C;
T ← (LdF[IBuf,14,4]) - T;
*NewStkP - (MaxStack-1) will carry when NewStkP .gr. MaxStack-2.
IBuf ← (IBuf) and not (360C), Skip[Carry];
xfTemp1 ← (xfTemp1) + T + 1;*xfTemp1 ← NewStkP+1
T ← xfTemp, Call[.+2];
*Loop here
T ← (Zero) + T + 1, xfTemp1, GoTo[.+3,R<0];
PFetch1[LOCAL,Stack];
xfTemp1 ← (xfTemp1) - 1, Return;*Loop
T ← RSh[IBuf,10];
xfBrkByte ← (xfBrkByte) or T, LoadPage[moPage];
StkP ← IBuf, GoToP[moTail];
%

*200b-217b reserved for processor-dependent ESC opcodes

Esc200:

*Opcodes for Mesa Input/Ouput.
* Stack[0:7]=XXX, Stack[10:13]=Task, Stack[14:17]=I/O register no.

***This won’t work on 3MB ethernet because of H4PE’s.
@INPUT:
T ← Stack&-1, At[EscD10,0];
Input[Stack], Return;

@OUTPUT:
T ← Stack&-1, LoadPage[xfPage1], At[EscD10,1];
Output[Stack];*5 mi to task, avoiding Gotcha
OnPage[xfPage1];
UseCTask, Call[xfRet];
GoTo[xfTail];

%LoadRAMJ is called with a flag word at TOS and a long pointer at 2OS,,3OS
to the LoadRam table, formatted as follows:
Word 0/
Version number (always has been 0)
Word 3n+1/
40:43,,addr
Word 3n+2/
0:17
Word 3n+3/
20:37
LP,,LPhi/
point at Word 0
xfTemp1/
even for inline Refresh, odd for normal tasking refresh.
RTemp1/
even to believe the starting address, odd to exit with a
NextInst to the next opcode inline.
Discontinuity of storage refresh is a potential problem, but since the
inline refresh is about 4 times faster than normal refresh, we accept
the possibility of a 25 percent longer than normal wait for refreshing
some RAMs.
%
@LOADRAMJ:
T ← (Stack&-1) xor (1C), At[EscD10,2];*Load RAM and Jump
RTemp1 ← T, LoadPage[opPage1];*Save bits, jump complemented
xfTemp1 ← T, CallP[StackLP];*Allow tasking if not jumping
**Used to test VersionID at LP+0 here.
*Get jump flag (1 => no jump)
RTemp ← IP[FFault]C, Call[moStkPSave];
Stack ← (Stack) and not (1C);*Set trap-on-fault in FFault
LoadPageExternal[LRJpage];
StkP ← RTemp, GoToExternal[LRJStart];

@RPrinter:
T ← Printer, GoTo[moPushT], At[EscD10,3];

@WPrinter:
Printer ← Stack&-1, Return, At[EscD10,4];

*Read & Write Ram format:
* Stack=40:43,,addr, (Stack-1)=40:43, (Stack-2)=0:17, (Stack-3)=20:37.
@READRAM:
T ← Stack&-1, LoadPage[xfPage1], At[EscD10,5];
RTemp ← T;
OnPage[xfPage1];
T ← 1C, Call[CSRead];*Read 20:37
T ← 0C, Call[CSRead];*Read 0:17
T ← 3C, Call[CSRead];*Read 40:43
T ← RTemp;
Stack ← (LSh[Stack,14]) or T, GoTo[xfTail];

*Subroutine CSRead reads control store for ReadRam Opcode.
CSRead:
APCTask&APC ← RTemp;
ReadCS;
*Successor of ReadCS must be even to avoid smashing return link.
T ← CSData, GoTo[xfPushTR], DispTable[1,1,0];

*NOTE: can start arbitrary task with JRAM; code which is called may Return
*to execute next Opcode
@JRAM:
APCTask&APC ← Stack&-1, GoTo[moRet], At[EscD10,6];

*Alpha = 207b CCOPR in MesaIO.Mc
*Alpha = 210b FPOPR in MesaIO.Mc

@STARTIO:
T ← Stack&-1, LoadPageExternal[StartIOPage], At[EscD10,11];
RTemp1 ← (Zero) xnor T, GoToExternal[StartIOLoc];

*Alpha = 212b DESOPR in MesaIO.Mc

%READR pushes the current value of any RM register. At entry, TOS is the
octal address of the register. At exit, this has been replaced by the value
of the register. NWW (357b), xfWDC (076b), prTicks (355b), and
xfXTSReg (077b) could also be read by this opcode, but they want to be
referenced by machine-independent opcodes given earlier. This means that
only the following Dolphin-specific registers are of interest:

344b
xPageCountCount of ’good’ pages from Initial microcode
345b
xStorageFaultsSum of (2↑bad board no.) from Initial
346b
xHardBadPagesCount of hard bad pages from Initial
347b
xSoftBadPagesCount of soft bad pages from Initial
323b
vCrystalSpeed of system clock as function of crystal speed
and tick rate:
2*mHz at 2560 cycle period,
4*mHz at 1280 cycle period,
8*mHz at 640 cycle period.
Default is 320, (representing 8*40 mhz) or 1 ticks/640 cycles at 40
mhz clock, equivalent to 1 tick/64 microseconds. Currently
envisioned values are as follows:
64012802560cycles/tick
40 mhz320 160 80
44.5356 178 89
50400 200 100
%
@READR:
T ← Stack&-1, At[EscD10,13];
RTemp ← T, GoTo[moPshRRTemp];

%0S contains the value which is the argument for LoadTimer.
1S specifies the interrupt mask for naked notifies when the timer goes off.
The argument for LoadTimer has the following fields:
0:3
new stateNormally this will be state 5 ("simple timer")
4:11d
new dataSpecifies the timer period in
12:15d
slot numberThis should specify slot 16b for single-stage timers;
I think 14b to 16b are free for multistage timers,
provided the interrupt always occurs for slot 16b.
%
@USERTIMER:
LoadTimer[Stack&-1], At[EscD10,14];
RTemp ← Or[And[IP[TimerInts],360],And[Sub[IP[TimerInts],1],17]]C;
LU ← MNBR ← Stack&-1, Call[moStkPSave];
*Jump here to init prTicks (at prTime+1), NWW (at RSImage+1), and ClockHi
*(at ClockLo+1); fall through to init TimerInts.
moMNBRWRx:
T ← MNBR;
Stack&+1 ← T, Call[moStkPSwap];
moTail:
LU ← NextInst[IBuf];
moTailx:
NIRet;

TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD10,15];

:IF[WithTMS1000]; **************************************
@SETTIME:
*TMS 1000 code
LU ← MNBR ← Stack&-1, At[EscD10,16];
RTemp ← IP[RSImage]C, Call[moStkPSave];
T ← MNBR, Skip[R>=0];
Stack ← T ← (Stack) or T, LoadPage[opPage1], Skip;
Stack ← T ← (Stack) and not T, LoadPage[opPage1];
StkP ← RTemp, RS232 ← T, GoToP[.+1];
OnPage[opPage1];
Skip[TimeOut];
Stack&+1 ← 0C, GoTo[P5Tail];
Stack&+1 ← 100000C, GoTo[P5Tail];
:ELSE; *************************************************
@SETTIME:
TrapParm ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD10,16];
:ENDIF; ************************************************

*Alpha = 217b-377b undefined

:END[MesaESC];