/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:debug.c 12.0$ */
/* $ACIS:debug.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/standca/RCS/debug.c,v $ */

#ifndef lint
static char *rcsid = "$Header:debug.c 12.0$";
#endif

#include <ctype.h>
#include <assert.h>
#include "init.h"
#include "cmds.h"

#include "sa.h"

#ifdef ATR

#define OTHER_ENTER() debug_flag = ps2_enter_debugger();

#define OTHER_EXIT() ps2_exit_debugger();

#else ATR
#define OTHER_ENTER()
#define OTHER_EXIT()
#endif ATR

#ifdef TTY_CONSOLE
int (*alt_getchar)();
int (*alt_putchar)();
int (*deb_getchar)();
int (*deb_putchar)();
#define ENTER_DEBUGGER() { alt_getchar = deb_getchar; alt_putchar = deb_putchar; OTHER_ENTER() }
#define EXIT_DEBUGGER()  { alt_getchar = 0; alt_putchar = 0 ; OTHER_EXIT() }
#else
#define ENTER_DEBUGGER() { OTHER_ENTER() }
#define EXIT_DEBUGGER() { OTHER_EXIT() }
#endif TTY_CONSOLE

#define OUT_W(addr,value) * (int *) addr = value

#ifndef isdigit
#define isdigit(c) ('0' <= (c) && (c) <= '9')
#define isXdigit(c) (('a' <= (c) && (c) <= 'f') || ('A' <= (c) && (c) <= 'F' ))
#define isxdigit(c) (isdigit(c) || isXdigit(c))
#define isspace(c) (c == ' ' || c == '\t')
#endif

#define isop(c) ((c) == '+' || (c) == '-' || (c) == '*' || (c) == '/' || (c) == '!' || (c) == RIGHT_PAREN || (c) == '^' || (c) == '&' || (c) == '|' || (c) == '<' || (c) == '>')	/* test if (c) is diadic operator */
#define ATOX(ptr) expr(ptr,0x10)	/* convert - hex default */
#define ATON(ptr) expr(ptr,10)		/* convert - decimal default */
#define LEFT_PAREN	'('		/* left parenthesis */
#define RIGHT_PAREN	')'		/* right parenthesis */

#define NWORDS 8

typedef short word;

#define MAX_LINE	128	       /* somewhat short, but limited stack size */
#define MAX_ARG		(MAX_LINE/4)
#define SHORT_LINE 64		       /* for short responses */

#include "debug.h"

short *break_addr[MAX_BREAK] = {
	0
};				       /* the break points */
short break_instn[MAX_BREAK] = {
	0
};				       /* the instructions that were there */
int break_counter[MAX_BREAK] = {
	0
};				       /* the number of times to ignore it */
int break_count = 0;		       /* the number of break points set */
int break_set = 0;		       /* when breakpoints are set */
int err_flag;
struct symtab *closest(), *scansym();
char *index();
char *screen_buffer, *screen_save;     /* the screen buffer and where to save it */
int screen_size;		       /* how long the screen buffer is */
extern int screen_lines;
static int screen_saved = 0;	       /* if we have saved the screen */
int iar_mask = 0xffffffff;	       /* bits to mask out in the iar */
int *watch_addr[MAX_BREAK] = {
	0
};
int watch_value[MAX_BREAK] = {
	0
};
int watch_count = 0;
int go_step = 0;		       /* = 1 if step after go */
int save_mq;			       /* the saved MQ register */
int save_mcpc;			       /* the saved MCSPCS register */
int debug_option;		       /* the debugger debug flag etc */
int option_mask;		       /* mask for step count */
#define DEBUG_DEBUG	0x01
#define SHOW_REGS	0x02
#define SCAN_SYM	0x04		/* use scan_sym to find statics */
#define CS_PRINT	0x08		/* show CS symbolicly */
#define LPS_2		0x10		/* have go issue LPS 2 for ex packets */
#define OPTION_TRACE_TABLE 0x20		/* use trace table */

#ifdef ROMPC
int ioim1_gsr;		/* IOIM GSR */
#endif ROMPC

#ifdef DEBUG
#define DEBUGX(stmt) if (debug_option&DEBUG_DEBUG) stmt
#else
#define DEBUGX(stmt)
#endif


#define ARG_0	0
#define ARG_1	1
#define ARG_2	2
#define ARG_3	3


struct cmd {
	short cmd_number;	       /* the number of the command */
	short cmd_args;		       /* the number of args */
	char *cmd;		       /* the name of command */
	char *help_text;	       /* some helpfull text */
} cmds[] = {
/*
 * NOTE: the makefile looks for lines with the a particular format in order
 * to build the #define's in cmds.h - be careful when changing.
 * in particular be careful of changing the format of the following lines.
 * order is important only to the 'help' command printout and to resolve
 * ambiguous commands (e.g. "s" = "scr" because it is first).
 */
ASCII,	       ARG_1,  "ascii",	"[addr]		display in ascii",
BREAK,	       ARG_2,  "break",	"[addr] [count]	set/print break points",
DISPMEM,       ARG_2,  "display",	"addr [count]	display memory",
DEFINE,	       ARG_0,  "define", "symbol addr	create symbol table entry",
DUMP,	       ARG_3,  "dump",	"addr [count incr]	dump memory",
VERSION,       ARG_0,  "version",	"		print out current version number",
VECTOR,	       ARG_1,  "vector",	"			reset interrupt vectors",
HELP,	       ARG_0,  "help",	"[cmd|*]		print help text",
QMARK,	       ARG_0,  "?",	0,
MODHALF,       ARG_2,  "/half",	"addr		modify halfword",
MODBYTE,       ARG_2,  "/byte",	"addr		modify byte",
MODWORD,       ARG_2,  "/word",	"addr		modify word",
CALL,	       ARG_3,  "call",	"addr [args]	call C routine",
CLEAR,	       ARG_1,  "clear",	"[addr]		clear given/all break/watch points",
IDENT,	       ARG_3,  "ident",	"[addr count flag]	print identification",
IOR,	       ARG_2,  "ior",	"addr [count]	do an ior at addr",
IOW,	       ARG_2,  "iow",	"addr value		do an iow at addr",
UNASM,	       ARG_2,  "unasm",	"addr [count]	disassemble code",
REGISTER,      ARG_2,  "reg",	"[reg] [value]	print/change register",
REFLECT,       ARG_0,  "reflect","		reflect to previous vector",
SCR,	       ARG_2,  "scr",	"[reg] [value]	print/change SCR",
GO,	       ARG_1,  "go",	" 			restart after stopped",
STEP,	       ARG_2,  "step",	"[[addr] [count]]	step count instructions",
SYMBOL,	       ARG_1,  "symbol","[addr]		symbol >= addr",
SYMTAB,	       ARG_1,  "symtab",	"addr		provide symbol table",
MOVE,	       ARG_3,  "move",	"source count target - move memory",
SER,	       ARG_1,  "ser",	"[value]		print MMU SER",
STATUS,	       ARG_0,  "$",     "			display status",
SHOW,	       ARG_1,	"show", "[addr]		display/set save screen",
EQUALS,	       ARG_1,  "=","addr [fmt=%x]		printf fmt value",
STAR,	       ARG_0,  "*", 0,
WATCH,	       ARG_1,  "watch","[addr]		set/print watch points",
LOOKUP,        ARG_0,  "lookup","symbol		lookup symbol in symtab",
TRACEBACK,     ARG_3,  "trace","[iar sp]		trace stack",
CLS,	       ARG_0,  "cls",   "			clear screen",
OPTION,	       ARG_2,	"option","value [count]	set option flags",
FILL,	       ARG_3,  "fill","[addr] [count] [value]	fill memory",
USERCMD,       ARG_3,  "usercmd", "?		user command",
HATIPT,	       ARG_2,	"hatipt", "page [count]	display hat/ipt entries",
TLB,	       ARG_2,	"tlb", "entry [count]	display tlb entries",
PCDISPMEM,     ARG_2,  "pcdisplay",	"addr [count]	display pc memory",
INB,	       ARG_2,  "inb","port [count]	read count byte(s) from the PC's I/O space",
OUTB,	       ARG_2,  "outb","port value		write a byte to the PC's I/O space",
INHW,	       ARG_2,  "inhw","port [count]	read count half word(s) [16 bits] from the PC's I/O space",
OUTHW,	       ARG_2,  "outhw","port  value	write a half word [16 bits] to the PC's I/O space",
#ifdef ATR
PCR,	       ARG_0,  "pcif","		 	print PCIF registers",
#endif ATR
0,	ARG_0,	0,	0		/* terminate command list */
};

