[comp.os.minix] Minix Debugger for the ATARI ST

bds@lzaz.ATT.COM (B.SZABLAK) (12/22/88)

/*
 * mdb.c - MINIX program debugger
 *
 * Written by Bruce D. Szablak
 *
 * This free software is provided for non-commerical use. No warrantee
 * of fitness for any use is implied. You get what you pay for. Anyone
 * may make modifications and distribute them, but please keep this header
 * in the distribution.
 */

#define T_OK		0
#define T_GETINS	1
#define T_GETDATA	2
#define T_GETUSER	3
#define	T_SETINS	4
#define T_SETDATA	5
#define T_SETUSER	6
#define T_RESUME	7
#define T_EXIT		8
#define T_STEP		9

#include "stdio.h"
#include "signal.h"
#include "ctype.h"
#include "setjmp.h"
#include "user.h"

struct proc *prc;

#define BP_REG	14
#define MAXLINE 120
#define REG(r) ((long)&((struct proc *)0)->p_reg[r])
#define PC ((long)&((struct proc *)0)->p_pcpsw.pc)
#define MAXB 10
#define MAXP 3
#define MAXARG 20
#define SIZ (1 + sizeof(struct proc)/sizeof(long))
#define MAXLINE 120

long ptrace(), strtol(), reg_addr(), lbuf[SIZ], saddr, eaddr, dasm();
int curpid, cursig;
char	*strcpy(), *prog, sbuf[MAXLINE], cbuf[MAXLINE], *cmd, *addr_to_name(),
	*getexp(), *cmdstart;
jmp_buf mainlp;
extern errno;

struct b_pnt
{
	struct b_pnt *nxt, *prv;
	long addr;
	long oldval;
	char cmd[1];
} *b_head, *curpnt;

cleanup()
{
	curpid = 0;
	curpnt = 0;
	while (b_head) freepnt(b_head);
}

dowait()
{
	int stat, ls, hs;

	while (wait(&stat) != curpid) {};
	ls = stat & 0xFF;
	hs = (stat >> 8) & 0xFF;
	if (ls == 0)
	{
		if (hs != 127)
			printf("child exited with status %d\n", hs);
		cleanup();
		return 0;
	}
	if (hs == 0)
	{
		printf("child terminated by signal %d\n", ls & 0x7F);
		if (ls & 0x80) printf("(core dumped)\n");
		cleanup();
		return 0;
	}
	return cursig = hs;
}
		
update()
{
	int i;

	for (i = 0; i < (SIZ-1); i++)
	{
		lbuf[i] = ptrace(T_GETUSER, curpid, (long)(i*sizeof(long)),0L);
	}
	saddr = (long)prc->p_map[T].mem_vir << CLICK_SHIFT;
	eaddr = (long)(prc->p_map[D].mem_vir + prc->p_map[D].mem_len)
							<< CLICK_SHIFT;
}

findbpnt(verbose)
{
	for (curpnt = b_head; curpnt; curpnt = curpnt->nxt)
	{
		if (curpnt->addr == prc->p_pcpsw.pc)
		{
			ptrace(T_SETINS, curpid, curpnt->addr, curpnt->oldval);
			if (curpnt->cmd[0] != '\n')
				cmd = strcpy(cbuf, curpnt->cmd);
			else if (verbose)
				printf("Breakpoint hit.\n");
			return;
		}
	}
	if (verbose) printf("Unknown breakpoint hit.\n");
}

disp_regs()
{
	int i;

	if (curpid <= 0)
	{
		printf("No active process.\n");
		return 1;
	}
	printf("\npc=%lx psw=%x\n\n",(long)prc->p_pcpsw.pc, prc->p_pcpsw.psw);
	printf(
"      0        1        2        3        4        5        6        7\nD");
	for (i = 0; i < 8; i++) printf(" %08lx", prc->p_reg[i]);
	printf("\nA");
	for (; i < NR_REGS; i++) printf(" %08lx", prc->p_reg[i]);
	printf(" %08lx\n\n", (long)prc->p_sp);
	return 0;
}

char *
skip(s)
	register char *s;
{
	while (isspace(*s)) ++s;
	return *s ? s : s-1;
}

endcmd()
{
	while (*cmd != '\n' && *cmd != ';') ++cmd;
}

#define BREAK(l) (0xA0000000 | ((l) & 0xFFFF))

exebpnt(restart)
{
	ptrace(T_STEP, curpid, 0L, (long)restart);
	if (dowait() == 0) return 1;
	ptrace(T_SETINS, curpid, curpnt->addr, BREAK(curpnt->oldval));
	curpnt = 0;
	return 0;
}

tstart(req, verbose, val, cnt)
{
	if (curpid == 0)
	{
		if (verbose) printf("No active process.\n");
		return;
	}
	if (req == T_EXIT)
	{
		ptrace(T_EXIT, curpid, 0L, (long)val);
		dowait();
		return;
	}
	if (cnt==0) cnt = 1;
	do
	{
		if (curpnt)
		{
			if (exebpnt(val)) return;
			if (req == T_RESUME) cnt++;
			val = 0;
		}
		else
		{
			ptrace(req, curpid, 0L, (long)val);
			if (dowait() == 0) return;
			val = 0;
			switch (cursig)
			{
			case	SIGEMT: /* breakpoint */
				update();
				findbpnt(cnt <= 1);
				break;
			case	SIGTRAP: /* trace trap? */
				if (req == T_STEP) break;
			default: /* signal */
				val = cursig;
				break;
			}
		}
	}
	while (--cnt > 0);
	update();
	dasm((long)prc->p_pcpsw.pc, 1, 1);
}

catch(sig)
{
	signal(sig, catch);
	if (sig == SIGINT || sig == SIGQUIT) return;
	tstart(T_EXIT, 0, sig, 0);
	exit(0);
}

run(name, argstr, tflg)
	char *name, *argstr;
{
	int procid;

	if( (procid = fork()) == 0 )
	{
		char *argv[MAXARG], *inf = 0, *outf = 0;
		int argc;

		if (tflg && ptrace(T_OK, 0, 0L, 0L) < 0)
		{
			perror("ptrace");
			exit(127);
		}
		argv[0] = name;
		for (argc = 1; ; )
		{
			argstr = skip(argstr);
			if (*argstr == '\n' || *argstr == ';')
			{
				argv[argc] = 0;
				if (inf) freopen(inf, "r", stdin);
				if (outf) freopen(outf, "w", stdout);
			        execv(name, argv);
				perror("execv");
			        exit( 127 );
			}
			if (*argstr == '<') inf = argstr+1;
			else if (*argstr == '>') outf = argstr+1;
			else if (argc == MAXARG)
			{
				printf("Too many arguments.\n");
				exit(127);
			}
			else argv[argc++] = argstr;
			while (!isspace(*argstr)) argstr++;
			if (*argstr == '\n') argstr[1] = '\n', argstr[2] = 0;
			*argstr++ = 0;
		}
	}
	if (procid < 0)
	{
		printf("Fork failed.\n");
	}
	return procid;
}

#define ADDQ(l) (((l >> 16) & 0xF1FF) == 0x508F)
#define ADDQ_CNT(l) ((((l >> 25) - 1) & 7) + 1)
#define LEA(l) ((l >> 16) == 0x4FEF)
#define LEA_DISP(l) ((long)(int)l)
#define ADDA(l) ((int)(l >> 16) == 0xDFFC)

