/* nova_sys.c: NOVA simulator interface

   Copyright (c) 1993-2002, Robert M. Supnik

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the name of Robert M Supnik shall not
   be used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   17-Sep-01	RMS	Removed multiconsole support
   31-May-01	RMS	Added multiconsole support
   14-Mar-01	RMS	Revised load/dump interface (again)
   22-Dec-00	RMS	Added second terminal support
   10-Dec-00	RMS	Added Eclipse support
   08-Dec-00	BKR	Added plotter support
   30-Oct-00	RMS	Added support for examine to file
   15-Oct-00	RMS	Added stack, byte, trap instructions
   14-Apr-99	RMS	Changed t_addr to unsigned
   27-Oct-98	RMS	V2.4 load interface
   24-Sep-97	RMS	Fixed bug in device name table (found by Charles Owen)
*/

#include "nova_defs.h"
#include <ctype.h>

extern DEVICE cpu_dev;
extern UNIT cpu_unit;
#if defined (ECLIPSE)
extern DEVICE map_dev;
#endif
extern DEVICE ptr_dev, ptp_dev;
extern DEVICE plt_dev;
extern DEVICE tti_dev, tto_dev;
extern DEVICE tti1_dev, tto1_dev;
extern DEVICE clk_dev, lpt_dev;
extern DEVICE dkp_dev, dsk_dev;
extern DEVICE mta_dev;
extern UNIT tti_unit, tto_unit;
extern UNIT tti1_unit, tto1_unit;
extern REG cpu_reg[];
extern uint16 M[];
extern int32 saved_PC;

/* SCP data structures

   sim_name		simulator name string
   sim_PC		pointer to saved PC register descriptor
   sim_emax		number of words needed for examine
   sim_devices		array of pointers to simulated devices
   sim_stop_messages	array of pointers to stop messages
   sim_load		binary loader
*/

#if defined (ECLIPSE)
char sim_name[] = "ECLIPSE";
#else
char sim_name[] = "NOVA";
#endif

REG *sim_PC = &cpu_reg[0];

int32 sim_emax = 4;

DEVICE *sim_devices[] = {
	&cpu_dev,
#if defined (ECLIPSE)
	&map_dev,
#endif
	&ptr_dev, &ptp_dev,
	&tti_dev, &tto_dev,
	&tti1_dev, &tto1_dev,
	&clk_dev, &plt_dev,
	&lpt_dev, &dsk_dev,
	&dkp_dev, &mta_dev,
	NULL };

const char *sim_stop_messages[] = {
	"Unknown error",
	"Unknown I/O instruction",
	"HALT instruction",
	"Breakpoint",
	"Nested indirect address limit exceeded",
	"Nested indirect interrupt address limit exceeded",
	"Nested indirect trap address limit exceeded",
	"Read breakpoint",
	"Write breakpoint"  };

/* Binary loader

   Loader format consists of blocks, optionally preceded, separated, and
   followed by zeroes.  Each block consists of:

	lo_count
	hi_count
	lo_origin
	hi_origin
	lo_checksum
	hi_checksum
	lo_data byte	---
	hi_data byte	 |
	:		 > -count words
	lo_data byte	 |
	hi_data byte	---

   If the word count is [0,-20], then the block is normal data.
   If the word count is [-21,-n], then the block is repeated data.
   If the word count is 1, the block is the start address.
   If the word count is >1, the block is an error block.
*/

t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag)
{
int32 data, csum, count, state, i;
t_addr origin;

if ((*cptr != 0) || (flag != 0)) return SCPE_ARG;
state = 0;
while ((i = getc (fileref)) != EOF) {
	switch (state) {
	case 0:						/* leader */
		count = i;
		state = (count != 0);
		break;
	case 1:						/* high count */
		csum = count = (i << 8) | count;
		state = 2;
		break;
	case 2:						/* low origin */
		origin = i;
		state = 3;
		break;
	case 3:						/* high origin */
		origin = (i << 8) | origin;
		csum = csum + origin;
		state = 4;
		break;
	case 4:						/* low checksum */
		csum = csum + i;
		state = 5;
		break;
	case 5:						/* high checksum */
		csum = csum + (i << 8);
		if (count == 1) saved_PC = origin;	/* count = 1? */
		if (count <= 1) {			/* count = 0/1? */
			if (csum & 0177777) return SCPE_CSUM;
			state = 0;
			break;  }
		if (count < 0100000) {			/* count > 1 */
			state = 8;
			break;  }
		count = 0200000 - count;
		state = 6;
		break;
	case 6:						/* low data  */
		data = i;
		state = 7;
		break;
	case 7:						/* high data */
		data = (i << 8) | data;
		csum = csum + data;
		if (count > 20) {			/* large block */
			for (count = count - 1; count == 1; count--) {
				if (origin >= MEMSIZE) return SCPE_NXM;
				M[origin] = data;
				origin = origin + 1;  }  }
		if (origin >= MEMSIZE) return SCPE_NXM;
		M[origin] = data;
		origin = origin + 1;
		count = count - 1;
		if (count == 0) {
			if (csum & 0177777) return SCPE_CSUM;
			state = 0;
			break;  }
		state = 6;
		break;
	case 8:						/* error block */
		if (i == 0377) state = 0;
		break;  }				/* end switch */
	}						/* end while */

/* Ok to find end of tape between blocks or in error state */

return ((state == 0) || (state == 8))? SCPE_OK: SCPE_FMT;
}

/* Symbol tables */

