/* static char rcsid[] = "$Header: xcmds.c,v 1.2 85/05/03 17:47:03 ed Exp $" */

/************************************************************************
 *									*
 *				Copyright 1984				*
 *			VALID LOGIC SYSTEMS INCORPORATED		*
 *									*
 *	This listing contains confidential proprietary information	*
 *	which is not to be disclosed to unauthorized persons without	*
 *	written consent of an officer of Valid Logic Systems 		*
 *	Incoroporated.							*
 *									*
 *	The copyright notice appearing above is included to provide	*
 *	statutory protection in the event of unauthorized or 		*
 *	unintentional public disclosure.				*
 *									*
 ************************************************************************/


/*
 * ERM - Externded Resident Monitor
 * Initial Version - 4/10/85 - ELP
 */

#include <sys/types.h>
#include <a.out.h>
#include "cpu.h"
#include "erm.h"
#include "setjmp.h"

extern struct display dispfmt;
extern u_char erm_base, *erm_fmt;
extern u_int *erm_regsave;
extern u_char erm_disasm_buf[];
extern struct bpt bpts[];
extern jmp_buf bpt_jmpbuf;
extern u_char erm_trace_type;
extern u_long erm_trace;
extern struct commtable *curr_comms;
extern struct commtable commtable[];
extern u_char erm_inp_radix, erm_out_radix;

char erm_align_errmsg[] = "%s: address on odd byte boundry\n";
#define alignerr(a)	printf(erm_align_errmsg, a)

/*
 *	dispbyte - This routine will display memory byte by byte.
 */

dispbyte (addr, ret_getnum)
/*
register u_char *addr;
register ret_getnum;
*/
{
	do_mem (addr, ret_getnum, "db", 'c', 1, 0);
}

/*
 *	displong - This routine will display memory longword by 
 *		longword.
 */

displong (addr, ret_getnum)
/*
register u_long *addr;
register ret_getnum;
*/
{
	do_mem (addr, ret_getnum, "dl", 'l', 4, 0);
}

/*
 *	dispshort - This routine will display memory short by short.
 */

dispshort (addr, ret_getnum)
/*
register u_short *addr;
register ret_getnum;
*/
{
	do_mem (addr, ret_getnum, "ds", 's', 2, 0);
}

/*
 *	modbyte - This routine will display memory byte by byte,
 *		and allow modification.
 */

modbyte (addr, ret_getnum)
/*
register u_char *addr;
register ret_getnum;
*/
{
	do_mem (addr, ret_getnum, "mb", 'c', 1, 1);
}

/*
 *	modlong - This routine will display memory longword by 
 *		longword, and allow modification.
 */

modlong (addr, ret_getnum)
/*
register u_long *addr;
register ret_getnum;
*/
{
	do_mem (addr, ret_getnum, "ml", 'l', 4, 1);
}

/*
 *	modshort - This routine will display memory short by short,
 *		and allow modification.
 */

modshort (addr, ret_getnum)
/*
register u_short *addr;
register ret_getnum;
*/
{
	do_mem (addr, ret_getnum, "ms", 's', 2, 1);
}

/*
 *	do_mem - Common code for memory display/modification routines.
 */

do_mem (addr, ret_getnum, err_str, fmt_type, fmt_len, mod_flag)
register u_int addr, ret_getnum, fmt_len, mod_flag;
register char *err_str, fmt_type;
{
	register int retval;
	int value;

	if (fmt_type != 'c' && (int) addr & 0x1)
		return alignerr (err_str);
	do
	 {
		printf ("%x: ", addr);
		printf (erm_fmt, (fmt_type == 'c') ? *(u_char *) addr :
			(fmt_type == 's') ? *(u_short *) addr:*(u_int *) addr);
		putchar (' ');
		retval = getnum (&value, -1);
		if (retval == 1) /* just got cr */
			addr += fmt_len;
		else if (retval == 0) /* got new val */
			if (!mod_flag)
				printf ("%s: Cannot modify memory with this command\n",
					err_str);
			else if (fmt_type == 'c')
				*(u_char *) addr = (u_char) value;
			else if (fmt_type == 's')
				*(u_short *) addr = (u_short) value;
			else
				*(u_long *) addr = (u_long) value;
	 } while (retval == 0 || retval == 1);
}

/*
 *	dispmem - implements the 'dm' command.  This command will
 *		display 16 bytes followed by 16 characters.
 */