backtrace(all)
{
	char sep;
	long pc, bp, off, val, obp;

	if (curpid <= 0)
	{
		printf("No process.\n");
		return;
	}
	pc = prc->p_pcpsw.pc;
	bp = prc->p_reg[BP_REG];
	if (bp == 0)
	{
		printf("No active frame.\n");
		return;
	}
	errno = 0;
	do
	{
		symbolic(pc, '(');
		pc = ptrace(T_GETDATA, curpid, bp+4, 0L);
		off = ptrace(T_GETDATA, curpid, pc, 0L);
		obp = bp;
		bp += 2*sizeof(val);
		if (ADDQ(off))
			off = ADDQ_CNT(off) + bp;
		else if (LEA(off))
			off = LEA_DISP(off) + bp;
		else if (ADDA(off))
			off = ptrace(T_GETDATA, curpid, pc+2, 0L) + bp;
		else
			goto skiplp;

		for (;;)
		{
			if (errno) return;
			val = ptrace(T_GETDATA, curpid, bp, 0L) >> 16;
			printf("0x%04x", (int)val);
			bp += sizeof(int);
			if (bp >= off) break;
			putc(',', stdout);
		}

		skiplp:

		fputs(")\n", stdout);
		bp = ptrace(T_GETDATA, curpid, obp, 0L);
	}
	while (all && bp);
}

freepnt(pnt)
	struct b_pnt *pnt;
{
	if (pnt->prv) pnt->prv->nxt = pnt->nxt;
	else b_head = pnt->nxt;
	if (pnt->nxt) pnt->nxt->prv = pnt->prv;
	if (curpid > 0) ptrace(T_SETINS, curpid, pnt->addr, pnt->oldval);
	free(pnt);
	if (pnt == curpnt) curpnt = 0;
}

breakpt(addr, cmd)
	long addr;
	char *cmd;
{
	char *s;
	struct b_pnt *new, *malloc();
	int lng;

	if (curpid <= 0)
	{
		printf("No active process.\n");
		return;
	}
	for (new = b_head; new; new=new->nxt)
		if (new->addr == addr)
		{
			printf("Breakpoint already exists here.\n");
			return;
		}
	new = malloc(sizeof(struct b_pnt) + strlen(cmd));
	if (new == 0)
	{
		printf("No room.\n");
		return;
	}
	new->nxt = b_head;
	new->prv = 0;
	if (b_head) b_head->prv = new;
	b_head = new;
	new->addr = addr;
	strcpy(new->cmd, cmd);
	new->oldval = ptrace(T_GETINS, curpid, addr, 0L);
	ptrace(T_SETINS, curpid, addr, BREAK(new->oldval));
	if (ptrace(T_GETINS, curpid, addr, 0L) != BREAK(new->oldval))
	{
		perror("Can't set breakpoint");
		freepnt(new);
	}
}

modify(addr, cnt, verbose)
	long addr;
{
	long curval, off;

	if (curpid == 0)
	{
		printf("No active process.\n");
		return;
	}
	curval = ptrace(T_GETDATA, curpid, addr, 0L);
	do
	{
		if (cursig == SIGTRAP) cursig = 0;
		if (verbose)
		{
			off = ptrace(T_GETUSER, curpid, PC, 0L);
			dasm(off, 1, 0);
		}
		if (curpnt && exebpnt(cursig)) return;
		else
		{
			ptrace(T_STEP, curpid, addr, 0L);
			switch (dowait())
			{
			case	0: return;
			case	SIGEMT: update(); findbpnt(0); break;
			}
		}
		if (curval != ptrace(T_GETDATA, curpid, addr, 0L))
		{
			printf("Modification detected\n");
			break;
		}
	}
	while (--cnt);
	update();
	dasm(prc->p_pcpsw.pc, 1, 1);
	return;
}

display(addr, req)
	long addr;
{
	int count, size, out, shift;
	long val, msk;
	char fmt;

	if (curpid == 0)
	{
		printf("No active process\n");
		return;
	}
	count = strtol(cmd, &cmd, 0);
	if (count == 0) count = 1;
	if (*cmd == 'i' || *cmd == 'I')
	{
		if (req == T_GETUSER)
		{
			printf("Can't disassemble a register's contents.\n");
			longjmp(mainlp);
		}
		dasm(addr, count, *cmd == 'i');
		return;
	}
	switch (*cmd++)
	{
	case	'b': size = sizeof(char); break;
	case	'h': size = sizeof(short); break;
	case	'l': size = sizeof(long); break;
	default	   : size = sizeof(int); --cmd; break;
	}
	switch (fmt = *cmd)
	{
	case	'c': count *= size; size = sizeof(char); break;
	case	's': addr = ptrace(req, curpid, addr, 0L); req = T_GETDATA;
	case	'a': size = sizeof(char);
		     break;
	}
	out = 0;
	msk = size == sizeof(long) ? 0xFFFFFFFF : (1L << (8*size)) - 1;
	shift = 32 - 8*size;
	do
	{
		val = (ptrace(req, curpid, addr, 0L) >> shift) & msk;
		if (out == 0) printf("\n0x%08lx: ", addr);
		switch (fmt)
		{
		case	'c':
			printf(isprint(val) ? "   %c " : "\\%03o ", (char)val);
			if (++out == 8) out = 0;
			break;
		case	'u':
			printf("%12lu ", val); if (++out == 4) out = 0; break;
		case	'x':
			printf("%*lx ", 2*size, val);
			if (++out == (size == 4 ? 4 : 8)) out = 0;
			break;
		case	'o':
			printf("%*lo ", 3*size, val);
			if (++out == (size == 4 ? 4 : 8)) out = 0;
			break;
		case	's':
		case	'a':
			if (val) fputc((char)val, stdout);
			else goto exitlp;
			if (++out == 64) out = 0;
			break;
		default:
		case	'd':
			printf("%12ld ", val); if (++out == 4) out = 0; break;
		}
		addr += size;
	}
	while (--count > 0 || fmt == 's' || fmt == 'a');
exitlp:
	fputc('\n', stdout);
}

fill(addr, req)
	long addr;
{
	int count, size, shift, seg;
	long val, msk, nval;
	char fmt;

	if (curpid == 0)
	{
		printf("No active process\n");
		return;
	}
	count = strtol(cmd, &cmd, 0);
	switch (*cmd++)
	{
	case	'b': size = sizeof(char); break;
	case	'h': size = sizeof(short); break;
	case	'l': size = sizeof(long); break;
	default	   : size = sizeof(int); --cmd; break;
	}
	shift = 32 - 8*size;
	msk = (0x80000000 >> 8*size);
	cmd = getexp(cmd, &nval, &seg);
	nval <<= shift;
	do
	{
		val = ptrace(req, curpid, addr, 0L) | (nval & msk);
		val &= (nval | ~msk);
		ptrace(req+3, curpid, addr, val);
		addr += size;
	}
	while (--count>0);
}

command(savestr)
	char *savestr;
{
	char c, *p;
	int i, seg;
	long exp;
	struct b_pnt *bp;

	seg = S; /* don't restrict segment expressions are in */
	cmdstart = cmd = skip(cmd);
	cmd = getexp(cmd, &exp, &seg);
	if (exp == 0)
	{
		seg = T;
		exp = prc->p_pcpsw.pc;
	}
	switch (c = *cmd++)
	{
	case '!': /* escape to shell OR set variable to value */
		if (cmd == cmdstart+1)
		{
			cmd = skip(cmd);
			if (*cmd == '\n' || *cmd == ';')
			{
				i = run("/bin/sh", "\n", 0);
			}
			else
			{
				for (p=cmd+1; *p && !isspace(*p); p++) {};
				*p++ = 0;
				i = run(cmd, *p ? p : "\n", 0);
			}
			if (i > 0) while (wait(&seg) != i) {};
			break;
		}
		if (seg == 'T')
		{
			printf("Can only modify data variables.\n");
			break;
		}
		fill(exp, T_GETDATA);
		break;
	case 'T': /* top line of backtrace */
		backtrace(0);
		break;
	case 't': /* back trace */
		backtrace(1);
		break;
	case '/': /* print variable value */
		display(exp, T_GETDATA);
		break;
	case 'x': /* print registers and instruction */
		if (disp_regs()) break;
		/*FALLTHROUGH*/
	case 'X': /* print instruction */
		i = strtol(cmd, &cmd, 0);
		if (curpid > 0)
			dasm(exp, i ? i : 1, 1);
		else
			printf("No active process.\n");
		break;
	case 'R': /* run program with no args */
	case 'r': /* run program with args (possibly defaulted) */
		tstart(T_EXIT, 0, 0, 0);
		if (c == 'r')
		{
			cmd = skip(cmd);
			if (*cmd == '\n' || *cmd == ';') cmd = sbuf;
			else strcpy(sbuf, cmd);
		}
		else
		{
			cmd = "\n";
		}
		if (curpid = run(prog, cmd, 1))
		{
			if (dowait())
			{
				ptrace(T_SETUSER, curpid, REG(BP_REG), 0L);
				update();
				printf("Process stopped.\n");
			}
		}
		break;
	case 'c': /* continue program - ignore signal */
		cursig = 0;
	case 'C': /* continue program - handle signal */
		i = 0;
		if (seg == T && curpnt == 0 && cmd != cmdstart+1)
		{
			breakpt(exp, "\n");
			curpnt = b_head;
			ptrace(T_SETINS, curpid, curpnt->addr, curpnt->oldval);
			i = 1;
		}
		tstart(T_RESUME, 1, cursig, (int)strtol(cmd, &cmd, 0));
		if (i) /* remove temporary bp */
		{
			freepnt(b_head);
		}
		if (cursig == SIGEMT) return;
		if (curpid) printf("Process stopped by signal %d\n", cursig);
		break;
	case 'i': /* single step - ignore signal */
		tstart(T_STEP, 1, 0, (int)strtol(cmd, &cmd, 0));
		break;
	case 'I': /* single step - handle signal */
		tstart(T_STEP, 1, cursig, (int)strtol(cmd, &cmd, 0));
		break;
	case 'm': /* single step until location modified */
	case 'M': /* single step until location modified - verbose */
		modify(exp, (int)strtol(cmd, &cmd, 0), c == 'M');
		break;
	case 'k': /* kill current program */
		tstart(T_EXIT, 1, 0, 0);
		break;
	case 'b': /* set a breakpoint at the given line */
		if (seg == D)
		{
			printf("Address not in text space.\n");
			return;
		}
		breakpt(exp, skip(cmd));
		cmd = "\n";
		return;
	case 'B': /* print list of currently active breakpoints */
		for (i = 1, bp = b_head; bp; bp=bp->nxt, i++)
		{
			p = addr_to_name(bp->addr - saddr, &exp);
			printf("%2d: %s+0x%lx (0x%lx) - %s",
				i, p, exp, bp->addr, bp->cmd);
		}
		break;
	case 'd': /* delete breakpoint */
		if (seg == T)
		{
			for (bp = b_head; bp && --exp; bp=bp->nxt) {};
			if (bp) { freepnt(bp); break; }
		}
		printf("No such breakpoint.\n");
		break;
	case 'D': /* delete all breakpoints */
		while(b_head) freepnt(b_head);
		break;
	case 'q': /* quit */
		tstart(T_EXIT, 0, 0, 0L, 0);
		exit(0);
	case '\n':
	case ';':
		if (isdigit(*cmdstart)) symbolic(exp, '\n');
		else printf("0x%lx\n", exp);
		--cmd;
		break;
	case '$':
		cmdstart = cmd;
		cmd = skip(cmd+2);
		if (*cmd == '!')
		{
			cmd++;
			fill(reg_addr(cmdstart), T_GETUSER);
			break;
		}
		if (*skip(cmd+2) == '/')
		{
			cmd++;
			display(reg_addr(cmdstart), T_GETUSER);
			break;
		}
		/*FALLTHROUGH*/
	default:
		printf("Unknown command.\n");
		break;
	}
	while (*cmd != '\n' && *cmd != ';') ++cmd;
	if (*cmd == ';') cmd = skip(cmd+1);
}

main(argc, argv)
	char *argv[];
{
	int i;

	prc  = lbuf;
	for (i = 1; i < NSIG; i++) signal(i, catch);
	strcpy(sbuf, "\n");
	prog = argc > 1 ? argv[1] : "a.out";
	if (argc > 2) getsyms(argv[2]);

	setjmp(&mainlp);
	while ((printf("* "), fflush(stdout),
			fgets(cbuf, sizeof(cbuf), stdin)) != EOF)
	{
		if (strlen(cbuf) == sizeof(cbuf)-1)
		{
			printf("Command line too long.\n");
			continue;
		}
		cmd = cbuf;
		command();
		while (*cmd != '\n') command();
	}
	tstart(T_EXIT, 0, 0, 0);
	exit(0);
}

bds@lzaz.ATT.COM (B.SZABLAK) (12/22/88)

/*
 * dbexp.c - MINIX expresion parser
 *
 * Written by Bruce D. Szablak
 *
 * This free software is provided for non-commerical use. No warrantee
 * of fitness for any use is implied. You get what you pay for. Anyone
 * may make modifications and distribute them, but please keep this header
 * in the distribution. NOTE: A PORTION OF THIS FILE IS DERIVED FROM THE
 * SOURCE TO ANM.C. BEWARE THAT OTHER RESTRICTIONS MAY APPLY.
 */

#include <ctype.h>
#include <setjmp.h>
#include <stdio.h>
#include "user.h"
#include "out.h"

extern jmp_buf mainlp;
extern curpid;

int nsyms;
struct outname *nbufp = NULL;
long value(), strtol(), lookup(), ptrace();

#define idchar(c) (isalpha(c) || isdigit(c) || (c) == '_')

char *
addr_to_name(rel_addr, off_p)
	long rel_addr, *off_p;
{
	register int i, l = 0, r = nsyms;

	do
	{
		i = (l + r) >> 1;
		if (rel_addr < nbufp[i].on_valu) r = i;
		else if (rel_addr > nbufp[i].on_valu) l = i + 1;
		else break;
	}
	while (l < r);
	if (l == nsyms || r == 0)
	{
		*off_p = rel_addr;
		return "_start";
	}
	if (rel_addr < nbufp[i].on_valu) i--;
	*off_p = rel_addr - nbufp[i].on_valu;
	return nbufp[i].on_mptr;
}

symbolic(addr, sep)
	long addr;
	char sep;
{
	long off;
	extern long saddr, eaddr;

	if (addr < saddr || addr > eaddr)
	{
		printf("0x%lx%c", addr, sep);
		return;
	}
	fputs(addr_to_name(addr - saddr, &off), stdout);
	if (off) printf("+0x%lx", off);
	fputc(sep, stdout);
}

char *
getexp(buf, exp_p, seg_p)
	char *buf;
	int *seg_p;
	long *exp_p;
{
	extern char *skip();
	long v = 0L;

	buf = skip(buf);
	if ((isalpha(*buf) && (isspace(buf[1]) || buf[1]==';'))
		|| *buf=='\n' || *buf==';' || *buf=='/' || *buf == '!')
	{
		*exp_p = 0L;
		return buf;
	}
	v = value(buf, &buf, seg_p);
	buf = skip(buf);
	if (*buf == '+')
	{
		v += value(skip(buf+1), &buf, seg_p);
	}
	else if (*buf == '-')
	{
		v -= value(skip(buf+1), &buf, seg_p);
	}
	*exp_p = v;
	return skip(buf);
}

long
reg_addr(s)
	char *s;
{
	long val;

	switch (*s++)
	{
	case	'a': case 'A': val = 32; break;
	case	'd': case 'D': val = 0; break;
	default: goto error;
	}
	if (*s >= '0' && *s <= '7')
	{
		return val + 4*(*s - '0');
	}

error:
	printf("Unknown register: %2.2s\n", s);
	longjmp(mainlp);
}

