:TITLE[Fault];*Fault handler

%Ed Fiala 10 July 1984: Fix bug in previous repair by assuming that EarlyXfer
write-protect faults can’t happen.
Ed Fiala 2 July 1984: Fix StkP not restored problem for early-xfer faults.
Ed Fiala 16 May 1984: Added back 6 mi to fix NextInst/NextData page fault
bug from 1st mi of opcode.
Ed Fiala 19 March 1984: Remove @FixBlt; receive page 0 subr’s from
Initialize; bum ~4b mi from BLTLFixup; add redundant register defs
for NotifyInterrupt. Push the PC backup formerly done in TrapFlap
(MesaOP3 opcode 377b), at FixPCandStkP, and at @FixBitBlt down into
MesaP; this left only one call to BackupPC in StackErrorz, so changed
it to use a CallExternal and eliminated the BackupPC procedure.
Absolutely locate EmNotifyA+5 for Midas breakpoints when Pilot1 symbols
aren’t loaded.
Ed Fiala 8 November 1983: Change arg for kfcr on StackError. Change
FixPCOnly to FixPCandStkP for FixBLT and FixBLTL (saves 2 mi). Eliminate
FixXfer while jumping into the xfer trap fixup code for FixEarlyXfer;
eliminate StartMemTrap by noting that PCB odd is impossible on opcode 377
(this saved 3 mi); change fixup for BLT. Change entry at prFaultLoc.
Ed Fiala 11 May 1983: Crash on page or WP fault on io task reference;
had to discard LogSE in the reference carcass left for Midas to make room
for a larger MP code. Slowed page and WP faults 2 cycles; speeded up WP
faults 1 cycle using Dispatch instead of Skip[ALU#0]; added 3 mi. Corrected
timing comments.
Ed Fiala 26 May 1982: Removed useless MemStat← in StartMemTrap.
Ed Fiala 18 May 1982: Add long timing comment; improve page fault (etc.)
timing by 6 to 8 cycles by avoiding StkP save/restore unless stack error.
Don’t show MP code when going to Midas; add ResetMemErrs on MC22Crash;
don’t preserve storage (because of power consumption on 12v supply when
doing Refresh in a tight loop).
Ed Fiala 23 February 1982: fix code at CrashLoc; added long comment about
MC1/2 errors; reinsert FixEarlyXfer PCB odd check; change for XferFixup
fault; add T ← (LOCAL) - (2C) at FixEarlyXfer.
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.
Earlier edits by Ed Fiala, Rich Johnsson, Ev Neely, RonJoiner, Jim Frandeen,
Kennedy, Chang.


Problems:
1) PFetch4; NextInst from task 0 will normally fault abort at MesaRefillLoc
if refill occurs, and it will be treated as a refill page fault rather than
an ordinary data page fault. Hence, the emulator must never exit with this
sequence if a page fault is possible. This could be fixed by manually
locating all 4 mi at MesaRefill in MesaOP2.mc and checking not for page
0 but rather for one of these locations.
2) Try to do ReadPipe only on MC12Err path, if that path not made longer.
3) Do something useful in the mi at MC12Err+2.
4) MP report of storage failures would be desirable.
5) If branch condition on first ReadPipe mi could be eliminated (2 above),
then could pick off MC1 crash in that mi by testing for FFault odd; then
test for CTask=0 in the bare Disp at MC12Err+2 and branch on ALU#0 in
the dispatch table to an mi that does RTMP←. This would save 2 cycles
on the slowest paths.

DELAY ON FAULTS until tasking MUST BE MINIMIZED to avoid data lates on io
devices. Although present io devices eventually recover from data lates,
keyboard input can be lost and display jitter occurs; there are other
potential problems.

In computing time for an MC1 fault, allow ~6 cycles after the PFetch/PStore;
the mi after that is aborted until the fault starts; the abort lasts
~10 cycles, unaffected by suspended cycles while transport for previous
references takes place. Then the code here beginning at "FaultOccurred"
is executed. The assumption is made that only the emulator task can
legitimately have references that fault, but the fault may not occur until
another task is running. If a reference initiated by another task faults,
then an MC1 crash occrs (MP codes 220d+task).

In the event the emulator was running at the time of a fault, it is
restarted at a fault address and tasks after a delay of 56 to 72 cycles.
In this case, io task timing is worsened by ~66 to ~82 cycles minus the time
to the next normal emulator tasking point. The next two emulator tasks
occur after 11 and 4 cycles, respectively, which somewhat mitigates the long
time to task.

If an io task was running, however, the emulator TPC is changed to a fault
address and the state of the io task is restored after ~87 or ~93 cycles;
in this case, time in the fault handler is totally additive to worst
case timing in the absence of a fault.

