:TITLE[Fault];*Fault handler

*Error Codes for Fault Handler
MC[RCSCrash,200];
*R or CS parity error
MC[BPCrash,201];
*Real Breakpoint
MC[MOBCrash,202];
*Map Out of Bounds
MC[H4PECrash,203];
*H4PE
MC[MOB&H4PECrash,204];
*MOB and H4PE
MC[MC2Crash, 205];
*MC2 error when unable to handle it by RETURNing
MC[MC22Crash,206];
*2 MC2 errors
MC[MC1Crash,207];
*MC1 fault when emulator couldn’t accept it
MC[LPCrash,210];
*Fault from the instruction following a LoadPage
MC[StkCrash,211];
*Stack over/underflow
MC[NStkCrash,166];
*Not Stack over/underflow (8 bits)

*Fault handler
*Determine what to do based on the type of error and bits in FFAULT RM 366).
*Bits in FFAULT are:
*
0: MC2 errors RETURN if 1, crash if 0
*
2: H4PE errors from task 7 return to the task that faulted rather
*
than crashing (because of Ethernet problem of delivering H4PE’s
*
if a malformed packet arrives)
*
3: Midas is present (1), so "crash" means breakpoint, else put a
*
code in MP and halt.
*
15: MC1/StackOvf errors handled by notifying PFEntry in emulator (1),
*
or by crashing (0)

*The following code (nearly a copy of the page 0 code in Kernel) sends
*control to FaultStart after saving state, and FaultStart figures out whether
*Midas is present and whether to send a breakpoint or a fault message.


OnPage[0];
SetTask[17];

RV[PipeReg,60]; *Pipe Ram Entry goes here
MC[pPipeReg,360];
MC[pPipeReg2,362];
RV[PipeReg1,61];
RV[PipeReg2,62];
RV[PipeReg3,63];
RV[PipeReg4,64];
RV[PipeReg5,65];


*Page Zero stuff
*We put the instruction for BufferRefill here..
x377x:
gotop[x377x], at[377];*dummy instruction
loadpage[0], goto[x377x], at[0]; *Emulator buffer refill code is on page 0
T ← APCTASK&APC, AT[1];*Fault entry. Save APC first, then the other volatile regs.
RXAPC ← T, AT[100];
T ← GETRSPEC[147], AT[101];*ctask, ncia
RXCTASK ← T, AT[102];
T ← (GETRSPEC[103]) xor (377c), AT[103];*sstkp, stkp (stkp is read complemented)
RXSTK ← T, AT[104];
RTMP ← 20c, AT[105]; *Set stkp to 20 in case there was a stack overflow pending
Stkp ← RTMP, AT[106];
T ← (GETRSPEC[107]) xnor (0c), AT[107];*aluresult, saluf (both read complemented)
RXALU ← T, AT[110];
T ← GETRSPEC[157], LOADPAGE[0], AT[111]; *page, parity, bootreason
RXPPB ← T, RESETERRORS,goto[FaultStart], AT[112];