dispmem (addr, ret_getnum)
register u_char *addr;
{
	register u_char *save;
	register long i;

	do
	 {
		save = addr;
		printf ("%x: ", addr);
		for (i=0; i < 16; i++)
			printf ("%02x ", *addr++);
		for (addr=save, i=0; i < 16; i++, addr++)
			printf ("%c", (*addr >= 0x20 && *addr < 0x7F) ?
				*addr : '.');
		i = getnum (0, -1);
	 } while (i == 1);
}

/*
 *	dispregs - implements the 'dr' command. This command will
 *		display the current user registers.
 */

char erm_regs [] = "d0d1d2d3d4d5d6d7a0a1a2a3a4a5a6spsrpc";

dispregs ()
{
	register u_int i, *reg_ptr;
	register char *ptr;

	ptr = erm_regs;
	reg_ptr = erm_regsave;
	while (*ptr)
	 {
		for (i=0; *ptr && i < 4; i++, reg_ptr++)
		 {
			putchar (*ptr++);
			putchar (*ptr++);
			printf ("= ");
			printf (erm_fmt, *reg_ptr);
			putchar (' ');
		 }
		printf ("\n");
	 }
}

/*
 *	modregs - This routine will allow the modification of the 
 *		saved registers.  Beware you can put a big hole
 *		in your foot this way.
 */

modregs ()
{
	register u_int retval, *reg_ptr;
	register char *ptr = 0;
	u_int value;

	do
	 {
		if (ptr == 0 || *ptr == 0)
		 {
			ptr = erm_regs;
			reg_ptr = erm_regsave;
		 }
		putchar (*ptr++);
		putchar (*ptr++);
		printf ("= ");
		printf (erm_fmt, *reg_ptr);
		putchar (' ');
		retval = getnum (&value, -1);
		if (retval == 0)
		 {
			*reg_ptr = value;
			ptr -= 2;
		 }
		else reg_ptr++;
	 }while (retval == 0 || retval == 1);
}

/*
 *	disass - implements the 'di' command.  This command is 
 *		used to disassemble text.
 *
 */

disass (addr, ret_getnum)
register short *addr;
{
	register u_int retval;

	if ((int) addr & 0x1)
		return alignerr ("di");
	do
	 {
		retval = disasm (addr, erm_disasm_buf);
		printf ("%x: ", addr);
		while (addr < (short *) retval)
			printf ("%02x", *(u_char *) addr++);
		printf (" = %s ", erm_disasm_buf);
	 } while (getnum (0, -1) == 1);
}

/*
 *	setbpt - This function allows the setting of breakpoints and
 *		the printing of the list of active breakpoints.
 */

setbpt (addr, ret_getnum)
register u_short *addr;
{
	if (ret_getnum == 1)
		pr_bpts ();
	else if ((int) addr & 0x1)
		alignerr ("br");
	else do_bpt (addr, ERM_BPT_PERM);
}

/*
 *	do_bpt - This is common code for setting a breakpoint.
 */

do_bpt (addr, type)
register u_short *addr;
register u_short type;
{
	register struct bpt *bpt_ptr;

	for (bpt_ptr = bpts; bpt_ptr < &bpts[ERM_MAXBPT]; bpt_ptr++)
		if (bpt_ptr -> bpt_flag && bpt_ptr -> bpt_pc == addr)
		 {
			printf ("Breakpoint already set at %x\n", addr);
			return -1;
		 }
	for (bpt_ptr = bpts; bpt_ptr < &bpts[ERM_MAXBPT]; bpt_ptr++)
		if (bpt_ptr -> bpt_flag == 0) break;
	if (bpt_ptr == &bpts[ERM_MAXBPT])
	 {
		printf ("Too many breapoints set, use 'bc' to clear one\n");
		return -1;
	 }
	bpt_ptr -> bpt_oldval = *addr;
	bpt_ptr -> bpt_pc = addr;
	bpt_ptr -> bpt_count = 1;
	bpt_ptr -> bpt_flag = type;
	return 0;
}

/*
 *	clrbpt - This routine clears break points previously set
 */

clrbpt (addr, ret_getnum)
register u_short *addr;
{
	register struct bpt *bpt_ptr;

	if ((int)addr & 0x1)
		return alignerr ("bc");
	for (bpt_ptr = bpts; bpt_ptr < &bpts[ERM_MAXBPT]; bpt_ptr++)
		if (bpt_ptr -> bpt_flag &&
		    bpt_ptr -> bpt_pc == addr) break;
	if (bpt_ptr == &bpts[ERM_MAXBPT])
		return printf ("No breakpoint set at %x\n", addr);
	bpt_ptr -> bpt_oldval = 0;
	bpt_ptr -> bpt_pc = 0;
	bpt_ptr -> bpt_count = 0;
	bpt_ptr -> bpt_flag = 0;
}