An even longer time in the fault handler (up to ~102 cycles) occurs when
an io task was running at the onset of the fault and doing a "Return" to
the emulator. However, this only happens when no other io task was
requesting service at the onset of the fault, so it is not as bad as the
additive ~93 cycles.
%

%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.
%
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 ← RCy[Page&Par&Boot,4];
*LoadPage is necessary because, after ResetErrors, the Page register will no
*longer be disabled by the error condition. RXPPB[10b..14b] wind up with the
*StkOvf, CSPE, RMPE, and MC12Err bits, respectively. None of these bits =1
*generally means a breakpoint has occurred.
RXPPB ← T, LoadPage[FaultPage], GoTo[NotStkFault,H2Bit8’];
T ← (SStkP&NStkP) xor (377C);

OnPage[FaultPage];

RXSTK ← IP[RXCTask]C;
StkP ← RXSTK, RXSTK ← T, NoRegILockOK;
*aluresult, saluf (both read complemented)
NotStkFault:
T ← (ALUResult&NSALUF) xnor (0C);
RXALU ← 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.
*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,10,4]) - 1;
*Get pipe A (Timing for this mi = 8 cycles on MC12Err)
ReadPipe[PipeReg], FreezeResult, GoTo[MC12Err,ALU=0];
LU ← (RXPPB) and (140C), 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,10,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. 1000b 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, GoTo[.].
%
:IF[WithMidas]; *********************************
Crash:
LU ← LdF[FFault,3,1], Call[Crash1];
GoTo[.];*Wait for boot...
%Spin doing refresh allowing a Midas connection later; starting Midas
will boot the machine and start Kernel, but storage will remain intact for
examination. But don’t refresh in a tight loop that would consume more power
on storage boards, ?heating them up and draining the ?volt power supply.
Refresh[REFR];
RTMP ← (RTMP) - 1, GoTo[.,R>=0];
REFR ← (REFR) + (20C);
RTMP ← 2400C, GoTo[.-3];*2371b = 2560 cycle timer
%
*Show MP code and halt if Midas not connected, else MP code not shown.
Crash1:
PipeReg5 ← (LSh[PipeReg5,11]) or T, GoTo[PNIP,ALU=0];
LoadPage[MidasPage];
LU ← LdF[RXPPB,10,1];
OnPage[MidasPage];
RXPPB ← LCy[RXPPB,4], GoTo[.+3,ALU#0];*Unrotate RXPPB for Midas
T ← (SStkP&NStkP) xor (377C);
RXSTK ← T;
LU ← LdF[RXPPB,4,4];
RTMP ← Or[And[IP[MapEntry],360],And[Sub[IP[MapEntry],1],17]]C, Skip[ALU#0];
RXCTask ← (RXCTask) xnor (170000C), GoToExternal[MidasBreakLoc];
StkP ← RTMP;
T ← LdF[PipeReg,11,7];*Map row address’
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 ← LdF[PipeReg5,7,11], 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,3,3]) - T;
T ← (Zero) - T, Call[FltPsh];*CardNumber
*Map Flags (LogSE, WP, Dirty, Ref) into MapFlags (LogSE is always shown as 1).
T ← (LdF[PipeReg5,0,3]) 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,6,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;

*LoadPageExternal[FaultPage]; GoToExternal[CrashLoc]; crashes any task.
Call[PNIP], At[CrashLoc];
BugHalt:
GoTo[.];
:ELSE; ******************************************
Crash:
Call[PNIP], At[CrashLoc];
BugHalt:
GoTo[.];
:ENDIF; *****************************************


OnPage[FaultPage];

%
Arrive at MC12Err if an MC1 or MC2 error occurred without any accompanying
CSPE, RMPE, or StkOvf error.

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 or not MOB errors are indicated correctly in MC1ErA
and MC1ErB. 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 = 26 cycles from loc 1 to here.
%
MC12Err:
LU ← Dispatch[PipeReg,4,2];*Dispatch on H4pe, MapBnd
***This Dispatch is only useful on the MC1/MC2 path.
*Dispatch on MC2ErA’, MC2ErB’, MC1ErA’, and MC1ErB’ bits
LU ← Dispatch[PipeReg,0,4], Disp[.+1];
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];
T ← LdF[RXCTask,4,4], GoTo[T17RestoreB];

MC2ErAB:
*
ResetMemErrs, GoTo[MC22Die], At[MC12,0];*MC2A/B & MC1A/B
*
ResetMemErrs, GoTo[MC22Die], At[MC12,1];*MC2A/B & MC1A
*
ResetMemErrs, GoTo[MC22Die], At[MC12,2];*MC2A/B & MC1B
ResetMemErrs, GoTo[MC22Die], 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

