/* ibm1130_cpu.c: IBM 1130 CPU simulator

   Based on the SIMH package written by Robert M Supnik

 * (C) Copyright 2002, Brian Knittel.
 * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
 * RISK basis, there is no warranty of fitness for any purpose, and the rest of the
 * usual yada-yada. Please keep this notice and the copyright in any distributions
 * or modifications.
 *
 * This is not a supported product, but I welcome bug reports and fixes.
 * Mail to simh@ibm1130.org

   25-Jun-01 BLK	Written
   10-May-02 BLK	Fixed bug in MDX instruction
   27-Mar-02 BLK	Made BOSC work even in short form
   16-Aug-02 BLK	Fixed bug in multiply instruction; didn't work with negative values
   18-Mar-03 BLK	Fixed bug in divide instruction; didn't work with negative values
   23-Jul-03 BLK	Prevented tti polling in CGI mode
   24-Nov-03 BLK	Fixed carry bit error in subtract and subtract double, found by Bob Flanders
   20-Oct-04 BLK	Changed "(unsigned int32)" to "(uint32)" to accomodate improved definitions of simh types
   					Also commented out my echo command as it's now a standard simh command
   27-Nov-05 BLK    Added Arithmetic Factor Register support per Carl Claunch  (GUI only)
   06-Dec-06 BLK	Moved CGI stuff out of ibm1130_cpu.c

>> To do: verify actual operands stored in ARF, need to get this from state diagrams in the schematic set
   Also: determine how many bits are actually stored in the IAR in a real 1130, by forcing wraparound
   and storing the IAR.

   IBM 1800 support is just beginning. Mode set is done (SET CPU 1800 or SET CPU 1130).
   Index registers are handled (1800 has real registers, 1130 uses core locations 1, 2 and 3 -- 
      but does the 1800 make its hardware index registers appear in the address space?)
   Need to add: memory protect feature, more interrupt levels, GUI mods, IO device mods, timers, watchdog.
   Memory protect was interesting -- they borrowed one of the two parity bits. XIO(0) on 1800 is used for
   interval timers, console data switches, console sense/program select/CE switches, interrupt mask register,
   programmed interrupt, console interrupt and operations monitor (watchdog)
   very interesting stuff.

   The register state for the IBM 1130 CPU is:

   IAR				instruction address register
   ACC				accumulator
   EXT				accumulator extension
   Oflow			overflow bit
   Carry			carry bit
   CES				console entry switches
   ipl				current interrupt level, -1 = non interrupt
   iplpending		bitmap of pending interrupts
   wait_state		current CPU state: running or waiting
   DSW				console run/stop switch device status word
   RUNMODE			processor step/run mode (may also imply IntRun)
   BREAK			breakpoint address
   WRU				simulator-break character
   IntRun			Int Run flag (causes level 5 interrupt after every instruction)
   ILSW0..5			interrupt level status words
   XR1, 2, 3		for IBM 1800 only, index registers 1, 2, and 3

   The SAR (storage address register) and SBR (storage buffer register) are updated
   but not saved in the CPU state; they matter only to the GUI.

   Interrupt handling: interrupts occur when any device on any level has an
   active interrupt.  XIO commands can clear specific IRQ bits. When this
   happens, we have to evaluate all devices on the same IRQ level for remaining
   indicators. The flag int_req is set with a bit corresponding to the IRQ level
   when any interrupt indicator is activated.

   The 1130 console has a switch that controls several run modes: SS (single processor
   step), SCLK (single clock step), SINST (single instruction step), INT_RUN
   (IRQ 5 after each non interrupt-handler instruction) and RUN (normal operation).
   This simulator does not implement SS and SCLK. The simulator GUI console handles
   SINST, so we only have to worry about INT_RUN. The console command SET CPU IntRun sets
   the tmode (trace mode) flag; this causes a level 5 interrupt after each
   instruction.

   The IBM 1130 instruction formats are

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	
   |  opcode      | F|  T  |                       |   general format
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   |  opcode      | 0|  T  |     DISPLACEMENT      |   short instruction
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   |  opcode      | 1|  T  | I|     MODIFIER       |   long instruction
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   |                  ADDRESS                      |   
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   opcode in MSBits

   F = format. 0 = short (1 word), 1 = long (2 word) instruction

   T = Tag    00 = no index register (e.g. IAR relative)
              01 = use index register 1 (e.g. core address 1 = M[1])
              02 = use index register 2 (e.g. core address 2 = M[2])
              03 = use index register 3 (e.g. core address 3 = M[3])

   DISPLACEMENT = two's complement (must be sign-extended)

   I = Indirect

   Note that IAR = instruction address+1 when instruction is being decoded.

   In normal addressing mode, effective address (EA) is computed as follows:

   F = 0  T = 0         EA = IAR + DISPLACEMENT
       0      1              IAR + DISPLACEMENT + M[1] 
       0      2              IAR + DISPLACEMENT + M[2]
       0      3              IAR + DISPLACEMENT + M[3]

   F = 1  T = 0  I = 0  EA = ADDRESS
       1      1      0       ADDRESS + M[1]
       1      2      0       ADDRESS + M[2]
       1      3      0       ADDRESS + M[3]
       1      0      1       M[ADDRESS]
       1      1      1       M[ADDRESS + M[1]]
       1      2      1       M[ADDRESS + M[2]]
       1      3      1       M[ADDRESS + M[3]]

   Loads or stores are then made to/from MEM[EA]. Some instructions have special
   weird addressing modes. Simulator code precomputes standard addressing for
   all instructions though it's not always used.

   General notes:

   Adding I/O devices requires modifications to three modules:

	ibm1130_defs.h		add interrupt request definitions
	ibm1130_cpu.c		add XIO command linkages
	ibm1130_sys.c		add to sim_devices array
*/

/* ------------------------------------------------------------------------
 * Definitions
 * ------------------------------------------------------------------------ */

#include <stdarg.h>

#include "ibm1130_defs.h"

#define save_ibkpt	(cpu_unit.u3)			/* will be SAVEd */

#define UPDATE_BY_TIMER
#define ENABLE_BACKTRACE
/* #define USE_MY_ECHO_CMD	*/				/* simh now has echo command built in */
#define ENABLE_1800_SUPPORT					/* define to enable support for 1800 CPU simulation mode */

static void cgi_start(void);
static void cgi_stop(t_stat reason);
static int simh_status_to_stopcode (int status);

/* hook pointers from scp.c */
void (*sim_vm_init) (void) = &sim_init;
extern char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream);
extern void (*sim_vm_post) (t_bool from_scp);
extern CTAB *sim_vm_cmd;

/* space to store extra simulator-specific commands */
#define MAX_EXTRA_COMMANDS 10
CTAB x_cmds[MAX_EXTRA_COMMANDS];

#ifdef _WIN32
#   define CRLF "\r\n"
#else
#   define CRLF "\n"
#endif

/* ------------------------------------------------------------------------
 * initializers for globals
 * ------------------------------------------------------------------------ */

#define SIGN_BIT(v)   ((v) & 0x8000)
#define DWSIGN_BIT(v) ((v) & 0x80000000)

uint16 M[MAXMEMSIZE];				/* core memory, up to 32Kwords (note: don't even think about trying 64K) */
uint16 ILSW[6] = {0,0,0,0,0,0};		/* interrupt level status words */
uint16 XR[3] = {0,0,0};				/* IBM 1800 index registers */
int32 IAR;							/* instruction address register */
int32 prev_IAR;						/* instruction address register at start of current instruction */
int32 SAR, SBR;						/* storage address/buffer registers */
int32 OP, TAG, CCC;					/* instruction decoded pieces */
int32 CES;							/* console entry switches */
int32 ACC, EXT;						/* accumulator and extension */
int32 ARF;							/* arithmetic factor, a non-addressable internal CPU register */
int32 RUNMODE;						/* processor run/step mode */
int32 ipl = -1;						/* current interrupt level (-1 = not handling irq) */
int32 iplpending = 0;				/* interrupted IPL's */
int32 tbit = 0;						/* trace flag (causes level 5 IRQ after each instr) */
int32 V = 0, C = 0;					/* condition codes */
int32 wait_state = 0;				/* wait state (waiting for an IRQ) */
int32 wait_lamp = TRUE;				/* alternate indicator to light the wait lamp on the GUI */
int32 int_req = 0;					/* sum of interrupt request levels active */
int32 int_lamps = 0;				/* accumulated version of int_req - gives lamp persistence */
int32 int_mask;						/* current active interrupt mask (ipl sensitive) */
int32 mem_mask;						/* mask for memory address bits based on current memory size */
int32 cpu_dsw = 0;					/* CPU device status word */
int32 ibkpt_addr = -1;				/* breakpoint addr */
int32 sim_gui = TRUE;				/* enable gui */
t_bool running = FALSE;				/* TRUE if CPU is running */
t_bool power   = TRUE;				/* TRUE if CPU power is on */
t_bool cgi     = FALSE;				/* TRUE if we are running as a CGI program */
t_bool cgiwritable = FALSE;			/* TRUE if we can write the disk images back to the image file in CGI mode */
t_bool is_1800 = FALSE;				/* TRUE if we are simulating an IBM 1800 processor */
t_stat reason;						/* CPU execution loop control */