/*
 *	bpt_reset - This routine is called to restore all breakpoints
 *		to their original value.  This is done to prevent strange
 *		disassemblies.
 */

bpt_reset (bpt_pc)
register u_short *bpt_pc;
{
	register struct bpt *bpt_ptr;

	for (bpt_ptr = bpts; bpt_ptr < &bpts[ERM_MAXBPT]; bpt_ptr++)
		if (bpt_ptr -> bpt_flag == ERM_BPT_PERM)
			*bpt_ptr -> bpt_pc = bpt_ptr -> bpt_oldval;
		else if (bpt_ptr -> bpt_flag == ERM_BPT_GOTILL)
		 {
			*bpt_ptr -> bpt_pc = bpt_ptr -> bpt_oldval;
			if (bpt_ptr -> bpt_pc == bpt_pc)
			 {
				bpt_ptr -> bpt_oldval = 0;
				bpt_ptr -> bpt_pc = 0;
				bpt_ptr -> bpt_count = 0;
				bpt_ptr -> bpt_flag = 0;
			 }
		 }
}

/*
 *	bpt_set - This routine is called before restarting things to
 *		set all the breakpoints.
 */

bpt_set ()
{
	register struct bpt *bpt_ptr;

	for (bpt_ptr = bpts; bpt_ptr < &bpts[ERM_MAXBPT]; bpt_ptr++)
		if (bpt_ptr -> bpt_flag == ERM_BPT_PERM || 
		    bpt_ptr -> bpt_flag == ERM_BPT_GOTILL)
		 {
			bpt_ptr -> bpt_oldval = *bpt_ptr -> bpt_pc;
			*bpt_ptr -> bpt_pc = ERM_TRAP15;
		 }
}

/*
 *	pr_bpts - This routine prints out the breakpoint table.
 */

pr_bpts ()
{
	register struct bpt *bpt_ptr;
	register first = 1;

	for (bpt_ptr = bpts; bpt_ptr < &bpts[ERM_MAXBPT]; bpt_ptr++)
		if (bpt_ptr -> bpt_flag) goto print;
	return printf ("No breakpoints set\n");
print:
	printf ("Breakpoints are at: ");
	for (bpt_ptr = bpts; bpt_ptr < &bpts[ERM_MAXBPT]; bpt_ptr++)
		if (bpt_ptr -> bpt_flag)
		 {
			printf ("%s%x%s", first ? " " : ", ", bpt_ptr -> bpt_pc,
				bpt_ptr->bpt_flag == ERM_BPT_GOTILL ? "(O)" :
				"");
			first = 0;
		 }
	putchar ('\n');
}

/*
 *	gotocmd - This routine implements the goto or jump function.  If
 *		a value is supplied it is stuffed into the pc and then we
 *		jump to it.  Sounds like fun huh?
 */

gotocmd (addr, ret_getnum)
register u_short *addr;
{
	if (ret_getnum == 0)
	 {
		if ((int) addr & 0x1)
			return alignerr ("go");
		erm_regsave [ERM_PC] = (u_long) addr;
	 }
	erm_longjmp (bpt_jmpbuf, 1);
}

/*
 *	go_till - This command is used to execute up to a certain point.
 *		This is accomplished by setting a temporary breakpoint
 *		and then executing a go.
 */
go_till (addr, ret_getnum)
register u_short *addr;
{
	if (ret_getnum != 0)
		return printf ("Must supply stopping address to 'gt'\n");
	if ((int)addr & 0x1)
		return alignerr ("gt");
	if (!do_bpt (addr, ERM_BPT_GOTILL))
		erm_longjmp (bpt_jmpbuf, 1);
}

/*
 *	trace_cmd - This command will trace 'count' instructions.
 */

trace_cmd (count, ret_getnum)
register u_long count;
{
	if (ret_getnum) erm_trace = 1;
	else erm_trace = count;
	erm_trace_type = ERM_TRACE_REG;
	erm_regsave [ERM_SR] |= ERM_TRACE_BIT;
	erm_longjmp (bpt_jmpbuf, 1);
}

/*
 *	questioncmd - This is the help message.
 */