#define I_V_FL		18				/* flag bits */
#define I_M_FL		037				/* flag width */
#define I_V_NPN		000				/* no operands */
#define I_V_R		001				/* reg */
#define I_V_D		002				/* device */
#define I_V_RD		003				/* reg,device */
#define I_V_M		004				/* mem addr */
#define I_V_RM		005				/* reg,mem addr */
#define I_V_RR		006				/* operate */
#define I_V_BY		007				/* Nova byte pointer */
#define I_V_2AC		010				/* reg,reg */
#define I_V_RSI		011				/* reg,short imm */
#define I_V_LI		012				/* long imm */
#define I_V_RLI		013				/* reg,long imm */
#define I_V_LM		014				/* long mem addr */
#define I_V_RLM		015				/* reg,long mem addr */
#define I_V_FRM		016				/* flt reg,long mem addr */
#define I_V_FST		017				/* flt long mem, status */
#define I_V_XP		020				/* XOP */
#define I_NPN		(I_V_NPN << I_V_FL)
#define I_R		(I_V_R << I_V_FL)
#define I_D		(I_V_D << I_V_FL)
#define I_RD		(I_V_RD << I_V_FL)
#define I_M		(I_V_M << I_V_FL)
#define I_RM		(I_V_RM << I_V_FL)
#define I_RR		(I_V_RR << I_V_FL)
#define I_BY		(I_V_BY << I_V_FL)
#define I_2AC		(I_V_2AC << I_V_FL)
#define I_RSI		(I_V_RSI << I_V_FL)
#define I_LI		(I_V_LI << I_V_FL)
#define I_RLI		(I_V_RLI << I_V_FL)
#define I_LM		(I_V_LM << I_V_FL)
#define I_RLM		(I_V_RLM << I_V_FL)
#define I_FRM		(I_V_FRM << I_V_FL)
#define I_FST		(I_V_FST << I_V_FL)
#define I_XP		(I_V_XP << I_V_FL)

static const int32 masks[] = {
0177777, 0163777, 0177700, 0163700,
0174000, 0160000, 0103770, 0163477,
0103777, 0103777, 0177777, 0163777,
0176377, 0162377, 0103777, 0163777,
0100077  };

static const char *opcode[] = {
 "JMP", "JSR", "ISZ", "DSZ",
 "LDA", "STA",
#if defined (ECLIPSE)
 "ADI", "SBI", "DAD", "DSB",
 "IOR", "XOR", "ANC", "XCH",
 "SGT", "SGE", "LSH", "DLSH",
 "HXL", "HXR", "DHXL", "DHXR",
 "BTO", "BTZ", "SBZ", "SZBO",
 "LOB", "LRB", "COB", "LDB",
 "STB", "PSH", "POP",
 "LMP", "SYSC",
 "PSHR", "POPB", "BAM", "POPJ",
         "RTN", "BLM", "DIVX",
 "MUL", "MULS", "DIV", "DIVS",
 "SAVE", "RSTR",
 "XOP",
 "FAS", "FAD", "FSS", "FSD",
 "FMS", "FMD", "FDS", "FDD",
 "FAMS", "FAMD", "FSMS", "FSMD",
 "FMMS", "FMMD", "FDMS", "FDMD",
 "FLDS", "FLDD", "FSTS", "FSTD",
 "FLAS", "FLMD", "FFAS", "FFMD",
 "FNOM", "FRH", "FAB", "FNEG",
 "FSCAL", "FEXP", "FINT", "FHLV",
 "FNS", "FSA", "FSEQ", "FSNE",
 "FSLT", "FSGE", "FSLE", "FSGT",
 "FSNM", "FSND", "FSNU", "FSNUD",
 "FSNO", "FSNOD", "FSNUO", "FSNER",
 "FSST", "FLST",
 "FTE", "FTD", "FCLE",
 "FPSH", "FPOP",
 "FCMP", "FMOV",
 "CMV", "CMP", "CTR", "CMT",
 "EJMP", "EJSR", "EISZ", "EDSZ",
         "ELDA", "ESTA", "ELEF",
 "ELDB", "ESTB", "DSPA",
 "PSHJ", "CLM", "SNB",
 "MSP", "XCT", "HLV",
 "IORI", "XORI", "ANDI", "ADDI",
#endif
 "COM", "COMZ", "COMO", "COMC",
 "COML", "COMZL", "COMOL", "COMCL",
 "COMR", "COMZR", "COMOR", "COMCR",
 "COMS", "COMZS", "COMOS", "COMCS",
 "COM#", "COMZ#", "COMO#", "COMC#",
 "COML#", "COMZL#", "COMOL#", "COMCL#",
 "COMR#", "COMZR#", "COMOR#", "COMCR#",
 "COMS#", "COMZS#", "COMOS#", "COMCS#",
 "NEG", "NEGZ", "NEGO", "NEGC",
 "NEGL", "NEGZL", "NEGOL", "NEGCL",
 "NEGR", "NEGZR", "NEGOR", "NEGCR",
 "NEGS", "NEGZS", "NEGOS", "NEGCS",
 "NEG#", "NEGZ#", "NEGO#", "NEGC#",
 "NEGL#", "NEGZL#", "NEGOL#", "NEGCL#",
 "NEGR#", "NEGZR#", "NEGOR#", "NEGCR#",
 "NEGS#", "NEGZS#", "NEGOS#", "NEGCS#",
 "MOV", "MOVZ", "MOVO", "MOVC",
 "MOVL", "MOVZL", "MOVOL", "MOVCL",
 "MOVR", "MOVZR", "MOVOR", "MOVCR",
 "MOVS", "MOVZS", "MOVOS", "MOVCS",
 "MOV#", "MOVZ#", "MOVO#", "MOVC#",
 "MOVL#", "MOVZL#", "MOVOL#", "MOVCL#",
 "MOVR#", "MOVZR#", "MOVOR#", "MOVCR#",
 "MOVS#", "MOVZS#", "MOVOS#", "MOVCS#",
 "INC", "INCZ", "INCO", "INCC",
 "INCL", "INCZL", "INCOL", "INCCL",
 "INCR", "INCZR", "INCOR", "INCCR",
 "INCS", "INCZS", "INCOS", "INCCS",
 "INC#", "INCZ#", "INCO#", "INCC#",
 "INCL#", "INCZL#", "INCOL#", "INCCL#",
 "INCR#", "INCZR#", "INCOR#", "INCCR#",
 "INCS#", "INCZS#", "INCOS#", "INCCS#",
 "ADC", "ADCZ", "ADCO", "ADCC",
 "ADCL", "ADCZL", "ADCOL", "ADCCL",
 "ADCR", "ADCZR", "ADCOR", "ADCCR",
 "ADCS", "ADCZS", "ADCOS", "ADCCS",
 "ADC#", "ADCZ#", "ADCO#", "ADCC#",
 "ADCL#", "ADCZL#", "ADCOL#", "ADCCL#",
 "ADCR#", "ADCZR#", "ADCOR#", "ADCCR#",
 "ADCS#", "ADCZS#", "ADCOS#", "ADCCS#",
 "SUB", "SUBZ", "SUBO", "SUBC",
 "SUBL", "SUBZL", "SUBOL", "SUBCL",
 "SUBR", "SUBZR", "SUBOR", "SUBCR",
 "SUBS", "SUBZS", "SUBOS", "SUBCS",
 "SUB#", "SUBZ#", "SUBO#", "SUBC#",
 "SUBL#", "SUBZL#", "SUBOL#", "SUBCL#",
 "SUBR#", "SUBZR#", "SUBOR#", "SUBCR#",
 "SUBS#", "SUBZS#", "SUBOS#", "SUBCS#",
 "ADD", "ADDZ", "ADDO", "ADDC",
 "ADDL", "ADDZL", "ADDOL", "ADDCL",
 "ADDR", "ADDZR", "ADDOR", "ADDCR",
 "ADDS", "ADDZS", "ADDOS", "ADDCS",
 "ADD#", "ADDZ#", "ADDO#", "ADDC#",
 "ADDL#", "ADDZL#", "ADDOL#", "ADDCL#",
 "ADDR#", "ADDZR#", "ADDOR#", "ADDCR#",
 "ADDS#", "ADDZS#", "ADDOS#", "ADDCS#",
 "AND", "ANDZ", "ANDO", "ANDC",
 "ANDL", "ANDZL", "ANDOL", "ANDCL",
 "ANDR", "ANDZR", "ANDOR", "ANDCR",
 "ANDS", "ANDZS", "ANDOS", "ANDCS",
 "AND#", "ANDZ#", "ANDO#", "ANDC#",
 "ANDL#", "ANDZL#", "ANDOL#", "ANDCL#",
 "ANDR#", "ANDZR#", "ANDOR#", "ANDCR#",
 "ANDS#", "ANDZS#", "ANDOS#", "ANDCS#",
 "ION", "IOF",
 "RDSW", "INTA", "MSKO", "IORST", "HALT",
#if !defined (ECLIPSE)
 "MUL", "DIV", "MULS", "DIVS",
 "PSHA", "POPA", "SAV", "RET",
 "MTSP", "MTFP", "MFSP", "MFFP",
 "LDB", "STB",
#endif
 "NIO", "NIOS", "NIOC", "NIOP",
 "DIA", "DIAS", "DIAC", "DIAP",
 "DOA", "DOAS", "DOAC", "DOAP",
 "DIB", "DIBS", "DIBC", "DIBP",
 "DOB", "DOBS", "DOBC", "DOBP",
 "DIC", "DICS", "DICC", "DICP",
 "DOC", "DOCS", "DOCC", "DOCP",
 "SKPBN", "SKPBZ", "SKPDN", "SKPDZ",
 NULL };

