*-----------------------------------------------------------
Title[Fault.mc...November 7, 1982  2:58 PM...Taft];
* Mesa fault handler
*-----------------------------------------------------------

%
If the fault was an emulator map fault or stack error, passes control to
the emulator task, which will generate a stack error trap or page fault
process switch as appropriate.

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

(In fault task, not having reset FaultInfo)
	ManyFaults	More than one fault
	NotEmuFault	Fault in non-emulator task
	NotMapFault	Memory system fault other than map fault
	UnknownFault	Fault of unknown origin

(In emulator task, the fault task never having been awakened)
	MesaMapFault	Map fault caused by IFU reference
	MesaFGParity	FG parity error
	MesaRamPE	IFU decoding Ram parity error
	BogusMapFault	IFU 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:
	FaultPointers	← Pointers
	FaultErrors	← NOT (Errors')
	FaultVal	← DBuf
	FaultInfo	← NOT (FaultInfo')
	FaultEmuPC	Emulator task PC at time of fault
%

*-----------------------------------------------------------
* Fault Task
*-----------------------------------------------------------

Set[XTask, IP[FLT]];

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

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

FaultTask:
	FaultErrors← 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
	FaultPointers← 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← (FaultErrors) 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← (FaultPointers) 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 in fault routine.
EmuFault:
	FaultVal← DBuf;
	RdTPC← EMU;
	FaultEmuPC← 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[XTask, IP[EMU]];
DontKnowRBase;
TopLevel;

MesaFGparity:
	Branch[.], Breakpoint, At[MesaTrapBase, 4];

MesaRamPE:
	Branch[.], Breakpoint, At[MesaTrapBase, 74];

* 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.
MesaMapFault:
	T← NOT (PCX'), At[MesaTrapBase, 0];
	T← T RSH 1;			* PCX/2 = word address in code segment
	PD← T-T-1, MemBase← CB;
	T← (Fetch← T)+1, Branch[., ALU<0]; * Execute this twice
	T← MD;				* Wait for fault to occur
BogusMapFault:
	Branch[.], Breakpoint;

*-----------------------------------------------------------
* 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;

	Call[RestoreStdRegs];
	RBase← RBase[FaultTemp];

* 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 SaveStack) at an inconvenient moment.
	PD← (FaultPointers) AND (300C);	* 200 = stack overflow, 100 = stack underflow
	T← FaultErrors, RBase← RBase[RTemp1], Branch[StackError, ALU#0];
	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 = FaultErrors 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← qPageFault, Branch[.+2, ALU#0];
	RTemp0← qWriteProtectFault;

	T← VAHi;
	FaultParam0← VALo;		* FaultParam0, 1 ← VA of fault
* The @#&%*!$ hardware returns ones in VAHi[0:3] !!
	FaultParam1← T AND (7777C);

* Now have trap or fault code in RTemp0.
* Must reset IFU to clear the IFU BrkIns← condition in case a break was pending
* and a fault occurred before it could be executed.  Note, however, that we do not
* zero the Break register, since we want Break to be stored as part of
* the process state.
	T← 50C;
	T← T-1, IFUReset, Branch[., ALU#0];
	T← RTemp0, Branch[MesaFault];	* Fault onto PDA.queue[T]

*-----------------------------------------------------------
StackError:			* Generate stack error trap.
* Enter: RBase = MesaRegsMain
*-----------------------------------------------------------
* 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 DSTK 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[StackDepth]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[StackDepth, 1]C);
	Return[ALU<0];

TopLevel;