int *regsave = (int *) REGSAVE;		/* where the registers are saved */
char serfmt[] = "\20\1DATA\2PROTECTION\3TLB-SPECIFICATION\4PAGE-FAULT\5MULTIPLE\6EXTERNAL-DEVICE\7IPT-SPECIFICATION\10ROS-WRITE\12TLB-RELOAD\13CORRECTABLE-ECC\14STORAGE\15LOAD\16I/O-ADDRESS\17STORAGE-ADDRESS\20RSC-NAKDN\21RSC-NAKDA\22SEGMENT-VIOLATION";

#define R(n) REGSAVE + n * 4

struct symtab internal[] = {
	"iar", 0,		       /* set at run time */
	"r0", R(0),
	"r1", R(1),
	"r2", R(2),
	"r3", R(3),
	"r4", R(4),
	"r5", R(5),
	"r6", R(6),
	"r7", R(7),
	"r8", R(8),
	"r9", R(9),
	"r10", R(10),
	"r11", R(11),
	"r12", R(12),
	"r13", R(13),
	"r14", R(14),
	"r15", R(15),
	"sp", R(1),
	"fp", R(14),
	0, 0
};

char *HO = "\33H";		/* home screen */
char *CL = "\33K";		/* clear screen */
char *CE = "\33I";		/* clear to eol */

help_cmd(n)
	register int n;
{
	register struct cmd *p = cmds;
	register char *s, *h;
	register int i = 0;

	if (n == 0)
		printf("Commands available are: (with leading * from top level)\n\n");
	for (; s = p->cmd; ++p)
		if (h = p->help_text)
			if (n == 0)
				printf("%s%s", s, (++i) & 07 ? "\t" : "\n");
			else if (n == p->cmd_number || n == STAR)
				printf("%s %s\n", s, h);
	if ((i & 07) != 0)
		printf("\n");
}


/*
 * lookup the command in "str" in the command table.
 * commands may be abbreviated.
 */
get_cmd(str)
	register char *str;
{
	register struct cmd *p = cmds;
	register int len = strlen(str);

	for (; p->cmd; ++p)
		if (strncmp(str, p->cmd, len) == 0)
			return (p->cmd_number);
	return (0);
}


static int dot;
int lastdebugcmd = HELP;	       /* default is to give help */
/*
 * user cmd is a mechanism for a user to add additional commands 
 * to the debugger by just pointing usercmd to a function to be
 * called; simplest way of doing that is to put a file into libc 
 * that has the following:
 *	int fn();
 *	int (*usercmd)() = fn;
 * one can also set usercmd on the fly but that is more difficult
 * and 'call' serves much the same purpose.
 */
int (*usercmd)();