static int32 int_masks[6] = {
	0x00, 0x20, 0x30, 0x38, 0x3C, 0x3E		/* IPL 0 is highest prio (sees no other interrupts) */
};

/* ------------------------------------------------------------------------
 * Function declarations
 * ------------------------------------------------------------------------ */

t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_reset (DEVICE *dptr);
t_stat cpu_svc (UNIT *uptr);
t_stat cpu_set_size (UNIT *uptr, int32 value, char *cptr, void *desc);
t_stat cpu_set_type (UNIT *uptr, int32 value, char *cptr, void *desc);
void calc_ints (void);

extern t_stat ts_wr (int32 data, int32 addr, int32 access);
extern t_stat detach_cmd (int flags, char *cptr);
extern UNIT cr_unit;
extern int32 sim_switches;

#ifdef ENABLE_BACKTRACE
	static void   archive_backtrace(char *inst);
	static void   reset_backtrace (void);
	static void   show_backtrace (int nshow);
	static t_stat backtrace_cmd (int flag, char *cptr);
#else
	#define archive_backtrace(inst)
	#define reset_backtrace()
	#define show_backtrace(ntrace)
#endif

#ifdef GUI_SUPPORT
#  define ARFSET(v) ARF = (v) & 0xFFFF		/* set Arithmetic Factor Register (used for display purposes only) */
#else
#  define ARFSET(v)							/* without GUI, no need for setting ARF */
#endif

static void   init_console_window (void);
static void   destroy_console_window (void);
static t_stat view_cmd (int flag, char *cptr);
static t_stat cpu_attach (UNIT *uptr, char *cptr);
static t_bool bsctest (int32 DSPLC, t_bool reset_V);
static void   exit_irq (void);
static void   trace_instruction (void);

/* ------------------------------------------------------------------------
 * CPU data structures:
 *    cpu_dev	CPU device descriptor
 *    cpu_unit	CPU unit descriptor
 *    cpu_reg	CPU register list
 *    cpu_mod	CPU modifier list
 *
 * The CPU is attachable; attaching a file to it write a log of instructions
 * and registers
 * ------------------------------------------------------------------------ */

#define UNIT_MSIZE	(1 << (UNIT_V_UF + 7))		/* flag for memory size setting */
#define UNIT_1800   (1 << (UNIT_V_UF + 0))		/* flag for 1800 mode */

UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX | UNIT_BINK | UNIT_ATTABLE | UNIT_SEQ, INIMEMSIZE) };

REG cpu_reg[] = {
	{ HRDATA (IAR, IAR, 32) },
	{ HRDATA (ACC, ACC, 32) },
	{ HRDATA (EXT, EXT, 32) },
	{ FLDATA (Oflow, V, 1) },
	{ FLDATA (Carry, C, 1) },
	{ HRDATA (CES, CES, 32) },
	{ HRDATA (ipl, ipl, 32), REG_RO },
	{ HRDATA (iplpending, iplpending, 32), REG_RO },
	{ HRDATA (wait_state, wait_state, 32)},
	{ HRDATA (DSW, cpu_dsw, 32), REG_RO },
	{ HRDATA (RUNMODE, RUNMODE, 32) },
	{ HRDATA (BREAK, ibkpt_addr, 32) },
	{ ORDATA (WRU, sim_int_char, 8) },
	{ FLDATA (IntRun, tbit, 1) },

	{ HRDATA (ILSW0, ILSW[0], 32), REG_RO },
	{ HRDATA (ILSW1, ILSW[1], 32), REG_RO },
	{ HRDATA (ILSW2, ILSW[2], 32), REG_RO },
	{ HRDATA (ILSW3, ILSW[3], 32), REG_RO },
	{ HRDATA (ILSW4, ILSW[4], 32), REG_RO },
	{ HRDATA (ILSW5, ILSW[5], 32), REG_RO },

#ifdef ENABLE_1800_SUPPORT
	{ HRDATA (IS_1800, is_1800, 32), REG_RO|REG_HIDDEN},		/* is_1800 flag is part of state, but hidden */
	{ HRDATA (XR1,     XR[0],   16), REG_RO|REG_HIDDEN},		/* index registers are unhidden if CPU set to 1800 mode */
	{ HRDATA (XR2,     XR[1],   16), REG_RO|REG_HIDDEN},
	{ HRDATA (XR3,     XR[2],   16), REG_RO|REG_HIDDEN},
#endif

	{ HRDATA (ARF, ARF, 32) },
	{ NULL}
};

MTAB cpu_mod[] = {
	{ UNIT_MSIZE,     4096, NULL,   "4KW",  &cpu_set_size},
	{ UNIT_MSIZE,     8192, NULL,   "8KW",  &cpu_set_size},
	{ UNIT_MSIZE,    16384, NULL,   "16KW", &cpu_set_size},
	{ UNIT_MSIZE,    32768, NULL,   "32KW", &cpu_set_size},
#ifdef ENABLE_1800_SUPPORT
	{ UNIT_1800,         0, "1130", "1130", &cpu_set_type},
	{ UNIT_1800, UNIT_1800, "1800", "1800", &cpu_set_type},
#endif
	{ 0 }  };

DEVICE cpu_dev = {
	"CPU", &cpu_unit, cpu_reg, cpu_mod,
	1, 16, 16, 1, 16, 16,
	&cpu_ex, &cpu_dep, &cpu_reset,
	NULL, cpu_attach, NULL};			/* attaching to CPU creates cpu log file */

/* ------------------------------------------------------------------------ 
 * Memory read/write -- save SAR and SBR on the way in and out
 *
 * (It can be helpful to set breakpoints on a = 1, 2, or 3 in these routines
 * to detect attempts to read/set index registers using normal memory addessing.
 * APL\1130 does this in some places, I think these are why it had to be modified
 * to run on the 1800. Of course not all read/write to 1, 2 or implies an attempt
 * to read/set and index register -- they could using the address in the normal way).
 * ------------------------------------------------------------------------ */

int32 ReadW  (int32 a)
{
	SAR = a;
	SBR = (int32) M[(a) & mem_mask];
	return SBR;
}

void WriteW (int32 a, int32 d)
{
	SAR = a;
	SBR = d;
	M[a & mem_mask] = (int16) d;
}

/* ------------------------------------------------------------------------ 
 * read and write index registers. On the 1130, they're in core addresses 1, 2, 3.
 * on the 1800, they're separate registers
 * ------------------------------------------------------------------------ */

static uint16 ReadIndex (int32 tag)
{
#ifdef ENABLE_1800_SUPPORT
	if (is_1800)
		return XR[tag-1];						/* 1800: fetch from register */
#endif
		
	SAR = tag;									/* 1130: ordinary read from memory (like ReadW) */
	SBR = (int32) M[(tag) & mem_mask];
	return SBR;
}

static void WriteIndex (int32 tag, int32 d)
{
#ifdef ENABLE_1800_SUPPORT
	if (is_1800) {
		XR[tag-1] = d;							/* 1800: store in register */
		return;
	}
#endif

	SAR = tag;									/* 1130: ordinary write to memory (same as WriteW) */
	SBR = d;
	M[tag & mem_mask] = (int16) d;
}

/* ------------------------------------------------------------------------ 
 * upcase - force a string to uppercase (ASCII)
 * ------------------------------------------------------------------------ */

char *upcase (char *str)
{
	char *s;

	for (s = str; *s; s++) {
		if (*s >= 'a' && *s <= 'z')
			*s -= 32;
	} 

	return str;
}

/* ------------------------------------------------------------------------ 
 * calc_ints - set appropriate bits in int_req if any interrupts are pending on given levels
 *
 * int_req:
 *    bit  5  4  3  2  1  0
 *          \  \  \  \  \  \
 *			 \  \  \  \  \  interrupt level 5 pending (lowest priority)
 *            \    . . .
 *             interrupt level 0 pending (highest priority)
 *
 * int_mask is set according to current interrupt level (ipl)
 *
 *			0  0  0  0  0  0	ipl = 0 (currently servicing highest priority interrupt)
 *			1  0  0  0  0  0		  1
 *			1  1  0  0  0  0		  2
 *			1  1  1  0  0  0		  3
 *			1  1  1  1  0  0		  4
 *		    1  1  1  1  1  0		  5 (currently servicing lowest priority interrupt)
 *			1  1  1  1  1  1		 -1 (not servicing an interrupt)
 * ------------------------------------------------------------------------ */

void calc_ints (void)
{
	register int i;
	register int32 newbits = 0;

    GUI_BEGIN_CRITICAL_SECTION	 			/* using critical section here so we don't mislead the GUI thread */

	for (i = 6; --i >= 0; ) {
		newbits >>= 1;
		if (ILSW[i])
			newbits |= 0x20;
	}

	int_req    = newbits;
	int_lamps |= int_req;
	int_mask   = (ipl < 0) ? 0xFFFF : int_masks[ipl];		/* be sure this is set correctly */

    GUI_END_CRITICAL_SECTION
}

/* ------------------------------------------------------------------------
 * instruction processor
 * ------------------------------------------------------------------------ */

#define INCREMENT_IAR 	IAR = (IAR + 1) & mem_mask
#define DECREMENT_IAR 	IAR = (IAR - 1) & mem_mask

void bail (char *msg)
{
	printf("%s\n", msg);
	exit(1);
}

static void weirdop (char *msg, int offset)
{
	printf("Weird opcode: %s at %04x\n", msg, IAR+offset);
}

static char *xio_devs[]  = {
	"0?", "console", "1142card", "1134papertape",
	"dsk0", "1627plot", "1132print", "switches",
	"1231omr", "2501card", "comm", "b?",
	"sys7", "d?", "e?", "f?",
	"10?", "dsk1", "dsk2", "dsk3",
	"dsk4", "dsk5", "dsk6", "dsk7+",
	"18?", "2250disp", "2741attachment", "1b",
	"1c?", "1d?", "1e?", "1f?"
};

static char *xio_funcs[] = {
	"0?", "write", "read", "sense_irq",
	"control", "initw", "initr", "sense"
};

t_stat sim_instr (void)
{
	extern int32 sim_interval;
	extern UNIT *sim_clock_queue;
	int32 i, eaddr, INDIR, IR, F, DSPLC, word2, oldval, newval, src, src2, dst, abit, xbit;
	int32 iocc_addr, iocc_op, iocc_dev, iocc_func, iocc_mod;
	char msg[50];
	int cwincount = 0, status;
	static long ninstr = 0;
	static char *intlabel[] = {"INT0","INT1","INT2","INT3","INT4","INT5"};

	if (cgi)								/* give CGI hook function a chance to do something */
		cgi_start();

	if (running)							/* this is definitely not reentrant */
		return -1;

	if (! power)							/* this matters only to the GUI */
		return STOP_POWER_OFF;

	running = TRUE;

	mem_mask = MEMSIZE - 1;					/* set other useful variables */
	calc_ints();

	/* Main instruction fetch/decode loop */

	reason = 0;
	wait_lamp = 0;							/* release lock on wait lamp */

#ifdef GUI_SUPPORT
	update_gui(TRUE);
	gui_run(TRUE);
#endif

	while (reason == 0)  {
		IAR &= mem_mask;

#ifdef GUI_SUPPORT
#ifndef UPDATE_BY_TIMER
#if (UPDATE_INTERVAL > 0)
			if (--cwincount <= 0) {
				update_gui(FALSE);			/* update console lamps only every so many instructions */
				cwincount = UPDATE_INTERVAL + (rand() % MIN(UPDATE_INTERVAL, 32));
			}
#else
			update_gui(FALSE);
#endif /* ifdef  UPDATE_INTERVAL */
#endif /* ifndef UPDATE_BY_TIMER */
#endif /* ifdef  GUI_SUPPORT     */

		if (sim_interval <= 0) {			/* any events timed out? */
			if (sim_clock_queue != NULL) {
				if ((status = sim_process_event()) != 0)
					reason = simh_status_to_stopcode(status);

				calc_ints();
				continue;
			}
		}

		if (int_req & int_mask) {			/* any pending interrupts? */
			for (i = 0; i <= 5; i++)		/* find highest pending interrupt */
				if ((int_req & int_mask) & (0x20 >> i))
					break;

			if (i >= 6) {					/* nothing to do? */
				calc_ints();				/* weird. recalculate */
				continue; 					/* back to fetch */
			}

			GUI_BEGIN_CRITICAL_SECTION

			if (ipl >= 0)					/* save previous IPL in bit stack */
				iplpending |= (0x20 >> ipl);

			ipl = i;						/* set new interrupt level */
			int_mask = int_masks[i];		/* set appropriate mask */

			GUI_END_CRITICAL_SECTION

			wait_state = 0;					/* exit wait state */
			eaddr = ReadW(8+i);				/* get IRQ vector */
			archive_backtrace(intlabel[i]);
			WriteW(eaddr, IAR);				/* save IAR */
			IAR = (eaddr+1) & mem_mask;		/* go to next address */
			continue;						/* now continue processing */
		}									/* end if int_req */

		if (wait_state) {					/* waiting? */
			sim_interval = 0;				/* run the clock out */

			if (sim_qcount() <= (cgi ? 0 : 1)) {		/* one routine queued? we're waiting for keyboard only */
				if (keyboard_is_busy()) {						/* we are actually waiting for a keystroke */
					if ((status = sim_process_event()) != SCPE_OK) 	/* get it with wait_state still set */
						reason = simh_status_to_stopcode(status);
				}
				else {						/* CPU is not expecting a keystroke (keyboard interrupt) */
					if (wait_state == WAIT_OP)
						reason = STOP_WAIT;	/* end the simulation */
					else
						reason = STOP_INVALID_INSTR;
				}
			}

			if (gdu_active())				/* but don't stop simulator if 2250 GDU is running */
				reason = 0;

			continue;
		}

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

		ninstr++;
		if (cpu_unit.flags & UNIT_ATT)
			trace_instruction();			/* log CPU details if logging is enabled */

		prev_IAR = IAR;						/* save IAR before incrementing it */

		IR = ReadW(IAR);					/* fetch 1st word of instruction */
		INCREMENT_IAR;
		sim_interval = sim_interval - 1;	/* this constitutes one tick of the simulation clock */

		OP  = (IR >> 11) & 0x1F;			/* opcode */
		F   = IR & 0x0400;					/* format bit: 1 = long instr */
		TAG = IR & 0x0300;					/* tag bits: index reg x */
		if (TAG)
			TAG >>= 8;

		/* here I compute the usual effective address on the assumption that the instruction will need it. Some don't. */

		if (F) {							/* long instruction, ASSUME it's valid (have to decrement IAR if not) */
			INDIR = IR & 0x0080;			/* indirect bit */
			DSPLC = IR & 0x007F;			/* displacement or modifier */
			if (DSPLC & 0x0040)
				DSPLC |= ~ 0x7F;			/* sign extend */

			word2 = ReadW(IAR);				/* get reference address */
			INCREMENT_IAR;					/* bump the instruction address register */

			eaddr = word2;					/* assume standard addressing & compute effective address */
			if (TAG)						/* if indexed */
				eaddr += ReadIndex(TAG);	/* add index register value */
			if (INDIR)						/* if indirect addressing */
				eaddr = ReadW(eaddr);		/* pick up referenced address */
		}
		else {								/* short instruction, use displacement */
			INDIR = 0;						/* never indirect */
			DSPLC = IR & 0x00FF;			/* get displacement */
			if (DSPLC & 0x0080)
				DSPLC |= ~ 0xFF;

			if (TAG)						/* if indexed */
				eaddr = ReadIndex(TAG) + DSPLC;	/* add index register value */
			else
				eaddr = IAR + DSPLC;		/* otherwise relative to IAR after fetch */
		}

		switch (OP) { 						/* decode instruction */
			case 0x01:						/* --- XIO --- */
				iocc_addr = ReadW(eaddr);			/* get IOCC packet */
				iocc_op   = ReadW(eaddr|1);			/* note 'or' not plus, address must be even for proper operation */

				iocc_dev  = (iocc_op  >> 11) & 0x001F;
				iocc_func = (iocc_op  >>  8) & 0x0007;
				iocc_mod  =  iocc_op         & 0x00FF;

				if (cpu_unit.flags & UNIT_ATT)
					trace_io("* XIO %s %s mod %02x addr %04x", xio_funcs[iocc_func], xio_devs[iocc_dev], iocc_mod, iocc_addr);

/*				fprintf(stderr, "* XIO %s %s mod %02x addr %04x\n", xio_funcs[iocc_func], xio_devs[iocc_dev], iocc_mod, iocc_addr); */

				ACC = 0;							/* ACC is destroyed, and default XIO_SENSE_DEV result is 0 */

				switch (iocc_func) {
					case XIO_UNUSED:
						sprintf(msg, "Unknown op %x on device %02x", iocc_func, iocc_dev);
						xio_error(msg);
						break;
					
					case XIO_SENSE_IRQ:				/* examine current Interrupt Level Status Word */
						ACC = (ipl >= 0) ? ILSW[ipl] : 0;
						break;
					
					default:						/* perform device-specific operation */
						switch (iocc_dev) {
							case 0x01:				/* console keyboard and printer */
								xio_1131_console(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x02:				/* 1142 card reader/punch */
								xio_1142_card(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x03:				/* 1134 paper tape reader/punch */
								xio_1134_papertape(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x04:				/* CPU disk storage */
								xio_disk(iocc_addr, iocc_func, iocc_mod, 0);
								break;
							case 0x05:				/* 1627 plotter */
								xio_1627_plotter(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x06:				/* 1132 Printer */
								xio_1132_printer(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x07:				/* console switches, stop key, run mode */
								xio_1131_switches(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x08:				/* 1231 optical mark reader */
								xio_1231_optical(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x09:				/* 2501 card reader */
								xio_2501_card(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x0a:				/* synchronous comm adapter */
								xio_sca(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x0c:				/* IBM System/7 interprocessor link */
								xio_system7(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x11:				/* 2310 Disk Storage, Drive 1, or 2311 Disk Storage Drive. Drive 1, Disk 1 */
								xio_disk(iocc_addr, iocc_func, iocc_mod, 1);
								break;
							case 0x12:				/* 2310 Disk Storage, Drive 2, or 2311 Disk Storage Drive. Drive 1, Disk 2 */
								xio_disk(iocc_addr, iocc_func, iocc_mod, 2);
								break;
							case 0x13:				/* 2310 Disk Storage, Drive 3, or 2311 Disk Storage Drive. Drive 1, Disk 3 */
								xio_disk(iocc_addr, iocc_func, iocc_mod, 3);
								break;
							case 0x14:				/* 2310 Disk Storage, Drive 4, or 2311 Disk Storage Drive. Drive 1, Disk 4 */
								xio_disk(iocc_addr, iocc_func, iocc_mod, 4);
								break;
							case 0x15:				/* 1403 Printer */
								xio_1403_printer(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x16:				/* 2311 Disk Storage Drive. Drive 1, Disk 5 */
								xio_disk(iocc_addr, iocc_func, iocc_mod, -1);
								break;
							case 0x17:				/* 2311 Disk Storage Drive, Drive 2, Disk 1 through 5 */
								xio_disk(iocc_addr, iocc_func, iocc_mod, -1);
								break;
							case 0x19:				/* 2250 Display Unit */
								xio_2250_display(iocc_addr, iocc_func, iocc_mod);
								break;
							case 0x1a:				/* 2741 Attachment (nonstandard serial interface used by APL\1130 */
								xio_t2741_terminal(iocc_addr, iocc_func, iocc_mod);
								break;
							default:
								sprintf(msg, "unknown device %02x", iocc_dev);
								xio_error(msg);
								break;
						}
				}

				calc_ints();						/* after every XIO, reset int_mask just in case */
				break;

			case 0x02:						/* --- SLA,SLT,SLC,SLCA,NOP - Shift Left family --- */
				if (F) {
					weirdop("Long Left Shift", -2);
					DECREMENT_IAR;
				}

				CCC = ((TAG == 0) ? DSPLC : ReadIndex(TAG)) & 0x003F;
				ARFSET(CCC);
				if (CCC == 0)
					break;							/* shift of zero is a NOP */

				switch (IR & 0x00C0) {
					case 0x0040:					/* SLCA */
						if (TAG) {
							while (CCC > 0 && (ACC & 0x8000) == 0) {
								ACC <<= 1;
								CCC--;
							}
							C = (CCC != 0);
							WriteIndex(TAG, (ReadIndex(TAG) & 0xFF00) | CCC);	/* put low 6 bits back into index register and zero bits 8 and 9 */
							break;
						}
						/* if TAG == 0, fall through and treat like normal shift SLA */

					case 0x0000:					/* SLA  */
						while (CCC > 0) {
							C    = (ACC & 0x8000);
							ACC  = (ACC << 1) & 0xFFFF;
							CCC--;
						}
						break;

					case 0x00C0:					/* SLC  */
						if (TAG) {
							while (CCC > 0 && (ACC & 0x8000) == 0) {
								abit = (EXT & 0x8000) >> 15;
								ACC  = ((ACC << 1) & 0xFFFF) | abit;
								EXT  = (EXT << 1);
								CCC--;
							}
							C = (CCC != 0);
							WriteIndex(TAG, ReadIndex(TAG) & 0xFF00 | CCC);		/* put 6 bits back into low byte of index register */
							break;
						}
						/* if TAG == 0, fall through and treat like normal shift SLT */

					case 0x0080:					/* SLT  */
						while (CCC > 0) {
							C    = (ACC & 0x8000);
							abit = (EXT & 0x8000) >> 15;
							ACC  = ((ACC << 1) & 0xFFFF) | abit;
							EXT  =  (EXT << 1) & 0xFFFF;
							CCC--;
						}
						break;

					default:
						bail("SLA switch, can't happen");
						break;
				}
				break;

			case 0x03:						/* --- SRA, SRT, RTE - Shift Right family --- */
				if (F) {
					weirdop("Long Right Shift", -2);
					DECREMENT_IAR;
				}

				CCC = ((TAG == 0) ? DSPLC : ReadIndex(TAG)) & 0x3F;
				ARFSET(CCC);
				if (CCC == 0)
					break;							/* NOP */

				switch (IR & 0x00C0) {
					case 0x0000:					/* SRA  */
						ACC = (CCC < 16) ? ((ACC & 0xFFFF) >> CCC) : 0;
						CCC = 0;
						break;

					case 0x0040:					/* invalid */
						wait_state = WAIT_INVALID_OP;
						break;

					case 0x0080:					/* SRT */
						while (CCC > 0) {
							xbit = (ACC & 0x0001) << 15;
							abit = (ACC & 0x8000);
							ACC  = (ACC >> 1) & 0x7FFF | abit;
							EXT  = (EXT >> 1) & 0x7FFF | xbit;
							CCC--;
						}
						break;

					case 0x00C0:					/* RTE */
						while (CCC > 0) {
							abit = (EXT & 0x0001) << 15;
							xbit = (ACC & 0x0001) << 15;
							ACC  = (ACC >> 1) & 0x7FFF | abit;
							EXT  = (EXT >> 1) & 0x7FFF | xbit;
							CCC--;
						}
						break;

					default:
						bail("SRA switch, can't happen");
						break;
				}
				break;

			case 0x04:						/* --- LDS - Load Status --- */
				if (F) {							/* never fetches second word? */
					weirdop("Long LDS", -2);
					DECREMENT_IAR;
				}

				V = (DSPLC & 1);
				C = (DSPLC & 2) >> 1;
				break;

			case 0x05:						/* --- STS - Store Status --- */
				newval = ReadW(eaddr) & 0xFF00;
				if (C)
					newval |= 2;
				if (V)
					newval |= 1;

				WriteW(eaddr, newval);
				C = V = 0;							/* clear flags after storing */
				break;

			case 0x06:						/* --- WAIT --- */
				wait_state = WAIT_OP;
				if (F) {							/* what happens if we use long format? */
					weirdop("Long WAIT", -2);
					DECREMENT_IAR;					/* assume it wouldn't have fetched 2nd word? */
				}
				break;

			case 0x08:						/* --- BSI - Branch and store IAR --- */
				if (F) {
					if (bsctest(IR, F))				/* do standard BSC long format testing */
						break;						/* if any condition is true, do nothing */
				}
				WriteW(eaddr, IAR);					/* do subroutine call */
				archive_backtrace("BSI");			/* save info in back-trace buffer */
				IAR = (eaddr + 1) & mem_mask;
				break;

			case 0x09:						/* --- BSC - Branch and skip on Condition --- */
				if (F) {
					if (bsctest(IR, F))				/* long format; any indicator cancels branch */
						break;

					archive_backtrace((DSPLC & 0x40) ? "BOSC" : "BSC");	/* save info in back-trace buffer */
					IAR = eaddr;					/* no indicator means branch taken */
				}
				else {								/* short format: skip if any indicator hits */
					if (bsctest(IR, F)) {
						archive_backtrace((DSPLC & 0x40) ? "BOSC" : "BSC");		/* save info in back-trace buffer */
						INCREMENT_IAR;
					}
				}
/* 27Mar02: moved this test out of the (F) condition; BOSC works even in the
 * short form. The displacement field in this instruction is always the set of
 * condition bits, and the interrupt clear bit doesn't collide. */

				if (DSPLC & 0x40) {		/* BOSC = exit from interrupt handler */
					exit_irq();
					cwincount = 0;
				}
				break;

			case 0x0c:						/* --- LDX - Load Index	--- */
				if (F)
					eaddr = (INDIR) ? ReadW(word2) : word2;
				else
					eaddr = DSPLC;

				if (TAG)
					WriteIndex(TAG, eaddr);
				else {
					archive_backtrace("LDX");		/* save info in back-trace buffer */
					IAR = eaddr;					/* what happens in short form? can onlyjump to low addresses? */
				}
				break;

			case 0x0d:						/* --- STX - Store Index --- */
				if (F) {							/* compute EA without any indexing */
					eaddr = (INDIR) ? ReadW(word2) : word2;
				}
				else {
					eaddr = IAR + DSPLC;
				}
				WriteW(eaddr, TAG ? ReadIndex(TAG) : IAR);
				break;

			case 0x0e:						/* --- MDX - Modify Index and Skip --- */
				if (F) {							/* long format: adjust memory location */
					if (TAG) {
						oldval = ReadIndex(TAG);	/* add word2 to index */
						newval = oldval + (INDIR ? ReadW(word2) : word2);
						WriteIndex(TAG, newval);
					}
					else {
						oldval = ReadW(word2);
						DSPLC = IR & 0x00FF;		/* use extended displacement (no INDIR bit, it's is part of displacement in this op) */
						if (DSPLC & 0x0080)
							DSPLC |= ~ 0xFF;
						newval = oldval + DSPLC;	/* add modifier to @word2 */
						WriteW(word2, newval);
					}
				}
				else {								/* short format: adust IAR or index */
					if (TAG) {
						oldval = ReadIndex(TAG);	/* add displacement to index */
						newval = oldval + DSPLC;
						WriteIndex(TAG, newval);
					}
					else {
						oldval = IAR;				/* add displacement to IAR */
						newval = IAR + DSPLC;
						archive_backtrace("MDX");
						IAR    = newval & mem_mask;
					}
				}

				if ((F || TAG) && (((newval & 0xFFFF) == 0) || ((oldval & 0x8000) != (newval & 0x8000)))) {
					archive_backtrace("SKP");
					INCREMENT_IAR;					/* skip if index sign change or zero */
				}
				break;

			case 0x10:						/* --- A - Add --- */
				/* in adds and subtracts, carry is set or cleared, overflow is set only */
				src  = ReadW(eaddr);
				ARFSET(src);
				src2 = ACC;
				ACC  = (ACC + src) & 0xFFFF;

				C = ACC < src;
				if (! V)
					V = SIGN_BIT((~src ^ src2) & (src ^ ACC));
				break;

			case 0x11:						/* --- AD - Add Double --- */
				src  = ((ACC << 16) | (EXT & 0xFFFF));
				ARFSET(EXT);
				src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1);
				dst  = src + src2;
				ACC  = (dst >> 16) & 0xFFFF;
				EXT  = dst & 0xFFFF;

				C = (uint32) dst < (uint32) src;
				if (! V)
					V = DWSIGN_BIT((~src ^ src2) & (src ^ dst));
				break;

			case 0x12:						/* --- S - Subtract	--- */
				src  = ACC;
				ARFSET(src);
				src2 = ReadW(eaddr);
				ACC  = (ACC-src2) & 0xFFFF;

				C = src < src2;
				if (! V)
					V = SIGN_BIT((src ^ src2) & (src ^ ACC));
				break;

			case 0x13:						/* --- SD - Subtract Double	--- */
				src  = ((ACC << 16) | (EXT & 0xFFFF));
				ARFSET(EXT);
				src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1);
				dst  = src - src2;
				ACC  = (dst >> 16) & 0xFFFF;
				EXT  = dst & 0xFFFF;

				C = (uint32) src < (uint32) src2;
				if (! V)
					V = DWSIGN_BIT((src ^ src2) & (src ^ dst));
				break;

			case 0x14:						/* --- M - Multiply	--- */
				if ((src = ACC & 0xFFFF)  & 0x8000)	/* sign extend the values */
					src	 |= ~0xFFFF;
				if ((src2 = ReadW(eaddr)) & 0x8000)
					src2 |= ~0xFFFF;

				ARFSET(src2);
				dst = src * src2;
				ACC = (dst >> 16) & 0xFFFF;			/* split the results */
				EXT = dst & 0xFFFF;
				break;

			case 0x15:						/* --- D - Divide --- */
				src  = ((ACC << 16) | (EXT & 0xFFFF));
				if ((src2 = ReadW(eaddr)) & 0x8000)
					src2 |= ~0xFFFF;				/* oops: sign extend was missing, fixed 18Mar03 */

				ARFSET(src2);

				if (src2 == 0)
					V = 1;							/* divide by zero just sets overflow, ACC & EXT are undefined */
				else {
					ACC = (src / src2) & 0xFFFF;
					EXT = (src % src2) & 0xFFFF;
				}
				break;

			case 0x18:						/* --- LD - Load ACC --- */
				ACC = ReadW(eaddr);
				break;

			case 0x19:						/* --- LDD - Load Double --- */
				ACC = ReadW(eaddr);
				EXT = ReadW(eaddr|1);				/* notice address is |1 not +1 */
				break;

			case 0x1a:						/* --- STO - Store ACC --- */
				WriteW(eaddr, ACC);
				break;

			case 0x1b:						/* --- STD - Store Double --- */
				WriteW(eaddr|1, EXT);
				WriteW(eaddr,   ACC);				/* order is important: if odd addr, only ACC is stored */
				break;

			case 0x1c:						/* --- AND - Logical AND --- */
				src = ReadW(eaddr); 
				ARFSET(src);
				ACC &= src;
				break;

			case 0x1d:						/* --- OR - Logical OR --- */
				src = ReadW(eaddr); 
				ARFSET(src);
				ACC |= src;
				break;

			case 0x1e:						/* --- EOR - Logical Excl OR --- */
				src = ReadW(eaddr); 
				ARFSET(src);
				ACC ^= src;
				break;

			case 0x16:
			case 0x17:
#ifdef ENABLE_1800_SUPPORT
				if (is_1800) {
					if (OP == 0x16) {		/* --- CMP - Compare --- */
						src  = ACC;						/* like subtract but result isn't stored */
						src2 = ReadW(eaddr);
						dst  = (ACC-src2) & 0xFFFF;
						C    = src < src2;

						if (dst & 0x8000)				/* if ACC <  operand, skip 1 instruction */
							IAR = IAR+1;
						else if ((dst & 0xFFFF) == 0)	/* if ACC == operand, skip 2 instructions */
							IAR = IAR+2;
					}
					else {					/* --- DCMP - Compare Double --- */
						src  = ((ACC << 16) | (EXT & 0xFFFF));
						src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1);
						dst  = src - src2;
						C    = (uint32) src < (uint32) src2;

						if (dst & 0x80000000)			/* if ACC_EXT <  operand, skip 1 instruction */
							IAR = IAR+1;
						else if (dst == 0)				/* if ACC_EXT == operand, skip 2 instructions */
							IAR = IAR+2;
					}

					break;								/* these are legal instructions on the 1800 */
				}
#endif
				/* 1130: these are not legal instructions, fall through */

			default:
/* all invalid instructions act like waits */
/*			case 0x00: */
/*			case 0x07: */
/*			case 0x0a: */
/*			case 0x0b: */
/*			case 0x0e: */
/*			case 0x0f: */
/*			case 0x1f: */
				wait_state = WAIT_INVALID_OP;
				if (F)
					DECREMENT_IAR;					/* assume it wouldn't have fetched 2nd word? */

				break;
		}											/* end instruction decode switch */

		if (RUNMODE != MODE_RUN && RUNMODE != MODE_INT_RUN)
			reason = STOP_WAIT;

		if (tbit && (ipl < 0)) {					/* if INT_RUN mode, set IRQ5 after this instr */
			GUI_BEGIN_CRITICAL_SECTION
			SETBIT(cpu_dsw, CPU_DSW_INT_RUN);
			SETBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP);
			int_req |= INT_REQ_5;
			GUI_END_CRITICAL_SECTION
		}
	}										/* end main loop */

#ifdef GUI_SUPPORT
	gui_run(FALSE);
#endif

	running   = FALSE;
	int_lamps = 0;							/* display only currently active interrupts while halted */

	if (reason == STOP_WAIT || reason == STOP_INVALID_INSTR) {
		wait_state = 0;						/* on resume, don't wait */
		wait_lamp = TRUE;					/* but keep the lamp lit on the GUI */

		CLRBIT(cpu_dsw, CPU_DSW_PROGRAM_STOP);	/* and on resume, reset program start bit */
		if ((cpu_dsw & CPU_DSW_PROGRAM_STOP) == 0)
			CLRBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP);
	}

	if (cgi)								/* give CGI hook function a chance to do something */
		cgi_stop(reason);

	return reason;
}

/*
 * simh_status_to_stopcode - convert a SCPE_xxx value from sim_process_event into a STOP_xxx code
 */
						
static int simh_status_to_stopcode (int status)
{
	return (status == SCPE_BREAK) ? STOP_BREAK     :
		   (status == SCPE_STOP)  ? STOP_IMMEDIATE :
		   (status == SCPE_STEP)  ? STOP_STEP      : STOP_OTHER;
}

/* ------------------------------------------------------------------------ 
 * bsctest - perform standard set of condition tests. We return TRUE if any
 * of the condition bits specified in DSPLC test positive, FALSE if none are true.
 * If reset_V is TRUE, we reset the oVerflow flag after testing it.
 * ------------------------------------------------------------------------ */

static t_bool bsctest (int32 DSPLC, t_bool reset_V)
{
	if (DSPLC & 0x01) {						/* Overflow off (note inverted sense) */
		if (! V)
			return TRUE;
		else if (reset_V)					/* reset after testing */
			V = 0;
	}

	if (DSPLC & 0x02) {						/* Carry off (note inverted sense) */
		if (! C)
			return TRUE;
	}

	if (DSPLC & 0x04)	   					/* Even */
		if ((ACC & 1) == 0)
			return TRUE;

	if (DSPLC & 0x08)						/* Positive */
		if ((ACC & 0x8000) == 0 && ACC != 0)
			return TRUE;

	if (DSPLC & 0x10)						/* Negative */
		if (ACC & 0x8000)
			return TRUE;

	if (DSPLC & 0x20)		 				/* Zero */
		if ((ACC & 0xFFFF) == 0)
			return TRUE;

	return FALSE;
}

/* ------------------------------------------------------------------------ 
 * exit_irq - pop interrupt stack as part of return from subroutine (BOSC) 
 * ------------------------------------------------------------------------ */

static void exit_irq (void)
{
	int i, bit;
	
	GUI_BEGIN_CRITICAL_SECTION

	if (ipl == 5 && tbit) {				/* if we are exiting an INT_RUN interrupt, clear it for the next instruction */
		CLRBIT(cpu_dsw, CPU_DSW_INT_RUN);
		if ((cpu_dsw & CPU_DSW_PROGRAM_STOP) == 0)
			CLRBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP);
	}

	ipl = -1;							/* default: return to main processor level */
	int_mask = 0xFFFF;

	if (iplpending) {					/* restore previous interrupt status */
		for (i = 0, bit = 0x20; i < 6; i++, bit >>= 1) {
			if (iplpending & bit) {
				iplpending &= ~bit;
				ipl = i;
				int_mask = int_masks[i];
				break;
			}
		}
	}
	GUI_END_CRITICAL_SECTION

	calc_ints();						/* recompute pending interrupt mask */
}										/* because we probably cleared some ILSW bits before this instruction */

/* let a device halt the simulation */

void break_simulation (t_stat stopreason)
{
	reason = stopreason;
}

/* ------------------------------------------------------------------------ 
 * SIMH required routines
 * ------------------------------------------------------------------------ */

/* ------------------------------------------------------------------------ 
 * Reset routine
 * ------------------------------------------------------------------------ */

t_stat cpu_reset (DEVICE *dptr)
{
	wait_state = 0;						/* cancel wait */
	wait_lamp  = TRUE;					/* but keep the wait lamp lit on the GUI */

	if (cpu_unit.flags & UNIT_ATT) {						/* record reset in CPU log */
		fseek(cpu_unit.fileref, 0, SEEK_END);
		fprintf(cpu_unit.fileref, "---RESET---" CRLF);
	}

	GUI_BEGIN_CRITICAL_SECTION

	CLRBIT(cpu_dsw, CPU_DSW_PROGRAM_STOP|CPU_DSW_INT_RUN);
	CLRBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP);

	reset_backtrace();

	ipl = -1;
	int_mask = 0xFFFF;
	int_req    = 0;						/* hmmm, it SHOULD reset the int req, right? */
	int_lamps  = 0;
	iplpending = 0;
	memset(ILSW, 0, sizeof(ILSW));

	cpu_dsw = 0;						/* clear int req and prot stop bits */
	tbit = 0;							/* cancel INT_RUN mode */

	C = V = 0;							/* clear processor flags */
	IAR = SAR = SBR = 0;				/* clear IAR and other registers */
	ACC = EXT = OP = TAG = CCC = C = V = 0;

	mem_mask = MEMSIZE - 1;				/* wraparound mask */

	GUI_END_CRITICAL_SECTION

	return cpu_svc(&cpu_unit);			/* reset breakpoint */
}

/* ------------------------------------------------------------------------ 
 * Memory examine
 * ------------------------------------------------------------------------ */

t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
	if (vptr == NULL) return SCPE_ARG;

	/* check this out -- save command hits it in weird way */
	/* I wish I remembered what I meant when I wrote that */
	if (addr < MEMSIZE) {
		*vptr = M[addr] & 0xFFFF;
		return SCPE_OK;
	}
	return SCPE_NXM;
}

/* ------------------------------------------------------------------------ 
 * Memory deposit
 * ------------------------------------------------------------------------ */

t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
	if (addr < MEMSIZE) {
		M[addr] = (uint16) (val & 0xFFFF);
		return SCPE_OK;
	}
	return SCPE_NXM;
}

/* ------------------------------------------------------------------------ 
 * Breakpoint service
 * ------------------------------------------------------------------------ */

t_stat cpu_svc (UNIT *uptr)
{
	if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt)
		ibkpt_addr = save_ibkpt;

	save_ibkpt = -1;
	return SCPE_OK;
}

/* ------------------------------------------------------------------------ 
 * Memory allocation
 * ------------------------------------------------------------------------ */

t_stat cpu_set_size (UNIT *uptr, int32 value, char *cptr, void *desc)
{
	t_bool used;
	int32 i;

	if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 0xFFF) != 0))
		return SCPE_ARG;

	for (i = value, used = FALSE; i < (int32) MEMSIZE; i++) {
		if (M[i] != 0) {
			used = TRUE;
			break;
		}
	}

	if (used && ! get_yn ("Really truncate memory [N]?", FALSE))
		return SCPE_OK;

	for (i = MEMSIZE; i < value; i++)  		/* clear expanded area */
		M[i] = 0;

	MEMSIZE = value;
	mem_mask = MEMSIZE - 1;

	return SCPE_OK;
}

