*-----------------------------------------------------------
Title[Interp.mc...November 23, 1982  3:03 PM...Taft];
* Instruction interpreter (PrincOps, chapter 4) and related code,
* including all pre-PrincOps initialization.
*-----------------------------------------------------------
%
	CONTENTS, by order of occurence

Instruction execution
	ESC		Escape
	ESCL		Escape Long
	OpcodeTrap	Main opcode trap
	ESCOpcodeTrap	ESC/ESCL opcode trap

Initial state
	Boot		Self-initiated boot
	BootOrStart	Externally-initiated boot
	MesaInitialize	Establish initial conditions for Mesa execution
	TasksOff	Reset all I/O devices
	RestoreStdRegs	Restore standard Mesa emulator registers

Miscellaneous
	SetDMuxAddress	Load DMux address and read muffler
	MesaIFUNotReady	IFU not ready trap
	RequestAUT	Request action by Asynchronous Utility Task
%

*-----------------------------------------------------------
IFUR[ESC, 2, MemBase[MDS]];			* Escape
IFUR[ESCL, 3, MemBase[MDS]];			* Escape Long
*-----------------------------------------------------------

	T← RTemp6← (ID)-(Add[ESCMax, 1]C);
	T← StackNoUfl, BigBDispatch← T,	* Dispatch to ESCTable + (opcode-ESCMax-1) MOD 400B
		Branch[ESCUnimplTrap, ALU>=0]; * Branch if out of bounds
	RTemp6← (RTemp6)+(Add[ESCMax, 1]C), * Recover original opcode (in case of trap)
		BranchExternal[ESCTableLoc];

%
ESC and ESCL share a single opcode space, dispatched on the alpha byte.
The only difference is that ESCL opcodes have a beta byte whereas ESC opcodes do not.

The entry point for each ESC opcode is declared by including the statements
	NewOp; ESCEntry[name];
before the first microinstruction of the code implementing the named opcode.
This causes a microinstruction to be assembled in the appropriate location in the
ESC dispatch table; this instruction branches (off-page) to the code itself.
If the opcode completes in one cycle or the first instruction does not use FF,
the instruction itself may be assembled in the ESC dispatch table by substituting
a comma for the trailing semicolon in the ESCEntry statement.

After dispatch:
	T = Stack[StkP]
	RBase = MesaRegsMain
	MemBase = MDS
	RTemp6 = opcode
Exit is via the usual NextOpcode.

All unimplemented ESC opcodes cause a trap to occur; the trap is
EscOpcodeTrap[opcode] = TrapOne[@ETT[opcode], opcode].  The microcode implementing
an ESC opcode may conditionally trap (e.g., for illegal arguments) by optionally
putting a trap parameter in TrapParam and then branching to ESCOpcodeTrap.

Note: the ESCOpcodeUnimpl[xxx] statments may appear either below or in
another source file.  They appear below when the unimplemented opcodes in
a group are unrelated, but in a separate source file when they are related to
other opcodes which are implemented in that source file.
%

* All undefined ESC opcodes come here to execute TrapOne[@ETT[opcode], opcode];
* Note: can get here during dispatch if ESC opcode is out of bounds; in this case,
* the pending dispatch must be squashed.
ESCUnimplTrap:
	T← RTemp6;
	TrapParam← T, Branch[ESCOpcodeTrap], At[Sub[ESCTableLoc, 1]];