dump_cmd(argc, argv)
	char **argv;
{
	register int count;
	register int i;
	register char *p;
	int arg2, arg3;

	p = argv[0];
	while (p && *p == '*')
		++p;		       /* ignore leading *'s */
	if (p == 0 || *p == 0 || *p == '.')
		i = lastdebugcmd;
	else
		i = lastdebugcmd = get_cmd(p);

	err_flag = 0;

	if (i > 0) {
/* nargs is the actual number of args (including command name) that was
 * given and expected.
 */
		register int nargs = cmds[i - 1].cmd_args + 1;
		/* actual number of args allowed */

		if (nargs > argc)
			nargs = argc;
		if (nargs > 1)
			dot = ATOX(argv[1]);
		count = nargs > 2 ? ATON(argv[2]) : 10;
		arg2 = nargs > 3 ? ATON(argv[3]) : 0;
		arg3 = nargs > 4 ? ATON(argv[4]) : 0;
	}
	if (err_flag)
		return;		       /* argument in error */
	switch (i) {
	case BREAK:		       /* set break point */
		if (argc < 2)
			break_list();
		else
			break_cmd(dot, argc < 3 ? 0 : count);
		break;
	case WATCH:		       /* set watch point */
		if (argc < 2)
			watch_list();
		else
			watch_cmd(dot);
		break;
	case UNASM:
		dot = do_unasm(dot, count);
		break;
	case HELP:
	case QMARK:
		help_cmd(argc > 1 ? get_cmd(argv[1]) : 0);
		break;
	case VERSION:
		printf("%s\n", rcsid);
		break;
	case REFLECT:
		if (debug_state == NORMAL_STATE || debug_state == PM_STATE) {
			not_stopped();
			break;
		}
		reflect();	       /* fix it up to reflect interrupt properly */
	case GO:		       /* go on with execution */
		if (debug_state == NORMAL_STATE || debug_state == PM_STATE) {
			not_stopped();
			break;
		}
		if (argc > 1)
			MTSR(SCR_IAR, dot); /* set IAR */
		set_watch();	       /* set watch point values */
		if (get_break(MFSR(SCR_IAR) & iar_mask) >= 0) {
			DEBUGX(printf("step past breakpoint\n")); /* DEBUG */
			go_step = 1;
			step(1);
		}
		go_step = 0;
		step_count = 0;	       /* don't step it */
#ifdef IBMRTPC
		DEBUGX(printf("reset csr\n")); /* DEBUG */
		OUT_W(CSR_Reset, 0);   /* reset CSR */
#endif IBMRTPC
		mtsr(SCR_MQ, save_mq); /* restore the MQ */
		set_break();	       /* set the break points */
		restart(debug_state, (debug_option&LPS_2) ? 0x20 : 0);
		break;
	case ASCII:		       /* display memory in ascii */
		dot = ascii(dot, count);
		break;
	case DUMP:		       /* display memory */
		dot = dump(dot, count, arg2);
		break;
	case DISPMEM:		       /* display memory */
		dot = display(dot, count, dot);
		break;
	case PCDISPMEM:		       /* display memory */
#ifdef IBMRTPC
		dot = display(dot + 0xf4000000, count, dot);
#endif
#ifdef ATR
		dot = display(set_512_window(dot) + pcif_512_fw, count, dot);
#endif
		break;
	case CLEAR:
		if (argc < 2) {
			break_count = 0; /* get rid of em */
			watch_count = 0; /* watch points too */
		} else
			break_clear(dot);
		break;
	case CLS:
		printf(CL);	       /* clear screen (ibm3101) */
		break;
	case CALL:
		set_break();	       /* set the break points */
		set_watch();
		call(dot, count, arg2, arg3);
		break;
	case REGISTER:
		if (debug_state == NORMAL_STATE) {
			not_stopped();
			break;
		}
		if (argc >= 3) {
			int new = ATOX(argv[2]);
			dot = atoi(argv[1]);
			if (dot < 0 || dot > 15) {
				badreg();
				break;
			}
			setreg(dot, new);
		} else
			printregs(debug_state);
		break;
	case STATUS:
		debug_print(debug_state); /* print out status */
		break;

	case IDENT:
		{
			extern char etext, edata, end;

			if (argc < 2) {
				dot = (int) & etext; /* probably should be long */
				count = &edata - &etext;
			}
			if (argc < 3)
				count = 0x10000;
			ident(dot, dot + count, arg2); /* print ident info */
			break;
		}
	case IOW:		       /* ior or iow */
		iow(dot, count);
		++dot;
		break;
	case IOR:		       /* ior or iow */
		do {
			printf(" ior(%x) --> ", dot);
			i = ior(dot);
			printf("%08x (%d)\n", i, i);
			dot += 1;      /* advance to next address */
		} while (--count > 0);
		break;
	case STEP:
		go_step = 0;	       /* not in go step mode */
		set_watch();
		switch (argc) {
		case 0:
		case 1:
			count = 1;     /* default is 1 step */
			break;
		case 2:
			count = ATON(argv[1]); /* use given step count */
			break;
		default:	       /* step addr count */
			MTSR(SCR_IAR, dot); /* start at given location */
			break;
		}
		step(count);
		break;
	case SYMTAB:
		if (hfetch(dot) == -1)
			bad_addr();
		else
			{
			printf("was %x\n",symtab);
			symtab = (struct symtab *)dot;
			}
		break;
	case SYMBOL:
		prsym(dot, argc > 2 ? count : 0x10000, "%x = ");
		printf("\n");
		break;
	case EQUALS:
		printf(argc > 2 ? argv[2] : "0x%x", dot);
		printf("\n");
		break;
	case SER:
		if (argc > 1)
			printf("SER=%b\n", dot, serfmt);
		else
			printser();
		break;
	case SCR:		       /* scr = system control register */
		if (argc >= 3) {
			int new = ATOX(argv[2]);
			dot = atoi(argv[1]);
			if (dot < 0 || dot > 15) {
				badreg();
				break;
			}
			MTSR(dot, new);
			if (MFSR(dot) != new)
				printf("SCR%d didn't set\n", dot);
		} else
			sys_regs();
		break;
	case MODBYTE:
		dot = modify(dot, count, 1);
		break;
	case MODWORD:
		dot = modify(dot, count, 4);
		break;
	case MODHALF:
		dot = modify(dot, count, 2);
		break;
	case MOVE:
		{
			register int last;
			printf("move %x (%d bytes) to %x\n", dot, count, last = ATOX(argv[3]));
			bcopy(dot, last, count);
			break;
		}
	case VECTOR:		       /* re-initialize important vectors */
		_init_vectors(argc > 1 ? dot : IRQ_0 + IRQ_MC + IRQ_PC + IRQ_PG);
		break;
	case SHOW:		       /* display the screen buffer */
		if (argc > 1) {
			screen_saved = 0; /* prevent trouble */
			screen_save = (char *)dot; /* hope its actually there */
			break;
		}
		if (screen_saved) {
			save_screen(screen_save, screen_buffer, screen_size);
			getchar();
		}
		break;
	case DEFINE:
		if (argc <= 1)
			printf("name required\n");
		else
			define(argv[1], symtab, argc <= 2 ? dot : ATOX(argv[2]));
		break;
	case LOOKUP:
		lookup_sym(argc <= 1 ? "" : argv[1]);
		break;
	case TRACEBACK:
		if (argc > 2)
			traceback(dot, count, arg2);
		else
			traceback(MFSR(SCR_IAR), ((int *)regsave)[1], arg2);
		/* trace it back */
		break;
	case OPTION:
		if (argc > 1)
			debug_option = dot;
		if (argc > 2)
			option_mask = (1 << count) - 1;
		printf("options=%b mask=%x\n", debug_option, "\20\1DEBUG_DEBUG\2SHOW_REGS\3SCAN_SYM\4CS_PRINT\5LPS_2\6-TRACE_TABLE", option_mask);
		break;
	case FILL:
		printf("filling %x ... %x (%d bytes) with %x\n",dot,dot+count-1,count,arg2);
		while (--count >= 0)
			bstore(dot++, arg2);
		break;
	case USERCMD:
		if (usercmd == 0)
			printf("no usercmd available\n");
		else
			(*usercmd)(dot,count,arg2,arg3);
		break;
	case HATIPT:
		dot = hatprint(dot,count);
		break;
	case TLB:
		tlbprint(dot,count);
		break;
#ifdef ATR
	case PCR :
		pcif_print();
		break;
#endif ATR

	case INB:		       /* ior or iow */
		if(argc == 2)
			count = 1;
		for (;--count >= 0 && !err_flag; ) {
#ifdef ATR
#define PC_IO_LIMIT	0x100
			if ((unsigned) dot < PC_IO_LIMIT)
				printf("warning: cannot reference I/O addresses < %x\n",PC_IO_LIMIT);
			printf("inb(%x / %x) --> ", dot,pcif_io_b+dot);
#endif
#ifdef IBMRTPC
			printf("inb(%x / %x) --> ", dot,IO_BASE+dot);
#endif
			i = in(dot);
			printf("0x%x (%d)\n", i, i);
			dot += 1;      /* advance to next address */
		}
		break;

	case OUTB:		       /* ior or iow */
		out(dot, count);
		++dot;
		break;

	case INHW:		       /* ior or iow */
		if(argc == 2)
			count = 1;
		for (;--count >= 0 && !err_flag; ) {
#ifdef ATR
			if ((unsigned) dot < PC_IO_LIMIT)
				printf("warning: cannot reference I/O addresses < %x\n",PC_IO_LIMIT);
			printf("inh(%x / %x) --> ", dot,pcif_io_hw+dot);
#endif
#ifdef IBMRTPC
			printf("inh(%x / %x) --> ", dot,IO_BASE+dot);
#endif
			i = INW(dot);
			printf("0x%x (%d)\n", i, i);
			dot += 1;      /* advance to next address */
		}
		break;

	case OUTHW:		       /* ior or iow */
		OUTW(dot, count);
		++dot;
		break;

	default:
		printf("unknown command\n");
	}
}


debug_print(debug_state)
{

	printf("Status: ");
	if (debug_state != NORMAL_STATE) {
		debug_status(debug_state);
#ifdef IBMRTPC
		csr_print();
#endif IBMRTPC
		printregs(debug_state);
		sys_regs();
		show_instn(debug_state);
	} else
		not_stopped();
}


show_instn(debug_state)
	register int debug_state;
{
	register int iar;

	if (debug_state != NORMAL_STATE) {
		if ((mfsr(SCR_MCSPCS) & PCS_IADDR) == 0 &&
		    (MFSR(SCR_ICS) & PROBLEM_ICS) == 0 &&
		    hfetch(iar = MFSR(SCR_IAR) & iar_mask) != -1)
			{
			printf("%s",CE);
			do_unasm(iar, 1); /* display 1 inst. */
			}
	}
}


ascii(start, count)
	register char *start;
	register int count;
{

	register int c;
	register int lines = 0;

	for (; c = bfetch(start); ++start) {
		if (c <= 0 || c & 0x80)
			break;
		if (c < 040 && c != '\t' && c != '\n') {
			putchar('^');
			putchar(c+'@');
		} else
			putchar(c);
		if (c == '\n' && ++lines >= screen_lines - 1) {
			if (pause() <= 04)
				break;
			else
				lines = 0;
		}
	}
	putchar('\n');
	return((int)start+1);
}


/*
 * display memory at "start" with the label "addr"
 * normally "start" and "addr" are the same except
 * when displaying PC memory.
 */
display(start, count, addr)
	register word * start;
	register int count;
	register word * addr;
{
	register int i;
	register char *p;
	register int c;
	register value;
	int lines = 0;
	int sum;
	int same = 0;

	do {
		for (i = 0; i < NWORDS; ++i)
			if (sum = hfetch(start + i))
				break;
		if (sum == 0) {
			if (same++) {
				if (same == 2) {
					printf("...\n");
					if (++lines >= screen_lines - 1) {
						if (pause() <= 04)
							break;
						else
							lines = 0;
					}
				}
				start += NWORDS;
				addr += NWORDS;
				continue;
			}
		} else
			same = 0;
		printf("%08x: ", addr);
		p = (char *)start;
		for (i = 0; i < NWORDS; ++i) {
			if ((value = hfetch(start)) == -1) {
				bad_addr();
				return ((int)start);
			}
			printf("%04x ", value);
			++start;
			++addr;
		}
		printf("  |");
		for (i = 0; i < NWORDS * sizeof(word); ++i) {
			c = bfetch(p++);
			if (c < 040 || c >= 0177)
				c = '.';
			putchar(c);
		}
		printf("|");
		printf("\n");
		if (++lines >= screen_lines - 1) {
			if (pause() <= 04)
				break;
			else
				lines = 0;
		}
	} while (--count > 0);
	return ((int)addr);
}