static const opc_val[] = {
 0000000+I_M, 0004000+I_M, 0010000+I_M, 0014000+I_M,
 0020000+I_RM, 0040000+I_RM,
#if defined (ECLIPSE)
 0100010+I_RSI, 0100110+I_RSI, 0100210+I_2AC, 0100310+I_2AC,
 0100410+I_2AC, 0100510+I_2AC, 0100610+I_2AC, 0100710+I_2AC,
 0101010+I_2AC, 0101110+I_2AC, 0101210+I_RSI, 0101310+I_RSI,
 0101410+I_RSI, 0101510+I_RSI, 0101610+I_RSI, 0101710+I_RSI,
 0102010+I_2AC, 0102110+I_2AC, 0102210+I_2AC, 0102310+I_2AC,
 0102410+I_2AC, 0102510+I_2AC, 0102610+I_2AC, 0102710+I_2AC,
 0103010+I_2AC, 0103110+I_2AC, 0103210+I_2AC,
 0113410+I_NPN, 0103510+I_RSI,
 0103710+I_NPN, 0107710+I_NPN, 0113710+I_NPN, 0117710+I_NPN,
                0127710+I_NPN, 0133710+I_NPN, 0137710+I_NPN,
 0143710+I_NPN, 0147710+I_NPN, 0153710+I_NPN, 0157710+I_NPN,
 0163710+I_LI, 0167710+I_NPN,
 0100030+I_XP,
 0100050+I_2AC, 0100150+I_2AC, 0100250+I_2AC, 0100350+I_2AC,
 0100450+I_2AC, 0100550+I_2AC, 0100650+I_2AC, 0100750+I_2AC,
 0101050+I_FRM, 0101150+I_FRM, 0101250+I_FRM, 0101350+I_FRM,
 0101450+I_FRM, 0101550+I_FRM, 0101650+I_FRM, 0101750+I_FRM,
 0102050+I_FRM, 0102150+I_FRM, 0102250+I_FRM, 0102350+I_FRM,
 0102450+I_2AC, 0102550+I_FRM, 0102650+I_2AC, 0102750+I_FRM,
 0103050+I_R, 0123050+I_R, 0143050+I_R, 0163050+I_R,
 0103150+I_R, 0123150+I_R, 0143150+I_R, 0163150+I_R,
 0103250+I_NPN, 0107250+I_NPN, 0113250+I_NPN, 0117250+I_NPN,
 0123250+I_NPN, 0127250+I_NPN, 0133250+I_NPN, 0137250+I_NPN,
 0143250+I_NPN, 0147250+I_NPN, 0153250+I_NPN, 0157250+I_NPN,
 0163250+I_NPN, 0167250+I_NPN, 0173250+I_NPN, 0177250+I_NPN,
 0103350+I_FST, 0123350+I_FST,
 0143350+I_NPN, 0147350+I_NPN, 0153350+I_NPN,
 0163350+I_NPN, 0167350+I_NPN,
 0103450+I_2AC, 0103550+I_2AC,
 0153650+I_NPN, 0157650+I_NPN, 0163650+I_NPN, 0167650+I_NPN,
 0102070+I_LM, 0106070+I_LM, 0112070+I_LM, 0116070+I_LM,
                0122070+I_RLM, 0142070+I_RLM, 0162070+I_RLM,
 0102170+I_RLM, 0122170+I_RLM, 0142170+I_RLM,
 0102270+I_LM,  0102370+I_2AC, 0102770+I_2AC,
 0103370+I_R, 0123370+I_R, 0143370+I_R,
 0103770+I_RLI, 0123770+I_RLI, 0143770+I_RLI, 0163770+I_RLI, 
#endif
 0100000+I_RR, 0100020+I_RR, 0100040+I_RR, 0100060+I_RR,
 0100100+I_RR, 0100120+I_RR, 0100140+I_RR, 0100160+I_RR,
 0100200+I_RR, 0100220+I_RR, 0100240+I_RR, 0100260+I_RR,
 0100300+I_RR, 0100320+I_RR, 0100340+I_RR, 0100360+I_RR,
 0100010+I_RR, 0100030+I_RR, 0100050+I_RR, 0100070+I_RR,
 0100110+I_RR, 0100130+I_RR, 0100150+I_RR, 0100170+I_RR,
 0100210+I_RR, 0100230+I_RR, 0100250+I_RR, 0100270+I_RR,
 0100310+I_RR, 0100330+I_RR, 0100350+I_RR, 0100370+I_RR,
 0100400+I_RR, 0100420+I_RR, 0100440+I_RR, 0100460+I_RR,
 0100500+I_RR, 0100520+I_RR, 0100540+I_RR, 0100560+I_RR,
 0100600+I_RR, 0100620+I_RR, 0100640+I_RR, 0100660+I_RR,
 0100700+I_RR, 0100720+I_RR, 0100740+I_RR, 0100760+I_RR,
 0100410+I_RR, 0100430+I_RR, 0100450+I_RR, 0100470+I_RR,
 0100510+I_RR, 0100530+I_RR, 0100550+I_RR, 0100570+I_RR,
 0100610+I_RR, 0100630+I_RR, 0100650+I_RR, 0100670+I_RR,
 0100710+I_RR, 0100730+I_RR, 0100750+I_RR, 0100770+I_RR,
 0101000+I_RR, 0101020+I_RR, 0101040+I_RR, 0101060+I_RR,
 0101100+I_RR, 0101120+I_RR, 0101140+I_RR, 0101160+I_RR,
 0101200+I_RR, 0101220+I_RR, 0101240+I_RR, 0101260+I_RR,
 0101300+I_RR, 0101320+I_RR, 0101340+I_RR, 0101360+I_RR,
 0101010+I_RR, 0101030+I_RR, 0101050+I_RR, 0101070+I_RR,
 0101110+I_RR, 0101130+I_RR, 0101150+I_RR, 0101170+I_RR,
 0101210+I_RR, 0101230+I_RR, 0101250+I_RR, 0101270+I_RR,
 0101310+I_RR, 0101330+I_RR, 0101350+I_RR, 0101370+I_RR,
 0101400+I_RR, 0101420+I_RR, 0101440+I_RR, 0101460+I_RR,
 0101500+I_RR, 0101520+I_RR, 0101540+I_RR, 0101560+I_RR,
 0101600+I_RR, 0101620+I_RR, 0101640+I_RR, 0101660+I_RR,
 0101700+I_RR, 0101720+I_RR, 0101740+I_RR, 0101760+I_RR,
 0101410+I_RR, 0101430+I_RR, 0101450+I_RR, 0101470+I_RR,
 0101510+I_RR, 0101530+I_RR, 0101550+I_RR, 0101570+I_RR,
 0101610+I_RR, 0101630+I_RR, 0101650+I_RR, 0101670+I_RR,
 0101710+I_RR, 0101730+I_RR, 0101750+I_RR, 0101770+I_RR,
 0102000+I_RR, 0102020+I_RR, 0102040+I_RR, 0102060+I_RR,
 0102100+I_RR, 0102120+I_RR, 0102140+I_RR, 0102160+I_RR,
 0102200+I_RR, 0102220+I_RR, 0102240+I_RR, 0102260+I_RR,
 0102300+I_RR, 0102320+I_RR, 0102340+I_RR, 0102360+I_RR,
 0102010+I_RR, 0102030+I_RR, 0102050+I_RR, 0102070+I_RR,
 0102110+I_RR, 0102130+I_RR, 0102150+I_RR, 0102170+I_RR,
 0102210+I_RR, 0102230+I_RR, 0102250+I_RR, 0102270+I_RR,
 0102310+I_RR, 0102330+I_RR, 0102350+I_RR, 0102370+I_RR,
 0102400+I_RR, 0102420+I_RR, 0102440+I_RR, 0102460+I_RR,
 0102500+I_RR, 0102520+I_RR, 0102540+I_RR, 0102560+I_RR,
 0102600+I_RR, 0102620+I_RR, 0102640+I_RR, 0102660+I_RR,
 0102700+I_RR, 0102720+I_RR, 0102740+I_RR, 0102760+I_RR,
 0102410+I_RR, 0102430+I_RR, 0102450+I_RR, 0102470+I_RR,
 0102510+I_RR, 0102530+I_RR, 0102550+I_RR, 0102570+I_RR,
 0102610+I_RR, 0102630+I_RR, 0102650+I_RR, 0102670+I_RR,
 0102710+I_RR, 0102730+I_RR, 0102750+I_RR, 0102770+I_RR,
 0103000+I_RR, 0103020+I_RR, 0103040+I_RR, 0103060+I_RR,
 0103100+I_RR, 0103120+I_RR, 0103140+I_RR, 0103160+I_RR,
 0103200+I_RR, 0103220+I_RR, 0103240+I_RR, 0103260+I_RR,
 0103300+I_RR, 0103320+I_RR, 0103340+I_RR, 0103360+I_RR,
 0103010+I_RR, 0103030+I_RR, 0103050+I_RR, 0103070+I_RR,
 0103110+I_RR, 0103130+I_RR, 0103150+I_RR, 0103170+I_RR,
 0103210+I_RR, 0103230+I_RR, 0103250+I_RR, 0103270+I_RR,
 0103310+I_RR, 0103330+I_RR, 0103350+I_RR, 0103370+I_RR,
 0103400+I_RR, 0103420+I_RR, 0103440+I_RR, 0103460+I_RR,
 0103500+I_RR, 0103520+I_RR, 0103540+I_RR, 0103560+I_RR,
 0103600+I_RR, 0103620+I_RR, 0103640+I_RR, 0103660+I_RR,
 0103700+I_RR, 0103720+I_RR, 0103740+I_RR, 0103760+I_RR,
 0103410+I_RR, 0103430+I_RR, 0103450+I_RR, 0103470+I_RR,
 0103510+I_RR, 0103530+I_RR, 0103550+I_RR, 0103570+I_RR,
 0103610+I_RR, 0103630+I_RR, 0103650+I_RR, 0103670+I_RR,
 0103710+I_RR, 0103730+I_RR, 0103750+I_RR, 0103770+I_RR,
 0060177+I_NPN, 0060277+I_NPN,
 0060477+I_R, 0061477+I_R, 0062077+I_R, 0062677+I_NPN, 0063077+I_NPN,
#if !defined (ECLIPSE)
 0073301+I_NPN, 0073101+I_NPN, 0077201+I_NPN, 0077001+I_NPN,
 0061401+I_R, 0061601+I_R, 0062401+I_NPN, 0062601+I_NPN,
 0061001+I_R, 0060001+I_R, 0061201+I_R, 0060201+I_R,
 0060401+I_BY, 0062001+I_BY,
#endif
 0060000+I_D, 0060100+I_D, 0060200+I_D, 0060300+I_D,
 0060400+I_RD, 0060500+I_RD, 0060600+I_RD, 0060700+I_RD,
 0061000+I_RD, 0061100+I_RD, 0061200+I_RD, 0061300+I_RD,
 0061400+I_RD, 0061500+I_RD, 0061600+I_RD, 0061700+I_RD,
 0062000+I_RD, 0062100+I_RD, 0062200+I_RD, 0062300+I_RD,
 0062400+I_RD, 0062500+I_RD, 0062600+I_RD, 0062700+I_RD,
 0063000+I_RD, 0063100+I_RD, 0063200+I_RD, 0063300+I_RD,
 0063400+I_D, 0063500+I_D, 0063600+I_D, 0063700+I_D,
 -1 };
 