long
value(s, s_p, seg_p)
	char *s, **s_p;
	int *seg_p;
{
	long k;

	if (*s == '\'') /* handle character constants here */
	{
		*s_p = s + 2;
		return s[1];
	}
	if (*s == '-' || isdigit(*s))
	{
		return strtol(s, s_p, 0);
	}
	if (*s == '$')
	{
		k = reg_addr(s+1);
		*s_p = s + 3;
		return ptrace(3, curpid, k, 0L);
	}
	return lookup(s, s_p, seg_p);
}

long
lookup(s, s_p, seg_p)
	char *s, **s_p;
	int *seg_p;
{
	extern long saddr;
	char c;
	int i, l;

	for (l = 1; idchar(s[l]); ++l ) {}
	c = s[l]; s[l] = 0;
	
	if (strcmp("_start", s) == 0)
	{
		*seg_p = T;
		*(*s_p = s + 6) = c;
		return saddr;
	}
	for (i = 0; i < nsyms; i++)
		if (strcmp(nbufp[i].on_mptr, s) == 0)
		{
			*seg_p = (nbufp[i].on_type & S_TYP) == S_MIN ? T : D;
			*(*s_p = s + l) = c;
			return nbufp[i].on_valu + saddr;
		}

	printf("%s: symbol not found\n", s);
	longjmp(mainlp);
}

/******************END OF ORIGINAL CODE***********************/

/*
 * The following code is actually a highly edited
 * version of anm.c - The ACK version of nm
 * That file's header follows:
 */

/* @(#)anm.c	1.6 */
/*
**	print symbol tables for
**	ACK object files
**
**	anm [-gopruns] [name ...]
*/

#define	ushort	unsigned short
#define	ushort	unsigned short

long	off;
char	*malloc();
char	*realloc();
long	s_base[S_MAX];	/* for specially encoded bases */

getsyms(file)
	char *file;
{
	int	compare();
	FILE		*fi;
	struct	outsect	sbuf;
	struct	outhead	hbuf;
	struct	outname	nbuf;
	char		*cbufp;
	long		fi_to_co;
	long		n;
	unsigned	readcount;
	int		j;

	fi = fopen(file,"r");
	if (fi == NULL) {
		fprintf(stderr, "db: cannot open %s\n", file);
		return;
	}

	getofmt((char *)&hbuf, SF_HEAD, fi);
	if (BADMAGIC(hbuf)) {
		fprintf(stderr, "db: %s-- bad format\n", file);
		fclose(fi);
		return;
	}

	n = hbuf.oh_nname;
	if (n == 0) {
		fprintf(stderr, "db: %s-- no name list\n", file);
		fclose(fi);
		return;
	}

	if (hbuf.oh_nchar == 0) {
		fprintf(stderr, "db: %s-- no names\n", file);
		fclose(fi);
		return;
	}
	if ((readcount = hbuf.oh_nchar) != hbuf.oh_nchar) {
		fprintf(stderr, "db: string area too big in %s\n", file);
		exit(2);
	}

	/* store special section bases */
	if (hbuf.oh_flags & HF_8086) {
		int i;
		for (i=0; i<hbuf.oh_nsect; i++) {
			getofmt((char *)&sbuf, SF_SECT, fi);
			s_base[i+S_MIN] =
				(sbuf.os_base>>12) & 03777760;
		}
	}
		 
	if ((cbufp = (char *)malloc(readcount)) == NULL) {
		fprintf(stderr, "db: out of memory on %s\n", file);
		exit(2);
	}
	fseek(fi, OFF_CHAR(hbuf), 0);
	if (fread(cbufp, 1, readcount, fi) == 0) {
		fprintf(stderr, "db: read error on %s\n", file);
		exit(2);
	}
	fi_to_co = (long)cbufp - OFF_CHAR(hbuf);

	fseek(fi, OFF_NAME(hbuf), 0);
	nsyms = 0;
	while (--n >= 0) {
		getofmt((char *)&nbuf, SF_NAME, fi);

		if (nbuf.on_foff == 0)
			continue; /* skip entries without names */

		if ((nbuf.on_type&S_EXT)==0)
			continue;

		nbuf.on_mptr = (char *)(nbuf.on_foff + fi_to_co);

		/* adjust value for specially encoded bases */
		if (hbuf.oh_flags & HF_8086) {
		    if (((nbuf.on_type&S_ETC) == 0) ||
			((nbuf.on_type&S_ETC) == S_SCT)) {
			j = nbuf.on_type&S_TYP;
			if ((j>=S_MIN) && (j<=S_MAX))
			    nbuf.on_valu += s_base[j];
		    }
		}

		if (nbufp == NULL)
			nbufp = (struct outname *)malloc(sizeof(struct outname));
		else
			nbufp = (struct outname *)realloc(nbufp, (nsyms+1)*sizeof(struct outname));
		if (nbufp == NULL) {
			fprintf(stderr, "db: out of memory on %s\n", file);
			exit(2);
		}
		nbufp[nsyms++] = nbuf;
	}

	qsort(nbufp, nsyms, sizeof(struct outname), compare);

	fclose(fi);
}

compare(p1, p2)
struct outname	*p1, *p2;
{
	if (p1->on_valu < p2->on_valu) return -1;
	if (p1->on_valu > p2->on_valu) return 1;
	return 0;
}

getofmt(p, s, f)
register char	*p;
register char	*s;
register FILE	*f;
{
	register i;
	register long l;

	for (;;) {
		switch (*s++) {
/*		case '0': p++; continue; */
		case '1':
			*p++ = getc(f);
			continue;
		case '2':
			i = getc(f);
			i |= (getc(f) << 8);
			*((short *)p) = i; p += sizeof(short);
			continue;
		case '4':
			l = (long)getc(f);
			l |= ((long)getc(f) << 8);
			l |= ((long)getc(f) << 16);
			l |= ((long)getc(f) << 24);
			*((long *)p) = l; p += sizeof(long);
			continue;
		default:
		case '\0':
			break;
		}
		break;
	}
}

bds@lzaz.ATT.COM (B.SZABLAK) (12/22/88)

/*
 * mdbdis.c - MINIX program disassembler for the debugger
 *
 * Written by Bruce D. Szablak
 *
 * This free software is provided for non-commerical use. No warrantee
 * of fitness for any use is implied. You get what you pay for. Anyone
 * may make modifications and distribute them, but please keep this header
 * in the distribution.
 */

#include <stdio.h>

#define BYTE 0
#define WORD 1
#define LONG 2

#define BFIELD(w,b,l) (((w) >> ((b)-(l)+1)) & (int)((1L<<(l))-1))
#define BTST(w,b) ((w) & (1 << (b)))
char opwfmt[] = "%s.%c\t";
#define OPI(s,w) printf(opwfmt, s, w)

char size[] = "bwl";
char *cc[] = {"ra", "f", "hi", "ls", "cc", "cs", "ne", "eq", "vc", "vs",
	"pl", "mi", "ge", "lt", "gt", "le" };

char *bitop[] = { "btst", "bchg", "bclr", "bset" };
char *imedop[] = { "ori", "andi", "subi", "addi", "?", "eori", "cmpi" };
char *misc1[] = { "negx", "clr", "neg", "not" };
char *misc2[] = { "reset", "nop", "stop", "rte", "??", "rts", "trapv", "rtr" };
char *shf[] = { "as", "ls", "rox", "ro" };

char *fmts[] = { "d%d", "a%d", "(a%d)", "(a%d)+", "-(a%d)" };

extern curpid;
extern char *addr_to_name();
extern long saddr, eaddr;

long gaddr, gbuf, ptrace();
int gempty, gisize, gjmp;

short
gword()
{
	if (gempty)
	{
		gempty = 0;
		gbuf = ptrace(1, curpid, gaddr, 0L);
		gaddr += 2;
		return gbuf >> 16;
	}
	gempty = 1;
	gaddr += 2;
	return gbuf;
}

