: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 xfTemp global frame address output registers xfGFIWord CODE,CODEhi code base register GLOBAL global base register xfTemp modified 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 xfFrame pointer to frame - 4 T pointer to frame - 4 xfFSI displacement to head-of-list in AV temps xBuf frame 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: 00 Word[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. x1 No frames available--trap with original FSI as parameter. 10 No 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]; (2048)\f5 2188f0 8f5 26f0 10f5 76f0 10f5 323f0 6f5 50f0 6f5 927f0 14f5 1538f0 3f5 6880f0 10f5 1426f0 8f5 1120f0 10f5 1709f0 5f5 2244f0 10f5 143f0 6f5 402f0 6f5 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];(1795)\f5 2701f0 3f5 47f0 3f5 47f0 3f5 47f0 3f5 47f0 3f5 47f0 3f5 47f0 3f5 47f0 3f5 48f0 3f5 48f0 3f5 48f0 3f5 48f0 3f5 48f0 3f5 79f0 3f5 264f0 3f5 225f0 5f5 60f0 3f5 47f0 3f5 47f0 3f5 47f0 3f5 47f0 3f5 100f0 3f5