static const char *skip[] = {
 "SKP", "SZC", "SNC", "SZR", "SNR", "SEZ", "SBN",
 NULL };

static const char *device[] = {
#if defined (ECLIPSE)
 "ERCC", "MAP",
#endif
 "TTI", "TTO", "PTR", "PTP", "RTC", "PLT", "CDR", "LPT",
 "DSK", "MTA", "DCM", "ADCV", "DKP", "CAS",
 "TTI1", "TTO1", "CPU",
 NULL };

static const int32 dev_val[] = {
#if defined (ECLIPSE)
 002, 003,
#endif
 010, 011, 012, 013, 014, 015, 016, 017,
 020, 022, 024, 030, 033, 034, 
 050, 051, 077,
 -1 };

/* Address decode

   Inputs:
	*of	=	output stream
	addr	=	current PC
	ind	=	indirect flag
	mode	=	addressing mode
	disp	=	displacement
	ext	=	true if extended address
	cflag	=	true if decoding for CPU
   Outputs:
	return	=	error code
*/

t_stat fprint_addr (FILE *of, t_addr addr, int32 ind, int32 mode,
	int32 disp, t_bool ext, int32 cflag)
{
int32 dsign, dmax;

if (ext) dmax = AMASK + 1;				/* get max disp */
else dmax = I_M_DISP + 1;
dsign = dmax >> 1;					/* get disp sign */
if (ind) fprintf (of, "@");				/* indirect? */
switch (mode & 03) {					/* mode */
case 0:							/* absolute */
	fprintf (of, "%-o", disp);
	break;
case 1:							/* PC rel */
	if (disp & dsign) {
		if (cflag) fprintf (of, "%-o", (addr - (dmax - disp)) & AMASK);
		else fprintf (of, ".-%-o", dmax - disp);  }
	else {	if (cflag) fprintf (of, "%-o", (addr + disp) & AMASK);
		else fprintf (of, ".+%-o", disp);  }
	break;
case 2:							/* AC2 rel */
	if (disp & dsign) fprintf (of, "-%-o,2", dmax - disp);
	else fprintf (of, "%-o,2", disp);
	break;
case 3:							/* AC3 rel */
	if (disp & dsign) fprintf (of, "-%-o,3", dmax - disp);
	else fprintf (of, "%-o,3", disp);
	break;  }					/* end switch */
return SCPE_OK;
}

/* Symbolic output

   Inputs:
	*of	=	output stream
	addr	=	current PC
	*val	=	pointer to values
	*uptr	=	pointer to unit
	sw	=	switches
   Outputs:
	status	=	error code
*/

t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
	UNIT *uptr, int32 sw)
{
int32 cflag, i, j, c1, c2, inst, dv, src, dst, skp;
int32 ind, mode, disp, dev;
int32 byac, extind, extdisp, xop;

cflag = (uptr == NULL) || (uptr == &cpu_unit);
c1 = (val[0] >> 8) & 0177;
c2 = val[0] & 0177;
if (sw & SWMASK ('A')) {				/* ASCII? */
	fprintf (of, (c2 < 040)? "<%03o>": "%c", c2);
	return SCPE_OK;  }
if (sw & SWMASK ('C')) {				/* character? */
	fprintf (of, (c1 < 040)? "<%03o>": "%c", c1);
	fprintf (of, (c2 < 040)? "<%03o>": "%c", c2);
	return SCPE_OK;  }
if (!(sw & SWMASK ('M'))) return SCPE_ARG;		/* mnemonic? */

/* Instruction decode */

inst = val[0];
for (i = 0; opc_val[i] >= 0; i++) {			/* loop thru ops */
    j = (opc_val[i] >> I_V_FL) & I_M_FL;		/* get class */
    if ((opc_val[i] & 0177777) == (inst & masks[j])) {	/* match? */
	src = I_GETSRC (inst);				/* opr fields */
	dst = I_GETDST (inst);
	skp = I_GETSKP (inst);
	ind = inst & I_IND;				/* mem ref fields */
	mode = I_GETMODE (inst);
	disp = I_GETDISP (inst);
	dev = I_GETDEV (inst);				/* IOT fields */
	byac = I_GETPULSE (inst);			/* byte fields */
	xop = I_GETXOP (inst);				/* XOP fields */
	extind = val[1] & A_IND;			/* extended fields */
	extdisp = val[1] & AMASK;
	for (dv = 0; (dev_val[dv] >= 0) && (dev_val[dv] != dev); dv++) ;

	switch (j) {					/* switch on class */
	case I_V_NPN:					/* no operands */
		fprintf (of, "%s", opcode[i]);		/* opcode */
		break;
	case I_V_R:					/* reg only */
		fprintf (of, "%s %-o", opcode[i], dst);
		break;
	case I_V_D:					/* dev only */
		if (dev_val[dv] >= 0)
			fprintf (of, "%s %s", opcode[i], device[dv]);
		else fprintf (of, "%s %-o", opcode[i], dev);
		break;
	case I_V_RD:					/* reg, dev */
		if (dev_val[dv] >= 0)
			fprintf (of, "%s %-o,%s", opcode[i], dst, device[dv]);
		else fprintf (of, "%s %-o,%-o", opcode[i], dst, dev);
		break;
	case I_V_M:					/* addr only */
		fprintf (of, "%s ", opcode[i]);
		fprint_addr (of, addr, ind, mode, disp, FALSE, cflag);
		break;
	case I_V_RM:					/* reg, addr */
		fprintf (of, "%s %-o,", opcode[i], dst);
		fprint_addr (of, addr, ind, mode, disp, FALSE, cflag);
		break;
	case I_V_RR:					/* operate */
		fprintf (of, "%s %-o,%-o", opcode[i], src, dst);
		if (skp) fprintf (of, ",%s", skip[skp-1]);
		break;
	case I_V_BY:					/* byte */
		fprintf (of, "%s %-o,%-o", opcode[i], byac, dst);
		break;
	case I_V_2AC:					/* reg, reg */
		fprintf (of, "%s %-o,%-o", opcode[i], src, dst);
		break;
	case I_V_RSI:					/* reg, short imm */
		fprintf (of, "%s %-o,%-o", opcode[i], src + 1, dst);
		break;
	case I_V_LI:					/* long imm */
		fprintf (of, "%s %-o", opcode[i], val[1]);
		return -1;
	case I_V_RLI:					/* reg, long imm */
		fprintf (of, "%s %-o,%-o", opcode[i], val[1], dst);
		return -1;
	case I_V_LM:					/* long addr */
		fprintf (of, "%s ", opcode[i]);
		fprint_addr (of, addr, extind, mode, extdisp, TRUE, cflag);
		return -1;
	case I_V_RLM:					/* reg, long addr */
		fprintf (of, "%s %-o,", opcode[i], dst);
		fprint_addr (of, addr, extind, mode, extdisp, TRUE, cflag);
		return -1;
	case I_V_FRM:					/* flt reg, long addr */
		fprintf (of, "%s %-o,", opcode[i], dst);
		fprint_addr (of, addr, extind, src, extdisp, TRUE, cflag);
		return -1;
	case I_V_FST:					/* flt status */
		fprintf (of, "%s ", opcode[i]);
		fprint_addr (of, addr, extind, dst, extdisp, AMASK + 1, cflag);
		return -1;
	case I_V_XP:					/* XOP */
		fprintf (of, "%s %-o,%-o,%-o", opcode[i], src, dst, xop);
		break;  }				/* end case */
	return SCPE_OK;  }				/* end if */
	}						/* end for */
return SCPE_ARG;
}