/*
 * print out memory in a full format 
 * one line per word
 */
dump(start, count, incr)
	register int *start;
	register int count;
	register int incr;
{
	register value;
	int lines = 0;

	if (incr == 0)
		incr = 1;	/* default increment */
	for (; --count >= 0; start += incr) {
		prsym(start, 0x10000, "%08x ");
		if ((value = hfetch(start)) == -1) {
			bad_addr();
			return ((int)start);
		}
		value = wfetch(start);
		printf("0x%08x ", value);
		printf("%12d ", value);
		printf("'%c' ",(value < 040 || value >= 0177) ? '.' : value);
		prsym(value, 0x10000, "%08x ");
		printf("\n");
		if (++lines >= screen_lines - 1) {
			if (pause() <= 04)
				break;
			else
				lines = 0;
		}
	}
	return ((int)start);
}



/*
 * call the function at 'start' with arguments 'count', 'arg2', 'arg3'
 * do the call via a LPS so that we allow interrupts etc.
 * we build the appropriate instructions in low core and then
 * execute them.
 */

call(start, count, arg2, arg3)
	register int start, count, arg2, arg3;
{
	register short *p = (short *)CODE_ADDR;

	p[0] = LPS;
	p[1] = (short)(int)(p + 2);	       /* the fake interrupt vector */
	*(int *)(p + 2) = start;       /* location to call */
	p[4] = debug_state == NORMAL_STATE ? USER_ICS : mfsr(SCR_ICS);
	/* normal user mode */
	p[5] = 0;		       /* CS */
	debug_state = NORMAL_STATE;    /* til something happens */
	callabs(p, count, arg2, arg3);       /* call the code */
}


/*
 * change value at "start" (for "count" lines) of length "length"
 * length == 1, 2 , or 4
 * if count <=0 then don't check address validity as this can cause
 * problems when accessing the I/O map.
 * if count < 0 then don't display the old value as this can also
 * cause problems when addess the I/O map.
 */
modify(start, count, length)
	register start, length, count;
{
	char buff[SHORT_LINE];
	register int n;
	register oldcount = count;     /* special case for I/O bus */

	if (count <= 0)
		count = 1;	       /* always do at least one */
	for (; --count >= 0;) {
		prsym(start, 0x10000, "%08x ");
		putchar(':');
		if (oldcount > 0) {
			if (length == 1)
				n = hfetch(start & ~1);	/* get the half word */
			else if (length == 2)
				n = hfetch(start); /* get the half word */
			else
				n = hfetch(start) | hfetch(start + 2);
			if (n == -1) {
				bad_addr();
				return (start);
			}
		}
		if (oldcount >= 0) {
			if (length == 1)
				printf("%02x ", bfetch(start));
			else if (length == 2)
				printf("%04x ", hfetch(start));
			else
				printf("%08x ", wfetch(start));
		}
		gets(buff);
		if (*buff == '^') {
			start -= length;
			continue;
		}
		if (strcmp(buff, "end") == 0 || (*buff && *buff < 012))
			break;
		if (*buff && !isspace(*buff)) {
			err_flag = 0;
			n = ATOX(buff);
			if (err_flag)
				continue;
			if (length == 1)
				bstore(start,n);
			else if (length == 2)
				hstore(start,n);
			else
				wstore(start,n);
		}
		start += length;
	}
	return (start);
}


pause()
{
	register int c;

	put_status(50, "<HOLDING>");
	c = _getchar();
	put_status(50, "         ");
	return (c);
}


/*
 * interface to the real expression routine
 * which takes a 'char **' and updates it
 */
expr(ptr, base)
	char *ptr;
	register int base;
{
	return (_expr(&ptr, base));
}


/*
 * pick up operands of the form:
 * item [+- item] ...
 */

_expr(pptr, base)
	register char **pptr;
	register int base;
{
	register int n1 = item(pptr, base), n2, op;

	for (;;) {
		op = *(*pptr);
		if (op == 0 || op == RIGHT_PAREN)
			break;
		++(*pptr);	       /* point at next item */
		if (op == '<' || op == '>')
			{
			if (*(*pptr) != op)
				goto badop;
			++(*pptr);
			}
		n2 = item(pptr, base);
		switch (op) {
		case '-':
			n1 -= n2;
			break;
		case '+':
			n1 += n2;
			break;
		case '*':
			n1 *= n2;
			break;
		case '/':
			n1 /= n2;
			break;
		case '!':
			if (hfetch(n1) == -1) {
				bad_addr();
				break;
			}
			switch (n2) {
			case 1:
				n1 = bfetch(n1);
				break;
			case 2:
				n1 = hfetch(n1);
				break;
			case 4:
				n1 = wfetch(n1);
				break;
			default:
				bad_addr();
			}
			break;
		case '^':
			n1 ^= n2;
			break;
		case '&':
			n1 &= n2;
			break;
		case '|':
			n1 |= n2;
			break;
		case '<':
			n1 <<= n2;
			break;
		case '>':
			n1 >>= n2;
			break;
		default:
		badop:
			printf("bad operator");
			++err_flag;
			break;
		}
	}
	return (n1);
}


/* get an item (hex, decimal, number or a symbol value */
int item(pptr, base)
	register char **pptr;
	register int base;
{
	register int n2;
	register char *ptr = *pptr;
	register char *s = ptr + 1;
	register int op;
	int err = 0;
	register int flag = 0;
#define NOT_DIGIT 1
#define NOT_XDIGIT 2

	if (*ptr == '=') {
		++(*pptr);	       /* bump the pointer past = */
		return (vtop(_expr(pptr, base)));
	}
	if (*ptr == LEFT_PAREN) {
		++(*pptr);	       /* bump the pointer past = */
		n2 = _expr(pptr, base);
		if (**pptr != ')') {
			printf(") expected\n");
			++err_flag;
		} else ++(*pptr);
		return (n2);
	}
	for (; *s && !isop(*s); ++s) {
		if (isdigit(*s))
			continue;		/* it is a normal digit */
		if (!isxdigit(*s))
			flag |= NOT_XDIGIT;
		else
			flag |= NOT_DIGIT;
	}
	op = *s;
	*s = 0;			       /* terminate the string */
	if (ptr[0] == '0' && ptr[1] == 'x')
		n2 = (atox(ptr + 2));
	else if (ptr[0] == '0' && ptr[1] == 'd')
		n2 = (atoi(ptr + 2));
	else if (*ptr == '_')
		n2 = (lookup(ptr + 1, 0, symtab));
	else if (*ptr == '.')
		n2 = dot;	       /* current location */
	else if (*ptr == '\'')
		n2 = ptr[1];	       /* just single letter for now */
else if (*ptr == '+' || *ptr == '-' ||
	    (base == 10 && isdigit(*ptr) && (flag & NOT_DIGIT) == 0))
		n2 = atoi(ptr);
	else if (n2 = lookup(ptr, &err, internal))
		n2 = *(int *)n2;       /* return value of internals */
	else if ((n2 = lookup(ptr, &err, symtab)) && err == 0);
	else if (base == 16 && isxdigit(*ptr) && (flag & NOT_XDIGIT) == 0)
		n2 = atox(ptr);
	else {
		++err_flag;
		printf("%s ??\n", ptr);
	}
	*pptr = s;		       /* point to delimeter */
	*s = op;		       /* restore  delimeter */
	return (n2);
}