/* processor type */

t_stat cpu_set_type (UNIT *uptr, int32 value, char *cptr, void *desc)
{
	REG *r;

	is_1800 = (value & UNIT_1800) != 0;					/* set is_1800 mode flag */

	for (r = cpu_reg; r->name != NULL; r++) {			/* unhide or hide 1800-specific registers & state */
		if (strnicmp(r->name, "XR", 2) == 0) {
			if (value & UNIT_1800)
				CLRBIT(r->flags, REG_HIDDEN|REG_RO);
			else
				SETBIT(r->flags, REG_HIDDEN|REG_RO);
		}
	}

	return SCPE_OK;
}

/* ------------------------------------------------------------------------ 
 * IO function for console switches
 * ------------------------------------------------------------------------ */

void xio_1131_switches (int32 addr, int32 func, int32 modify)
{
	char msg[80];

	switch (func) {
		case XIO_READ:
			WriteW(addr, CES);
			break;

		case XIO_SENSE_DEV:
			ACC = cpu_dsw;
			break;

		default:
			sprintf(msg, "Invalid console switch function %x", func);
			xio_error(msg);
	}
}

/* ------------------------------------------------------------------------ 
 * Illegal IO operation.  Not yet sure what the actual CPU does in this case
 * ------------------------------------------------------------------------ */

