:TITLE[MesaESC];*ESC and ESCL opcodes
%Ed Fiala 21 May 1984: Change VERSION date.
Ed Fiala 17 April 1984: Debug @DSHIFT and @BLTLR; move refill trap to
MesaOP2; fix bug in MapLP change; create lskPage; bum 1 mi in AllocFail;
fix bug in @PO change; bum 1 mi at BLTSetup; bum 1 mi in @WRWP; fix
bug at AllocFail reported by Blackman.
Ed Fiala 10 November 1983: Change @ME and @MX from Esc to regular
opcodes; restore @JS; move @NILCK, @UDIV, @LUDIV, @ROB;
add @BNDCKL, @XE, @XF, @LSK; delete @LSTE, @LSTF, @SLOB, @LLOB, @DBS,
WithNILCK, WithLInt, WithDLogic, LSPage; change traps; improve MapLP;
deimplement LPChecking; change VERSION; change PO; change frame fault
part of @AF.
Ed Fiala 21 December 1982: Deimplement @JS opcode and put NILCK in its
position (WithNILCK conditional).
Ed Fiala 29 November 1982: Add LoadStack opcode = Esc 177b as a comment;
needs more work to combine code with LSTE and 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.
***Placement is as follows:
271b mi on moPage
113b mi on xfPage1
23b mi on opPage0
5b mi on opPage1 (+1b mi if CacheLocals=1)
2b mi on opPage2
40b mi on opPage3
3b mi on wrMDSPage
27b mi on csPage
20b mi on lskPage
1b mi on piPage
1b mi on dvPage1
2b mi on ppPage
+4b 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
-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
+2b mi on moPage if WithBLTLR=1
+2b mi on moPage if WithRotate=1
+1b mi on opPage1 if WithRotate=1
+44b mi on dsPage0 if WithDShift=1 (must be page with refill)
+1b mi on moPage 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) Check @BLTLR, @ROTATE.
5) PStore1[MDS,RTemp] could be subr shared with MesaOP3.
6) Assign distinct trap location for AssignRef opcode.
7) Is LocalCache reload needed at @WOB exit?
8) Bum the common entry sequences for BLTLR, BLEL, and BLECL.
%
OnPage[moPage];
*At BackTrap, the address of the control link will be computed as
*RTemp+T = EscTrapOffset+(2xalpha).
moUnimp:
xfTrapParm0 ← T, LoadPage[opPage0];*Even placement
moUnimpx:
T ← (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;
%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 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.
%
MC[EscLast,216];
*ESC--extended Opcodes accessed by dispatching on alpha.
@ESC:T ← MNBR ← CNextData[IBuf], Call[EscDispatch], Opcode[370];
LU ← NextInst[IBuf], CallX[P7Tailx];
*ESCL--extended opcodes dispatching on alpha using beta as operand.
@ESCL:T ← MNBR ← CNextData[IBuf], Call[EscDispatch], Opcode[371];
LU ← NextInst[IBuf], CallX[P7Tailx];
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
xfTrapParm0 ← T, LoadPage[xfPage1], Disp[Esc00], At[EscTop,0];* 0b-17b
Disp[Esc20], At[EscTop,1];*20b-37b
Disp[Esc40], At[EscTop,2];*40b-57b
xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscTop,3];
:UNLESS[WithFloatingPoint]; **************************** MesaFP
xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscTop,4];
:ENDIF; ************************************************
xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscTop,5];
:UNLESS[WithCedar]; ************************************ CedarGC
xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscTop,6];
:ENDIF; ************************************************
Disp[Esc160], At[EscTop,7];*160b-177b
Disp[Esc200], At[EscTop,10];*200b-217b
OnPage[xfPage1];
xfUnimp:
T ← (RTemp) + (EscTrapOffset), GoToP[BackTrap];
@a00:LoadPage[opPage0], GoToP[xfUnimp], At[EscD0,0];
@a01:LoadPage[opPage0], GoToP[xfUnimp], At[EscD0,1];
*2 to 6 (@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];
xfTailx:
MemStat ← Normal, NIRet;
*Subr MapLP creates a BR pair from a virtual page for the Map opcodes.
MapLP:T ← Stack;
MapLPx:LPhi ← T;*Only the left-half of LPhi matters for disp=0.
T ← LSh[Stack&-1,10], Skip[R>=0];
LPhi ← (LPhi) or not (0C);*VA>=2↑22 = VP>= 2↑16 illegal
LP ← T, Return;
*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 ← Stack, 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];*Head-of-list (can’t fault)
prData ← T, Call[TtoxfCount];
*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. Backup PC and StkP and cause FrameFault with original
*FSI as trap parameter in prData and qFrameFaultOs in prConditionQ.
AllocFail:
prData ← (prData) - (AVOffset);
Stack&+1, LoadPage[opPage0];
prConditionQ ← qFrameFaultOs, GoToP[prBackPCFault];
*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
[S+2] = source link of previous xfer (usually LOCAL of previous context)
[S+1] = indirect destination link of previous xfer (pointer to port)
Zero port.inport (only the first word of the input port has to be zeroed).
If the source link is non-zero, then store it in port.outport.
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];
RTemp ← T, LoadPage[opPage1];*RTemp ← source link
OnPage[piPage];
:IF[CacheLocals]; ********************************
T ← Stack, GoToP[portinz,ALU=0];
OnPage[opPage1];
PStore1[MDS,RZero], Call[P5Ret];
T ← (Form2[AllOnes]) + T;
portinz:
PStore1[MDS,RTemp];
*Since LOCAL is quadword-aligned, a port in LOCAL 0 to 3 can only begin
*at LOCAL+2; if so, reload store-through LOCAL cache.
T ← LOCAL;
LU ← (Form-4[Stack&-1]) - T, GoTo[CLCR2];
:ELSE; *******************************************
T ← Stack&-1, SkipP[ALU#0];*T ← [S+1]
OnPage[opPage1];
PStore1[MDS,RTemp], Return;*Source = 0 => port.inport←0 & exit
PStore1[MDS,RZero], Task;*Port.inport ← 0.
T ← (Form2[AllOnes]) + T;*Port.outport ← source link.
PStore1[MDS,RTemp], GoTo[P5Tail];
:ENDIF; ******************************************
%Port Out. The port is a four-word structure which begins with a word
that is 2 mod 4 aligned (so that a pointer to the port is an indirect
control link). [S],[S-1] is a long pointer to the port, but since the port
is known to be in MDS, the high part of the long pointer is ignored.
inport ← LOCAL (word 0 only; word 1 doesn’t matter since LOCAL is type 0)
xfTemp/xfTemp1 ← destination link from words 2 and 3 of the port.
xfMY ← short pointer to the port (= indirect link).
Timing = (14.5-2.25)+44-8 = 48.25 cycles to Xfer.
%
@PO:Stack&-1, GoToP[.+1], At[EscD0,15];
OnPage[xfPage1];
POR1:T ← (Stack&-1) + (2C);
*Destination link into xfTemp/xfTemp1 from the outport.
PFetch2[MDS,xfTemp], Task;
xfMY ← T;
xfMY ← T ← (xfMY) - (2C);
*Save LOCAL in word 0 of inport
PStore1[MDS,LOCAL], Call[SavPCinFrame];
PStore1[MDS,xfOldPC];
MemStat ← Or[EarlyXfer!,xfTypePORT!]C, GoTo[Xfer];
*Port Out Responding
@POR:Stack&-1, GoToP[POR1], At[EscD0,16];
*Esc 17b (@SPP) is in MesaP.Mc
Esc20:
*Disable Interrupts (Timing: 23.5 cycles).
@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).
@XOR:T ← Stack&-1, At[EscD1,2];
LU ← NextInst[IBuf];
Stack ← (Stack) xor T, NIRet;
OnPage[ppPage];
PshPop2:
Stack&+1;
T ← Stack&-2, Return;
*Double And (Timing: 28.5 cycles).
@DAND:T ← Stack&-2, LoadPage[ppPage], At[EscD1,3];
Stack ← (Stack) and T, CallP[PshPop2];
Stack ← (Stack) and T, GoTo[moPsh];
*Double Inclusive Or (Timing: 28.5 cycles).
@DIOR:T ← Stack&-2, LoadPage[ppPage], At[EscD1,4];
Stack ← (Stack) or T, CallP[PshPop2];
Stack ← (Stack) or T, GoTo[moPsh];
*Double Exclusive Or (Timing: 28.5 cycles).
@DXOR:T ← Stack&-2, LoadPage[ppPage], At[EscD1,5];
Stack ← (Stack) xor T, CallP[PshPop2];
Stack ← (Stack) xor T, GoTo[moPsh];
%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).
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:
xfTrapParm0 ← 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;
32.5 cycles if S .eq. -20b;
45.5 or 48.5 cycles if 0 .le. S .le 37b
42.5 or 45.5 cycles if -1 .ge. S .ge. -37b and S .ne. -20b.
%
:IF[WithDShift]; ***************************************
@DSHIFT:
T ← Stack&-1, LoadPage[dsPage0], 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 20b-S, load CycleControl with D,,M = 0,,S-1 and use RF.
RTemp ← T, GoToP[DShiftRight,ALU<0];
*Shifting left.
OnPage[dsPage0];
LU ← (RTemp) - (40C);
*Point StkP at low-order word.
Stack&-1, Skip[ALU<0];
T ← Stack ← 0C, GoTo[DS0Push0];*LShift .ge. 40b gives zero
T ← (RTemp) or (360C);
LU ← LdF[RTemp,13,1];
*For leftshift by S, the control 0,,17b-S is generated as 377b - (17b,,S).
T ← (R400) - T - 1, GoTo[DSL1,ALU=0];*377 - T.
*Left-shift of 20b .le. S .le. 37b.
RTemp ← T, Call[RTempToCycle];
T ← WFA[Stack];
Stack ← 0C;
LU ← NextInst[IBuf];
Stack&+1 ← T, NIRet;
RTempToCycle:
CycleControl ← RTemp, Return;
*Left-shift of 0 .le. S .le. 17b: right-shift high-order word first.
*For rightshift by 20b-S (0<S<20b), control is 0,,S-1.
DSL1:RTemp ← (RTemp) - 1;
CycleControl ← RTemp, RTemp ← T, NoRegILockOK;
*This RF gets low-word lshift 20b-S.
T ← RF[Stack], Call[RTempToCycle];
Stack ← WFA[Stack];
Stack&+1;
*Left-shift high word and OR bits shifted out of low word.
LU ← NextInst[IBuf];
Stack ← (WFA[Stack]) or T, NIRet;
OnPage[dsPage0];
DShiftRight:
LU ← (RTemp) + (40C);
T ← LdF[RTemp,14,4], Skip[ALU>=0];
T ← Stack&-1 ← 0C, GoTo[DS0Push0];*Shift .le. -40b gives 0
*For leftshift (WFA) by 20b-(-S), control is DBX=0, MWX=-(S mod 20b)-1
*= (S mod 20b)’ = [(S mod 20b) xor 17b] for (-S mod 20b) .ne. 0.
T ← (LdF[AllOnes,14,4]) xor T, Skip[ALU#0];
Stack&-1 ← Stack&-1, GoTo[DS0Push0];*Shift of exactly -20b
RTemp1 ← T;*(S mod 20b)’
*For rightshift (RF) by -S mod 20b, control is DBX=0, MWX=(S-1) mod 20b
*which is equivalent to DBX=0, MWX=(S mod 20b)-1 because S=0 has been
*eliminated.
T ← (LdF[RTemp,14,4]) - 1;
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[DS0Push0];
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];
DS0Push0:
LU ← NextInst[IBuf];
Stack&+1 ← 0C, NIRet;
:ELSE; *************************************************
@DSHIFT:
xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD1,7];
:ENDIF; ************************************************
*Lengthen Integer (Timing: 23.5 or 24.5 cycles).
@LINT:T ← Stack&-1, LoadPage[opPage2], Skip[R<0], At[EscD1,10];
T ← 0C, GoToP[SkipPushT];
T ← AllOnes, GoToP[SkipPushT];*SkipPushT in MesaOP2.Mc
*Jump Stack (Timing: 38.5 cycles).
@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
*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];
*Divide--(0,,2OS)/TOS. Single word dividend and divisor, no overflow check.
*The remainder is left above the Stack.
@UDIV:LU ← MNBR ← Stack&-1, At[EscD1,14];
T ← RZero, LoadPage[dvPage1], GoTo[UDiv1];
%Long Divide--(2OS,,3OS)/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:LU ← MNBR ← Stack&-1, At[EscD1,15];
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;
*Read Overhead Byte. Error check alpha in 1..4
*Timing: 29.75 to 34.75 cycles.
@ROB:T ← (NextData[IBuf]) - 1, At[EscD1,16];
T ← (Stack&-1) - T - 1;
PFetch1[MDS,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];
%Format of a StateVector is as follows:
0 to MaxStack-1stack words
MaxStackBrkByte,,StkP(0 => empty stack)
MaxStack+10 for a dst
LocalFrameHandle for a fault
MaxStack+2FrameSizeIndex for an allocFault
Long pointer if a memFault
other data if an otherFault
%
%Dump Stack.
Save stack and set StkP to 0; also save the break byte.
LOCAL+beta points at the MDS block where state is saved.
Timing: 47.75 + 17/word cycles.
%
Esc40:
@DSK:T ← SStkP, At[EscD2,0];*SStkP .eq. (NStkP xor 377) here
T ← (LSh[xfBrkByte,10]) + T;
RTemp ← T, LoadPage[opPage3];
xfTemp1 ← T ← MaxStack, GoToP[.+1];
OnPage[opPage3];*This can be any page with Tail
T ← (MNBR ← NextData[IBuf]) + T;
PStore1[LOCAL,RTemp], Task;
T ← Sub[MaxStack!,1]C;
T ← (LdF[RTemp,10,10]) - T;
*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 ← offset in saved Stack
T ← (AllOnes) + T, Skip[ALU#0];
: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
T ← 377C, Return;
%Xfer and Enable
NOTE: The programmer must ensure that XE doesn’t trap or fault because
xfWDC would be wrong. XE is used by fixed frame trap handlers which must
run with interrupts disabled.
***No error check on xfWDC decrement.
(LOCAL+beta+0)↑ is the two-word source link, but this is known to be not
type 1, so only word 0 need be fetched.
(LOCAL+beta+2)↑ is the two-word destination link.
Timing: (16.75-2.25)+40 = 54.5 cycles to Xfer
%
OnPage[xfPage1];
XEFSub:PFetch1[MDS,xfTemp];*xfTemp/1 ← dest. link G at LOCAL+beta+2/3
T ← (RZero) + T + 1;*Bypass kludge ok
PFetch1[MDS,xfTemp1];*Dest. link byte PC
T ← RSh[Cycle&PCXF,10];
PFetch1[LOCAL,xfMY], Return;*Source link
*Note that NextData must precede SavePC call
@XE:T ← (LOCAL) + 1, LoadPage[xfPage1], At[EscD2,1];
GoToP[.+1];
OnPage[xfPage1];
T ← (CycleControl ← CNextData[IBuf]) + T + 1, Call[XEFSub];
xfWDC ← (xfWDC) - 1, Call[SavPCinFrame];
PStore1[MDS,xfOldPC];
MemStat ← Or[EarlyXfer!,xfTypeXFER!]C, GoTo[Xfer];
*Xfer and Free. Timing: (16.75-2.25)+36 = 50.5 cycles to Xfer
@XF:T ← (LOCAL) - (4C), At[EscD2,2];
xfFrame ← T, LoadPage[xfPage1];
PFetch1[MDS,xfFSI], GoToP[.+1];
OnPage[xfPage1];
T ← (LOCAL) + 1;
T ← (CycleControl ← CNextData[IBuf]) + T + 1, Call[XEFSub];
T ← PCFreg, Call[xfSaveRETPC];
MemStat ← Or[FreeFrame!,EarlyXfer!,xfTypeXFER!]C, GoTo[Xfer];
*Load Stack opcode.
*Timing: 16.75+40+[7x(stacksize+2)] cycles
@LSK:T ← NextData[IBuf], At[EscD2,3];
*xfTemp is pointer to saved state.
xfTemp ← T, LoadPage[lskPage];
T ← (xfTemp) + (MaxStack);
OnPage[lskPage];
PFetch1[LOCAL,RTemp1];*RTemp1 ← 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[RTemp1,14,4]) - T;
*NewStkP - (MaxStack-1) will carry when NewStkP .gr. MaxStack-2.
RTemp1 ← (RTemp1) 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[RTemp1,10];
xfBrkByte ← (xfBrkByte) or T, LoadPage[moPage];
StkP ← RTemp1, GoToP[moTail];
*Trap if 4OS,,3OS >= 2OS,,TOS unsigned; StkP ← StkP-2.
*Number - bound reverses the carry-out for bound - number - 1.
*Timing = 14.5+15 cycles.
@BNDCKL:
T ← Stack&-2, LoadPage[opPage0], At[EscD2,4];
LU ← (Stack&+1) - T, GoToP[.+1];*High number - bound
OnPage[opPage0];
T ← Stack&-2, FreezeResult, Skip[ALU=0];
Stack&+1, FreezeResult, GoTo[BndCk1];
LU ← (Stack&+1) - T, GoTo[BndCk1];*Low number - bound
*NILCK
*Timing: 23.5 cycles.
**No need to worry about PFetch to the stack completing in Esc opcode.
@NILCK:LU ← Stack, LoadPage[opPage0], At[EscD2,5];
NILCK1:RTemp ← sPointerTrap, DblGoTo[P4Trap,P4NoTrap,ALU=0];
*Nil Check Long (Timing: 25.5 cycles)
@NILCKL:
T ← Stack&-1, At[EscD2,6];
LU ← (Stack&+1) or T, LoadPage[opPage0], GoTo[NILCK1];
OnPage[opPage3];
BLTSetup:
LPDest ← T, GoToP[StackLP];
*Block Transfer Long Reversed--had trap support for Trinity (10.0) but I was
*told this was mandatory for Klamath (11.0) Pilot release for some reason.
*[S],,[S-1] = long pointer to a destination block, [S-2] = cardinal count,
*and [S-3],,[S-4] = long pointer to a source block. Transfer the block.
*Timing = 45.5 + 23/word cycles.
:IF[WithBLTLR]; ****************************************
@BLTLR:T ← RHMask[Stack&-1], At[EscD2,7];
LPDestHi ← T, LoadPage[opPage3];
LPDestHi ← (LSh[LPDestHi,10]) + T + 1;
OnPage[opPage3];
T ← Stack&-2, LoadPage[opPage1], Call[BLTSetup];
Stack&+3;
LU ← Stack;
BLTLR1:T ← (Stack) - 1, Skip[ALU#0];
Stack&-3, GoTo[BLTdonex];
PFetch1[LP,RTemp], Call[P7Ret];
PStore1[LPDest,RTemp];
Nop;
*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:xfTrapParm0 ← 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.
%
@BLEL:T ← RHMask[Stack&-1], At[EscD2,10];
LPDestHi ← T, LoadPage[opPage3];
LPDestHi ← (LSh[LPDestHi,10]) + T + 1;
OnPage[opPage3];
T ← Stack&-2, LoadPage[opPage1], Call[BLTSetup];
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.
%
@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];
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:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,10];
@BLECL:xfTrapParm0 ← 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
%
@CKSUM:T ← RHMask[Stack&-1], At[EscD2,12];
LPhi ← T, LoadPage[opPage1];
LPhi ← (LSh[LPhi,10]) + T + 1, CallP[StackLPz];
*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:
xfTrapParm0 ← 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:
xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,14];
:ENDIF; ************************************************
*Byte Block Transfer--have trap support.
@BYTBLT:
xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,15];
*Byte Block Transfer Reversed--have trap support.
@BYTBLTR:
xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD2,16];
%Returns 32d-bit result with information about the microcode.
[S]:Day of release encoded as number of days since 1 January 1901
[S-1]:
Bits 0:3Engineering 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:7Version number mod 16d
Bits 8:15Flags:
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,1];
MakeYear[124];*84
MakeMonth[5];*May
MakeDay[25];*21
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:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,0];
*Signed Divide--have trap support.
@SDIV:xfTrapParm0 ← 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:xfTrapParm0 ← 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:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,3];
@ESC64:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,4];
@ESC65:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,5];
@ESC66:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,6];
@ESC67:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,7];
@ESC70:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,10];
@ESC71:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,11];
@ESC72:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,12];
@ESC73:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,13];
@ESC74:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,14];
@ESC75:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,15];
@ESC76:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD3,16];
@ESC77:xfTrapParm0 ← 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;
T ← Stack ← (Stack) or (IntPendingBit), Skip[ALU#0];
T ← Stack ← (Stack) and not (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];*PNIPStart=IMX 176b
*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];
@a177:xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD7,17];
*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[opPage0], At[EscD10,1];
Output[Stack];*5 mi to task, avoiding Gotcha
OnPage[opPage0];
UseCTask, Call[P4Ret];
GoTo[P4Tail];
%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:
344bxPageCountCount of ’good’ pages from Initial microcode
345bxStorageFaultsSum of (2↑bad board no.) from Initial
346bxHardBadPagesCount of hard bad pages from Initial
347bxSoftBadPagesCount of soft bad pages from Initial
323bvCrystalSpeed 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:3new stateNormally this will be state 5 ("simple timer")
4:11dnew dataSpecifies the timer period in
12:15dslot 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;
xfTrapParm0 ← 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:
xfTrapParm0 ← T, LoadPage[opPage0], GoTo[moUnimpx], At[EscD10,16];
:ENDIF; ************************************************
*Alpha = 217b-377b undefined
:END[MesaESC];