int debug;
do_unasm(start, count)
	register int start;
{
	register int n;
	register int lines = 0;
	char buff[SHORT_LINE];
	register char *p;
	short instns[2];
	register int value;

	do {
		if ((value = hfetch(start)) == -1) {
			printf("%08x: ", start);
			bad_addr();
			break;
		}
		instns[0] = value;
		instns[1] = hfetch(start+2);
		n = _unasm(start, instns, buff);
		if (p = index(buff, '\n'))
			*p = 0;	       /* remove the \n */
		n &= 07;
		printf("%s", buff);
		prsym(start, 0x1000, "		");
		printf("\n");
		start += n;	       /* length in bytes ? */
		if (++lines >= screen_lines - 1) {
			if (pause() <= 04)
				break;
			else
				lines = 0;
		}
	} while (--count > 0);
	return ((int)start);
}


ident(p, e, flag)
	register char *p, *e;
{

	printf("ident: %x ... %x\n", p, e);
	for (; p < e; ++p) {
		if (bfetch(p) == '$' && bfetch(p+1) == 'H' && bfetch(p+2) == 'e' && bfetch(p+3) == 'a') {
			if (flag)
				printf("%x: ", p);
			p = (char *) ascii(p, 512);
		}
	}
}


_debugger(state)
	register int state;
{
	register int argc;
	char *argv[MAX_ARG];
	char line[MAX_LINE];

	if (debug_state != NORMAL_STATE)
		printf("HELP: recursive entry to debugger!\n");
	go_step = 0;		       /* just in case */
	debug_state = state;	       /* keep track of the current state */
	if (state != PM_VEC)
		internal[0].value = state;     /* KLUDGE the value of _iar */
	rm_break();		       /* remove break points */
	if ((debug_option & SHOW_REGS) == 0)
		show_instn(state);	/* already shown */
	for (;;) {
		printf("DEBUG> ");
		if (gets(line) == 0)
			return;
		argc = _parse(line, line, argv);
		if (debug_state == NORMAL_STATE &&
		    (argc == 0 || strcmp(argv[1], "return") == 0))
			return;
		dump_cmd(argc, argv);
	}
}


char *sys_regnames[] = {
	"", "", "", "",		       /* SCR 0 - 3 */
	"", "", "COUS", "COU",	       /* SCR 4 - 7 */
	"TS", "ECR", "MQ", "MCPC",     /* SCR 8 - 11 */
	"IRB", "IAR", "ICS", "CS"      /* SCR 9 - 15 */
};

sys_regs()
{
	register int i;

	for (i = 0; i < 16;) {
		if ((i & 03) == 0)
			printf("SCR %02d-%02d ", i, i + 3);
		printf("%08x %-4s", MFSR(i), sys_regnames[i]);
		printf((++i & 03) ? "  " : "%s\n",CE);
	}
	printf("%s\n",CE);
}


mcpc_print()
{
	register int n = MFSR(SCR_MCSPCS) & 0xffff;

	if (n)
		printf(" MCPC=%b", n,
		    "\20\2DATA-ADDR\3INSTN-ADDR\4ILL-OPCD\5PRIV-OPCD\6TRAP\7UNKNOWN\10KNOWN\12I/O\13RSC-TO\14DATA-TO\15INSTN-TO\16PARITY\20RSC-CHK");
}


bad_addr()
{
	printf("bad address!\n");
	++err_flag;
}


/*
 * resume execution after an interrupt.
 * v = address of interrupt vector to do a lps on
 * t = 0 if we don't execute at least one instruction
 *     0x10 if we will execution at least one instruction
 */
restart(v, t)
	register int t;
	register int v;
{
	register short *p = (short *)CODE_ADDR;
	FN q = (FN)p;

/* assert(v >= INT0_VEC && v <= PROG_VEC);	/* no longer needed?? */
	p[0] = LM_R0;
	p[1] = REGSAVE;
	p[2] = LPS + t;
	p[3] = (short)v;	       /* the old interrupt vector */
/* delay(1);			/* short delay as insurance */
	DEBUGX(printf(" ==> iar=%8x ics=%4x cs=%4x  \r", ((struct ivec *)v)->old_iar, ((struct ivec *)v)->old_ics, ((struct ivec *)v)->old_cs));
	/* DEBUG */
	debug_state = NORMAL_STATE;    /* til something happens */
	EXIT_DEBUGGER();		/* leave debugger */
	callabs(q);
/* NOT REACHED */
}


/*
 * step given number of instructions.
 * do this by requesting a level 0 interrupt
 * and doing a LPS T,...
 * if the current instruction is a breakpoint (static)
 * then do a LPS 0,... in order to simulate the execution
 * of the instruction.
 */
step(count)
	register int count;
{
#define PRIO(x) ((x)&07)
	register struct ivec *ivec = (struct ivec *)debug_state;
	register int x;
	register int t = TRACE_BIT;

	DEBUGX(printf("stepping %d\n", count));	/* DEBUG */
	if (debug_state == NORMAL_STATE || debug_state == PM_STATE) {
		not_stopped();
		return;
	}
	step_count = count;
	if (PRIO(ivec->old_ics) == 0)
		ivec->old_ics |= 1;    /* make it at least priority 1 */
	if (ivec->old_ics & NOINT_ICS)
		ivec->old_ics &= ~NOINT_ICS; /* allow interrupts */
	if (ivec == (struct ivec *)PC_VEC &&
	    BPT == instnfetch(ivec->old_iar, ivec->old_ics)) {
		/* pick up next instruction */
		DEBUGX(printf("skipping BPT instruction\n")); /* DEBUG */
		ivec->old_iar++;
		t = 0;
	}
	/* special case for BPT */
/* following assumes that we are at level 0 already */
	x = mfsr(SCR_IRB);
	x |= IRQ_0;		       /* request level 0 interrupt */
	mtsr(SCR_IRB, x);	       /* and put it back into register */
	DEBUGX(printf("level 0 interrupt requested\n")); /* DEBUG */
#ifdef IBMRTPC
	OUT_W(CSR_Reset, 0);	       /* clear CSR */
#endif IBMRTPC
	mtsr(SCR_MQ, save_mq);	       /* restore the MQ */
	restart(debug_state, t);       /* execute at least one (usually) */
}


MFSR(reg)
	register int reg;
{
	register struct ivec *v = (struct ivec *)debug_state;

	if (debug_state) {
		if (reg == SCR_ICS)
			return (hfetch(&v->old_ics));
		if (reg == SCR_CS)
			return (hfetch(&v->old_cs));
		if (reg == SCR_IAR)
			return (wfetch(&v->old_iar));
		if (reg == SCR_MQ)
			return (save_mq);
		if (reg == SCR_MCPC)
			return (save_mcpc);
	}
	return (mfsr(reg));
}


MTSR(reg, value)
	register int reg, value;
{
	register struct ivec *v = (struct ivec *)debug_state;

	if (debug_state) {
		if (reg == SCR_ICS) {
			hstore(&v->old_ics, value);
			return;
		}
		if (reg == SCR_CS) {
			hstore(&v->old_cs, value);
			return;
		}
		if (reg == SCR_IAR) {
			wstore(&v->old_iar,value);
			return;
		}
		if (reg == SCR_MQ) {
			save_mq = value;
			return;
		}
		if (reg == SCR_MCPC) {
			save_mcpc = value;
			return;
		}
	}
	mtsr(reg, value);
}


setreg(reg, value)
	register int reg, value;
{
	register int *regs = (int *)regsave;

	regs[reg] = value;
}