void xio_error (char *msg)
{
	printf("*** XIO error at %04x: %s\n", prev_IAR, msg);
	if (cgi)									/* if this happens in CGI mode, probably best to halt */
		break_simulation(STOP_CRASH);
}

/* ------------------------------------------------------------------------ 
 * register_cmd - add a command to the extensible command table
 * ------------------------------------------------------------------------ */

t_stat register_cmd (char *name, t_stat (*action)(int32 flag, char *ptr), int arg, char *help)
{
	int i;

	for (i = 0; i < MAX_EXTRA_COMMANDS; i++) {	/* find end of command table */
		if (x_cmds[i].action == action)
			return SCPE_OK;						/* command is already there, just return */
		if (x_cmds[i].name == NULL)
			break;
	}

	if (i >= (MAX_EXTRA_COMMANDS-1)) {			/* no more room (we need room for the NULL) */
		fprintf(stderr, "The command table is full - rebuild the simulator with more free slots\n");
		return SCPE_ARG;
	}

	x_cmds[i].action = action;					/* add new command */
	x_cmds[i].name   = name;
	x_cmds[i].arg    = arg;
	x_cmds[i].help   = help;

	i++;
	x_cmds[i].action = NULL;					/* move the NULL terminator */
	x_cmds[i].name   = NULL;

	return SCPE_OK;
}

