Implemented colorize disassembly.

This commit is contained in:
Daniel Beer 2010-03-30 13:55:35 +13:00
parent 6f3d899b44
commit 9d83386048
9 changed files with 783 additions and 438 deletions

View File

@ -40,7 +40,7 @@ install: mspdebug mspdebug.man
.SUFFIXES: .c .o
mspdebug: main.o fet.o rf2500.o dis.o uif.o ihex.o elf32.o stab.o util.o \
bsl.o sim.o symmap.o gdb.o btree.o
bsl.o sim.o symmap.o gdb.o btree.o parse.o
$(CC) $(LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS)
.c.o:

239
dis.c
View File

@ -23,6 +23,7 @@
#include "dis.h"
#include "stab.h"
#include "util.h"
#include "parse.h"
/**********************************************************************/
/* Disassembler
@ -528,107 +529,211 @@ static const char *const msp430_reg_names[] = {
"R12", "R13", "R14", "R15"
};
/* Given an operands addressing mode, value and associated register,
* print the canonical representation of it to stdout.
*
* Returns the number of characters printed.
*/
static int format_operand(char *buf, int max_len,
msp430_amode_t amode, u_int16_t addr,
msp430_reg_t reg)
static int format_addr(msp430_amode_t amode, u_int16_t addr)
{
const char *prefix = "";
const char *suffix = "";
char rbuf[32];
char name[64];
u_int16_t offset;
int numeric = 0;
assert (reg >= 0 && reg < ARRAY_LEN(msp430_reg_names));
int len;
int count = 0;
const char *prefix = "";
switch (amode) {
case MSP430_AMODE_REGISTER:
return snprintf(buf, max_len, "%s", msp430_reg_names[reg]);
case MSP430_AMODE_INDEXED:
snprintf(rbuf, sizeof(rbuf), "(%s)", msp430_reg_names[reg]);
suffix = rbuf;
numeric = 1;
break;
case MSP430_AMODE_SYMBOLIC:
case MSP430_AMODE_REGISTER:
case MSP430_AMODE_INDIRECT:
case MSP430_AMODE_INDIRECT_INC:
return 0;
case MSP430_AMODE_IMMEDIATE:
prefix = "#";
case MSP430_AMODE_INDEXED:
numeric = 1;
break;
case MSP430_AMODE_ABSOLUTE:
prefix = "&";
break;
case MSP430_AMODE_INDIRECT:
return snprintf(buf, max_len, "@%s", msp430_reg_names[reg]);
case MSP430_AMODE_INDIRECT_INC:
return snprintf(buf, max_len, "@%s+", msp430_reg_names[reg]);
case MSP430_AMODE_IMMEDIATE:
prefix = "#";
numeric = 1;
break;
default:
return snprintf(buf, max_len, "???");
}
len = printf("%s", prefix);
if (len >= 0)
count += len;
if ((!numeric ||
(addr >= 0x200 && addr < 0xfff0)) &&
!stab_nearest(addr, name, sizeof(name), &offset) &&
!offset)
return snprintf(buf, max_len, "%s%s%s",
prefix, name, suffix);
!offset) {
colorize("1m");
len = printf("%s", name);
colorize("0m");
} else {
colorize("32m");
len = printf(numeric ? "0x%x" : "0x%04x", addr);
colorize("0m");
}
return snprintf(buf, max_len,
numeric ? "%s0x%x%s" : "%s0x%04x%s",
prefix, addr, suffix);
if (len >= 0)
count += len;
return count;
}
static int format_reg(msp430_amode_t amode, msp430_reg_t reg)
{
const char *prefix = "";
const char *suffix = "";
int len;
int count = 0;
switch (amode) {
case MSP430_AMODE_REGISTER:
break;
case MSP430_AMODE_INDEXED:
prefix = "(";
suffix = ")";
break;
case MSP430_AMODE_IMMEDIATE:
case MSP430_AMODE_SYMBOLIC:
case MSP430_AMODE_ABSOLUTE:
return 0;
case MSP430_AMODE_INDIRECT_INC:
suffix = "+";
case MSP430_AMODE_INDIRECT:
prefix = "@";
break;
}
assert (reg >= 0 && reg < ARRAY_LEN(msp430_reg_names));
len = printf("%s", prefix);
if (len >= 0)
count += len;
colorize("33m");
len = printf("%s", msp430_reg_names[reg]);
colorize("0m");
if (len >= 0)
count += len;
len = printf("%s", suffix);
if (len >= 0)
count += len;
return count;
}
/* Given an operands addressing mode, value and associated register,
* print the canonical representation of it to stdout.
*
* Returns the number of characters printed.
*/
static int format_operand(msp430_amode_t amode, u_int16_t addr,
msp430_reg_t reg)
{
int len;
int count = 0;
len = format_addr(amode, addr);
if (len >= 0)
count += len;
len = format_reg(amode, reg);
if (len >= 0)
count += len;
return count;
}
/* Write assembly language for the instruction to this buffer */
int dis_format(char *buf, int max_len,
const struct msp430_instruction *insn)
static void dis_format(const struct msp430_instruction *insn)
{
int count = 0;
int len;
/* Opcode mnemonic */
count = snprintf(buf, max_len, "%s", msp_op_name(insn->op));
if (insn->is_byte_op)
count += snprintf(buf + count, max_len - count, ".B");
while (count < 8 && count + 1 < max_len)
buf[count++] = ' ';
colorize("36m");
len = printf("%s%s", msp_op_name(insn->op),
insn->is_byte_op ? ".B" : "");
colorize("0m");
if (len >= 0)
count += len;
while (count < 8) {
count++;
printf(" ");
}
/* Source operand */
if (insn->itype == MSP430_ITYPE_DOUBLE) {
count += format_operand(buf + count,
max_len - count,
insn->src_mode,
len = format_operand(insn->src_mode,
insn->src_addr,
insn->src_reg);
if (len >= 0)
count += len;
if (count + 1 < max_len)
buf[count++] = ',';
while (count < 19 && count + 1 < max_len)
buf[count++] = ' ';
if (count + 1 < max_len)
buf[count++] = ' ';
printf(",");
count++;
while (count < 19) {
count++;
printf(" ");
}
printf(" ");
count++;
}
/* Destination operand */
if (insn->itype != MSP430_ITYPE_NOARG)
count += format_operand(buf + count,
max_len - count,
insn->dst_mode,
format_operand(insn->dst_mode,
insn->dst_addr,
insn->dst_reg);
buf[count] = 0;
return count;
}
void disassemble(u_int16_t offset, u_int8_t *data, int length)
{
int first_line = 1;
while (length) {
struct msp430_instruction insn;
int retval;
int count;
int i;
u_int16_t oboff;
char obname[64];
if (!stab_nearest(offset, obname, sizeof(obname), &oboff)) {
colorize("1m");
if (!oboff)
printf("%s:\n", obname);
else if (first_line)
printf("%s+0x%x:\n", obname, oboff);
colorize("0m");
}
first_line = 0;
retval = dis_decode(data, offset, length, &insn);
count = retval > 0 ? retval : 2;
if (count > length)
count = length;
colorize("36m");
printf(" %04x:", offset);
colorize("0m");
for (i = 0; i < count; i++)
printf(" %02x", data[i]);
while (i < 7) {
printf(" ");
i++;
}
if (retval >= 0)
dis_format(&insn);
printf("\n");
offset += count;
length -= count;
data += count;
}
}

5
dis.h
View File

@ -205,8 +205,7 @@ struct msp430_instruction {
int dis_decode(u_int8_t *code, u_int16_t offset, u_int16_t len,
struct msp430_instruction *insn);
/* Write assembly language for the instruction to this buffer */
int dis_format(char *buf, int max_len,
const struct msp430_instruction *insn);
/* Print a disassembly on stdout */
void disassemble(u_int16_t offset, u_int8_t *buf, int length);
#endif

290
main.c
View File

@ -23,17 +23,13 @@
#include <errno.h>
#include <unistd.h>
#ifdef USE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
#include "dis.h"
#include "device.h"
#include "binfile.h"
#include "stab.h"
#include "util.h"
#include "gdb.h"
#include "parse.h"
static const struct device *msp430_dev;
@ -42,13 +38,12 @@ static const struct device *msp430_dev;
*/
static int syms_are_modified;
static int is_interactive;
static int syms_modify_check(void)
static int syms_modify_check(voiv)
{
char buf[32];
if (!syms_are_modified || !is_interactive)
if (!syms_are_modified)
return 0;
for (;;) {
@ -72,80 +67,6 @@ static int syms_modify_check(void)
return 0;
}
/**********************************************************************
* Command-line interface
*/
char *get_arg(char **text)
{
char *start;
char *end;
if (!text)
return NULL;
start = *text;
while (*start && isspace(*start))
start++;
if (!*start)
return NULL;
end = start;
while (*end && !isspace(*end))
end++;
if (*end)
while (*end && isspace(*end))
*(end++) = 0;
*text = end;
return start;
}
struct command {
const char *name;
int (*func)(char **arg);
const char *help;
};
static const struct command all_commands[];
const struct command *find_command(const char *name)
{
int i;
for (i = 0; all_commands[i].name; i++)
if (!strcasecmp(name, all_commands[i].name))
return &all_commands[i];
return NULL;
}
static int process_command(char *arg)
{
const char *cmd_text;
int len = strlen(arg);
while (len && isspace(arg[len - 1]))
len--;
arg[len] = 0;
cmd_text = get_arg(&arg);
if (cmd_text) {
const struct command *cmd = find_command(cmd_text);
if (cmd)
return cmd->func(&arg);
fprintf(stderr, "unknown command: %s (try \"help\")\n",
cmd_text);
return -1;
}
return 0;
}
/************************************************************************
* Command definitions
*/
@ -183,13 +104,13 @@ static int cmd_md(char **arg)
return -1;
}
if (stab_parse(off_text, &offset) < 0) {
if (addr_exp(off_text, &offset) < 0) {
fprintf(stderr, "md: can't parse offset: %s\n", off_text);
return -1;
}
if (len_text) {
if (stab_parse(len_text, &length) < 0) {
if (addr_exp(len_text, &length) < 0) {
fprintf(stderr, "md: can't parse length: %s\n",
len_text);
return -1;
@ -231,7 +152,7 @@ static int cmd_mw(char **arg)
return -1;
}
if (stab_parse(off_text, &offset) < 0) {
if (addr_exp(off_text, &offset) < 0) {
fprintf(stderr, "md: can't parse offset: %s\n", off_text);
return -1;
}
@ -259,55 +180,6 @@ static int cmd_mw(char **arg)
return 0;
}
static void disassemble(u_int16_t offset, u_int8_t *data, int length)
{
int first_line = 1;
while (length) {
struct msp430_instruction insn;
int retval;
int count;
int i;
u_int16_t oboff;
char obname[64];
if (!stab_nearest(offset, obname, sizeof(obname), &oboff)) {
if (!oboff)
printf("%s:\n", obname);
else if (first_line)
printf("%s+0x%x:\n", obname, oboff);
}
first_line = 0;
retval = dis_decode(data, offset, length, &insn);
count = retval > 0 ? retval : 2;
if (count > length)
count = length;
printf(" %04x:", offset);
for (i = 0; i < count; i++)
printf(" %02x", data[i]);
while (i < 7) {
printf(" ");
i++;
}
if (retval >= 0) {
char buf[128];
dis_format(buf, sizeof(buf), &insn);
printf("%s", buf);
}
printf("\n");
offset += count;
length -= count;
data += count;
}
}
static int cmd_dis(char **arg)
{
char *off_text = get_arg(arg);
@ -321,13 +193,13 @@ static int cmd_dis(char **arg)
return -1;
}
if (stab_parse(off_text, &offset) < 0) {
if (addr_exp(off_text, &offset) < 0) {
fprintf(stderr, "dis: can't parse offset: %s\n", off_text);
return -1;
}
if (len_text) {
if (stab_parse(len_text, &length) < 0) {
if (addr_exp(len_text, &length) < 0) {
fprintf(stderr, "dis: can't parse length: %s\n",
len_text);
return -1;
@ -437,8 +309,8 @@ static int cmd_hexout(char **arg)
return -1;
}
if (stab_parse(off_text, &off) < 0 ||
stab_parse(len_text, &length) < 0)
if (addr_exp(off_text, &off) < 0 ||
addr_exp(len_text, &length) < 0)
return -1;
if (hexout_start(filename) < 0)
@ -508,7 +380,7 @@ static int cmd_run(char **arg)
device_status_t status;
if (bp_text) {
if (stab_parse(bp_text, &bp_addr) < 0) {
if (addr_exp(bp_text, &bp_addr) < 0) {
fprintf(stderr, "run: can't parse breakpoint: %s\n",
bp_text);
return -1;
@ -554,7 +426,7 @@ static int cmd_set(char **arg)
reg_text++;
reg = atoi(reg_text);
if (stab_parse(val_text, &value) < 0) {
if (addr_exp(val_text, &value) < 0) {
fprintf(stderr, "set: can't parse value: %s\n", val_text);
return -1;
}
@ -676,7 +548,7 @@ static int cmd_prog(char **arg)
FILE *in;
int result = 0;
if (syms_modify_check())
if (is_interactive() && syms_modify_check())
return 0;
in = fopen(*arg, "r");
@ -722,7 +594,7 @@ static int cmd_eval(char **arg)
u_int16_t offset;
char name[64];
if (stab_parse(*arg, &addr) < 0) {
if (addr_exp(*arg, &addr) < 0) {
fprintf(stderr, "=: can't parse: %s\n", *arg);
return -1;
}
@ -743,7 +615,7 @@ static int cmd_sym_load_add(int clear, char **arg)
FILE *in;
int result = 0;
if (clear && syms_modify_check())
if (clear && is_interactive() && syms_modify_check())
return 0;
in = fopen(*arg, "r");
@ -827,7 +699,7 @@ static int cmd_sym(char **arg)
}
if (!strcasecmp(subcmd, "clear")) {
if (syms_modify_check())
if (is_interactive() && syms_modify_check())
return 0;
stab_clear();
syms_are_modified = 0;
@ -845,7 +717,7 @@ static int cmd_sym(char **arg)
return -1;
}
if (stab_parse(val_text, &value) < 0) {
if (addr_exp(val_text, &value) < 0) {
fprintf(stderr, "sym: can't parse value: %s\n",
val_text);
return -1;
@ -913,70 +785,11 @@ static int cmd_gdb(char **arg)
return gdb_server(msp430_dev, port);
}
static int cmd_help(char **arg)
{
char *topic = get_arg(arg);
if (topic) {
const struct command *cmd = find_command(topic);
if (!cmd) {
fprintf(stderr, "help: unknown command: %s\n", topic);
return -1;
}
fputs(cmd->help, stdout);
} else {
int i;
int max_len = 0;
int rows, cols;
int total = 0;
for (i = 0; all_commands[i].name; i++) {
int len = strlen(all_commands[i].name);
if (len > max_len)
max_len = len;
total++;
}
max_len += 2;
cols = 72 / max_len;
rows = (total + cols - 1) / cols;
printf("Available commands:\n");
for (i = 0; i < rows; i++) {
int j;
printf(" ");
for (j = 0; j < cols; j++) {
int k = j * rows + i;
const struct command *cmd = &all_commands[k];
if (k >= total)
break;
printf("%s", cmd->name);
for (k = strlen(cmd->name); k < max_len; k++)
printf(" ");
}
printf("\n");
}
printf("Type \"help <command>\" for more information.\n");
printf("Press Ctrl+D to quit.\n");
}
return 0;
}
static int cmd_read(char **arg)
{
char *filename = get_arg(arg);
FILE *in;
char buf[1024];
int was_interactive = is_interactive;
if (!filename) {
fprintf(stderr, "read: filename must be specified\n");
@ -990,7 +803,6 @@ static int cmd_read(char **arg)
return -1;
}
is_interactive = 0;
while (fgets(buf, sizeof(buf), in)) {
char *cmd = buf;
@ -1000,21 +812,19 @@ static int cmd_read(char **arg)
if (*cmd == '#')
continue;
if (process_command(cmd) < 0) {
if (process_command(cmd, 0) < 0) {
fprintf(stderr, "read: error processing %s\n",
filename);
fclose(in);
is_interactive = was_interactive;
return -1;
}
}
is_interactive = was_interactive;
fclose(in);
return 0;
}
static const struct command all_commands[] = {
const struct command all_commands[] = {
{"=", cmd_eval,
"= <expression>\n"
" Evaluate an expression using the symbol table.\n"},
@ -1042,6 +852,10 @@ static const struct command all_commands[] = {
"mw <address> bytes ...\n"
" Write a sequence of bytes to a memory address. Byte values are\n"
" two-digit hexadecimal numbers.\n"},
{"opt", cmd_opt,
"opt [name] [value]\n"
" Query or set option variables. With no arguments, displays all\n"
" available options.\n"},
{"prog", cmd_prog,
"prog <filename>\n"
" Erase the device and flash the data contained in a binary file.\n"
@ -1111,59 +925,6 @@ static void usage(const char *progname)
progname, progname, progname, progname);
}
#ifndef USE_READLINE
#define LINE_BUF_SIZE 128
static char *readline(const char *prompt)
{
char *buf = malloc(LINE_BUF_SIZE);
if (!buf) {
perror("readline: can't allocate memory");
return NULL;
}
for (;;) {
printf("(mspdebug) ");
fflush(stdout);
if (fgets(buf, LINE_BUF_SIZE, stdin))
return buf;
if (feof(stdin))
break;
printf("\n");
}
free(buf);
return NULL;
}
#define add_history(x)
#endif
static void reader_loop(void)
{
printf("\n");
cmd_help(NULL);
is_interactive = 1;
for (;;) {
char *buf = readline("(mspdebug) ");
if (buf) {
add_history(buf);
process_command(buf);
free(buf);
} else if (!syms_modify_check()) {
break;
}
}
printf("\n");
}
#define MODE_RF2500 0x01
#define MODE_UIF 0x02
#define MODE_UIF_BSL 0x04
@ -1238,6 +999,7 @@ int main(int argc, char **argv)
return -1;
}
parse_init();
ctrlc_init();
if (stab_init() < 0)
return -1;
@ -1272,9 +1034,11 @@ int main(int argc, char **argv)
/* Process commands */
if (optind < argc) {
while (optind < argc)
process_command(argv[optind++]);
process_command(argv[optind++], 0);
} else {
do {
reader_loop();
} while (syms_modify_check());
}
msp430_dev->close();

View File

@ -79,7 +79,7 @@ table formats are still usable in this mode).
MSPDebug can accept commands either through an interactive prompt, or
non-interactively when specified on the command line. The supported
commands are listed below.
.IP "= \fIexpression\fR"
.IP "\fB=\fR \fIexpression\fR"
Evaluate an address expression and show both its value, and the result
when the value is looked up in reverse in the current symbol
table. This result is of the form \fIsymbol\fR+\fIoffset\fR, where
@ -88,28 +88,28 @@ question.
See the section marked \fBADDRESS EXPRESSIONS\fR for more information on
the syntax of expressions.
.IP "dis \fIaddress\fR [\fIlength\fR]"
.IP "\fBdis\fR \fIaddress\fR [\fIlength\fR]"
Dissassemble a section of memory. Both arguments may be address
expressions. If no length is specified, a section of the default
length (64 bytes) is disassembled and shown.
If symbols are available, then all addresses used as operands are
translated into \fIsymbol\fR+\fIoffset\fR form.
.IP "erase"
.IP "\fBerase\fR"
Erase the device under test. All code memory is erased (but not
information or boot memory).
.IP "gdb [\fIport\fR]"
.IP "\fBgdb\fR [\fIport\fR]"
Start a GDB remote stub, optionally specifying a TCP port to listen on.
If no port is given, the default port is 2000.
MSPDebug will wait for a connection on this port, and then act as a
GDB remote stub until GDB disconnects. The basic GDB protocol is
supported, plus the monitor commands "erase" and "reset".
.IP "help [\fIcommand\fR]"
.IP "\fBhelp\fR [\fIcommand\fR]"
Show a brief listing of available commands. If an argument is
specified, show the syntax for the given command. The help text shown
when no argument is given is also shown when MSPDebug starts up.
.IP "hexout \fIaddress\fR \fIlength\fR \fIfilename\fR"
.IP "\fBhexout\fR \fIaddress\fR \fIlength\fR \fIfilename\fR"
Read the specified section of the device memory and save it to an
Intel HEX file. The address and length arguments may both be address
expressions.
@ -118,7 +118,7 @@ If the specified file already exists, then it will be overwritten. If
you need to dump memory from several disjoint memory regions, you can
do this by saving each section to a separate file. The resulting files
can then be concatenated together to form a single valid HEX file.
.IP "md \fIaddress\fR [\fIlength\fR]"
.IP "\fBmd\fR \fIaddress\fR [\fIlength\fR]"
Read the specified section of device memory and display it as a
canonical\-style hexdump. Both arguments may be address expressions. If
no length is specified, a section of the default length (64 bytes) is
@ -128,14 +128,24 @@ The output is split into three columns. The first column shows the
starting address for the line. The second column lists the hexadecimal
values of the bytes. The final column shows the ASCII characters
corresponding to printable bytes, and . for non-printing characters.
.IP "mw \fIaddress\fR \fIbytes\fR ..."
.IP "\fBmw\fR \fIaddress\fR \fIbytes\fR ..."
Write a sequence of bytes at the given memory address. The address given
may be an address expression. Bytes values are two-digit hexadecimal
numbers separated by spaces.
Unless used in the simulation mode, this command can only be used for
programming flash memory.
.IP "prog \fIfilename\fR"
.IP "\fBopt\fR [\fIname\fR] [\fIvalue\fR]"
Query, set or list option variables. MSPDebug's behaviour can be configured
using option variables, described below in the section \fBOPTIONS\fR.
Option variables may be of three types: boolean, numeric or text. Numeric
values may be specified as address expressions.
With no arguments, this command displays all available option variables.
With just an option name as its argument, it displays the current value
of that option.
.IP "\fBprog\fR \fIfilename\fR"
Erase and reprogram the device under test using the binary file
supplied. The file format will be auto-detected and may be either
Intel HEX or ELF32.
@ -145,16 +155,16 @@ from the file into the symbol table (discarding any existing symbols),
if they are present.
The CPU is reset and halted before and after programming.
.IP "read \fIfilename\fR"
.IP "\fBread\fR \fIfilename\fR"
Read commands from the given file, line by line and process each one.
Any lines whose first non-space character is \fB#\fR are ignored. If
an error occurs while processing a command, the rest of the file is not
processed.
.IP "regs"
.IP "\fBregs\fR"
Show the current value of all CPU registers in the device under test.
.IP "reset"
.IP "\fBreset\fR"
Reset (and halt) the CPU of the device under test.
.IP "run [\fIbreakpoint\fR]"
.IP "\fBrun\fR [\fIbreakpoint\fR]"
Run the CPU, optionally specifying a breakpoint. The breakpoint can be
specified as an address expression.
@ -166,41 +176,41 @@ the user.
After the CPU halts, the current register values are shown as well as
a disassembly of the first few instructions at the address selected
by the program counter.
.IP "set \fIregister\fR \fIvalue\fR"
.IP "\fBset\fR \fIregister\fR \fIvalue\fR"
Alter the value of a register. Registers are specified as numbers from
0 through 15. Any leading non-numeric characters are ignored (so a
register may be specified as, for example, "R12"). The value argument
is an address expression.
.IP "step [\fIcount\fR]"
.IP "\fBstep\fR [\fIcount\fR]"
Step the CPU through one or more instructions. After stepping, the new
register values are displayed, as well as a disassembly of the
instructions at the address selected by the program counter.
An optional count can be specified to step multiple times. If no
argument is given, the CPU steps once.
.IP "sym clear"
.IP "\fBsym clear\fR"
Clear the symbol table, deleting all symbols.
.IP "sym set \fIname\fR \fIvalue\fR"
.IP "\fBsym set\fR \fIname\fR \fIvalue\fR"
Set or alter the value of a symbol. The value given may be an address
expression.
.IP "sym del \fIname\fR"
.IP "\fBsym del\fR \fIname\fR"
Delete the given symbol from the symbol table.
.IP "sym load \fIfilename\fR"
.IP "\fBsym load\fR \fIfilename\fR"
Load symbols from the specified file and add them to the symbol table.
The file format will be auto-detected and may be either ELF32 or a
BSD-style symbol listing (like the output from \fBnm\fR(1)).
Symbols can be combined from many sources, as the syms command adds
to the existing symbol table without discarding existing symbols.
.IP "sym add \fIfilename\fR"
.IP "\fBsym add\fR \fIfilename\fR"
This command is similar to \fBsym load\fR, except that the symbol table
is not cleared first. By using this command, symbols from multiple
sources can be combined.
.IP "sym savemap \fIfilename\fR"
.IP "\fBsym savemap\fR \fIfilename\fR"
Save all symbols currently defined to the given file. The symbols are
saved as a BSD-style symbol table. Note that symbol types are not stored
by MSPDebug, and all symbols are saved as type \fBt\fR.
.IP "sym find [\fIregex\fR]"
.IP "\fBsym find\fR [\fIregex\fR]"
Search for symbols. If a regular expression is given, then all symbols
matching the expression are printed. If no expression is specified, then
the entire symbol table is listed.
@ -224,6 +234,10 @@ The following are all valid examples of address expressions:
.B __bss_end-__bss_start
.SH SEE ALSO
\fBnm\fR(1), \fBgdb\fR(1), \fBobjcopy\fR(1)
.SH OPTIONS
MSPDebug's behaviour can be configured via the following variables:
.IP "\fBcolor\fR (boolean)"
If true, MSPDebug will colorize disassembly output.
.SH BUGS
If you find any bugs, you should report them to the author at
daniel@tortek.co.nz. It would help if you could include a transcript

428
parse.c Normal file
View File

@ -0,0 +1,428 @@
/* MSPDebug - debugging tool for MSP430 MCUs
* Copyright (C) 2009, 2010 Daniel Beer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifdef USE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
#include "stab.h"
#include "parse.h"
static struct option *option_list;
void register_option(struct option *o)
{
o->next = option_list;
option_list = o;
}
static struct option *find_option(const char *name)
{
struct option *o;
for (o = option_list; o; o = o->next)
if (!strcasecmp(o->name, name))
return o;
return NULL;
}
static int interactive_call;
int is_interactive(void)
{
return interactive_call;
}
char *get_arg(char **text)
{
char *start;
char *end;
if (!text)
return NULL;
start = *text;
while (*start && isspace(*start))
start++;
if (!*start)
return NULL;
end = start;
while (*end && !isspace(*end))
end++;
if (*end)
while (*end && isspace(*end))
*(end++) = 0;
*text = end;
return start;
}
const struct command *find_command(const char *name)
{
int i;
for (i = 0; all_commands[i].name; i++)
if (!strcasecmp(name, all_commands[i].name))
return &all_commands[i];
return NULL;
}
int process_command(char *arg, int interactive)
{
const char *cmd_text;
int len = strlen(arg);
while (len && isspace(arg[len - 1]))
len--;
arg[len] = 0;
cmd_text = get_arg(&arg);
if (cmd_text) {
const struct command *cmd = find_command(cmd_text);
if (cmd) {
int old = interactive_call;
int ret;
interactive_call = interactive;
ret = cmd->func(&arg);
interactive_call = old;
return 0;
}
fprintf(stderr, "unknown command: %s (try \"help\")\n",
cmd_text);
return -1;
}
return 0;
}
#ifndef USE_READLINE
#define LINE_BUF_SIZE 128
static char *readline(const char *prompt)
{
char *buf = malloc(LINE_BUF_SIZE);
if (!buf) {
perror("readline: can't allocate memory");
return NULL;
}
for (;;) {
printf("(mspdebug) ");
fflush(stdout);
if (fgets(buf, LINE_BUF_SIZE, stdin))
return buf;
if (feof(stdin))
break;
printf("\n");
}
free(buf);
return NULL;
}
#define add_history(x)
#endif
void reader_loop(void)
{
printf("\n");
cmd_help(NULL);
for (;;) {
char *buf = readline("(mspdebug) ");
if (!buf)
break;
add_history(buf);
process_command(buf, 1);
free(buf);
}
printf("\n");
}
const char *type_text(option_type_t type)
{
switch (type) {
case OPTION_BOOLEAN:
return "boolean";
case OPTION_NUMERIC:
return "numeric";
case OPTION_TEXT:
return "text";
}
return "unknown";
}
int cmd_help(char **arg)
{
char *topic = get_arg(arg);
if (topic) {
const struct command *cmd = find_command(topic);
const struct option *opt = find_option(topic);
if (cmd) {
printf("COMMAND: %s\n", cmd->name);
fputs(cmd->help, stdout);
if (opt)
printf("\n");
}
if (opt) {
printf("OPTION: %s (%s)\n", opt->name,
type_text(opt->type));
fputs(opt->help, stdout);
}
if (!(cmd || opt)) {
fprintf(stderr, "help: unknown command: %s\n", topic);
return -1;
}
} else {
int i;
int max_len = 0;
int rows, cols;
int total = 0;
for (i = 0; all_commands[i].name; i++) {
int len = strlen(all_commands[i].name);
if (len > max_len)
max_len = len;
total++;
}
max_len += 2;
cols = 72 / max_len;
rows = (total + cols - 1) / cols;
printf("Available commands:\n");
for (i = 0; i < rows; i++) {
int j;
printf(" ");
for (j = 0; j < cols; j++) {
int k = j * rows + i;
const struct command *cmd = &all_commands[k];
if (k >= total)
break;
printf("%s", cmd->name);
for (k = strlen(cmd->name); k < max_len; k++)
printf(" ");
}
printf("\n");
}
printf("Type \"help <command>\" for more information.\n");
printf("Press Ctrl+D to quit.\n");
}
return 0;
}
static char token_buf[64];
static int token_len;
static int token_mult;
static int token_sum;
static int token_add(void)
{
int i;
u_int16_t value;
if (!token_len)
return 0;
token_buf[token_len] = 0;
token_len = 0;
/* Is it a decimal? */
i = 0;
while (token_buf[i] && isdigit(token_buf[i]))
i++;
if (!token_buf[i]) {
token_sum += token_mult * atoi(token_buf);
return 0;
}
/* Is it hex? */
if (token_buf[0] == '0' && tolower(token_buf[1]) == 'x') {
token_sum += token_mult * strtol(token_buf + 2, NULL, 16);
return 0;
}
/* Look up the name in the symbol table */
if (!stab_get(token_buf, &value)) {
token_sum += token_mult * value;
return 0;
}
fprintf(stderr, "unknown token: %s\n", token_buf);
return -1;
}
int addr_exp(const char *text, int *addr)
{
token_len = 0;
token_mult = 1;
token_sum = 0;
while (*text) {
if (isalnum(*text) || *text == '_' || *text == '$' ||
*text == '.' || *text == ':') {
if (token_len + 1 < sizeof(token_buf))
token_buf[token_len++] = *text;
} else {
if (token_add() < 0)
return -1;
if (*text == '+')
token_mult = 1;
if (*text == '-')
token_mult = -1;
}
text++;
}
if (token_add() < 0)
return -1;
*addr = token_sum & 0xffff;
return 0;
}
static void display_option(const struct option *o)
{
printf("%32s = ", o->name);
switch (o->type) {
case OPTION_BOOLEAN:
printf("%s", o->data.numeric ? "true" : "false");
break;
case OPTION_NUMERIC:
printf("0x%x (%d)", o->data.numeric,
o->data.numeric);
break;
case OPTION_TEXT:
printf("%s", o->data.text);
break;
}
printf("\n");
}
static int parse_option(struct option *o, const char *word)
{
switch (o->type) {
case OPTION_BOOLEAN:
o->data.numeric = (isdigit(word[0]) && word[0] > '0') ||
word[0] == 't' || word[0] == 'y' ||
(word[0] == 'o' && word[1] == 'n');
break;
case OPTION_NUMERIC:
return addr_exp(word, &o->data.numeric);
case OPTION_TEXT:
strncpy(o->data.text, word, sizeof(o->data.text));
o->data.text[sizeof(o->data.text) - 1] = 0;
break;
}
return 0;
}
int cmd_opt(char **arg)
{
const char *opt_text = get_arg(arg);
struct option *opt = NULL;
if (opt_text) {
opt = find_option(opt_text);
if (!opt) {
fprintf(stderr, "opt: no such option: %s\n",
opt_text);
return -1;
}
}
if (**arg) {
if (parse_option(opt, *arg) < 0) {
fprintf(stderr, "opt: can't parse option: %s\n",
*arg);
return -1;
}
} else if (opt_text) {
display_option(opt);
} else {
struct option *o;
for (o = option_list; o; o = o->next)
display_option(o);
}
return 0;
}
static struct option option_color = {
.name = "color",
.type = OPTION_BOOLEAN,
.help = "Colorize disassembly output.\n"
};
int colorize(const char *text)
{
if (!option_color.data.numeric)
return 0;
return printf("\x1b[%s", text);
}
void parse_init(void)
{
register_option(&option_color);
}

109
parse.h Normal file
View File

@ -0,0 +1,109 @@
/* MSPDebug - debugging tool for MSP430 MCUs
* Copyright (C) 2009, 2010 Daniel Beer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef PARSE_H_
#define PARSE_H_
typedef int (*command_func_t)(char **arg);
struct command {
const char *name;
int (*func)(char **arg);
const char *help;
};
/* The global command table, defined in main.c */
extern const struct command all_commands[];
/* Retrieve the next word from a pointer to the rest of a command
* argument buffer. Returns NULL if no more words.
*/
char *get_arg(char **text);
/* Process a command, returning -1 on error or 0 if executed
* successfully.
*
* The interactive argument specifies whether or not the command
* should be executed in an interactive context.
*/
int process_command(char *arg, int interactive);
/* Run the reader loop, exiting when the user presses Ctrl+D.
*
* Commands executed by the reader loop are executed in interactive
* context.
*/
void reader_loop(void);
/* Help command. Displays a command list with no argument, or help
* for a particular command.
*/
int cmd_help(char **arg);
/* Colourized output has been requested by the user. */
int colorize(const char *text);
/* Return non-zero if executing in an interactive context. */
int is_interactive(void);
/* Parse an address expression, storing the result in the integer
* pointed to. Returns 0 if parsed successfully, -1 if not.
*/
int addr_exp(const char *text, int *value);
/* Options interface. Options may be declared by any module and
* registered with the parser.
*
* They can then be manipulated by the "set" command (function
* declared below.
*/
typedef enum {
OPTION_BOOLEAN,
OPTION_NUMERIC,
OPTION_TEXT
} option_type_t;
struct option {
const char *name;
option_type_t type;
const char *help;
union {
char text[128];
int numeric;
} data;
struct option *next;
};
/* Add an option to the parser's list. Options can't be removed from
* the list once added.
*/
void register_option(struct option *o);
/* Command function for settings options. With no arguments, displays
* a list of available options.
*/
int cmd_opt(char **arg);
/* Initialise the parser, and register built-ins. */
void parse_init(void);
#endif

3
sim.c
View File

@ -24,6 +24,7 @@
#include "dis.h"
#include "util.h"
#include "stab.h"
#include "parse.h"
#define MEM_SIZE 65536
@ -88,7 +89,7 @@ static int fetch_io(u_int16_t addr, int is_byte, u_int32_t *data_ret)
len--;
text[len] = 0;
if (!stab_parse(text, &data)) {
if (!addr_exp(text, &data)) {
if (data_ret)
*data_ret = data;
return 0;

77
stab.c
View File

@ -166,10 +166,8 @@ int stab_get(const char *name, u_int16_t *value)
struct sym_key skey;
sym_key_init(&skey, name);
if (btree_get(sym_table, &skey, value)) {
fprintf(stderr, "stab: can't find symbol: %s\n", name);
if (btree_get(sym_table, &skey, value))
return -1;
}
return 0;
}
@ -243,79 +241,6 @@ int stab_re_search(const char *regex, stab_callback_t cb)
return count;
}
static char token_buf[64];
static int token_len;
static int token_mult;
static int token_sum;
static int token_add(void)
{
int i;
struct sym_key skey;
u_int16_t value;
if (!token_len)
return 0;
token_buf[token_len] = 0;
token_len = 0;
/* Is it a decimal? */
i = 0;
while (token_buf[i] && isdigit(token_buf[i]))
i++;
if (!token_buf[i]) {
token_sum += token_mult * atoi(token_buf);
return 0;
}
/* Is it hex? */
if (token_buf[0] == '0' && tolower(token_buf[1]) == 'x') {
token_sum += token_mult * strtol(token_buf + 2, NULL, 16);
return 0;
}
/* Look up the name in the symbol table */
sym_key_init(&skey, token_buf);
if (!btree_get(sym_table, &skey, &value)) {
token_sum += token_mult * (int)value;
return 0;
}
fprintf(stderr, "stab: unknown token: %s\n", token_buf);
return -1;
}
int stab_parse(const char *text, int *addr)
{
token_len = 0;
token_mult = 1;
token_sum = 0;
while (*text) {
if (isalnum(*text) || *text == '_' || *text == '$' ||
*text == '.' || *text == ':') {
if (token_len + 1 < sizeof(token_buf))
token_buf[token_len++] = *text;
} else {
if (token_add() < 0)
return -1;
if (*text == '+')
token_mult = 1;
if (*text == '-')
token_mult = -1;
}
text++;
}
if (token_add() < 0)
return -1;
*addr = token_sum & 0xffff;
return 0;
}
int stab_nearest(u_int16_t addr, char *ret_name, int max_len,
u_int16_t *ret_offset)
{