Command processor is now an object.
This commit is contained in:
parent
90d39a96f5
commit
ba6e3366ff
2
Makefile
2
Makefile
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
243
devcmd.c
243
devcmd.c
|
@ -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 = {
|
||||
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));
|
||||
}
|
||||
|
|
4
devcmd.h
4
devcmd.h
|
@ -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
272
dis.c
|
@ -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
13
dis.h
|
@ -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
9
gdb.c
|
@ -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
4
gdb.h
|
@ -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
27
main.c
|
@ -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
545
parse.c
|
@ -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
110
parse.h
|
@ -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
|
20
rtools.c
20
rtools.c
|
@ -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);
|
||||
}
|
||||
|
|
4
rtools.h
4
rtools.h
|
@ -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
1
sim.c
|
@ -24,7 +24,6 @@
|
|||
#include "dis.h"
|
||||
#include "util.h"
|
||||
#include "stab.h"
|
||||
#include "parse.h"
|
||||
|
||||
#define MEM_SIZE 65536
|
||||
|
||||
|
|
79
sym.c
79
sym.c
|
@ -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,28 +323,36 @@ 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 = {
|
||||
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 =
|
||||
|
@ -371,12 +372,10 @@ static struct command command_sym = {
|
|||
" 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
4
sym.h
|
@ -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
22
util.c
|
@ -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
3
util.h
|
@ -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
|
||||
|
|
2
vector.c
2
vector.c
|
@ -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;
|
||||
|
||||
|
|
2
vector.h
2
vector.h
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue