/* pdp11_cpu.c: PDP-11 CPU simulator

   Copyright (c) 1993, 1994, Robert M Supnik, Digital Equipment Corporation
   Commercial use prohibited

   17-Jul-94	RMS	Corrected updating of MMR1 if MMR0 locked

   The register state for the PDP-11 is:

   REGFILE[0:5][0]	general register set
   REGFILE[0:5][1]	alternate general register set
   STACKFILE[4]		stack pointers for kernel, supervisor, unused, user
   PC			program counter
   PSW			processor status word
    <15:14> = CM	 current processor mode
    <13:12> = PM	 previous processor mode
    <11> = RS		 register set select
    <7:5> = IPL		 interrupt priority level
    <4> = TBIT		 trace trap enable
    <3:0> = NZVC	 condition codes
   FR[0:5]		floating point accumulators
   FPS			floating point status register
   FEC			floating exception code
   FEA			floating exception address
   MMR0,1,2,3		memory management control registers
   APRFILE[0:63]	memory management relocation registers for
			 kernel, supervisor, unused, user
    <31:16> = PAR	 processor address registers
    <15:0> = PDR	 processor data registers
   PIRQ			processor interrupt request register
   CPUERR		CPU error register
   MEMERR		memory system error register
   CCR			cache control register
   MAINT		maintenance register
   HITMISS		cache status register
   SR			switch register
   DR			display register
*/

/* The PDP-11 has many instruction formats:

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	double operand
   |  opcode   |   source spec   |     dest spec   |	010000:067777
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	110000:167777

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	register + operand
   |        opcode      | src reg|     dest spec   |	004000:004777
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	070000:077777

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	single operand
   |           opcode            |     dest spec   |	000100:000177
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	000300:000377
							005000:007777
							105000:107777

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	single register
   |                opcode                |dest reg|	000200:000207
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	000230:000237

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	no operand
   |                     opcode                    |	000000:000007
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	branch
   |       opcode          |  branch displacement  |	000400:003477
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	100000:103477

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	EMT/TRAP
   |       opcode          |       trap code       |	104000:104777
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	cond code operator
   |                opcode             | immediate |	000240:000277
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   An operand specifier consists of an addressing mode and a register.
   The addressing modes are:

   0	register direct		R		op = R
   1	register deferred	(R)		op = M[R]
   2	autoincrement		(R)+		op = M[R]; R = R + length
   3	autoincrement deferred	@(R)+		op = M[M[R]]; R = R + 2
   4	autodecrement		-(R)		R = R - length; op = M[R]
   5	autodecrement deferred	@-(R)		R = R - 2; op = M[M[R]]
   6	displacement		d(R)		op = M[R + disp]
   7	displacement deferred	@d(R)		op = M[M[R + disp]]

   There are eight general registers, R0-R7.  R6 is the stack pointer,
   R7 the PC.  The combination of addressing modes with R7 yields:

   27	immediate		#n		op = M[PC]; PC = PC + 2
   37	absolute		@#n		op = M[M[PC]]; PC = PC + 2
   67	relative		d(PC)		op = M[PC + disp]
   77	relative deferred	@d(PC)		op = M[M[PC + disp]]
*/

/* This routine is the instruction decode routine for the PDP-11.  It
   is called from the simulator control program to execute instructions
   in simulated memory, starting at the simulated PC.  It runs until an
   enabled exception is encountered.

   General notes:

   1. Virtual address format.  PDP-11 memory management uses the 16b
      virtual address, the type of reference (instruction or data), and
      the current mode, to construct the 22b physical address.  To
      package this conveniently, the simulator uses a 19b pseudo virtual
      address, consisting of the 16b virtual address prefixed with the
      current mode and ispace/dspace indicator.  These are precalculated
      as isenable and dsenable for ispace and dspace, respectively, and
      must be recalculated whenever MMR0, MMR3, or PSW<cm> changes.

   2. Traps and interrupts.  Variable trap_req bit-encodes all possible
      traps.  In addition, an interrupt pending bit is encoded as the
      lowest priority trap.  Traps are processed by trap_vec and trap_clear,
      which provide the vector and subordinate traps to clear, respectively.

      Variable int_req bit encodes all possible interrupts.  It is masked
      under the interrupt masks, int_mask[ipl].  If any interrupt request
      is not masked, the interrupt bit is set in trap_req.  While most
      interrupts are handled centrally, a device can supply an interrupt
      acknowledge routine.

   3. PSW handling.  The PSW is kept as components, for easier access.
      Because the PSW can be explicitly written as address 17777776,
      all instructions must update PSW before executing their last write.

   4. Adding I/O devices.  This requires modifications to three modules:

	pdp11_defs.h		add interrupt request definitions
	pdp11_cpu.c		add I/O page linkages
	pdp11_sys.c		add to sim_devices
*/

/* Definitions and global data */

#include "pdp11_defs.h"
#include <setjmp.h>
#define calc_is(md) ((md) << VA_V_MODE)
#define calc_ds(md) (calc_is((md)) | ((MMR3 & dsmask[(md)])? VA_DS: 0))
#define calc_MMR1(val) (MMR1 = MMR1? ((val) << 8) | MMR1: (val))
#define calc_ints(lv,rq,tr) (((rq) & int_mask[(lv)])? \
	((tr) | TRAP_INT) : ((tr) & ~TRAP_INT))
#define GET_SIGN_W(v) ((v) >> 15)
#define GET_SIGN_B(v) ((v) >> 7)
#define GET_Z(v) ((v) == 0)
#define JMP_PC(x) old_PC = PC; PC = (x)
#define BRANCH_F(x) old_PC = PC; PC = (PC + (((x) + (x)) & 0377)) & 0177777
#define BRANCH_B(x) old_PC = PC; PC = (PC + (((x) + (x)) | 0177400)) & 0177777
#define ILL_ADR_FLAG 0200000
#define save_ibkpt (cpu_unit.u3)			/* will be SAVEd */
#define last_pa (cpu_unit.u4)				/* and RESTOREd */

unsigned short M[MEMSIZE >> 1] = { 0 };
int R[8] = { 0 }, REGFILE[6][2] = { 0 }, STACKFILE[4] = { 0 };
fpac_t FR[6] = { 0 };
int FPS = 0, FEC = 0, FEA = 0;
int APRFILE[64] = { 0 }, MMR0 = 0, MMR1 = 0, MMR2 = 0, MMR3 = 0;
int saved_PC = 0, old_PC = 0, trap_req = 0;
int PSW = 0, PIRQ = 0, SR = 0, DR = 0;
int CPUERR = 0, MEMERR = 0, CCR = 0, HITMISS = 0;
int MAINT = (0 << 9) + (0 << 8) + (4 << 4);		/* bit<9> = Q/U */
							/*  <8> = hwre FP */
							/*  <6:4> = sys type */
int stop_trap = 1, stop_vecabort = 1, stop_spabort = 1;
int int_req = 0, wait_state = 0, wait_enable = 0;
int isenable = 0, dsenable = 0, ibkpt_addr = ILL_ADR_FLAG;
int cm = 0, pm = 0, rs = 0, ipl = 0, tbit = 0;
int N = 0, Z = 0, V = 0, C = 0;
int dsmask[4] = { MMR3_KDS, MMR3_SDS, 0, MMR3_UDS };
int int_mask[8] = { INT_IPL0, INT_IPL1, INT_IPL2, INT_IPL3,
	INT_IPL4, INT_IPL5, INT_IPL6, INT_IPL7 };
jmp_buf save_env;
extern int sim_int_char;

/* Function declarations */

int cpu_ex (int *vptr, int addr, UNIT *uptr, int sw);
int cpu_dep (int val, int addr, UNIT *uptr, int sw);
int cpu_reset (DEVICE *dptr);
int cpu_svc (UNIT *uptr);
int GeteaB (int spec);
int GeteaW (int spec);
int relocR (int addr);
int relocW (int addr);
int ReadW (int addr);
int ReadB (int addr);
int ReadMW (int addr);
int ReadMB (int addr);
void WriteW (int data, int addr);
void WriteB (int data, int addr);
void PWriteW (int data, int addr);
void PWriteB (int data, int addr);
int iopageR (int *data, int addr, int access);
int iopageW (int data, int addr, int access);

int CPU_rd (int *data, int addr, int access);
int CPU_wr (int data, int addr, int access);
int APR_rd (int *data, int addr, int access);
int APR_wr (int data, int addr, int access);
int SR_MMR012_rd (int *data, int addr, int access);
int SR_MMR012_wr (int data, int addr, int access);
int MMR3_rd (int *data, int addr, int access);
int MMR3_wr (int data, int addr, int access);
extern int std_rd (int *data, int addr, int access);
extern int std_wr (int data, int addr, int access);
extern int lpt_rd (int *data, int addr, int access);
extern int lpt_wr (int data, int addr, int access);
extern int rk_rd (int *data, int addr, int access);
extern int rk_wr (int data, int addr, int access);
extern int rl_rd (int *data, int addr, int access);
extern int rl_wr (int data, int addr, int access);
extern int rx_rd (int *data, int addr, int access);
extern int rx_wr (int data, int addr, int access);
extern int rk_inta (void);

/* Auxiliary data structures */

struct iolink {						/* I/O page linkage */
	int	low;					/* low I/O addr */
	int	high;					/* high I/O addr */
	int	(*read)();				/* read routine */
	int	(*write)();  };				/* write routine */

struct iolink iotable[] = {
	{ 017777740, 017777777, &CPU_rd, &CPU_wr },
	{ 017777546, 017777567, &std_rd, &std_wr },
	{ 017777514, 017777517, &lpt_rd, &lpt_wr },
	{ 017777400, 017777417, &rk_rd, &rk_wr },
	{ 017774400, 017774411, &rl_rd, &rl_wr },
	{ 017777170, 017777173, &rx_rd, &rx_wr },
	{ 017777600, 017777677, &APR_rd, &APR_wr },
	{ 017772200, 017772377, &APR_rd, &APR_wr },
	{ 017777570, 017777577, &SR_MMR012_rd, &SR_MMR012_wr },
	{ 017772516, 017772517, &MMR3_rd, &MMR3_wr },
	{ 0, 0, NULL }  };

int int_vec[32] = {					/* int req to vector */
	0, 0, 0, VEC_PIRQ, VEC_CLK, 0, 0, VEC_PIRQ,
	VEC_RK, VEC_RL, VEC_RX, 0, 0, 0, 0, VEC_PIRQ,
	VEC_TTI, VEC_TTO, VEC_PTR, VEC_PTP, VEC_LPT, 0, 0, 0,
	0, 0, 0, 0, VEC_PIRQ, VEC_PIRQ, VEC_PIRQ, VEC_PIRQ  };

int (*int_ack[32])() = {				/* int ack routines */
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	&rk_inta, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL  };

int trap_vec[TRAP_V_MAX] = {				/* trap req to vector */
	VEC_RED, VEC_ODD, VEC_MME, VEC_NXM,
	VEC_PAR, VEC_PRV, VEC_ILL, VEC_BPT,
	VEC_IOT, VEC_EMT, VEC_TRAP, VEC_TRC,
	VEC_YEL, VEC_PWRFL, VEC_FPE };

int trap_clear[TRAP_V_MAX] = {				/* trap clears */
	TRAP_RED+TRAP_PAR+TRAP_YEL+TRAP_TRC,
	TRAP_ODD+TRAP_PAR+TRAP_YEL+TRAP_TRC,
	TRAP_MME+TRAP_PAR+TRAP_YEL+TRAP_TRC,
	TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC,
	TRAP_PAR+TRAP_TRC, TRAP_PRV+TRAP_TRC,
	TRAP_ILL+TRAP_TRC, TRAP_BPT+TRAP_TRC,
	TRAP_IOT+TRAP_TRC, TRAP_EMT+TRAP_TRC,
	TRAP_TRAP+TRAP_TRC, TRAP_TRC,
	TRAP_YEL, TRAP_PWRFL, TRAP_FPE };

/* CPU data structures

   cpu_dev	CPU device descriptor
   cpu_unit	CPU unit descriptor
   cpu_reg	CPU register list
*/

REG cpu_reg[] = {
	{ ORDATA (PC, saved_PC, 16) },
	{ ORDATA (R0, REGFILE[0][0], 16) },
	{ ORDATA (R1, REGFILE[1][0], 16) },
	{ ORDATA (R2, REGFILE[2][0], 16) },
	{ ORDATA (R3, REGFILE[3][0], 16) },
	{ ORDATA (R4, REGFILE[4][0], 16) },
	{ ORDATA (R5, REGFILE[5][0], 16) },
	{ ORDATA (R10, REGFILE[0][1], 16) },
	{ ORDATA (R11, REGFILE[1][1], 16) },
	{ ORDATA (R12, REGFILE[2][1], 16) },
	{ ORDATA (R13, REGFILE[3][1], 16) },
	{ ORDATA (R14, REGFILE[4][1], 16) },
	{ ORDATA (R15, REGFILE[5][1], 16) },
	{ ORDATA (KSP, STACKFILE[KERNEL], 16) },
	{ ORDATA (SSP, STACKFILE[SUPER], 16) },
	{ ORDATA (USP, STACKFILE[USER], 16) },
	{ ORDATA (PSW, PSW, 16) },
	{ GRDATA (CM, PSW, 8, 2, PSW_V_CM) },
	{ GRDATA (PM, PSW, 8, 2, PSW_V_PM) },
	{ GRDATA (IPL, PSW, 8, 3, PSW_V_IPL) },
	{ FLDATA (RS, PSW, PSW_V_RS) },
	{ FLDATA (N, PSW, PSW_V_N) },
	{ FLDATA (Z, PSW, PSW_V_Z) },
	{ FLDATA (V, PSW, PSW_V_V) },
	{ FLDATA (C, PSW, PSW_V_C) },
	{ ORDATA (SR, SR, 16) },
	{ ORDATA (DR, DR, 16) },
	{ ORDATA (MEMERR, MEMERR, 16) },
	{ ORDATA (CCR, CCR, 16) },
	{ ORDATA (MAINT, MAINT, 16) },
	{ ORDATA (HITMISS, HITMISS, 16) },
	{ ORDATA (CPUERR, CPUERR, 16) },
	{ ORDATA (INT, int_req, 32), REG_RO },
	{ ORDATA (TRAPS, trap_req, TRAP_V_MAX) },
	{ ORDATA (PIRQ, PIRQ, 16) },
	{ FLDATA (WAIT, wait_state, 0) },
	{ FLDATA (WAIT_ENABLE, wait_enable, 0) },
	{ ORDATA (STOP_TRAPS, stop_trap, TRAP_V_MAX) },
	{ FLDATA (STOP_VECA, stop_vecabort, 0) },
	{ FLDATA (STOP_SPA, stop_spabort, 0) },
	{ ORDATA (FAC0H, FR[0].h, 32) },
	{ ORDATA (FAC0L, FR[0].l, 32) },
	{ ORDATA (FAC1H, FR[1].h, 32) },
	{ ORDATA (FAC1L, FR[1].l, 32) },
	{ ORDATA (FAC2H, FR[2].h, 32) },
	{ ORDATA (FAC2L, FR[2].l, 32) },
	{ ORDATA (FAC3H, FR[3].h, 32) },
	{ ORDATA (FAC3L, FR[3].l, 32) },
	{ ORDATA (FAC4H, FR[4].h, 32) },
	{ ORDATA (FAC4L, FR[4].l, 32) },
	{ ORDATA (FAC5H, FR[5].h, 32) },
	{ ORDATA (FAC5L, FR[5].l, 32) },
	{ ORDATA (FPS, FPS, 16) },
	{ ORDATA (FEA, FEA, 16) },
	{ ORDATA (FEC, FEC, 4) },
	{ ORDATA (MMR0, MMR0, 16) },
	{ ORDATA (MMR1, MMR1, 16) },
	{ ORDATA (MMR2, MMR2, 16) },
	{ ORDATA (MMR3, MMR3, 16) },
	{ GRDATA (KIPAR0, APRFILE[000], 8, 16, 16) },
	{ GRDATA (KIPDR0, APRFILE[000], 8, 16, 0) },
	{ GRDATA (KIPAR1, APRFILE[001], 8, 16, 16) },
	{ GRDATA (KIPDR1, APRFILE[001], 8, 16, 0) },
	{ GRDATA (KIPAR2, APRFILE[002], 8, 16, 16) },
	{ GRDATA (KIPDR2, APRFILE[002], 8, 16, 0) },
	{ GRDATA (KIPAR3, APRFILE[003], 8, 16, 16) },
	{ GRDATA (KIPDR3, APRFILE[003], 8, 16, 0) },
	{ GRDATA (KIPAR4, APRFILE[004], 8, 16, 16) },
	{ GRDATA (KIPDR4, APRFILE[004], 8, 16, 0) },
	{ GRDATA (KIPAR5, APRFILE[005], 8, 16, 16) },
	{ GRDATA (KIPDR5, APRFILE[005], 8, 16, 0) },
	{ GRDATA (KIPAR6, APRFILE[006], 8, 16, 16) },
	{ GRDATA (KIPDR6, APRFILE[006], 8, 16, 0) },
	{ GRDATA (KIPAR7, APRFILE[007], 8, 16, 16) },
	{ GRDATA (KIPDR7, APRFILE[007], 8, 16, 0) },
	{ GRDATA (KDPAR0, APRFILE[010], 8, 16, 16) },
	{ GRDATA (KDPDR0, APRFILE[010], 8, 16, 0) },
	{ GRDATA (KDPAR1, APRFILE[011], 8, 16, 16) },
	{ GRDATA (KDPDR1, APRFILE[011], 8, 16, 0) },
	{ GRDATA (KDPAR2, APRFILE[012], 8, 16, 16) },
	{ GRDATA (KDPDR2, APRFILE[012], 8, 16, 0) },
	{ GRDATA (KDPAR3, APRFILE[013], 8, 16, 16) },
	{ GRDATA (KDPDR3, APRFILE[013], 8, 16, 0) },
	{ GRDATA (KDPAR4, APRFILE[014], 8, 16, 16) },
	{ GRDATA (KDPDR4, APRFILE[014], 8, 16, 0) },
	{ GRDATA (KDPAR5, APRFILE[015], 8, 16, 16) },
	{ GRDATA (KDPDR5, APRFILE[015], 8, 16, 0) },
	{ GRDATA (KDPAR6, APRFILE[016], 8, 16, 16) },
	{ GRDATA (KDPDR6, APRFILE[016], 8, 16, 0) },
	{ GRDATA (KDPAR7, APRFILE[017], 8, 16, 16) },
	{ GRDATA (KDPDR7, APRFILE[017], 8, 16, 0) },
	{ GRDATA (SIPAR0, APRFILE[020], 8, 16, 16) },
	{ GRDATA (SIPDR0, APRFILE[020], 8, 16, 0) },
	{ GRDATA (SIPAR1, APRFILE[021], 8, 16, 16) },
	{ GRDATA (SIPDR1, APRFILE[021], 8, 16, 0) },
	{ GRDATA (SIPAR2, APRFILE[022], 8, 16, 16) },
	{ GRDATA (SIPDR2, APRFILE[022], 8, 16, 0) },
	{ GRDATA (SIPAR3, APRFILE[023], 8, 16, 16) },
	{ GRDATA (SIPDR3, APRFILE[023], 8, 16, 0) },
	{ GRDATA (SIPAR4, APRFILE[024], 8, 16, 16) },
	{ GRDATA (SIPDR4, APRFILE[024], 8, 16, 0) },
	{ GRDATA (SIPAR5, APRFILE[025], 8, 16, 16) },
	{ GRDATA (SIPDR5, APRFILE[025], 8, 16, 0) },
	{ GRDATA (SIPAR6, APRFILE[026], 8, 16, 16) },
	{ GRDATA (SIPDR6, APRFILE[026], 8, 16, 0) },
	{ GRDATA (SIPAR7, APRFILE[027], 8, 16, 16) },
	{ GRDATA (SIPDR7, APRFILE[027], 8, 16, 0) },
	{ GRDATA (SDPAR0, APRFILE[030], 8, 16, 16) },
	{ GRDATA (SDPDR0, APRFILE[030], 8, 16, 0) },
	{ GRDATA (SDPAR1, APRFILE[031], 8, 16, 16) },
	{ GRDATA (SDPDR1, APRFILE[031], 8, 16, 0) },
	{ GRDATA (SDPAR2, APRFILE[032], 8, 16, 16) },
	{ GRDATA (SDPDR2, APRFILE[032], 8, 16, 0) },
	{ GRDATA (SDPAR3, APRFILE[033], 8, 16, 16) },
	{ GRDATA (SDPDR3, APRFILE[033], 8, 16, 0) },
	{ GRDATA (SDPAR4, APRFILE[034], 8, 16, 16) },
	{ GRDATA (SDPDR4, APRFILE[034], 8, 16, 0) },
	{ GRDATA (SDPAR5, APRFILE[035], 8, 16, 16) },
	{ GRDATA (SDPDR5, APRFILE[035], 8, 16, 0) },
	{ GRDATA (SDPAR6, APRFILE[036], 8, 16, 16) },
	{ GRDATA (SDPDR6, APRFILE[036], 8, 16, 0) },
	{ GRDATA (SDPAR7, APRFILE[037], 8, 16, 16) },
	{ GRDATA (SDPDR7, APRFILE[037], 8, 16, 0) },
	{ GRDATA (UIPAR0, APRFILE[060], 8, 16, 16) },
	{ GRDATA (UIPDR0, APRFILE[060], 8, 16, 0) },
	{ GRDATA (UIPAR1, APRFILE[061], 8, 16, 16) },
	{ GRDATA (UIPDR1, APRFILE[061], 8, 16, 0) },
	{ GRDATA (UIPAR2, APRFILE[062], 8, 16, 16) },
	{ GRDATA (UIPDR2, APRFILE[062], 8, 16, 0) },
	{ GRDATA (UIPAR3, APRFILE[063], 8, 16, 16) },
	{ GRDATA (UIPDR3, APRFILE[063], 8, 16, 0) },
	{ GRDATA (UIPAR4, APRFILE[064], 8, 16, 16) },
	{ GRDATA (UIPDR4, APRFILE[064], 8, 16, 0) },
	{ GRDATA (UIPAR5, APRFILE[065], 8, 16, 16) },
	{ GRDATA (UIPDR5, APRFILE[065], 8, 16, 0) },
	{ GRDATA (UIPAR6, APRFILE[066], 8, 16, 16) },
	{ GRDATA (UIPDR6, APRFILE[066], 8, 16, 0) },
	{ GRDATA (UIPAR7, APRFILE[067], 8, 16, 16) },
	{ GRDATA (UIPDR7, APRFILE[067], 8, 16, 0) },
	{ GRDATA (UDPAR0, APRFILE[070], 8, 16, 16) },
	{ GRDATA (UDPDR0, APRFILE[070], 8, 16, 0) },
	{ GRDATA (UDPAR1, APRFILE[071], 8, 16, 16) },
	{ GRDATA (UDPDR1, APRFILE[071], 8, 16, 0) },
	{ GRDATA (UDPAR2, APRFILE[072], 8, 16, 16) },
	{ GRDATA (UDPDR2, APRFILE[072], 8, 16, 0) },
	{ GRDATA (UDPAR3, APRFILE[073], 8, 16, 16) },
	{ GRDATA (UDPDR3, APRFILE[073], 8, 16, 0) },
	{ GRDATA (UDPAR4, APRFILE[074], 8, 16, 16) },
	{ GRDATA (UDPDR4, APRFILE[074], 8, 16, 0) },
	{ GRDATA (UDPAR5, APRFILE[075], 8, 16, 16) },
	{ GRDATA (UDPDR5, APRFILE[075], 8, 16, 0) },
	{ GRDATA (UDPAR6, APRFILE[076], 8, 16, 16) },
	{ GRDATA (UDPDR6, APRFILE[076], 8, 16, 0) },
	{ GRDATA (UDPAR7, APRFILE[077], 8, 16, 16) },
	{ GRDATA (UDPDR7, APRFILE[077], 8, 16, 0) },
	{ ORDATA (OLDPC, old_PC, 16), REG_RO },
	{ ORDATA (BREAK, ibkpt_addr, 17) },
	{ ORDATA (WRU, sim_int_char, 8) },
	{ NULL}  };

UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX, MEMSIZE) };

DEVICE cpu_dev = {
	"CPU", &cpu_unit, cpu_reg, NULL,
	1, 8, 22, 2, 8, 16,
	&cpu_ex, &cpu_dep, &cpu_reset,
	NULL, NULL, NULL };

int sim_instr (void)
{
extern int sim_interval;
extern UNIT *sim_clock_queue;
register int IR, srcspec, srcreg, dstspec, dstreg;
register int src, src2, dst;
register int i, t, sign, oldrs, trapnum;
int reason, trapea, abortval;
extern int sim_process_event (void);
extern int sim_activate (UNIT *uptr, int interval);
extern int reset_all (int start_device);
void fp11 (int IR);

/* Restore register state

	1. PSW components
	2. Active register file based on PSW<rs>
	3. Active stack pointer based on PSW<cm>
	4. Memory management control flags
	5. Interrupt system
*/

cm = (PSW >> PSW_V_CM) & 03;				/* call calc_is,ds */
pm = (PSW >> PSW_V_PM) & 03;
rs = (PSW >> PSW_V_RS) & 01;
ipl = (PSW >> PSW_V_IPL) & 07;				/* call calc_ints */
tbit = (PSW >> PSW_V_TBIT) & 01;
N = (PSW >> PSW_V_N) & 01;
Z = (PSW >> PSW_V_Z) & 01;
V = (PSW >> PSW_V_V) & 01;
C = (PSW >> PSW_V_C) & 01;

for (i = 0; i < 6; i++) R[i] = REGFILE[i][rs];
SP = STACKFILE[cm];
PC = saved_PC;

isenable = calc_is (cm);
dsenable = calc_ds (cm);

CPU_wr (PIRQ, 017777772, WRITE);			/* rewrite PIRQ */
trap_req = calc_ints (ipl, int_req, trap_req);
trapea = 0;
reason = 0;

/* Abort handling

   If an abort occurs in memory management or memory access, the lower
   level routine executes a longjmp to this area OUTSIDE the main
   simulation loop.  The longjmp specifies a trap mask which is OR'd
   into the trap_req register.  Simulation then resumes at the fetch
   phase, and the trap is sprung.

   Aborts which occur within a trap sequence (trapea != 0) require
   special handling.  If the abort occured on the stack pushes, and
   the mode (encoded in trapea) is kernel, an "emergency" kernel
   stack is created at 4, and a red zone stack trap taken.
*/

abortval = setjmp (save_env);				/* set abort hdlr */
if (abortval != 0) {
	trap_req = trap_req | abortval;			/* or in trap flag */
	if ((trapea > 0) && (stop_vecabort)) reason = STOP_VECABORT;
	if ((trapea < 0) && (stop_spabort)) reason = STOP_SPABORT;
	if (trapea == ~KERNEL) {			/* kernel stk abort? */
		setTRAP (TRAP_RED);
		setCPUERR (CPUE_RED);
		STACKFILE[KERNEL] = 4;
		if (cm == KERNEL) SP = 4;  }  }

/* Main instruction fetch/decode loop

   Check for traps or interrupts.  If trap, locate the vector and check
   for stop condition.  If interrupt, locate the vector.
*/ 

while (reason == 0)  {
if (sim_interval <= 0) {				/* check clock queue */
	reason = sim_process_event ();
	trap_req = calc_ints (ipl, int_req, trap_req);
	continue;  }
if (trap_req) {						/* check traps, ints */
	trapea = 0;					/* assume srch fails */
	if (t = trap_req & TRAP_ALL) {			/* if a trap */
		for (trapnum = 0; trapnum < TRAP_V_MAX; trapnum++) {
			if ((t >> trapnum) & 1) {
				trapea = trap_vec[trapnum];
				trap_req = trap_req & ~trap_clear[trapnum];
				if ((stop_trap >> trapnum) & 1)
					reason = trapnum + 1;
				break;  }  }  }
	else if (t = int_req & int_mask[ipl]) {		/* if an interrupt */
		for (i = 0; i < 32; i++) {
			if ((t >> i) & 1) {
				int_req = int_req & ~(1 << i);
				if (int_ack[i]) trapea = int_ack[i]();
				else trapea = int_vec[i];
				trapnum = TRAP_V_MAX;
				break;  }  }  }
	if (trapea == 0) {				/* nothing to do? */
		trap_req = calc_ints (ipl, int_req, 0);	/* recalculate */
		continue;  }				/* back to fetch */

/* Process a trap or interrupt

   1. Exit wait state
   2. Save the current SP and PSW
   3. Read the new PC, new PSW from trapea, kernel data space
   4. Get the mode and stack selected by the new PSW
   5. Push the old PC and PSW on the new stack
   6. Update SP, PSW, and PC
   7. If not stack overflow, check for stack overflow
*/

	wait_state = 0;					/* exit wait state */
	STACKFILE[cm] = SP;
	PSW = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) |
		(ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) |
		(N << PSW_V_N) | (Z << PSW_V_Z) |
		(V << PSW_V_V) | (C << PSW_V_C);
	oldrs = rs;
	src = ReadW (trapea | calc_ds (KERNEL));
	src2 = ReadW ((trapea + 2) | calc_ds (KERNEL));
	t = (src2 >> PSW_V_CM) & 03;
	trapea = ~t;					/* flag pushes */
	WriteW (PSW, ((STACKFILE[t] - 2) & 0177777) | calc_ds (t));
	WriteW (PC, ((STACKFILE[t] - 4) & 0177777) | calc_ds (t));
	trapea = 0;					/* clear trap flag */
	pm = cm;
	cm = t;						/* call calc_is,ds */
	rs = (src2 >> PSW_V_RS) & 01;
	ipl = (src2 >> PSW_V_IPL) & 07;			/* call calc_ints */
	tbit = (src2 >> PSW_V_TBIT) & 01;
	N = (src2 >> PSW_V_N) & 01;
	Z = (src2 >> PSW_V_Z) & 01;
	V = (src2 >> PSW_V_V) & 01;
	C = (src2 >> PSW_V_C) & 01;
	if (rs != oldrs) {				/* if rs chg, swap */
		for (i = 0; i < 6; i++) {
			REGFILE[i][oldrs] = R[i];
			R[i] = REGFILE[i][rs];  }  }
	SP = (STACKFILE[cm] - 4) & 0177777;		/* update SP, PC */
	JMP_PC (src);
	isenable = calc_is (cm);
	dsenable = calc_ds (cm);
	trap_req = calc_ints (ipl, int_req, trap_req);
	if ((SP < STKLIM) && (cm == KERNEL) &&
	    (trapnum != TRAP_V_RED) && (trapnum != TRAP_V_YEL)) {
		setTRAP (TRAP_YEL);
		setCPUERR (CPUE_YEL);  }
	continue;  }					/* end if traps */

/* Fetch and decode next instruction */

if (tbit) setTRAP (TRAP_TRC);
if (wait_state) {					/* wait state? */
	if (sim_clock_queue != NULL) sim_interval = 0;	/* force check */
	else reason = STOP_WAIT;
	continue;  }

if (PC == ibkpt_addr) {					/* breakpoint? */
	save_ibkpt = ibkpt_addr;			/* save bkpt */
	ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG;		/* disable */
	sim_activate (&cpu_unit, 1);			/* sched re-enable */
	reason = STOP_IBKPT;				/* stop simulation */
	continue;  }

if (update_MM) {					/* if mm not frozen */
	MMR1 = 0;
	MMR2 = PC;  }
IR = ReadW (PC | isenable);				/* fetch instruction */
PC = (PC + 2) & 0177777;				/* incr PC, mod 65k */
sim_interval = sim_interval - 1;
srcspec = (IR >> 6) & 077;				/* src, dst specs */
dstspec = IR & 077;
srcreg = (srcspec <= 07);				/* src, dst = rmode? */
dstreg = (dstspec <= 07);
switch ((IR >> 12) & 017) {				/* decode IR<15:12> */

/* Opcode 0: no operands, specials, branches, JSR, SOPs */

case 000:
	switch ((IR >> 6) & 077) {			/* decode IR<11:6> */
	case 000:					/* no operand */
		if (IR > 000010) {			/* 000010 - 000077 */
			setTRAP (TRAP_ILL);		/* illegal */
			break;  }
		switch (IR) {				/* decode IR<2:0> */
		case 0:					/* HALT */
		    	if (cm == KERNEL) reason = STOP_HALT;
			else {	setTRAP (TRAP_PRV);
				setCPUERR (CPUE_HALT);  }
			break;
		case 1:					/* WAIT */
			if (cm == KERNEL && wait_enable) wait_state = 1;
			break;
		case 3:					/* BPT */
			setTRAP (TRAP_BPT);
			break;
		case 4:					/* IOT */
			setTRAP (TRAP_IOT);
			break;
		case 5:					/* RESET */
			if (cm == KERNEL) {
				reset_all (1);
				PIRQ = 0;
				int_req = 0;
				MMR0 = MMR0 & ~(MMR0_MME | MMR0_FREEZE);
				MMR3 = 0;
				trap_req = trap_req & ~TRAP_INT;
				dsenable = calc_ds (cm);  }
			break;

/* Opcode 0: specials, continued */

		case 2:					/* RTI */
		case 6:					/* RTT */
		    	src = ReadW (SP | dsenable);
		    	src2 = ReadW (((SP + 2) & 0177777) | dsenable);
			STACKFILE[cm] = SP = (SP + 4) & 0177777;
			oldrs = rs;
			if (cm == KERNEL) {
				cm = (src2 >> PSW_V_CM) & 03;
				pm = (src2 >> PSW_V_PM) & 03;
				rs = (src2 >> PSW_V_RS) & 01;
				ipl = (src2 >> PSW_V_IPL) & 07;  }
			else {	cm = cm | ((src2 >> PSW_V_CM) & 03);
				pm = pm | ((src2 >> PSW_V_PM) & 03);
				rs = rs | ((src2 >> PSW_V_RS) & 01);  }
			tbit = (src2 >> PSW_V_TBIT) & 01;
			N = (src2 >> PSW_V_N) & 01;
			Z = (src2 >> PSW_V_Z) & 01;
			V = (src2 >> PSW_V_V) & 01;
			C = (src2 >> PSW_V_C) & 01;
			trap_req = calc_ints (ipl, int_req, trap_req);
			isenable = calc_is (cm);
			dsenable = calc_ds (cm);
			if (rs != oldrs) {
				for (i = 0; i < 6; i++) {
				    	REGFILE[i][oldrs] = R[i];
				    	R[i] = REGFILE[i][rs];  }  }
			SP = STACKFILE[cm];
			JMP_PC (src);
			if ((IR == 000002) && tbit) setTRAP (TRAP_TRC);
			break;
		case 7:					/* MFPT */
			R[0] = 5;			/* report J-11 */
			break;	}			/* end switch no ops */
		break;					/* end case no ops */

/* Opcode 0: specials, continued */

	case 001:					/* JMP */
		if (dstreg) setTRAP (TRAP_ILL);
		else {	JMP_PC (GeteaW (dstspec) & 0177777);  }
		break;					/* end JMP */
	case 002:					/* RTS et al*/
		if (IR < 000210) {			/* RTS */
			dstspec = dstspec & 07;
			JMP_PC (R[dstspec]);
			R[dstspec] = ReadW (SP | dsenable);
			SP = (SP + 2) & 0177777;
			break;  } 			/* end if RTS */
		if (IR < 000230) {
			setTRAP (TRAP_ILL);
			break;  }
		if (IR < 000240) {			/* SPL */
			if (cm == KERNEL) ipl = IR & 07;
			trap_req = calc_ints (ipl, int_req, trap_req);
			break;  }			/* end if SPL */
		if (IR < 000260) {			/* clear CC */
			if (IR & 010) N = 0;
			if (IR & 004) Z = 0;
			if (IR & 002) V = 0;
			if (IR & 001) C = 0;
			break;  }			/* end if clear CCs */
		if (IR & 010) N = 1;			/* set CC */
		if (IR & 004) Z = 1;
		if (IR & 002) V = 1;
		if (IR & 001) C = 1;
		break;					/* end case RTS et al */
	case 003:					/* SWAB */
		dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = ((dst & 0377) << 8) | ((dst >> 8) & 0377);
		N = GET_SIGN_B (dst & 0377);
		Z = GET_Z (dst & 0377);
		V = C = 0;
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;  				/* end SWAB */

/* Opcode 0: branches, JSR */

	case 004: case 005:				/* BR */
		BRANCH_F (IR);
		break;
	case 006: case 007:				/* BR */
		BRANCH_B (IR);
		break;
	case 010: case 011:				/* BNE */
		if (Z == 0) { BRANCH_F (IR); } 
		break;
	case 012: case 013:				/* BNE */
		if (Z == 0) { BRANCH_B (IR); }
		break;
	case 014: case 015:				/* BEQ */
		if (Z) { BRANCH_F (IR); } 
		break;
	case 016: case 017:				/* BEQ */
		if (Z) { BRANCH_B (IR); }
		break;
	case 020: case 021:				/* BGE */
		if ((N ^ V) == 0) { BRANCH_F (IR); } 
		break;
	case 022: case 023:				/* BGE */
		if ((N ^ V) == 0) { BRANCH_B (IR); }
		break;
	case 024: case 025:				/* BLT */
		if (N ^ V) { BRANCH_F (IR); }
		break;
	case 026: case 027:				/* BLT */
		if (N ^ V) { BRANCH_B (IR); }
		break;
	case 030: case 031:				/* BGT */
		if ((Z | (N ^ V)) == 0) { BRANCH_F (IR); } 
		break;
	case 032: case 033:				/* BGT */
		if ((Z | (N ^ V)) == 0) { BRANCH_B (IR); }
		break;
	case 034: case 035:				/* BLE */
		if (Z | (N ^ V)) { BRANCH_F (IR); } 
		break;
	case 036: case 037:				/* BLE */
		if (Z | (N ^ V)) { BRANCH_B (IR); }
		break;
	case 040: case 041: case 042: case 043:		/* JSR */
	case 044: case 045: case 046: case 047:
		if (dstreg) setTRAP (TRAP_ILL);
		else {	srcspec = srcspec & 07;
			dst = GeteaW (dstspec);
			SP = (SP - 2) & 0177777;
			if (update_MM) MMR1 = calc_MMR1 (0366);
			WriteW (R[srcspec], SP | dsenable);
			if ((SP < STKLIM) && (cm == KERNEL)) {
				setTRAP (TRAP_YEL);
				setCPUERR (CPUE_YEL);  }
			R[srcspec] = PC;
			JMP_PC (dst & 0177777);  }
		break;					/* end JSR */

/* Opcode 0: SOPs */

	case 050:					/* CLR */
		N = V = C = 0;
		Z = 1;
		if (dstreg) R[dstspec] = 0;
		else WriteW (0, GeteaW (dstspec));
		break;
	case 051:					/* COM */
		dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = dst ^ 0177777;
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = 0;
		C = 1;
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;
	case 052:					/* INC */
		dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = (dst + 1) & 0177777;
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = (dst == 0100000);
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;
	case 053:					/* DEC */
		dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = (dst - 1) & 0177777;
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = (dst == 077777);
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;
	case 054:					/* NEG */
		dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = (-dst) & 0177777;
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = (dst == 0100000);
		C = Z ^ 1;
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;
	case 055:					/* ADC */
		dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = (dst + C) & 0177777;
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = (C && (dst == 0100000));
		C = C & Z;
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;

/* Opcode 0: SOPs, continued */

	case 056:					/* SBC */
		dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = (dst - C) & 0177777;
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = (C && (dst == 077777));
		C = (C && (dst == 0177777));
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;
	case 057:					/* TST */
		dst = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = C = 0;
		break;
	case 060:					/* ROR */
		src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = (src >> 1) | (C << 15);
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		C = (src & 1);
		V = N ^ C;
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;
	case 061:					/* ROL */
		src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = ((src << 1) | C) & 0177777;
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		C = GET_SIGN_W (src);
		V = N ^ C;
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;
	case 062:					/* ASR */
		src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = (src >> 1) | (src & 0100000);
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		C = (src & 1);
		V = N ^ C;
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;
	case 063:					/* ASL */
		src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = (src << 1) & 0177777;
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		C = GET_SIGN_W (src);
		V = N ^ C;
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;

/* Opcode 0: SOPS, continued

   Notes:
   - MxPI must mask GeteaW returned address to force ispace
   - MxPI must set MMR1 for SP recovery in case of fault
*/

	case 064:					/* MARK */
		i = (PC + dstspec + dstspec) & 0177777;
		JMP_PC (R[5]);
		R[5] = ReadW (i | dsenable);
		SP = (i + 2) & 0177777;
		break;
	case 065:					/* MFPI */
		if (dstreg) {
			if ((dstspec == 6) && (cm != pm)) dst = STACKFILE[pm];
			else dst = R[dstspec];  }
		else dst = ReadW ((GeteaW (dstspec) & 0177777) | calc_is (pm));
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = 0;
		SP = (SP - 2) & 0177777;
		if (update_MM) MMR1 = calc_MMR1 (0366);
		WriteW (dst, SP | dsenable);
		if ((cm == KERNEL) && (SP < STKLIM)) {
			setTRAP (TRAP_YEL);
			setCPUERR (CPUE_YEL);  }
		break;
	case 066:					/* MTPI */
		dst = ReadW (SP | dsenable);
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = 0;
		SP = (SP + 2) & 0177777;
		if (update_MM) MMR1 = 026;
		if (dstreg) {
			if ((dstspec == 6) && (cm != pm)) STACKFILE[pm] = dst;
			else R[dstspec] = dst;  }
		else {	i = ((cm == pm) && (cm == USER))?
				calc_ds (pm): calc_is (pm);
			WriteW (dst, (GeteaW (dstspec) & 0177777) | i);  }
		break;
	case 067:					/* SXT */
		dst = N? 0177777: 0;
		Z = N ^ 1;
		V = 0;
		if (dstreg) R[dstspec] = dst;
		else WriteW (dst, GeteaW (dstspec));
		break;

/* Opcode 0: SOPs, continued */

	case 070:					/* CSM */
		if (((MMR3 & MMR3_CSM) == 0) || (cm == KERNEL))
			setTRAP (TRAP_ILL);
		else {	dst = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
			PSW = (cm << PSW_V_CM) | (pm << PSW_V_PM) |
				(rs << PSW_V_RS) | (ipl << PSW_V_IPL) | 
				(tbit << PSW_V_TBIT);
			STACKFILE[cm] = SP;
			WriteW (PSW, ((SP - 2) & 0177777) | calc_ds (SUPER));
			WriteW (PC, ((SP - 4) & 0177777) | calc_ds (SUPER));
			WriteW (dst, ((SP - 6) & 0177777) | calc_ds (SUPER));
			SP = (SP - 6) & 0177777;
			pm = cm;
			cm = SUPER;
			tbit = 0;
			isenable = calc_is (cm);
			dsenable = calc_ds (cm);
			PC = ReadW (010 | isenable);  }
		break;
	case 072:					/* TSTSET */
		if (dstreg) setTRAP (TRAP_ILL);
		else {	dst = ReadMW (GeteaW (dstspec));
			N = GET_SIGN_W (dst);
			Z = GET_Z (dst);
			V = 0;
			C = (dst & 1);
			PWriteW (R[0] | 1, last_pa);
			R[0] = dst;  }
		break;
	case 073:					/* WRTLCK */
		if (dstreg) setTRAP (TRAP_ILL);
		else {	N = GET_SIGN_W (R[0]);
			Z = GET_Z (R[0]);
			V = 0;
			WriteW (R[0], GeteaW (dstspec));  }
		break;
	default:
		setTRAP (TRAP_ILL);
		break;  }				/* end switch SOPs */
	break;						/* end case 000 */

/* Opcodes 01 - 06: double operand word instructions

   Add: v = [sign (src) = sign (src2)] and [sign (src) != sign (result)]
   Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)]
*/

case 001:						/* MOV */
	dst = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
	N = GET_SIGN_W (dst);
	Z = GET_Z (dst);
	V = 0;
	if (dstreg) R[dstspec] = dst;
	else WriteW (dst, GeteaW (dstspec));
	break;
case 002:						/* CMP */
	src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
	src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
	dst = (src - src2) & 0177777;
	N = GET_SIGN_W (dst);
	Z = GET_Z (dst);
	V = GET_SIGN_W ((src ^ src2) & (~src2 ^ dst));
	C = (src < src2);
	break;
case 003:						/* BIT */
	src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
	src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
	dst = src2 & src;
	N = GET_SIGN_W (dst);
	Z = GET_Z (dst);
	V = 0;
	break;
case 004:						/* BIC */
	src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
	src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
	dst = src2 & ~src;
	N = GET_SIGN_W (dst);
	Z = GET_Z (dst);
	V = 0;
	if (dstreg) R[dstspec] = dst;
	else PWriteW (dst, last_pa);
	break;
case 005:						/* BIS */
	src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
	src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
	dst = src2 | src;
	N = GET_SIGN_W (dst);
	Z = GET_Z (dst);
	V = 0;
	if (dstreg) R[dstspec] = dst;
	else PWriteW (dst, last_pa);
	break;
case 006:						/* ADD */
	src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
	src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
	dst = (src2 + src) & 0177777;
	N = GET_SIGN_W (dst);
	Z = GET_Z (dst);
	V = GET_SIGN_W ((~src ^ src2) & (src ^ dst));
	C = (dst < src);
	if (dstreg) R[dstspec] = dst;
	else PWriteW (dst, last_pa);
	break;

/* Opcode 07: EIS, FIS (not implemented), CIS (not implemented)

   Notes:
   - The code assumes that the host int length is at least 32 bits.
   - MUL carry: C is set if the (signed) result doesn't fit in 16 bits.
   - Divide has three error cases:
	1. Divide by zero.
	2. Divide largest negative number by -1.
	3. (Signed) quotient doesn't fit in 16 bits.
     Cases 1 and 2 must be tested in advance, to avoid C runtime errors.
   - ASHx left: overflow if the bits shifted out do not equal the sign
     of the result (convert shift out to 1/0, xor against sign).
   - ASHx right: if right shift sign extends, then the shift and
     conditional or of shifted -1 is redundant.  If right shift zero
     extends, then the shift and conditional or does sign extension.
*/

case 007:
	srcspec = srcspec & 07;
	switch ((IR >> 9) & 07)  {			/* decode IR<11:9> */
	case 0:						/* MUL */
		src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
		src = R[srcspec];
		if (GET_SIGN_W (src2)) src2 = src2 | ~077777;
		if (GET_SIGN_W (src)) src = src | ~077777;
		dst = src * src2;
		R[srcspec] = (dst >> 16) & 0177777;
		R[srcspec | 1] = dst & 0177777;
		N = (dst < 0);
		Z = GET_Z (dst);
		V = 0;
		C = ((dst > 077777) || (dst < -0100000));
		break;
	case 1:						/* DIV */
		src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
		src = (R[srcspec] << 16) | R[srcspec | 1];
		if (src2 == 0) {
			V = C = 1;
			break;  }
		if ((src == 020000000000) && (src2 == 0177777)) {
			V = 1;
			C = 0;
			break;  }
		if (GET_SIGN_W (src2)) src2 = src2 | ~077777;
		if (GET_SIGN_W (R[srcspec])) src = src | ~017777777777;
		dst = src / src2;
		if ((dst >= 077777) || (dst < -0100000)) {
			V = 1;
			C = 0;
			break;  }
		R[srcspec] = dst & 0177777;
		R[srcspec | 1] = (src - (src2 * dst)) & 0177777;
		N = (dst < 0);
		Z = GET_Z (dst);
		V = C = 0;
		break;

/* Opcode 7: EIS, continued */

	case 2:						/* ASH */
		src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
		src2 = src2 & 077;
		sign = GET_SIGN_W (R[srcspec]);
		src = sign? R[srcspec] | ~077777: R[srcspec];
		if (src2 == 0) {			/* [0] */
			dst = src;
			V = C = 0;  }
		else if (src2 <= 15) {			/* [1,15] */
			dst = src << src2;
			i = (src >> (16 - src2)) & 0177777;
			V = (i != ((dst & 0100000)? 0177777: 0));
			C = (i & 1);  }
		else if (src2 <= 31) {			/* [16,31] */
			dst = 0;
			V = (src != 0);
			C = (src << (src2 - 16)) & 1;  }
		else {					/* [-32,-1] */
			dst = (src >> (64 - src2)) | (-sign << (src2 - 32));
			V = 0;
			C = ((src >> (63 - src2)) & 1);  }
		dst = R[srcspec] = dst & 0177777;
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		break;
	case 3:						/* ASHC */
		src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
		src2 = src2 & 077;
		sign = GET_SIGN_W (R[srcspec]);
		src = (R[srcspec] << 16) | R[srcspec | 1];
		if (src2 == 0) { 			/* [0] */
			dst = src;
			V = C = 0;  }
		else if (src2 <= 31) {			/* [1,31] */
			dst = src << src2;
			i = (src >> (32 - src2)) | (-sign << src2);
			V = (i != ((dst & 020000000000)? -1: 0));
			C = (i & 1);  }
		else {					/* [-32,-1] */
			dst = (src >> (64 - src2)) | (-sign << (src2 - 32));
			V = 0;
			C = ((src >> (63 - src2)) & 1);  }
		i = R[srcspec] = (dst >> 16) & 0177777;
		dst = R[srcspec | 1] = dst & 0177777;
		N = GET_SIGN_W (i);
		Z = GET_Z (dst | i);
		break;

/* Opcode 7: EIS, continued */

	case 4:						/* XOR */
		dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
		dst = dst ^ R[srcspec];
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = 0;
		if (dstreg) R[dstspec] = dst;
		else PWriteW (dst, last_pa);
		break;
	case 5:						/* FIS - not impl */
		setTRAP (TRAP_ILL);
		break;
	case 6:						/* CIS - not impl */
		setTRAP (TRAP_ILL);
		break;
	case 7:						/* SOB */
		R[srcspec] = (R[srcspec] - 1) & 0177777;
		if (R[srcspec]) {
			JMP_PC ((PC - dstspec - dstspec) & 0177777);  }
		break;  }				/* end switch EIS */
	break;						/* end case 007 */

/* Opcode 10: branches, traps, SOPs */

case 010:
	switch ((IR >> 6) & 077) {			/* decode IR<11:6> */
	case 000: case 001:				/* BPL */
		if (N == 0) { BRANCH_F (IR); } 
		break;
	case 002: case 003:				/* BPL */
		if (N == 0) { BRANCH_B (IR); }
		break;
	case 004: case 005:				/* BMI */
		if (N) { BRANCH_F (IR); } 
		break;
	case 006: case 007:				/* BMI */
		if (N) { BRANCH_B (IR); }
		break;
	case 010: case 011:				/* BHI */
		if ((C | Z) == 0) { BRANCH_F (IR); } 
		break;
	case 012: case 013:				/* BHI */
		if ((C | Z) == 0) { BRANCH_B (IR); }
		break;
	case 014: case 015:				/* BLOS */
		if (C | Z) { BRANCH_F (IR); } 
		break;
	case 016: case 017:				/* BLOS */
		if (C | Z) { BRANCH_B (IR); }
		break;
	case 020: case 021:				/* BVC */
		if (V == 0) { BRANCH_F (IR); } 
		break;
	case 022: case 023:				/* BVC */
		if (V == 0) { BRANCH_B (IR); }
		break;
	case 024: case 025:				/* BVS */
		if (V) { BRANCH_F (IR); } 
		break;
	case 026: case 027:				/* BVS */
		if (V) { BRANCH_B (IR); }
		break;
	case 030: case 031:				/* BCC */
		if (C == 0) { BRANCH_F (IR); } 
		break;
	case 032: case 033:				/* BCC */
		if (C == 0) { BRANCH_B (IR); }
		break;
	case 034: case 035:				/* BCS */
		if (C) { BRANCH_F (IR); } 
		break;
	case 036: case 037:				/* BCS */
		if (C) { BRANCH_B (IR); }
		break;
	case 040: case 041: case 042: case 043:		/* EMT */
		setTRAP (TRAP_EMT);
		break;
	case 044: case 045: case 046: case 047:		/* TRAP */
		setTRAP (TRAP_TRAP);
		break;

/* Opcode 10, continued: SOPs */

	case 050:					/* CLRB */
		N = V = C = 0;
		Z = 1;
		if (dstreg) R[dstspec] = R[dstspec] & 0177400;
		else WriteB (0, GeteaB (dstspec));
		break;
	case 051:					/* COMB */
		dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
		dst = (dst ^ 0377) & 0377;
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		V = 0;
		C = 1;
		if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
		else PWriteB (dst, last_pa);
		break;
	case 052:					/* INCB */
		dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
		dst = (dst + 1) & 0377;
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		V = (dst == 0200);
		if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
		else PWriteB (dst, last_pa);
		break;
	case 053:					/* DECB */
		dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
		dst = (dst - 1) & 0377;
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		V = (dst == 0177);
		if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
		else PWriteB (dst, last_pa);
		break;
	case 054:					/* NEGB */
		dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
		dst = (-dst) & 0377;
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		V = (dst == 0200);
		C = (Z ^ 1);
		if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
		else PWriteB (dst, last_pa);
		break;
	case 055:					/* ADCB */
		dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
		dst = (dst + C) & 0377;
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		V = (C && (dst == 0200));
		C = C & Z;
		if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
		else PWriteB (dst, last_pa);
		break;

/* Opcode 10: SOPs, continued */

	case 056:					/* SBCB */
		dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
		dst = (dst - C) & 0377;
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		V = (C && (dst == 0177));
		C = (C && (dst == 0377));
		if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
		else PWriteB (dst, last_pa);
		break;
	case 057:					/* TSTB */
		dst = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec));
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		V = C = 0;
		break;
	case 060:					/* RORB */
		src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
		dst = ((src & 0377) >> 1) | (C << 7);
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		C = (src & 1);
		V = N ^ C;
		if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
		else PWriteB (dst, last_pa);
		break;
	case 061:					/* ROLB */
		src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
		dst = ((src << 1) | C) & 0377;
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		C = GET_SIGN_B (src & 0377);
		V = N ^ C;
		if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
		else PWriteB (dst, last_pa);
		break;
	case 062:					/* ASRB */
		src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
		dst = ((src & 0377) >> 1) | (src & 0200);
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		C = (src & 1);
		V = N ^ C;
		if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
		else PWriteB (dst, last_pa);
		break;
	case 063:					/* ASLB */
		src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
		dst = (src << 1) & 0377;
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		C = GET_SIGN_B (src & 0377);
		V = N ^ C;
		if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
		else PWriteB (dst, last_pa);
		break;

/* Opcode 10: SOPs, continued

   Notes:
   - MTPS cannot alter the T bit
   - MxPD must mask GeteaW returned address, dspace is from cm not pm
   - MxPD must set MMR1 for SP recovery in case of fault
*/

	case 064:					/* MTPS */
		dst = dstreg? R[dstspec]: ReadB (GeteaB (dstspec));
		if (cm == KERNEL) {
			ipl = (dst >> PSW_V_IPL) & 07;
			trap_req = calc_ints (ipl, int_req, trap_req);  }
		N = (dst >> PSW_V_N) & 01;
		Z = (dst >> PSW_V_Z) & 01;
		V = (dst >> PSW_V_V) & 01;
		C = (dst >> PSW_V_C) & 01;
		break;
	case 065:					/* MFPD */
		if (dstreg) {
			if ((dstspec == 6) && (cm != pm)) dst = STACKFILE[pm];
			else dst = R[dstspec];  }
		else dst = ReadW ((GeteaW (dstspec) & 0177777) | calc_ds (pm));
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = 0;
		SP = (SP - 2) & 0177777;
		if (update_MM) MMR1 = calc_MMR1 (0366);
		WriteW (dst, SP | dsenable);
		if ((cm == KERNEL) && (SP < STKLIM)) {
			setTRAP (TRAP_YEL);
			setCPUERR (CPUE_YEL);  }
		break;
	case 066:					/* MTPD */
		dst = ReadW (SP | dsenable);
		N = GET_SIGN_W (dst);
		Z = GET_Z (dst);
		V = 0;
		SP = (SP + 2) & 0177777;
		if (update_MM) MMR1 = 026;
		if (dstreg) {
			if ((dstspec == 6) && (cm != pm)) STACKFILE[pm] = dst;
			else R[dstspec] = dst;  }
		else WriteW (dst, (GeteaW (dstspec) & 0177777) | calc_ds (pm));
		break;
	case 067:					/* MFPS */
		dst = (ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) |
			(N << PSW_V_N) | (Z << PSW_V_Z) |
			(V << PSW_V_V) | (C << PSW_V_C);
		N = GET_SIGN_B (dst);
		Z = GET_Z (dst);
		V = 0;
		if (dstreg) R[dstspec] = (dst & 0200)? 0177400 | dst: dst;
		else WriteB (dst, GeteaB (dstspec));
		break;
	default:
		setTRAP (TRAP_ILL);
		break; }				/* end switch SOPs */
	break;						/* end case 010 */

/* Opcodes 11 - 16: double operand byte instructions

   Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)]
   Sub: v = [sign (src) != sign (src2)] and [sign (src) = sign (result)]
*/

case 011:						/* MOVB */
	dst = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec));
	N = GET_SIGN_B (dst);
	Z = GET_Z (dst);
	V = 0;
	if (dstreg) R[dstspec] = (dst & 0200)? 0177400 | dst: dst;
	else WriteB (dst, GeteaB (dstspec));
	break;
case 012:						/* CMPB */
	src = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec));
	src2 = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec));
	dst = (src - src2) & 0377;
	N = GET_SIGN_B (dst);
	Z = GET_Z (dst);
	V = GET_SIGN_B ((src ^ src2) & (~src2 ^ dst));
	C = (src < src2);
	break;
case 013:						/* BITB */
	src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec));
	src2 = dstreg? R[dstspec]: ReadB (GeteaB (dstspec));
	dst = (src2 & src) & 0377;
	N = GET_SIGN_B (dst);
	Z = GET_Z (dst);
	V = 0;
	break;
case 014:						/* BICB */
	src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec));
	src2 = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
	dst = (src2 & ~src) & 0377;
	N = GET_SIGN_B (dst);
	Z = GET_Z (dst);
	V = 0;
	if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
	else PWriteB (dst, last_pa);
	break;
case 015:						/* BISB */
	src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec));
	src2 = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
	dst = (src2 | src) & 0377;
	N = GET_SIGN_B (dst);
	Z = GET_Z (dst);
	V = 0;
	if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
	else PWriteB (dst, last_pa);
	break;
case 016:						/* SUB */
	src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
	src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
	dst = (src2 - src) & 0177777;
	N = GET_SIGN_W (dst);
	Z = GET_Z (dst);
	V = GET_SIGN_W ((src ^ src2) & (~src ^ dst));
	C = (src2 < src);
	if (dstreg) R[dstspec] = dst;
	else PWriteW (dst, last_pa);
	break;

/* Opcode 17: floating point */

case 017:
	fp11 (IR);					/* floating point */
	break;						/* end case 017 */
	}						/* end switch op */
}							/* end main loop */

/* Simulation halted */

PSW = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) |
	(ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) |
	(N << PSW_V_N) | (Z << PSW_V_Z) | (V << PSW_V_V) | (C << PSW_V_C);
for (i = 0; i < 6; i++) REGFILE[i][rs] = R[i];
STACKFILE[cm] = SP;
saved_PC = PC & 0177777;
return reason;
}							/* end sim_instr */

/* Effective address calculations

   Inputs:
	spec	=	specifier <5:0>
   Outputs:
	ea	=	effective address
			<15:0> =  virtual address
			<16> =    instruction/data data space
			<18:17> = mode

   Data space calculation: the PDP-11 features both instruction and data
   spaces.  Instruction space contains the instruction and any sequential
   add ons (eg, immediates, absolute addresses).  Data space contains all
   data operands and indirect addresses.  If data space is enabled, then
   memory references are directed according to these rules:

	Mode	Index ref	Indirect ref		Direct ref
	10..16	na		na			data
	17	na		na			instruction
	20..26	na		na			data
	27	na		na			instruction
	30..36	na		data			data
	37	na		instruction (absolute)	data
	40..46	na		na			data
	47	na		na			instruction
	50..56	na		data			data
	57	na		instruction		data
	60..67	instruction	na			data
	70..77	instruction	data			data

   According to the PDP-11 Architecture Handbook, MMR1 records all
   autoincrement and autodecrement operations, including those which
   explicitly reference the PC.  For the J-11, this is only true for
   autodecrement operands, autodecrement deferred operands, and
   autoincrement destination operands that involve a write to memory.
   The simulator follows the Handbook, for simplicity.

   Notes:

   - dsenable will direct a reference to data space if data space is enabled
   - ds will direct a reference to data space if data space is enabled AND if
	the specifier register is not PC; this is used for 17, 27, 37, 47, 57
   - Modes 2x, 3x, 4x, and 5x must update MMR1 if updating enabled
   - Modes 46 and 56 must check for stack overflow if kernel mode
*/

/* Effective address calculation for words */

int GeteaW (int spec)
{
int adr, reg, ds;

reg = spec & 07;					/* register number */
ds = (reg == 7)? isenable: dsenable;			/* dspace if not PC */
switch (spec >> 3) {					/* decode spec<5:3> */
default:						/* can't get here */
case 1:							/* (R) */
	return (R[reg] | ds);
case 2:							/* (R)+ */
	R[reg] = ((adr = R[reg]) + 2) & 0177777;
	if (update_MM) MMR1 = calc_MMR1 (020 | reg);
	return (adr | ds);
case 3:							/* @(R)+ */
	R[reg] = ((adr = R[reg]) + 2) & 0177777;
	if (update_MM) MMR1 = calc_MMR1 (020 | reg);
	adr = ReadW (adr | ds);
	return (adr | dsenable);
case 4:							/* -(R) */
	adr = R[reg] = (R[reg] - 2) & 0177777;
	if (update_MM) MMR1 = calc_MMR1 (0360 | reg);
	if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) {
		setTRAP (TRAP_YEL);
		setCPUERR (CPUE_YEL);  }
	return (adr | ds);
case 5:							/* @-(R) */
	adr = R[reg] = (R[reg] - 2) & 0177777;
	if (update_MM) MMR1 = calc_MMR1 (0360 | reg);
	if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) {
		setTRAP (TRAP_YEL);
		setCPUERR (CPUE_YEL);  }
	adr = ReadW (adr | ds);
	return (adr | dsenable);
case 6:							/* d(r) */
	adr = ReadW (PC | isenable);
	PC = (PC + 2) & 0177777;
	return (((R[reg] + adr) & 0177777) | dsenable);
case 7:							/* @d(R) */
	adr = ReadW (PC | isenable);
	PC = (PC + 2) & 0177777;
	adr = ReadW (((R[reg] + adr) & 0177777) | dsenable);
	return (adr | dsenable);
	}						/* end switch */
}							/* end GeteaW */

/* Effective address calculation for bytes */

int GeteaB (int spec)
{
int adr, reg, ds, delta;

reg = spec & 07;					/* reg number */
ds = (reg == 7)? isenable: dsenable;			/* dspace if not PC */
switch (spec >> 3) {					/* decode spec<5:3> */
default:						/* can't get here */
case 1:							/* (R) */
	return (R[reg] | ds);
case 2:							/* (R)+ */
	delta = 1 + (reg >= 6);				/* 2 if R6, PC */
	R[reg] = ((adr = R[reg]) + delta) & 0177777;
	if (update_MM) MMR1 = calc_MMR1 ((delta << 3) | reg);
	return (adr | ds);
case 3:							/* @(R)+ */
	adr = R[reg];
	R[reg] = ((adr = R[reg]) + 2) & 0177777;
	if (update_MM) MMR1 = calc_MMR1 (020 | reg);
	adr = ReadW (adr | ds);
	return (adr | dsenable);
case 4:							/* -(R) */
	delta = 1 + (reg >= 6);				/* 2 if R6, PC */
	adr = R[reg] = (R[reg] - delta) & 0177777;
	if (update_MM) MMR1 = calc_MMR1 ((((-delta) & 037) << 3) | reg);
	if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) {
		setTRAP (TRAP_YEL);
		setCPUERR (CPUE_YEL);  }
	return (adr | ds);
case 5:							/* @-(R) */
	adr = R[reg] = (R[reg] - 2) & 0177777;
	if (update_MM) MMR1 = calc_MMR1 (0360 | reg);
	if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) {
		setTRAP (TRAP_YEL);
		setCPUERR (CPUE_YEL);  }
	adr = ReadW (adr | ds);
	return (adr | dsenable);
case 6:							/* d(r) */
	adr = ReadW (PC | isenable);
	PC = (PC + 2) & 0177777;
	return (((R[reg] + adr) & 0177777) | dsenable);
case 7:							/* @d(R) */
	adr = ReadW (PC | isenable);
	PC = (PC + 2) & 0177777;
	adr = ReadW (((R[reg] + adr) & 0177777) | dsenable);
	return (adr | dsenable);
	}						/* end switch */
}							/* end GeteaB */

/* Read byte and word routines, read only and read-modify-write versions

   Inputs:
	va	=	virtual address, <18:16> = mode, I/D space
   Outputs:
	data	=	data read from memory or I/O space
*/

int ReadW (int va)
{
int pa, data;

if (va & 1) {						/* odd address? */
	setCPUERR (CPUE_ODD);
	ABORT (TRAP_ODD);  }
pa = relocR (va);					/* relocate */
if (pa < MEMSIZE) return (M[pa >> 1]);			/* memory address? */
if (pa < IOPAGEBASE) {					/* I/O address? */
	setCPUERR (CPUE_NXM);
	ABORT (TRAP_NXM);  }
if (iopageR (&data, pa, READ) != SCPE_OK) {		/* invalid I/O addr? */
	setCPUERR (CPUE_TMO);
	ABORT (TRAP_NXM);  }
return data;
}

int ReadB (int va)
{
int pa, data;

pa = relocR (va);					/* relocate */
if (pa < MEMSIZE) return (va & 1? M[pa >> 1] >> 8: M[pa >> 1]) & 0377;
if (pa < IOPAGEBASE) {					/* I/O address? */
	setCPUERR (CPUE_NXM);
	ABORT (TRAP_NXM);  }
if (iopageR (&data, pa, READ) != SCPE_OK) {		/* invalid I/O addr? */
	setCPUERR (CPUE_TMO);
	ABORT (TRAP_NXM);  }
return ((va & 1)? data >> 8: data) & 0377;
}

int ReadMW (int va)
{
int data;

if (va & 1) {						/* odd address? */
	setCPUERR (CPUE_ODD);
	ABORT (TRAP_ODD);  }
last_pa = relocW (va);					/* reloc, wrt chk */
if (last_pa < MEMSIZE) return (M[last_pa >> 1]);	/* memory address? */
if (last_pa < IOPAGEBASE) {				/* I/O address? */
	setCPUERR (CPUE_NXM);
	ABORT (TRAP_NXM);  }
if (iopageR (&data, last_pa, READ) != SCPE_OK) {	/* invalid I/O addr? */
	setCPUERR (CPUE_TMO);
	ABORT (TRAP_NXM);  }
return data;
}

int ReadMB (int va)
{
int data;

last_pa = relocW (va);					/* reloc, wrt chk */
if (last_pa < MEMSIZE)
	return (va & 1? M[last_pa >> 1] >> 8: M[last_pa >> 1]) & 0377;
if (last_pa < IOPAGEBASE) {				/* I/O address? */
	setCPUERR (CPUE_NXM);
	ABORT (TRAP_NXM);  }
if (iopageR (&data, last_pa, READ) != SCPE_OK) {	/* invalid I/O addr? */
	setCPUERR (CPUE_TMO);
	ABORT (TRAP_NXM);  }
return ((va & 1)? data >> 8: data) & 0377;
}

/* Write byte and word routines

   Inputs:
	data	=	data to be written
	va	=	virtual address, <18:16> = mode, I/D space, or
	pa	=	physical address
   Outputs: none
*/

void WriteW (int data, int va)
{
int pa;

if (va & 1) {						/* odd address? */
	setCPUERR (CPUE_ODD);
	ABORT (TRAP_ODD);  }
pa = relocW (va);					/* relocate */
if (pa < MEMSIZE) {					/* memory address? */
	M[pa >> 1] = data;
	return;  }
if (pa < IOPAGEBASE) {					/* I/O address? */
	setCPUERR (CPUE_NXM);
	ABORT (TRAP_NXM);  }
if (iopageW (data, pa, WRITE) != SCPE_OK) {		/* invalid I/O addr? */
	setCPUERR (CPUE_TMO);
	ABORT (TRAP_NXM);  }
return;
}

void WriteB (int data, int va)
{
int pa;

pa = relocW (va);					/* relocate */
if (pa < MEMSIZE) {					/* memory address? */
	if (va & 1) M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8);
	else M[pa >> 1] = (M[pa >> 1] & ~0377) | data;
	return;  }             
if (pa < IOPAGEBASE) {					/* I/O address? */
	setCPUERR (CPUE_NXM);
	ABORT (TRAP_NXM);  }
if (iopageW (data, pa, WRITEB) != SCPE_OK) {		/* invalid I/O addr? */
	setCPUERR (CPUE_TMO);
	ABORT (TRAP_NXM);  }
return;
}

void PWriteW (int data, int pa)
{
if (pa < MEMSIZE) {					/* memory address? */
	M[pa >> 1] = data;
	return;  }
if (pa < IOPAGEBASE) {					/* I/O address? */
	setCPUERR (CPUE_NXM);
	ABORT (TRAP_NXM);  }
if (iopageW (data, pa, WRITE) != SCPE_OK) {		/* invalid I/O addr? */
	setCPUERR (CPUE_TMO);
	ABORT (TRAP_NXM);  }
return;
}

void PWriteB (int data, int pa)
{
if (pa < MEMSIZE) {					/* memory address? */
	if (pa & 1) M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8);
	else M[pa >> 1] = (M[pa >> 1] & ~0377) | data;
	return;  }             
if (pa < IOPAGEBASE) {					/* I/O address? */
	setCPUERR (CPUE_NXM);
	ABORT (TRAP_NXM);  }
if (iopageW (data, pa, WRITEB) != SCPE_OK) {		/* invalid I/O addr? */
	setCPUERR (CPUE_TMO);
	ABORT (TRAP_NXM);  }
return;
}

/* Relocate virtual address, read access

   Inputs:
	va	=	virtual address, <18:16> = mode, I/D space
   Outputs:
	pa	=	physical address
   On aborts, this routine aborts back to the top level simulator
   with an appropriate trap code.

   Notes:
   - APRFILE[UNUSED] is all zeroes, forcing non-resident abort
   - Aborts must update MMR0<15:13,6:1> if updating is enabled
*/

int relocR (int va)
{
int dbn, plf, apridx, apr, pa;

if (MMR0 & MMR0_MME) {					/* if mmgt */
	apridx = (va >> VA_V_APF) & 077;		/* index into APR */
	apr = APRFILE[apridx];				/* with va<18:13> */
	dbn = va & VA_BN;				/* extr block num */
	plf = (apr & PDR_PLF) >> 2;			/* extr page length */
	if ((apr & PDR_NR) == 0) {			/* if non-resident */
		if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE);
		MMR0 = MMR0 | MMR0_NR;
		ABORT (TRAP_MME);  }			/* abort ref */
	if ((apr & PDR_ED)? dbn < plf: dbn > plf) {	/* if pg lnt error */
		if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE);
		MMR0 = MMR0 | MMR0_PL;
		ABORT (TRAP_MME);  }			/* abort ref */
	pa = (va & VA_DF) + ((apr >> 10) & 017777700);
	if ((MMR3 & MMR3_M22E) == 0) {
		pa = pa & 0777777;
		if (pa >= 0760000) pa = 017000000 | pa;  }  }
else {	pa = va & 0177777;				/* mmgt off */
	if (pa >= 0160000) pa = 017600000 | pa;  }
return pa;
}							/* end relocR */

/* Relocate virtual address, write access

   Inputs:
	va	=	virtual address, <18:16> = mode, I/D space
   Outputs:
	pa	=	physical address
   On aborts, this routine aborts back to the top level simulator
   with an appropriate trap code.

   Notes:
   - APRFILE[UNUSED] is all zeroes, forcing non-resident abort
   - Aborts must update MMR0<15:13,6:1> if updating is enabled
*/

int relocW (int va)
{
int dbn, plf, apridx, apr, pa;

if (MMR0 & MMR0_MME) {					/* if mmgt */
	apridx = (va >> VA_V_APF) & 077;		/* index into APR */
	apr = APRFILE[apridx];				/* with va<18:13> */
	dbn = va & VA_BN;				/* extr block num */
	plf = (apr & PDR_PLF) >> 2;			/* extr page length */
	if ((apr & PDR_NR) == 0) {			/* if non-resident */
		if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE);
		MMR0 = MMR0 | MMR0_NR;
		ABORT (TRAP_MME);  }			/* abort ref */
	if ((apr & PDR_ED)? dbn < plf: dbn > plf) {	/* if pg lnt error */
		if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE);
		MMR0 = MMR0 | MMR0_PL;
		ABORT (TRAP_MME);  }			/* abort ref */
	if ((apr & PDR_RW) == 0) {			/* if rd only error */
		if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE);
		MMR0 = MMR0 | MMR0_RO;
		ABORT (TRAP_MME);  }			/* abort ref */
	APRFILE[apridx] = apr | PDR_W;			/* set W */
	pa = (va & VA_DF) + ((apr >> 10) & 017777700);
	if ((MMR3 & MMR3_M22E) == 0) {
		pa = pa & 0777777;
		if (pa >= 0760000) pa = 017000000 | pa;  }  }
else {	pa = va & 0177777;				/* mmgt off */
	if (pa >= 0160000) pa = 017600000 | pa;  }
return pa;
}							/* end relocW */

/* Relocate virtual address, console access

   Inputs:
	va	=	virtual address
	sw	=	switches
   Outputs:
	pa	=	physical address
   On aborts, this routine returns -1
*/

int relocC (int va, int sw)
{
int mode, dbn, plf, apridx, apr, pa;

if (MMR0 & MMR0_MME) {					/* if mmgt */
	if (sw & SWMASK ('K')) mode = KERNEL;
	else if (sw & SWMASK ('S')) mode = SUPER;
	else if (sw & SWMASK ('U')) mode = USER;
	else if (sw & SWMASK ('P')) mode = (PSW >> PSW_V_PM) & 03;
	else mode = (PSW >> PSW_V_CM) & 03;
	va = va | ((sw & SWMASK ('D'))? calc_ds (mode): calc_is (mode));
	apridx = (va >> VA_V_APF) & 077;		/* index into APR */
	apr = APRFILE[apridx];				/* with va<18:13> */
	dbn = va & VA_BN;				/* extr block num */
	plf = (apr & PDR_PLF) >> 2;			/* extr page length */
	if ((apr & PDR_NR) == 0) return -1;
	if ((apr & PDR_ED)? dbn < plf: dbn > plf) return -1;
	pa = (va & VA_DF) + ((apr >> 10) & 017777700);
	if ((MMR3 & MMR3_M22E) == 0) {
		pa = pa & 0777777;
		if (pa >= 0760000) pa = 017000000 | pa;  }  }
else {	pa = va & 0177777;				/* mmgt off */
	if (pa >= 0160000) pa = 017600000 | pa;  }
return pa;
}							/* end relocC */

/* I/O page lookup and linkage routines

   Inputs:
	*data	=	pointer to data to read, if READ
	data	=	data to store, if WRITE or WRITEB
	pa	=	address
	access	=	READ, WRITE, or WRITEB
   Outputs:
	status	=	SCPE_OK or SCPE_NXM
*/

int iopageR (int *data, int pa, int access)
{
int stat;
struct iolink *p;

for (p = &iotable[0]; p -> low != 0; p++ ) {
	if ((pa >= p -> low) && (pa <= p -> high))  {
		stat = p -> read (data, pa, access);
		trap_req = calc_ints (ipl, int_req, trap_req);
		return stat;  }  }
return SCPE_NXM;
}

int iopageW (int data, int pa, int access)
{
int stat;
struct iolink *p;

for (p = &iotable[0]; p -> low != 0; p++ ) {
	if ((pa >= p -> low) && (pa <= p -> high))  {
		stat = p -> write (data, pa, access);
		trap_req = calc_ints (ipl, int_req, trap_req);
		return stat;  }  }
return SCPE_NXM;
}

/* I/O page routines for CPU registers

   Switch register and memory management registers

   SR 	17777570	read only
   MMR0 17777572	read/write, certain bits unimplemented or read only
   MMR1 17777574	read only
   MMR2 17777576	read only
   MMR3 17777516	read/write, certain bits unimplemented
*/

int SR_MMR012_rd (int *data, int pa, int access)
{
switch ((pa >> 1) & 3) {				/* decode pa<2:1> */
case 0:							/* SR */
	*data = SR;
	return SCPE_OK;
case 1:							/* MMR0 */
	*data = MMR0 & MMR0_IMP;
	return SCPE_OK;
case 2:							/* MMR1 */
	*data = MMR1;
	return SCPE_OK;
case 3:							/* MMR2 */
	*data = MMR2;
	return SCPE_OK;  }				/* end switch pa */
}							/* end SR_MMR012_rd */

int SR_MMR012_wr (int data, int pa, int access)
{
switch ((pa >> 1) & 3) {				/* decode pa<2:1> */
case 0:							/* DR */
	DR = data;
	return SCPE_OK;
case 1:							/* MMR0 */
	if (access == WRITEB) data = (pa & 1)?
		(MMR0 & 0377) | (data << 8): (MMR0 & ~0377) | data;
	MMR0 = (MMR0 & ~MMR0_RW) | (data & MMR0_RW);
	return SCPE_OK;
default:						/* MMR1, MMR2 */
	return SCPE_OK;  }				/* end switch pa */
}							/* end SR_MMR012_wr */

int MMR3_rd (int *data, int pa, int access)		/* MMR3 */
{
*data = MMR3 & MMR3_IMP;
return SCPE_OK;
}

int MMR3_wr (int data, int pa, int access)		/* MMR3 */
{
if (pa & 1) return SCPE_OK;
MMR3 = data & MMR3_RW;
#ifdef PDP11_45
MMR3 = MMR3 & ~(MMR3_BME + MMR3_M22E);			/* for UNIX V6 */
#endif
dsenable = calc_ds (cm);
return SCPE_OK;
}

/* PARs and PDRs.  These are grouped in I/O space as follows:

	17772200 - 17772276	supervisor block
	17772300 - 17772376	kernel block
	17777600 - 17777676	user block

   Within each block, the subblocks are I PDR's, D PDR's, I PAR's, D PAR's

   Thus, the algorithm for converting between I/O space addresses and
   APRFILE indices is as follows:

	idx<3:0> =	dspace'page	=	pa<4:1>
	par	=	PDR vs PAR	=	pa<5>
	idx<5:4> =	ker/sup/user	=	pa<8>'~pa<6>

   Note that the W bit is read only; it is cleared by any write to an APR
*/

int APR_rd (int *data, int pa, int access)
{
int left, idx;

idx = (pa >> 1) & 017;					/* dspace'page */
left = (pa >> 5) & 1;					/* PDR vs PAR */
if ((pa & 0100) == 0) idx = idx | 020;			/* 1 for super, user */
if (pa & 0400) idx = idx | 040;				/* 1 for user only */
*data = left? (APRFILE[idx] >> 16) & 0177777: APRFILE[idx] & PDR_IMP;
return SCPE_OK;
}

int APR_wr (int data, int pa, int access)
{
int left, idx, curr;

idx = (pa >> 1) & 017;					/* dspace'page */
left = (pa >> 5) & 1;					/* PDR vs PAR */
if ((pa & 0100) == 0) idx = idx | 020;			/* 1 for super, user */
if (pa & 0400) idx = idx | 040;				/* 1 for user only */
curr = left? (APRFILE[idx] >> 16) & 0177777: APRFILE[idx] & PDR_IMP;
if (access == WRITEB) data = (pa & 1)?
	(curr & 0377) | (data << 8): (curr & ~0377) | data;
if (left) APRFILE[idx] =
	((APRFILE[idx] & 0177777) | (data << 16)) & ~PDR_W;
else APRFILE[idx] =
	((APRFILE[idx] & ~PDR_RW) | (data & PDR_RW)) & ~PDR_W;
return SCPE_OK;
}

/* CPU control registers

   MEMERR	17777744	read only, clear on write
   CCR		17777746	read/write
   MAINT	17777750	read only
   HITMISS	17777752	read only
   CPUERR	17777766	read only, clear on write
   PIRQ		17777772	read/write, with side effects
   PSW		17777776	read/write, with side effects
*/

int CPU_rd (int *data, int pa, int access)
{
switch ((pa >> 1) & 017) {				/* decode pa<4:1> */
case 2: 						/* MEMERR */
	*data = MEMERR;
	MEMERR = 0;
	return SCPE_OK;
case 3:							/* CCR */
	*data = CCR;
	return SCPE_OK;
case 4:							/* MAINT */
	*data = MAINT;
	return SCPE_OK;
case 5:							/* Hit/miss */
	*data = HITMISS;
	return SCPE_OK;
case 013:						/* CPUERR */
	*data = CPUERR & CPUE_IMP;
	CPUERR = 0;
	return SCPE_OK;
case 015:						/* PIRQ */
	*data = PIRQ;
	return SCPE_OK;
case 017:						/* PSW */
	if (access == READC) *data = PSW;
	else *data = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) |
		(ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) |
		(N << PSW_V_N) | (Z << PSW_V_Z) |
		(V << PSW_V_V) | (C << PSW_V_C);
	return SCPE_OK;  }				/* end switch PA */
return SCPE_NXM;					/* unimplemented */
}

/* CPU control registers, continued */

int CPU_wr (int data, int pa, int access)
{
int i, pl, curr, oldrs;

switch ((pa >> 1) & 017) {				/* decode pa<4:1> */
case 2: 						/* MEMERR */
	MEMERR = 0;
	return SCPE_OK;
case 3:							/* CCR */
	if (access == WRITEB) data = (pa & 1)?
		(CCR & 0377) | (data << 8): (CCR & ~0377) | data;
	CCR = data;
	return SCPE_OK;
case 4:							/* MAINT */
	return SCPE_OK;
case 5:							/* Hit/miss */
	return SCPE_OK;
case 013:						/* CPUERR */
	CPUERR = 0;
	return SCPE_OK;
case 015:						/* PIRQ */
	if (access == WRITEB) {
		if (pa & 1) data = data << 8;
		else return SCPE_OK;  }
	int_req = int_req & ~(INT_PIR7 + INT_PIR6 + INT_PIR5 + INT_PIR4 +
		INT_PIR3 + INT_PIR2 + INT_PIR1);
	PIRQ = data & PIRQ_RW;
	pl = 0;
	if (PIRQ & PIRQ_PIR1) { int_req = int_req | INT_PIR1; pl = 0042;  }
	if (PIRQ & PIRQ_PIR2) { int_req = int_req | INT_PIR2; pl = 0104;  }
	if (PIRQ & PIRQ_PIR3) { int_req = int_req | INT_PIR3; pl = 0146;  }
	if (PIRQ & PIRQ_PIR4) { int_req = int_req | INT_PIR4; pl = 0210;  }
	if (PIRQ & PIRQ_PIR5) { int_req = int_req | INT_PIR5; pl = 0252;  }
	if (PIRQ & PIRQ_PIR6) { int_req = int_req | INT_PIR6; pl = 0314;  }
	if (PIRQ & PIRQ_PIR7) { int_req = int_req | INT_PIR7; pl = 0356;  }
	PIRQ = PIRQ | pl;
	return SCPE_OK;

/* CPU control registers, continued

   Note: Explicit writes to the PSW do not modify the T bit
*/

case 017:						/* PSW */
	if (access == WRITEC) {				/* console access? */
		PSW = data & PSW_RW;
		return SCPE_OK;  }
	curr = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) |
		(ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) |
		(N << PSW_V_N) | (Z << PSW_V_Z) |
		(V << PSW_V_V) | (C << PSW_V_C);
	STACKFILE[cm] = SP;
	if (access == WRITEB) data = (pa & 1)?
		(curr & 0377) | (data << 8): (curr & ~0377) | data;
	curr = (curr & ~PSW_RW) | (data & PSW_RW);
	oldrs = rs;
	cm = (curr >> PSW_V_CM) & 03;			/* call calc_is,ds */
	pm = (curr >> PSW_V_PM) & 03;
	rs = (curr >> PSW_V_RS) & 01;
	ipl = (curr >> PSW_V_IPL) & 07;
	N = (curr >> PSW_V_N) & 01;
	Z = (curr >> PSW_V_Z) & 01;
	V = (curr >> PSW_V_V) & 01;
	C = (curr >> PSW_V_C) & 01;
	if (rs != oldrs) {
		for (i = 0; i < 6; i++) {
			REGFILE[i][oldrs] = R[i];
			R[i] = REGFILE[i][rs];  }  }
	SP = STACKFILE[cm];
	isenable = calc_is (cm);
	dsenable = calc_ds (cm);
	return SCPE_OK;  }				/* end switch pa */
return SCPE_NXM;					/* unimplemented */
}

/* Other CPU routines

	cpu_ex		read simulated memory
	cpu_dep		write simulated memory
	cpu_reset	reset the CPU
	cpu_svc		re-enable breakpoint after SCP service
*/

int cpu_ex (int *vptr, int addr, UNIT *uptr, int sw)
{
if (vptr == NULL) return SCPE_ARG;
if (sw & SWMASK ('V')) {				/* -v */
	if (addr >= VASIZE) return SCPE_NXM;
	addr = relocC (addr, sw);			/* relocate */
	if (addr < 0) return SCPE_REL;  }
if (addr < MEMSIZE) {
	*vptr = M[addr >> 1] & 0177777;
	return SCPE_OK;  }
if (addr < IOPAGEBASE) return SCPE_NXM;
return iopageR (vptr, addr, READC);
}

int cpu_dep (int val, int addr, UNIT *uptr, int sw)
{
if (sw & SWMASK ('V')) {				/* -v */
	if (addr >= VASIZE) return SCPE_NXM;
	addr = relocC (addr, sw);			/* relocate */
	if (addr < 0) return SCPE_REL;  }
if (addr < MEMSIZE) {
	M[addr >> 1] = val & 0177777;
	return SCPE_OK;  }
if (addr < IOPAGEBASE) return SCPE_NXM;
return iopageW (val, addr, WRITEC);
}

int cpu_reset (DEVICE *dptr)
{
PIRQ = MMR0 = MMR1 = MMR2 = MMR3 = 0;
DR = CPUERR = MEMERR = CCR = HITMISS = 0;
PSW = 000340;
trap_req = 0;
wait_state = 0;
return SCPE_OK;
}

int cpu_svc (UNIT *uptr)
{
if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt;
return SCPE_OK;
}