/* Address parse

   Inputs:
	*cptr	=	pointer to input string
	addr	=	current PC
	ext	=	extended address
	cflag	=	true if parsing for CPU
	val[3]	=	output array
   Outputs:
	optr	=	pointer to next char in input string
			NULL if error
*/

#define A_FL	001					/* CPU flag */
#define A_NX	002					/* index seen */
#define A_PER	004					/* period seen */
#define A_NUM	010					/* number seen */
#define A_SI	020					/* sign seen */
#define A_MI	040					/* - seen */

char *get_addr (char *cptr, t_addr addr, t_bool ext, int32 cflag, int32 *val)
{
int32 d, r, x, pflag;
char gbuf[CBUFSIZE];
int32 dmax, dsign;

if (ext) dmax = AMASK + 1;				/* get max disp */
else dmax = I_M_DISP + 1;
dsign = dmax >> 1;					/* get disp sign */
val[0] = 0;						/* no indirect */
val[1] = 0;						/* PC rel */
val[2] = 0;						/* no addr */

pflag = cflag & A_FL;					/* isolate flag */
if (*cptr == '@') {					/* indirect? */
	val[0] = 1;
	cptr++;  }		
if (*cptr == '.') {					/* relative? */
	pflag = pflag | A_PER;
	x = 1;						/* "index" is PC */
	cptr++;  }
if (*cptr == '+') {					/* + sign? */
	pflag = pflag | A_SI;
	cptr++;  }
else if (*cptr == '-') {				/* - sign? */
	pflag = pflag | A_MI | A_SI;
	cptr++;  }	
if (*cptr != 0) {					/* number? */
	cptr = get_glyph (cptr, gbuf, ',');		/* get glyph */
	d = get_uint (gbuf, 8, AMASK, &r);
	if (r != SCPE_OK) return NULL;
	pflag = pflag | A_NUM;  }
if (*cptr != 0) {					/* index? */
	cptr = get_glyph (cptr, gbuf, 0);		/* get glyph */
	x = get_uint (gbuf, 8, I_M_DST, &r);
	if ((r != SCPE_OK) || (x < 2)) return NULL;
	pflag = pflag | A_NX;  }

/* Address parse, continued */

switch (pflag) {					/* case on flags */
case A_NUM: case A_NUM+A_SI:				/* ~CPU, (+)num */
	if (d < dmax) val[2] = d;
	else return NULL;
	break;
case A_NUM+A_FL: case A_NUM+A_SI+A_FL:			/* CPU, (+)num */
	if (d < dmax) val[2] = d;
	else if (((d >= (((int32) addr - dsign) & AMASK)) &&
		  (d < (((int32) addr + dsign) & AMASK))) ||
		  (d >= ((int32) addr + (-dsign & AMASK)))) {
		val[1] = 1;				/* PC rel */
		val[2] = (d - addr) & (dmax - 1);  }
	else return NULL;
	break;
case A_PER: case A_PER+A_FL:				/* . */
case A_PER+A_SI+A_NUM: case A_PER+A_SI+A_NUM+A_FL:	/* . + num */
case A_PER+A_SI+A_MI+A_NUM:				/* . - num */
case A_PER+A_SI+A_MI+A_NUM+A_FL:
case A_NX+A_NUM: case A_NX+A_NUM+A_FL:			/* num, ndx */
case A_NX+A_SI+A_NUM: case A_NX+A_SI+A_NUM+A_FL:	/* +num, ndx */
case A_NX+A_SI+A_MI+A_NUM:				/* -num, ndx */
case A_NX+A_SI+A_MI+A_NUM+A_FL:
	val[1] = x;					/* set mode */
	if (((pflag & A_MI) == 0) && (d < dsign)) val[2] = d;
	else if ((pflag & A_MI) && (d <= dsign)) val[2] = (dmax - d);
	else return NULL;
	break;
default:
	return NULL;  }					/* end case */
return cptr;
}