debug_status(state)
	register int state;
{
	register struct ivec *v = (struct ivec *)state;
	register int i;		       /* for watch point check */
	register int iar = wfetch(&v->old_iar);

	switch (state) {
	case 0:
		printf("in debugger\n");
		return;
	case PC_VEC:
		lps();		       /* do a lps to get off of program check level */
		if (get_break(iar & iar_mask) >= 0)
			printf("break point");
		else
			printf("program check");
		break;
	case MC_VEC:
		lps();		       /* do a lps to get off of machine check level */
		printf("machine check");
		break;
	case SVC_VEC:
		printf("SVC");
		break;
	case PROG_VEC:
		printf("debugger call");
		break;
	case PM_VEC:
		printf("post mortem");
		break;
	case INT0_VEC:		       /* interrupt level 0 */
		if (step_count) {
			printf("instruction step");
			break;
		}
/* fall thru in case not instruction step */
	default:
		printf("level %d intr.", (state - INT0_VEC) >> 4);
		break;
	}
	printf(": IAR = %x ", iar);
	prsym(iar, 0x10000, (char *)0); /* symbolicly */
	printf(" ICS=%b ", hfetch(&v->old_ics),
	    "\20\10CHKSTOP-MASK\11INTR-MASK\12XLATE\13USER\14STG-PROT\15PERIE");
	printf((debug_option & CS_PRINT) ? "CS=%b " : "CS=%x ",
		hfetch(&v->old_cs),
		"\20\1TB\2OV\3RESERVED\4C0\5GT\6EQ\7LT\10PZ");

	mcpc_print();
	if (step_count > 1)
		printf(" %d remaining steps%s\n", step_count,CE);
	printf("%s\n",CE);
#ifdef ROMPC
	if (state==PC_VEC && 
#ifdef MOD135
	(cpu_model == CPU_ROMPC || cpu_model == CPU_MOD135)) {
#else
	cpu_model == CPU_ROMPC) {
#endif MOD135
		int ecr = mfsr(SCR_ECR);		/* get ECR */
		print_ecr(ecr>>24,ecr&0xffffff);	/* display ECR */
	}
#endif ROMPC
	for (i = 0; i < watch_count; ++i)
		if (*watch_addr[i] != watch_value[i]) {
			prsym(watch_addr[i], 0x10000, "watch point changed: %x ");
			printf(" %x ==> %x\n", watch_value[i], *watch_addr[i]);
		}
}

printregs(ivec)
	register struct ivec *ivec;
{
	register int *regs = (int *)regsave;
	register int i;

	for (i = 0; i < 16;) {
		printf("R%2d  %08x", i, regs[i]);
		printf((++i & 03) ? "   " : "%s\n",CE);
	}
	printf("%s\n",CE);
}


#define ON(x) (x) ? "1" : "0"

#define K * 1024
#define M * 1024 * 1024

static int memsizes[16] = {
	0, 64 K, 64 K, 64 K, 64 K, 64 K, 64 K, 64 K,
	128 K, 256 K, 512 K, 1 M, 2 M, 4 M, 8 M, 16 M
};


printser()
{
/* cannot use MMU_IOBASE because it doesn't return a value (DUMB!) */
	register int mmubase = MMUBASE; /* get base address */
	register int ser;
	register int i;
	register int reg;
	register int hatipt;
	register int ramsize;
	register int pagesize;

	for (i = 0; i < MMU_NSEGREGS; i += MMU_SEGREGSTEP) {
		reg = ior(mmubase + MMU_SEGR + i);
		printf("SR%02d = %8x (P=%s, C=%s, I=%s, SID=%03x S=%s K=%s)\n",
		    i, reg, ON(reg & MMU_SEG_P), ON(reg & MMU_SEG_C), ON(reg & MMU_SEG_I),
		    (reg & MMU_SEG_SID) >> MMU_SID_SHIFT, ON(reg & MMU_SEG_S), ON(reg & MMU_SEG_K));
	}
/* printf("I/O Base address=%x (%x)\n",mmubase>>16,mmubase);	/* debug */
	ser = ior(mmubase + MMU_SER);	/* pick up the SER */
	printf("SER=%b ", ser, serfmt);
	printf("SEAR=%x ", ior(mmubase + MMU_SEAR));
	printf("TRAR=%x ", ior(mmubase + MMU_TRAR));
	reg = ior(mmubase + MMU_TCR);
	printf("TCR=%b HAT/IPT=%x\n", reg,
	    "\20\114K-PAGE\12RESERVED\13INTR-TLB-RELOAD\14INTR-CECC\15TLIPT\16RAS-DIAG\17ISPER\20V=R", hatipt = reg & 0xff);
	pagesize = (reg & 0x100) ? 4096 : 2048;
	reg = ior(mmubase + MMU_RAM);
	ramsize = memsizes[reg & 0x0f];
	printf("RAM=%x (start=%x, size=%x) ", reg, (reg & 0xff0) << 12, ramsize);

	reg = ior(mmubase + MMU_ROS);
	i = memsizes[reg & 0x0f];
	printf("ROS=%x (PARITY=%s start=%x, size=%x)\n", reg, ON(reg & 0x1000), (reg & 0xff0) << 12, i);
	printf("HAT/IPT addr=%x\n", hatipt * (ramsize * 16 / pagesize));
}


csr_print()
{
	register int csr;
#ifdef IBMRTPC
#ifdef ATR
	if (cpu == CPU_ATR)
		csr = -1;
	else
#endif ATR
		csr = wfetch(CSR);
	if (csr != -1)
		printf("CSR=%b%s\n", csr,
			"\20\11PIO-PENDING\12PLANAR-BUSY\13CH-RESET-CAPTURED\14DMA-EXECPTION\15I/O-CHECK\16INV-OPERATION\17PROT-VIOLATION\20PIO-DMA\21DMA-ERR-CH8\22DMA-ERR-CH7\23DMA-ERR-CH6\24DMA-ERR-CH5\25DMA-ERR-CH4\26DMA-ERR-CH3\27DMA-ERR-CH2\30DMA-ERR-CH0\31PIO-ERR\33SYSTEM-ATTN\34SOFT-RESET\35POWER\37INTR-PENDING\40EXCEPTION", CE);
#endif IBMRTPC
}


break_cmd(start,count)
	register short *start;
{
	register int i;
	register int instn;

	for (i = 0; i < break_count; ++i)
		if (break_addr[i] == start) {
			break_counter[i] = count;
			return;	       /* rest are already set */
		}
	if (i >= MAX_BREAK) {
		printf("only %d breakpoints allowed\n", MAX_BREAK);
		return;
	}
	if (((int)start & 01) || (instn = hfetch(start)) == -1) {
		bad_addr();
		return;
	}
	break_addr[i] = start;
	break_instn[i] = instn;
	break_counter[i] = count;
	break_count++;
}


watch_cmd(start)
	register short *start;
{
	register int i;

	for (i = 0; i < watch_count; ++i)
		if (watch_addr[i] == (int *)start)
			return;	       /* already set */
	if (i >= MAX_BREAK) {
		printf("only %d watchpoints allowed\n", MAX_BREAK);
		return;
	}
	if (((int)start & 03) || hfetch(start) == -1) {
		bad_addr();
		return;
	}
	watch_addr[i] = (int *)start;
	watch_value[i] = *(int *)start;	/* prime it */
	watch_count++;
}


get_break(start)
	register short *start;
{
	register int i;

	for (i = 0; i < break_count; ++i)
		if (break_addr[i] == start)
			return (i);    /* got it! */
	return (-1);
}


set_break()
{
/*
 * put breakpoints into the code
 */
	register int i;

	if (break_set)
		return;
	for (i = 0; i < break_count; ++i)
		*break_addr[i] = BPT;  /* put in break point */
	break_set = 1;
}


rm_break()
{
/*
 * remove breakpoints
 */
	register int i;

	if (break_set)
		for (i = 0; i < break_count; ++i)
			*break_addr[i] = break_instn[i];
	break_set = 0;
}


break_list()
{
/*
 * list breakpoints
 */
	register int i;

	printf("%d break points ...\n", break_count);
	for (i = 0; i < break_count; ++i)
		{
		printf("%6d ",break_counter[i]);
		do_unasm((int)break_addr[i], 1); /* show it */
		}
}


break_clear(start)
	short *start;
{
/*
 * remove given breakpoint
 */
	register int i;

	if ((i = get_break(start)) < 0)
		printf("none at %x\n", start);
	else {
		--break_count;
		for (; i < break_count; ++i) {
			break_addr[i] = break_addr[i + 1];
			break_instn[i] = break_instn[i + 1];
			break_counter[i] = break_counter[i + 1];
		}
	}
}


