+ * Print the contents of the addresses within the given range
+ * according to the given format.
+ */
+
+typedef struct {
+ String name;
+ String printfstring;
+ int length;
+} Format;
+
+private Format fmt[] = {
+ { "d", " %d", sizeof(short) },
+ { "D", " %ld", sizeof(long) },
+ { "o", " %o", sizeof(short) },
+ { "O", " %lo", sizeof(long) },
+ { "x", " %04x", sizeof(short) },
+ { "X", " %08x", sizeof(long) },
+ { "b", " \\%o", sizeof(char) },
+ { "c", " '%c'", sizeof(char) },
+ { "s", "%c", sizeof(char) },
+ { "f", " %f", sizeof(float) },
+ { "g", " %g", sizeof(double) },
+ { nil, nil, 0 }
+};
+
+private Format *findformat(s)
+String s;
+{
+ register Format *f;
+
+ f = &fmt[0];
+ while (f->name != nil and not streq(f->name, s)) {
+ ++f;
+ }
+ if (f->name == nil) {
+ error("bad print format \"%s\"", s);
+ }
+ return f;
+}
+
+/*
+ * Retrieve and print out the appropriate data in the given format.
+ * Floats have to be handled specially to allow the compiler to
+ * convert them to doubles when passing to printf.
+ */
+
+private printformat (f, addr)
+Format *f;
+Address addr;
+{
+ union {
+ char charv;
+ short shortv;
+ int intv;
+ float floatv;
+ double doublev;
+ } value;
+
+ value.intv = 0;
+ dread(&value, addr, f->length);
+ if (streq(f->name, "f")) {
+ printf(f->printfstring, value.floatv);
+ } else {
+ printf(f->printfstring, value);
+ }
+}
+
+public Address printdata(lowaddr, highaddr, format)
+Address lowaddr;
+Address highaddr;
+String format;
+{
+ int n;
+ register Address addr;
+ Format *f;
+
+ if (lowaddr > highaddr) {
+ error("first address larger than second");
+ }
+ f = findformat(format);
+ n = 0;
+ for (addr = lowaddr; addr <= highaddr; addr += f->length) {
+ if (n == 0) {
+ printf("%08x: ", addr);
+ }
+ printformat(f, addr);
+ ++n;
+ if (n >= (16 div f->length)) {
+ printf("\n");
+ n = 0;
+ }
+ }
+ if (n != 0) {
+ printf("\n");
+ }
+ prtaddr = addr;
+ return addr;
+}
+
+/*
+ * The other approach is to print n items starting with a given address.
+ */
+
+public printndata(count, startaddr, format)
+int count;
+Address startaddr;
+String format;
+{
+ int i, n;
+ Address addr;
+ Format *f;
+ Boolean isstring;
+ char c;
+
+ if (count <= 0) {
+ error("non-positive repetition count");
+ }
+ f = findformat(format);
+ isstring = (Boolean) streq(f->name, "s");
+ n = 0;
+ addr = startaddr;
+ for (i = 0; i < count; i++) {
+ if (n == 0) {
+ printf("%08x: ", addr);
+ }
+ if (isstring) {
+ printf("\"");
+ dread(&c, addr, sizeof(char));
+ while (c != '\0') {
+ printchar(c);
+ ++addr;
+ dread(&c, addr, sizeof(char));
+ }
+ printf("\"\n");
+ n = 0;
+ addr += sizeof(String);
+ } else {
+ printformat(f, addr);
+ ++n;
+ if (n >= (16 div f->length)) {
+ printf("\n");
+ n = 0;
+ }
+ addr += f->length;
+ }
+ }
+ if (n != 0) {
+ printf("\n");
+ }
+ prtaddr = addr;
+}
+
+/*
+ * Print out a value according to the given format.
+ */
+
+public printvalue(v, format)
+long v;
+String format;
+{
+ Format *f;
+ char *p, *q;
+
+ f = findformat(format);
+ if (streq(f->name, "s")) {
+ putchar('"');
+ p = (char *) &v;
+ q = p + sizeof(v);
+ while (p < q) {
+ printchar(*p);
+ ++p;
+ }
+ putchar('"');
+ } else {
+ printf(f->printfstring, v);
+ }
+ putchar('\n');
+}
+
+/*
+ * Print out an execution time error.
+ * Assumes the source position of the error has been calculated.
+ *
+ * Have to check if the -r option was specified; if so then
+ * the object file information hasn't been read in yet.
+ */
+
+public printerror()
+{
+ extern Integer sys_nsig;
+ extern String sys_siglist[];
+ integer err;
+
+ if (isfinished(process)) {
+ err = exitcode(process);
+ if (err == 0) {
+ printf("\"%s\" terminated normally\n", objname);
+ } else {
+ printf("\"%s\" terminated abnormally (exit code %d)\n",
+ objname, err
+ );
+ }
+ erecover();
+ }
+ err = errnum(process);
+ putchar('\n');
+ printsig(err);
+ putchar(' ');
+ printloc();
+ putchar('\n');
+ if (curline > 0) {
+ printlines(curline, curline);
+ } else {
+ printinst(pc, pc);
+ }
+ erecover();
+}
+
+/*
+ * Print out a signal.
+ */
+
+private String illinames[] = {
+ "reserved addressing fault",
+ "privileged instruction fault",
+ "reserved operand fault"
+};
+
+private String fpenames[] = {
+ nil,
+ "integer overflow trap",
+ "integer divide by zero trap",
+ "floating overflow trap",
+ "floating/decimal divide by zero trap",
+ "floating underflow trap",
+ "decimal overflow trap",
+ "subscript out of range trap",
+ "floating overflow fault",
+ "floating divide by zero fault",
+ "floating underflow fault"
+};
+
+public printsig (signo)
+integer signo;
+{
+ integer code;
+
+ if (signo < 0 or signo > sys_nsig) {
+ printf("[signal %d]", signo);
+ } else {
+ printf("%s", sys_siglist[signo]);
+ }
+ code = errcode(process);
+ if (signo == SIGILL) {
+ if (code >= 0 and code < sizeof(illinames) / sizeof(illinames[0])) {
+ printf(" (%s)", illinames[code]);
+ }
+ } else if (signo == SIGFPE) {
+ if (code > 0 and code < sizeof(fpenames) / sizeof(fpenames[0])) {
+ printf(" (%s)", fpenames[code]);
+ }
+ }
+}
+
+/*
+ * Note the termination of the program. We do this so as to avoid
+ * having the process exit, which would make the values of variables
+ * inaccessible. We do want to flush all output buffers here,
+ * otherwise it'll never get done.
+ */
+
+public endprogram()
+{
+ Integer exitcode;
+
+ stepto(nextaddr(pc, true));
+ printnews();
+ exitcode = argn(1, nil);
+ if (exitcode != 0) {
+ printf("\nexecution completed (exit code %d)\n", exitcode);
+ } else {
+ printf("\nexecution completed\n");
+ }
+ getsrcpos();
+ erecover();
+}
+
+/*
+ * Single step the machine a source line (or instruction if "inst_tracing"
+ * is true). If "isnext" is true, skip over procedure calls.
+ */
+
+private Address getcall();
+
+public dostep(isnext)
+Boolean isnext;
+{
+ register Address addr;
+ register Lineno line;
+ String filename;
+ Address startaddr;
+
+ startaddr = pc;
+ addr = nextaddr(pc, isnext);
+ if (not inst_tracing and nlhdr.nlines != 0) {
+ line = linelookup(addr);
+ while (line == 0) {
+ addr = nextaddr(addr, isnext);
+ line = linelookup(addr);
+ }
+ curline = line;
+ } else {
+ curline = 0;
+ }
+ stepto(addr);
+ filename = srcfilename(addr);
+ setsource(filename);
+}
+
+typedef char Bpinst;
+
+#define BP_OP O_BPT /* breakpoint trap */
+
+#define BP_ERRNO SIGTRAP /* signal received at a breakpoint */
+
+/*
+ * Setting a breakpoint at a location consists of saving
+ * the word at the location and poking a BP_OP there.
+ *
+ * We save the locations and words on a list for use in unsetting.
+ */
+
+typedef struct Savelist *Savelist;
+
+struct Savelist {
+ Address location;
+ Bpinst save;
+ short refcount;
+ Savelist link;
+};
+
+private Savelist savelist;
+
+/*
+ * Set a breakpoint at the given address. Only save the word there
+ * if it's not already a breakpoint.
+ */
+
+public setbp(addr)
+Address addr;
+{
+ Bpinst w, save;
+ register Savelist newsave, s;
+
+ for (s = savelist; s != nil; s = s->link) {
+ if (s->location == addr) {
+ s->refcount++;
+ return;
+ }
+ }
+ iread(&save, addr, sizeof(save));
+ newsave = new(Savelist);
+ newsave->location = addr;
+ newsave->save = save;
+ newsave->refcount = 1;
+ newsave->link = savelist;
+ savelist = newsave;
+ w = BP_OP;
+ iwrite(&w, addr, sizeof(w));
+}
+
+/*
+ * Unset a breakpoint; unfortunately we have to search the SAVELIST
+ * to find the saved value. The assumption is that the SAVELIST will
+ * usually be quite small.
+ */
+
+public unsetbp(addr)
+Address addr;
+{
+ register Savelist s, prev;
+
+ prev = nil;
+ for (s = savelist; s != nil; s = s->link) {
+ if (s->location == addr) {
+ iwrite(&s->save, addr, sizeof(s->save));
+ s->refcount--;
+ if (s->refcount == 0) {
+ if (prev == nil) {
+ savelist = s->link;
+ } else {
+ prev->link = s->link;
+ }
+ dispose(s);
+ }
+ return;
+ }
+ prev = s;
+ }
+ panic("unsetbp: couldn't find address %d", addr);
+}
+
+/*
+ * VAX instruction decoder, derived from adb.