long
glong()
{
	gempty = 1;
	gbuf = ptrace(1, curpid, gaddr, 0L);
	gaddr += 4;
	return gbuf;
}

reladdr(sep)
	char sep;
{
	int d = gword();
	symbolic(gaddr + d - 2, sep);
}

movem(from, predec, rmsk)
{
	int b, f = 0;

	if (!from) putc(',', stdout);
	for (b = 16; b--; rmsk >>= 1)
	{
		if (rmsk & 1)
		{
			if (f) putc('/', stdout);
			else f = 1;
			if (predec) printf("%c%d", b>7 ? 'a' : 'd', b % 8);
			else printf("%c%d", b>7 ? 'd' : 'a', 7 - b % 8);
		}
	}
	if (from) putc(',', stdout);
}

op1(mode,reg)
{
	int	d;
	char	*s;
	long	l;

	if (mode < 5)
	{
		printf(fmts[mode], reg);
		return;
	}
	if (mode == 5)
	{
		printf("%d(a%d)", gword(), reg);
		return;
	}
	if (mode == 6)
	{
		d = gword();
		printf("%d(a%d,%c%d.%c)",
			BFIELD(d,7,8) | (BTST(d,7) ? 0xFF00 : 0), reg,
			BTST(d,15) ? 'a' : 'd', BFIELD(d,14,3),
			BTST(d,11) ? 'l' : 'w');
		return;
	}
	switch (reg)
	{
	case 0:	printf("%d", gword()); break;
	case 1:	symbolic(glong(), '\0'); break;
	case 2: l = gaddr;
		printf("%d(pc) {", d = gword());
		symbolic(l+d, '}');
		break;
	case 3: d = gword();
		printf("%d(pc,%c%d.%c)",
			BFIELD(d,7,8) | (BTST(d,7) ? 0xFF00 : 0),
			BTST(d,15) ? 'a' : 'd', BFIELD(d,14,3),
			BTST(d,11) ? 'l' : 'w');
		break;
	case 4: putc('#', stdout);
		if (gisize == LONG) symbolic(glong(), '\0');
		else if (gisize == BYTE) printf("%d", (char)gword());
		else printf("%d", gword());
		break;
	case 5: printf("sr"); break;
	}
}

op2(f,m1,r1,m2,r2) /* f set means order passed, clear reverses order */
{
	if (f) op1(m1,r1); else op1(m2,r2);
	putc(',',stdout);
	if (f) op1(m2,r2); else op1(m1,r1);
}

opmode(op, opm, reg, m, r)
	char *op;
{
	OPI(op, size[gisize=BFIELD(opm,1,2)]);
	op2(BTST(opm,2),0,reg,m,r);
}

long
dasm(addr,cnt, symflg)
	long addr;
{
	int	tflg = 0;
	register unsigned int w, m1, m2, r1, r2, op;
	char 	ds;

	gaddr = addr; gempty = 1;
	while (tflg || cnt--)
	{
		tflg = 0;
		if (symflg) symbolic(gaddr, '\t');
		else printf("0x%lx ", gaddr);
		w = gword();
		m1 = BFIELD(w,8,3); m2 = BFIELD(w,5,3);
		r1 = BFIELD(w,11,3); r2 = BFIELD(w,2,3);
		op = BFIELD(w,15,4);
		switch (op)
		{
		case 0x0:
			if (m2 == 1)
			{
				OPI("movep", BTST(w,6) ? 'l' : 'w');
				op2(BTST(w,7),0,r1,5,r2);
				break;
			}
			if (BTST(w,8))
			{
				OPI(bitop[BFIELD(w,7,2)], m2 ? 'b' : 'l');
				op2(1,0,r1,m2,r2);
				break;
			}
			if (r2 == 4)
			{
				OPI(bitop[BFIELD(w,7,2)], m2 ? 'b' : 'l');
				gisize = WORD;
				op2(1,7,4,m2,r2);
				break;
			}
			OPI(imedop[r1],size[gisize = m1]);
			op2(1,7,4,m2,r2);
			break;
		case 0x1:
			gisize = BYTE; goto domove;
		case 0x2:
			gisize = LONG; goto domove;
		case 0x3:
			gisize = WORD;

			domove:
				OPI("move", size[gisize]);
				op2(1,m2,r2,m1,r1);
			break;
		case 0x4:
			if (BTST(w,8))
			{
				if (BTST(w,6))
				{
					printf("lea\t");
					op1(m2,r2);
					printf(",a%d", r1);
					break;
				}
				printf("chk\t");
				op1(m2,r2);
				printf(",d%d", r1);
				break;
			}
			if (r1 < 4)
			{
				if (m1 == 3)
				{
					printf("move\t");
					gisize = WORD;
					if (r1 == 0) printf("sr,");
					op1(m2,r2);
					if (r1 == 2) printf(",ccr");
					if (r1 == 3) printf(",sr");
					break;
				}
				OPI(misc1[r1], size[m1]);
				op1(m2,r2);
				break;
			}
			else if (r1 == 4)
			{
				switch(m1)
				{
				case 0: printf("nbcd\t"); break;
				case 1: printf(m2 ? "pea\t" : "swap\t"); break;
				case 2:
				case 3:	OPI(m2 ? "movem" : "ext",
					    BTST(w,6) ? 'l' : 'w');
					if (m2) movem(1,m2==4, gword());
					break;
				}
				op1(m2,r2);
				break;
			}
			if (r1 == 5)
			{
				if (m1 == 3) printf("tas\t");
				else OPI("tst", size[m1]);
				op1(m2,r2);
				break;
			}
			if (r1 == 6)
			{
				OPI("movem", BTST(w,6) ? 'l' : 'w');
				op = gword();
				op1(m2,r2);
				movem(0,m2==4,op);
				break;
			}
			if (BTST(w,7))
			{
				printf(BTST(w,6) ? "jmp\t" : "jsr\t");
				op1(m2,r2);
				break;
			}
			switch (m2)
			{
			case 0:
			case 1:	printf("trap\t#%d", BFIELD(w,3,4));
				tflg = 1;
				break;
			case 2: printf("link\ta%d,#%d", r2, gword()); break;
			case 3: printf("unlk\ta%d", r2); break;
			case 4: printf("move.l a%d,usp", r2); break;
			case 5: printf("move.l usp,a%d", r2); break;
			case 6: printf(misc2[r2]); break;
			}
			break;
		case 0x5:
			if (BFIELD(w,7,2) == 3)
			{
				op = BFIELD(w,11,4);
				if (m2 == 1)
				{
					printf("db%s\td%d,",cc[op], r2);
					reladdr('\0');
				}
				else
				{
					printf("s%s\t",cc[op]);
					op1(m2,r2);
				}
			}
			else
			{
				printf("%sq\t#%d,",BTST(w,8)?"sub":"add",
					((r1 - 1) & 7) + 1);
				op1(m2,r2);
			}
			break;
		case 0x6:
			ds = BFIELD(w,7,8);
			printf("b%s.%c\t", cc[BFIELD(w,11,4)], ds ? 's' : 'w');
			if (m1) symbolic(gaddr+ds);
			else reladdr('\0');
			break;
		case 0x7:
			printf("moveq\t#%d,d%d",BFIELD(w,7,8),r1);
			break;
		case 0x8:
			if (m1 == 3 || m1 == 7)
			{
				printf("div%c\t", BTST(w,8) ? 's' : 'u');
				op2(0,0,r1,m2,r2);
			}
			else if (m1 == 4 && (m2 == 1 || m2 == 0))
			{
				printf(m2 ? "sbcd\t-(a%d),-(a%d)"
					  : "sbcd\td%d,d%d", r2, r1);
			}
			else
			{
				opmode("or",m1,r1,m2,r2);
			}
			break;
		case 0x9:
		case 0xD:
			if ((m2 == 0 || m2 == 1) && m1 > 3 && m1 < 7)
			{
				OPI(op == 9 ? "subx" : "addx",
					size[BFIELD(w,7,2)]);
				m2 <<= 2;
				op2(1,m2,r2,m2,r1);
			}
			else if (m1 == 3 || m1 == 7)
			{
				gisize = m1 == 3 ? WORD : LONG;
				OPI(op==9 ? "sub" : "add", size[gisize]);
				op2(1,m2,r2,1,r1);
			}
			else
			{
				opmode(op==9 ? "sub" : "add",m1,r1,m2,r2);
			}
			break;
		case 0xB:
			if (BTST(w,8))
			{
				if (m2 == 1)
				{
					OPI("cmpm", size[BFIELD(w,7,2)]);
					printf("(a%d)+,(a%d)+",r2,r1);
				}
				else
				{
					opmode("eor",m1,r1,m2,r2);
				}
			}
			else
			{
				opmode("cmp",m1,r1,m2,r2);
			}
			break;
		case 0xC:
			if (m1 == 3 || m1 == 7)
			{
				printf("mul%c\t", m1==7 ? 's' : 'u');
				op2(0,0,r1,m2,r2);
			}
			else if (m1 == 4 && (m2 == 1 || m2 == 0))
			{
				printf(m2 ? "abcd\t-(a%d),-(a%d)"
					  : "abcd\td%d,d%d", r2, r1);
			}
			else if (m1 == 5)
			{
				op = BTST(w,3) ? 'a' : 'd';
				printf("exg\t%c%d,%c%d",op,r1,op,r2);
			}
			else if (m1 == 6)
			{
				printf("exg\td%d,a%d",r1,r2);
			}
			else
			{
				opmode("and",m1,r1,m2,r2);
			}
			break;
		case 0xE:
			if (BFIELD(w,7,2) == 3)
			{
				printf("%s%c\t",shf[BFIELD(w,10,2)],
					BTST(w,8) ? 'l' : 'r');
				op1(m2,r2);
			}
			else
			{
				printf("%s%c\t",shf[BFIELD(w,4,2)],
					BTST(w,8) ? 'l' : 'r');
				if (BTST(w,5))
				{
					op2(1,0,r1,0,r2);
				}
				else
				{
					printf("#%d,",r1);
					op1(0,r2);
				}
			}
			break;
		case 0xA:
		case 0xF:
			printf("%x", w);
			break;
		}
		putc('\n',stdout);
	}
	return gaddr;
}