/* Parse two registers 

   Inputs:
	*cptr	=	input string
	term	=	second terminating character
	val	=	output array
   Outputs:
	optr	=	pointer to next char in input string
			NULL if error
*/

char *get_2reg (char *cptr, char term, int32 *val)
{
char gbuf[CBUFSIZE];
t_stat r;

cptr = get_glyph (cptr, gbuf, ',');			/* get register */
val[0] = get_uint (gbuf, 8, I_M_SRC, &r);
if (r != SCPE_OK) return NULL;
cptr = get_glyph (cptr, gbuf, term);			/* get register */
val[1] = get_uint (gbuf, 8, I_M_DST, &r);
if (r != SCPE_OK) return NULL;
return cptr;
}

/* Symbolic input

   Inputs:
	*cptr	=	pointer to input string
	addr	=	current PC
	*uptr	=	pointer to unit
	*val	=	pointer to output values
	sw	=	switches
   Outputs:
	status	=	error status
*/

t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
{
int32 cflag, d, i, j, amd[3];
t_stat r, rtn;
char gbuf[CBUFSIZE];

cflag = (uptr == NULL) || (uptr == &cpu_unit);
while (isspace (*cptr)) cptr++;				/* absorb spaces */
if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */
	if (cptr[0] == 0) return SCPE_ARG;		/* must have 1 char */
	val[0] = (t_value) cptr[0];
	return SCPE_OK;  }
if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */
	if (cptr[0] == 0) return SCPE_ARG;		/* must have 1 char */
	val[0] = ((t_value) cptr[0] << 8) + (t_value) cptr[1];
	return SCPE_OK;  }

/* Instruction parse */

rtn = SCPE_OK;						/* assume 1 word */
cptr = get_glyph (cptr, gbuf, 0);			/* get opcode */
for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ;
if (opcode[i] == NULL) return SCPE_ARG;
val[0] = opc_val[i] & 0177777;				/* get value */
j = (opc_val[i] >> I_V_FL) & I_M_FL;			/* get class */

