[comp.os.minix] Updated version of MDB Part

bds@lzaz.ATT.COM (B.SZABLAK) (03/30/89)

>mdb ( on minix-st only ) disassembles the instructions b??.s wrong:
>It thinks always the long (medium ?) form is used.
>Patch, compile, and this works.
>(but there are some more bugs -- i will try to fix )

>Klamer Schutte (.signature at end)

Thanks for the fix! Here is an updated version of mdb which is ATARI specific
but might be a good start for some PC enthusiast. Note: requires the
ptrace system call.
------------------------------------cut here----------------------------------
echo x - mdb.1
gres '^X' '' > mdb.1 << '/'
XCOMMAND
X   mdb(1)	- MINIX Debugger
X
XINVOCATION
X   mdb [<executable> [<link file>]]
X
XEXPLANATION
X   Mdb provides a means of debugging minix programs. It supports symbolic
X   debugging if a "link file" exists; the link file is the output of the
X   ACK linker: ld. The arguments to mdb are:
X
X	<executable>	- The MINIX executable file. Defaults to "a.out".
X	<link file>	- The MINIX link file. Defaults to none.
X
X   Once started, mdb will display a command prompt: '*'. A command may
X   be entered in the following form:
X
X	[<expression>][<command> [<argument>]]
X
X   <expression> is a symbolic expression representing a location
X   in memory or a register, and should be in the form:
X
X	[<value> [+|- <value>]]
X
X   A <value> may be a symbol, a register, or a constant.
X   The symbol may be any external symbol found in the link file, or "_start"
X   if no link file is specified. "_start" represents the beginning address
X   of the program. A register is specified by a '$' followed by the
X   register's name, while a constant may be either an octal, decimal or hex
X   unsigned long integer expressed using standard C notation (no 'L' suffix
X   should be used). A character constant is represented by a single quote
X   followed by the character.
X
X   Not all commands use an address, but those that do have a default
X   value of the current program counter with the exception of the continue
X   command: 'c' and 'C'. Not all commands use an argument
X   list, but those that do default to an empty list. If the argument
X   contains a semi-colon (';'), then except for the breakpoint
X   command ('b') the string following the semi-colon is assumed to be a
X   new command.
X
X   Following is the list of valid commands. When mdb is first started,
X   no process is active. Most of the following commands require an active
X   process which may be created using the run ('r') command.
X
X	! - If the exclaimation point begins the command line then the
X	    MINIX program that follows it is executed. The default
X	    command is "/bin/sh". Note: full path names are required.
X
X		Example: * !vi mdb.c
X
X	    If the expression preceeding the exclaimation point is a
X	    valid data address, then the arguments specify how to modify
X	    its contents. The argument is to be in the form:
X		[<constant>] [<size>] [<expression>]
X	    and are interpreted as: "fill the data space starting at the
X	    specified address with <constant> values given by <expression>
X	    and are of size byte ('b'), half word ('h', i.e. short),
X	    or long word ('l')." The default value of constant is 1, of
X	    size is 'h', and of expression is 0.
X
X		Example: * _buf ! 20b 'a
X
X	T - This command prints the current active function in the program with
X	    its arguments.
X
X		Example: * T
X
X	t - This command prints the current function invocation
X	    heirarchy with their arguments.
X
X		Example: * t
X
X	/ - This command prints the values starting at the address
X	    specified. The argument is to be in the form:
X		[<constant>][<size>][<format>]
X	    format may have the values:
X		a - Displays characters until a zero value is found
X		    (<constant> and <size> are ignored).
X		c - Displays <constant> values as characters.
X		d - Displays <constant> values as signed decimal numbers.
X		i - Disassembles <constant> instructions (<size> ignored).
X		I - Disassembles <constant> instructions (<size> ignored).
X		o - Displays <constant> vlaues as octal numbers.
X		s - Displays characters until a zero value is found
X		    starting at the address contained at the location
X		    specified (<constant> and <size> are ignored).
X		u - Displays <constant> vlaues as unsigned decimal numbers.
X		x - Displays <constant> vlaues as hexadecimal numbers.
X
X		Example: * $a6-2/hx
X
X	x - This command prints the registers and the instructions
X	    starting at the specified address. An optional constant
X	    argument limits the number of instructions printed.
X
X		Example: * x
X
X	X - This command prints the instructions starting at the specified
X	    address. An optional constant argument limits the number of
X	    instructions printed.
X
X		Example: * _start X 10
X
X	R - This command starts a process using the executable given
X	    as mbd's first argument. The process will have no arguments
X	    and will be stopped prior to executing any instructions.
X
X		Example: * R
X
X	r - This command starts a process using the executable given
X	    as mbd's first argument. The process will be given the
X	    arguments on the command line up to the first semi-colon.
X	    If no arguments are specifed, then the arguments supplied
X	    with the last 'r' command are used. The processes' standard
X	    input and output may be redirected. The process will be
X	    stopped prior to executing any instructions.
X
X		Example: * r 3 <input >output
X
X	C - This command results in the stopped process being restarted
X	    with a pending signal specfied by the constant argument.
X	    A breakpoint is placed at the address specified if one is
X	    not already there, and if placed it is deleted when the
X	    process stops for any reason. No default address is assumed.
X
X		Example: * C 2
X
X	c - This command results in the stopped process being restarted
X	    with all pending signals canceled. A breakpoint is placed
X	    at the address specified if one is not already there, and
X	    if placed it is deleted when the process stops for any reason.
X	    No default address is assumed.
X
X		Example: * _func+4 c
X
X	I - This command results in the stopped process executing
X	    instructions in single step mode. The number of instructions
X	    executed is given in the constant argument. The signal
X	    that stopped the process will be sent when execution begins.
X
X		Example: * I 10
X
X
X	i - This command results in the stopped process executing
X	    instructions in single step mode. The number of instructions
X	    executed is given in the constant argument. All pending
X	    signals are canceled before execution begins.
X
X		Example: * i 10
X
X	M - This command results in the stopped process resuming
X	    execution until the long word at the location specified by the
X	    address is modified or until the number of instructions
X	    specified in the obtional constant argument are executed.
X	    The default number is 65,536. Each executed instruction is
X	    displayed prior to execution.
X
X		Example: * _var M 100
X
X	m - This command results in the stopped process resuming
X	    execution until the long word at the location specified by the
X	    address is modified or until the number of instructions
X	    specified in the obtional constant argument are executed.
X	    The default number is 65,536.
X
X		Example: * _var m
X
X	k - This command results in the current active process being
X	    terminated.
X
X		Example: * k
X
X	B - This command results in all the currently active breakpoints
X	    being listed.
X
X		Example: * B
X
X	b - This command results in a breakpoint being placed at the
X	    location specified by the address in program space. The
X	    string that follows the command contains the command(s) that
X	    are executed when the breakpoint is hit. It is recommended
X	    that breakpoints be placed at an offset of 4 bytes from the
X	    function entry point to permit a valid frame to be set up
X	    for back-tracing.
X
X		Example: * _func+4 b t;_var/lx
X
X	d - This command results in the breakpoint at the address
X	    specified being deleted.
X
X		Example: * _func+4 d
X
X	D - This command results in all breakpoints being deleted.
X
X		Example: * D
X
X	q - This command results in the currently active process being
X	    terminated and mdb exiting.
X
X		Example: * q
X
XRESULTS
X   Mdb's exit status is always zero.
X
XREFERENCES
X   ptrace(2)
/
echo x - user.h
gres '^X' '' > user.h << '/'
X#include "h/type.h"
X#include "h/const.h"
X#include "kernel/type.h"
X#include "kernel/const.h"
X#undef printf
X
Xstruct proc {
X#ifdef i8088
X  int p_reg[NR_REGS];		/* process' registers */
X#endif
X#ifdef ATARI_ST
X  long p_reg[NR_REGS];		/* process' registers */
X#endif
X  int *p_sp;			/* stack pointer */
X  struct pc_psw p_pcpsw;	/* pc and psw as pushed by interrupt */
X#ifdef i8088
X  int *p_splimit;		/* lowest legal stack value */
X#endif
X#ifdef ATARI_ST
X  int *p_splow;			/* lowest observed stack value */
X  int p_trap;			/* trap type (only low byte) */
X#endif
X  int p_flags;			/* P_SLOT_FREE, SENDING, RECEIVING, etc. */
X  struct mem_map p_map[NR_SEGS];/* memory map */
X#ifdef ATARI_ST
X  phys_clicks p_shadow;		/* set if shadowed process image */
X  int p_nflips;			/* statistics */
X  char p_physio;		/* cannot be (un)shadowed now if set */
X#endif
X  int p_pid;			/* process id passed in from MM */
X
X  real_time user_time;		/* user time in ticks */
X  real_time sys_time;		/* sys time in ticks */
X  real_time child_utime;	/* cumulative user time of children */
X  real_time child_stime;	/* cumulative sys time of children */
X  real_time p_alarm;		/* time of next alarm in ticks, or 0 */
X
X  struct proc *p_callerq;	/* head of list of procs wishing to send */
X  struct proc *p_sendlink;	/* link to next proc wishing to send */
X  message *p_messbuf;		/* pointer to message buffer */
X  int p_getfrom;		/* from whom does process want to receive? */
X
X  struct proc *p_nextready;	/* pointer to next ready process */
X  int p_pending;		/* bit map for pending signals 1-16 */
X};
/
echo x - strtol.c
gres '^X' '' > strtol.c << '/'
Xlong 
Xstrtol(s, p, base)
X	char	*s, **p;
X{
X	long	r;
X	int	sign, digit, hexlow, hexup, c;
X
X	r = 0L;
X	sign = 0;
X	while (*s == ' ' || *s == '\t') ++s;
X	if (*s == '-')
X	{
X		sign = 1;
X		++s;
X	}
X	if (base == 0)
X	{
X		if (*s == '0') base = (s[1] == 'x' || s[1] == 'X') ? 16 : 8;
X		else base = 10;
X	}
X	if (*s == '0' && (s[1] == 'x' || s[1] == 'X') && base == 16) s += 2;
X	digit = (base <= 10) ? (base + '0' - 1) : '9';
X	hexlow = 'a' + base - 10;
X	hexup = 'A' + base - 10;
X	for (c = *s ; ; c = *++s)
X	{
X		if (c >= '0' && c <= digit) c = c - '0';
X		else if (c >= 'a' && c < hexlow) c = c - 'a' + 10;
X		else if (c >= 'A' && c < hexup)	c = c - 'A' + 10;
X		else
X		{
X			if (p != 0) *p = s;
X			return sign ? -r : r;
X		}
X		r = r * base + c;
X	}
X}/
echo x - makefile
gres '^X' '' > makefile << '/'
XCFLAGS=-DATARI_ST -I/usr/src -T/usr/tmp
XOBJS=mdb.o mdbexp.o mdbdis.o strtol.o
XLD=/usr/lib/ld
XCV=/usr/lib/cv
XLIB=/usr/lib/libc.a /usr/lib/end.o
X
Xmdb: $(OBJS)
X	$(LD) -o $*.out -c /usr/lib/crtso.o $(OBJS) $(LIB)
X	$(CV) $*.out $@
X	chmem =10000 $@
/
echo x - out.h
gres '^X' '' > out.h << '/'
X/*
X * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands.
X * See the copyright notice in the file "../Copyright".
X */
X/*
X * output format for ACK assemblers
X */
X#ifndef ushort
X#define ushort	unsigned short
X#endif ushort
X
Xstruct outhead {
X	ushort 	oh_magic;	/* magic number */
X	ushort 	oh_stamp;	/* version stamp */
X	ushort	oh_flags;	/* several format flags */
X	ushort	oh_nsect;	/* number of outsect structures */
X	ushort	oh_nrelo;	/* number of outrelo structures */
X	ushort	oh_nname;	/* number of outname structures */
X	long	oh_nemit;	/* sum of all os_flen */
X	long	oh_nchar;	/* size of string area */
X};
X
X#define O_MAGIC	0x0201		/* magic number of output file */
X#define	O_STAMP	0		/* version stamp */
X#define MAXSECT	64		/* Maximum number of sections */
X
X#define	HF_LINK	0x0004		/* unresolved references left */
X#define	HF_8086	0x0008		/* os_base specially encoded */
X
Xstruct outsect {
X	long 	os_base;	/* startaddress in machine */
X	long	os_size;	/* section size in machine */
X	long	os_foff;	/* startaddress in file */
X	long	os_flen;	/* section size in file */
X	long	os_lign;	/* section alignment */
X};
X
Xstruct outrelo {
X	char	or_type;	/* type of reference */
X	char	or_sect;	/* referencing section */
X	ushort	or_nami;	/* referenced symbol index */
X	long	or_addr;	/* referencing address */
X};
X
Xstruct outname {
X	union {
X	  char	*on_ptr;	/* symbol name (in core) */
X	  long	on_off;		/* symbol name (in file) */
X	}	on_u;
X#define on_mptr	on_u.on_ptr
X#define on_foff	on_u.on_off
X	ushort	on_type;	/* symbol type */
X	ushort	on_desc;	/* debug info */
X	long	on_valu;	/* symbol value */
X};
X
X/*
X * relocation type bits
X */
X#define RELSZ	0x07		/* relocation length */
X#define RELO1	   1		/* 1 byte */
X#define RELO2	   2		/* 2 bytes */
X#define RELO4	   4		/* 4 bytes */
X#define RELPC	0x08		/* pc relative */
X#define RELBR	0x10		/* High order byte lowest address. */
X#define RELWR	0x20		/* High order word lowest address. */
X
X/*
X * section type bits and fields
X */
X#define S_TYP	0x007F		/* undefined, absolute or relative */
X#define S_EXT	0x0080		/* external flag */
X#define S_ETC	0x7F00		/* for symbolic debug, bypassing 'as' */
X
X/*
X * S_TYP field values
X */
X#define S_UND	0x0000		/* undefined item */
X#define S_ABS	0x0001		/* absolute item */
X#define S_MIN	0x0002		/* first user section */
X#define S_MAX	S_TYP		/* last user section */
X
X/*
X * S_ETC field values
X */
X#define S_SCT	0x0100		/* section names */
X#define S_LIN	0x0200		/* hll source line item */
X#define S_FIL	0x0300		/* hll source file item */
X#define S_MOD	0x0400		/* ass source file item */
X#define S_COM	0x1000		/* Common name. */
X
X/*
X * structure format strings
X */
X#define SF_HEAD		"22222244"
X#define SF_SECT		"44444"
X#define SF_RELO		"1124"
X#define SF_NAME		"4224"
X
X/*
X * structure sizes (bytes in file; add digits in SF_*)
X */
X#define SZ_HEAD		20
X#define SZ_SECT		20
X#define SZ_RELO		8
X#define SZ_NAME		12
X
X/*
X * file access macros
X */
X#define BADMAGIC(x)	((x).oh_magic!=O_MAGIC)
X#define OFF_SECT(x)	SZ_HEAD
X#define OFF_EMIT(x)	(OFF_SECT(x) + ((long)(x).oh_nsect * SZ_SECT))
X#define OFF_RELO(x)	(OFF_EMIT(x) + (x).oh_nemit)
X#define OFF_NAME(x)	(OFF_RELO(x) + ((long)(x).oh_nrelo * SZ_RELO))
X#define OFF_CHAR(x)	(OFF_NAME(x) + ((long)(x).oh_nname * SZ_NAME))
/

bds@lzaz.ATT.COM (B.SZABLAK) (03/30/89)

/*
 * 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"
#include "sgtty.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;
struct sgttyb in_gttyb, sv_gttyb; /* initial and saved */
struct tchars in_chars, sv_chars;
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(dotty)
{
	int i;

	if (dotty)
	{
		ioctl(0, TIOCGETP, &sv_gttyb);
		ioctl(0, TIOCGETC, &sv_chars);
		ioctl(0, TIOCSETP, &in_gttyb);
		ioctl(0, TIOCSETC, &in_chars);
	}
	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;
	ioctl(0, TIOCSETP, &sv_gttyb);
	ioctl(0, TIOCSETC, &sv_chars);
	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(0);
				findbpnt(cnt <= 1);
				break;
			case	SIGTRAP: /* trace trap? */
				if (req == T_STEP) break;
			default: /* signal */
				val = cursig;
				break;
			}
		}
	}
	while (--cnt > 0);
	update(1);
	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 == ';')
			{
				extern char **environ;

				argv[argc] = 0;
				if (inf) freopen(inf, "r", stdin);
				if (outf) freopen(outf, "w", stdout);
			        execve(name, argv, environ);
				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) & 0xF13F) == 0x500F)
#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);
	ioctl(0, TIOCSETP, &sv_gttyb);
	ioctl(0, TIOCSETC, &sv_chars);
	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(0); findbpnt(0); break;
			}
		}
		if (curval != ptrace(T_GETDATA, curpid, addr, 0L))
		{
			printf("Modification detected\n");
			break;
		}
	}
	while (--cnt);
	update(1);
	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(1);
				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 'v': printf("MDB version 1.0\n"); 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;
	ioctl(0, TIOCGETP, &in_gttyb);
	ioctl(0, TIOCGETC, &in_chars);

	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) (03/30/89)

/*
 * mdbdis.c - MINIX program disassembler
 *
 * 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.w", 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.%c\t#%d,",BTST(w,8)?"sub":"add",
					size[BFIELD(w,7,2)],
					((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 (ds) symbolic(gaddr+ds,'\0');
			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.w\t",shf[BFIELD(w,10,2)],
					BTST(w,8) ? 'l' : 'r');
				op1(m2,r2);
			}
			else
			{
				printf("%s%c.%c\t",shf[BFIELD(w,4,2)],
					BTST(w,8) ? 'l' : 'r',
					size[BFIELD(w,7,2)]);
				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;
}