*-----------------------------------------------------------
Title[DMesaFaults.mc...September 23, 1981 3:29 PM...Taft];
*-----------------------------------------------------------

%
Mesa fault handler.

If the fault was an emulator map fault or a stack error, and the
emulator running at the time was Mesa, then calls the appropriate
trap procedure in SD.

For any other fault, halts at one of the following breakpoints:

(In fault task, not having reset FaultInfo)
ManyFaultsMore than one fault
NotEmuFaultFault in non-emulator task
NotMapFaultMemory system fault other than map fault
UnknownFaultFault of unknown origin

(In emulator task, having reset FaultInfo)
AEmuStackErrStack error from Alto emulator
AEmuMapFaultMap fault from Alto emulator

(In emulator task, the fault task never having been awakened)
MesaMapFaultMap fault caused by IFU reference
MesaFGParityFG parity error
MesaRamPEIFU decoding Ram parity error
BogusMapFaultIFU gave a map fault, but the page referenced
by PCX is not vacant and shouldn’t have faulted.

In the case of faults passed to the emulator task, interesting information
is left in R-registers, as follows:
FltErrorsNOT (Errors’)
FaultInfoNOT (Pipe2’)
FltEmuPCEmulator TPC at time of fault
FaultValDBuf -- value being stored if faulted on Store←

Note that this version of the fault task does NOT try to emulate an XM Alto.
%

*-----------------------------------------------------------
* Fault Task
*-----------------------------------------------------------

Set[XTask, IP[FLT]];

Subroutine;
FLTInitPC:
T← FLT, CoReturn;
TopLevel;

T← A0, RBase← RBase[FaultInfo];
TIOA← T, Block;

FaultTask:
FltErrors← NOT (Errors’);* Read this always
FaultInfo← NOT (Pipe2’);* Doesn’t clear FaultInfo

* Test for memory system fault
T← (FaultInfo) AND (fi.numfaults);
PD← T XOR (fi.numFaults);* See if we have all bits set;
FltPointers← Pointers, Branch[ChkStkErr, ALU=0]; * All ones => no fault

