*----------------------------------------------------------- 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