/*
 * we have just had an interrupt and have done a 'stm' to save
 * the registers at 'REGSAVE' and have set up a debugger stack
 * and called 'trap' with the trap type (an integer between 0 and
 * 0xa).
 * trap will check if we are single stepping and if so it
 * will check for watch and break points and then continue
 * stepping. otherwise we pass control to the debugger.
 */

trap(type)
	register int type;		/* must be register */
{
	register int i;
	register debug_flag = 0;
	register struct ivec *v = type + (struct ivec *)INT0_VEC;
	register short *iar;


#ifdef ROMPC
/*
 * must read IOIM General Status Register before we reference any 
 * register that is accessed thru the same IOIM (sigh).
 */
	if ((int) v == PC_VEC && cpu== CPU_RTPC &&
#ifdef MOD135
	(cpu_model == CPU_ROMPC || cpu_model == CPU_MOD135))
#else
	cpu_model == CPU_ROMPC)
#endif MOD135
		ioim1_gsr = ior(((ior(IOIM1_BASE) & 0xff) << 16) + IOIM_GSR);
#endif ROMPC
#ifdef IBMRTPC
#ifdef ATR
	if (cpu == CPU_ATR)
		debug_flag = 0;
	else
#endif ATR
		debug_flag = (*(int *)CSR) & CSR_OHOH;
#endif IBMRTPC
	ENTER_DEBUGGER();		/* flip state */
	DEBUGX(printf("trap %x ...", v)); /* DEBUG */
	if (type == 0 && step_count > 1 && debug_flag == 0) {
		--step_count;	       /* count down */
		for (i = watch_count; --i >= 0;)
			if (*watch_addr[i] != watch_value[i]) {
				debug_flag = 1;
				goto watch_trip;
			}
		iar = (short *)((int)v->old_iar & iar_mask);
		/*
		 * check to see if we've hit a break-point and stop
		 * if we have.
		 */
		for (i = break_count; --i >= 0;)
			if (break_addr[i] == iar && (break_counter[i]==0 ||
				--break_counter[i] == 0)) {
				debug_flag = 2;
				goto watch_trip;
			}
		if ((debug_option & SHOW_REGS) && (option_mask & step_count) == 0) {
			register int old = debug_state;
			register int save_mq = mfsr(SCR_MQ); /* save the MQ */

			debug_state = (int)v;
			printf(HO);	/* home (ibm3101) */
			debug_print(debug_state);
			mtsr(SCR_MQ, save_mq); /* restore the MQ */
			debug_state = old;
		}
		restart((int)v, TRACE_BIT);
	}
	/* continue counting */
watch_trip:			       /* watch point encountered */
/*
 * if IRQ 0 is set then clear it so that we do not get
 * more interrupts later.
 */
	i = mfsr(SCR_IRB);
	if (i & IRQ_0)
		mtsr(SCR_IRB, i & ~IRQ_0); /* kill level 0 request */
/* putchar('\n');		/* not usually needed */
	if (type == 0 && debug_flag == 0 && go_step) {
		go_step = 0;	       /* no longer go stepping */
		set_break();	       /* set break points */
		restart((int)v, 0);    /* and continue go execution */
	}
	save_mq = mfsr(SCR_MQ);	       /* save the MQ register */
	save_mcpc = mfsr(SCR_MCPC);	       /* save the MCPC register */
	/*
	 * test to see if we have encountered a break-point.
	 * if so test to see if the break-point count has expired.
	 * if not we continue execution by stepping past the break-point.
	 */
	if ((int) v == PC_VEC && debug_flag == 0 && (save_mcpc&PCS_TRAP)) {
		iar = (short *)((int)v->old_iar & iar_mask);
		for (i = break_count; --i >= 0;)
			if (break_addr[i] == iar && (break_counter[i] &&
				--break_counter[i] != 0)) {
			DEBUGX(printf("step past breakpoint\n")); /* DEBUG */
			rm_break();	/* remove break points */
			go_step = 1;	/* step past break point */
			debug_state = (int)v;
			step(1);	/* and go! */
			}
	}
	if (screen_buffer && screen_save) {
		screen_restore(screen_buffer, screen_save, screen_size);
		screen_saved = 1;
	}
	if (debug_option & SHOW_REGS) {
		register int old = debug_state;

		debug_state = (int)v;
		printf(HO);	/* home (ibm3101) */
		debug_print(debug_state);
		debug_state = old;
	} else
		debug_status(v);
	if ((int) v == MC_VEC) {
		printf("Resetting Keyboard...\n");
		init_kbd();	       /* just in case if a RSC reset */
	}
	_debugger((int)v);
	restart(debug_state, 0);       /* restart execution again */
}


lookup(ptr, errflag, symtab)
	register char *ptr;
	register int *errflag;
	register struct symtab *symtab;
{
	register struct symtab *s;

	if (errflag)
		*errflag = 0;
	for (ALLSYMS(s))
		if (strncmp(s->symbol, ptr, MAXSYMLEN) == 0)
			return (s->value);
	if (errflag)
		*errflag = 1;
	else {
		err_flag++;	       /* global error flag */
		printf("%s not found\n", ptr);
	}
	return (0);
}


define(ptr, symtab, value)
	register char *ptr;
	register struct symtab *symtab;
	register int value;
{
	register struct symtab *s;
	static char nullsymbol[MAXSYMLEN];

	for (ALLSYMS(s))
		if (strncmp(s->symbol, ptr, MAXSYMLEN) == 0)
			{
			printf("%s was %x, now %x\n",ptr,s->value,value);
			s->value = value;
			return;
			}
	if (s && bcmp(s->symbol,nullsymbol,MAXSYMLEN) == 0 &&
			bcmp((s+1)->symbol,nullsymbol,MAXSYMLEN) == 0)
		{
		strncpy(s->symbol, ptr, MAXSYMLEN);
		s->value = value;
		printf("%s defined as %x\n",ptr,s->value);
		}
	else
		printf("no room for more symbols\n");
	return (0);
}


/*
 * find closest symbol at or before the given address
 * return it's symbol table pointer.
 */
struct symtab *closest(addr, offset)
	register unsigned addr, offset;
{
	register struct symtab *s, *p = 0;
	register unsigned n;

	if (symtab == 0)
		return (scansym(addr, offset));	/* fake it */
	if (debug_option&SCAN_SYM)
		{
		if ((s = scansym(addr, offset)) && (n = addr - s->value) < offset) 
			offset = n, p = s;
		}
	for (ALLSYMS(s))
		if (s->value <= addr && (n = addr - s->value) < offset)
			offset = n, p = s;
	return (p);
}


/*
 * find all the symbols begining with "str"
 * and print them out.
 */
lookup_sym(str)
	register char *str;
{
	register struct symtab *s;
	register int len = strlen(str);
	int lines = 0;

	for (ALLSYMS(s))
		if (strncmp(s->symbol, str, len) == 0)
			{
			printf("%.8s %08x\n", s->symbol, s->value);
			if (++lines >= screen_lines - 1)
				{
				if (pause() <= 04)
					break;
				else
					lines = 0;
				}
			}
}


int prsym(start, offset, fmt)
	register unsigned start, offset;
	register char *fmt;
{
	register unsigned nstart = start & iar_mask; /* get masked version */
	register struct symtab *s = closest(nstart, offset);

	if (fmt)
		printf(fmt, start);
	if (nstart >= 0x100 && s) {
		printf("%s", s->symbol);
		if (s->value != nstart)
			printf("+0x%x ", nstart - s->value);
		else
			printf(" ");
	}
	return (s != 0);
}


set_watch()
{
	register int i;

	for (i = 0; i < watch_count; ++i)
		watch_value[i] = *watch_addr[i];
	if (screen_saved) {
		screen_restore(screen_save, screen_buffer, screen_size);
		screen_saved = 0;
	}
}


watch_list()
{
/*
 * list watchpoints
 */
	register int i;

	printf("%d watch points ...\n", watch_count);
	for (i = 0; i < watch_count; ++i) {
		prsym((unsigned)watch_addr[i], 0x10000, "%08x ");	/* show it */
		printf(" now %08x\n", *watch_addr[i]);
	}
}