* Memory system fault, see what kind
PD← (FaultInfo) AND (fi.numFaults); * All zeroes => one fault
PD← (FaultInfo) AND (fi.emuFault), Branch[ManyFaults, ALU#0];
PD← (FltErrors) AND (pipe4.notMapTrouble), Branch[NotEmuFault, ALU=0];
Branch[NotMapFault, ALU#0];

* Emulator map fault. Clear it and pass fault to emulator.
B← FaultInfo’, Branch[EmuFault];

* Not memory system fault, check for stack overflow or underflow
ChkStkErr:
PD← (FltPointers) AND (300C);* 200 = stack overflow, 100 = stack underflow
Branch[UnknownFault, ALU=0];

* Emulator map fault or stack error.
* Save emulator’s state and restart emulator at Fault0.
EmuFault:
FaultVal← DBuf;
RdTPC← EMU;
FltEmuPC← NOT (Link);* TPC data is complemented
Call[GetEmuFaultPC];
LdTPC← T;
Block, Branch[FaultTask];

* Fault conditions that we can’t handle:
ManyFaults:
Branch[.], Breakpoint;* More than 1 fault
NotEmuFault:
Branch[.], Breakpoint;* Not Emulator fault
NotMapFault:
Branch[.], Breakpoint;* Not map fault
UnknownFault:
Branch[.], Breakpoint;* Don’t know what fault occurred

*-----------------------------------------------------------
* IFU faults for the Mesa instruction set
*-----------------------------------------------------------
Set[MesaTrapBase, Sub[300, LShift[MesaInsSet, 6]]];
Set[XTask, IP[EMU]];
DontKnowRBase;
TopLevel;

MesaFGparity:
At[MesaTrapBase, 4], Branch[.], Breakpoint;
:IfMEP;
At[MesaTrapBase, 5], Branch[.], Breakpoint;
At[MesaTrapBase, 6], Branch[.], Breakpoint;
:EndIf;

MesaRamPE:
At[MesaTrapBase, 74], Branch[.], Breakpoint;
:IfMEP;
At[MesaTrapBase, 75], Branch[.], Breakpoint;
At[MesaTrapBase, 76], Branch[.], Breakpoint;
:EndIf;

:If[AltoMode];
********** Alto version **********
MesaMapFault:
At[MesaTrapBase, 0], Branch[.], Breakpoint;
:IfMEP;
At[MesaTrapBase, 1], Branch[.], Breakpoint;
At[MesaTrapBase, 2], Branch[.], Breakpoint;
:EndIf;
:Else;
******** PrincOps version ********
MesaMapFault:
At[MesaTrapBase, 0], MemBase← Code, Branch[MapFault1];
:IfMEP;
At[MesaTrapBase, 1], Stack← MD, MemBase← Code, Branch[MapFault1];
At[MesaTrapBase, 2], StkP+1, MemBase← Code, Branch[MapFault1];
:EndIf;

* For the IFU to have given a map fault exception, some byte in [PCX..PCX+IL)
* must lie on a vacant page. So simply reference those bytes, in the
* expectation that a fault will occur and the fault task will
* wrench control away from here.
MapFault1:
T← NOT (PCX’);
T← T RSH 1;* PCX/2 = word address in code segment
T← (Fetch← T)+1;
Fetch← T;
T← MD;* Wait for fault to occur
BogusMapFault:
Branch[.], Breakpoint;
:EndIf;
**********************************

*-----------------------------------------------------------
* Emulator Task
* The emulator is restarted here by the fault task after an emulator
* map fault or stack error.
*-----------------------------------------------------------

Subroutine;
GetEmuFaultPC:
T← EMU, CoReturn;
TopLevel;

RBase← RBase[FltPipe0];
FltTemp← A0, Call[RestoreALUFM]; * In case faulted from BITBLT
TIOA← FltTemp;* In case faulted from I/O operation

:If[AltoMode];
********** Alto version **********
* Which emulator was running at the time?
T← NOT (IFUMLH’);
PD← T AND (14000C);* Test InsSet (Alto=0, Mesa=2)
PD← (FltPointers) AND (300C), Branch[FaultFromMesa, ALU#0];

* Fault from Alto emulator.
Branch[AEmuMapFault, ALU=0];
AEmuStackErr:
Branch[.], Breakpoint;
AEmuMapFault:
Branch[.], Breakpoint;

:Else;
******** PrincOps version ********
PD← (FltPointers) AND (300C);
:EndIf;
**********************************

* Fault from Mesa emulator.
* If we got a stack error or StkP is now out of bounds, must give a StackError
* in preference to a PageFault, because otherwise page fault processing would trap
* (in SaveState) at an inconvenient moment. ALU = stack overflow/underflow bits here.
FaultFromMesa:
T← FltErrors, RBase← RBase[RTemp1], Branch[.+2, ALU=0];
Branch[StackError];* Fault caused by stack error
SCall[CheckStkP];
Branch[StackError];* StkP now out of bounds

*-----------------------------------------------------------
* Handle emulator map fault.
*-----------------------------------------------------------
* Must distinguish between page faults and WP faults. It is a WP fault if the
* map entry is WP and not dirty (i.e., not vacant) and the reference was a Store.
* T = FltErrors here.

T← T AND (Or[pipe4.wProtect!, pipe4.dirty!]C);
RTemp0← PRef;
RTemp0← (RTemp0) AND (pipe5.store’);
T← (RTemp0) OR T;* Bits are disjoint
PD← T#(Pipe4.wProtect);* ALU=0 iff WP=1, dirty=0, store’=0
RTemp0← IfE[AltoMode, 0, qPageFault, sPageFault], Branch[.+2, ALU#0];
RTemp0← IfE[AltoMode, 0, qWriteProtectFault, sWriteProtect];

T← VAHi;
:If[AltoMode];
********** Alto version **********
OTPReg← VALo;
OTPReg← RCY[T, OTPReg, 10];* OTPReg← virtual page of fault
:Else;
******** PrincOps version ********
FaultParam0← VALo;* FaultParam0, 1 ← VA of fault
* The @#&%*!$ hardware returns ones in VAHi[0:3] !!
FaultParam1← T AND (7777C);
:EndIf;
**********************************

* Now have trap or fault code in RTemp0.
* Must reset IFU to clear BrkByte in case a break was pending and a fault
* occurred before it could be executed.
T← 50C;
T← T-1, IFUReset, Branch[., ALU#0];
:If[AltoMode];
********** Alto version **********
T← RTemp0, Branch[SavePCAndTrap]; * Trap via SD[T]
:Else;
******** PrincOps version ********
T← RTemp0, Branch[MesaFault];* Fault onto PDA.queue[T]
:EndIf;
**********************************

*-----------------------------------------------------------
StackError:
* Generate stack error trap.
* Enter: RBase[RTemp0]
*-----------------------------------------------------------
* Attempt to restore StkP to its initial state. But if initial state was also
* out of bounds, reset StkP to maximum legal value, because otherwise the
* subsequent trap XFER or the DST executed by the trap routine would suffer
* a recursive stack error trap.
* Note: in principle I should RestoreStkP only if XferFlags.xfInvalid=0; however,
* I believe it’s impossible to get a stack error while the context is invalid.

RestoreStkP;
T← Add[sizeStack]C, SCall[CheckStkP];
StkP← T;* Out of bounds, reset it
T← sStackError, Branch[SavePCTrapNRStkP];

*-----------------------------------------------------------
CheckStkP:
* Returns +1 if StkP is out of bounds, +2 normally.
* Clobbers RTemp0
*-----------------------------------------------------------
Subroutine;

RTemp0← TIOA&StkP;
PD← (RTemp0)-(Add[sizeStack, 1]C);
Return[ALU<0];

TopLevel;

*-----------------------------------------------------------
* Unimplemented opcodes
*-----------------------------------------------------------

:If[AltoMode];
********** Alto version **********
*-----------------------------------------------------------
* StoreMDS[@LocalBase[L].pc]↑ ← PC;
* XFER[dst: FetchMDS[@SD[sUnimplemented]]↑, src: L];
*-----------------------------------------------------------

OpcodeTrap:
T← sUnimplemented, Branch[SavePCAndTrap];
:IfMEP;
T← sUnimplemented, Stack← MD, Branch[SavePCAndTrap];
T← sUnimplemented, StkP+1, Branch[SavePCAndTrap];
:EndIf;

:Else;
******** PrincOps version ********
*-----------------------------------------------------------
* Cedar extension to unimplemented opcode trapping.
* Generalized for use in opcodes that are implemented partially by
* microcode and partially by software.
* Enter: RBase = RMforIFU
*
TrapParam = trap parameter, if there is one
* Branch to OpcodeTrap for main opcodes or to MiscOpcodeTrap for MISC opcodes.
* opTrap: POINTER = FetchMDS[@SD[p137B]];
* IF opTrap=NIL THEN TrapZero[sUnimplemented]
* ELSE BEGIN
* handler: ControlLink = FetchMDS[opTrap+opcode];
* PC ← savedPC; SP ← savedSP;
* IF ValidContext[] THEN StoreMDS[@LocalBase[LF].pc]↑ ← PC;
* XFER[dst: handler, src: LF, push: FALSE, trap: TRUE];
* StoreMDS[@LF.trapParam, trapParameter];
* END;
*-----------------------------------------------------------

* IFU dispatches here for undefined opcodes, and microcode also branches
* here to cause a trap when the opcode can’t be handled by the microcode.
OpcodeTrap:
RTemp0← T← A0, MemBase← Code, Branch[OpTrap1];
:IfMEP;
Stack← MD, Branch[.-1];
StkP+1, Branch[.-2];
:EndIf;

* Branch here for MISC opcode traps.
MiscOpcodeTrap:
RTemp0← T← (ID)-1, MemBase← Code; * Know ID = IL = 2 here.

* T = RTemp0 = 0 for main opcode, 1 for MISC opcode.
OpTrap1:
RTemp1← T-(PCX’)-1;* PC of byte to dispatch on
T← RSH[RTemp1, 1];* T← word address
Fetch← T, FlipMemBase;* Fetch the word; MemBase← MDS
T← MD, RTemp1← And[Add[SDLoc, 137], 177400]C,
Branch[.+2, R even];* Which byte?
T← LSH[T, 10];* Odd, left-justify
T← RCY[RTemp0, T, 10];* T← opcode or 400B+alpha

* Now T = opTrapTable offset. Trap via control link in opTrapTable.
RTemp1← (RTemp1) OR (And[Add[SDLoc, 137], 377]C);
Fetch← RTemp1;* Fetch SD[137]
PD← MD, RestoreStkP;
T← T+MD, Branch[NoOpTrapTbl, ALU=0];
Fetch← T, XferFlags← xf.trap;* Fetch control link of trap handler
RTemp1← NOT (PCX’), Call[SavePCInFrame];
DLink← MD, MemBase← MDS, Branch[Xfer];

* If SD[137]=0 then no opcode trap table is set up.
* Trap via SD[sUnimplemented] in the traditional manner.
NoOpTrapTbl:
T← sUnimplemented, Branch[SavePCAndTrap];
:EndIf;
**********************************