MC22Die:
T ← MC22Crash, GoTo[Crash];
MC1Die:
T ← MC1Crash, GoTo[PipeTaskCrash];

%First, crash if a page or write protect fault from an io task occurs. Then,
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 ← 17C;
LU ← (LdF[PipeReg2,10,4]) xor T;
T ← (PipeReg) and (177C), Skip[ALU=0];*Low 7 bits of VPage
T ← MC1Crash, GoTo[PipeTaskCrash];
*(High 7 bits of VPage’ or low 7 bits)’; bits 0 and 1 wind up both 1.
T ← (LSh[PipeReg1,7]) xnor T;
LU ← Dispatch[PipeReg5,12,1];*Test dirty’: 0=> page fault
FaultParm ← T, Disp[.+1];
FaultParm ← LdF[FaultParm,2,16], Skip, DispTable[2];*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
FaultParm ← (Zero) - 1, GoTo[MFault];

*Arrive at StackTrap on a stack error, possibly in conjunction with an MC1/2
*error; IP[RXCTask]=374 is in StkP and the value of StkP when the fault
*occurred is in RXSTK.
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:
45 or 46 cycles on MOB;
51 or 52 cycles on Stack overflow or underflow;
59 to 66 cycles on page or 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:
44 cycles on H4PE;
72 to 73 cycles on Stack overflow or underflow;
73 to 74 cycles on MOB;
87 or 93 cycles on page or WP fault.
+ 9 cycles if APC must be fixed for an io task returning to the emulator,
but this is not a bad timing case because no other io task was requesting
service when the fault occurred.
%
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,4,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;
*This mi is manually located so that Pilot2 page 0 code can jump to it.
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 regular or jump buffer refill trap when non-emulator is
*running at the time of fault entry.
*Also jump here on task 0, location 0, PFetch4.
CMSt2:
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.
**StackErrorLoc = IMX 136 is a good error breakpoint in Midas.
StackErrorz:
*test SStkP for MaxStack+1 or more
LU ← (SStkP&NStkP) - (LShift[Add[MaxStack!,1],10]C), At[StackErrorLoc];
MemStat ← Trap, Skip[Carry’];
T ← MaxStack, Skip;
T ← SStkP;
xBuf2 ← T, Call[StkPExch];
LoadPageExternal[opPage0];
T ← (PCXreg) - 1, CallExternal[BackupPCLoc];
LoadPageExternal[opPage3];
xfSD ← T ← sStackError, GoToExternal[KfcrLoc];


EmNotifyA:
UseCTask, xBuf ← T, At[EmuNotifyLoc];*xBuf ← emulator’s T
%FINALLY, task after the following delays:
51 cycles on emulator MOB;
57 cycles on stack overflow or underflow;
65 to 72 cycles on page or WP 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];

%The microprocessor arrives here when task 0 was running at the fault entry;
the simple sequence below EmNotifyB is executed instead when some other task
was running. Extra code is needed here to distinguish jump buffer refill
faults and regular buffer refill faults; since the code following a PFetch4
buffer refill doesn’t task until the time for a fault has passed, these
special cases occur only when the emulator is running.

At this point we have:
xBufemulator’s T at the time of the fault;
xBuf1emulator’s TPC;
xBuf2CIA of the aborted mi.
StkP has been restored to its value at the onset of the fault and MNBR is
still intact, but SALUF has been smashed. 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 6 below.

2) A PFetch4 fault occurred on page 0 & task 0’s TPC points at the 1st
mi of a bytecode (TPC = 01xxxxxxxx01). It is assumed 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 & 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 & 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, & 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 indicating 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 occured during Xfer. We want to back out and redo the opcode,
but LOCAL, GLOBAL, CODE, PCB, and PCF may have changed and must be restored.
Restore these registers using the code beginning at xfFault in MesaOP3.mc
(which is also used on traps from xfer).

6) 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<PCX, then the buffer was
refilled between the NextInst and the fault, and PCB has been advanced
by 8 bytes). In the normal case, the trap is started using this PC, and
it is not necessary to unwind the instruction. If any special unwinding
is necessary,it is indicated by a value in MemStat[13:15].
%
T ← 6C, At[FaultPage,71];**LOCATE FOR MIDAS BREAKPOINTS
LU ← LdF[xBuf2,4,4];
*Go if emulator page 0 fault; test for RefType=6 (PFetch4)
LU ← (NSALUF) xor T, GoTo[CameFromPage0,ALU=0];
*GoTo CheckMemStat if not a PFetch4.
LU ← (LdF[xBuf2,4,4]) xor T, GoTo[CheckMemStat,ALU#0];
*The operation is a jump buffer refill if it is a PFetch4 from page 6.
DblGoTo[CMSt0,ResumeBytecode,ALU#0];


CameFromPage0:
LU ← xBuf2, Skip[ALU=0];*test for aborted CIA = 0
Dispatch[MemStat,15,3], GoTo[CMSt1];*Page 0, not PFetch4
*Jump if the fault happens at location 0 for a PFetch4.
xBuf1 ← LCy[xBuf1,6], GoTo[CMSt2,ALU=0];
*The operation is a PFetch4 from page 0 but not from location 0. ASSUME
*it is buffer refill for a NextInst of NextData. Test for emulator
*TPC = 01xxxxxxxx01; put the high 2 and low 2 bits of TPC in the right-half
*with task 0 in the middle.
T ← 101C;
LU ← (RHMask[xBuf1]) xor T;
T ← RCy[xBuf1,6], Skip[ALU#0];*T ← original xBuf1
*TPC = 01xxxxxxxx01, fault was from 1st mi of bytecode. Do not backup
*PC or restore StkP from SStkP because PCX and SStkP are not advanced to
*this current opcode until AFTER T2 of the 1st mi of the bytecode.
PCF ← AllOnes, GoTo[NIPageFault];*PCF ← 7
*Fault was NOT from 1st mi of bytecode; find out whether emulator’s TPC
*points at a NextInst or NextData.
xBuf2 ← T;*xBuf2 ← emulator’s TPC instead of aborted CIA
PCB ← (PCB) + (4C);
T ← PCF ← RZero;*Does TPC point to a NextInst?
APCTask&APC ← xBuf2;
ReadCS;
*Branch on probable NextData; fall through on probable NextInst.
CSData, GoTo[CMSt0,R Odd];
%Jump to ResumeByteCode on a PFetch4 from page 6 (Mesa jumps are all on
page 6). Continue the jump after filling IBuf with -1, and eventually get
to opcode 377, which will start the trap.
%
ResumeBytecode:
T ← xBuf;*Restore emulator’s T register
IBuf ← (Zero) - 1, Task;
IBuf1 ← (Zero) - 1;
IBuf2 ← (Zero) - 1;
APCTask&APC ← xBuf2;*Return to the NextInst or whatever
IBuf3 ← (Zero) - 1, Return;*Force bytecode 377


CMSt0:
Dispatch[MemStat,15,3], GoTo[CMSt1];*NextData fault
CheckMemStat:
Dispatch[MemStat,15,3];
CMSt1:
Disp[.+1];
*Normal PC and StkP fixup
T ← SStkP, GoTo[SMTrpx], At[FixDisp,Normal!];

@FixEarlyXfer:
T ← (SStkP&NStkP) xor (377C), At[FixDisp,EarlyXfer!];
xBuf2 ← IP[FaultParm]C, Call[StkPExch];
*Obtain the fault parameter in T; then restore StkP in case the fault
*occurred on an xfer which trapped or on an xfer from the scheduler.
*In other cases, StkP will be restored from SStkP in the MesaOP3 code
*below the label xfBackPC. Only not-in-core faults are possible for xfer
*because the writes are all confined to the current procedure’s local
*frame words -4 to +3 (which are on the same page) and into the FSI table
*which is resident; hence, no need to check for write-protect faults here.
T ← (Stack) and not (140000C), Call[StkPExch];
LoadPageExternal[xfPage1];
prConditionQ ← qPageFaultOs, GoToExternal[xfFaultLoc];

*StkP points at the count word on the stack.
@FixBLTL:
*one byte inst cannot have refilled buffer
Stack ← (Stack) + 1, At[FixDisp,BltLFixup!];*count + 1
Stack&-2, LoadPageExternal[opPage3];
T ← xBuf, CallExternal[BLTLFixLoc];*source + T
LoadPageExternal[opPage3];
*Since StartMemTrapLoc=0016b, this call is at 15b
Stack&+2, CallExternal[BLTLFixLoc];*dest + T
%Enter MesaP at prFaultLoc with value to restore to StkP in xBuf2, fault
queue in prConditionQ, fault parameter in prData and in T, byte PC not
backed up yet [But backing the PC up more than once is ok.].
FaultParm has been set up by task 17b as follows:
-1 => Map Out of Bounds
negative => write protect, page in low 14 bits
positive => page fault, page in low 14 bits
Jump opcode and NextInst buffer refill page faults exit via ResumeByteCode
above, execute NextInst and some unspecified final microinstruction including
NIRet in the old context, then opcode 377 (@TrapFlap in MesaOP3), which jumps
back here to start the fault.
***Are interrupts disabled on opcode 377?
%
*Normal PC and StkP fixup
T ← SStkP, At[StartMemTrapLoc];
SMTrpx:
xBuf2 ← IP[FaultParm]C, Call[StkPExch];
T ← (Stack) and not (140000C);*T ← saved fault parameter
LU ← (Stack) + 1;
prData ← T, LoadPageExternal[opPage0], Skip[ALU<0];
*Page fault or memory out of bounds
prConditionQ ← qPageFaultOs, GoToExternal[prFaultLoc];
*Write protect fault
prConditionQ ← qWriteProtectOs, GoToExternal[prFaultLoc];

@FixBitBlt:
T ← (SStkP&NStkP) xor (377C), GoTo[SMTrpx], At[FixDisp,BitBltFixup!];

*NextInst or NextData fault from 1st mi of opcode.
NIPageFault:
T ← (SStkP&NStkP) xor (377C);
xBuf2 ← IP[FaultParm]C, Call[StkPExch];
*T ← saved fault parameter
T ← (Stack) and not (140000C);
*Must be page fault or memory out of bounds--can’t be write protect fault.
prConditionQ ← qPageFaultOs;
prData ← T, LoadPageExternal[opPage0];
StkP ← xBuf2, GoToExternal[prNoBackPCFaultLoc];

%PNIP displays the number in T in the maintenance panel and returns, where T
is meaningfully in the range 0 to 9999d (0 to 23417b). Does not task unless
Called from task 0. Its registers, RTemp and RTemp1, should not conflict
with those for the Midas Kernel or with any used by tasks 14 to 17 because of
Calls from Fault.Mc, which might subsequently go to Midas; any io task that
Calls PNIP and expects to continue should be wary of the fact that PNIP’s
registers are defined for task 0 and might conflict.

The loop below must allow 800 ns after ClearMPanel and 400 ns after IncMPanel.
If necessary the high two bits of RTemp can be used to increase loop time.
ClearMPanel and IncMPanel may be illegal in the same mi with a true branch
condition. For large numbers, map/storage refresh may fail when Called by an
io task.
%

PNIP:
RTemp ← T, ClearMPanel, At[PNIPStart];
UseCTask;
T ← APCTask&APC, UseCTask, Call[PFExit];
*14 cycles after ClearMPanel and 8 cycles after IncMPanel should be safe
*on the fastest machines.
PNIPl:
RTemp ← (LdF[RTemp,2,16]) - 1;
RTemp1 ← T, Skip[ALU>=0];
APCTask&APC ← RTemp1, GoTo[PFExit];
LU ← LdF[RTemp1,0,4];
Skip[ALU=0];
IncMPanel, GoTo[PNIPl];
IncMPanel, Return;

*NotifyInterrupt ORs T into NWW and sets IntPending. Uses registers
*0 and 1 in whatever task Calls. Show that these registers are used in
*the Pilot1.regs file to prevent inadvertent programming errors.
RM[T5IntTemp1,100];
RM[T5IntTemp2,101];
RM[T6IntTemp1,100];
RM[T6IntTemp2,101];
RM[T7IntTemp1,100];
RM[T7IntTemp2,101];
RM[T10IntTemp1,100];
RM[T10IntTemp2,101];
RM[T11IntTemp1,100];
RM[T11IntTemp2,101];
RM[T12IntTemp1,100];
RM[T12IntTemp2,101];
RM[T13IntTemp1,100];
RM[T13IntTemp2,101];
RM[T14IntTemp1,100];
RM[T14IntTemp2,101];
RM[T15IntTemp1,100];
RM[T15IntTemp2,101];
SetTask[4];
RV[T4IntTemp1,0];
RV[T4IntTemp2,1];

NotifyInterrupt:

T4IntTemp1 ← T, At[NotifyInterruptLoc];*Save interupt mask
T4IntTemp2 ← IP[NWW]C, Skip[ALU#0];*Point to NWW register
Return;*No change
T ← (SStkP&NStkP) xor (377C);*T ← StkP
StkP ← T4IntTemp2, T4IntTemp2 ← T, NoRegILockOK;
T ← T4IntTemp1;*T ← interrupt mask
Stack ← (Stack) or T;*set bits in NWW
Stack&-1;*Point StkP at RSImage
T ← Stack ← (Stack) or (IntPendingBit);
LU ← StkP ← T4IntTemp2, RS232 ← T, Return;

:END[Fault];