questioncmd ()
{
	register struct commtable *comm_ptr;
	register i;

	for (i=0, comm_ptr = curr_comms; comm_ptr -> comchar0; comm_ptr++, i++)
	 {
		printf ("%c%c %s %-26s", comm_ptr-> comchar0, 
		    comm_ptr -> comchar1, comm_ptr -> flag ? "<value> -" : 
		    "        -", comm_ptr -> helpmsg);
		if (i&1) putchar ('\n');
	 }
	if (i&1)putchar('\n');
}


u_char	erm_oct_fmt[] = "%11o",
	erm_dec_fmt[] = "%10u",
	erm_hex_fmt[] = "%8x";

/*
 *	chgoradix - Change output radix.
 */

chgoradix (new_radix, ret_getnum)
register u_int new_radix, ret_getnum;
{
	if (ret_getnum != 0)
		return printf ("Current output radix is %d\n", erm_out_radix);
	if (new_radix == 8)
		erm_fmt = erm_oct_fmt;
	else if (new_radix == 10)
		erm_fmt = erm_dec_fmt;
	else if (new_radix == 16)
		erm_fmt = erm_hex_fmt;
	else
		return printf ("Invalid output radix %d\n", new_radix);
	erm_out_radix = new_radix;
	printf ("New output radix is %d\n", erm_out_radix);
}

/*
 *	chgiradix - Change input radix.
 */

chgiradix (new_radix, ret_getnum)
register u_int new_radix, ret_getnum;
{
	if (ret_getnum != 0)
		printf ("Current input radix is %d\n", erm_inp_radix);
	else if (new_radix == 8 || new_radix == 10 || new_radix == 16)
	 {
		erm_inp_radix = new_radix;
		printf ("New input radix is %d\n", erm_inp_radix);
	 }
	else
		printf ("Invalid input radix %d\n", new_radix);
}

#define	CALL2	0x4E90		/* jsr a0@	*/
#define CALL6	0x4EB9		/* jsr <adr>	*/
#define ADDQL	0x5080		/* addql #x,	*/
#define ADDL	0xD1FC		/* addl #X,	*/

backtrace ()
{
	backtr (erm_regsave [ERM_A6], 10);
}

backtr(link, cnt)
register int link, cnt;
{
	register long	rtn, p, inst;
	long		calladr, entadr;
	int		n = 1, i, argn;
	extern		main();

	while(cnt--)
	{
		p = link; calladr = -1; entadr = -1;
		link = fetch(p, 4); p += 4;
		rtn = fetch(p, 4);
		if (fetch(rtn - 6, 2) == CALL6)
		{
			entadr = fetch(rtn - 4, 4);
			calladr = rtn - 6;
		}
		else if (fetch(rtn - 2, 2) == CALL2)
			calladr = rtn - 2;

		inst = fetch(rtn, 2);
		if ((inst & 0xF1C0) == ADDQL)
		{
			argn = (inst>>9) & 07;
			if (argn == 0) argn = 8;
		}
		else if ((inst & 0xF1FC) == ADDL)
			argn = fetch(rtn + 2, 4);
		else argn = 0;
		if (argn && (argn % 4)) argn = (argn/4) + 1;
		else argn /= 4;

		if (calladr != -1) showoffset(calladr);
		else printf("????????");
		putchar(':');
		putchar('\t');
		if (entadr != -1) showoffset(entadr);
		else printf("????????");
		putchar(' '); putchar('(');
		if (argn) printf("%8x", fetch(p += 4, 4)); 
		for(i = 1; i < argn; i++)
			printf(", %8x", fetch(p += 4, 4));
		printf(")\n");
		if ((entadr == (long) main) || (++n > 15)) break;
	}
}

fetch(adr, size)
{
	register long data = 0;

	if (size == 4) data = *((int *) adr);
	else data = *((short *) adr);
	return(data);
}

static char *symstart = 0;

erm_syminit ()
{
	extern symbytes, oldsyms, symcmp, edata, _edata;

	if (!symstart)
		if (symbytes != symcmp) {
			symbytes &= 0x7FFFFFFF;
			symstart = (char *)&edata;
			oldsyms = 1;
		} else symbytes = 0;
}


/*
 * showsym: lookup the typed-in symbol in the symbol table which is
 *          mapped in at DEBUG_VA
 *	    Return the value of the symbol or 0 if it is not found
 */