switch (j) {						/* case on class */
case I_V_NPN:						/* no operand */
	break;
case I_V_R:						/* IOT reg */
	cptr = get_glyph (cptr, gbuf, 0);		/* get register */
	d = get_uint (gbuf, 8, I_M_DST, &r);
	if (r != SCPE_OK) return SCPE_ARG;
	val[0] = val[0] | (d << I_V_DST);		/* put in place */
	break;
case I_V_RD:						/* IOT reg,dev */
	cptr = get_glyph (cptr, gbuf, ',');		/* get register */
	d = get_uint (gbuf, 8, I_M_DST, &r);
	if (r != SCPE_OK) return SCPE_ARG;
	val[0] = val[0] | (d << I_V_DST);		/* put in place */
case I_V_D:						/* IOT dev */
	cptr = get_glyph (cptr, gbuf, 0);		/* get device */
	for (i = 0; (device[i] != NULL) && (strcmp (device[i], gbuf) != 0);
		i++);
	if (device[i] != NULL) val[0] = val[0] | dev_val[i];
	else {	d = get_uint (gbuf, 8, I_M_DEV, &r);
		if (r != SCPE_OK) return SCPE_ARG;
		val[0] = val[0] | (d << I_V_DEV);  }
	break;
case I_V_RM:						/* reg, addr */
	cptr = get_glyph (cptr, gbuf, ',');		/* get register */
	d = get_uint (gbuf, 8, I_M_DST, &r);
	if (r != SCPE_OK) return SCPE_ARG;
	val[0] = val[0] | (d << I_V_DST);		/* put in place */
case I_V_M:						/* addr */
	cptr = get_addr (cptr, addr, FALSE, cflag, amd);
	if (cptr == NULL) return SCPE_ARG;
	val[0] = val[0] | (amd[0] << I_V_IND) | (amd[1] << I_V_MODE) | amd[2];
	break;
case I_V_RR:						/* operate */
	cptr = get_2reg (cptr, ',', amd);		/* get 2 reg */
	if (cptr == NULL) return SCPE_ARG;
	val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST);
	if (*cptr != 0) {				/* skip? */
		cptr = get_glyph (cptr, gbuf, 0);	/* get skip */
		for (i = 0; (skip[i] != NULL) &&
			(strcmp (skip[i], gbuf) != 0); i++) ;
		if (skip[i] == NULL) return SCPE_ARG;
		val[0] = val[0] | (i + 1);  }		/* end for */
	break;
case I_V_BY:						/* byte */
	cptr = get_2reg (cptr, 0, amd);			/* get 2 reg */
	if (cptr == NULL) return SCPE_ARG;
	val[0] = val[0] | (amd[0] << I_V_PULSE) | (amd[1] << I_V_DST);
	break;
case I_V_2AC:						/* reg, reg */
	cptr = get_2reg (cptr, 0, amd);			/* get 2 reg */
	if (cptr == NULL) return SCPE_ARG;
	val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST);
	break;
case I_V_RSI:						/* reg, short imm */
	cptr = get_glyph (cptr, gbuf, ',');		/* get immediate */
	d = get_uint (gbuf, 8, I_M_SRC + 1, &r);
	if ((d == 0) || (r != SCPE_OK)) return SCPE_ARG;
	val[0] = val[0] | ((d - 1) << I_V_SRC);		/* put in place */
	cptr = get_glyph (cptr, gbuf, 0);		/* get register */
	d = get_uint (gbuf, 8, I_M_DST, &r);
	if (r != SCPE_OK) return SCPE_ARG;
	val[0] = val[0] | (d << I_V_DST);		/* put in place */
	break;
case I_V_RLI:						/* reg, long imm */
	cptr = get_glyph (cptr, gbuf, ',');		/* get immediate */
	val[1] = get_uint (gbuf, 8, DMASK, &r);
	if (r != SCPE_OK) return SCPE_ARG;
	cptr = get_glyph (cptr, gbuf, 0);		/* get register */
	d = get_uint (gbuf, 8, I_M_DST, &r);
	if (r != SCPE_OK) return SCPE_ARG;
	val[0] = val[0] | (d << I_V_DST);		/* put in place */
	rtn = -1;
	break;
case I_V_LI:						/* long imm */
	cptr = get_glyph (cptr, gbuf, 0);		/* get immediate */
	val[1] = get_uint (gbuf, 8, DMASK, &r);
	if (r != SCPE_OK) return SCPE_ARG;
	rtn = -1;
	break;
case I_V_RLM:						/* reg, long mem */
	cptr = get_glyph (cptr, gbuf, ',');		/* get register */
	d = get_uint (gbuf, 8, I_M_DST, &r);
	if (r != SCPE_OK) return SCPE_ARG;
	val[0] = val[0] | (d << I_V_DST);		/* put in place */
case I_V_LM:						/* long mem */
	cptr = get_addr (cptr, addr, TRUE, cflag, amd);
	if (cptr == NULL) return SCPE_ARG;
	val[0] = val[0] | (amd[1] << I_V_MODE);
	val[1] = (amd[0] << A_V_IND) | amd[2];
	rtn = -1;
	break;
case I_V_FRM:						/* flt reg, long mem */
	cptr = get_glyph (cptr, gbuf, ',');		/* get register */
	d = get_uint (gbuf, 8, I_M_DST, &r);
	if (r != SCPE_OK) return SCPE_ARG;
	val[0] = val[0] | (d << I_V_DST);		/* put in place */
	cptr = get_addr (cptr, addr, TRUE, cflag, amd);
	if (cptr == NULL) return SCPE_ARG;
	val[0] = val[0] | (amd[1] << I_V_SRC);
	val[1] = (amd[0] << A_V_IND) | amd[2];
	rtn = -1;
	break;
case I_V_FST:						/* flt status */
	cptr = get_addr (cptr, addr, TRUE, cflag, amd);
	if (cptr == NULL) return SCPE_ARG;
	val[0] = val[0] | (amd[1] << I_V_DST);
	val[1] = (amd[0] << A_V_IND) | amd[2];
	rtn = -1;
	break;
case I_V_XP:						/* XOP */
	cptr = get_2reg (cptr, ',', amd);		/* get 2 reg */
	if (cptr == NULL) return SCPE_ARG;
	val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST);	
	cptr = get_glyph (cptr, gbuf, 0);		/* get argument */
	d = get_uint (gbuf, 8, I_M_XOP, &r);
	if (r != SCPE_OK) return SCPE_ARG;
	val[0] = val[0] | (d << I_V_XOP);
	break;  }					/* end case */
if (*cptr != 0) return SCPE_ARG;			/* any leftovers? */
return rtn;
}