#ifdef USE_MY_ECHO_CMD
/* ------------------------------------------------------------------------ 
 * echo_cmd - just echo the command line
 * ------------------------------------------------------------------------ */

static t_stat echo_cmd (int flag, char *cptr)
{
	printf("%s\n", cptr);
	return SCPE_OK;
}
#endif

/* ------------------------------------------------------------------------ 
 * sim_init - initialize simulator upon startup of scp, before reset
 * ------------------------------------------------------------------------ */

void sim_init (void)
{
	sim_gui = ! (sim_switches & SWMASK('G'));	/* -g means no GUI */

	sim_vm_cmd = x_cmds;						/* provide list of additional commands */

#ifdef GUI_SUPPORT
	/* set hook routines for GUI command processing */
	if (sim_gui) {
		sim_vm_read = &read_cmdline;
		sim_vm_post = &update_gui;
	}
#endif

#ifdef ENABLE_BACKTRACE
	/* add the BACKTRACE command */
	register_cmd("BACKTRACE", &backtrace_cmd, 0, "ba{cktrace} {n}          list last n branches/skips/interrupts\n");
#endif

	register_cmd("VIEW",      &view_cmd,      0, "v{iew} filename          view a text file with notepad\n");

#ifdef USE_MY_ECHO_CMD
	register_cmd("ECHO",      &echo_cmd,      0, "echo args...             echo arguments passed to command\n");
#endif
}

/* ------------------------------------------------------------------------ 
 * archive_backtrace - record a jump, skip, branch or whatever
 * ------------------------------------------------------------------------ */

#ifdef ENABLE_BACKTRACE

#define MAXARCHIVE 16

static struct tag_arch {
	int iar;
	char *inst;
} arch[MAXARCHIVE];
int narchived = 0, archind = 0;

static void archive_backtrace (char *inst)
{
	static int prevind;

	if (narchived < MAXARCHIVE)
		narchived++;

	if (narchived > 0 && arch[prevind].iar == prev_IAR)
		return;

	arch[archind].iar  = prev_IAR;
	arch[archind].inst = inst;

	prevind = archind;
	archind = (archind+1) % MAXARCHIVE;
}

static void reset_backtrace (void)
{
	narchived = 0;
	archind = 0;
}

void void_backtrace (int afrom, int ato)
{
	int i;

	afrom &= mem_mask;
	ato   &= mem_mask;

	for (i = 0; i < narchived; i++)
		if (arch[i].iar >= afrom && arch[i].iar <= ato)
			arch[i].inst = "OVERWRITTEN";
}

static void show_backtrace (int nshow)
{
	int n = narchived, i = archind;

	if (n > nshow) n = nshow;

	while (--n >= 0) {
		i = (i > 0) ? (i-1) : (MAXARCHIVE-1);
		printf("from %04x (%s) ", arch[i].iar, arch[i].inst);
	}

	if (narchived)
		putchar('\n');
}

static t_stat backtrace_cmd (int flag, char *cptr)
{
	int n;

	if ((n = atoi(cptr)) <= 0)
		n = 6;

	show_backtrace(n);
	return SCPE_OK;
}
#else

/* stub this for the disk routine */

void void_backtrace (int afrom, int ato)
{
}

#endif

/*************************************************************************************
 * CPU log routines -- attaching a file to the CPU creates a trace of instructions and register values
 *
 * Syntax is WEIRD:
 *
 * attach cpu logfile					log instructions and registers to file "logfile"
 * attach -f cpu cpu.log				log instructions, registers and floating point acc
 * attach -m cpu mapfile logfile		read addresses from "mapfile", log instructions to "logfile"
 * attach -f -m cpu mapfile logfile		same and log floating point stuff too
 *
 * mapfile if specified is a list of symbols and addresses of the form:
 *       symbol hexval
 *
 * e.g.
 * FSIN   082E
 * FARC   09D4
 * FMPY   09A4
 * NORM   0976
 * XMDS   095A
 * START  021A
 *
 * These values are easily obtained from a load map created by
 * XEQ       L
 *
 * The log output is of the form
 *
 *  IAR             ACC  EXT  (flt)    XR1  XR2  XR3 CVI      FAC      OPERATION
 * --------------- ---- ---- -------- ---- ---- ---- --- ------------- -----------------------
 * 002a       002a 1234 5381  0.14222 00b3 0236 3f7e CV   1.04720e+000 4c80 BSC  I  ,0028   
 * 081d PAUSE+000d 1234 5381  0.14222 00b3 0236 3f7e CV   1.04720e+000 7400 MDM  L  00f0,0 (0)   
 * 0820 PAUSE+0010 1234 5381  0.14222 00b3 0236 3f7e CV   1.04720e+000 7201 MDX   2 0001   
 * 0821 PAUSE+0011 1234 5381  0.14222 00b3 0237 3f7e CV   1.04720e+000 6a03 STX   2 0003   
 * 0822 PAUSE+0012 1234 5381  0.14222 00b3 0237 3f7e CV   1.04720e+000 6600 LDX  L2 0231   
 * 0824 PAUSE+0014 1234 5381  0.14222 00b3 0231 3f7e CV   1.04720e+000 4c00 BSC  L  ,0237   
 * 0237 START+001d 1234 5381  0.14222 00b3 0231 3f7e CV   1.04720e+000 4480 BSI  I  ,3fff   
 * 082f FSIN +0001 1234 5381  0.14222 00b3 0231 3f7e CV   1.04720e+000 4356 BSI   3 0056   
 * 3fd5 ILS01+35dd 1234 5381  0.14222 00b3 0231 3f7e CV   1.04720e+000 4c00 BSC  L  ,08de   
 *
 * IAR - instruction address register value, optionally including symbol and offset
 * ACC - accumulator
 * EXT - extension
 * flt - ACC+EXT interpreted as the mantissa of a floating pt number (value 0.5 -> 1)
 * XR* - index registers
 * CVI - carry, overflow and interrupt indicators
 * FAC - floating point accumulator (exponent at 125+XR3, mantissa at 126+XR3 and 127+XR3)
 * OP  - opcode value and crude disassembly
 *
 * flt and FAC are displayed only when the -f flag is specified in the attach command
 * The label and offset and displayed only when the -m flag is specified in the attach command
 *
 * The register values shown are the values BEFORE the instruction is executed.
 *************************************************************************************/