int
showsym(namep, printSymVal)
/*A5*/	register char	*namep;  /* Possibly NULL ptr to sought-after sym   */
/*D7*/	register int	printSymVal;	/* 0==> DON'T print value of symbol */
{
	char		symbol[64];
/*A4*/	register char	*sp	= &symbol[0];
/*D6*/	register u_long	symval	= 0 ;	/* Value of sought-after symbol */
	extern	 int	symbytes,
			oldsyms;

	if (symbytes == 0) {
		printf("\nSorry, there is no symbol table.\n");
		return(0);
	}

	for (;;)
	{
/*A3*/		register char	* cp	=  namep;
/*A2*/		register char	* cpMax	= &namep[sizeof(symbol)-1]; /* Last slot */

		/*
		 * Either use the symbol from our invocation call,
		 * or get symbol from operator's console.
		 */
		if (namep) {
			/*
			 * Use the symbol from the parameter list:
			 * Copy symbol to our local buffer.
			 */
			while (*cp && (cp < cpMax))
				*sp++ = *cp++;
			/*
			 * Now pointing at the NUL terminator character.
			 * Compensate for subsequent '*--sp'
			 * NOTE:  Also deals with degenerate case: no symbol.
			 */
			sp++;
			break;

		} else {
			/*
			 * Get symbol from operator's console.
			 */
			*sp = getchar();
		}

		if (*sp == '\b')
		{
			if (sp == &symbol[0])
				printf("=");
			else
				--sp, printf(" \b");
			continue;
		}

		if (sp == &symbol[sizeof(symbol)-1]) sp--;

		/*
		 * Check for termination condition -- end-of-symbol.
		 */
		if (*sp++ == '=' || sp[-1] == '\n')
			break;
	}

	*--sp = '\0';		/* NUL terminate the symbol */

	/*
	 * Avoid any further processing if no symbol
	 * has been specified -- maybe user changed his mind.
	 */
	if (sp == &symbol[0]) {
		printf(" No symbol specified. ");
		return NULL;
	}
	if (oldsyms) {
		register char *symp = (char *)symstart;
		register int count = symbytes;
		int i,ndef = 0;

		while (count > 0) {
/*D5*/			register int length = 6 + strlen(symp+6)+1;

			if (strcmp(symp+6,symbol) == 0) {
/*D4*/				register char type;

				++ndef;
				switch (*symp) {
				case UNDEF:	   type = 'u'; break;
				case ABS:	   type = 'a'; break;
				case TEXT:	   type = 't'; break;
				case DATA:	   type = 'd'; break;
				case BSS:	   type = 'b'; break;
				case UNDEF|EXTERN: type = 'U'; break;
				case ABS|EXTERN:   type = 'A'; break;
				case TEXT|EXTERN:  type = 'T'; break;
				case DATA|EXTERN:  type = 'D'; break;
				case BSS|EXTERN:   type = 'B'; break;
				default:	   type = '?'; break;
				}

				symval = 0;
				for (i=0; i<4; i++)
				    symval = (symval << 8)+(u_char)symp[i+2];
				/*
				 * Printf only if we were so told.
				 */
#ifdef	QUICKEN
				if (printSymVal)
#endif	QUICKEN
					printf("  0x%x %c", symval, type);
			}
			symp  += length;
			count -= length;
		}
		if (ndef == 0)	    printf(" no such symbol\n");
		else if (ndef == 1)
			if (printSymVal) printf("\n");
			else	;
		else		    printf("  (multiple definitions)\n");
	}
	else {
		printf("\nSorry, new format symbol table lookup is not yet\n");
		printf("implemented.\n");
	}
	return(symval);
}

/*
 * Find the symbol whose value is closest to,
 * but not greater than a value and PRINT the symbol
 * name and offset.
 */
showoffset(value)
   register u_long value;
{
	register u_long offset = 0xFFFFFFFF;
	char *symbol;
	extern int symbytes, oldsyms;

	if (symbytes == 0) {
		printf ("0x%x", value);
		return;
	}
	if (oldsyms) {
		register char *symp = (char *)symstart;
		register int count = symbytes;

		while (count > 0) {
			register int i;
			register u_long symval = 0;
			register int length = 6 + strlen(symp+6)+1;

			for (i=0; i<4; i++)
			    symval = (symval << 8)+(u_char)symp[i+2];
			if (symval <= value && (value - symval) < offset)
				offset = value - symval, symbol = symp + 6;
			symp  += length;
			count -= length;
		}
	}
	else {
		printf("\nSorry, new format symbol table lookup is not yet\n");
		printf("implemented.\n");
		return;
	}
	if (offset == (u_long)0xFFFFFFFF)
		printf(" That is below all symbols\n");
	else if (offset == 0)
		printf("0x%10X: %s", value, symbol);
	else
		printf("0x%10X: %s+0x%x", value, symbol, offset);
}