Steinsbo%hsr.uninett%NORUNIX.BITNET@cunyvm.cuny.edu (Bjarne Steinsbo) (01/04/89)

>X-Originator: "B.SZABLAK" <bds@lzaz.att.com>
>X-Bitnet-Sender:   info-minix-request@UDEL.EDU
>Organization: AT&T ISL Lincroft NJ USA


/*
 * mdb.c - MINIX program debugger
 *
 * Written by Bruce D. Szablak
 *
 * This free software is provided for non-commerical use. No warrantee
 * of fitness for any use is implied. You get what you pay for. Anyone
 * may make modifications and distribute them, but please keep this header
 * in the distribution.
 */

#define T_OK            0
#define T_GETINS        1
#define T_GETDATA       2
#define T_GETUSER       3
#define T_SETINS        4
#define T_SETDATA       5
#define T_SETUSER       6
#define T_RESUME        7
#define T_EXIT          8
#define T_STEP          9

#include "stdio.h"
#include "signal.h"
#include "ctype.h"
#include "setjmp.h"
#include "user.h"

struct proc *prc;

#define BP_REG  14
#define MAXLINE 120
#define REG(r) ((long)&((struct proc *)0)->p_regr|)
#define PC ((long)&((struct proc *)0)->p_pcpsw.pc)
#define MAXB 10
#define MAXP 3
#define MAXARG 20
#define SIZ (1 + sizeof(struct proc)/sizeof(long))
#define MAXLINE 120

long ptrace(), strtol(), reg_addr(), lbufSIZ|, saddr, eaddr, dasm();
int curpid, cursig;
char    *strcpy(), *prog, sbufMAXLINE|, cbufMAXLINE|, *cmd, *addr_to_name(),
        *getexp(), *cmdstart;
jmp_buf mainlp;
extern errno;

struct b_pnt
{
        struct b_pnt *nxt, *prv;
        long addr;
        long oldval;
        char cmd1|;
} *b_head, *curpnt;

cleanup()
{
        curpid = 0;
        curpnt = 0;
        while (b_head) freepnt(b_head);
}

dowait()
{
        int stat, ls, hs;

        while (wait(&stat) != curpid) {};
        ls = stat & 0xFF;
        hs = (stat >> 8) & 0xFF;
        if (ls == 0)
        {
                if (hs != 127)
                        printf("child exited with status %d\n", hs);
                cleanup();
                return 0;
        }
        if (hs == 0)
        {
                printf("child terminated by signal %d\n", ls & 0x7F);
                if (ls & 0x80) printf("(core dumped)\n");
                cleanup();
                return 0;
        }
        return cursig = hs;
}

update()
{
        int i;

        for (i = 0; i < (SIZ-1); i++)
        {
                lbufi| = ptrace(T_GETUSER, curpid, (long)(i*sizeof(long)),0L);
        }
        saddr = (long)prc->p_mapT|.mem_vir << CLICK_SHIFT;
        eaddr = (long)(prc->p_mapD|.mem_vir + prc->p_mapD|.mem_len)
                                                        << CLICK_SHIFT;
}

findbpnt(verbose)
{
        for (curpnt = b_head; curpnt; curpnt = curpnt->nxt)
        {
                if (curpnt->addr == prc->p_pcpsw.pc)
                {
                        ptrace(T_SETINS, curpid, curpnt->addr, curpnt->oldval);
                        if (curpnt->cmd0| != '\n')
                                cmd = strcpy(cbuf, curpnt->cmd);
                        else if (verbose)
                                printf("Breakpoint hit.\n");
                        return;
                }
        }
        if (verbose) printf("Unknown breakpoint hit.\n");
}

disp_regs()
{
        int i;

        if (curpid <= 0)
        {
                printf("No active process.\n");
                return 1;
        }
        printf("\npc=%lx psw=%x\n\n",(long)prc->p_pcpsw.pc, prc->p_pcpsw.psw);
        printf(
"      0        1        2        3        4        5        6        7\nD");
        for (i = 0; i < 8; i++) printf(" %08lx", prc->p_regi|);
        printf("\nA");
        for (; i < NR_REGS; i++) printf(" %08lx", prc->p_regi|);
        printf(" %08lx\n\n", (long)prc->p_sp);
        return 0;
}

char *
skip(s)
        register char *s;
{
        while (isspace(*s)) ++s;
        return *s ? s : s-1;
}

endcmd()
{
        while (*cmd != '\n' && *cmd != ';') ++cmd;
}

#define BREAK(l) (0xA0000000 | ((l) & 0xFFFF))

exebpnt(restart)
{
        ptrace(T_STEP, curpid, 0L, (long)restart);
        if (dowait() == 0) return 1;
        ptrace(T_SETINS, curpid, curpnt->addr, BREAK(curpnt->oldval));
        curpnt = 0;
        return 0;
}

tstart(req, verbose, val, cnt)
{
        if (curpid == 0)
        {
                if (verbose) printf("No active process.\n");
                return;
        }
        if (req == T_EXIT)
        {
                ptrace(T_EXIT, curpid, 0L, (long)val);
                dowait();
                return;
        }
        if (cnt==0) cnt = 1;
        do
        {
                if (curpnt)
                {
                        if (exebpnt(val)) return;
                        if (req == T_RESUME) cnt++;
                        val = 0;
                }
                else
                {
                        ptrace(req, curpid, 0L, (long)val);
                        if (dowait() == 0) return;
                        val = 0;
                        switch (cursig)
                        {
                        case    SIGEMT: /* breakpoint */
                                update();
                                findbpnt(cnt <= 1);
                                break;
                        case    SIGTRAP: /* trace trap? */
                                if (req == T_STEP) break;
                        default: /* signal */
                                val = cursig;
                                break;
                        }
                }
        }
        while (--cnt > 0);
        update();
        dasm((long)prc->p_pcpsw.pc, 1, 1);
}

catch(sig)
{
        signal(sig, catch);
        if (sig == SIGINT || sig == SIGQUIT) return;
        tstart(T_EXIT, 0, sig, 0);
        exit(0);
}

run(name, argstr, tflg)
        char *name, *argstr;
{
        int procid;

        if( (procid = fork()) == 0 )
        {
                char *argvMAXARG|, *inf = 0, *outf = 0;
                int argc;

                if (tflg && ptrace(T_OK, 0, 0L, 0L) < 0)
                {
                        perror("ptrace");
                        exit(127);
                }
                argv0| = name;
                for (argc = 1; ; )
                {
                        argstr = skip(argstr);
                        if (*argstr == '\n' || *argstr == ';')
                        {
                                argvargc| = 0;
                                if (inf) freopen(inf, "r", stdin);
                                if (outf) freopen(outf, "w", stdout);
                                execv(name, argv);
                                perror("execv");
                                exit( 127 );
                        }
                        if (*argstr == '<') inf = argstr+1;
                        else if (*argstr == '>') outf = argstr+1;
                        else if (argc == MAXARG)
                        {
                                printf("Too many arguments.\n");
                                exit(127);
                        }
                        else argvargc++| = argstr;
                        while (!isspace(*argstr)) argstr++;
                        if (*argstr == '\n') argstr1| = '\n', argstr2| = 0;
                        *argstr++ = 0;
                }
        }
        if (procid < 0)
        {
                printf("Fork failed.\n");
        }
        return procid;
}

#define ADDQ(l) (((l >> 16) & 0xF1FF) == 0x508F)
#define ADDQ_CNT(l) ((((l >> 25) - 1) & 7) + 1)
#define LEA(l) ((l >> 16) == 0x4FEF)
#define LEA_DISP(l) ((long)(int)l)
#define ADDA(l) ((int)(l >> 16) == 0xDFFC)

backtrace(all)
{
        char sep;
        long pc, bp, off, val, obp;

        if (curpid <= 0)
        {
                printf("No process.\n");
                return;
        }
        pc = prc->p_pcpsw.pc;
        bp = prc->p_regBP_REG|;
        if (bp == 0)
        {
                printf("No active frame.\n");
                return;
        }
        errno = 0;
        do
        {
                symbolic(pc, '(');
                pc = ptrace(T_GETDATA, curpid, bp+4, 0L);
                off = ptrace(T_GETDATA, curpid, pc, 0L);
                obp = bp;
                bp += 2*sizeof(val);
                if (ADDQ(off))
                        off = ADDQ_CNT(off) + bp;
                else if (LEA(off))
                        off = LEA_DISP(off) + bp;
                else if (ADDA(off))
                        off = ptrace(T_GETDATA, curpid, pc+2, 0L) + bp;
                else
                        goto skiplp;

                for (;;)
                {
                        if (errno) return;
                        val = ptrace(T_GETDATA, curpid, bp, 0L) >> 16;
                        printf("0x%04x", (int)val);
                        bp += sizeof(int);
                        if (bp >= off) break;
                        putc(',', stdout);
                }

                skiplp:

                fputs(")\n", stdout);
                bp = ptrace(T_GETDATA, curpid, obp, 0L);
        }
        while (all && bp);
}

freepnt(pnt)
        struct b_pnt *pnt;
{
        if (pnt->prv) pnt->prv->nxt = pnt->nxt;
        else b_head = pnt->nxt;
        if (pnt->nxt) pnt->nxt->prv = pnt->prv;
        if (curpid > 0) ptrace(T_SETINS, curpid, pnt->addr, pnt->oldval);
        free(pnt);
        if (pnt == curpnt) curpnt = 0;
}

breakpt(addr, cmd)
        long addr;
        char *cmd;
{
        char *s;
        struct b_pnt *new, *malloc();
        int lng;

        if (curpid <= 0)
        {
                printf("No active process.\n");
                return;
        }
        for (new = b_head; new; new=new->nxt)
                if (new->addr == addr)
                {
                        printf("Breakpoint already exists here.\n");
                        return;
                }
        new = malloc(sizeof(struct b_pnt) + strlen(cmd));
        if (new == 0)
        {
                printf("No room.\n");
                return;
        }
        new->nxt = b_head;
        new->prv = 0;
        if (b_head) b_head->prv = new;
        b_head = new;
        new->addr = addr;
        strcpy(new->cmd, cmd);
        new->oldval = ptrace(T_GETINS, curpid, addr, 0L);
        ptrace(T_SETINS, curpid, addr, BREAK(new->oldval));
        if (ptrace(T_GETINS, curpid, addr, 0L) != BREAK(new->oldval))
        {
                perror("Can't set breakpoint");
                freepnt(new);
        }
}

modify(addr, cnt, verbose)
        long addr;
{
        long curval, off;

        if (curpid == 0)
        {
                printf("No active process.\n");
                return;
        }
        curval = ptrace(T_GETDATA, curpid, addr, 0L);
        do
        {
                if (cursig == SIGTRAP) cursig = 0;
                if (verbose)
                {
                        off = ptrace(T_GETUSER, curpid, PC, 0L);
                        dasm(off, 1, 0);
                }
                if (curpnt && exebpnt(cursig)) return;
                else
                {
                        ptrace(T_STEP, curpid, addr, 0L);
                        switch (dowait())
                        {
                        case    0: return;
                        case    SIGEMT: update(); findbpnt(0); break;
                        }
                }
                if (curval != ptrace(T_GETDATA, curpid, addr, 0L))
                {
                        printf("Modification detected\n");
                        break;
                }
        }
        while (--cnt);
        update();
        dasm(prc->p_pcpsw.pc, 1, 1);
        return;
}

display(addr, req)
        long addr;
{
        int count, size, out, shift;
        long val, msk;
        char fmt;

        if (curpid == 0)
        {
                printf("No active process\n");
                return;
        }
        count = strtol(cmd, &cmd, 0);
        if (count == 0) count = 1;
        if (*cmd == 'i' || *cmd == 'I')
        {
                if (req == T_GETUSER)
                {
                        printf("Can't disassemble a register's contents.\n");
                        longjmp(mainlp);
                }
                dasm(addr, count, *cmd == 'i');
                return;
        }
        switch (*cmd++)
        {
        case    'b': size = sizeof(char); break;
        case    'h': size = sizeof(short); break;
        case    'l': size = sizeof(long); break;
        default    : size = sizeof(int); --cmd; break;
        }
        switch (fmt = *cmd)
        {
        case    'c': count *= size; size = sizeof(char); break;
        case    's': addr = ptrace(req, curpid, addr, 0L); req = T_GETDATA;
        case    'a': size = sizeof(char);
                     break;
        }
        out = 0;
        msk = size == sizeof(long) ? 0xFFFFFFFF : (1L << (8*size)) - 1;
        shift = 32 - 8*size;
        do
        {
                val = (ptrace(req, curpid, addr, 0L) >> shift) & msk;
                if (out == 0) printf("\n0x%08lx: ", addr);
                switch (fmt)
                {
                case    'c':
                        printf(isprint(val) ? "   %c " : "\\%03o ", (char)val);
                        if (++out == 8) out = 0;
                        break;
                case    'u':
                        printf("%12lu ", val); if (++out == 4) out = 0; break;
                case    'x':
                        printf("%*lx ", 2*size, val);
                        if (++out == (size == 4 ? 4 : 8)) out = 0;
                        break;
                case    'o':
                        printf("%*lo ", 3*size, val);
                        if (++out == (size == 4 ? 4 : 8)) out = 0;
                        break;
                case    's':
                case    'a':
                        if (val) fputc((char)val, stdout);
                        else goto exitlp;
                        if (++out == 64) out = 0;
                        break;
                default:
                case    'd':
                        printf("%12ld ", val); if (++out == 4) out = 0; break;
                }
                addr += size;
        }
        while (--count > 0 || fmt == 's' || fmt == 'a');
exitlp:
        fputc('\n', stdout);
}

fill(addr, req)
        long addr;
{
        int count, size, shift, seg;
        long val, msk, nval;
        char fmt;

        if (curpid == 0)
        {
                printf("No active process\n");
                return;
        }
        count = strtol(cmd, &cmd, 0);
        switch (*cmd++)
        {
        case    'b': size = sizeof(char); break;
        case    'h': size = sizeof(short); break;
        case    'l': size = sizeof(long); break;
        default    : size = sizeof(int); --cmd; break;
        }
        shift = 32 - 8*size;
        msk = (0x80000000 >> 8*size);
        cmd = getexp(cmd, &nval, &seg);
        nval <<= shift;
        do
        {
                val = ptrace(req, curpid, addr, 0L) | (nval & msk);
                val &= (nval | ~msk);
                ptrace(req+3, curpid, addr, val);
                addr += size;
        }
        while (--count>0);
}

command(savestr)
        char *savestr;
{
        char c, *p;
        int i, seg;
        long exp;
        struct b_pnt *bp;

        seg = S; /* don't restrict segment expressions are in */
        cmdstart = cmd = skip(cmd);
        cmd = getexp(cmd, &exp, &seg);
        if (exp == 0)
        {
                seg = T;
                exp = prc->p_pcpsw.pc;
        }
        switch (c = *cmd++)
        {
        case '!': /* escape to shell OR set variable to value */
                if (cmd == cmdstart+1)
                {
                        cmd = skip(cmd);
                        if (*cmd == '\n' || *cmd == ';')
                        {
                                i = run("/bin/sh", "\n", 0);
                        }
                        else
                        {
                                for (p=cmd+1; *p && !isspace(*p); p++) {};
                                *p++ = 0;
                                i = run(cmd, *p ? p : "\n", 0);
                        }
                        if (i > 0) while (wait(&seg) != i) {};
                        break;
                }
                if (seg == 'T')
                {
                        printf("Can only modify data variables.\n");
                        break;
                }
                fill(exp, T_GETDATA);
                break;
        case 'T': /* top line of backtrace */
                backtrace(0);
                break;
        case 't': /* back trace */
                backtrace(1);
                break;
        case '/': /* print variable value */
                display(exp, T_GETDATA);
                break;
        case 'x': /* print registers and instruction */
                if (disp_regs()) break;
                /*FALLTHROUGH*/
        case 'X': /* print instruction */
                i = strtol(cmd, &cmd, 0);
                if (curpid > 0)
                        dasm(exp, i ? i : 1, 1);
                else
                        printf("No active process.\n");
                break;
        case 'R': /* run program with no args */
        case 'r': /* run program with args (possibly defaulted) */
                tstart(T_EXIT, 0, 0, 0);
                if (c == 'r')
                {
                        cmd = skip(cmd);
                        if (*cmd == '\n' || *cmd == ';') cmd = sbuf;
                        else strcpy(sbuf, cmd);
                }
                else
                {
                        cmd = "\n";
                }
                if (curpid = run(prog, cmd, 1))
                {
                        if (dowait())
                        {
                                ptrace(T_SETUSER, curpid, REG(BP_REG), 0L);
                                update();
                                printf("Process stopped.\n");
                        }
                }
                break;
        case 'c': /* continue program - ignore signal */
                cursig = 0;
        case 'C': /* continue program - handle signal */
                i = 0;
                if (seg == T && curpnt == 0 && cmd != cmdstart+1)
                {
                        breakpt(exp, "\n");
                        curpnt = b_head;
                        ptrace(T_SETINS, curpid, curpnt->addr, curpnt->oldval);
                        i = 1;
                }
                tstart(T_RESUME, 1, cursig, (int)strtol(cmd, &cmd, 0));
                if (i) /* remove temporary bp */
                {
                        freepnt(b_head);
                }
                if (cursig == SIGEMT) return;
                if (curpid) printf("Process stopped by signal %d\n", cursig);
                break;
        case 'i': /* single step - ignore signal */
                tstart(T_STEP, 1, 0, (int)strtol(cmd, &cmd, 0));
                break;
        case 'I': /* single step - handle signal */
                tstart(T_STEP, 1, cursig, (int)strtol(cmd, &cmd, 0));
                break;
        case 'm': /* single step until location modified */
        case 'M': /* single step until location modified - verbose */
                modify(exp, (int)strtol(cmd, &cmd, 0), c == 'M');
                break;
        case 'k': /* kill current program */
                tstart(T_EXIT, 1, 0, 0);
                break;
        case 'b': /* set a breakpoint at the given line */
                if (seg == D)
                {
                        printf("Address not in text space.\n");
                        return;
                }
                breakpt(exp, skip(cmd));
                cmd = "\n";
                return;
        case 'B': /* print list of currently active breakpoints */
                for (i = 1, bp = b_head; bp; bp=bp->nxt, i++)
                {
                        p = addr_to_name(bp->addr - saddr, &exp);
                        printf("%2d: %s+0x%lx (0x%lx) - %s",
                                i, p, exp, bp->addr, bp->cmd);
                }
                break;
        case 'd': /* delete breakpoint */
                if (seg == T)
                {
                        for (bp = b_head; bp && --exp; bp=bp->nxt) {};
                        if (bp) { freepnt(bp); break; }
                }
                printf("No such breakpoint.\n");
                break;
        case 'D': /* delete all breakpoints */
                while(b_head) freepnt(b_head);
                break;
        case 'q': /* quit */
                tstart(T_EXIT, 0, 0, 0L, 0);
                exit(0);
        case '\n':
        case ';':
                if (isdigit(*cmdstart)) symbolic(exp, '\n');
                else printf("0x%lx\n", exp);
                --cmd;
                break;
        case '$':
                cmdstart = cmd;
                cmd = skip(cmd+2);
                if (*cmd == '!')
                {
                        cmd++;
                        fill(reg_addr(cmdstart), T_GETUSER);
                        break;
                }
                if (*skip(cmd+2) == '/')
                {
                        cmd++;
                        display(reg_addr(cmdstart), T_GETUSER);
                        break;
                }
                /*FALLTHROUGH*/
        default:
                printf("Unknown command.\n");
                break;
        }
        while (*cmd != '\n' && *cmd != ';') ++cmd;
        if (*cmd == ';') cmd = skip(cmd+1);
}

main(argc, argv)
        char *argv|;
{
        int i;

        prc  = lbuf;
        for (i = 1; i < NSIG; i++) signal(i, catch);
        strcpy(sbuf, "\n");
        prog = argc > 1 ? argv1| : "a.out";
        if (argc > 2) getsyms(argv2|);

        setjmp(&mainlp);
        while ((printf("* "), fflush(stdout),
                        fgets(cbuf, sizeof(cbuf), stdin)) != EOF)
        {
                if (strlen(cbuf) == sizeof(cbuf)-1)
                {
                        printf("Command line too long.\n");
                        continue;
                }
                cmd = cbuf;
                command();
                while (*cmd != '\n') command();
        }
        tstart(T_EXIT, 0, 0, 0);
        exit(0);
}