:TITLE[Fault]; *Fault handler %Ed Fiala 19 February 1982: fix code at CrashLoc; added long comment about MC1/2 errors; reinsert FixEarlyXfer PCB odd check. Ed Fiala 4 February 1982: bum 6 mi; eliminate LogSE stuff (doesn't work because of LoadPage errors on continuing and because correctable and uncorrectable errors not distinguished); change buffer refill faults so PCB_PCB+4 always is done after the fault, since cannot guarantee it is done before the fault; restore the StkP write at fault entry because StkP .eq. 17b causes endless fault reentry (since this slows faults by 6 cycles, should conditionally do this as discussed in the note below); remove 'don't care' PCB odd check at FixEarlyXfer; change XferFixup. Ed Fiala 25 September 1981 Cleanup formatting; put return-to-midas code inside WithMidas conditional; preserve storage after crashes; make MP codes same as more precise AMesa codes; reduced time to task on faults by 32 cycles; use NSALUF rather than FaultRefType; bum 27b mi; deimplemented NextInstCrash check; eliminate absolute placement at fault initiation; verify PFetch4 on page 0 in fault interpretation; eliminate FaultPage1; fix bug in PFetch4 fault at location 0, in BackupPC; add StackErrorLoc. Ed Fiala 30 April 1981: Use IP[xxx]C for pointers to registers; include mi at abs.loc. 0 and 1, PFExitLoc; fix capitalization. Ed Fiala 18 March 1981: Don't smash MNBR on emulator faults; restore LOCAL from MNBR on EarlyXfer page fault; bum 2 mi; fix MapEntry and QuadAddr on Midas fault reporting. Earlier edits by Rich Johnsson, Ev Neely, RonJoiner, Jim Frandeen, Kennedy, Chang. Problems: 1) PFetch4; NextInst will normally fault abort at MesaRefillLoc and will be treated as a NextInst refill fault instead of ordinary reference; can improve things by ensuring task 0 from PipeReg2 (almost in SALUF?). 2) If memory errors are assumed to be noncoincident with RMPE, CSPE, and stack overflow/underflow errors, then can use the H2Bit8 branch condition to test the Page&Par&Boot register in T for a memory error and avoid the StkP nonsense in this case saving 8 cycles on the time-critical path; alternatively put Page&Par&Boot rcy 4 in T and directly test for stack overflow, saving 6 cycles. 3) Try to do ReadPipe only on MC12Err path, if that path not made longer. 4) MP report of storage failures; eliminate MP on breakpoint with Midas connected. % %Code here replaces that in Kernel or Initial performing the same function. This code determines what to do based on the type of error, and bits in FFault, which are: 0: Reserved for Initial's MemInit storage diagnostic (no function here). 2: H4 parity errors ignored if 0, crash if 1 (Ethernet generates them routinely) 3: Midas is present (1), so "crash" means breakpoint, else put a code in MP and GoTo[.] until booted. 15: If 1, notify emulator for MC1 and stack overflow errors; if 0, crash. Initialization (at InitEnd in MesaX) changes this bit from 0 to 1, so these errors trap to Mesa during normal operation. The DELAY ON PAGE FAULTS until tasking MUST BE MINIMIZED to avoid data lates on io devices. Although the present io devices eventually recover from data lates, keyboard input characters get lost and display jitter occurs, and there are other potential problems. In computing the time for an MC1 fault, allow ~6 cycles of execution after the PFetch/PStore; the mi after that is aborted until the fault starts, and I think (??) that the abort lasts ~10 cycles, unaffected by suspended cycles while transport for previous references takes place. % SetTask[17]; LoadPageExternal[0], GoToExternal[377], At[0]; *Buffer refill trap FaultOccurred: T _ APCTask&APC, At[1]; *Must save APC first RXAPC _ T; T _ (CTask&NCIA) xnor (170000C); RXCTask _ T; *It is necessary to ensure that StkP .ne. 17b here because, even though *ResetErrors clears stack overflow, StkP .eq. 17b instantly regenerates it *causing an endless loop back to location 1. So set StkP to a value that *will be used later. T _ (SStkP&NStkP) xor (377C); RXSTK _ IP[RXCTask]C; StkP _ RXSTK, RXSTK _ T, NoRegILockOK; *aluresult, saluf (both read complemented) T _ (ALUResult&NSALUF) xnor (0C); RXALU _ T; *LoadPage is necessary because, after ResetErrors, the Page register will no *longer be disabled by the error condition. T _ Page&Par&Boot, LoadPage[FaultPage]; RXPPB _ T, ResetErrors; *Test first for time critical MC1/2 error without any accompanying StkOvf, *CSPE, or RMPE. Can't change ALU branch conditions until after ResetErrors. OnPage[FaultPage]; *The four bits tested here are the Parity register (Stack overflow, CS parity *error, RM parity error, and Memory error. Memory errors are time-critical, *so test for them first. LU _ (LdF[RXPPB,4,4]) - 1; *Get pipe A (Timing for this mi = 8 cycles on MC12Err) ReadPipe[PipeReg], FreezeResult, GoTo[MC12Err,ALU=0]; LU _ (RXPPB) and (3000C), Skip[ALU>=0]; *Assume Midas breakpoint--i.e., SetFault function executed. T _ BrkPCrash, GoTo[CTaskCrash]; T _ RMCSCrash, GoTo[.+3,ALU#0]; *Jump if CS or RM PE *Have to do ResetMemErrs in case stack overflow occurred in conjunction with *an MC1/MC2 error. FFault, ResetMemErrs, GoTo[StackTrap,R Odd]; *StkOvf only T _ StkCrash, GoTo[Crash]; *CS or RM parity error, possibly in combination with other errors. T _ (LdF[RXPPB,4,4]) + T, GoTo[Crash]; CTaskCrash: T _ (LdF[RXCTask,0,4]) + T, GoTo[Crash]; *The computation here is CrashCode+PipeTask = CrashCode+(PipeTask' xor 17b) *= CrashCode+(17b-PipeTask') = (CrashCode+17b)-PipeTask'. The value in T *when we get here is CrashCode+17b. PipeTaskCrash: T _ (LdF[PipeReg2,10,4]) - T; T _ (Zero) - T, GoTo[Crash]; %Display maintenance panel code in T (MP code must be .ls 400b if the pipe registers are of interest). Then, if Midas is present, save memory error information in RM 100-107 for Midas and transfer control to the Midas Kernel. However, RM 100-107 are not clobbered for simple breakpoints since these registers might be in use by some task (usually task 4). If Midas is not present, spin in a storage refresh loop, allowing storage to be examined by connecting with Midas later. % Crash: PipeReg5 _ (LSh[PipeReg5,10]) or T; ***NOTE: RM 352-353 will be smashed by PNIP. RTMP _ Or[And[IP[MapEntry],360],And[Sub[IP[MapEntry],1],17]]C, Call[PNIP]; :IF[WithMidas]; ********************************* LoadPage[MidasPage]; StkP _ RTMP; *Locate on page 17 with the Midas Kernel so that this code can be easily *overwritten when Midas isn't present. Reformat saved Pipe into RM 100-107 *for Midas (for MC1 and MC2 errors; not always interesting). OnPage[MidasPage]; LU _ LdF[FFault,3,1]; LU _ LdF[RXPPB,4,4], GoTo[RefreshOnly,ALU=0]; T _ LdF[PipeReg,11,7], Skip[ALU#0]; *Map row address' RXCTask _ (RXCTask) xnor (170000C), GoToExternal[MidasBreakLoc]; PipeReg1 _ (LSh[PipeReg1,7]) xnor T; *Map row'' u Map column'' *StkP was pointed at the MapEntry register during fault entry. T _ LdF[PipeReg1,2,16], Call[FltPsh]; *MapEntry_page no. T _ (LdF[PipeReg2,10,4]) xor T, Call[FltPsh]; *TaskNumber T _ (LdF[PipeReg2,14,4]) xor T, Call[FltPsh]; *RefType T _ RHMask[PipeReg5], Call[FltPsh]; *CrashCode *Card no. (0..7) into CardNumber; offset by 5 to get actual board *number in card cage. NOTE: 14b in Card implies that this part of *the pipe is not filled by the reference. (X xor 7) + 5 = 14b - X. T _ 14C; T _ (LdF[PipeReg5,4,3]) - T; T _ (Zero) - T, Call[FltPsh]; *CardNumber *Map Flags (LogSE, WP, Dirty, Ref) into MapFlags T _ (LdF[PipeReg5,0,4]) xor T, Call[FltPsh]; T _ LdF[PipeReg4,12,6]; *Main column address (6 bits) PipeReg3 _ (LSh[PipeReg3,6]) or T; *x,x,Blk.1',,main row addr PipeReg5 _ LdF[PipeReg5,7,1]; T _ LdF[PipeReg3,2,16]; PipeReg5 _ (LSh[PipeReg5,16]) or T; *And Blk.0... *This is the (15-bit) quadword number within a 128k card. *Bits 1 and 2 give the block number. T _ (PipeReg5) xnor (100000C), Call[FltPsh]; *QuadAddr T _ RSh[PipeReg,10], Call[FltPsh]; *Interesting syndrome into 107 **Unfixup RXCTask for Kernel (should fixup Kernel and then remove **this mi in all software systems. RXCTask _ (RXCTask) xnor (170000C), GoToExternal[MidasFaultLoc]; FltPsh: UseCTask, Stack&+1 _ T; T _ 17C, Return; :ENDIF; ***************************************** RefreshOnly: Refresh[REFR]; REFR _ (REFR) + (20C), GoTo[.-1]; *LoadPageExternal[FaultPage]; GoToExternal[CrashLoc]; crashes any task. Call[PNIP], At[CrashLoc]; BugHalt: GoTo[.]; OnPage[FaultPage]; % There are a number of bugs and non-features in the MC1/MC2 error reporting hardware which account for the peculiar way things are done here; the comments here are based upon my reading of the hardware drawings and might be wrong: 1) ResetMemErrs resets the H4PE, MOB, MC1ErA, MC1ErB, MC2ErA, and MC2ErB flipflops. Also, the next reference reloads the MC1ErA and MC1ErB flipflops. The next reference using the same pipe will reload the MC2ErA or MC2ErB flipflop. H4PE can only be reset by ResetMemErrs. 2) H4PE's do not set MC1ErA or MC1ErB, so it is impossible to report the reference and task number for these with any certainty. Hence, the MP code uses +CTask rather than +PipeTask. 3) I am not sure whether MOB errors are indicated correctly in MC1ErA and MC1ErB or not. Hence, they report +CTask rather than +PipeTask in the MP. 4) MC2ErA and MC2ErB have pipe-specific clocks, so an error indication will remain true until another reference uses the same pipe. This means that the reference after one causing an MC2 error won't disturb its error indication. 5) MC1ErA and MC1ErB have a common clock, and only one of these can be indicated at a time. The hardware is SUPPOSED to fault before another reference starts, but if the preceding reference was a PFetch4 with error correction which didn't fault, and if the transport for that reference occurred between the faulting reference and the fault, then one more instruction will be executed before the fault task executes at location 1. This instruction could be another reference. If an extraneous reference does take place, the original MC1ErA/B indication would be replaced by the results of the extraneous reference, possibly getting the MCNoneCrash MP code. 6) MC2 is never started if MC1 gets a fault, so it is impossible to have both MC1ErX and MC2ErX indicated at one time. Timing = 32 cycles from loc 1 to here. % MC12Err: Dispatch[PipeReg,4,2]; *Dispatch on H4pe, MapBnd ***This Dispatch is only useful on the MC1/MC2 path. *Dispatch on MC2ErA', MC2ErB', and MC1ErA' bits Dispatch[PipeReg,0,4], Disp[.+1]; StkP _ RXSTK, Disp[MC2ErAB], DispTable[4]; *None *23rd or 24th? bit of memory address = 1 causes MOB. T _ MOBCrash, FFault, DblGoTo[MOBTrap,CTaskCrash,R Odd]; *MOB %Ignore improbably legit MOB&H4PE on IOStore4 to flush frequently occurring fake MOB&H4PE by 3MB Ethernet input task. Some 3MB Ethernet controllers cause H4PE's on Input's and IOStore4's erroneously; XWTask keeps control in xiTask so that the H4PE will be reported no later than the 1st mi of the next task to run, so that LoadPage errors won't happen. We could refine this check by continuing from H4PE's only when xiTask is running, but this would require one additional NOP in xiTask after IOStore4's or Input's, and it wouldn't work on gateways with more than one Ethernet controller. % LU _ LdF[FFault,2,1], Skip; *H4PE LU _ LdF[FFault,2,1]; *H4PE&MOB *Check 'ignore H4PE' bit in FFault. ResetMemErrs, Skip[ALU=0]; T _ H4PECrash, GoTo[CTaskCrash]; StkP _ RXSTK, GoTo[T17RestoreA]; MC2ErAB: * T _ MC22Crash, GoTo[Crash], At[MC12,0]; *MC2A/B & MC1A/B * T _ MC22Crash, GoTo[Crash], At[MC12,1]; *MC2A/B & MC1A * T _ MC22Crash, GoTo[Crash], At[MC12,2]; *MC2A/B & MC1B T _ MC22Crash, GoTo[Crash], At[MC12,3]; *MC2A/B * ResetMemErrs, GoTo[MC2ErA], At[MC12,4]; *MC2A & MC1A/B * ResetMemErrs, GoTo[MC2ErA], At[MC12,5]; *MC2A & MC1A ResetMemErrs, GoTo[MC2ErA], At[MC12,6]; *MC2A & MC1B ResetMemErrs, GoTo[MC2ErA], At[MC12,7]; *MC2A * ReadPipe[PipeReg], ResetMemErrs, GoTo[MC2ErB], At[MC12,10]; *MC2B & MC1A/B ReadPipe[PipeReg], ResetMemErrs, GoTo[MC2ErB], At[MC12,11]; *MC2B & MC1A * ReadPipe[PipeReg], ResetMemErrs, GoTo[MC2ErB], At[MC12,12]; *MC2B & MC1B ReadPipe[PipeReg], ResetMemErrs, GoTo[MC2ErB], At[MC12,13]; *MC2B * ResetMemErrs, FFault, * DblGoTo[MC1Notify,MC1Die,R Odd], At[MC12,14]; *MC1A/B ResetMemErrs, FFault, DblGoTo[MC1Notify,MC1Die,R Odd], At[MC12,15]; *MC1A ReadPipe[PipeReg,,FFault], ResetMemErrs, DblGoTo[MC1Notify,MC1Die,R Odd], At[MC12,16]; *MC1B T _ MCNoneCrash, GoTo[Crash], At[MC12,17]; *None MC1Die: T _ MC1Crash, GoTo[PipeTaskCrash]; %Set FaultParm to indicate cause of trap: -1 => Memory Out of Bounds negative => write protect positive => page fault Reference type put in NSALUF for fault handling later. NOTE: must not attempt to leave the fault results in PipeRegx because another fault (H4PE or LogSE) might clobber PipeRegx when an io task runs prior to servicing this fault. % MC1Notify: T _ (PipeReg) and (177C); *Low 7 bits of VPage *(High 7 bits of VPage' or low 7 bits)'; bits 0 and 1 wind up both 1. T _ (LSh[PipeReg1,7]) xnor T; LU _ LdF[PipeReg5,12,1]; *Test dirty': 0=> page fault FaultParm _ T, Skip[ALU#0]; FaultParm _ LdF[FaultParm,2,16], Skip; *page fault *Bits 0 and 1 in FaultParm are both 1 after the xnor and we want them to *be 2 so that VPage = 37777b won't be confused with an MOB error. FaultParm _ (FaultParm) and not (40000C); *write protect fault MFault: LU _ LdF[RXCTask,0,4]; T _ (PipeReg2) or not (17C), DblGoTo[TestTA,TestTB,ALU#0]; MOBTrap: *Set trap parameter for MOB StkP _ RXSTK; FaultParm _ (Zero) - 1, GoTo[MFault]; StackTrap: LU _ LdF[RXCTask,0,4]; T _ Xor[StkCrash!,377]C, DblGoTo[TestTA,TestTB,ALU#0]; %Notify emulator at EmNotifyA if emulator was interrupted, else at EmNotifyB. SALUF is used later at CheckStackTrap to distinguish Stack errors from others and in other fault handling to give RefType. Time from location 1 through this non-tasking return: 53 or 54 cycles on MOB; 48 or 49 cycles on Stack overflow or underflow; 61 to 68 cycles on page fault; 62 to 69 cycles on write protect fault. % TestTB: RTMP _ LoA[EmuNotifyLoc], Skip; TestTA: RTMP _ LoA[NonEmuNotifyLoc]; APCTask&APC _ RTMP; SALUF _ T, Return; %As an interim approximation to error logging, MC2 errors occuring on a page with LogSE set will store the pipe data + symdrome at VM 710 and resume. HOWEVER, LogSE may cause faults on references which could not otherwise fault, and this might cause LoadPage errors or unusual delays some places; LoadPage errors are not supposed to happen but not all of these are fixed. MC2ErA: T _ LHMask[MemSyndrome], Skip; MC2ErB: T _ LSh[MemSyndrome,10]; PipeReg _ (RHMask[PipeReg]) or T; LU _ LdF[PipeReg5,10,1]; *getting the flag bit RTMP _ 377C, Skip[ALU=0]; *test (inverted) LogSE T _ MC2Crash, GoTo[PipeTaskCrash]; *RTMP _ 377C will be used as odd base register. Since there is *no overflow and RTMP[0:7]=0 all will work correctly. T _ 311C; * 311+377 = 710 OddPStore4[RTMP,PipeReg]; T _ 315C; * 315+377 = 714 OddPStore2[RTMP,PipeReg4], GoTo[T17RestoreA]; % :IF[WithMidas]; ********************************* MC2ErA: T _ LHMask[MemSyndrome], Skip; MC2ErB: T _ LSh[MemSyndrome,10]; PipeReg _ (RHMask[PipeReg]) or T; T _ MC2Crash, GoTo[PipeTaskCrash]; :ELSE; ****************************************** MC2ErA: MC2ErB: T _ MC2Crash, GoTo[PipeTaskCrash]; :ENDIF; ***************************************** %T17CheckAPC resumes an interrupted non-emulator task after the emulator's TPC has been changed; T17RestoreA and T17RestoreB are the entries used when the emulator's TPC has not been changed. Because APC is used not only for tasking and returning but also for APCTask&APC_ and for dispatches, APC must sometimes be restored to the value saved at the onset of the trap--otherwise, the mi after a Dispatch or APCTask&APC_ would continue incorrectly. However, if the mi being continued contains a Return, and if APCTask=0, then control will go to the restored APC rather than the emulator's new TPC. Consequently, if APCTask=0, the code below checks the mi at the continue address for a Return; if so, APC is changed to the emulator's new TPC; if not, APC is restored to its trap value. Delay from location 1 through the Restore below has been: 52 cycles on H4PE; 71 to 81 cycles on Stack overflow or underflow; 74 to 84 cycles on MOB; 84 to 98 cycles on page fault; 85 to 99 cycles on write protect fault. % T17CheckAPC: LU _ LdF[RXAPC,0,4], At[ContNonEmuLoc]; *test saved APCTask *Only the two low-order bits of T are significant for ReadCS. T _ 5C, GoTo[T17RestoreA,ALU#0]; *Saved APC is for emulator, must read aborted mi. APCTask&APC _ RXCTask; ReadCS; *This mi need not be at an even mi because task 17b's TPC is unimportant. LU _ (LdF[CSData,6,3]) - T - 1; *(JC field) - 6; Return = 6 T _ LdF[RXCTask,4,4], GoTo[T17RestoreB,ALU#0]; RXAPC _ LoA[NonEmuPFLoc], GoTo[T17RestoreB]; *aborted mi does Return. %Restoring control to a task, as done here, is safe against every type of interruption except between LoadPage and the subsequent mi or between a reference and the next mi using the bypass kludge. However, the error check here will crash distinctively for a LoadPage problem, and since a following reference is aborted if a page or write protect fault is about to happen for a PREVIOUS reference, intervention between a reference and the bypass kludge should only be possible in one of the following three situations: 1) A Preceding PFetch4 experiences error correction with all 8 cycles of suspension for its transport occurring between a reference and the next mi using the bypass kludge, and that reference itself page faults; but page faults by io tasks aren't allowed, so this won't happen. 2) Correctable error logging occurs between a reference and the bypass kludge (we don't ever use LogSE). 3) An H4PE intervenes between a reference and the bypass kludge; this is illegal except for the 3mb Ethernet where enough Nop's after each Input and IOStore4 ensure that any H4PE happens safely. % T17RestoreA: T _ LdF[RXCTask,4,4]; *Compare page bits T17RestoreB: LU _ (LdF[RXPPB,0,4]) xor T; *with saved page register T _ RXALU, Skip[ALU=0]; *result register T _ LPCrash, GoTo[CTaskCrash]; *LoadPage error APCTask&APC _ RXCTask; Return, Restore, A _ RXAPC, LU _ T, NoRegILockOK; *back to faulted Task SetTask[0]; NotifyBack: xBuf1 _ (xBuf1) or (HiA[ContNonEmuLoc,17]); APCTask&APC _ xBuf1, xBuf1 _ T, NoRegILockOK; *GoToExternal[PFExitLoc] by MesaRefill (MesaJ.Mc) and by PNIP *(Initialize.Mc); manually located because MesaJ is in Pilot2 overlay. PFExit: Return, At[PFExitLoc]; EmNotifyB: UseCTask, xBuf _ T, At[NonEmuNotifyLoc]; T _ APCTask&APC; *Prepare to notify back to Task 17, location T17CheckAPC xBuf1 _ LoA[ContNonEmuLoc], Call[NotifyBack]; *PF handling starts here if non-emulator was interrupted. *If page fault timing is not good enough to prevent UTVFC data lates in the *worst case, tasking quickly here will help avoid data lates 2/3 of the time, *so the next two calls are brief (11 cycles and 4 cycles). T _ StkCrash, Call[CheckStackTrap], At[NonEmuPFLoc]; *xBuf2 _ Saved CIA and restore StkP. T _ (Stack) and not (170000C), Call[StkPExch]; *Cannot be buffer refill trap or location 0 abort Dispatch[MemStat,15,3], GoTo[CMSt1]; %SALUF holds either [PipeReg2 u 360b] (on a page, write protect, or MOB fault) or StkCrash' (stack overflow (StkP=17b) or underflow (StkP=0)). NSALUF then reads either RefType or StkCrash. First check for a stack error; if not, save StkP in xBuf2 and point StkP at task 17's RXCTask register; the caller will then save CIA of the aborted mi and restore StkP. This is done in two short subroutines rather than one longer one so io tasks which may have fallen behind during the long page fault service will have a better opportunity to catch up. % CheckStackTrap: LU _ (NSALUF) xor T; xBuf2 _ IP[RXCTask]C, GoTo[StackErrorz,ALU=0]; T _ (SStkP&NStkP) xor (377C); StkPExch: StkP _ xBuf2, xBuf2 _ T, NoRegILockOK, Return; *We have a stack error. Cause the trap immediately. Restore StkP to *the value in SStkP saved at the beginning of the opcode. *Also GoToExternal here on minimal stack errors. StackErrorz: *test SStkP for MaxStack+1 or more LU _ (SStkP&NStkP) - (LShift[Add[MaxStack!,1],10]C), At[StackErrorLoc]; MemStat _ Or[Trap!,NoPushSD!]C, Skip[Carry']; T _ MaxStack, Skip; T _ SStkP; xBuf2 _ T, Call[StkPExch]; T _ (PCXreg) - 1, Call[BackupPC]; ***LoadPage after Return prevents LogSE and could cause H4PE crash. LoadPageExternal[opPage3]; T _ sStackError, GoToExternal[KfcrLoc]; EmNotifyA: UseCTask, xBuf _ T, At[EmuNotifyLoc]; *xBuf _ emulator's T %FINALLY, task after the following delays: 59 or 60 cycles on emulator MOB; 56 or 57 cycles on stack overflow or underflow; 73 to 80 cycles on page fault; 68 to 75 cycles on write protect fault. % T _ APCTask&APC, Task; *xBuf1 _ emulator's TPC and Task xBuf1 _ T; *PF handling starts here if the emulator was interrupted. Note that if the *emulator was NOT interrupted, then the fault cannot have come from buffer *refill. Since control entered here, the emulator's PC is in RXCTask. T _ StkCrash, Call[CheckStackTrap]; T _ (Stack) and not (170000C), Call[StkPExch]; %At this point we have: xBuf emulator's T at the time of the fault; xBuf1 emulator's TPC; xBuf2 CIA of the aborted mi. The major problem with faults is to determine the PC to store in the frame, and whether to continue the opcode, or cause the trap immediately. The cases are: 1) The fault was detected at location 0, the 1st mi of the buffer refill trap. In this case, buffer refill was just starting when some previous reference faulted. Treat this as in case 7 below. 2) A PFetch4 fault occurred on page 0 and the emulator's TPC points at the 1st mi of a bytecode (TPC = 01xxxxxxxx01). It is assumed that the fault is due to a NextData/NextInst buffer refill; SStkP and PCX do not yet reflect advance from the previous bytecode (which doesn't happen until T2 of the 1st mi), so the trap is started with PC = 2*PCB + 7 and with the current value of StkP rather than SStkP (NextData/NextInst was accessing an operand from location 0 of the buffer, so the opcode was byte 7 of the previous buffer, and PCB _ (PCB) + (4C) never happens before the fault). 3) A PFetch4 fault occurred on page 0 and the emulator's TPC points elsewhere than as in case 2. In this case, it is assumed that the fault is due to a NextData or NextInst buffer refill which are differentiated by the low bit of F1 in the mi pointed at by the emulator's TPC. If the fault is due to a NextData, nothing special is done, but for a NextInst, IBuf to IBuf3 are set to -1, PCF to 0, PCB _ PCB+4, and control is sent to the NextInst (i.e., to the emulator's TPC instead of to the emulator's CIA and with ALU branch conditions and TPC smashed) so other work done in the mi after the NextInst can be completed before the trap. Control then goes to opcode 377, which will cause the trap with PC = 2*PCB + PCX - 1. 4) A PFetch4 fault occurred on page 6. This is a jump opcode buffer refill. Store -1 in IBuf to IBuf3 and continue the emulator at its CIA with ALU branch conditions and TPC smashed. 5) The fault was due to an Xfer buffer refill (MemStat[13:15] = XferFixup). Store -1 in PCF[IBuf], setup T with RSh[xfTemp,1]+CODE, simulating the bypass kludge, and jump into MesaX at the location after the PFetch4. Xfer will finish and transfer control to opcode 377, which starts the trap. 6) The fault occured during the early phases of Xfer. We want to back out and redo the opcode, but CODE may have changed and we need it to compute the PC to save. Call Loadgc to reload from the current LOCAL. 7) If none of these situations hold, the PC is (PCB*2) + q, where q = if (PCF>=PCX) then PCX-1 else PCX-9 (if PCF store, 1=> fetch StkP _ xBuf2, Skip[ALU=0]; *point to count, test result to alu Stack _ (Stack) + 1, GoTo[FixPCOnly]; *fetch; done with fixup Stack _ (Stack) + 1; *Count + 1 Stack&-1, Call[DecGlorp]; *Source - 1 Stack&+2, Call[DecGlorp]; *Dest - 1 GoTo[FixPCOnly]; DecGlorp: Stack _ (Stack) - 1, Return; FixBitBlt: T _ (PCXReg) - 1, Call[BackupPC], At[FixDisp,BitBltFixup!]; T _ (SStkP&NStkP) xor (377C), GoTo[SMTrpx]; FixPCOnly: LU _ PCB, GoTo[MTStkPb,R Odd], At[FixDisp,Normal!]; *normal PC fixup Nop; T _ (PCXReg) - 1, Call[BackupPC]; T _ SStkP, GoTo[SMTrpx]; *PCF_PCX-1 always; only PCB is in doubt. If PCX is large and PCF small, *then PCF has advanced to the next quadword, so PCB_PCB-4 is required. *Alternatively, if PCX is 0, then the opcode started in previous quadword, *so PCB_PCB-4 unless PCF .eq. 7 indicating that PCF_PCX-1 and PCB_PCB-4 *occurred as part of trap service. BackupPC: LU _ (PCFreg) - T, Skip[ALU>=0]; *PCX was 0; opcode started in prev. quadword T _ 7C, GoTo[.-1]; xBuf2 _ T, Skip[ALU>=0]; PCB _ (PCB) - (4C); *PCX large, PCF small = > prev. quadword PCF _ xBuf2, Return; %Here PCB,PCF is correct pc to save for trap. It will be done through KFCB. Enter MesaP at prFault with fault offset in T and parameter in prData. FaultParm has been set up by the Trap handler as follows: -1 => Map Out of Bounds negative => write protect, page in low 14 bits positive => page fault, page in low 14 bits % StartMemTrap: LU _ PCB, DblGoTo[MTStkP,MTSStkP,R Odd], At[StartMemTrapLoc]; MTStkP: T _ (SStkP&NStkP) xor (377C), GoTo[SMTrpx]; MTStkPa: T _ (SStkP&NStkP) xor (377C), GoTo[SMTrpx]; MTStkPb: T _ (SStkP&NStkP) xor (377C), GoTo[SMTrpx]; MTSStkP:T _ SStkP; SMTrpx: xBuf2 _ IP[FaultParm]C, Call[StkPExch]; T _ (Stack) and not (140000C); *T _ saved fault parameter LU _ (Stack) + 1; prData _ T, Skip[ALU<0]; T _ qPageFaultOs, Skip; *Page fault or memory out of bounds T _ qWriteProtectOs; *ShortPtr to Fault[WriteProtectFault] StkP _ xBuf2; *Restore stack pointer xfBrkByte _ 40400C; *Cancel any pending break byte LoadPageExternal[opPage0]; MemStat _ Or[Trap!,NoPushSD!]C, GoToExternal[prFaultLoc]; :END[Fault];e12(1795)\f5