*-----------------------------------------------------------
OpcodeTrap:			* Main opcode trap
* This is the IFU dispatch for all unimplemented main opcodes.
* TrapOne[@SD[sOpcodeTrap], opcode];
*-----------------------------------------------------------
KnowRBase[MesaRegsMain];

	MemBase← CB;
	TrapParam← NOT (PCX');
	T← (TrapParam) RSH 1;
	T← A0, Fetch← T;
	TrapParam← DPF[T, 10, 10, MD], T← MD, Branch[.+2, R odd];
	TrapParam← T AND (377C);
	T← TrapWithParam[sOpcodeTrap], Branch[SavePCAndTrap];


*-----------------------------------------------------------
ESCOpcodeTrap:			* ESC opcode trap
* ESC opcodes come here to trap either because they are unimplemented or
* because they are trapping for some other reason (e.g., exceptional conditions
* detected by the floating point opcodes).  In the former case, TrapParam = opcode
* already.  In the latter case, TrapParam may optionally contain a trap parameter.
* In any event, RTemp6 = opcode (left over from ESC dispatch).
* TrapOne[@ETT[opcode], trapParam];
*-----------------------------------------------------------

	RBase← RBase[MesaRegsMain];

* Make ETT index look like SD index with trapParamFlag set,
* and pretend this is an SD trap.
	T← (RTemp6)+(Add[Sub[ETT!, SD!], TrapParamFlag!]C),
		Branch[SavePCAndTrap];

*-----------------------------------------------------------
* Pre-PrincOps initialization
* Display task branches here upon a 1-push boot (case 2).
*-----------------------------------------------------------
Set[XTask, IP[DHT]];
DontKnowRBase;

Boot:
	Call[TasksOff];
	Call[EmuInitPC];
	LdTPC← T, TaskingOn;
	Block;
* should never awaken again, since we did an I/O Reset.
	Breakpoint, Branch[.];

Subroutine;
EmuInitPC:
	T← EMU, CoReturn;		* Return EMU start PC
TopLevel;
	Branch[InitMap];

*-----------------------------------------------------------
* .eb-format microcode image is started here upon initial loading or 2-push boot.
*-----------------------------------------------------------
Set[XTask, IP[EMU]];

BootOrStart: At[BootOrStartLoc],
	Branch[InitMap];


*-----------------------------------------------------------
* InitMap returns here when done.
*-----------------------------------------------------------
StartEmulator:
	TaskingOff;
	RBase← RBase[MesaRegsAlt];
	T← A0, NoReschedule;
	InsSetOrEvent← T;		* Disable event counters
	MaintPanel← A0, IFUReset;
	Call[RestoreStdRegs];		* Restore ShC, TIOA, ALUFM to standard values
KnowRBase[MesaRegsMain];

* Initialize all base registers to 0,,0
	T← LShift[37, 10]C;		* MemBase addressed from A[3:7]
InitBRLoop:
	RTemp0← A0, MemBase← T;
	BRHi← RTemp0;
	T← T-(400C);
	BRLo← RTemp0, Branch[InitBRLoop, ALU>=0];

*-----------------------------------------------------------
* Task initialization routines all work according to the following pattern:
* XXXInitPC:
*	T← XXX, CoReturn;		* T← task number
*	first instruction of task initialization;
* Thus, calling XXXInitPC loads Link with the PC of the first instruction
* of the init routine and T with the task number.  These are precisely
* the arguments needed to do a LdTPC!
* Note: leave FF free in calls to the XXXInitPC routines.
* ETemp4 = 0 if booting, -1 if restarting emulator.
*-----------------------------------------------------------

InitTasks:
* First, init all non-emulator task PCs to -1.  ETemp1 = -1 here.
	RTemp0← 17C;
	Link← RTemp1;
	RTemp0← (RTemp0)-1, LdTPC← RTemp0;
	Branch[.-2, ALU#0];

* Now init specific task PCs
	Call[JNKInitPC];
	LdTPC← T, Wakeup[JNK];

	Call[EOTInitPC];
	LdTPC← T, Wakeup[EOT];

	Call[EITInitPC];
	LdTPC← T, Wakeup[EIT];

	Call[DSKInitPC];
	LdTPC← T, Wakeup[DSK];

	Call[FLTInitPC];
	LdTPC← T, Wakeup[FLT];

	Call[AUTInitPC];
	LdTPC← T, Wakeup[AUT];

* Display task initialization is slightly complicated, because the tasks
* must be set up according to whether the Alto terminal is hooked to the
* DispY board or the DispM board.
* DisplayInitConfig figures out what the configuration is, and sets a flag.
* Next, DHTInitPC and DWTInitPC return the starting PCs and task numbers
* for special (non-Alto terminal) microcode that may be present, or for
* dummy microcode in DisplayMain otherwise.
* Finally, THTInitPC and TWTInitPC return the starting PCs and task numbers
* for the Alto terminal microcode; the task numbers will be either AHT/AWT
* or DHT/DWT, depending on whether or not the DispM board is installed.
* If no DispM board is installed, this initialization will override the
* earlier DHT/DWT task initialization.
* Note: THTInitPC handles its own wakeup, since there's no way to
* issue Wakeup to a task given as a variable.

	IFUReset;			* Here to break consecutive .+1 constraints
	Call[DisplayInitConfig];
KnowRBase[TWTRegion];

	Call[DHTInitPC];		* For special display microcode
	LdTPC← T, Wakeup[DHT];
	Call[DWTInitPC];
	LdTPC← T;

	Call[THTInitPC];		* For Alto terminal microcode
	LdTPC← T;
	Call[TWTInitPC];
	LdTPC← T, TaskingOn;
	Branch[GermLoad];		* Go boot the Germ

*-----------------------------------------------------------
MesaInitialize:			* Entry to Mesa Emulator
*-----------------------------------------------------------

	RBase← RBase[MesaRegsAlt];
	T← Add[100000, LShift[MesaInsSet, 10]]C;
	T← WP← A0, InsSetOrEvent← T;
	WDC← T+1, MemBX← 0S;		* MemBX← first register group
	TickCount← T+1;
	Break← A0;
	Sticky← A0, Call[RestoreStdRegs];
KnowRBase[MesaRegsMain];
	XTS← T← A0;
	MDSHi← A0;
	XferFlags← A0;
	GFShadow← StkP← T;

* Note: all BRs have already been set to 0,,0, which is the correct initial value
* for MDS, LF, GF, and IOBR.  Need only set PDA.
	T← PDAHi;
	SLink← A0, MemBase← PDA, Call[BRHiGetsT];
	T← sBoot, Branch[MTrap];	* Xfer[dst: Fetch[@SD[sBoot]]↑, src:0]


*-----------------------------------------------------------
TasksOff:				* Reset all I/O devices.
* This is now accomplished simply by issuing an IOReset.
* IOReset is controlled by the manifold register addressed by 2240-2257,
* which has the following things in it (DMAdr[0:11]):
*	Bit 8:	EclUp		(normally 1)
*	Bit 9:	EnableRfPd'	(normally 0)
*	Bit 10:	IOReset'	(normally 1)
*	Bit 11:	RunRfsh		(normally 1)
* Returns with TaskingOff, RBase=MesaRegsMain.  Clobbers T, RTemp0, RTemp1
*-----------------------------------------------------------
Subroutine;

	TaskingOff;
	T← A0, RBase← RBase[MesaRegsMain];
	RTemp0← Link;
TopLevel;
	TIOA← T;			* Don't address any IO register
	RTemp1← 2000C;
	T← (RTemp1) OR (251C), Call[SetDMuxAddress]; * IOReset' ← 0
	UseDMD;
	T← (RTemp1) OR (253C), Call[SetDMuxAddress]; * IOReset' ← 1
Subroutine;
	Link← RTemp0;
	UseDMD, Return;

*-----------------------------------------------------------
RestoreStdRegs:			* Restore standard Mesa emulator registers
* Exit:	RBase = MesaRegsMain
*	ShC, TIOA, ALUFM[15], ALUFM[17] restored to standard values
* Clobbers T
*-----------------------------------------------------------
Subroutine;
DontKnowRBase;

	RBase← RBase[MesaRegsMain];
	T← A0, ShC← StdShC;
	TIOA← T;
	T← AFM15;
	ALUFMRW← T, ALUF[15];
	T← AFM17;
	ALUFMRW← T, ALUF[17], Return;

TopLevel;


*-----------------------------------------------------------
SetDMuxAddress:				* Load DMux address and read muffler.
* Enter: T[4:15] = DMux address
* Exit:	T[0] = MulDivArg[0] = muffler data
* Clobbers T and MulDivArg (= RVRel 17).
*-----------------------------------------------------------
Subroutine;

	MulDivArg← 13C;
SetDLp:
	T← T+(MidasStrobe← T);		* Shift address bit from B[4]
	MulDivArg← NOT (MulDivArg), Branch[., R>=0]; * Delay 2 cycles
	MulDivArg← (MulDivArg)-1, Branch[SetDLp, ALU#0];
	T← MulDivArg← ALUFMem, Return;


*-----------------------------------------------------------
MesaIFUNotReady:			* IFU not ready trap
*-----------------------------------------------------------

	NextOpcode, At[MesaTrapBase, 34];

*-----------------------------------------------------------
RequestAUT:			* Request action by Asynchronous Utility Task.
* Entry: T = desired starting PC
* Exit:	T = ALU = 0 if failed to submit request because one was
*		already pending; # 0 if succeeded.
*	RBase clobbered.
* Note: the code beginning at the specified PC will execute as AUT,
* and should finish by branching to AUTStart.  The state of task-specific
* registers is undefined at start and finish.
* Note: RequestAUT will never fail when called by the Emulator, assuming
* all emulator calls are with TaskingOn.
*-----------------------------------------------------------
Subroutine;

	RBase← RBase[AUTPC];
	AUTPC← (AUTPC) OR (100000C), Branch[.+2, R>=0]; * Test and set flag

* AUT request already pending.  Return with ALU=0.
	T← A0, Return;

* AUT is free, and we have now locked out further requests.
* Set the desired starting PC, awaken the task, and return with ALU#0.
* Note that AUTPC = 100000 here.
	AUTPC← T← T OR (AUTPC), Wakeup[AUT], Return;


*-----------------------------------------------------------
* The AUT code itself.
* AUTPC = 0 => no request is pending.
* AUTPC = 100000 + starting PC => request is pending.
*-----------------------------------------------------------
Subroutine;
DontKnowRBase;
Set[XTask, IP[AUT]];

AUTInitPC:
	AUTPC← A0;			* Task initialization
AUTDispatch:
	T← AUT, CoReturn;

TopLevel;

* Microcode routines started by AUT finish by branching here, or by returning.
* AUTPC[0]=1 => new request is pending.
AUTStart:
	RBase← RBase[AUTPC];
	T← AUTPC← A0, Link← AUTPC, Branch[AUTDispatch, R<0];
	TIOA← T, Block, Branch[AUTStart]; * Nothing to do