debugger
diff --git a/src/cmd/db/command.c b/src/cmd/db/command.c
new file mode 100644
index 0000000..55ed02a
--- /dev/null
+++ b/src/cmd/db/command.c
@@ -0,0 +1,311 @@
+/*
+ *
+ *	debugger
+ *
+ */
+
+#include "defs.h"
+#include "fns.h"
+
+char	BADEQ[] = "unexpected `='";
+
+BOOL	executing;
+extern	char	*lp;
+
+char	eqformat[ARB] = "z";
+char	stformat[ARB] = "zMi";
+
+ADDR	ditto;
+
+ADDR	dot;
+WORD	dotinc;
+WORD	adrval, cntval, loopcnt;
+int	adrflg, cntflg;
+
+/* command decoding */
+
+int
+command(char *buf, int defcom)
+{
+	char	*reg;
+	char	savc;
+	char	*savlp=lp;
+	char	savlc = lastc;
+	char	savpc = peekc;
+	static char lastcom = '=', savecom = '=';
+
+	if (defcom == 0)
+		defcom = lastcom;
+	if (buf) {
+		if (*buf==EOR)
+			return(FALSE);
+		clrinp();
+		lp=buf;
+	}
+	do {
+		adrflg=expr(0);		/* first address */
+		if (adrflg){
+			dot=expv;
+			ditto=expv;
+		}
+		adrval=dot;
+
+		if (rdc()==',' && expr(0)) {	/* count */
+			cntflg=TRUE;
+			cntval=expv;
+		} else {
+			cntflg=FALSE;
+			cntval=1;
+			reread();
+		}
+
+		if (!eol(rdc()))
+			lastcom=lastc;		/* command */
+		else {
+			if (adrflg==0)
+				dot=inkdot(dotinc);
+			reread();
+			lastcom=defcom;
+		}
+		switch(lastcom) {
+		case '/':
+		case '=':
+		case '?':
+			savecom = lastcom;
+			acommand(lastcom);
+			break;
+
+		case '>':
+			lastcom = savecom; 
+			savc=rdc();
+			if (reg=regname(savc))
+				rput(correg, reg, dot);
+			else	
+				error("bad variable");
+			break;
+
+		case '!':
+			lastcom=savecom;
+			shell(); 
+			break;
+
+		case '$':
+			lastcom=savecom;
+			printdollar(nextchar()); 
+			break;
+
+		case ':':
+			if (!executing) { 
+				executing=TRUE;
+				subpcs(nextchar());
+				executing=FALSE;
+				lastcom=savecom;
+			}
+			break;
+
+		case 0:
+			prints(DBNAME);
+			break;
+
+		default: 
+			error("bad command");
+		}
+		flushbuf();
+	} while (rdc()==';');
+	if (buf == 0)
+		reread();
+	else {
+		clrinp();
+		lp=savlp;
+		lastc = savlc;
+		peekc = savpc;
+	}
+
+	if(adrflg)
+		return dot;
+	return 1;
+}
+
+/*
+ * [/?][wml]
+ */
+
+void
+acommand(int pc)
+{
+	int eqcom;
+	Map *map;
+	char *fmt;
+	char buf[512];
+
+	if (pc == '=') {
+		eqcom = 1;
+		fmt = eqformat;
+		map = dotmap;
+	} else {
+		eqcom = 0;
+		fmt = stformat;
+		if (pc == '/')
+			map = cormap;
+		else
+			map = symmap;
+	}
+	if (!map) {
+		sprint(buf, "no map for %c", pc);
+		error(buf);
+	}
+
+	switch (rdc())
+	{
+	case 'm':
+		if (eqcom)
+			error(BADEQ); 
+		cmdmap(map);
+		break;
+
+	case 'L':
+	case 'l':
+		if (eqcom)
+			error(BADEQ); 
+		cmdsrc(lastc, map);
+		break;
+
+	case 'W':
+	case 'w':
+		if (eqcom)
+			error(BADEQ); 
+		cmdwrite(lastc, map);
+		break;
+
+	default:
+		reread();
+		getformat(fmt);
+		scanform(cntval, !eqcom, fmt, map, eqcom);
+	}
+}
+
+void
+cmdsrc(int c, Map *map)
+{
+	u32int w;
+	long locval, locmsk;
+	ADDR savdot;
+	ushort sh;
+	char buf[512];
+	int ret;
+
+	if (c == 'L')
+		dotinc = 4;
+	else
+		dotinc = 2;
+	savdot=dot;
+	expr(1); 
+	locval=expv;
+	if (expr(0))
+		locmsk=expv; 
+	else
+		locmsk = ~0;
+	if (c == 'L')
+		while ((ret = get4(map, dot, &w)) > 0 &&  (w&locmsk) != locval)
+			dot = inkdot(dotinc);
+	else
+		while ((ret = get2(map, dot, &sh)) > 0 && (sh&locmsk) != locval)
+			dot = inkdot(dotinc);
+	if (ret < 0) { 
+		dot=savdot; 
+		error("%r");
+	}
+	symoff(buf, 512, dot, CANY);
+	dprint(buf);
+}
+
+static char badwrite[] = "can't write process memory or text image";
+
+void
+cmdwrite(int wcom, Map *map)
+{
+	ADDR savdot;
+	char *format;
+	int pass;
+
+	if (wcom == 'w')
+		format = "x";
+	else
+		format = "X";
+	expr(1);
+	pass = 0;
+	do {
+		pass++;  
+		savdot=dot;
+		exform(1, 1, format, map, 0, pass);
+		dot=savdot;
+		if (wcom == 'W') {
+			if (put4(map, dot, expv) <= 0)
+				error(badwrite);
+		} else {
+			if (put2(map, dot, expv) <= 0)
+				error(badwrite);
+		}
+		savdot=dot;
+		dprint("=%8t"); 
+		exform(1, 0, format, map, 0, pass);
+		newline();
+	} while (expr(0));
+	dot=savdot;
+}
+
+/*
+ * collect a register name; return register offset
+ * this is not what i'd call a good division of labour
+ */
+
+char *
+regname(int regnam)
+{
+	static char buf[64];
+	char *p;
+	int c;
+
+	p = buf;
+	*p++ = regnam;
+	while (isalnum(c = readchar())) {
+		if (p >= buf+sizeof(buf)-1)
+			error("register name too long");
+		*p++ = c;
+	}
+	*p = 0;
+	reread();
+	return (buf);
+}
+
+/*
+ * shell escape
+ */
+
+void
+shell(void)
+{
+	int	rc, unixpid;
+	char *argp = lp;
+
+	while (lastc!=EOR)
+		rdc();
+	if ((unixpid=fork())==0) {
+		*lp=0;
+		execl("/bin/rc", "rc", "-c", argp, 0);
+		exits("execl");				/* botch */
+	} else if (unixpid == -1) {
+		error("cannot fork");
+	} else {
+		mkfault = 0;
+		while ((rc = waitpid()) != unixpid){
+			if(rc == -1 && mkfault){
+				mkfault = 0;
+				continue;
+			}
+			break;
+		}
+		prints("!"); 
+		reread();
+	}
+}
diff --git a/src/cmd/db/defs.h b/src/cmd/db/defs.h
new file mode 100644
index 0000000..02ef085
--- /dev/null
+++ b/src/cmd/db/defs.h
@@ -0,0 +1,111 @@
+/*
+ * db - common definitions
+ * something of a grab-bag
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+#include <mach.h>
+
+typedef long WORD;
+typedef ulong ADDR;
+
+#define	HUGEINT	0x7fffffff	/* enormous WORD */
+
+#define	MAXOFF	0x1000000
+#define	INCDIR	"/usr/lib/adb"
+#define	DBNAME	"db\n"
+#define CMD_VERBS	"?/=>!$: \t"
+
+typedef	int	BOOL;
+
+#define MAXPOS	80
+#define MAXLIN	128
+#define	ARB	512
+#define MAXCOM	64
+#define MAXARG	32
+#define LINSIZ	4096
+#define	MAXSYM	255
+
+#define EOR	'\n'
+#define SPC	' '
+#define TB	'\t'
+
+#define	STDIN	0
+#define	STDOUT	1
+
+#define	TRUE	(-1)
+#define	FALSE	0
+
+
+/*
+ * run modes
+ */
+
+#define	SINGLE	1
+#define	CONTIN	2
+
+/*
+ * breakpoints
+ */
+
+#define	BKPTCLR	0	/* not a real breakpoint */
+#define BKPTSET	1	/* real, ready to trap */
+#define BKPTSKIP 2	/* real, skip over it next time */
+#define	BKPTTMP	3	/* temporary; clear when it happens */
+
+struct bkpt {
+	ADDR	loc;
+	uchar	save[4];
+	int	count;
+	int	initcnt;
+	int	flag;
+	char	comm[MAXCOM];
+	struct bkpt *nxtbkpt;
+};
+typedef struct bkpt	BKPT;
+
+#define	BADREG	(-1)
+
+/*
+ * common globals
+ */
+
+extern	WORD	adrval;
+extern	vlong	expv;
+extern	int	adrflg;
+extern	WORD	cntval;
+extern	int	cntflg;
+extern	WORD	loopcnt;
+extern	ADDR	maxoff;
+extern	ADDR	localval;
+extern	ADDR	maxfile;
+extern	ADDR	maxstor;
+
+extern	ADDR	dot;
+extern	WORD	dotinc;
+
+extern	int	xargc;
+
+extern	BOOL	wtflag;
+extern	char	*corfil, *symfil;
+extern	BOOL	mkfault;
+extern	BOOL	regdirty;
+
+extern	int	pid;
+extern	int	pcsactive;
+#define	NNOTE 10
+extern	int	nnote;
+extern	char	note[NNOTE][ERRMAX];
+
+extern	int	ending;
+extern	Fhdr	*corhdr, *symhdr;
+extern	Map	*cormap, *symmap, *dotmap;
+extern	Regs	*correg;
+
+extern	BKPT	*bkpthead;
+extern	int	kflag;
+extern	int	lastc, peekc;
diff --git a/src/cmd/db/expr.c b/src/cmd/db/expr.c
new file mode 100644
index 0000000..8d33e7f
--- /dev/null
+++ b/src/cmd/db/expr.c
@@ -0,0 +1,397 @@
+/*
+ *
+ *	debugger
+ *
+ */
+
+#include "defs.h"
+#include "fns.h"
+
+static long	dbround(long, long);
+
+extern	ADDR	ditto;
+vlong	expv;
+
+static WORD
+ascval(void)
+{
+	Rune r;
+
+	if (readchar() == 0)
+		return (0);
+	r = lastc;
+	while(quotchar())	/*discard chars to ending quote */
+		;
+	return((WORD) r);
+}
+
+/*
+ * read a floating point number
+ * the result must fit in a WORD
+ */
+
+static WORD
+fpin(char *buf)
+{
+	union {
+		WORD w;
+		float f;
+	} x;
+
+	x.f = atof(buf);
+	return (x.w);
+}
+
+WORD
+defval(WORD w)
+{
+	if (expr(0))
+		return (expv);
+	else
+		return (w);
+}
+
+int
+expr(int a)
+{	/* term | term dyadic expr |  */
+	int	rc;
+	WORD	lhs;
+
+	rdc();
+	reread();
+	rc=term(a);
+	while (rc) {
+		lhs = expv;
+		switch ((int)readchar()) {
+
+		case '+':
+			term(a|1);
+			expv += lhs;
+			break;
+
+		case '-':
+			term(a|1);
+			expv = lhs - expv;
+			break;
+
+		case '#':
+			term(a|1);
+			expv = dbround(lhs,expv);
+			break;
+
+		case '*':
+			term(a|1);
+			expv *= lhs;
+			break;
+
+		case '%':
+			term(a|1);
+			if(expv != 0)
+				expv = lhs/expv;
+			else{
+				if(lhs)
+					expv = 1;
+				else
+					expv = 0;
+			}
+			break;
+
+		case '&':
+			term(a|1);
+			expv &= lhs;
+			break;
+
+		case '|':
+			term(a|1);
+			expv |= lhs;
+			break;
+
+		case ')':
+			if ((a&2)==0)
+				error("unexpected `)'");
+
+		default:
+			reread();
+			return(rc);
+		}
+	}
+	return(rc);
+}
+
+int
+term(int a)
+{	/* item | monadic item | (expr) | */
+	u32int u;
+
+	switch ((int)readchar()) {
+
+	case '*':
+		term(a|1);
+		if (get4(cormap, (ADDR)expv, &u) < 0)
+			error("%r");
+		expv = u;
+		return(1);
+
+	case '@':
+		term(a|1);
+		if (get4(symmap, (ADDR)expv, &u) < 0)
+			error("%r");
+		expv = u;
+		return(1);
+
+	case '-':
+		term(a|1);
+		expv = -expv;
+		return(1);
+
+	case '~':
+		term(a|1);
+		expv = ~expv;
+		return(1);
+
+	case '(':
+		expr(2);
+		if (readchar()!=')')
+			error("syntax error: `)' expected");
+		return(1);
+
+	default:
+		reread();
+		return(item(a));
+	}
+}
+
+int
+item(int a)
+{	/* name [ . local ] | number | . | ^  | <register | 'x | | */
+	char	*base;
+	char	savc;
+	ulong u;
+	Symbol s;
+	char gsym[MAXSYM], lsym[MAXSYM];
+
+	readchar();
+	if (isfileref()) {
+		readfname(gsym);
+		rdc();			/* skip white space */
+		if (lastc == ':') {	/* it better be */
+			rdc();		/* skip white space */
+			if (!getnum(readchar))
+				error("bad number");
+			if (expv == 0)
+				expv = 1;	/* file begins at line 1 */
+			if(file2pc(gsym, expv, &u) < 0)
+				error("%r");
+			expv = u;
+			return 1;
+		}
+		error("bad file location");
+	} else if (symchar(0)) {	
+		readsym(gsym);
+		if (lastc=='.') {
+			readchar();	/* ugh */
+			if (lastc == '.') {
+				lsym[0] = '.';
+				readchar();
+				readsym(lsym+1);
+			} else if (symchar(0)) {
+				readsym(lsym);
+			} else
+				lsym[0] = 0;
+			if (localaddr(cormap, correg, gsym, lsym, &u) < 0)
+				error("%r");
+			expv = u;
+		}
+		else {
+			if (lookupsym(0, gsym, &s) < 0)
+				error("symbol not found");
+			if (s.loc.type != LADDR)
+				error("symbol not kept in memory");
+			expv = s.loc.addr;
+		}
+		reread();
+	} else if (getnum(readchar)) {
+		;
+	} else if (lastc=='.') {	
+		readchar();
+		if (!symchar(0) && lastc != '.') {
+			expv = dot;
+		} else {
+			if (findsym(locaddr(dbrget(cormap, mach->pc)), CTEXT, &s) < 0)
+				error("no current function");
+			if (lastc == '.') {
+				lsym[0] = '.';
+				readchar();
+				readsym(lsym+1);
+			} else
+				readsym(lsym);
+			if (localaddr(cormap, correg, s.name, lsym, &u) < 0)
+				error("%r");
+			expv = u;
+		}	
+		reread();
+	} else if (lastc=='"') {
+		expv=ditto;
+	} else if (lastc=='+') {
+		expv=inkdot(dotinc);
+	} else if (lastc=='^') {
+		expv=inkdot(-dotinc);
+	} else if (lastc=='<') {
+		savc=rdc();
+		base = regname(savc);
+		expv = dbrget(cormap, base);
+	}
+	else if (lastc=='\'')
+		expv = ascval();
+	else if (a)
+		error("address expected");
+	else {	
+		reread();
+		return(0);
+	}
+	return(1);
+}
+
+#define	MAXBASE	16
+
+/* service routines for expression reading */
+int
+getnum(int (*rdf)(void))
+{
+	char *cp;
+	int base, d;
+	BOOL fpnum;
+	char num[MAXLIN];
+
+	base = 0;
+	fpnum = FALSE;
+	if (lastc == '#') {
+		base = 16;
+		(*rdf)();
+	}
+	if (convdig(lastc) >= MAXBASE)
+		return (0);
+	if (lastc == '0')
+		switch ((*rdf)()) {
+		case 'x':
+		case 'X':
+			base = 16;
+			(*rdf)();
+			break;
+
+		case 't':
+		case 'T':
+			base = 10;
+			(*rdf)();
+			break;
+
+		case 'o':
+		case 'O':
+			base = 8;
+			(*rdf)();
+			break;
+		default:
+			if (base == 0)
+				base = 8;
+			break;
+		}
+	if (base == 0)
+		base = 10;
+	expv = 0;
+	for (cp = num, *cp = lastc; ;(*rdf)()) {
+		if ((d = convdig(lastc)) < base) {
+			expv *= base;
+			expv += d;
+			*cp++ = lastc;
+		}
+		else if (lastc == '.') {
+			fpnum = TRUE;
+			*cp++ = lastc;
+		} else {
+			reread();
+			break;
+		}
+	}
+	if (fpnum)
+		expv = fpin(num);
+	return (1);
+}
+
+void
+readsym(char *isymbol)
+{
+	char	*p;
+	Rune r;
+
+	p = isymbol;
+	do {
+		if (p < &isymbol[MAXSYM-UTFmax-1]){
+			r = lastc;
+			p += runetochar(p, &r);
+		}
+		readchar();
+	} while (symchar(1));
+	*p = 0;
+}
+
+void
+readfname(char *filename)
+{
+	char	*p;
+	Rune	c;
+
+	/* snarf chars until un-escaped char in terminal char set */
+	p = filename;
+	do {
+		if ((c = lastc) != '\\' && p < &filename[MAXSYM-UTFmax-1])
+			p += runetochar(p, &c);
+		readchar();
+	} while (c == '\\' || strchr(CMD_VERBS, lastc) == 0);
+	*p = 0;
+	reread();
+}
+
+int
+convdig(int c)
+{
+	if (isdigit(c))
+		return(c-'0');
+	else if (!isxdigit(c))
+		return(MAXBASE);
+	else if (isupper(c))
+		return(c-'A'+10);
+	else
+		return(c-'a'+10);
+}
+
+int
+symchar(int dig)
+{
+	if (lastc=='\\') {
+		readchar();
+		return(TRUE);
+	}
+	return(isalpha(lastc) || lastc>0x80 || lastc=='_' || dig && isdigit(lastc));
+}
+
+static long
+dbround(long a, long b)
+{
+	long w;
+
+	w = (a/b)*b;
+	if (a!=w)
+		w += b;
+	return(w);
+}
+
+ulong
+dbrget(Map *map, char *name)
+{
+	ulong u;
+
+	USED(map);
+	if(rget(correg, name, &u) < 0)
+		return ~(ulong)0;
+	return u;
+}
diff --git a/src/cmd/db/fns.h b/src/cmd/db/fns.h
new file mode 100644
index 0000000..32bd94f
--- /dev/null
+++ b/src/cmd/db/fns.h
@@ -0,0 +1,91 @@
+void		acommand(int);
+void		attachprocess(void);
+void		bkput(BKPT*, int);
+void		bpwait(void);
+int		charpos(void);
+void		chkerr(void);
+void		clrinp(void);
+void		cmdmap(Map*);
+void		cmdsrc(int, Map*);
+void		cmdwrite(int, Map*);
+int		command(char*, int);
+int		convdig(int);
+void		ctrace(int);
+WORD		defval(WORD);
+void		delbp(void);
+ulong	dbrget(Map*, char*);
+void		done(void);
+int		dprint(char*, ...);
+Map*		dumbmap(int);
+void		endline(void);
+void		endpcs(void);
+int		eol(int);
+void		error(char*);
+void		errors(char*, char*);
+void		execbkpt(BKPT*, int);
+char*		exform(int, int, char*, Map*, int, int);
+int		expr(int);
+/*
+void		fixregs(Map*);
+void		adjustreg(char*, ulong, long);
+*/
+void		flush(void);
+void		flushbuf(void);
+char*		getfname(void);
+void		getformat(char*);
+int		getnum(int (*)(void));
+void		grab(void);
+void		iclose(int, int);
+ADDR		inkdot(long);
+int		isfileref(void);
+int		item(int);
+void		killpcs(void);
+void		kmsys(void);
+void		main(int, char**);
+int		mapimage(void);
+void		newline(void);
+int		nextchar(void);
+void		notes(void);
+void		oclose(void);
+void		outputinit(void);
+void		printc(int);
+void		printdollar(int);
+void		printesc(int);
+void		printlocals(Symbol*, Regs*);
+void		printmap(char*, Map*);
+void		printparams(Symbol*, Regs*);
+void		printpc(void);
+void		printregs(int);
+void		prints(char*);
+void		printsource(long);
+void		printsym(void);
+void		printsyscall(void);
+int		quotchar(void);
+int		rdc(void);
+int		readchar(void);
+void		readsym(char*);
+void		redirin(int, char*);
+void		redirout(char*);
+void		readfname(char *);
+void		reread(void);
+char*		regname(int);
+//vlong		rget(Map*, char*);
+Regdesc*	rname(char*);
+//void		rput(Map*, char*, vlong);
+int		runpcs(int, int);
+void		runrun(int);
+void		runstep(ulong, int);
+BKPT*		scanbkpt(ADDR adr);
+void		scanform(long, int, char*, Map*, int);
+void		setbp(void);
+void		setcor(void);
+void		setsym(void);
+void		setup(void);
+void		setvec(void);
+void		shell(void);
+void		startpcs(void);
+void		subpcs(int);
+int		symchar(int);
+int		term(int);
+void		ungrab(void);
+int		valpr(long, int);
diff --git a/src/cmd/db/format.c b/src/cmd/db/format.c
new file mode 100644
index 0000000..ef05364
--- /dev/null
+++ b/src/cmd/db/format.c
@@ -0,0 +1,388 @@
+/*
+ *
+ *	debugger
+ *
+ */
+
+#include "defs.h"
+#include "fns.h"
+
+void
+scanform(long icount, int prt, char *ifp, Map *map, int literal)
+{
+	char	*fp;
+	char	c;
+	int	fcount;
+	ADDR	savdot;
+	int firstpass;
+
+	firstpass = 1;
+	while (icount) {
+		fp=ifp;
+		savdot=dot;
+		/*now loop over format*/
+		while (*fp) {
+			if (!isdigit(*fp))
+				fcount = 1;
+			else {
+				fcount = 0;
+				while (isdigit(c = *fp++)) {
+					fcount *= 10;
+					fcount += c-'0';
+				}
+				fp--;
+			}
+			if (*fp==0)
+				break;
+			fp=exform(fcount,prt,fp,map,literal,firstpass);
+			firstpass = 0;
+		}
+		dotinc=dot-savdot;
+		dot=savdot;
+		if (--icount)
+			dot=inkdot(dotinc);
+	}
+}
+
+char *
+exform(int fcount, int prt, char *ifp, Map *map, int literal, int firstpass)
+{
+	/* execute single format item `fcount' times
+	 * sets `dotinc' and moves `dot'
+	 * returns address of next format item
+	 */
+	vlong	v;
+	WORD	w;
+	ulong	savdot;
+	u16int	u2;
+	u32int	u4;
+	u64int	u8;
+	char	*fp;
+	char	c, modifier;
+	int	i;
+	ushort sh, *sp;
+	uchar ch, *cp;
+	Symbol s;
+	char buf[512];
+	extern int printcol;
+
+	fp = 0;
+	while (fcount > 0) {
+		fp = ifp;
+		c = *fp;
+		modifier = *fp++;
+		if (firstpass) {
+			firstpass = 0;
+			if (!literal  && (c == 'i' || c == 'I' || c == 'M')
+					&& (dot & (mach->pcquant-1))) {
+				dprint("warning: instruction not aligned");
+				printc('\n');
+			}
+			if (prt && modifier != 'a' && modifier != 'A') {
+				symoff(buf, 512, dot, CANY);
+				dprint("%s%c%16t", buf, map==symmap? '?':'/');
+			}
+		}
+		if (printcol==0 && modifier != 'a' && modifier != 'A')
+			dprint("\t\t");
+		switch(modifier) {
+
+		case SPC:
+		case TB:
+			dotinc = 0;
+			break;
+
+		case 't':
+		case 'T':
+			dprint("%*t", fcount);
+			dotinc = 0;
+			return(fp);
+
+		case 'a':
+			symoff(buf, sizeof(buf), dot, CANY);
+			dprint("%s%c%16t", buf, map==symmap? '?':'/');
+			dotinc = 0;
+			break;
+
+		case 'A':
+			dprint("%#lux%10t", dot);
+			dotinc = 0;
+			break;
+
+		case 'p':
+			if (get4(map, dot, &u4) < 0)
+				error("%r");
+			w = u4;
+			symoff(buf, sizeof(buf), w, CANY);
+			dprint("%s%16t", buf);
+			dotinc = mach->szaddr;
+			break;
+
+		case 'u':
+		case 'd':
+		case 'x':
+		case 'o':
+		case 'q':
+			if (literal)
+				u2 = (ushort) dot;
+			else if (get2(map, dot, &u2) < 0)
+				error("%r");
+			w = u2;
+			dotinc = 2;
+			if (c == 'u')
+				dprint("%-8lud", w);
+			else if (c == 'x')
+				dprint("%-8#lux", w);
+			else if (c == 'd')
+				dprint("%-8ld", w);
+			else if (c == 'o')
+				dprint("%-8#luo", w);
+			else if (c == 'q')
+				dprint("%-8#lo", w);
+			break;
+
+		case 'U':
+		case 'D':
+		case 'X':
+		case 'O':
+		case 'Q':
+			if (literal)
+				u4 = (long) dot;
+			else if (get4(map, dot, &u4) < 0)
+				error("%r");
+			dotinc = 4;
+			if (c == 'U')
+				dprint("%-16lud", u4);
+			else if (c == 'X')
+				dprint("%-16#lux", u4);
+			else if (c == 'D')
+				dprint("%-16ld", u4);
+			else if (c == 'O')
+				dprint("%-#16luo", u4);
+			else if (c == 'Q')
+				dprint("%-#16lo", u4);
+			break;
+		case 'Z':
+		case 'V':
+		case 'Y':
+			if (literal)
+				v = dot;
+			else if (get8(map, dot, &u8) < 0)
+				error("%r");
+			dotinc = 8;
+			if (c == 'Y')
+				dprint("%-20#llux", u8);
+			else if (c == 'V')
+				dprint("%-20lld", u8);
+			else if (c == 'Z')
+				dprint("%-20llud", u8);
+			break;
+		case 'B':
+		case 'b':
+		case 'c':
+		case 'C':
+			if (literal)
+				ch = (uchar) dot;
+			else if (get1(map, dot, &ch, 1)  < 0)
+				error("%r");
+			if (modifier == 'C')
+				printesc(ch);
+			else if (modifier == 'B' || modifier == 'b')
+				dprint("%-8#lux", (long) ch);
+			else
+				printc(ch);
+			dotinc = 1;
+			break;
+
+		case 'r':
+			if (literal)
+				sh = (ushort) dot;
+			else if (get2(map, dot, &sh) < 0)
+				error("%r");
+			dprint("%C", sh);
+			dotinc = 2;
+			break;
+
+		case 'R':
+			if (literal) {
+				sp = (u16int*)(void*)&dot;
+				dprint("%C%C", sp[0], sp[1]);
+				endline();
+				dotinc = 4;
+				break;
+			}
+			savdot=dot;
+			while ((i = get2(map, dot, &u2) > 0) && u2) {
+				dot=inkdot(2);
+				dprint("%C", u2);
+				endline();
+			}
+			if (i < 0)
+				error("%r");
+			dotinc = dot-savdot+2;
+			dot=savdot;
+			break;
+
+		case 's':
+			if (literal) {
+				cp = (uchar*)(void*)&dot;
+				for (i = 0; i < 4; i++)
+					buf[i] = cp[i];
+				buf[i] = 0;
+				dprint("%s", buf);
+				endline();
+				dotinc = 4;
+				break;
+			}
+			savdot = dot;
+			for(;;){
+				i = 0;
+				do{
+					if (get1(map, dot, (uchar*)(void*)&buf[i], 1) < 0)
+						error("%r");
+					dot = inkdot(1);
+					i++;
+				}while(!fullrune(buf, i));
+				if(buf[0] == 0)
+					break;
+				buf[i] = 0;
+				dprint("%s", buf);
+				endline();
+			}
+			dotinc = dot-savdot+1;
+			dot = savdot;
+			break;
+
+		case 'S':
+			if (literal) {
+				cp = (uchar*) &dot;
+				for (i = 0; i < 4; i++)
+					printesc(cp[i]);
+				endline();
+				dotinc = 4;
+				break;
+			}
+			savdot=dot;
+			while ((i = get1(map, dot, &ch, 1) > 0) && ch) {
+				dot=inkdot(1);
+				printesc(ch);
+				endline();
+			}
+			if (i < 0)
+				error("%r");
+			dotinc = dot-savdot+1;
+			dot=savdot;
+			break;
+
+
+		case 'I':
+		case 'i':
+			dotinc = mach->das(map, dot, modifier, buf, sizeof(buf));
+			if (dotinc < 0)
+				error("%r");
+			dprint("%s\n", buf);
+			break;
+
+		case 'M':
+			dotinc = mach->hexinst(map, dot, buf, sizeof(buf));
+			if (dotinc < 0)
+				error("%r");
+			dprint("%s", buf);
+			if (*fp) {
+				dotinc = 0;
+				dprint("%48t");
+			} else
+				dprint("\n");
+			break;
+
+		case 'f':
+			/* BUG: 'f' and 'F' assume szdouble is sizeof(vlong) in the literal case */
+			if (literal) {
+				v = mach->swap8((ulong)dot);
+				memmove(buf, &v, mach->szfloat);
+			}else if (get1(map, dot, (uchar*)buf, mach->szfloat) < 0)
+				error("%r");
+			mach->ftoa32(buf, sizeof(buf), (void*) buf);
+			dprint("%s\n", buf);
+			dotinc = mach->szfloat;
+			break;
+
+		case 'F':
+			/* BUG: 'f' and 'F' assume szdouble is sizeof(vlong) in the literal case */
+			if (literal) {
+				v = mach->swap8(dot);
+				memmove(buf, &v, mach->szdouble);
+			}else if (get1(map, dot, (uchar*)buf, mach->szdouble) < 0)
+				error("%r");
+			mach->ftoa64(buf, sizeof(buf), (void*) buf);
+			dprint("%s\n", buf);
+			dotinc = mach->szdouble;
+			break;
+
+		case 'n':
+		case 'N':
+			printc('\n');
+			dotinc=0;
+			break;
+
+		case '"':
+			dotinc=0;
+			while (*fp != '"' && *fp)
+				printc(*fp++);
+			if (*fp)
+				fp++;
+			break;
+
+		case '^':
+			dot=inkdot(-dotinc*fcount);
+			return(fp);
+
+		case '+':
+			dot=inkdot((WORD)fcount);
+			return(fp);
+
+		case '-':
+			dot=inkdot(-(WORD)fcount);
+			return(fp);
+
+		case 'z':
+			if (findsym(locaddr(dot), CTEXT, &s) >= 0)
+				dprint("%s() ", s.name);
+			printsource(dot);
+			printc(EOR);
+			return fp;
+
+		default:
+			error("bad modifier");
+		}
+		if (map->seg[0].fd >= 0)
+			dot=inkdot(dotinc);
+		fcount--;
+		endline();
+	}
+
+	return(fp);
+}
+
+void
+printesc(int c)
+{
+	static char hex[] = "0123456789abcdef";
+
+	if (c < SPC || c >= 0177)
+		dprint("\\x%c%c", hex[(c&0xF0)>>4], hex[c&0xF]);
+	else
+		printc(c);
+}
+
+ADDR
+inkdot(WORD incr)
+{
+	ADDR	newdot;
+
+	newdot=dot+incr;
+	if ((incr >= 0 && newdot < dot)
+	||  (incr < 0 && newdot > dot))
+		error("address wraparound");
+	return(newdot);
+}
diff --git a/src/cmd/db/input.c b/src/cmd/db/input.c
new file mode 100644
index 0000000..8cd091b
--- /dev/null
+++ b/src/cmd/db/input.c
@@ -0,0 +1,169 @@
+/*
+ *
+ *	debugger
+ *
+ */
+
+#include "defs.h"
+#include "fns.h"
+
+Rune	line[LINSIZ];
+extern	int	infile;
+Rune	*lp;
+int	peekc,lastc = EOR;
+int	eof;
+
+/* input routines */
+
+int
+eol(int c)
+{
+	return(c==EOR || c==';');
+}
+
+int
+rdc(void)
+{
+	do {
+		readchar();
+	} while (lastc==SPC || lastc==TB);
+	return(lastc);
+}
+
+void
+reread(void)
+{
+	peekc = lastc;
+}
+
+void
+clrinp(void)
+{
+	flush();
+	lp = 0;
+	peekc = 0;
+}
+
+int
+readrune(int fd, Rune *r)
+{
+	char buf[UTFmax];
+	int i;
+
+	for(i=0; i<UTFmax && !fullrune(buf, i); i++)
+		if(read(fd, buf+i, 1) <= 0)
+			return -1;
+	chartorune(r, buf);
+	return 1;
+}
+
+int
+readchar(void)
+{
+	Rune *p;
+
+	if (eof)
+		lastc=0;
+	else if (peekc) {
+		lastc = peekc;
+		peekc = 0;
+	}
+	else {
+		if (lp==0) {
+			for (p = line; p < &line[LINSIZ-1]; p++) {
+				eof = readrune(infile, p) <= 0;
+				if (mkfault) {
+					eof = 0;
+					error(0);
+				}
+				if (eof) {
+					p--;
+					break;
+				}
+				if (*p == EOR) {
+					if (p <= line)
+						break;
+					if (p[-1] != '\\')
+						break;
+					p -= 2;
+				}
+			}
+			p[1] = 0;
+			lp = line;
+		}
+		if ((lastc = *lp) != 0)
+			lp++;
+	}
+	return(lastc);
+}
+
+int
+nextchar(void)
+{
+	if (eol(rdc())) {
+		reread();
+		return(0);
+	}
+	return(lastc);
+}
+
+int
+quotchar(void)
+{
+	if (readchar()=='\\')
+		return(readchar());
+	else if (lastc=='\'')
+		return(0);
+	else
+		return(lastc);
+}
+
+void
+getformat(char *deformat)
+{
+	char *fptr;
+	BOOL	quote;
+	Rune r;
+
+	fptr=deformat;
+	quote=FALSE;
+	while ((quote ? readchar()!=EOR : !eol(readchar()))){
+		r = lastc;
+		fptr += runetochar(fptr, &r);
+		if (lastc == '"')
+			quote = ~quote;
+	}
+	lp--;
+	if (fptr!=deformat)
+		*fptr = '\0';
+}
+
+/*
+ *	check if the input line if of the form:
+ *		<filename>:<digits><verb> ...
+ *
+ *	we handle this case specially because we have to look ahead
+ *	at the token after the colon to decide if it is a file reference
+ *	or a colon-command with a symbol name prefix. 
+ */
+
+int
+isfileref(void)
+{
+	Rune *cp;
+
+	for (cp = lp-1; *cp && !strchr(CMD_VERBS, *cp); cp++)
+		if (*cp == '\\' && cp[1])	/* escape next char */
+			cp++;
+	if (*cp && cp > lp-1) {
+		while (*cp == ' ' || *cp == '\t')
+			cp++;
+		if (*cp++ == ':') {
+			while (*cp == ' ' || *cp == '\t')
+				cp++;
+			if (isdigit(*cp))
+				return 1;
+		}
+	}
+	return 0;
+}
diff --git a/src/cmd/db/main.c b/src/cmd/db/main.c
new file mode 100644
index 0000000..8e57403
--- /dev/null
+++ b/src/cmd/db/main.c
@@ -0,0 +1,262 @@
+/*
+ * db - main command loop and error/interrupt handling
+ */
+#include "defs.h"
+#include "fns.h"
+
+int wtflag = OREAD;
+BOOL kflag;
+
+BOOL mkfault;
+ADDR maxoff;
+
+int xargc;		/* bullshit */
+
+extern	BOOL	executing;
+extern	int	infile;
+int	exitflg;
+extern	int	eof;
+
+int	alldigs(char*);
+void	fault(void*, char*);
+
+extern	char	*Ipath;
+jmp_buf env;
+static char *errmsg;
+
+Fhdr *symhdr, *corhdr;
+
+void
+usage(void)
+{
+	fprint(2, "usage: db [-kw] [-m machine] [-I dir] [symfile] [pid]\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int i, omode;
+	char *s;
+	char *name;
+	Fhdr *hdr;
+
+	name = 0;
+	outputinit();
+	maxoff = MAXOFF;
+	omode = OREAD;
+	ARGBEGIN{
+	default:
+		usage();
+	case 'A':
+		abort();
+	case 'k':
+		kflag = 1;
+		break;
+	case 'w':
+		omode = ORDWR;
+		break;
+	case 'I':
+		s = ARGF();
+		if(s == 0)
+			dprint("missing -I argument\n");
+		else
+			Ipath = s;
+		break;
+	case 'm':
+		name = ARGF();
+		if(name == 0)
+			dprint("missing -m argument\n");
+		break;
+	}ARGEND
+
+	/*
+	 * Unix and Plan 9 differ on what the right order of pid, text, and core is.
+	 * I never remember anyway.  Let's just accept them in any order.
+	 */
+	for(i=0; i<argc; i++){
+		if(alldigs(argv[i])){
+			if(pid){
+				dprint("already have pid %d; ignoring pid %d\n", pid, argv[i]);
+				continue;
+			}
+			if(corhdr){
+				dprint("already have core %s; ignoring pid %d\n", corfil, pid);
+				continue;
+			}
+			pid = atoi(argv[i]);
+			continue;
+		}
+		if((hdr = crackhdr(argv[i], omode)) == nil){
+			dprint("crackhdr %s: %r\n", argv[i]);
+			continue;
+		}
+		dprint("%s: %s %s %s\n", argv[i], hdr->aname, hdr->mname, hdr->fname);
+		if(hdr->ftype == FCORE){
+			if(pid){
+				dprint("already have pid %d; ignoring core %s\n", pid, argv[i]);
+				uncrackhdr(hdr);
+				continue;
+			}
+			if(corhdr){
+				dprint("already have core %s; ignoring core %s\n", corfil, argv[i]);
+				uncrackhdr(hdr);
+				continue;
+			}
+			corhdr = hdr;
+			corfil = argv[i];
+		}else{
+			if(symhdr){
+				dprint("already have text %s; ignoring text %s\n", symfil, argv[i]);
+				uncrackhdr(hdr);
+				continue;
+			}
+			symhdr = hdr;
+			symfil = argv[i];
+		}
+	}
+
+	if(symhdr==nil){
+		symfil = "a.out";
+		if(pid){
+			if((s = proctextfile(pid)) != nil){
+				dprint("pid %d: text %s\n", pid, s);
+				symfil = s;
+			}
+		}
+		/* XXX pull command from core */
+
+		if((symhdr = crackhdr(symfil, omode)) == nil){
+			dprint("crackhdr %s: %r\n", symfil);
+			symfil = nil;
+		}
+	}
+
+	if(!mach)
+		mach = machcpu;
+
+	/*
+	 * Set up maps.
+	 */
+	symmap = allocmap();
+	cormap = allocmap();
+	if(symmap == nil || cormap == nil)
+		sysfatal("allocating maps: %r");
+
+	if(symhdr){
+		if(mapfile(symhdr, 0, symmap, nil) < 0)
+			dprint("mapping %s: %r\n", symfil);
+		mapfile(symhdr, 0, cormap, nil);
+	}
+
+	dotmap = dumbmap(-1);
+
+	/*
+	 * show initial state and drop into the execution loop.
+	 */
+	notify(fault);
+	setsym();
+	if(setjmp(env) == 0){
+		if (pid || corhdr)
+			setcor();	/* could get error */
+		if (correg) {
+			dprint("%s\n", mach->exc(cormap, correg));
+			printpc();
+		}
+	}
+
+	setjmp(env);
+	if (executing)
+		delbp();
+	executing = FALSE;
+	for (;;) {
+		flushbuf();
+		if (errmsg) {
+			dprint(errmsg);
+			printc('\n');
+			errmsg = 0;
+			exitflg = 0;
+		}
+		if (mkfault) {
+			mkfault=0;
+			printc('\n');
+			prints(DBNAME);
+		}
+		clrinp();
+		rdc();
+		reread();
+		if (eof) {
+			if (infile == STDIN)
+				done();
+			iclose(-1, 0);
+			eof = 0;
+			longjmp(env, 1);
+		}
+		exitflg = 0;
+		command(0, 0);
+		reread();
+		if (rdc() != '\n')
+			error("newline expected");
+	}
+}
+
+int
+alldigs(char *s)
+{
+	while(*s){
+		if(*s<'0' || '9'<*s)
+			return 0;
+		s++;
+	}
+	return 1;
+}
+
+void
+done(void)
+{
+	if (pid)
+		endpcs();
+	exits(exitflg? "error": 0);
+}
+
+/*
+ * An error occurred; save the message for later printing,
+ * close open files, and reset to main command loop.
+ */
+void
+error(char *n)
+{
+	errmsg = n;
+	iclose(0, 1);
+	oclose();
+	flush();
+	delbp();
+	ending = 0;
+	longjmp(env, 1);
+}
+
+void
+errors(char *m, char *n)
+{
+	static char buf[128];
+
+	sprint(buf, "%s: %s", m, n);
+	error(buf);
+}
+
+/*
+ * An interrupt occurred;
+ * seek to the end of the current file
+ * and remember that there was a fault.
+ */
+void
+fault(void *a, char *s)
+{
+	USED(a);
+	if(strncmp(s, "interrupt", 9) == 0){
+		seek(infile, 0L, 2);
+		mkfault++;
+		noted(NCONT);
+	}
+	noted(NDFLT);
+}
diff --git a/src/cmd/db/mkfile b/src/cmd/db/mkfile
new file mode 100644
index 0000000..65b8797
--- /dev/null
+++ b/src/cmd/db/mkfile
@@ -0,0 +1,26 @@
+<$PLAN9/src/mkhdr
+
+TARG=db
+OFILES=\
+	command.$O\
+	expr.$O\
+	format.$O\
+	input.$O\
+	main.$O\
+	output.$O\
+	pcs.$O\
+	runpcs.$O\
+	regs.$O\
+	trcrun.$O\
+	print.$O\
+	setup.$O\
+
+HFILES=defs.h\
+	fns.h\
+
+SHORTLIB=mach bio 9
+
+<$PLAN9/src/mkone
+
+CFLAGS=$CFLAGS -I../libmach2
+
diff --git a/src/cmd/db/output.c b/src/cmd/db/output.c
new file mode 100644
index 0000000..176cec2
--- /dev/null
+++ b/src/cmd/db/output.c
@@ -0,0 +1,159 @@
+/*
+ *
+ *	debugger
+ *
+ */
+
+#include "defs.h"
+#include "fns.h"
+
+int	printcol = 0;
+int	infile = STDIN;
+int	maxpos = MAXPOS;
+
+Biobuf	bstdout;
+
+void
+printc(int c)
+{
+	dprint("%c", c);
+}
+
+/* was move to next f1-sized tab stop; now just print a tab */
+int
+tconv(Fmt *f)
+{
+	return fmtstrcpy(f, "\t");
+}
+
+void
+flushbuf(void)
+{
+ 	if (printcol != 0)
+		printc(EOR);
+}
+
+void
+prints(char *s)
+{
+	dprint("%s",s);
+}
+
+void
+newline(void)
+{
+	printc(EOR);
+}
+
+#define	MAXIFD	5
+struct {
+	int	fd;
+	int	r9;
+} istack[MAXIFD];
+int	ifiledepth;
+
+void
+iclose(int stack, int err)
+{
+	if (err) {
+		if (infile) {
+			close(infile);
+			infile=STDIN;
+		}
+		while (--ifiledepth >= 0)
+			if (istack[ifiledepth].fd)
+				close(istack[ifiledepth].fd);
+		ifiledepth = 0;
+	} else if (stack == 0) {
+		if (infile) {
+			close(infile);
+			infile=STDIN;
+		}
+	} else if (stack > 0) {
+		if (ifiledepth >= MAXIFD)
+			error("$<< nested too deeply");
+		istack[ifiledepth].fd = infile;
+		ifiledepth++;
+		infile = STDIN;
+	} else {
+		if (infile) {
+			close(infile); 
+			infile=STDIN;
+		}
+		if (ifiledepth > 0) {
+			infile = istack[--ifiledepth].fd;
+		}
+	}
+}
+
+void
+oclose(void)
+{
+	flushbuf();
+	Bterm(&bstdout);
+	Binit(&bstdout, 1, OWRITE);
+}
+
+void
+redirout(char *file)
+{
+	int fd;
+
+	if (file == 0){
+		oclose();
+		return;
+	}
+	flushbuf();
+	if ((fd = open(file, 1)) >= 0)
+		seek(fd, 0L, 2);
+	else if ((fd = create(file, 1, 0666)) < 0)
+		error("cannot create");
+	Bterm(&bstdout);
+	Binit(&bstdout, fd, OWRITE);
+}
+
+void
+endline(void)
+{
+
+	if (maxpos <= printcol)
+		newline();
+}
+
+void
+flush(void)
+{
+	Bflush(&bstdout);
+}
+
+int
+dprint(char *fmt, ...)
+{
+	int n, w;
+	char *p;
+ 	char buf[4096];
+	Rune r;
+	va_list arg;
+
+	if(mkfault)
+		return -1;
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof buf, fmt, arg) - buf;
+	va_end(arg);
+	Bwrite(&bstdout, buf, n);
+	for(p=buf; *p; p+=w){
+		w = chartorune(&r, p);
+		if(r == '\n')
+			printcol = 0;
+		else
+			printcol++;
+	}
+	return n;
+}
+
+void
+outputinit(void)
+{
+	Binit(&bstdout, 1, OWRITE);
+	fmtinstall('t', tconv);
+}
diff --git a/src/cmd/db/pcs.c b/src/cmd/db/pcs.c
new file mode 100644
index 0000000..b876f50
--- /dev/null
+++ b/src/cmd/db/pcs.c
@@ -0,0 +1,177 @@
+/*
+ *
+ *	debugger
+ *
+ */
+
+#include "defs.h"
+#include "fns.h"
+
+char	NOPCS[] = "no process";
+
+/* sub process control */
+
+void
+subpcs(int modif)
+{
+	int	check;
+	int	runmode;
+	int	keepnote;
+	int	n, r;
+	ulong line, curr;
+	BKPT *bk;
+	char *comptr;
+
+	runmode=SINGLE;
+	r = 0;
+	keepnote=0;
+	loopcnt=cntval;
+	switch (modif) {
+
+		/* delete breakpoint */
+	case 'd': 
+	case 'D':
+		if ((bk=scanbkpt(dot)) == 0)
+			error("no breakpoint set");
+		bk->flag=BKPTCLR;
+		return;
+
+		/* set breakpoint */
+	case 'b': 
+	case 'B':
+		if (bk=scanbkpt(dot))
+			bk->flag=BKPTCLR;
+		for (bk=bkpthead; bk; bk=bk->nxtbkpt)
+			if (bk->flag == BKPTCLR)
+				break;
+		if (bk==0) {
+			bk = (BKPT *)malloc(sizeof(*bk));
+			if (bk == 0)
+				error("too many breakpoints");
+			bk->nxtbkpt=bkpthead;
+			bkpthead=bk;
+		}
+		bk->loc = dot;
+		bk->initcnt = bk->count = cntval;
+		bk->flag = modif == 'b' ? BKPTSET : BKPTTMP;
+		check=MAXCOM-1;
+		comptr=bk->comm;
+		rdc();
+		reread();
+		do {
+			*comptr++ = readchar();
+		} while (check-- && lastc!=EOR);
+		*comptr=0;
+		if(bk->comm[0] != EOR && cntflg == FALSE)
+			bk->initcnt = bk->count = HUGEINT;
+		reread();
+		if (check)
+			return;
+		error("bkpt command too long");
+
+		/* exit */
+	case 'k' :
+	case 'K':
+		if (pid == 0)
+			error(NOPCS);
+		dprint("%d: killed", pid);
+		pcsactive = 1;	/* force 'kill' ctl */
+		endpcs();
+		return;
+
+		/* run program */
+	case 'r': 
+	case 'R':
+		endpcs();
+		setup();
+		runmode = CONTIN;
+		break;
+
+		/* single step */
+	case 's': 
+		if (pid == 0) {
+			setup();
+			loopcnt--;
+		}
+		runmode=SINGLE;
+		keepnote=defval(1);
+		break;
+	case 'S':
+		if (pid == 0) {
+			setup();
+			loopcnt--;
+		}
+		keepnote=defval(1);
+		if(pc2line(dbrget(cormap, mach->pc), &line) < 0)
+			error("%r");
+		n = loopcnt;
+		dprint("%s: running\n", symfil);
+		flush();
+		for (loopcnt = 1; n > 0; loopcnt = 1) {
+			r = runpcs(SINGLE, keepnote);
+			if(pc2line(dot, &curr) < 0)
+				error("%r");
+			if (line != curr) {	/* on a new line of c */
+				line = curr;
+				n--;
+			}
+		}
+		loopcnt = 0;
+		break;
+		/* continue with optional note */
+	case 'c': 
+	case 'C': 
+		if (pid==0)
+			error(NOPCS);
+		runmode=CONTIN;
+		keepnote=defval(1);
+		break;
+
+	case 'n':	/* deal with notes */
+		if (pid==0)
+			error(NOPCS);
+		n=defval(-1);
+		if(n>=0 && n<nnote){
+			nnote--;
+			memmove(note[n], note[n+1], (nnote-n)*sizeof(note[0]));
+		}
+		notes();
+		return;
+
+	case 'h':	/* halt the current process */
+		if (adrflg && adrval == 0) {
+			if (pid == 0)
+				error(NOPCS);
+			ungrab();
+		}
+		else {
+			grab();
+			dprint("stopped at%16t");
+			goto Return;
+		}
+		return;
+
+	case 'x':	/* continue executing the current process */
+		if (pid == 0)
+			error(NOPCS);
+		ungrab();
+		return;
+
+	default:
+		error("bad `:' command");
+	}
+
+	if (loopcnt>0) {
+		dprint("%s: running\n", symfil);
+		flush();
+		r = runpcs(runmode,keepnote);
+	}
+	if (r)
+		dprint("breakpoint%16t");
+	else
+		dprint("stopped at%16t");
+    Return:
+	delbp();
+	printpc();
+	notes();
+}
diff --git a/src/cmd/db/print.c b/src/cmd/db/print.c
new file mode 100644
index 0000000..d136a78
--- /dev/null
+++ b/src/cmd/db/print.c
@@ -0,0 +1,406 @@
+/*
+ *
+ *	debugger
+ *
+ */
+#include "defs.h"
+#include "fns.h"
+
+extern	int	infile;
+extern	int	outfile;
+extern	int	maxpos;
+
+/* general printing routines ($) */
+
+char	*Ipath = INCDIR;
+static	int	tracetype;
+static void	printfp(Map*, int);
+
+/*
+ *	callback on stack trace
+ */
+static int
+ptrace(Map *map, Regs *regs, ulong pc, ulong nextpc, Symbol *sym, int depth)
+{
+	char buf[512];
+
+	USED(map);
+	if(sym){
+		dprint("%s(", sym->name);
+		printparams(sym, regs);
+		dprint(") ");
+	}else
+		dprint("%#lux ", pc);
+	printsource(pc);
+
+	dprint(" called from ");
+	symoff(buf, 512, nextpc, CTEXT);
+	dprint("%s ", buf);
+/*	printsource(nextpc); */
+	dprint("\n");
+	if(tracetype == 'C' && sym)
+		printlocals(sym, regs);
+	return depth<40;
+}
+
+static ulong *adrregvals;
+
+static int
+adrrw(Regs *regs, char *name, ulong *val, int isr)
+{
+	int i;
+
+	if((i = windindex(name)) == -1)
+		return correg->rw(correg, name, val, isr);
+	if(isr){
+		*val = adrregvals[i];
+		return 0;
+	}
+	werrstr("saved registers are immutable");
+	return -1;
+}
+
+Regs*
+adrregs(void)
+{
+	int i;
+	static Regs r;
+	static u32int x;
+
+	if(adrregvals== nil){
+		adrregvals = malloc(mach->nwindreg*sizeof(adrregvals[0]));
+		if(adrregvals == nil)
+			error("%r");
+	}
+	for(i=0; i<mach->nwindreg; i++){
+		if(get4(cormap, adrval+4*i, &x) < 0)
+			error("%r");
+		adrregvals[i] = x;
+	}
+	r.rw = adrrw;
+	return &r;
+}
+
+void
+printdollar(int modif)
+{
+	int	i;
+	u32int u4;
+	BKPT *bk;
+	Symbol s;
+	int	stack;
+	char	*fname;
+	char buf[512];
+	Regs *r;
+
+	if (cntflg==0)
+		cntval = -1;
+	switch (modif) {
+
+	case '<':
+		if (cntval == 0) {
+			while (readchar() != EOR)
+				;
+			reread();
+			break;
+		}
+		if (rdc() == '<')
+			stack = 1;
+		else {
+			stack = 0;
+			reread();
+		}
+		fname = getfname();
+		redirin(stack, fname);
+		break;
+
+	case '>':
+		fname = getfname();
+		redirout(fname);
+		break;
+
+	case 'a':
+		attachprocess();
+		break;
+
+	case 'k':
+		kmsys();
+		break;
+
+	case 'q':
+	case 'Q':
+		done();
+
+	case 'w':
+		maxpos=(adrflg?adrval:MAXPOS);
+		break;
+
+	case 'S':
+		printsym();
+		break;
+
+	case 's':
+		maxoff=(adrflg?adrval:MAXOFF);
+		break;
+
+	case 'm':
+		printmap("? map", symmap);
+		printmap("/ map", cormap);
+		break;
+
+	case 0:
+	case '?':
+		if (pid)
+			dprint("pid = %d\n",pid);
+		else
+			prints("no process\n");
+		flushbuf();
+
+	case 'r':
+	case 'R':
+		printregs(modif);
+		return;
+
+	case 'f':
+	case 'F':
+		printfp(cormap, modif);
+		return;
+
+	case 'c':
+	case 'C':
+		tracetype = modif;
+		if (adrflg)
+			r = adrregs();
+		else
+			r = correg;
+		if(stacktrace(cormap, correg, ptrace) <= 0)
+			error("no stack frame");
+		break;
+
+		/*print externals*/
+	case 'e':
+		for (i = 0; indexsym(i, &s)>=0; i++) {
+			if (s.class==CDATA)
+			if (s.loc.type==LADDR)
+			if (get4(cormap, s.loc.addr, &u4) > 0)
+				dprint("%s/%12t%#lux\n", s.name, (ulong)u4);
+		}
+		break;
+
+		/*print breakpoints*/
+	case 'b':
+	case 'B':
+		for (bk=bkpthead; bk; bk=bk->nxtbkpt)
+			if (bk->flag) {
+				symoff(buf, 512, (WORD)bk->loc, CTEXT);
+				dprint(buf);
+				if (bk->count != 1)
+					dprint(",%d", bk->count);
+				dprint(":%c %s", bk->flag == BKPTTMP ? 'B' : 'b', bk->comm);
+			}
+		break;
+
+	case 'M':
+		fname = getfname();
+		if (machbyname(fname) == 0)
+			dprint("unknown name\n");;
+		break;
+	default:
+		error("bad `$' command");
+	}
+
+}
+
+char *
+getfname(void)
+{
+	static char fname[ARB];
+	char *p;
+
+	if (rdc() == EOR) {
+		reread();
+		return (0);
+	}
+	p = fname;
+	do {
+		*p++ = lastc;
+		if (p >= &fname[ARB-1])
+			error("filename too long");
+	} while (rdc() != EOR);
+	*p = 0;
+	reread();
+	return (fname);
+}
+
+static void
+printfp(Map *map, int modif)
+{
+	Regdesc *rp;
+	int i;
+	int ret;
+	char buf[512];
+
+	for (i = 0, rp = mach->reglist; rp->name; rp += ret) {
+		ret = 1;
+		if (!(rp->flags&RFLT))
+			continue;
+		ret = fpformat(map, rp, buf, sizeof(buf), modif);
+		if (ret < 0) {
+			werrstr("Register %s: %r", rp->name);
+			error("%r");
+		}
+			/* double column print */
+		if (i&0x01)
+			dprint("%40t%-8s%-12s\n", rp->name, buf);
+		else
+			dprint("\t%-8s%-12s", rp->name, buf);
+		i++;
+	}
+}
+
+void
+redirin(int stack, char *file)
+{
+	char pfile[ARB];
+
+	if (file == 0) {
+		iclose(-1, 0);
+		return;
+	}
+	iclose(stack, 0);
+	if ((infile = open(file, 0)) < 0) {
+		strcpy(pfile, Ipath);
+		strcat(pfile, "/");
+		strcat(pfile, file);
+		if ((infile = open(pfile, 0)) < 0) {
+			infile = STDIN;
+			error("cannot open");
+		}
+	}
+}
+
+void
+printmap(char *s, Map *map)
+{
+	int i;
+
+	if (!map)
+		return;
+	if (map == symmap)
+		dprint("%s%12t`%s'\n", s, symfil==nil ? "-" : symfil);
+	else if (map == cormap)
+		dprint("%s%12t`%s'\n", s, corfil==nil ? "-" : corfil);
+	else
+		dprint("%s\n", s);
+	for (i = 0; i < map->nseg; i++) {
+		dprint("%s%8t%-16#lux %-16#lux %-16#lux %s\n", map->seg[i].name,
+			map->seg[i].base, map->seg[i].base+map->seg[i].size, map->seg[i].offset,
+			map->seg[i].file ? map->seg[i].file : "");
+	}
+}
+
+/*
+ *	dump the raw symbol table
+ */
+void
+printsym(void)
+{
+	int i;
+	Symbol *sp, s;
+
+	for (i=0; indexsym(i, &s)>=0; i++){
+		sp = &s;
+		switch(sp->type) {
+		case 't':
+		case 'l':
+			dprint("%8#lux t %s\n", sp->loc.addr, sp->name);
+			break;
+		case 'T':
+		case 'L':
+			dprint("%8#lux T %s\n", sp->loc.addr, sp->name);
+			break;
+		case 'D':
+		case 'd':
+		case 'B':
+		case 'b':
+		case 'a':
+		case 'p':
+		case 'm':
+			dprint("%8#lux %c %s\n", sp->loc.addr, sp->type, sp->name);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+#define	STRINGSZ	128
+
+/*
+ *	print the value of dot as file:line
+ */
+void
+printsource(long dot)
+{
+	char str[STRINGSZ];
+
+	if (fileline(dot, str, STRINGSZ) >= 0)
+		dprint("%s", str);
+}
+
+void
+printpc(void)
+{
+	char buf[512];
+	ulong u;
+
+	if(rget(correg, mach->pc, &u) < 0)
+		error("%r");
+	dot = u;
+	if(dot){
+		printsource((long)dot);
+		printc(' ');
+		symoff(buf, sizeof(buf), (long)dot, CTEXT);
+		dprint("%s/", buf);
+		if (mach->das(cormap, dot, 'i', buf, sizeof(buf)) < 0)
+			error("%r");
+		dprint("%16t%s\n", buf);
+	}
+}
+
+void
+printlocals(Symbol *fn, Regs *regs)
+{
+	int i;
+	u32int v;
+	Symbol s;
+
+	for (i = 0; indexlsym(fn, i, &s)>=0; i++) {
+		if (s.class != CAUTO)
+			continue;
+		if(lget4(cormap, correg, s.loc, &v) >= 0)
+			dprint("%8t%s.%s/%10t%#lux\n", fn->name, s.name, v);
+		else
+			dprint("%8t%s.%s/%10t?\n", fn->name, s.name);
+	}
+}
+
+void
+printparams(Symbol *fn, Regs *regs)
+{
+	int i;
+	Symbol s;
+	u32int v;
+	int first = 0;
+
+	for (i = 0; indexlsym(fn, i, &s)>=0; i++) {
+		if (s.class != CPARAM)
+			continue;
+		if (first++)
+			dprint(", ");
+		if(lget4(cormap, correg, s.loc, &v) >= 0)
+			dprint("%s=%#lux", s.name, v);
+		else
+			dprint("%s=?", s.name);
+	}
+}
diff --git a/src/cmd/db/regs.c b/src/cmd/db/regs.c
new file mode 100644
index 0000000..4a9a442
--- /dev/null
+++ b/src/cmd/db/regs.c
@@ -0,0 +1,44 @@
+/*
+ * code to keep track of registers
+ */
+
+#include "defs.h"
+#include "fns.h"
+
+/*
+ * print the registers
+ */
+void
+printregs(int c)
+{
+	Regdesc *rp;
+	int i;
+	ulong u;
+
+	if(correg == nil){
+		dprint("registers not mapped\n");
+		return;
+	}
+
+	for (i = 1, rp = mach->reglist; rp->name; rp++, i++) {
+		if ((rp->flags & RFLT)) {
+			if (c != 'R')
+				continue;
+			if (rp->format == '8' || rp->format == '3')
+				continue;
+		}
+		rget(correg, rp->name, &u);
+		if(rp->format == 'Y')
+			dprint("%-8s %-20#llux", rp->name, (uvlong)u);
+		else
+			dprint("%-8s %-12#lux", rp->name, (ulong)u);
+		if ((i % 3) == 0) {
+			dprint("\n");
+			i = 0;
+		}
+	}
+	if (i != 1)
+		dprint("\n");
+	dprint ("%s\n", mach->exc(cormap, correg));
+	printpc();
+}
diff --git a/src/cmd/db/runpcs.c b/src/cmd/db/runpcs.c
new file mode 100644
index 0000000..4c05366
--- /dev/null
+++ b/src/cmd/db/runpcs.c
@@ -0,0 +1,205 @@
+/*
+ *
+ *	debugger
+ *
+ */
+
+#include "defs.h"
+#include "fns.h"
+
+BKPT *bkpthead;
+
+BOOL bpin;
+
+int pid;
+int nnote;
+int ending;
+char note[NNOTE][ERRMAX];
+
+/* service routines for sub process control */
+
+int
+runpcs(int runmode, int keepnote)
+{
+	int rc;
+	BKPT *bkpt;
+	ADDR x;
+
+	rc = 0;
+	if (adrflg)
+		rput(correg, mach->pc, dot);
+	if(rget(correg, mach->pc, &dot) < 0)
+		error("%r");
+	flush();
+	while (--loopcnt >= 0) {
+		if(loopcnt != 0)
+			printpc();
+		if (runmode == SINGLE) {
+			bkpt = scanbkpt(dot);
+			if (bkpt) {
+				switch(bkpt->flag){
+				case BKPTTMP:
+					bkpt->flag = BKPTCLR;
+					break;
+				case BKPTSKIP:
+					bkpt->flag = BKPTSET;
+					break;
+				}
+			}
+			runstep(dot, keepnote);
+		} else {
+			if(rget(correg, mach->pc, &x) < 0)
+				error("%r");
+			if ((bkpt = scanbkpt(x)) != 0) {
+				execbkpt(bkpt, keepnote);
+				keepnote = 0;
+			}
+			setbp();
+			runrun(keepnote);
+		}
+		keepnote = 0;
+		delbp();
+		if(rget(correg, mach->pc, &dot) < 0)
+			error("%r");
+		/* real note? */
+		if (nnote > 0) {
+			keepnote = 1;
+			rc = 0;
+			continue;
+		}
+		bkpt = scanbkpt(dot);
+		if(bkpt == 0){
+			keepnote = 0;
+			rc = 0;
+			continue;
+		}
+		/* breakpoint */
+		if (bkpt->flag == BKPTTMP)
+			bkpt->flag = BKPTCLR;
+		else if (bkpt->flag == BKPTSKIP) {
+			execbkpt(bkpt, keepnote);
+			keepnote = 0;
+			loopcnt++;	/* we didn't really stop */
+			continue;
+		}
+		else {
+			bkpt->flag = BKPTSKIP;
+			--bkpt->count;
+			if ((bkpt->comm[0] == EOR || command(bkpt->comm, ':') != 0)
+			&&  bkpt->count != 0) {
+				execbkpt(bkpt, keepnote);
+				keepnote = 0;
+				loopcnt++;
+				continue;
+			}
+			bkpt->count = bkpt->initcnt;
+		}
+		rc = 1;
+	}
+	return(rc);
+}
+
+/*
+ * finish the process off;
+ * kill if still running
+ */
+
+void
+endpcs(void)
+{
+	BKPT *bk;
+
+	if(ending)
+		return;
+	ending = 1;
+	if (pid) {
+		if(pcsactive){
+			killpcs();
+			pcsactive = 0;
+		}
+		pid=0;
+		nnote=0;
+		for (bk=bkpthead; bk; bk = bk->nxtbkpt)
+			if (bk->flag == BKPTTMP)
+				bk->flag = BKPTCLR;
+			else if (bk->flag != BKPTCLR)
+				bk->flag = BKPTSET;
+	}
+	bpin = FALSE;
+	ending = 0;
+}
+
+/*
+ * start up the program to be debugged in a child
+ */
+
+void
+setup(void)
+{
+
+	nnote = 0;
+	startpcs();
+	bpin = FALSE;
+	pcsactive = 1;
+}
+
+/*
+ * skip over a breakpoint:
+ * remove breakpoints, then single step
+ * so we can put it back
+ */
+void
+execbkpt(BKPT *bk, int keepnote)
+{
+	runstep(bk->loc, keepnote);
+	bk->flag = BKPTSET;
+}
+
+/*
+ * find the breakpoint at adr, if any
+ */
+
+BKPT *
+scanbkpt(ADDR adr)
+{
+	BKPT *bk;
+
+	for (bk = bkpthead; bk; bk = bk->nxtbkpt)
+		if (bk->flag != BKPTCLR && bk->loc == adr)
+			break;
+	return(bk);
+}
+
+/*
+ * remove all breakpoints from the process' address space
+ */
+
+void
+delbp(void)
+{
+	BKPT *bk;
+
+	if (bpin == FALSE || pid == 0)
+		return;
+	for (bk = bkpthead; bk; bk = bk->nxtbkpt)
+		if (bk->flag != BKPTCLR)
+			bkput(bk, 0);
+	bpin = FALSE;
+}
+
+/*
+ * install all the breakpoints
+ */
+
+void
+setbp(void)
+{
+	BKPT *bk;
+
+	if (bpin == TRUE || pid == 0)
+		return;
+	for (bk = bkpthead; bk; bk = bk->nxtbkpt)
+		if (bk->flag != BKPTCLR)
+			bkput(bk, 1);
+	bpin = TRUE;
+}
diff --git a/src/cmd/db/setup.c b/src/cmd/db/setup.c
new file mode 100644
index 0000000..02871e2
--- /dev/null
+++ b/src/cmd/db/setup.c
@@ -0,0 +1,145 @@
+/*
+ * init routines
+ */
+#include "defs.h"
+#include "fns.h"
+
+char	*symfil;
+char	*corfil;
+
+Map	*symmap;
+Map	*cormap;
+Regs	*correg;
+Map	*dotmap;
+
+void
+setsym(void)
+{
+	if(symhdr && syminit(symhdr) < 0)
+		dprint("syminit: %r\n");
+/*
+	Symbol s;
+	if (mach->sbreg && lookup(0, mach->sbreg, &s) < 0)
+		mach->sb = s.loc.addr;
+*/
+}
+
+void
+setcor(void)
+{
+	unmapproc(cormap);
+	unmapfile(corhdr, cormap);
+	free(correg);
+	correg = nil;
+
+	if (pid > 0) {
+		if (mapproc(pid, cormap, &correg) < 0)
+			dprint("mapproc %d: %r\n", pid);
+	} else {
+		if (corhdr) {
+			if (mapfile(corhdr, 0, cormap, &correg) < 0)
+				dprint("mapfile %s: %r\n", corfil);
+		} else
+			dprint("no core image\n");
+	}
+	kmsys();
+	return;
+}
+
+Map*
+dumbmap(int fd)
+{
+	Map *dumb;
+	Seg s;
+
+	dumb = allocmap();
+	memset(&s, 0, sizeof s);
+	s.fd = fd;
+	s.base = 0;
+	s.offset = 0;
+	s.size = 0xFFFFFFFF;
+	s.name = "data";
+	s.file = "<dumb>";
+	if(addseg(dumb, s) < 0){
+		freemap(dumb);
+		return nil;
+	}
+	if(mach == nil)
+		mach = machcpu;
+	return dumb;
+}
+
+/*
+ * set up maps for a direct process image (/proc)
+ */
+void
+cmdmap(Map *map)
+{
+	int i;
+	char name[MAXSYM];
+
+	rdc();
+	readsym(name);
+	i = findseg(map, name, nil);
+	if (i < 0)	/* not found */
+		error("Invalid map name");
+
+	if (expr(0)) {
+	//	if (strcmp(name, "text") == 0)
+	//		textseg(expv, &fhdr);
+		map->seg[i].base = expv;
+	} else
+		error("Invalid base address"); 
+	if (expr(0))
+		map->seg[i].size = expv - map->seg[i].base;
+	else
+		error("Invalid end address"); 
+	if (expr(0))
+		map->seg[i].offset = expv; 
+	else
+		error("Invalid file offset"); 
+/*
+	if (rdc()=='?' && map == cormap) {
+		if (fcor)
+			close(fcor);
+		fcor=fsym;
+		corfil = symfil;
+		cormap = symmap;
+	} else if (lastc == '/' && map == symmap) {
+		if (fsym)
+			close(fsym);
+		fsym=fcor;
+		symfil=corfil;
+		symmap=cormap;
+	} else
+		reread();
+*/
+}
+
+void
+kmsys(void)
+{
+	int i;
+
+	i = findseg(symmap, "text", symfil);
+	if (i >= 0) {
+		symmap->seg[i].base = symmap->seg[i].base&~mach->ktmask;
+		symmap->seg[i].size = -symmap->seg[i].base;
+	}
+	i = findseg(symmap, "data", symfil);
+	if (i >= 0) {
+		symmap->seg[i].base = symmap->seg[i].base&~mach->ktmask;
+		symmap->seg[i].size = -symmap->seg[i].base;
+	}
+}
+
+void
+attachprocess(void)
+{
+	if (!adrflg) {
+		dprint("used pid$a\n");
+		return;
+	}
+	pid = adrval;
+	setcor();
+}
diff --git a/src/cmd/db/trcrun.c b/src/cmd/db/trcrun.c
new file mode 100644
index 0000000..807f902
--- /dev/null
+++ b/src/cmd/db/trcrun.c
@@ -0,0 +1,288 @@
+/*
+ * functions for running the debugged process
+ */
+
+#include "defs.h"
+#include "fns.h"
+
+
+int child;
+int msgfd = -1;
+int notefd = -1;
+int pcspid = -1;
+int pcsactive = 0;
+
+void
+setpcs(void)
+{
+	char buf[128];
+
+	if(pid && pid != pcspid){
+		if(msgfd >= 0){
+			close(msgfd);
+			msgfd = -1;
+		}
+		if(notefd >= 0){
+			close(notefd);
+			notefd = -1;
+		}
+		pcspid = -1;
+		sprint(buf, "/proc/%d/ctl", pid);
+		msgfd = open(buf, OWRITE);
+		if(msgfd < 0)
+			error("can't open control file");
+		sprint(buf, "/proc/%d/note", pid);
+		notefd = open(buf, ORDWR);
+		if(notefd < 0)
+			error("can't open note file");
+		pcspid = pid;
+	}
+}
+
+void
+msgpcs(char *msg)
+{
+	char err[ERRMAX];
+
+	setpcs();
+	if(write(msgfd, msg, strlen(msg)) < 0 && !ending){
+		errstr(err, sizeof err);
+		if(strcmp(err, "interrupted") != 0)
+			endpcs();
+		errors("can't write control file", err);
+	}
+}
+
+/*
+ * empty the note buffer and toss pending breakpoint notes
+ */
+void
+unloadnote(void)
+{
+	char err[ERRMAX];
+
+	setpcs();
+	for(; nnote<NNOTE; nnote++){
+		switch(read(notefd, note[nnote], sizeof note[nnote])){
+		case -1:
+			errstr(err, sizeof err);
+			if(strcmp(err, "interrupted") != 0)
+				endpcs();
+			errors("can't read note file", err);
+		case 0:
+			return;
+		}
+		note[nnote][ERRMAX-1] = '\0';
+		if(strncmp(note[nnote], "sys: breakpoint", 15) == 0)
+			--nnote;
+	}
+}
+
+/*
+ * reload the note buffer
+ */
+void
+loadnote(void)
+{
+	int i;
+	char err[ERRMAX];
+
+	setpcs();
+	for(i=0; i<nnote; i++){
+		if(write(notefd, note[i], strlen(note[i])) < 0){
+			errstr(err, sizeof err);
+			if(strcmp(err, "interrupted") != 0)
+				endpcs();
+			errors("can't write note file", err);
+		}
+	}
+	nnote = 0;
+}
+
+void
+notes(void)
+{
+	int n;
+
+	if(nnote == 0)
+		return;
+	dprint("notes:\n");
+	for(n=0; n<nnote; n++)
+		dprint("%d:\t%s\n", n, note[n]);
+}
+
+void
+killpcs(void)
+{
+	msgpcs("kill");
+}
+
+void
+grab(void)
+{
+	flush();
+	msgpcs("stop");
+	bpwait();
+}
+
+void
+ungrab(void)
+{
+	msgpcs("start");
+}
+
+void
+doexec(void)
+{
+	char *argl[MAXARG];
+	char args[LINSIZ];
+	char *p;
+	char **ap;
+	char *thisarg;
+
+	ap = argl;
+	p = args;
+	*ap++ = symfil;
+	for (rdc(); lastc != EOR;) {
+		thisarg = p;
+		if (lastc == '<' || lastc == '>') {
+			*p++ = lastc;
+			rdc();
+		}
+		while (lastc != EOR && lastc != SPC && lastc != TB) {
+			*p++ = lastc;
+			readchar();
+		}
+		if (lastc == SPC || lastc == TB)
+			rdc();
+		*p++ = 0;
+		if (*thisarg == '<') {
+			close(0);
+			if (open(&thisarg[1], OREAD) < 0) {
+				print("%s: cannot open\n", &thisarg[1]);
+				_exits(0);
+			}
+		}
+		else if (*thisarg == '>') {
+			close(1);
+			if (create(&thisarg[1], OWRITE, 0666) < 0) {
+				print("%s: cannot create\n", &thisarg[1]);
+				_exits(0);
+			}
+		}
+		else
+			*ap++ = thisarg;
+	}
+	*ap = 0;
+	exec(symfil, argl);
+	perror(symfil);
+}
+
+char	procname[100];
+
+void
+startpcs(void)
+{
+	if ((pid = fork()) == 0) {
+		pid = getpid();
+		msgpcs("hang");
+		doexec();
+		exits(0);
+	}
+
+	if (pid == -1)
+		error("can't fork");
+	child++;
+	sprint(procname, "/proc/%d/mem", pid);
+	corfil = procname;
+	msgpcs("waitstop");
+	bpwait();
+	if (adrflg)
+		rput(correg, mach->pc, adrval);
+	while (rdc() != EOR)
+		;
+	reread();
+}
+
+void
+runstep(ulong loc, int keepnote)
+{
+	int nfoll;
+	ulong foll[3];
+	BKPT bkpt[3];
+	int i;
+
+	if(mach->foll == 0){
+		dprint("stepping unimplemented; assuming not a branch\n");
+		nfoll = 1;
+		foll[0] = loc+mach->pcquant;
+	}else {
+		nfoll = mach->foll(cormap, correg, loc, foll);
+		if (nfoll < 0)
+			error("%r");
+	}
+	memset(bkpt, 0, sizeof bkpt);
+	for(i=0; i<nfoll; i++){
+		if(foll[i] == loc)
+			error("can't single step: next instruction is dot");
+		bkpt[i].loc = foll[i];
+		bkput(&bkpt[i], 1);
+	}
+	runrun(keepnote);
+	for(i=0; i<nfoll; i++)
+		bkput(&bkpt[i], 0);
+}
+
+void
+bpwait(void)
+{
+	setcor();
+	unloadnote();
+}
+
+void
+runrun(int keepnote)
+{
+	int on;
+
+	on = nnote;
+	unloadnote();
+	if(on != nnote){
+		notes();
+		error("not running: new notes pending");
+	}
+	if(keepnote)
+		loadnote();
+	else
+		nnote = 0;
+	flush();
+	msgpcs("startstop");
+	bpwait();
+}
+
+void
+bkput(BKPT *bp, int install)
+{
+	char buf[256];
+	ulong loc;
+	int ret;
+
+	errstr(buf, sizeof buf);
+/*
+	if(mach->bpfix)
+		loc = (*mach->bpfix)(bp->loc);
+	else
+*/
+	loc = bp->loc;
+	if(install){
+		ret = get1(cormap, loc, bp->save, mach->bpsize);
+		if (ret > 0)
+			ret = put1(cormap, loc, mach->bpinst, mach->bpsize);
+	}else
+		ret = put1(cormap, loc, bp->save, mach->bpsize);
+	if(ret < 0){
+		sprint(buf, "can't set breakpoint at %#llux: %r", bp->loc);
+		print(buf);
+		read(0, buf, 100);
+	}
+}