not_stopped()
{
	if (debug_state == PM_STATE)
		printf("Can't do that in POST MORTEM mode!\n");
	else
		printf("not in DEBUG mode\n");
}


reflect()
{
/*
 * refect and interrupt back to the original holder of it
 * by taking that interrupt vector 'new' values and doing
 * a LPS on it.
 * we use the 'PROG_VEC' vector for this.
 */
	register struct ivec *v = (struct ivec *)debug_state;
	register struct svec *s = &save_vec[(debug_state - INT0_VEC) >> 4];

	((struct ivec *)PROG_VEC)->old_iar = s->new_iar;
	((struct ivec *)PROG_VEC)->old_ics = s->new_ics;
	((struct ivec *)PROG_VEC)->old_cs = v->old_cs;

	debug_state = PROG_VEC;	       /* return from program vector */
}


badreg()
{
	printf("bad register number\n");
}


/*
 * get the instruction at the current iar - return -1 if
 * the address is not valid.
 */

instnfetch(iar, ics)
	register short *iar;
	register int ics;
{
	register int instn;

	if (ics & TRANS_ICS)
		iar = (short *)vtop(iar);
	if ((int)iar < 0)
		instn = -1;
	else
		instn = hfetch(iar);
	return(instn);
}

/*
 * function to print out HAT/IPT
 * start is initial page number
 * count is number of entries to print
 *
 * also looks up SID in the segment registers so that a proper
 * virtual address can be displayed.
 */
hatprint(start,count)
int start, count;
{
	register struct hatipt_entry *ipte;
	register struct hatipt_entry *hatipt;
	int segreg[16];
	register int i = ior(MMUBASE + MMU_TCR);
	register int hashmask;		/* hash table size - 1 => hash mask */

	hashmask = (memsizes[ior(MMUBASE + MMU_RAM) & 0x0f] >> ((i & 0x100) ? 12 : 11)) - 1;
	hatipt = (struct hatipt_entry *) ((i&0xff) * ((hashmask+1) << 4));
/*	printf("hatipt=%x hashmask=%x\n",hatipt,hashmask);	/* DEBUG */

	if (count)
		{
		printf("page addrtag key KU SID    pn  hat  ipt w tid lock vaddr\n");
		printf("#     (hex)      01 SID        ptr  ptr       bits\n");
		}
	else
		count = 1;		/* count=0 means do 1 without header */
	if (start < 0)
		return;
	start &= hashmask;		/* ignore irelevent bits */
	for (i=0; i<16; ++i)
		{
		int reg = ior(MMUBASE + i);
		segreg[i] = (reg&MMU_SEG_PRESENT) ? (reg >> 2) & 0xfff : -1;
		}
	for ( ipte = &hatipt[start]; start <= hashmask && count > 0; --count, ++ipte, ++start)
		{
		int addrtag = wfetch(&ipte->key_addrtag);
		int key = (addrtag >> 30) & 03;
		int sid = (addrtag >> 17) & 0xfff;	/* SID */
		int pn = addrtag & 0x1ffff;
		printf("%04x %08x  %x %c%c %03x %05x %04x %04x ",
			start,
			addrtag,
			key,
			"WWWR"[key],			/* K=0 access */
			"-RWR"[key],			/* K=1 access */
			sid,				/* SID */
			pn,				/* pn */
			hfetch(&ipte->hat_ptr),
			hfetch(&ipte->ipt_ptr));
		printf("%02x %02x %04x ",
			bfetch(&ipte->w),
			bfetch(&ipte->tid),
			hfetch(&ipte->lockbits));
		for (i=0; i<16; ++i)
			if (segreg[i] == sid)
				{
				printf("%x",i);
				break;
				}
		if (i == 16)
			printf("?");
		printf("%07x\n",pn << 11);
		}
	return(start);
}


/*
 * function to print out TLB
 * start is initial tlb entry
 * count is number of entries to print
 *
 * also looks up SID in the segment registers so that a proper
 * virtual address can be displayed.
 */
#define NUMBER_TLBS	32
#define	TLB_ADDR	0x20
#define	TLB_RPN		0x40
#define	TLB_TID		0x60
tlbprint(start,count)
int start, count;
{
	int segreg[16];
	int i;

	if (count)
		{
		printf("entry addrtag sid   pn   RPN  v  key  w tid lock  vaddr    raddr\n");
		}
	else
		count = 1;		/* count=0 means do 1 without header */
	if (start < 0)
		return;
	for (i=0; i<16; ++i)
		{
		int reg = ior(MMUBASE + i);
		segreg[i] = (reg&MMU_SEG_PRESENT) ? (reg >> 2) & 0xfff : -1;
		}
	for (; start < NUMBER_TLBS && count > 0 && !err_flag; --count, ++start)
		{
		/* 
		 * raw_addrtag format: (IBM left to right numbering)
		 * 0-2		RESERVED
		 * 3-27 	addr_tag (address tag)
		 * 28-31 	RESERVED
		 *
		 * raw_RPN format:
		 * 0-15		RESERVED
		 * 16-28	RPN (real page number)
		 * 29		v (valid bit)
		 * 30-31	key (key bits)
		 *
		 * raw_TID format:
		 * 0-6		RESERVED
		 * 7		w (write)
		 * 8-15		TID (transaction ID)
		 * 16-31	lock bits
		 */
		unsigned raw_addrtag = ior(MMUBASE+TLB_ADDR+start);
		unsigned raw_RPN = ior(MMUBASE+TLB_RPN+start);
		unsigned raw_TID = ior(MMUBASE+TLB_TID+start);
		unsigned sid = (raw_addrtag >> 17) & 0xfff;	/* SID */
		unsigned pn = ((raw_addrtag) & 0x1fff0) + (start & 0xf);
		unsigned key = (raw_RPN) & 03;
		unsigned v = (raw_RPN >> 2) & 1;
		unsigned RPN = (raw_RPN >> 3) & 0x1fff;
		unsigned lockbits = (raw_TID) & 0xffff;
		unsigned TID = (raw_TID >> 16) & 0xff;
		unsigned w = (raw_TID >> 24) & 0x1;
		printf("%04x %08x %03x %05x %05x %01x %02x %c%c %01x %02x  %04x ",
			start,
			raw_addrtag,
			sid,				/* SID */
			pn,
			RPN,
			v,
			key,
			"WWWR"[key],			/* K=0 access */
			"-RWR"[key],			/* K=1 access */
			w,
			TID,
			lockbits);
		for (i=0; i<16; ++i)
			if (segreg[i] == sid)
				{
				printf("%x",i);
				break;
				}
		if (i == 16)
			printf("?");
		printf("%07x ",pn << 11);
		printf("%06x\n",RPN << 11);
		}
}


#ifdef ROMPC

char *ec_length[] = { "c", "h", "", "ts" };
char *ec_ops[] = { "l", "lm", "ior", "la", "st", "stm", "iow", "?" };
char *ec_cancel[] = { "", "-" };
print_ecr(ecr_count,ecr_addr)
{
	if (ecr_count) {
		printf("exception stack(%d):",ecr_count);
		for (; ecr_count-- > 0; ecr_addr += 16) {
			int reg = hfetch(ecr_addr+2);
			if (reg < 0)
				break;	/* invalid ecr_addr */
			
			printf("\t%s%s%s r%d,%x ",
				ec_cancel[reg&1],	/* cancelled? */
				ec_ops[(reg>>3)&0x07],	/* storage operation */
				ec_length[(reg>>6)&0x03],	/* operation length */
				(reg>>8)&0x0f,		/* register number */
				wfetch(ecr_addr+4)	/* address */
				);
			if (reg&(0x04<<3))
				printf("[%x] ", wfetch(ecr_addr+8));	/* data value */
		}
		if (ioim1_gsr)
			printf("ioim1_gsr=%x ",ioim1_gsr);
		printf("%s\n",CE);
	}
}
#endif ROMPC
