Command processor is now an object.

This commit is contained in:
Daniel Beer 2010-04-21 16:56:56 +12:00
parent 90d39a96f5
commit ba6e3366ff
23 changed files with 1356 additions and 1131 deletions

View File

@ -41,7 +41,7 @@ install: mspdebug mspdebug.man
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 device.o rtools.o sym.o devcmd.o \
parse.o vector.o
cproc.o vector.o cproc_util.o
$(CC) $(LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS)
.c.o:

604
cproc.c Normal file
View File

@ -0,0 +1,604 @@
/* 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 <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifdef USE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
#include "cproc.h"
#include "vector.h"
#include "stab.h"
#include "util.h"
struct cproc {
struct vector command_list;
struct vector option_list;
int lists_modified;
int modify_flags;
int in_reader_loop;
};
static struct cproc_option *find_option(cproc_t cp, const char *name)
{
int i;
for (i = 0; i < cp->option_list.size; i++) {
struct cproc_option *opt =
VECTOR_PTR(cp->option_list, i, struct cproc_option);
if (!strcasecmp(opt->name, name))
return opt;
}
return NULL;
}
static struct cproc_command *find_command(cproc_t cp, const char *name)
{
int i;
for (i = 0; i < cp->command_list.size; i++) {
struct cproc_command *cmd =
VECTOR_PTR(cp->command_list, i, struct cproc_command);
if (!strcasecmp(cmd->name, name))
return cmd;
}
return NULL;
}
static int namelist_cmp(const void *a, const void *b)
{
return strcasecmp(*(const char **)a, *(const char **)b);
}
/* NOTE: Both sort_lists and namelist_print assume that the first item in each
* vector element is a const char *
*/
static void sort_lists(cproc_t cp)
{
if (!cp->lists_modified)
return;
if (cp->command_list.ptr)
qsort(cp->command_list.ptr, cp->command_list.size,
cp->command_list.elemsize, namelist_cmp);
if (cp->option_list.ptr)
qsort(cp->option_list.ptr, cp->option_list.size,
cp->option_list.elemsize, namelist_cmp);
cp->lists_modified = 0;
}
static void namelist_print(struct vector *v)
{
int i;
int max_len = 0;
int rows, cols;
for (i = 0; i < v->size; i++) {
const char *text = VECTOR_AT(*v, i, const char *);
int len = strlen(text);
if (len > max_len)
max_len = len;
}
max_len += 2;
cols = 72 / max_len;
rows = (v->size + cols - 1) / cols;
for (i = 0; i < rows; i++) {
int j;
printf(" ");
for (j = 0; j < cols; j++) {
int k = j * rows + i;
const char *text;
if (k >= v->size)
break;
text = VECTOR_AT(*v, k, const char *);
printf("%s", text);
for (k = strlen(text); k < max_len; k++)
printf(" ");
}
printf("\n");
}
}
static const char *type_text(cproc_option_type_t type)
{
switch (type) {
case CPROC_OPTION_BOOL:
return "boolean";
case CPROC_OPTION_NUMERIC:
return "numeric";
case CPROC_OPTION_STRING:
return "text";
}
return "unknown";
}
static int cmd_help(cproc_t cp, char **arg)
{
const char *topic = get_arg(arg);
if (topic) {
const struct cproc_command *cmd = find_command(cp, topic);
const struct cproc_option *opt = find_option(cp, topic);
if (cmd) {
cproc_printf(cp, "\x1b[1mCOMMAND: %s\x1b[0m\n",
cmd->name);
fputs(cmd->help, stdout);
if (opt)
printf("\n");
}
if (opt) {
cproc_printf(cp, "\x1b[1mOPTION: %s (%s)\x1b[0m\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 {
sort_lists(cp);
printf("Available commands:\n");
namelist_print(&cp->command_list);
printf("\n");
printf("Available options:\n");
namelist_print(&cp->option_list);
printf("\n");
printf("Type \"help <topic>\" for more information.\n");
printf("Press Ctrl+D to quit.\n");
}
return 0;
}
static int parse_option(struct cproc_option *o, const char *word)
{
switch (o->type) {
case CPROC_OPTION_BOOL:
o->data.numeric = (isdigit(word[0]) && word[0] > '0') ||
word[0] == 't' || word[0] == 'y' ||
(word[0] == 'o' && word[1] == 'n');
break;
case CPROC_OPTION_NUMERIC:
return stab_exp(word, &o->data.numeric);
case CPROC_OPTION_STRING:
strncpy(o->data.text, word, sizeof(o->data.text));
o->data.text[sizeof(o->data.text) - 1] = 0;
break;
}
return 0;
}
static void display_option(const struct cproc_option *o)
{
printf("%32s = ", o->name);
switch (o->type) {
case CPROC_OPTION_BOOL:
printf("%s", o->data.numeric ? "true" : "false");
break;
case CPROC_OPTION_NUMERIC:
printf("0x%x (%d)", o->data.numeric,
o->data.numeric);
break;
case CPROC_OPTION_STRING:
printf("%s", o->data.text);
break;
}
printf("\n");
}
static int cmd_opt(cproc_t cp, char **arg)
{
const char *opt_text = get_arg(arg);
struct cproc_option *opt = NULL;
sort_lists(cp);
if (opt_text) {
opt = find_option(cp, 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 {
int i;
for (i = 0; i < cp->option_list.size; i++)
display_option(VECTOR_PTR(cp->option_list, i,
struct cproc_option));
}
return 0;
}
static int cmd_read(cproc_t cp, char **arg)
{
char *filename = get_arg(arg);
if (!filename) {
fprintf(stderr, "read: filename must be specified\n");
return -1;
}
return cproc_process_file(cp, filename);
}
static const struct cproc_command built_in_commands[] = {
{
.name = "help",
.func = cmd_help,
.help =
"help [command]\n"
" Without arguments, displays a list of commands. With a command\n"
" name as an argument, displays help for that command.\n"
},
{
.name = "opt",
.func = cmd_opt,
.help =
"opt [name] [value]\n"
" Query or set option variables. With no arguments, displays all\n"
" available options.\n"
},
{
.name = "read",
.func = cmd_read,
.help =
"read <filename>\n"
" Read commands from a file and evaluate them.\n"
}
};
static const struct cproc_option built_in_options[] = {
{
.name = "color",
.type = CPROC_OPTION_BOOL,
.help = "Colorize debugging output.\n"
}
};
cproc_t cproc_new(void)
{
cproc_t cp = malloc(sizeof(*cp));
if (!cp)
return NULL;
memset(cp, 0, sizeof(*cp));
vector_init(&cp->command_list, sizeof(struct cproc_command));
vector_init(&cp->option_list, sizeof(struct cproc_option));
if (vector_push(&cp->command_list, &built_in_commands,
ARRAY_LEN(built_in_commands)) < 0 ||
vector_push(&cp->option_list, &built_in_options,
ARRAY_LEN(built_in_options)) < 0) {
vector_destroy(&cp->command_list);
vector_destroy(&cp->option_list);
free(cp);
return NULL;
}
return cp;
}
void cproc_destroy(cproc_t cp)
{
vector_destroy(&cp->command_list);
vector_destroy(&cp->option_list);
free(cp);
}
int cproc_register_commands(cproc_t cp, const struct cproc_command *cmd,
int count)
{
if (vector_push(&cp->command_list, cmd, count) < 0)
return -1;
cp->lists_modified = 1;
return 0;
}
int cproc_register_options(cproc_t cp, const struct cproc_option *opt,
int count)
{
if (vector_push(&cp->option_list, opt, count) < 0)
return -1;
cp->lists_modified = 1;
return 0;
}
void cproc_printf(cproc_t cp, const char *fmt, ...)
{
char buf[256];
va_list ap;
int want_color = 0;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
cproc_get_int(cp, "color", &want_color);
if (!want_color) {
char *src = buf;
char *dst = buf;
for (;;) {
if (*src == 27) {
while (*src && !isalpha(*src))
src++;
if (*src)
src++;
}
if (!*src)
break;
*(dst++) = *(src++);
}
*dst = 0;
}
puts(buf);
}
void cproc_modify(cproc_t cp, int flags)
{
cp->modify_flags |= flags;
}
void cproc_unmodify(cproc_t cp, int flags)
{
cp->modify_flags &= ~flags;
}
int cproc_prompt_abort(cproc_t cp, int flags)
{
char buf[32];
if (!(cp->in_reader_loop && (cp->modify_flags & flags)))
return 0;
for (;;) {
printf("Symbols have not been saved since modification. "
"Continue (y/n)? ");
fflush(stdout);
if (!fgets(buf, sizeof(buf), stdin)) {
printf("\n");
return 1;
}
if (toupper(buf[0]) == 'Y')
return 0;
if (toupper(buf[0]) == 'N')
return 1;
printf("Please answer \"y\" or \"n\".\n");
}
return 0;
}
int cproc_get_int(cproc_t cp, const char *name, int *value)
{
struct cproc_option *opt = find_option(cp, name);
if (!opt)
return -1;
if (opt->type == CPROC_OPTION_NUMERIC ||
opt->type == CPROC_OPTION_BOOL) {
*value = opt->data.numeric;
return 0;
}
return -1;
}
int cproc_get_string(cproc_t cp, const char *name, char *value, int max_len)
{
struct cproc_option *opt = find_option(cp, name);
if (!opt)
return -1;
if (opt->type == CPROC_OPTION_STRING) {
strncpy(value, opt->data.text, max_len);
value[max_len - 1] = 0;
return 0;
}
return -1;
}
#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 int process_command(cproc_t cp, 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 cproc_command *cmd = find_command(cp, cmd_text);
if (cmd) {
int old = cp->in_reader_loop;
int ret;
cp->in_reader_loop = interactive;
ret = cmd->func(cp, &arg);
cp->in_reader_loop = old;
return ret;
}
fprintf(stderr, "unknown command: %s (try \"help\")\n",
cmd_text);
return -1;
}
return 0;
}
void cproc_reader_loop(cproc_t cp)
{
printf("\n");
cmd_help(cp, NULL);
printf("\n");
do {
for (;;) {
char *buf = readline("(mspdebug) ");
if (!buf)
break;
add_history(buf);
process_command(cp, buf, 1);
free(buf);
}
} while (cproc_prompt_abort(cp, CPROC_MODIFY_SYMS));
printf("\n");
}
int cproc_process_command(cproc_t cp, char *cmd)
{
return process_command(cp, cmd, 0);
}
int cproc_process_file(cproc_t cp, const char *filename)
{
FILE *in;
char buf[1024];
int line_no = 0;
in = fopen(filename, "r");
if (!in) {
fprintf(stderr, "read: can't open %s: %s\n",
filename, strerror(errno));
return -1;
}
while (fgets(buf, sizeof(buf), in)) {
char *cmd = buf;
line_no++;
while (*cmd && isspace(*cmd))
cmd++;
if (*cmd == '#')
continue;
if (process_command(cp, cmd, 0) < 0) {
fprintf(stderr, "read: error processing %s (line %d)\n",
filename, line_no);
fclose(in);
return -1;
}
}
fclose(in);
return 0;
}

129
cproc.h Normal file
View File

@ -0,0 +1,129 @@
/* 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 CPROC_H_
#define CPROC_H_
/* Command processor.
*
* This contains a list of all defined commands and options, plus modification
* flags.
*/
struct cproc;
typedef struct cproc *cproc_t;
/* Command definitions.
*
* Each command is specified as a tuple of name, command function and help
* text. When the command is invoked, the command function is called with
* a mutable pointer to the command line text, and a pointer to the command
* processor.
*/
typedef int (*cproc_command_func_t)(cproc_t cp, char **arg);
struct cproc_command {
const char *name;
cproc_command_func_t func;
const char *help;
};
/* Option definitions.
*
* Options come in three different types, and can be manipulated with the
* in-built "set" function.
*
* Default values may be specified by filling out the value portion in the
* option's definition.
*/
typedef enum {
CPROC_OPTION_STRING,
CPROC_OPTION_NUMERIC,
CPROC_OPTION_BOOL
} cproc_option_type_t;
struct cproc_option {
const char *name;
cproc_option_type_t type;
const char *help;
union {
char text[128];
int numeric;
} data;
};
/* Commmand processor modification flags.
*
* Within the context of a command processor, various data items may be
* marked as having been modified. These flags can be checked, and a prompt
* invoked to ask the user to confirm before proceeding with a destructive
* operation.
*
* The same prompting occurs when the user elects to quit the command
* processor.
*/
#define CPROC_MODIFY_SYMS 0x01
/* Create/destroy a command processor. The init function returns 0 if
* successful, or -1 if an error occurs.
*/
cproc_t cproc_new(void);
void cproc_destroy(cproc_t cp);
/* Register commands and options with the command processor. These functions
* return 0 on success or -1 if an error occurs (failure to allocate memory).
*/
int cproc_register_commands(cproc_t cp, const struct cproc_command *cmd,
int count);
int cproc_register_options(cproc_t cp, const struct cproc_option *opt,
int count);
/* Print a line of text on the command processor's standard output.
*
* ANSI colour codes can be embedded in the output text, and will be stripped
* out if colour is disabled.
*/
void cproc_printf(cproc_t cp, const char *fmt, ...);
/* This should be called before a destructive operation to give the user
* a chance to abort. If it returns 1, then the operation should be aborted.
*
* The flags argument should be a bitwise combination representing the bits
* modify_flags that will be affected by the operation.
*/
void cproc_modify(cproc_t cp, int flags);
void cproc_unmodify(cproc_t cp, int flags);
int cproc_prompt_abort(cproc_t cp, int flags);
/* Retrieve option values. These functions return 0 on success or -1 if
* an error occurs.
*/
int cproc_get_int(cproc_t cp, const char *opt, int *value);
int cproc_get_string(cproc_t cp, const char *opt, char *value, int max_len);
/* Run the reader loop */
void cproc_reader_loop(cproc_t cp);
/* Commands can be fed directly to the processor either one at a time,
* or by specifying a file to read from.
*/
int cproc_process_command(cproc_t cp, char *cmd);
int cproc_process_file(cproc_t cp, const char *filename);
#endif

302
cproc_util.c Normal file
View File

@ -0,0 +1,302 @@
/* 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 <assert.h>
#include "dis.h"
#include "cproc_util.h"
#include "stab.h"
#include "util.h"
static int format_addr(char *buf, int max_len,
msp430_amode_t amode, u_int16_t addr)
{
char name[64];
u_int16_t offset;
int numeric = 0;
const char *prefix = "";
switch (amode) {
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_SYMBOLIC:
break;
}
if ((!numeric ||
(addr >= 0x200 && addr < 0xfff0)) &&
!stab_nearest(addr, name, sizeof(name), &offset) &&
!offset)
return snprintf(buf, max_len,
"%s\x1b[1m%s\x1b[0m", prefix, name);
else if (numeric)
return snprintf(buf, max_len,
"%s\x1b[1m0x%x\x1b[0m", prefix, addr);
else
return snprintf(buf, max_len,
"%s\x1b[1m0x%04x\x1b[0m", prefix, addr);
}
static int format_reg(char *buf, int max_len,
msp430_amode_t amode, msp430_reg_t reg)
{
const char *prefix = "";
const char *suffix = "";
const char *name;
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;
}
name = dis_reg_name(reg);
if (!name)
name = "???";
return snprintf(buf, max_len,
"%s\x1b[33m%s\x1b[0m%s",
prefix, name, suffix);
}
/* 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)
{
int len = 0;
len += format_addr(buf, max_len, amode, addr);
len += format_reg(buf + len, max_len - len, amode, addr);
return len;
}
/* Write assembly language for the instruction to this buffer */
static int dis_format(char *buf, int max_len,
const struct msp430_instruction *insn)
{
int len;
int tlen;
int total = 0;
const char *opname = dis_opcode_name(insn->op);
if (!opname)
opname = "???";
len = snprintf(buf + total, max_len - total,
"\x1b[36m%s%s\x1b[0m", opname,
insn->is_byte_op ? ".B" : "");
tlen = textlen(buf + total);
total += len;
while (tlen < 8 && total < max_len) {
buf[total++] = ' ';
tlen++;
}
/* Source operand */
if (insn->itype == MSP430_ITYPE_DOUBLE) {
len = format_operand(buf + total,
max_len - total,
insn->src_mode,
insn->src_addr,
insn->src_reg);
tlen = textlen(buf + total);
total += len;
if (total < max_len)
buf[total++] = ',';
while (tlen < 15 && total < max_len) {
tlen++;
buf[total++] = ' ';
}
if (total < max_len)
buf[total++] = ' ';
}
/* Destination operand */
if (insn->itype != MSP430_ITYPE_NOARG)
total += format_operand(buf + total,
max_len - total,
insn->dst_mode,
insn->dst_addr,
insn->dst_reg);
if (total < max_len)
buf[total] = 0;
else if (total) {
total--;
buf[total] = 0;
}
return total;
}
void cproc_disassemble(cproc_t cp,
u_int16_t offset, const u_int8_t *data, int length)
{
int first_line = 1;
while (length) {
struct msp430_instruction insn = {0};
int retval;
int count;
int i;
u_int16_t oboff;
char obname[64];
char buf[256];
int len = 0;
if (!stab_nearest(offset, obname, sizeof(obname), &oboff)) {
if (!oboff)
cproc_printf(cp, "\x1b[m%s:\x1b[0m", obname);
else if (first_line)
cproc_printf(cp, "\x1b[m%s+0x%x:\x1b[0m",
obname, oboff);
}
first_line = 0;
retval = dis_decode(data, offset, length, &insn);
count = retval > 0 ? retval : 2;
if (count > length)
count = length;
len += snprintf(buf + len, sizeof(buf) - len,
" \x1b[36m%04x\x1b[0m:", offset);
for (i = 0; i < count; i++)
len += snprintf(buf + len, sizeof(buf) - len,
" %02x", data[i]);
while (i < 7) {
buf[len++] = ' ';
buf[len++] = ' ';
buf[len++] = ' ';
i++;
}
if (retval >= 0)
len += dis_format(buf + len, sizeof(buf) - len,
&insn);
cproc_printf(cp, "%s", buf);
offset += count;
length -= count;
data += count;
}
}
void cproc_hexdump(cproc_t cp, u_int16_t addr, const u_int8_t *data, int len)
{
int offset = 0;
while (offset < len) {
char buf[128];
int len = 0;
int i, j;
/* Address label */
len += snprintf(buf + len, sizeof(buf) - len,
" \x1b[36m%04x:\x1b[0m", offset + addr);
/* Hex portion */
for (i = 0; i < 16 && offset + i < len; i++)
len += snprintf(buf + len, sizeof(buf) - len,
" %02x", data[offset + i]);
for (j = i; j < 16; j++) {
buf[len++] = ' ';
buf[len++] = ' ';
buf[len++] = ' ';
}
/* Printable characters */
len += snprintf(buf + len, sizeof(buf) - len,
" \x1b[32m|");
for (j = 0; j < i; j++) {
int c = data[offset + j];
buf[len++] = (c >= 32 && c <= 126) ? c : '.';
}
for (; j < 16; j++)
buf[len++] = ' ';
len += snprintf(buf + len, sizeof(buf) - len,
"|\x1b[0m");
cproc_printf(cp, "%s", buf);
offset += i;
}
}
void cproc_regs(cproc_t cp, const u_int16_t *regs)
{
int i;
for (i = 0; i < 4; i++) {
int j;
char buf[128];
int len = 0;
for (j = 0; j < 4; j++)
buf[len++] = ' ';
for (j = 0; j < 4; j++) {
int k = j * 4 + i;
len += snprintf(buf + len, sizeof(buf) - len,
"(\x1b[1m%3s:\x1b[0m %04x) ",
dis_reg_name(k), regs[k]);
}
cproc_printf(cp, "%s", buf);
}
}

36
cproc_util.h Normal file
View File

@ -0,0 +1,36 @@
/* 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 CPROC_UTIL_H_
#define CPROC_UTIL_H_
#include <sys/types.h>
#include "cproc.h"
/* Print colorized disassembly on command processor standard output */
void cproc_disassemble(cproc_t cp, u_int16_t addr,
const u_int8_t *buf, int len);
/* Print colorized hexdump on standard output */
void cproc_hexdump(cproc_t cp, u_int16_t addr,
const u_int8_t *buf, int len);
/* Colorized register dump */
void cproc_regs(cproc_t cp, const u_int16_t *regs);
#endif

255
devcmd.c
View File

@ -25,62 +25,29 @@
#include "device.h"
#include "binfile.h"
#include "stab.h"
#include "parse.h"
#include "cproc.h"
#include "cproc_util.h"
#include "util.h"
#include "dis.h"
#define REG_COLUMNS 4
#define REG_ROWS ((DEVICE_NUM_REGS + REG_COLUMNS - 1) / REG_COLUMNS)
static void show_regs(u_int16_t *regs)
{
int i;
for (i = 0; i < REG_ROWS; i++) {
int j;
printf(" ");
for (j = 0; j < REG_COLUMNS; j++) {
int k = j * REG_ROWS + i;
if (k < DEVICE_NUM_REGS) {
printf("(");
colorize("1m");
printf("R%02d: ", k);
colorize("0m");
printf("%04x) ", regs[k]);
}
}
printf("\n");
}
}
static int cmd_regs(char **arg)
static int cmd_regs(cproc_t cp, char **arg)
{
u_int16_t regs[DEVICE_NUM_REGS];
u_int8_t code[16];
if (device_get()->getregs(regs) < 0)
return -1;
show_regs(regs);
cproc_regs(cp, regs);
/* Try to disassemble the instruction at PC */
if (device_get()->readmem(regs[0], code, sizeof(code)) < 0)
return 0;
disassemble(regs[0], (u_int8_t *)code, sizeof(code));
cproc_disassemble(cp, regs[0], (u_int8_t *)code, sizeof(code));
return 0;
}
static struct command command_regs = {
.name = "regs",
.func = cmd_regs,
.help =
"regs\n"
" Read and display the current register contents.\n"
};
static int cmd_md(char **arg)
static int cmd_md(cproc_t cp, char **arg)
{
char *off_text = get_arg(arg);
char *len_text = get_arg(arg);
@ -118,7 +85,7 @@ static int cmd_md(char **arg)
if (device_get()->readmem(offset, buf, blen) < 0)
return -1;
hexdump(offset, buf, blen);
cproc_hexdump(cp, offset, buf, blen);
offset += blen;
length -= blen;
@ -127,16 +94,7 @@ static int cmd_md(char **arg)
return 0;
}
static struct command command_md = {
.name = "md",
.func = cmd_md,
.help =
"md <address> [length]\n"
" Read the specified number of bytes from memory at the given\n"
" address, and display a hexdump.\n"
};
static int cmd_mw(char **arg)
static int cmd_mw(cproc_t cp, char **arg)
{
char *off_text = get_arg(arg);
char *byte_text;
@ -177,29 +135,12 @@ static int cmd_mw(char **arg)
return 0;
}
static struct command command_mw = {
.name = "mw",
.func = cmd_mw,
.help =
"mw <address> bytes ...\n"
" Write a sequence of bytes to a memory address. Byte values are\n"
" two-digit hexadecimal numbers.\n"
};
static int cmd_reset(char **arg)
static int cmd_reset(cproc_t cp, char **arg)
{
return device_get()->control(DEVICE_CTL_RESET);
}
static struct command command_reset = {
.name = "reset",
.func = cmd_reset,
.help =
"reset\n"
" Reset (and halt) the CPU.\n"
};
static int cmd_erase(char **arg)
static int cmd_erase(cproc_t cp, char **arg)
{
if (device_get()->control(DEVICE_CTL_HALT) < 0)
return -1;
@ -208,15 +149,7 @@ static int cmd_erase(char **arg)
return device_get()->control(DEVICE_CTL_ERASE);
}
static struct command command_erase = {
.name = "erase",
.func = cmd_erase,
.help =
"erase\n"
" Erase the device under test.\n"
};
static int cmd_step(char **arg)
static int cmd_step(cproc_t cp, char **arg)
{
char *count_text = get_arg(arg);
int count = 1;
@ -230,18 +163,10 @@ static int cmd_step(char **arg)
count--;
}
return cmd_regs(NULL);
return cmd_regs(cp, NULL);
}
static struct command command_step = {
.name = "step",
.func = cmd_step,
.help =
"step [count]\n"
" Single-step the CPU, and display the register state.\n"
};
static int cmd_run(char **arg)
static int cmd_run(cproc_t cp, char **arg)
{
char *bp_text = get_arg(arg);
int bp_addr;
@ -274,19 +199,10 @@ static int cmd_run(char **arg)
if (device_get()->control(DEVICE_CTL_HALT) < 0)
return -1;
return cmd_regs(NULL);
return cmd_regs(cp, NULL);
}
static struct command command_run = {
.name = "run",
.func = cmd_run,
.help =
"run [breakpoint]\n"
" Run the CPU until either a specified breakpoint occurs or the\n"
" command is interrupted.\n"
};
static int cmd_set(char **arg)
static int cmd_set(cproc_t cp, char **arg)
{
char *reg_text = get_arg(arg);
char *val_text = get_arg(arg);
@ -319,19 +235,11 @@ static int cmd_set(char **arg)
if (device_get()->setregs(regs) < 0)
return -1;
show_regs(regs);
cproc_regs(cp, regs);
return 0;
}
static struct command command_set = {
.name = "set",
.func = cmd_set,
.help =
"set <register> <value>\n"
" Change the value of a CPU register.\n"
};
static int cmd_dis(char **arg)
static int cmd_dis(cproc_t cp, char **arg)
{
char *off_text = get_arg(arg);
char *len_text = get_arg(arg);
@ -368,18 +276,10 @@ static int cmd_dis(char **arg)
if (device_get()->readmem(offset, buf, length) < 0)
return -1;
disassemble(offset, (u_int8_t *)buf, length);
cproc_disassemble(cp, offset, (u_int8_t *)buf, length);
return 0;
}
static struct command command_dis = {
.name = "dis",
.func = cmd_dis,
.help =
"dis <address> [length]\n"
" Disassemble a section of memory.\n"
};
static FILE *hexout_file;
static u_int16_t hexout_addr;
static u_int8_t hexout_buf[16];
@ -455,7 +355,7 @@ static int hexout_feed(u_int16_t addr, const u_int8_t *buf, int len)
return 0;
}
static int cmd_hexout(char **arg)
static int cmd_hexout(cproc_t cp, char **arg)
{
char *off_text = get_arg(arg);
char *len_text = get_arg(arg);
@ -510,14 +410,6 @@ fail:
return -1;
}
static struct command command_hexout = {
.name = "hexout",
.func = cmd_hexout,
.help =
"hexout <address> <length> <filename.hex>\n"
" Save a region of memory into a HEX file.\n"
};
static u_int8_t prog_buf[128];
static u_int16_t prog_addr;
static int prog_len;
@ -589,12 +481,12 @@ static int prog_feed(u_int16_t addr, const u_int8_t *data, int len)
return 0;
}
static int cmd_prog(char **arg)
static int cmd_prog(cproc_t cp, char **arg)
{
FILE *in;
int result = 0;
if (modify_prompt(MODIFY_SYMS))
if (cproc_prompt_abort(cp, CPROC_MODIFY_SYMS))
return 0;
in = fopen(*arg, "r");
@ -630,30 +522,95 @@ static int cmd_prog(char **arg)
return -1;
}
modify_clear(MODIFY_SYMS);
cproc_unmodify(cp, CPROC_MODIFY_SYMS);
return result;
}
static struct command command_prog = {
.name = "prog",
.func = cmd_prog,
.help =
"prog <filename>\n"
" Erase the device and flash the data contained in a binary file.\n"
" This command also loads symbols from the file, if available.\n"
static const struct cproc_command commands[] = {
{
.name = "regs",
.func = cmd_regs,
.help =
"regs\n"
" Read and display the current register contents.\n"
},
{
.name = "prog",
.func = cmd_prog,
.help =
"prog <filename>\n"
" Erase the device and flash the data contained in a binary file.\n"
" This command also loads symbols from the file, if available.\n"
},
{
.name = "md",
.func = cmd_md,
.help =
"md <address> [length]\n"
" Read the specified number of bytes from memory at the given\n"
" address, and display a hexdump.\n"
},
{
.name = "mw",
.func = cmd_mw,
.help =
"mw <address> bytes ...\n"
" Write a sequence of bytes to a memory address. Byte values are\n"
" two-digit hexadecimal numbers.\n"
},
{
.name = "reset",
.func = cmd_reset,
.help =
"reset\n"
" Reset (and halt) the CPU.\n"
},
{
.name = "erase",
.func = cmd_erase,
.help =
"erase\n"
" Erase the device under test.\n"
},
{
.name = "step",
.func = cmd_step,
.help =
"step [count]\n"
" Single-step the CPU, and display the register state.\n"
},
{
.name = "run",
.func = cmd_run,
.help =
"run [breakpoint]\n"
" Run the CPU until either a specified breakpoint occurs or the\n"
" command is interrupted.\n"
},
{
.name = "set",
.func = cmd_set,
.help =
"set <register> <value>\n"
" Change the value of a CPU register.\n"
},
{
.name = "dis",
.func = cmd_dis,
.help =
"dis <address> [length]\n"
" Disassemble a section of memory.\n"
},
{
.name = "hexout",
.func = cmd_hexout,
.help =
"hexout <address> <length> <filename.hex>\n"
" Save a region of memory into a HEX file.\n"
}
};
void devcmd_init(void)
int devcmd_register(cproc_t cp)
{
register_command(&command_md);
register_command(&command_mw);
register_command(&command_reset);
register_command(&command_erase);
register_command(&command_regs);
register_command(&command_run);
register_command(&command_step);
register_command(&command_set);
register_command(&command_dis);
register_command(&command_hexout);
register_command(&command_prog);
return cproc_register_commands(cp, commands, ARRAY_LEN(commands));
}

View File

@ -19,7 +19,9 @@
#ifndef DEVCMD_H_
#define DEVCMD_H_
#include "cproc.h"
/* Register device commands */
void devcmd_init(void);
int devcmd_register(cproc_t cp);
#endif

272
dis.c
View File

@ -17,12 +17,12 @@
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "parse.h"
#include "dis.h"
#include "stab.h"
#include "util.h"
/**********************************************************************/
@ -34,8 +34,8 @@
* Returns the number of bytes consumed in decoding, or -1 if the a
* valid single-operand instruction could not be found.
*/
static int decode_single(u_int8_t *code, u_int16_t offset, u_int16_t size,
struct msp430_instruction *insn)
static int decode_single(const u_int8_t *code, u_int16_t offset,
u_int16_t size, struct msp430_instruction *insn)
{
u_int16_t op = (code[1] << 8) | code[0];
int need_arg = 0;
@ -86,8 +86,8 @@ static int decode_single(u_int8_t *code, u_int16_t offset, u_int16_t size,
* Returns the number of bytes consumed or -1 if a valid instruction
* could not be found.
*/
static int decode_double(u_int8_t *code, u_int16_t offset, u_int16_t size,
struct msp430_instruction *insn)
static int decode_double(const u_int8_t *code, u_int16_t offset,
u_int16_t size, struct msp430_instruction *insn)
{
u_int16_t op = (code[1] << 8) | code[0];
int need_src = 0;
@ -175,7 +175,7 @@ static int decode_double(u_int8_t *code, u_int16_t offset, u_int16_t size,
* All jump instructions are one word in length, so this function
* always returns 2 (to indicate the consumption of 2 bytes).
*/
static int decode_jump(u_int8_t *code, u_int16_t offset, u_int16_t len,
static int decode_jump(const u_int8_t *code, u_int16_t offset, u_int16_t len,
struct msp430_instruction *insn)
{
u_int16_t op = (code[1] << 8) | code[0];
@ -396,7 +396,7 @@ static void find_emulated_ops(struct msp430_instruction *insn)
* successful, the decoded instruction is written into the structure
* pointed to by insn.
*/
int dis_decode(u_int8_t *code, u_int16_t offset, u_int16_t len,
int dis_decode(const u_int8_t *code, u_int16_t offset, u_int16_t len,
struct msp430_instruction *insn)
{
u_int16_t op;
@ -514,12 +514,8 @@ static const struct {
{MSP430_OP_TST, "TST"}
};
/* Return the mnemonic for an operation, if possible.
*
* If the argument is not a valid operation, this function returns the
* string "???".
*/
static const char *msp_op_name(msp430_op_t op)
/* Return the mnemonic for an operation, if possible. */
const char *dis_opcode_name(msp430_op_t op)
{
int i;
@ -527,7 +523,18 @@ static const char *msp_op_name(msp430_op_t op)
if (op == opcode_names[i].op)
return opcode_names[i].mnemonic;
return "???";
return NULL;
}
msp430_op_t dis_opcode_from_name(const char *name)
{
int i;
for (i = 0; i < ARRAY_LEN(opcode_names); i++)
if (!strcasecmp(name, opcode_names[i].mnemonic))
return opcode_names[i].op;
return -1;
}
static const char *const msp430_reg_names[] = {
@ -537,227 +544,34 @@ static const char *const msp430_reg_names[] = {
"R12", "R13", "R14", "R15"
};
static int format_addr(msp430_amode_t amode, u_int16_t addr)
msp430_reg_t dis_reg_from_name(const char *name)
{
char name[64];
u_int16_t offset;
int numeric = 0;
int len;
int count = 0;
const char *prefix = "";
const char *num = name;
switch (amode) {
case MSP430_AMODE_REGISTER:
case MSP430_AMODE_INDIRECT:
case MSP430_AMODE_INDIRECT_INC:
while (num && isdigit(*num))
num++;
if (*num) {
msp430_reg_t r = atoi(num);
if (r >= 0 && r <= 15)
return r;
}
if (!strcasecmp(name, "pc"))
return 0;
case MSP430_AMODE_IMMEDIATE:
prefix = "#";
case MSP430_AMODE_INDEXED:
numeric = 1;
break;
case MSP430_AMODE_ABSOLUTE:
prefix = "&";
break;
case MSP430_AMODE_SYMBOLIC:
break;
}
len = printf("%s", prefix);
if (len >= 0)
count += len;
if ((!numeric ||
(addr >= 0x200 && addr < 0xfff0)) &&
!stab_nearest(addr, name, sizeof(name), &offset) &&
!offset) {
colorize("1m");
len = printf("%s", name);
colorize("0m");
} else {
colorize("32m");
len = printf(numeric ? "0x%x" : "0x%04x", addr);
colorize("0m");
}
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 */
static void dis_format(const struct msp430_instruction *insn)
{
int count = 0;
int len;
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) {
len = format_operand(insn->src_mode,
insn->src_addr,
insn->src_reg);
if (len >= 0)
count += len;
printf(",");
count++;
while (count < 23) {
count++;
printf(" ");
}
printf(" ");
count++;
}
/* Destination operand */
if (insn->itype != MSP430_ITYPE_NOARG)
format_operand(insn->dst_mode,
insn->dst_addr,
insn->dst_reg);
}
int dis_opcode_by_name(const char *name, msp430_op_t *op)
{
int i;
for (i = 0; i < ARRAY_LEN(opcode_names); i++)
if (!strcasecmp(name, opcode_names[i].mnemonic)) {
if (op)
*op = opcode_names[i].op;
return 0;
}
if (!strcasecmp(name, "sp"))
return 1;
if (!strcasecmp(name, "sr"))
return 2;
return -1;
}
void disassemble(u_int16_t offset, u_int8_t *data, int length)
const char *dis_reg_name(msp430_reg_t reg)
{
int first_line = 1;
if (reg >= 0 && reg <= 15)
return msp430_reg_names[reg];
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;
}
return NULL;
}

13
dis.h
View File

@ -202,13 +202,14 @@ struct msp430_instruction {
* successful, the decoded instruction is written into the structure
* pointed to by insn.
*/
int dis_decode(u_int8_t *code, u_int16_t offset, u_int16_t len,
int dis_decode(const u_int8_t *code,
u_int16_t offset, u_int16_t len,
struct msp430_instruction *insn);
/* Look up an opcode by name. Returns 0 if successful, -1 otherwise. */
int dis_opcode_by_name(const char *name, msp430_op_t *op);
/* Print a disassembly on stdout */
void disassemble(u_int16_t offset, u_int8_t *buf, int length);
/* Look up names for registers and opcodes */
msp430_op_t dis_opcode_from_name(const char *name);
const char *dis_opcode_name(msp430_op_t op);
msp430_reg_t dis_reg_from_name(const char *name);
const char *dis_reg_name(msp430_reg_t reg);
#endif

9
gdb.c
View File

@ -30,7 +30,6 @@
#include "device.h"
#include "util.h"
#include "gdb.h"
#include "parse.h"
/************************************************************************
* GDB IO routines
@ -579,7 +578,7 @@ static int gdb_server(int port)
return gdb_errno ? -1 : 0;
}
static int cmd_gdb(char **arg)
static int cmd_gdb(cproc_t cp, char **arg)
{
char *port_text = get_arg(arg);
int port = 2000;
@ -595,7 +594,7 @@ static int cmd_gdb(char **arg)
return gdb_server(port);
}
static struct command command_gdb = {
static const struct cproc_command command_gdb = {
.name = "gdb",
.func = cmd_gdb,
.help =
@ -603,7 +602,7 @@ static struct command command_gdb = {
" Run a GDB remote stub on the given TCP/IP port.\n"
};
void gdb_init(void)
int gdb_register(cproc_t cp)
{
register_command(&command_gdb);
return cproc_register_commands(cp, &command_gdb, 1);
}

4
gdb.h
View File

@ -19,7 +19,9 @@
#ifndef GDB_H_
#define GDB_H_
#include "cproc.h"
/* Register the "gdb" command */
void gdb_init(void);
int gdb_register(cproc_t cp);
#endif

27
main.c
View File

@ -24,7 +24,6 @@
#include <unistd.h>
#include "dis.h"
#include "parse.h"
#include "device.h"
#include "binfile.h"
#include "stab.h"
@ -66,7 +65,7 @@ static void usage(const char *progname)
progname, progname, progname, progname);
}
static void process_rc_file(void)
static void process_rc_file(cproc_t cp)
{
const char *home = getenv("HOME");
char text[256];
@ -76,7 +75,7 @@ static void process_rc_file(void)
snprintf(text, sizeof(text), "%s/.mspdebug", home);
if (!access(text, F_OK))
process_file(text);
cproc_process_file(cp, text);
}
#define MODE_RF2500 0x01
@ -90,6 +89,7 @@ int main(int argc, char **argv)
const char *uif_device = NULL;
const char *bsl_device = NULL;
const struct device *msp430_dev = NULL;
cproc_t cp;
int opt;
int no_rc = 0;
int ret = 0;
@ -192,28 +192,33 @@ int main(int argc, char **argv)
return -1;
}
cp = cproc_new();
if (!cp ||
sym_register(cp) < 0 ||
devcmd_register(cp) < 0 ||
gdb_register(cp) < 0 ||
rtools_register(cp) < 0) {
perror("couldn't set up command parser");
return -1;
}
/* Initialise parsing */
device_set(msp430_dev);
ctrlc_init();
parse_init();
sym_init();
devcmd_init();
gdb_init();
rtools_init();
if (!no_rc)
process_rc_file();
process_rc_file(cp);
/* Process commands */
if (optind < argc) {
while (optind < argc) {
if (process_command(argv[optind++], 0) < 0) {
if (cproc_process_command(cp, argv[optind++]) < 0) {
ret = -1;
break;
}
}
} else {
reader_loop();
cproc_reader_loop(cp);
}
msp430_dev->close();

545
parse.c
View File

@ -1,545 +0,0 @@
/* 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>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>
#ifdef USE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
#include "util.h"
#include "parse.h"
#include "stab.h"
static struct option *option_list;
static struct command *command_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 struct command *find_command(const char *name)
{
struct command *c;
for (c = command_list; c; c = c->next)
if (!strcasecmp(c->name, name))
return c;
return NULL;
}
void register_command(struct command *c)
{
c->next = command_list;
command_list = c;
}
static int interactive_call = 1;
int is_interactive(void)
{
return interactive_call;
}
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;
}
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";
}
static const char *name_list[128];
static int num_names;
static int name_max_len;
static void name_start(void)
{
num_names = 0;
name_max_len = 0;
}
static void name_push(const char *text)
{
if (num_names < ARRAY_LEN(name_list)) {
int len = strlen(text);
name_list[num_names++] = text;
if (len > name_max_len)
name_max_len = len;
}
}
static int compare_name(const void *left, const void *right)
{
return strcasecmp(*(const char *const *)left,
*(const char *const *)right);
}
static void name_list_show(void)
{
int i;
int max_len = name_max_len + 2;
int rows, cols;
qsort(name_list, num_names, sizeof(name_list[0]),
compare_name);
cols = 72 / max_len;
rows = (num_names + cols - 1) / cols;
for (i = 0; i < rows; i++) {
int j;
printf(" ");
for (j = 0; j < cols; j++) {
int k = j * rows + i;
if (k >= num_names)
break;
printf("%s", name_list[k]);
for (k = strlen(name_list[k]); k < max_len; k++)
printf(" ");
}
printf("\n");
}
}
static int cmd_help(char **arg)
{
const char *topic = get_arg(arg);
if (topic) {
const struct command *cmd = find_command(topic);
const struct option *opt = find_option(topic);
if (cmd) {
colorize("1m");
printf("COMMAND: %s\n", cmd->name);
colorize("0m");
fputs(cmd->help, stdout);
if (opt)
printf("\n");
}
if (opt) {
colorize("1m");
printf("OPTION: %s (%s)\n", opt->name,
type_text(opt->type));
colorize("0m");
fputs(opt->help, stdout);
}
if (!(cmd || opt)) {
fprintf(stderr, "help: unknown command: %s\n", topic);
return -1;
}
} else {
const struct command *cmd;
const struct option *opt;
name_start();
for (cmd = command_list; cmd; cmd = cmd->next)
name_push(cmd->name);
printf("Available commands:\n");
name_list_show();
printf("\n");
name_start();
for (opt = option_list; opt; opt = opt->next)
name_push(opt->name);
printf("Available options:\n");
name_list_show();
printf("\n");
printf("Type \"help <topic>\" for more information.\n");
printf("Press Ctrl+D to quit.\n");
}
return 0;
}
static struct command command_help = {
.func = cmd_help,
.name = "help",
.help =
"help [command]\n"
" Without arguments, displays a list of commands. With a command\n"
" name as an argument, displays help for that command.\n"
};
#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);
printf("\n");
do {
for (;;) {
char *buf = readline("(mspdebug) ");
if (!buf)
break;
add_history(buf);
process_command(buf, 1);
free(buf);
}
} while (modify_prompt(MODIFY_ALL));
printf("\n");
}
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 stab_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;
}
static 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 command command_opt = {
.name = "opt",
.func = cmd_opt,
.help =
"opt [name] [value]\n"
" Query or set option variables. With no arguments, displays all\n"
" available options.\n"
};
int process_file(const char *filename)
{
FILE *in;
char buf[1024];
int line_no = 0;
in = fopen(filename, "r");
if (!in) {
fprintf(stderr, "read: can't open %s: %s\n",
filename, strerror(errno));
return -1;
}
while (fgets(buf, sizeof(buf), in)) {
char *cmd = buf;
line_no++;
while (*cmd && isspace(*cmd))
cmd++;
if (*cmd == '#')
continue;
if (process_command(cmd, 0) < 0) {
fprintf(stderr, "read: error processing %s (line %d)\n",
filename, line_no);
fclose(in);
return -1;
}
}
fclose(in);
return 0;
}
static int cmd_read(char **arg)
{
char *filename = get_arg(arg);
if (!filename) {
fprintf(stderr, "read: filename must be specified\n");
return -1;
}
return process_file(filename);
}
static struct command command_read = {
.name = "read",
.func = cmd_read,
.help =
"read <filename>\n"
" Read commands from a file and evaluate them.\n"
};
static struct option option_color = {
.name = "color",
.type = OPTION_BOOLEAN,
.help = "Colorize debugging 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);
register_command(&command_help);
register_command(&command_opt);
register_command(&command_read);
}
static int modify_flags;
void modify_set(int flags)
{
modify_flags |= flags;
}
void modify_clear(int flags)
{
modify_flags &= ~flags;
}
int modify_prompt(int flags)
{
char buf[32];
if (!(interactive_call && (modify_flags & flags)))
return 0;
for (;;) {
printf("Symbols have not been saved since modification. "
"Continue (y/n)? ");
fflush(stdout);
if (!fgets(buf, sizeof(buf), stdin)) {
printf("\n");
return 1;
}
if (toupper(buf[0]) == 'Y')
return 0;
if (toupper(buf[0]) == 'N')
return 1;
printf("Please answer \"y\" or \"n\".\n");
}
return 0;
}
void hexdump(int addr, const u_int8_t *data, int len)
{
int offset = 0;
while (offset < len) {
int i, j;
/* Address label */
colorize("36m");
printf(" %04x:", offset + addr);
colorize("0m");
/* Hex portion */
for (i = 0; i < 16 && offset + i < len; i++)
printf(" %02x", data[offset + i]);
for (j = i; j < 16; j++)
printf(" ");
/* Printable characters */
colorize("32m");
printf(" |");
for (j = 0; j < i; j++) {
int c = data[offset + j];
printf("%c", (c >= 32 && c <= 126) ? c : '.');
}
for (; j < 16; j++)
printf(" ");
printf("|\n");
colorize("0m");
offset += i;
}
}

110
parse.h
View File

@ -1,110 +0,0 @@
/* 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_
#include <sys/types.h>
typedef int (*command_func_t)(char **arg);
struct command {
const char *name;
int (*func)(char **arg);
const char *help;
struct command *next;
};
/* Add a command to the global command table */
void register_command(struct command *c);
/* 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);
int process_file(const char *filename);
/* 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);
/* Print an ANSI colour code, if the colour option has been set by
* the user.
*/
int colorize(const char *text);
/* Return non-zero if executing in an interactive context. */
int is_interactive(void);
/* Mark/unmark items as modified. The modify_prompt function, when
* called in interactive context, prompts the user before continuing
* if any of the items specified are modified. If the user elects
* to abort the operation, it returns non-zero.
*/
#define MODIFY_SYMS 0x01
#define MODIFY_ALL 0x01
void modify_set(int flags);
void modify_clear(int flags);
int modify_prompt(int flags);
/* 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);
/* Initialise the parser, and register built-ins. */
void parse_init(void);
/* Display a canonical hexdump */
void hexdump(int addr, const u_int8_t *data, int len);
#endif

View File

@ -27,7 +27,7 @@
#include "dis.h"
#include "rtools.h"
#include "stab.h"
#include "parse.h"
#include "cproc_util.h"
#define ISEARCH_OPCODE 0x0001
#define ISEARCH_BW 0x0002
@ -59,7 +59,8 @@ static int isearch_opcode(const char *term, char **arg,
return -1;
}
if (dis_opcode_by_name(opname, &q->insn.op) < 0) {
q->insn.op = dis_opcode_from_name(opname);
if (q->insn.op < 0) {
fprintf(stderr, "isearch: unknown opcode: %s\n", opname);
return -1;
}
@ -315,7 +316,8 @@ static int isearch_match(const struct msp430_instruction *insn,
return 1;
}
static int do_isearch(int addr, int len, const struct isearch_query *q)
static int do_isearch(cproc_t cp,
int addr, int len, const struct isearch_query *q)
{
u_int8_t *mbuf;
const struct device *dev = device_get();
@ -346,14 +348,14 @@ static int do_isearch(int addr, int len, const struct isearch_query *q)
int count = dis_decode(mbuf + i, addr + i, len - i, &insn);
if (count >= 0 && isearch_match(&insn, q))
disassemble(addr + i, mbuf + i, count);
cproc_disassemble(cp, addr + i, mbuf + i, count);
}
free(mbuf);
return 0;
}
static int cmd_isearch(char **arg)
static int cmd_isearch(cproc_t cp, char **arg)
{
const static struct {
const char *name;
@ -414,10 +416,10 @@ static int cmd_isearch(char **arg)
return -1;
}
return do_isearch(addr, len, &q);
return do_isearch(cp, addr, len, &q);
}
static struct command isearch_command = {
static const struct cproc_command isearch_command = {
.name = "isearch",
.func = cmd_isearch,
.help =
@ -437,7 +439,7 @@ static struct command isearch_command = {
" destination operand.\n"
};
void rtools_init(void)
int rtools_register(cproc_t cp)
{
register_command(&isearch_command);
return cproc_register_commands(cp, &isearch_command, 1);
}

View File

@ -19,7 +19,9 @@
#ifndef RTOOLS_H_
#define RTOOLS_H_
#include "cproc.h"
/* Register reverse-engineering tool commands. */
void rtools_init(void);
int rtools_register(cproc_t cp);
#endif

1
sim.c
View File

@ -24,7 +24,6 @@
#include "dis.h"
#include "util.h"
#include "stab.h"
#include "parse.h"
#define MEM_SIZE 65536

117
sym.c
View File

@ -26,10 +26,10 @@
#include "stab.h"
#include "binfile.h"
#include "util.h"
#include "parse.h"
#include "vector.h"
#include "sym.h"
static int cmd_eval(char **arg)
static int cmd_eval(cproc_t cp, char **arg)
{
int addr;
u_int16_t offset;
@ -51,20 +51,12 @@ static int cmd_eval(char **arg)
return 0;
}
static struct command command_eval = {
.name = "=",
.func = cmd_eval,
.help =
"= <expression>\n"
" Evaluate an expression using the symbol table.\n"
};
static int cmd_sym_load_add(int clear, char **arg)
static int cmd_sym_load_add(cproc_t cp, int clear, char **arg)
{
FILE *in;
int result = 0;
if (clear && modify_prompt(MODIFY_SYMS))
if (clear && cproc_prompt_abort(cp, CPROC_MODIFY_SYMS))
return 0;
in = fopen(*arg, "r");
@ -86,9 +78,9 @@ static int cmd_sym_load_add(int clear, char **arg)
fclose(in);
if (clear)
modify_clear(MODIFY_SYMS);
cproc_unmodify(cp, CPROC_MODIFY_SYMS);
else
modify_set(MODIFY_SYMS);
cproc_modify(cp, CPROC_MODIFY_SYMS);
return result;
}
@ -105,7 +97,7 @@ static int savemap_cb(const char *name, u_int16_t value)
return 0;
}
static int cmd_sym_savemap(char **arg)
static int cmd_sym_savemap(cproc_t cp, char **arg)
{
char *fname = get_arg(arg);
@ -131,7 +123,7 @@ static int cmd_sym_savemap(char **arg)
return -1;
}
modify_clear(MODIFY_SYMS);
cproc_unmodify(cp, CPROC_MODIFY_SYMS);
return 0;
}
@ -151,7 +143,7 @@ static int find_sym(const char *name, u_int16_t value)
return 0;
}
static int cmd_sym_find(char **arg)
static int cmd_sym_find(cproc_t cp, char **arg)
{
char *expr = get_arg(arg);
@ -215,9 +207,6 @@ static int renames_do(const char *replace)
count++;
}
if (count)
modify_set(MODIFY_SYMS);
printf("%d symbols renamed\n", count);
return 0;
}
@ -241,7 +230,7 @@ static int find_renames(const char *name, u_int16_t value)
return 0;
}
static int cmd_sym_rename(char **arg)
static int cmd_sym_rename(cproc_t cp, char **arg)
{
const char *expr = get_arg(arg);
const char *replace = get_arg(arg);
@ -269,10 +258,14 @@ static int cmd_sym_rename(char **arg)
regfree(&find_preg);
ret = renames_do(replace);
vector_destroy(&renames_vec);
return ret;
if (ret > 0)
cproc_modify(cp, CPROC_MODIFY_SYMS);
return ret >= 0 ? 0 : -1;
}
static int cmd_sym_del(char **arg)
static int cmd_sym_del(cproc_t cp, char **arg)
{
char *name = get_arg(arg);
@ -288,11 +281,11 @@ static int cmd_sym_del(char **arg)
return -1;
}
modify_set(MODIFY_SYMS);
cproc_modify(cp, CPROC_MODIFY_SYMS);
return 0;
}
static int cmd_sym(char **arg)
static int cmd_sym(cproc_t cp, char **arg)
{
char *subcmd = get_arg(arg);
@ -303,10 +296,10 @@ static int cmd_sym(char **arg)
}
if (!strcasecmp(subcmd, "clear")) {
if (modify_prompt(MODIFY_SYMS))
if (cproc_prompt_abort(cp, CPROC_MODIFY_SYMS))
return 0;
stab_clear();
modify_clear(MODIFY_SYMS);
cproc_unmodify(cp, CPROC_MODIFY_SYMS);
return 0;
}
@ -330,53 +323,59 @@ static int cmd_sym(char **arg)
if (stab_set(name, value) < 0)
return -1;
modify_set(MODIFY_SYMS);
cproc_modify(cp, CPROC_MODIFY_SYMS);
return 0;
}
if (!strcasecmp(subcmd, "del"))
return cmd_sym_del(arg);
return cmd_sym_del(cp, arg);
if (!strcasecmp(subcmd, "import"))
return cmd_sym_load_add(1, arg);
return cmd_sym_load_add(cp, 1, arg);
if (!strcasecmp(subcmd, "import+"))
return cmd_sym_load_add(0, arg);
return cmd_sym_load_add(cp, 0, arg);
if (!strcasecmp(subcmd, "export"))
return cmd_sym_savemap(arg);
return cmd_sym_savemap(cp, arg);
if (!strcasecmp(subcmd, "rename"))
return cmd_sym_rename(arg);
return cmd_sym_rename(cp, arg);
if (!strcasecmp(subcmd, "find"))
return cmd_sym_find(arg);
return cmd_sym_find(cp, arg);
fprintf(stderr, "sym: unknown subcommand: %s\n", subcmd);
return -1;
}
static struct command command_sym = {
.name = "sym",
.func = cmd_sym,
.help =
"sym clear\n"
" Clear the symbol table.\n"
"sym set <name> <value>\n"
" Set or overwrite the value of a symbol.\n"
"sym del <name>\n"
" Delete a symbol from the symbol table.\n"
"sym import <filename>\n"
" Load symbols from the given file.\n"
"sym import+ <filename>\n"
" Load additional symbols from the given file.\n"
"sym export <filename>\n"
" Save the current symbols to a BSD-style symbol file.\n"
"sym find <regex>\n"
" Search for symbols by regular expression.\n"
"sym rename <regex> <string>\n"
" Replace every occurance of a pattern in symbol names.\n"
static const struct cproc_command commands[] = {
{
.name = "=",
.func = cmd_eval,
.help =
"= <expression>\n"
" Evaluate an expression using the symbol table.\n"
},
{
.name = "sym",
.func = cmd_sym,
.help =
"sym clear\n"
" Clear the symbol table.\n"
"sym set <name> <value>\n"
" Set or overwrite the value of a symbol.\n"
"sym del <name>\n"
" Delete a symbol from the symbol table.\n"
"sym import <filename>\n"
" Load symbols from the given file.\n"
"sym import+ <filename>\n"
" Load additional symbols from the given file.\n"
"sym export <filename>\n"
" Save the current symbols to a BSD-style symbol file.\n"
"sym find <regex>\n"
" Search for symbols by regular expression.\n"
"sym rename <regex> <string>\n"
" Replace every occurance of a pattern in symbol names.\n"
}
};
int sym_init(void)
int sym_register(cproc_t cp)
{
register_command(&command_eval);
register_command(&command_sym);
return 0;
return cproc_register_commands(cp, commands, ARRAY_LEN(commands));
}

4
sym.h
View File

@ -19,7 +19,9 @@
#ifndef SYM_H_
#define SYM_H_
#include "cproc.h"
/* Register symbol-table manipulation commands */
void sym_init(void);
int sym_register(cproc_t cp);
#endif

22
util.c
View File

@ -241,3 +241,25 @@ void debug_hexdump(const char *label, const u_int8_t *data, int len)
offset += i;
}
}
int textlen(const char *text)
{
int count = 0;
for (;;) {
if (*text == 27) {
while (*text && !isalpha(*text))
text++;
if (*text)
text++;
}
if (!*text)
break;
count++;
text++;
}
return count;
}

3
util.h
View File

@ -42,4 +42,7 @@ char *get_arg(char **text);
void debug_hexdump(const char *label,
const u_int8_t *data, int len);
/* Get text length, not including ANSI codes */
int textlen(const char *text);
#endif

View File

@ -57,7 +57,7 @@ int vector_realloc(struct vector *v, int capacity)
return 0;
}
int vector_push(struct vector *v, void *data, int count)
int vector_push(struct vector *v, const void *data, int count)
{
int needed = v->size + count;

View File

@ -45,7 +45,7 @@ int vector_realloc(struct vector *v, int capacity);
/* Append any number of elements to the end of a vector, reallocating if
* necessary. Returns 0 on success or -1 if memory could not be allocated.
*/
int vector_push(struct vector *v, void *data, int count);
int vector_push(struct vector *v, const void *data, int count);
/* Dereference a vector, giving an expression for the element of type t at
* position i in vector v. Use as follows: