:IF[AltoMode]; *Last edited: 8 May 1981 by Fiala TITLE[MesaX--Alto.Mode]; :ELSE; TITLE[MesaX--Pilot.Mode]; :ENDIF; %Tentative implementation comments: The frame is freed only on RET and LSTF; only in these cases and on entry from LoadStateNF is the PC not saved; in all other cases the PC is saved in the frame. LOCAL, GLOBAL, and CODE point at quadwords (i.e., low two bits are 0). Left and right halves of CODEhi and PCBhi are equal. LOCALhi = GLOBALhi = MDShi; LOCAL points at a block configured as follows: LOCAL-1 FSI for local frame LOCAL+0 This frame's GLOBAL LOCAL+1 Byte PC saved when this frame Xfers LOCAL+2 caller's LOCAL (put in xfMY by SavePCInFrame) LOCAL+3 caller's xfMX (?) LOCAL+4+n Local n Frames on an FSI list are linked through word 0 with pointer to list head in word -1. The FSI table is locked into core, so references into it can't page fault; LOCAL-1 and LOCAL are guaranteed on the same page, so the LOCAL references by xfer can't page fault--this means that no page fault can occur during deallocation of the old frame. Hence, the only reason for EarlyXfer fault fixup state is so that CODE can be reloaded so that the PC saved by the trap will be correct--could remove this state by ensuring that PC was already saved at the onset of any fault. % *since xfPage1 has NextData's and NextInsts, it must have refill link at 377 xfRefill: PFetch4[PCB,IBuf,4], GoToP[MesaRefill], At[xfPage1p,377]; xfRet: Return; SaveRLink: xfRLink _ T, Return; EXMemStat: MemStat _ EarlyXfer, Return; :IF[AltoMode]; ************************************** SavePCXfer: T _ CODE, LoadPage[opPage3]; T _ (PCB) - T, Call[SPCIF0]; *15 bits, since code segments < 32k. :ELSE; ********************************************** SavePCXfer: T _ LSh[CODE,1]; T _ (LSh[PCB,1]) - T; *15 bits, since code segments are < 32k. T _ (PCFReg) + T; *T _ PCF + 2*(PCB-CODE) RTemp1 _ T, LoadPage[opPage3]; PStore1[LOCAL,RTemp1,1], CallP[SPCIF1]; :ENDIF; ********************************************* *xfXTSreg is used for Xfer traps (normally 0) Xfer: xfXTSreg _ Rsh[xfXTSreg,1], GoTo[XferNTC,R Even]; *Trap check *Cause xfer trap with 0 as reason, xfMX as parameter T _ xfMX, LoadPage[opPage3]; xfXTSreg _ 0C, GoToP[XTrap]; XferNTC: LU _ Dispatch[xfMX,16,1], Skip[R Odd]; *entry for no trap check T _ xfMX, Disp[XferType0]; T _ xfGFT, Disp[XferType1]; XferType0: *Control link in T is frame pointer; save it. xfCount _ T, DblGoTo[xfT0a,xfConFault,ALU#0], DispTable[2]; XferType2: *Indirect control link PFetch1[MDS,xfCount], Task; T _ xfGFT; LU _ Dispatch[xfCount,16,1], Skip[R Odd]; T _ xfCount, Disp[XferType0]; *Type 0 or 2 T _ (LdF[xfCount,0,11]) + T, Disp[.+1]; T _ (LdF[xfCount,0,11]) + T, Skip, DispTable[2]; *(2*gfi) + gft RTemp _ sUnbound, GoTo[StashMX]; *Type 3 PFetch2[MDS,xfTemp], Call[EXMemStat]; *Type 1 xfCount _ (LdF[xfCount,11,6]) + 1, GoTo[xfT1a]; xfConFault: *Control fault if control link = 0. MB => don't free frame RTemp _ sControlFault, DblGoTo[QTrap,QTrapF,MB]; xfT0a: PFetch2[MDS,xfTemp], Call[EXMemStat]; *Fetch G and PC T _ (LOCAL) - 1, Skip[MB]; PFetch1[MDS,xfFSI]; *Start freeing the current frame *LOCAL[-1] on same page as LOCAL[0], *so this reference won't page fault. T _ GLOBAL; T _ (xfTemp) xor T; *Compare new G to old G T _ GLOBAL _ (GLOBAL) xor T, GoTo[CValid,ALU=0]; Skip[ALU#0]; RTemp _ sUnbound, GoTo[StashMX]; *New G = 0, trap *This reference might page fault--if it does, GLOBAL and CODEhi are smashed. PFetch4[MDS,IBuf], Call[LoadC]; *Fetch global frame overhead xfGFIWord _ T; *Finish freeing the current frame if required. CValid: T _ (xfFSI) + (xfAV), GoTo[FreeDone,MB]; PFetch1[MDS,xBuf]; *fetch list head (won't page fault) *Store list head in Local[0]; garbage in other 3 words but don't care PStore4[LOCAL,xBuf,0], Call[xfRet]; *Won't page fault PStore1[MDS,LOCAL]; *Store LOCAL into list head *(won't page fault) :IF[AltoMode]; ************************************** FreeDone: T _ (LSh[xfTemp1,1]) - 1, Skip[R>=0]; xfTemp1 _ (Zero) - T, Skip; xfTemp1 _ (Zero) + T + 1; PCF _ xfTemp1, Task; :ELSE; ********************************************** FreeDone: PCF _ xfTemp1, Task; :ENDIF; ********************************************* MemStat _ XferFixup; T _ xfCount; LOCAL _ T, LoadPage[opPage3]; T _ Rsh[xfTemp1,1], GoToP[XferGO]; *Control Link is a procedure descriptor (158 cycles minimum to completion) XferType1: T _ (LdF[xfMX,0,11]) + T, Skip, DispTable[2]; *T _ gfi + gft XferType3: *Control link is unbound whatsis RTemp _ sUnbound, GoTo[StashMX]; T _ (LdF[xfMX,0,11]) + T; *2*gfi + gft PFetch2[MDS,xfTemp], Call[EXMemStat]; *Fetch G & entry vector offset T _ (LdF[xfMX,11,6]) + 1; *2*EVI + 1 since type = 01 xfCount _ T; xfT1a: T _ xfTemp; *New G PFetch4[MDS,IBuf], Skip[ALU#0]; *Fetch the global frame overhead RTemp _ sUnbound, GoTo[StashMX]; *Trap GLOBAL _ T, Call[LoadC]; T _ LSh[xfTemp1,1]; T _ xfCount _ (xfCount) + T + 1, LoadPage[opPage3]; *xfCount _ 2*EVI + 2 PFetch2[CODE,xfTemp], GoToP[.+1]; *Fetch PC and FSI OnPage[opPage3]; T _ IBuf; xfGFIWord _ T; LFCEntry: T _ xfAV; T _ (RHMask[xfTemp1]) + T, GoTo[xAlloc]; *Get a frame AllocRetx: xfTemp _ LSh[xfTemp,1]; *Form byte PC MNBR _ LOCAL, LOCAL _ T, NoRegILockOK; MemStat _ XferFixup; PCF _ xfTemp; PStore4[LOCAL,GLOBAL,0], Skip[MB]; *Init frame (Can't fault) T _ (MNBR) - 1, Call[Free]; T _ Rsh[xfTemp,1]; *Each cycle from here down represents ~ 0.15% performance slowdown XferGO: PFetch4[CODE,IBuf]; *Enter here from XferType0 PCB _ T; *bypass kludge PCB _ (PCB) and not (3C); :IF[CacheLocals]; ************************************* PFetch4[LOCAL,LocalCache0,4]; :ENDIF; *********************************************** :IF[AltoMode]; **************************************** T _ CODEhi; *Allow page fault to happen before LoadPage PCBhi _ T, LoadPage[0]; IBuf _ LCy[IBuf,10], Call[SwapBytes1]; :ELSE; ************************************************ T _ CODEhi; PCBhi _ T; :ENDIF; *********************************************** T _ xfBrkByte; MemStat _ Normal, Skip[ALU=0]; SkipData, Call[DoBB]; *Skip the instruction in IBuf DispMan: *Must execute one bytecode without interrupts T _ NextData[IBuf]; xfBrkByte _ T, Skip[ALU#0]; *skip over NOPs T _ NextData[IBuf], Call[.-1]; DoBB: xfBrkByte _ (xfBrkByte) or (40400C); xfBrkByte _ LCy[xfBrkByte,2]; APCTask&APC _ xfBrkByte; xfBrkByte _ Zero, Return; % *The following was a great idea saving 2 cycles/xfer but didn't work because *Pilot doesn't initialize xfBrkByte when it starts, so RunPilot won't work: LU _ (xfBrkByte) - (40400C); *40400b = 2001b rcy 2 for dispatch MemStat _ Normal, Skip[ALU=0]; SkipData, Call[DoBB]; *Skip next bytecode in IBuf *Even if IntPending is false, NextInst can't be used here because its NIRet *might cause a task switch that will result in IntPending being set true *before the next bytecode (which might be NOP) tests IntPending. DispMan: T _ NextData[IBuf]; xfBrkByte _ (xfBrkByte) or T, Skip[ALU#0]; *Skip NOPs T _ NextData[IBuf], Call[.-1]; DoBB: xfBrkByte _ LCy[xfBrkByte,2]; APCTask&APC _ xfBrkByte; FixxfBrkByte: xfBrkByte _ 40400C, Return; % OnPage[xfPage1]; *Load the C register from the global frame information in IBuf..IBuf2 LoadC: T _ MDShi; CODEhi _ T; ***Not sure whether or not CODEhi has to be restored on trap here*** T _ IBuf1, Skip[R Even]; RTemp _ sCSegSwappedOut, GoTo[StashMX]; LU _ LdF[IBuf2,0,10]; CODE _ T, GoTo[LoadCX,ALU#0]; *Done if short C (CODEhi_MDShi) T _ RHMask[IBuf2]; *Otherwise, same pointer in LH and RH CODEhi _ T, Skip[H2Bit8']; CODEhi _ T _ (CODEhi) or (100C); *Set bits 1 and 9 if out-of-bounds CODEhi _ (LSh[CODEhi,10]) or T; LoadCX: T _ IBuf, Return; *Return GFIWord in T OnPage[opPage3]; *Return with frame in T to P7PushT if MemStat.0 = 1, else to AllocRetx. *Entry from @ALLOC with FSI in T; also loop here for frame type 3 (indirect). Alloc: T _ (LSh[R400,1]) + T; *Know that xfAV = 1000b = LSh[R400,1] *Entry here from Xfer with MemStat.0 = 0 xAlloc: PFetch1[MDS,xfFrame], Task; *the head of the list (can't fault) xfTemp1 _ T; LU _ LdF[xfFrame,16,1], Skip[R Odd]; T _ xfFrame, DblGoTo[FrType2,FrType0,ALU#0]; T _ Rsh[xfFrame,2], DblGoTo[Alloc,FrType1,ALU#0]; FrType0: PFetch1[MDS,RTemp1]; *Fetch Frame[0] T _ xfTemp1; PStore1[MDS,RTemp1]; *Store into head of list T _ xfFrame; MemStat _ (MemStat) and not (100000C), DblGoTo[P7PushT,AllocRetx,R<0]; FrType2: T _ Rsh[xfFrame,2], GoTo[Alloc]; *Indirect FrType1: *Trap--check for call from @ALLOC MemStat _ (MemStat) and not (100000C), DblGoTo[AATrap,XATrap,R<0]; AATrap: xfATPreg _ LSh[xfATPreg,2], Call[SavePCInFrame]; *call was from @ALLOC LoadPage[xfPage1]; RTemp _ sAllocListEmpty, GoTo[QTrap]; *don't free frame XATrap: T _ xfMX; *call was from Xfer - check for LFC GoTo[.+3,ALU=0]; DoAllocTrap: xfATPreg _ T, LoadPage[xfPage1]; RTemp _ sAllocListEmpty, DblGoTo[QTrap,QTrapF,MB]; T _ (xfGFIWord) and not (177C); xfCount _ (LSh[xfCount,1]) + T; *LFC trap. Fab procedure descriptor * + is important - don't use OR T _ xfCount _ (xfCount) - (3C), GoTo[DoAllocTrap]; *SUBROUTINE Free frees the frame at T+1; T must have been loaded in mi that *does call (Timing ~ 56 cycles). Free: PFetch1[MDS,xfFSI]; *get the frame's FSI Free0: xfFrame _ T, UseCTask; *bypass kludge--depends on MDS being 0 T _ APCTask&APC, Task; xfRLink _ T; xfFSI _ T _ (xfFSI) + (xfAV); PFetch1[MDS,xBuf]; *Fetch list head T _ 100C; T _ xfFrame _ (xfFrame) + 1, SALUF _ T; *Store head into frame--trash next 3 words but don't care PStore4[MDS,xBuf]; T _ xfFSI; PStore1[MDS,xfFrame]; *Store frame into head APCTask&APC _ xfRLink, GoTo[P7Ret]; *Apparently this procedure receives a pointer in xfTemp and stores state *as follows: * xfTemp+MaxStack+1/ LOCAL * xfTemp+MaxStack/ StkP * xfTemp+(MaxStack-1) to +0 Stack words OnPage[xfPage1]; SaveState: *UseCTask in calling instruction T _ APCTask&APC, Call[SaveRLink]; *save link in rlink T _ (xfTemp) + (Add[MaxStack!,1]C); PStore1[MDS,LOCAL], Call[sv377]; T _ (NStkP) xor T; xfTemp2 _ T; T _ (xfTemp) + (MaxStack); PStore1[MDS,xfTemp2]; *Store StkP T _ (xfTemp2) - (Sub[MaxStack!,1]C); xfTemp1 _ MaxStack, Skip[Carry]; *Stack depth + 2 = MaxStack+(d-(MaxStack-1))+1 xfTemp1 _ (xfTemp1) + T + 1; T _ (StkP _ xfTemp1) - 1, Call[svpop]; *Loop returns here T _ (NStkP) xor T; T _ (AllOnes) + T, Skip[ALU#0]; *T _ offset in saved Stack APCTask&APC _ xfRlink, GoTo[xfRet]; svpop: T _ (xfTemp) + T; *T _ xfTemp + StkP - 1 PStore1[MDS,Stack]; sv377: T _ 377C, Return; *T _ true stack pointer *Entry from Process mechanism and MStart. Ensure no frame freeing. LoadStateNF: T _ 100C; SALUF _ T; LoadState: T _ (xfTemp) + (MaxStack); PFetch1[MDS,xfBrkByte], Task; *xfBrkByte _ new StkP T _ (xfTemp) + (Add[MaxStack!,1]C); NWW _ (NWW) and not (100000C); PFetch1[MDS,xfMX]; *xfMX _ new LOCAL T _ (xfTemp) + (Add[MaxStack!,2]C); StkP _ RMZero; PFetch1[MDS,xfMY]; T _ (xfBrkByte) and (17C); IBuf _ T, Task; *Stack depth saved for StkP _ below xfTemp1 _ MaxStack; *Check for MaxStack-1 or MaxStack, T _ d - (MaxStack-1) T _ (IBuf) - (Sub[MaxStack!,1]C); xfBrkByte _ Rsh[xfBrkByte,10], Skip[Carry]; *Stack depth + 2 = MaxStack+(d-(MaxStack-1))+1 xfTemp1 _ (xfTemp1) + T + 1; ** xfBrkByte _ (xfBrkByte) or (40400C); *(BrkByte*4 or 2001b) rcy 2 T _ (xfTemp) - 1, Call[.+1]; *Loop here xfTemp1 _ (xfTemp1) - 1; T _ (Zero) + T + 1, Skip[ALU>=0]; StkP _ IBuf, GoTo[Xfer]; PFetch1[MDS,Stack], Return; *Loop *In general, we want to call Free after all possible Xfer page faults *can occur, but before all other traps. *Unbound & CSegSwappedOut Traps come here StashMX:T _ xfMX; *RTemp holds SD index (through Free) xfOTPreg _ T, DblGoTo[QTrap,QTrapF,MB]; *XferTrap, AllocTrap, & Control Traps go here to free the frame, else to QTrap QTrapF: T _ (LOCAL) - 1, LoadPage[opPage3], Call[FreeX]; QTrap: RTemp _ (RTemp) + (xfAV), Task; T _ (RTemp) + (xfSDOffset); PFetch1[MDS,xfMX], GoTo[Xfer]; FreeX: PFetch1[MDS,xfFSI], GoToP[Free0]; OnPage[MStartPage]; *Start from Nova. MStart: xfTemp _ T; *Pointer to State for LoadState T _ MDShi; *Initialize some Mesa Emulator registers GLOBALhi _ T; GLOBAL _ 1C; *Clobber G so C will be reloaded at the 1st Xfer LOCALhi _ T; MemStat _ T _ 0C; xfMX _ 1C; NWW _ T, LoadPage[opPage3]; *Wipe out pending interrupts TickCount _ 3C, Call[FFaultStack]; Stack _ (Stack) or (1C); *FFault _ "trap on page fault" xfXTSReg _ T, LoadPage[xfPage1]; LOCAL _ 0C, GoToP[LoadStateNF]; OnPage[opPage3]; FFaultStack: RTemp _ IP[FFault]C; FFS1: StkP _ RTemp, RTemp _ T, NoRegILockOK, Return; *CODEhi and PCBhi are equal, so we only need to worry about the low word. *Compute byte PC (relative to CODE) of the opcode to be executed when this *frame next runs. :IF[AltoMode]; ************************************** SavePCInFrame: T _ CODE; T _ (PCB) - T; *15 bits, since code segments are < 32k. SPCIF0: T _ (PCF.word) + T, Skip[R Even]; RTemp1 _ (Zero) - T, Skip; :ELSE; ********************************************** SavePCInFrame: T _ LSh[CODE,1]; T _ (LSh[PCB,1]) - T; *15 bits, since code segments are < 32k. T _ (PCFReg) + T; :ENDIF; ********************************************* RTemp1 _ T; PStore1[LOCAL,RTemp1,1]; SPCIF1: T _ 100C; *Set MB T _ LOCAL, SALUF _ T; xfMY _ T, Return; MesaRefill7: PFetch4[PCB,IBuf,4], GoToP[MesaRefill], At[3777]; P7Tail: LU _ NextInst[IBuf], At[P7TailLoc]; P7Tailx:MemStat _ Normal, NIRet; *External function calls efcr: xfGFIWord, LoadPage[xfPage1], Skip[R Even]; PFetch1[CODE,xfMX], GoToP[SavePCXfer]; PFetch1[GLOBAL,xfMX], GoToP[SavePCXfer]; @EFC0: T _ (RMZero) - (1C), GoTo[efcr], Opcode[300]; T _ (RMZero) - (2C), GoTo[efcr], Opcode[301]; T _ (RMZero) - (3C), GoTo[efcr], Opcode[302]; T _ (RMZero) - (4C), GoTo[efcr], Opcode[303]; T _ (RMZero) - (5C), GoTo[efcr], Opcode[304]; T _ (RMZero) - (6C), GoTo[efcr], Opcode[305]; T _ (RMZero) - (7C), GoTo[efcr], Opcode[306]; T _ (RMZero) - (10C), GoTo[efcr], Opcode[307]; T _ (RMZero) - (11C), GoTo[efcr], Opcode[310]; T _ (RMZero) - (12C), GoTo[efcr], Opcode[311]; T _ (RMZero) - (13C), GoTo[efcr], Opcode[312]; T _ (RMZero) - (14C), GoTo[efcr], Opcode[313]; T _ (RMZero) - (15C), GoTo[efcr], Opcode[314]; T _ (RMZero) - (16C), GoTo[efcr], Opcode[315]; T _ (RMZero) - (17C), GoTo[efcr], Opcode[316]; T _ (RMZero) - (20C), GoTo[efcr], Opcode[317]; @EFCB: T _ NextData[IBuf] + 1, Opcode[320]; T _ (Zero) - T, GoTo[efcr]; *Local function calls lfcr: PFetch2[CODE,xfTemp], Call[SavePCInFrame]; xfXTSreg _ Rsh[xfXTSreg,1], Skip[R Odd]; xfMX _ Zero, GoTo[LFCEntry]; *in XferType1 *Cause trap with reason of 400, parameter of xfCount xfXTSreg _ 400C; T _ xfCount, GoTo[XTrap]; @LFC1: T _ xfCount _ 4C, GoTo[lfcr], Opcode[321]; T _ xfCount _ 6C, GoTo[lfcr], Opcode[322]; T _ xfCount _ 10C, GoTo[lfcr], Opcode[323]; T _ xfCount _ 12C, GoTo[lfcr], Opcode[324]; T _ xfCount _ 14C, GoTo[lfcr], Opcode[325]; T _ xfCount _ 16C, GoTo[lfcr], Opcode[326]; T _ xfCount _ 20C, GoTo[lfcr], Opcode[327]; T _ xfCount _ 22C, GoTo[lfcr], Opcode[330]; T _ xfCount _ 24C, GoTo[lfcr], Opcode[331]; T _ xfCount _ 26C, GoTo[lfcr], Opcode[332]; T _ xfCount _ 30C, GoTo[lfcr], Opcode[333]; T _ xfCount _ 32C, GoTo[lfcr], Opcode[334]; T _ xfCount _ 34C, GoTo[lfcr], Opcode[335]; T _ xfCount _ 36C, GoTo[lfcr], Opcode[336]; T _ xfCount _ 40C, GoTo[lfcr], Opcode[337]; T _ xfCount _ 42C, GoTo[lfcr], Opcode[340]; @LFCB: T _ (NextData[IBuf]) + 1, Opcode[341]; xfCount _ T; T _ xfCount _ LSh[xfCount,1], GoTo[lfcr]; *Stack function call @SFC: T _ Stack&-1, LoadPage[xfPage1], Opcode[342]; xfMX _ T, GoToP[SavePCXfer]; @RET: PFetch1[LOCAL,xfMX,2], Opcode[343]; *fetch Return link xfXTSreg _ Rsh[xfXTSreg,1], GoTo[RetTrap,R Odd]; T _ RMZero, LoadPage[xfPage1]; xfMY _ SALUF _ T, GoToP[XferNTC]; *MB=0 => free frame RetTrap:T _ 100C; SALUF _ T; *If RET traps, the frame is NOT freed T _ (LOCAL) + (6C); xfXTSreg _ 1000C; XTrap: xfXTPreg _ T, LoadPage[xfPage1]; RTemp _ sXferTrap, DblGoToP[QTrap,QTrapF,MB]; @LLKB: T _ NextData[IBuf], Opcode[344]; *xfGFIWord was set up by LoadC when this frame became current. T _ (Zero) - T - 1, xfGFIWord, Skip[R Even]; PFetch1[CODE,Stack], GoTo[P7Tail]; *Code link PFetch1[GLOBAL,Stack], GoTo[P7Tail]; *Frame link @PORTO: Call[SavePCInFrame], Opcode[345]; T _ Stack&-1, Task; PStore1[MDS,xfMY]; T _ (Zero) + T + 1, Task; PFetch1[MDS,xfMX]; T _ (AllOnes) + T, LoadPage[xfPage1]; xfMY _ T, GoToP[Xfer]; *This is for thinking about only *@PORTO: T _ (Stack&-1) + 1, LoadPage[xfPage1], Opcode[345]; * PFetch1[MDS,xfMX], GoToP[.+1]; *OnPage[xfPage1]; * T _ (AllOnes) + T; * xfMY _ T; * PStore1[MDS,LOCAL]; * T _ LSh[CODE,1]; * T _ (LSh[PCB,1]) - T; * T _ (PCFReg) + T; * RTemp1 _ T; * T _ 100C; * SALUF _ T; * PStore1[LOCAL,RTemp1,1], GoTo[Xfer]; @PORTI: LU _ xfMY, Opcode[346]; T _ xfMX, GoTo[portinz,ALU=0]; PStore1[MDS,RMZero], Call[P7Ret]; T _ (xfMX) + 1; portinz: PStore1[MDS,xfMY]; RefillLocalCache: *in case the port was in the active frame PFetch4[LOCAL,LocalCache0,4], GoTo[P7Tail]; @KFCB: T _ NextData[IBuf], Opcode[347]; kfcr: RTemp _ T, Call[SavePCInFrame], At[KFCRLoc]; LoadPage[xfPage1]; GoTo[QTrap]; @DESCB: T _ xfGFIWord, Opcode[350]; RTemp _ T; descbcom: T _ NextData[IBuf]; RTemp1 _ T; T _ (RTemp) and not (177C); T _ (LSh[RTemp1,1]) + T + 1, GoTo[P7PushT]; @DESCBS: T _ (Stack&-1) + (xfGFIOffset), Opcode[351]; PFetch1[MDS,RTemp], GoTo[descbcom]; @BLT: LP _ 0C, Opcode[352]; T _ MDShi, LoadPage[BLTPage]; BLTcom: LPhi _ T, GoToP[.+1]; OnPage[BLTPage]; *fixup: fetch => count + 1; store => source-1, dest-1, count+1 MemStat _ BltFixup, Call[BLTx]; * set Return address to BLTloop; BLTloop:LU _ Stack&-1; T _ Stack&+1, GoTo[BLTdone,ALU=0]; *get source, point to count Stack _ (Stack) - 1; *decrement count PFetch1[LP,RTemp]; Stack&+1; *point to dest T _ Stack&-2; *get dest, point to source PStore1[MDS,RTemp]; Stack _ (Stack) + 1; *increment source Stack&+2; Stack _ (Stack) + 1, GoTo[BLTint,IntPending]; *increment dest BLTx: Stack&-1, Return; *point to count, Return to BLTloop BLTdone:Stack&-2; BLTdonex: LoadPage[opPage3]; PFetch4[LOCAL,LocalCache0,4], GoToP[P7Tail]; BLTint: T _ (RMZero) + 1; BLTstop:LoadPage[opPage0]; PFetch4[LOCAL,LocalCache0,4], GoToP[NOPint]; @BLTL: T _ (Stack&-1) and (377C), Opcode[353]; LPdesthi _ T, LoadPage[BLTPage]; T _ LPdesthi _ (LSh[LPdesthi,10]) + T + 1, GoToP[.+1]; OnPage[BLTPage]; LPdesthi _ (FixVA[LPdesthi]) or T; T _ Stack&-2, LoadPage[opPage1]; LPdest _ T, CallP[StackLP]; RTemp1 _ Zero; MemStat _ BltLFixup; *fixup: source+T, dest+T, count+1 Stack&+3, Call[BLTLloop]; *point to count BLTLloop: LU _ Stack; *read count, point to source lo T _ RTemp1, GoTo[BLTLdone,ALU=0]; PFetch1[LP,RTemp]; Stack _ (Stack) - 1; *decrement count RTemp1 _ (RTemp1) + 1; *increment offset PStore1[LPdest,RTemp]; GoTo[BLTLint,IntPending]; Return; *goes to BLTLloop BLTLdone: Stack&-3, GoTo[BLTdonex]; BLTLint: Stack&-2; Call[BLTLbump]; *wait for page fault before updating Stack Stack&+2, Call[BLTLbump]; T _ (RMZero) + 1, GoTo[BLTstop]; BLTLbump: Stack _ (Stack) + T + 1; Stack&+1, FreezeResult; Stack _ (Stack) + 1, UseCOutAsCIn, Return; @BLTC: T _ CODE, Opcode[354]; LP _ T; T _ CODEhi, LoadPage[BLTPage], GoToP[BLTcom]; @BLTCL: T _ sUnimplemented, GoTo[kfcr], Opcode[355]; @ALLOC: MemStat _ 100000C, Opcode[356]; T _ Stack&-1; xfATPreg _ T, GoTo[Alloc]; *set up in case trap occurs P7PushT: Stack&+1 _ T, GoTo[P7Tail]; P7Ret: Return; @FREE: T _ (Stack&-1) - 1, Call[Free], Opcode[357]; LU _ NextInst[IBuf], Call[P7Tailx]; *Increment Wakeup Disable Counter (Disable Interrupts) @IWDC: xfWDC _ (xfWDC) + 1, GoTo[P7Tail], Opcode[360]; *Decrement Wakeup Disable Counter (Enable Interrupts) @DWDC: T _ (R400) or (52C), Task, Opcode[361]; PFetch1[MDS,xfTemp]; ** T _ (R400) or (52C), Opcode[361]; ** PFetch1[MDS,xfTemp], Call[FixxfBrkByte]; T _ xfTemp; NWW _ (NWW) or T; ***This code is very similar to MIPend, SetClrRS232, and DoInt*** RTemp _ IP[RSImage]C, GoTo[DWDCnone,ALU=0]; *see if any interrupts T _ (SStkP&NStkP) xor (377C), Call[FFS1]; *Exchange StkP/RTemp T _ Stack _ (Stack) or (IntPendingBit); *set IntPending StkP _ RTemp, RS232 _ T; DWDCnone: xfWDC _ (xfWDC) - 1, GoTo[DispMan]; @STOP: NWW _ (NWW) or (100000C), Call[SavePCInFrame], Opcode[362]; T _ CurrentState; PFetch1[MDS,xfTemp]; LoadPage[xfPage1]; UseCTask, CallP[SaveState]; PCB _ StopStopPC, Call[FFaultStack]; *FFault _ "crash on page fault" LoadPage[neStartPage]; Stack _ (Stack) and not (1C), GoToP[StartNova]; CATCH: SkipData, Call[P7Tail], Opcode[363]; *MISC - extended opcodes accessed by dispatching on alpha @MISC: T _ MNBR _ NextData[IBuf], Opcode[364]; RTemp _ T, LoadPage[xfPage1]; Dispatch[RTemp,12,2], GoToP[.+1]; OnPage[xfPage1]; Dispatch[RTemp,14,4], Disp[.+1]; Disp[@ASSOC], At[MiscDisp0,0]; *dispatch for alpha = 0 to 17b xfOTPreg _ T, GoTo[FlPnt], At[MiscDisp0,1]; *20b to 37b floating point T _ sUnimplemented, GoTo[MiscTrap], At[MiscDisp0,2]; *40b to 57b undefined xfOTPreg _ T, GoTo[Cedar], At[MiscDisp0,3]; *60b to 77b Cedar *Push alpha byte and trap for floating point or Cedar opcodes. **Note: FP microcode, when loaded, overwrites this mi and the one preceding it. FlPnt: T _ sFloatingPoint, GoTo[MiscTrap], At[MiscDisp0,17]; Cedar: T _ sCedarTrap, GoTo[MiscTrap], At[MiscDisp0,16]; *Associate - TOS contains map entry, (TOS-1) contains VP which is to get it. @ASSOC: T _ (Stack&-1) and not (100000C), At[MiscDisp1,0]; xBuf _ T, Call[MapLP]; ASSOC1: XMap[LP,xBuf,0], GoTo[MiscTail]; *Set Flags @SETF: T _ (Stack&-1) and not (100000C), At[MiscDisp1,1]; xBuf _ T, Call[MapLP]; XMap[LP,xBuf,0]; T _ LSh[xBuf3,10]; *Put flags,,card,blk0 in left byte T _ xBuf1 _ (RHMask[xBuf1]) or T; *blk1,rowaddr in low byte T _ (Zero) or not T, Call[StackGetsT]; *push old flags & page LU _ (LdF[xBuf3,11,3]) - 1; *=0 if map entry = VACANT xBuf _ (xBuf) and (70000C), Skip[ALU#0]; *isolate new flags, ignore LogSE xBuf _ T, GoTo[ASSOC1]; *Vacant entry, use old flags, oldpage T _ (Stack) and not (170000C); *Get old page number xBuf _ (xBuf) or T, GoTo[ASSOC1]; *new flags, old page *Subroutine MapLP creates a base reg pair from a virtual page number for the Map opcodes. MapLP: T _ LSh[Stack,10]; LP _ T; *Set low Base T _ LHMask[Stack&-1]; LPhi _ T, GoTo[xfRet,ALU>=0]; *Set high byte of high base LPhi _ (LPhi) or (40000C), GoTo[xfRet]; *set bit 1 if 0 is set. *Read & Write Ram format: Stack=40:43,,addr, (Stack-1)=40:43, (Stack-2)=0:17, (Stack-3)=20:37. @ReadRam: T _ LdF[Stack&-1,4,14], At[MiscDisp1,2]; *get address RTemp _ T; 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[MiscTail]; *Subroutine CSRead reads control store for ReadRam opcode. CSRead: APCTask&APC _ RTemp; ReadCS; T _ CSData, DispTable[1,1,0]; *successor of CSOp must be even StackGetsT: Stack&+1 _ T, Return; MC[VersionID,0]; *Checks compatibility with LRJ'd code. @LoadRamJ: T _ (Stack&-1) xor (1C), At[MiscDisp1,3]; RTemp1 _ T, LoadPage[opPage1]; *save bits, jump complemented T _ (Stack&-1) and (377C), CallP[StackLPx]; PFetch1[LP,RTemp,0], Call[MiscRet]; LU _ (RTemp) xor (VersionID); T _ sUnimplemented, GoTo[MiscTrap,ALU#0]; LoadPage[opPage3]; T _ (SStkP&NStkP) xor (377C), Call[FFaultStack]; *Make MC1/StkOvf errors crash rather than notifying emulator Stack _ (Stack) and not (1C); RTemp1 _ T _ (RTemp1) and (1C); *get jump flag (1 => no jump) xfTemp1 _ T, LoadPageExternal[LRJpage]; *allow Tasking if not jumping StkP _ RTemp, GoToExternal[LRJStart]; MiscRet: Return; MiscTrap: LoadPage[opPage3]; GoToP[kfcr]; T _ sUnimplemented, GoTo[MiscTrap], At[MiscDisp1,4]; *Opcodes for Mesa Input/Output. *Stack[0:7]=XXX, Stack[10:13]=task no., Stack[14:17]=I/O register number. @INPUT: T _ Stack&-1, At[MiscDisp1,5]; Input[Stack], GoTo[MiscTail]; @OUTPUT: T _ Stack&-1, At[MiscDisp1,6]; Output[Stack], GoTo[MiscTail]; * UseCTask, Call[xfRet]; * GoTo[MiscTail]; *Checksum Stack1 words starting at Stack2,,Stack3 with the checksum *accumulated in Stack0; algorithm is CSum _ (CSum + Word) lcy 1. At entry *StkP points at Stack3; at exit StkP points at Stack0 (contains csum); if *Stack0 result was -1, it is changed to 0. @CSum: T _ Stack&-1, LoadPage[opPage1], At[MiscDisp1,7]; LPhi _ T, LoadPage[opPage0], CallP[StackLPy]; *StackLPy returns after LP,,LPhi _ Stack2,,Stack3 with stack popped twice, *LPhi bounds-checked and in base register format. LU _ Stack&+1, LoadPage[CSPage]; PFetch4[LP,xBuf,0], DblGoToP[CSBeg,CSEnd,ALU#0]; OnPage[CSPage]; *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]; CSBeg: LP _ (LP) or (3C); *Compute address of next quadword T _ LP _ (LP) + 1, GoTo[CSLoop,IntPending']; **THIS LOADPAGE IS TOO SOON. Stack&+1, LoadPage[opPage0]; T _ 2C, GoToP[NOPint]; CSWord: Stack1 _ (Stack1) - 1; Stack0 _ (LCy[Stack0,1]) + T, Skip[ALU=0]; Stack0 _ (Stack0) + 1, UseCOutAsCIn, Return; Stack0 _ (Stack0) + 1, UseCOutAsCIn; CSEnd: LU _ (Stack0) xnor (0C); LoadPage[opPage3], Skip[ALU#0]; Stack&-2 _ 0C, GoToP[P7Tail]; Stack&-2, GoToP[P7Tail]; *SetMaintenancePanel opcode; just put value on Stack @SetMP: T _ Stack&-1, LoadPage[PNIPPage], At[MiscDisp1,10]; CallP[PNIP]; MiscTail: LU _ NextInst[IBuf]; MiscTailx: NIRet; *Read realtime clock (push[RM325]; push[MM430]; @RCLK: LoadPage[XMiscPage], At[MiscDisp1,11]; T _ (R400) or (30C), CallP[MXRClk]; Stack&+1 _ T; *Push[RM 325] T _ RTemp, GoTo[MiscPushT]; *Push [VM 430]; @RPRINTER: T _ Printer, GoTo[MiscPushT], At[MiscDisp1,12]; @WPRINTER: Printer _ Stack&-1, GoTo[MiscTail], At[MiscDisp1,13]; *Misc 14 is defined in MesaIO.Mc as the floating point board kludge io *opcode, as the CCA kludge io opcode, or as an unimplemented operation. T _ sUnimplemented, GoTo[MiscTrap], At[MiscDisp1,15]; *WMDS Deimplemented SetTime: T _ Stack&-1, LoadPage[RS232Page], At[MiscDisp1,16]; RTemp _ T, CallP[SetClrRS232]; Skip[TimeOut]; Stack&+1 _ 0C, GoTo[MiscTail]; Stack&+1 _ 100000C, GoTo[MiscTail]; OnPage[RS232Page]; SetClrRS232: RTemp1 _ IP[RSImage]C; T _ (SStkP&NStkP) xor (377C); StkP _ RTemp1, RTemp1 _ T, NoRegILockOK; T _ RTemp, Skip[R>=0]; Stack _ T _ (Stack) or T, Skip; Stack _ T _ (Stack) and not T; StkP _ RTemp1, RS232 _ T, Return; @SetDefaultPartition: T _ Stack&-1, LoadPage[XMiscPage], At[MiscDisp1,17]; RTemp _ T, CallP[MXPar]; MiscPushT: LU _ NextInst[IBuf]; Stack&+1 _ T, NIRet; @BitBLT: T _ Stack0, Opcode[365]; AC2 _ T; *Enter with StkP .eq. 2, Stack holding 2*scan-lines completed, and *Stack0 in AC2 (a pointer to the BitBlt table) T _ MDShi; AC2hi _ T, LoadPage[bbPage]; *Long pointer to BitBLT table in AC2,AC2hi T _ (Cycle&PCXF) or (100000C), GoTo[bbBitBlt]; *StartIO is presently used only for Ethernet io which leaves a result in AC0 *(77777b if no Ethernet board, else host number); Mesa ignores the result *and restores AC0. @StartIO: T _ Stack&-1, LoadPage[eePage], Opcode[366]; LU _ MNBR _ AC0, CallP[eeSIO]; T _ MNBR; *Unclobber AC0 (= Stack10 on 13 Jan 81) AC0 _ T, GoTo[P7Tail]; *Note: Stack[0:3] = task, Stack[4:17] = microaddress :IF[AltoMode]; ************************************** *Start next opcode on even byte @JRAM: LU _ Cycle&PCXF, Skip[R Even], Opcode[367]; CSkipData; *Can't cause refill Nop; LU _ APCTask&APC _ Stack&-1, Call[P7Ret]; :ELSE; ********************************************** @JRAM: LU _ APCTask&APC _ Stack&-1, Call[P7Ret], Opcode[367]; :ENDIF; ********************************************* LU _ NextInst[IBuf], Call[P7Tailx]; @DST: T _ NextData[IBuf], Opcode[370]; T _ (LOCAL) + T, LoadPage[xfPage1]; UseCTask, xfTemp _ T, CallP[SaveState]; T _ (xfTemp) + (Add[MaxStack!,2]C), GoTo[portinz]; ***???*** @LST: T _ NextData[IBuf], Opcode[371]; xfTemp _ T, Call[SavePCInFrame];*NextData must be before SavePC call T _ xfTemp, GoTo[LSTFgo]; @LSTF: T _ RMZero, Opcode[372]; T _ NextData[IBuf], SALUF _ T; *MB = 0 => free frame LSTFgo: T _ (LOCAL) + T, LoadPage[xfPage1]; xfTemp _ T, GoToP[LoadState]; *xfTemp is pointer to saved state. @WR: T _ NextData[IBuf], Opcode[374]; xfTemp _ T, LoadPage[xfPage1]; Dispatch[xfTemp,16,2]; OnPage[xfPage1]; T _ Stack&-1, Disp[.+1]; :IF[AltoMode]; ************************************** GoTo[MiscTail], xfWDC _ T, DispTable[2,17,1]; GoTo[MiscTail], xfXTSreg _ T; :ELSE; ********************************************** GoTo[MiscTail], xfWDC _ T, DispTable[3,17,1]; GoTo[MiscTail], xfXTSreg _ T; MDShi _ T; T _ MDShi _ (LSh[MDShi,10]) + T; LOCALhi _ T; GLOBALhi _ T, GoTo[MiscTail]; :ENDIF; ********************************************* @RR: T _ NextData[IBuf], Opcode[375]; xfTemp _ T, LoadPage[xfPage1]; Dispatch[xfTemp,15,3]; OnPage[xfPage1]; LoadPage[opPage3], Disp[.+1]; GoToP[P7PushT], T _ xfWDC, DispTable[6,17,1]; GoToP[P7PushT], T _ xfXTSreg; GoToP[P7PushT], T _ xfXTPreg; GoToP[P7PushT], T _ xfATPreg; GoToP[P7PushT], T _ xfOTPreg; GoToP[P7PushT], T _ MDShi; @BRK: T _ Zero, GoTo[kfcr], Opcode[376]; *Cause pagefault trap--done only by fault, not supposed to be encountered in *instruction stream. TrapFlap: T _ (PCFReg) - 1, Opcode[377]; *back up PCF by one RTemp _ T, LoadPage[0]; PCF _ RTemp, GoToP[StartMemTrap]; *Unused opcodes on page 7 T _ sUnimplemented, GoTo[kfcr], Opcode[373]; :END[MesaX];