t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw);

typedef struct tag_symentry {
	struct tag_symentry *next;
	int  addr;
	char sym[6];
} SYMENTRY, *PSYMENTRY;

static PSYMENTRY syms = NULL;
static t_bool new_log, log_fac;

static t_stat cpu_attach (UNIT *uptr, char *cptr)
{
	char mapfile[200], buf[200], sym[100];
	int addr;
	PSYMENTRY n, prv, s;
	FILE *fd;

	remove(cptr);							/* delete old log file, if present */
	new_log = TRUE;
	log_fac = sim_switches & SWMASK ('F');	/* display the FAC and the ACC/EXT as fixed point. */

	for (s = syms; s != NULL; s = n) {		/* free any old map entries */
		n = s->next;
		free(s);
	}
	syms = NULL;
		
	if (sim_switches & SWMASK('M')) {		/* use a map file to display relative addresses */
		cptr = get_glyph(cptr, mapfile, 0);
		if (! *mapfile) {
			printf("/m must be followed by a filename\n");
			return SCPE_ARG;
		}
		if ((fd = fopen(mapfile, "r")) == NULL) {
			perror(mapfile);
			return SCPE_OPENERR;
		}

		while (fgets(buf, sizeof(buf), fd) != NULL) {		/* read symbols & addresses, link in descending address order */
			if (sscanf(buf, "%s %x", sym, &addr) != 2)
				continue;
			if (*buf == ';')
				continue;

			for (prv = NULL, s = syms; s != NULL; prv = s, s = s->next) {
				if (s->addr < addr)
					break;
			}

			if ((n = malloc(sizeof(SYMENTRY))) == NULL) {
				printf("out of memory reading map!\n");
				break;
			}

			sym[5] = '\0';
			strcpy(n->sym, sym);
			upcase(n->sym);
			n->addr = addr;

			if (prv == NULL) {
				n->next = syms;
				syms = n;
			}
			else {
				n->next = prv->next;
				prv ->next = n;
			}
		}
		fclose(fd);
	}

	return attach_unit(uptr, quotefix(cptr));			/* fix quotes in filenames & attach */
}

static void trace_instruction (void)
{
	t_value v[2];
	float fac;
	short exp;
	int addr;
	PSYMENTRY s;
	long mant, sign;
	char facstr[20], fltstr[20];

	if ((cpu_unit.flags & UNIT_ATT) == 0)
		return;

	if (new_log) {
		fseek(cpu_unit.fileref, 0, SEEK_END);
		new_log = FALSE;

		fprintf(cpu_unit.fileref, " IAR%s  ACC  EXT %s XR1  XR2  XR3 CVI %sOPERATION" CRLF,
			syms ? "           " : "", log_fac ? " (flt)   " : "", log_fac ? "     FAC      " : "");
		fprintf(cpu_unit.fileref, "----%s ---- ---- %s---- ---- ---- --- %s-----------------------" CRLF,
			syms ? "-----------" : "", log_fac ? "-------- " : "", log_fac ? "------------- " : "");
	}

	if (! log_fac)	
		facstr[0] = fltstr[0] = '\0';
	else {
		mant = ((ACC & 0xFFFF) << 16) | (EXT & 0xFFFF);
		if (mant == 0x80000000) {
			sign = TRUE;
			fac = 1.f;
		}
		else {
			if ((sign = mant & 0x80000000) != 0)
				mant = -mant;
			fac = (float) mant * ((float) 1./ (float) (unsigned long) 0x80000000);
		}
		sprintf(fltstr, "%c%.5f ", sign ? '-' : ' ', fac);

		if (BETWEEN(M[3], 0x300, MEMSIZE-128)) {
			exp  = (short) ((M[M[3]+125] & 0xFF) - 128);
			mant = (M[M[3]+126] << 8) | ((M[M[3]+127] >> 8) & 0xFF);
			if ((sign = (mant & 0x00800000)) != 0)
				mant = (-mant) & 0x00FFFFFF;

			fac = (float) mant * ((float) 1. / (float) 0x00800000);

			if (exp > 30) {
				fac *= (float) (1 << 30);
				exp -= 30;
				while (exp > 0)
					fac *= 2;
			}
			else if (exp > 0)
				fac *= (float) (1 << exp);
			else if (exp < -30) {
				fac /= (float) (1 << 30);
				exp += 30;
				while (exp < 0)
					fac /= 2;
			}
			else if (exp < 0)
				fac /= (float) (1 << -exp);

			sprintf(facstr, "%c%.5e ", sign ? '-' : ' ', fac);
		}
		else
			strcpy(facstr, "             ");
	}

	addr = IAR & 0xFFFF;
	fprintf(cpu_unit.fileref, "%04x ", addr);

	if (syms) {
		for (s = syms; s != NULL; s = s->next)
			if (s->addr <= addr)
				break;
		
		if (s == NULL)
			fprintf(cpu_unit.fileref, "      %04x ", addr);
		else
			fprintf(cpu_unit.fileref, "%-5s+%04x ", s->sym, addr - s->addr);
	}

	fprintf(cpu_unit.fileref, "%04x %04x %s%04x %04x %04x %c%c%c %s",
		ACC & 0xFFFF, EXT & 0xFFFF, fltstr, M[1] & 0xFFFF, M[2] & 0xFFFF, M[3] & 0xFFFF,
		C ? 'C' : ' ', V ? 'V' : ' ', (ipl < 0) ? ' ' : (ipl+'0'), facstr);

	v[0] = M[ IAR    & mem_mask];
	v[1] = M[(IAR+1) & mem_mask];
	fprint_sym(cpu_unit.fileref, IAR & mem_mask, v, NULL, SWMASK('M'));		/* disassemble instruction */

	fputs(CRLF, cpu_unit.fileref);
}

void trace_io (char *fmt, ...)
{
	va_list args;

	if ((cpu_unit.flags & UNIT_ATT) == 0)
		return;

	va_start(args, fmt);							/* get pointer to argument list */
	vfprintf(cpu_unit.fileref, fmt, args);			/* write errors to cpu log file */
	va_end(args);

	fputs(CRLF, cpu_unit.fileref);
}

void trace_both (char *fmt, ...)
{
	va_list args;

	if (cpu_unit.flags & UNIT_ATT) {
		va_start(args, fmt);						/* get pointer to argument list */
		vfprintf(cpu_unit.fileref, fmt, args);
		va_end(args);
		fputs(CRLF, cpu_unit.fileref);
	}

	va_start(args, fmt);							/* get pointer to argument list */
	vfprintf(stdout, fmt, args);
	va_end(args);
	putchar('\n');
}

/* debugging */

void debug_print (char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	vprintf(fmt, args);
	if (cpu_unit.flags & UNIT_ATT)
		vfprintf(cpu_unit.fileref, fmt, args);
	va_end(args);

	if (strchr(fmt, '\n') == NULL) {		/* be sure to emit a newline */
		putchar('\n');
		if (cpu_unit.flags & UNIT_ATT)
			putc('\n', cpu_unit.fileref);
	}
}

#ifdef _WIN32
#include <windows.h>
#endif

/* view_cmd - let user view and/or edit a file (e.g. a printer output file, script, or source deck) */

static t_stat view_cmd (int flag, char *cptr)
{
#ifdef _WIN32
	char cmdline[256];

	sprintf(cmdline, "notepad %s", cptr);
	WinExec(cmdline, SW_SHOWNORMAL);
#endif
	return SCPE_OK;
}

/* web server version - hooks for CGI mode. These function pointer can be set by the CGI version's main() routine */

void (*cgi_start_hook)(void) = NULL;			/* these can be defined by a CGI wrapper to do things on start and stop of simulation */
void (*cgi_end_hook)(void)   = NULL;

static void cgi_start (void)
{
	if (cgi_start_hook != NULL)
		(*cgi_start_hook)();
}

static void cgi_stop (t_stat reason)
{
	if (cgi_end_hook != NULL)
		(*cgi_end_hook)();
}