FaultStart: lu ← (RXPPB) and (3000c), at[120]; *test R & CS parity
goto[RCSErr, ALU#0], lu ← (RXPPB) and (400c);*test memory error
goto[MC12Err, ALU#0], lu ← (RXPPB) and (4000c);*test stack ovf
goto[TryBP, ALU=0];
StkEr:
FFAULT, dblgoto[MC1NotifyEmulator, Crash,RODD], T ← StkCrash; *can emulator take fault?

RCSErr:
T ← RCSCrash, goto[Crash];

TryBP:
T ← BPCrash, goto[Crash];

*Get here with error code in T. If Midas is present, breakpoint. Otherwise, put
*the code into the maintenance panel and halt.
Crash:
lu ← ldf[FFAULT,3,1];
goto[Midas,ALU#0],PipeReg5 ← (lsh[PipeReg5,10]) or (T); *save error code in right half of PipeReg5
nop;
call[PNIP];
goto[.];

Midas:
RTMP ← 117c;
*Reformat saved Pipe into RM 100-105 for Midas. Note that pipe info (other
*than crash code and task number) is only interesting if CrashCode=205
Stkp ← RTMP;

SetTask[4]; *To allow symbolic names for RM 100-106
RV[MapEntryNumber,0];
RV[TaskNumber,1];
RV[RefType,2];
RV[CrashCode,3];
RV[CardNumber,4];
RV[MapFlags,5];
RV[QuadAddr,6];
RV[Syndrome,7];
SetTask[17];

T ← ldf[PipeReg,11,7]; *map row address
PipeReg1 ← (lsh[PipeReg1,7]) or T;
*Map location (page number) in bits 2-17b...
*goes into location 100 (but the bits are inverted)
T ← ldf[PipeReg1,2,16], Call[FltPsh];
Stack ← (Stack) xnor (140000c); *now contains the virtual page number

T ← 17c;*task number (upright)...
T ← (ldf[PipeReg2,10,4]) xor T, Call[FltPsh]; *into location 101

T ← 17c;*reference type (upright)...
T ← (ldf[PipeReg2,14,4]) xor T, Call[FltPsh]; *into location 102

T ← rhmask[PipeReg5], Call[FltPsh]; *Crash code into 103

T ← 7c;
T ← (ldf[PipeReg5,4,3]) xor T, Call[FltPsh]; *Card no. (0..7) into 104

T ← 17c;
T ← (ldf[PipeReg5,0,4]) xor T, Call[FltPsh]; *Map Flags (4 bits) into 105

T ← ldf[PipeReg4,12,6]; *Main column address (6 bits)
T ← (lsh[PipeReg3,6]) or T; *And Blk.1,,main row address
T ← (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 ← (zero) xnor T, Call[FltPsh]; *All upright into 106

T ← rsh[PipeReg,10], Call[FltPsh]; *The interesting syndrome into 107

lu ← ldf[RXPPB,4,4]; * test parity register
RTMP ← 177400c, skip[alu#0]; *Notify Midas at 7510b or 7512b
RTMP ← (RTMP) OR (110C), goto[MidasNotify]; *Go overlay ’Break’
RTMP ← (RTMP) OR (112C); *Go overlay ’MidasFault’
MidasNotify:
APCTask&APC ← RTMP, goto[PNret];
FltPsh:
UseCTask, Stack&+1 ← T, Goto[PNret];

MC12Err:
Stkp ← RXSTK; * not stack error, restore pointer
ReadPipe[PipeReg];*get A pipe
Dispatch[PipeReg,4,2];*dispatch on H4pe, MapBnd
Dispatch[PipeReg,0,2], Disp[NoH4BndEr];*dispatch on MC2ErA’, MC2ErB’

NoH4BndEr:
Disp[MC2ErAB], lu ← (PipeReg) and (20000c), AT[H4disp,0]; *test MC1ErA’ bit

BndEr:
T ← MOBCrash, goto[Crash], AT[H4disp,1]; *MOB error only

H4Er:
RTMP ← H4PECrash, AT[H4disp,2];
H4Er1:
lu ← ldf[FFAULT,2,1]; *Check for return to faulted task
Stkp ← RXSTK, skip[ALU=0];
T ← RTMP, goto[Crash];

*check for faulted task = 7 (Ether Input)
T ← 7c; *Ether Input task number
lu ← (ldf[RXCTASK,0,4]) xor (T);
skip[ALU#0];
ResetMemErrs, goto[T17Restore]; *back to faulted task
T ← RTMP, goto[Crash];


*Note: the following is not quite correct, since a REAL MOB error will be continued
*if it occurs with an H4PE. Life is hard...
H4BndEr:
RTMP ← MOB&H4PECrash, goto[H4Er1], AT[H4Disp,3]; *H4PE & MOB

MC2ErAB:
T ← MC22Crash, goto[Crash], AT[MC2ErDisp,0]; *Have both MC2 A & B error - crash
MC2ErA:
T ← lhmask[MemSyndrome], goto[MC2Er], AT[MC2ErDisp,1]; *MC2A error
MC2ErB:
ReadPipe[PipeReg], ResetMemErrs, AT[MC2ErDisp,2]; *MC2B error - read pipe entry
T ← lsh[MemSyndrome,10], goto[MC2Er];
NoMC2Er:
Goto[MC1Er,ALU=0], ResetMemErrs, AT[MC2ErDisp,3]; *Branch if MC1ErA’ = 0
ReadPipe[PipeReg], ResetMemErrs; *MC1B error - read pipe entry
MC1Er:
FFAULT, dblgoto[MC1NotifyEmulator,Crash,RODD], T ← MC1Crash; *can emulator take fault?

MC2Er:
ResetMemErrs;
lu ← ldf[FFAULT,0,1]; *check for return (1) or crash (0)
goto[MC2ErRet,ALU#0], PipeReg ← (rhmask[PipeReg]) or (T); *Stash the proper syndrome in
**PipeReg[0:7]
T ← MC2Crash, goto[Crash];
MC2ErRet:
return;


*Notify emulator at EmNotifyA if emulator was the interrupted task, else at EmNotifyB
MC1NotifyEmulator:
lu ← ldf[RXCTask,0,4];
RTMP ← 202c, goto[MC1NEx,ALU#0];
RTMP ← 200c; *emulator task interrupted - notify EmNotifyA
MNBR ← RXCTask; *save the emulator’s PC for further consideration
MC1NEx:
APC&APCTask ← RTMP;
SALUF ← T, return; *save the crash code in saluf, so that the emulator can use it

T17Restore: RXCTask ← (RXCTask) xnor (170000c), AT[204]; *complement CIA
T ← ldf[RXCTask,4,4];*page bits
lu ← (ldf[RXPPB,0,4]) xor (T);*compare with saved page register
goto[LoadPageError,ALU#0], T ← RXALU; *result register
APC&APCTask ← RXCTask;
Restore, A ← RXAPC, lu ← T, NoRegILockOK, Return; *back to faulted task

LoadPageError: T ← LPCrash, goto[Crash];


SetTask[0];
NotifyBack: RTEMP ← (RTEMP) or (170000C);
APC&APCTask ← RTEMP, goto[PFExit];
PFExit:
return;

EmNotifyA:
usectask, xBuf ← T, AT[200]; *save the emulator’s T in xBuf
T ← APC&APCTask, call[PFExit]; *save emulator’s TPC in T, and task switch

*
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 (complemented) is in MNBR.
PFEntryA:
xBuf1 ← T, loadpage[FaultPage1];
gotop[PFEntryAx];

EmNotifyB:
usectask, xBuf ← T, AT[202];
T ← APC&APCTask;
RTEMP ← 204c, call[NotifyBack]; *Prepare to notify back to task 17, location T17Restore

*
PF handling starts here if non-emulator was interrupted.
PFEntryB: xBuf1 ← T, loadpage[FaultPage1];
gotop[PFEntryBx];


OnPage[FaultPage1];

PFEntryAx:
T ← NStkCrash, Call[CheckStackTrap];
lu ← ldf[xBuf2,4,4], goto[CheckBufferRefill];

PFEntryBx:
T ← NStkCrash, Call[CheckStackTrap];
goto[CMSt1], Dispatch[MemStat,15,3]; *cannot be buffer refill trap


CheckStackTrap: lu ← (SALUF) xor (T);
T ← MNBR, goto[StackErrorz, ALU=0]; *set up to test page bits of emulator’s PC
xBuf2 ← (ZERO) xnor (T); *complement value
xBuf2 ← (xBuf2) and not (170000c), return;
*We have a stack error. Cause the trap immediately. Set Stkp back to beginning of last
*instruction. Let the PC fall where it may.

StackErrorz:
lu ← (GetRSpec[103])-(lshift[11,10]c); *test SStkp for 9 or more
T ← SStkp, skip[nocarry];
RTEMP ← 10c, goto[.+2];
RTEMP ← T;
Stkp ← RTEMP;
loadpage[7];
T ← sStackError, gotop[kfcr];


*At this point, xBuf contains the emulator’s T at the time of the fault,
*xBuf1 contains the emulator’s TPC, and xBuf2 contains the PC of the
*aborted instruction. The major problem with faults is to determine the
*PC to store in the frame, and whether to continue the instruction, or
*cause the trap immediately. The cases are:

*1) The fault was caused by a NextInst (PC is on page 0, TPC points to
*a NextInst). We set IBUF to -1, PCF to 0, and send control back to
*the NextInst. This will cause the faulted instruction to be completed,
*and control will go to opcode 377, which will cause the trap with
*PC = 2*PC + PCX -1.

*2) The fault was due to a NextData in the first microinstruction of
*a bytecode (TPC = 01xxxxxxxx01). The trap is started immediately, with
*PC = 2*PC - 1 (The NextData was trying to get an operand from location
*0 of the buffer, so the opcode is at location 7 of the previous buffer,
*but PC was incremented by 8 bytes before the fault was discovered).

*3) The fault was detected on page 6 and was due to a Pfetch4. This is
*a jump instruction buffer refill. We proceed as in case 1 without
*setting PCF.

*4) The fault was due to an Xfer buffer refill (MemStat[13:15] =
*XferFixup). This is handled just like a jump.

*5) The fault occured during the early phases of Xfer. We want to back
*out and redo the instruction, but CODE may have changed and we need it
*to compute the PC to save. Fetch G and the overhead, and call LoadC
*to reload from the current LOCAL.

*If none of these situations hold, the PC is (PC*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 PC 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].


CheckBufferRefill:
T ← 6c, goto[CameFromPage0,ALU=0]; *test for emulator page 0 fault
lu ← (ldf[xBuf2,4,4]) xor (T);
T ← (GETRSPEC[103]) xor (377c), goto[CheckMemStat,ALU#0]; *get ready to save stackpointer
*We came from page 6. If the operation was PFetch4, this is a jump buffer refill
IBuf3 ← pPipeReg2; *IBuf3 is a guaranteed free temporary
Stkp ← IBuf3, IBuf3 ← T, Task, NoRegILockOK; *now pointing at the operation
T ← 11c; *not 6 (PFetch4)
lu ← (ldf[Stack,14,4]) xor (T);
Stkp ← IBuf3, dblgoto[CMSt2,ContinueInterruptedBytecode,ALU#0];
*We have a PFetch4 from Page 6, i.e. JumpCity. We continue the jump after filling IBuf with -1’s,
*and eventually get to opcode 377.


CameFromPage0:
xBuf1 ← lcy[xBuf1,6]; *set up to test for TPC = 01xxxxxxxx01.
lu ← xBuf2;* test for aborted pc = 0
T ← 101c, goto[FPCOy, alu = 0];* high 2 and low 2 bits or addr, task 0 in middle
lu ← (rhmask[xBuf1]) xor (T);
goto[CBR1, ALU#0], T ← rcy[xBuf1,6]; * put TPC back
PC ← (PC) -(4c); *TPC = 01xxxxxxxx01, fault was from first instruction of bytecode
PCF ← AllOnes; * PCF ← 7
T ← (GetRSpec[103]) xor (377c), goto[SMTrpx]; * use current Stkp

CBR1:
xBuf2 ← T;* xBuf2 now contains TPC instead of aborted PC
T ← 0c; *Does TPC point to a NextInst?
APC&APCTask ← xBuf2;
ReadCS;
CSDATA, goto[CMSt0,Rodd];
PCF ← RZERO; *PCF ← 0
ContinueInterruptedBytecode:
T ← xBuf; *xBuf2 points to place to resume the bytecode
CIB1:
IBuf ← (Zero)-1, task;*force bytecode 377
IBuf1 ← (Zero)-1;
IBuf2 ← (Zero)-1;
IBuf3 ← (Zero)-1;
APC&APCTask ← xBuf2, goto[PFExit1];*Return to the NextInst


CMSt2:
Dispatch[MemStat,15,3], goto[CMSt1];
CMSt0:
Dispatch[MemStat,15,3], goto[CMSt1];
CheckMemStat:
Dispatch[MemStat,15,3];
CMSt1:
Disp[FixPCOnly];


FixXfer:
T ← xBuf, goto[CIB1],AT[FixDisp,1];

FixEarlyXfer:
PFetch1[LOCAL,GLOBAL,0],AT[FixDisp,4]; *Fetch G
T ← GLOBAL, task;
PFetch4[MDS,IBUF]; *Fetch the global frame overhead
LoadPage[xfPage1];
call[LoadC];
xfGfiWord ← T;
FPCOy:
T ← (PCXReg) - 1, goto[FPCOx];



FixBLTL:
T ← SStkp,AT[FixDisp,2];* prepare for fixup relative to saved stkp
RTEMP ← (T), task;
RTEMP ← (RTEMP) - (4c);
Stkp ← RTEMP;
T ← xBuf,Call[BumpGlorp];* source + T
Stack&+1, Call[FBumpStk];* count + 1
Stack&+1, Call[BumpGlorp];* dest + T
T ← (PCXReg) - 1, goto[FPCOx];* one byte inst cannot have refilled buffer

BumpGlorp:
Stack ← (Stack) + T;
Stack&+1,skip[NoCarry];
FBumpStk:
Stack ← (Stack) + 1, Return;
PFExit1:
Return;


FSetStkP:
StkP ← RTemp, RTemp ← T, NoRegILockOK, Return;

FixBlt:
T ← (SStkp) - 1, AT[FixDisp,3];* prepare for fixup relative to saved stkp
RTEMP ← pPipeReg2, Call[FSetStkP]; *Point StkP at operation, RTemp at count word
* Operation (complemented) is low order four bits of Stack
* We know the op was either Pfetch1 (type = 4) or Pstore1 (type = 10b)
T ← ldf[Stack,15,1];* we test Stack[13]: 0=> fetch, 1=> store
Stkp ← RTEMP, lu ← T, NoRegILockOK;* point to count, test result to alu
Stack ← (Stack) + 1, skip[alu#0]; * count + 1; now test fetch/store
T ← (PCXReg) - 1, goto[FPCOx];* fetch; done with fixup
Stack&-1, call[DecGlorp];* source - 1
Stack&+2, call[DecGlorp];* dest - 1
T ← (PCXReg) - 1, goto[FPCOx];

DecGlorp:
Stack ← (Stack) - 1, return;


FixPCOnly:
T ← (PCXReg) - 1,AT[FixDisp,0]; *normal PC fixup
FPCOx:
RTEMP ← T, skip[alu>=0];
PC ← (PC) - (4c);* PCX was 0, inst started in previous quadword
lu ← (PCFReg) - (T); *test for PCX large, PCF small
PCF ← RTEMP, skip[alu>=0]; *PCF is always PCX-1, only PC is in doubt
PC ← (PC) - (4c);


*Here PC,PCF is correct pc to save for trap. It will be done through KFCB.

StartMemTrap:
T ← SStkp;
SMTrpx:
RTEMP ← pPipeReg, Call[FSetStkP];*Point stkp to pipe registers
MemStat ← Normal;
T ← (Stack&+1) and (177c);*low 7 bits of VPage
T ← (lsh[Stack&+1,7]) or (T);*high 7 bits of VPage
xfOTPReg ← (zero) or not (T);
xfOTPReg ← (xfOTPReg) and not (140000c);
stack&+3, task;*point to flags
T ← ldf[Stack, 12, 1];*Test Dirty’: 0=> page fault
Stkp ← RTEMP, lu ← T, NoRegILockOK;*Restore stkp
skip[ALU=0], LoadPage[7];
T ← sWriteProtect, gotop[kfcr];
T ← sPageFault, gotop[kfcr];

:END[Fault];