Separated parsing from util.

This commit is contained in:
Daniel Beer 2010-04-10 14:56:29 +12:00
parent 7e309acdc5
commit 8758d8bc28
14 changed files with 955 additions and 871 deletions

View File

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

View File

@ -25,6 +25,7 @@
#include "device.h"
#include "binfile.h"
#include "stab.h"
#include "parse.h"
#include "util.h"
#include "dis.h"

View File

@ -23,10 +23,7 @@
#include <errno.h>
#include <unistd.h>
#include "device.h"
#include "binfile.h"
#include "stab.h"
#include "util.h"
#include "dis.h"
static const struct device *msp430_dev;

1
dis.c
View File

@ -20,6 +20,7 @@
#include <string.h>
#include <stdio.h>
#include "parse.h"
#include "dis.h"
#include "stab.h"
#include "util.h"

1
gdb.c
View File

@ -30,6 +30,7 @@
#include "device.h"
#include "util.h"
#include "gdb.h"
#include "parse.h"
/************************************************************************
* GDB IO routines

2
main.c
View File

@ -24,6 +24,7 @@
#include <unistd.h>
#include "dis.h"
#include "parse.h"
#include "device.h"
#include "binfile.h"
#include "stab.h"
@ -192,6 +193,7 @@ int main(int argc, char **argv)
/* Initialise parsing */
device_set(msp430_dev);
ctrlc_init();
parse_init();
sym_init();
devcmd_init();

View File

@ -72,10 +72,14 @@ core alone is emulated (no peripheral emulation).
During simulation, addresses below 0x0200 are assumed to be IO memory.
When data is written to an IO memory address, a message is displayed
on the console showing the program counter location, address written
to, and data. When data is read from IO memory, the user is notified
similarly and prompted to supply the data. At this prompt, address
expressions can be entered. The user can press Ctrl+C to abort an IO
request and stop execution.
to, and data. The data value is also written to simulated RAM at the
relevant address.
When data is read from IO memory, the user is notified similarly and
prompted to supply the data. At this prompt, address expressions can
be entered. If no value is entered, the value is read from simulated
RAM. The user can press Ctrl+C to abort an IO request and stop
execution.
This mode is intended for testing of changes to MSPDebug, and for
aiding the disassembly of MSP430 binaries (as all binary and symbol

791
parse.c Normal file
View File

@ -0,0 +1,791 @@
/* 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;
}
char *get_arg(char **text)
{
char *start;
char *end;
if (!text)
return NULL;
start = *text;
while (*start && isspace(*start))
start++;
if (!*start)
return NULL;
end = start;
while (*end && !isspace(*end))
end++;
if (*end)
while (*end && isspace(*end))
*(end++) = 0;
*text = end;
return start;
}
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) {
printf("COMMAND: %s\n", cmd->name);
fputs(cmd->help, stdout);
if (opt)
printf("\n");
}
if (opt) {
printf("OPTION: %s (%s)\n", opt->name,
type_text(opt->type));
fputs(opt->help, stdout);
}
if (!(cmd || opt)) {
fprintf(stderr, "help: unknown command: %s\n", topic);
return -1;
}
} else {
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 addr_exp(word, &o->data.numeric);
case OPTION_TEXT:
strncpy(o->data.text, word, sizeof(o->data.text));
o->data.text[sizeof(o->data.text) - 1] = 0;
break;
}
return 0;
}
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 disassembly output.\n"
};
int colorize(const char *text)
{
if (!option_color.data.numeric)
return 0;
return printf("\x1b[%s", text);
}
void parse_init(void)
{
register_option(&option_color);
register_command(&command_help);
register_command(&command_opt);
register_command(&command_read);
}
struct addr_exp_state {
int last_operator;
int data_stack[32];
int data_stack_size;
int op_stack[32];
int op_stack_size;
};
static int addr_exp_data(struct addr_exp_state *s, const char *text)
{
int value;
if (!s->last_operator || s->last_operator == ')') {
fprintf(stderr, "syntax error at token %s\n", text);
return -1;
}
/* Hex value */
if (*text == '0' && text[1] == 'x')
value = strtoul(text + 2, NULL, 16);
else if (isdigit(*text))
value = atoi(text);
else if (stab_get(text, &value) < 0) {
fprintf(stderr, "can't parse token: %s\n", text);
return -1;
}
if (s->data_stack_size + 1 > ARRAY_LEN(s->data_stack)) {
fprintf(stderr, "data stack overflow at token %s\n", text);
return -1;
}
s->data_stack[s->data_stack_size++] = value;
s->last_operator = 0;
return 0;
}
static int addr_exp_pop(struct addr_exp_state *s)
{
char op = s->op_stack[--s->op_stack_size];
int data1 = s->data_stack[--s->data_stack_size];
int data2 = 0;
int result = 0;
if (op != 'N')
data2 = s->data_stack[--s->data_stack_size];
assert (s->op_stack_size >= 0);
assert (s->data_stack_size >= 0);
switch (op) {
case '+':
result = data2 + data1;
break;
case '-':
result = data2 - data1;
break;
case '*':
result = data2 * data1;
break;
case '/':
if (!data1)
goto divzero;
result = data2 / data1;
break;
case '%':
if (!data1)
goto divzero;
result = data2 % data1;
break;
case 'N':
result = -data1;
break;
}
s->data_stack[s->data_stack_size++] = result;
return 0;
divzero:
fprintf(stderr, "divide by zero\n");
return -1;
}
static int can_push(struct addr_exp_state *s, char op)
{
char top;
if (!s->op_stack_size || op == '(')
return 1;
top = s->op_stack[s->op_stack_size - 1];
if (top == '(')
return 1;
switch (op) {
case 'N':
return 1;
case '*':
case '%':
case '/':
return top == '+' || top == '-';
default:
break;
}
return 0;
}
static int addr_exp_op(struct addr_exp_state *s, char op)
{
if (op == '(') {
if (!s->last_operator || s->last_operator == ')')
goto syntax_error;
} else if (op == '-') {
if (s->last_operator && s->last_operator != ')')
op = 'N';
} else {
if (s->last_operator && s->last_operator != ')')
goto syntax_error;
}
if (op == ')') {
/* ) collapses the stack to the last matching ( */
while (s->op_stack_size &&
s->op_stack[s->op_stack_size - 1] != '(')
if (addr_exp_pop(s) < 0)
return -1;
if (!s->op_stack_size) {
fprintf(stderr, "parenthesis mismatch: )\n");
return -1;
}
s->op_stack_size--;
} else {
while (!can_push(s, op))
if (addr_exp_pop(s) < 0)
return -1;
if (s->op_stack_size + 1 > ARRAY_LEN(s->op_stack)) {
fprintf(stderr, "operator stack overflow: %c\n", op);
return -1;
}
s->op_stack[s->op_stack_size++] = op;
}
s->last_operator = op;
return 0;
syntax_error:
fprintf(stderr, "syntax error at operator %c\n", op);
return -1;
}
static int addr_exp_finish(struct addr_exp_state *s, int *ret)
{
if (s->last_operator && s->last_operator != ')') {
fprintf(stderr, "syntax error at end of expression\n");
return -1;
}
while (s->op_stack_size) {
if (s->op_stack[s->op_stack_size - 1] == '(') {
fprintf(stderr, "parenthesis mismatch: (\n");
return -1;
}
if (addr_exp_pop(s) < 0)
return -1;
}
if (s->data_stack_size != 1) {
fprintf(stderr, "no data: stack size is %d\n",
s->data_stack_size);
return -1;
}
if (ret)
*ret = s->data_stack[0];
return 0;
}
int addr_exp(const char *text, int *addr)
{
const char *text_save = text;
int last_cc = 1;
char token_buf[64];
int token_len = 0;
struct addr_exp_state s = {0};
s.last_operator = '(';
for (;;) {
int cc;
/* Figure out what class this character is */
if (*text == '+' || *text == '-' ||
*text == '*' || *text == '/' ||
*text == '%' || *text == '(' ||
*text == ')')
cc = 1;
else if (!*text || isspace(*text))
cc = 2;
else if (isalnum(*text) || *text == '.' || *text == '_' ||
*text == '$' || *text == ':')
cc = 3;
else {
fprintf(stderr, "illegal character in expression: %c\n",
*text);
return -1;
}
/* Accumulate and process token text */
if (cc == 3) {
if (token_len + 1 < sizeof(token_buf))
token_buf[token_len++] = *text;
} else if (token_len) {
token_buf[token_len] = 0;
token_len = 0;
if (addr_exp_data(&s, token_buf) < 0)
goto fail;
}
/* Process operators */
if (cc == 1) {
if (addr_exp_op(&s, *text) < 0)
goto fail;
}
if (!*text)
break;
last_cc = cc;
text++;
}
if (addr_exp_finish(&s, addr) < 0)
goto fail;
return 0;
fail:
fprintf(stderr, "bad address expression: %s\n", text_save);
return -1;
}
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;
}

119
parse.h Normal file
View File

@ -0,0 +1,119 @@
/* 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);
/* Retrieve the next word from a pointer to the rest of a command
* argument buffer. Returns NULL if no more words.
*/
char *get_arg(char **text);
/* Process a command, returning -1 on error or 0 if executed
* successfully.
*
* The interactive argument specifies whether or not the command
* should be executed in an interactive context.
*/
int process_command(char *arg, int interactive);
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);
/* Parse an address expression, storing the result in the integer
* pointed to. Returns 0 if parsed successfully, -1 if not.
*/
typedef int (*token_func_t)(const char *text, int *value);
int addr_exp(const char *text, int *value);
/* 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);
#endif

View File

@ -26,6 +26,7 @@
#include "device.h"
#include "dis.h"
#include "rtools.h"
#include "parse.h"
#define ISEARCH_OPCODE 0x0001
#define ISEARCH_BW 0x0002

18
sim.c
View File

@ -24,6 +24,7 @@
#include "dis.h"
#include "util.h"
#include "stab.h"
#include "parse.h"
#define MEM_SIZE 65536
@ -88,6 +89,16 @@ static int fetch_io(u_int16_t addr, int is_byte, u_int32_t *data_ret)
len--;
text[len] = 0;
if (!len) {
if (data_ret) {
if (is_byte)
*data_ret = MEM_GETB(addr);
else
*data_ret = MEM_GETW(addr);
}
return 0;
}
if (!addr_exp(text, &data)) {
if (data_ret)
*data_ret = data;
@ -102,10 +113,13 @@ static void store_io(u_int16_t addr, int is_byte, u_int16_t data)
{
io_prefix("WRITE", addr, is_byte);
if (is_byte)
if (is_byte) {
printf(" => 0x%02x\n", data & 0xff);
else
MEM_SETB(addr, data & 0xff);
} else {
printf(" => 0x%04x\n", data);
MEM_SETW(addr, data);
}
}
static int fetch_operand(int amode, int reg, int is_byte,

1
sym.c
View File

@ -26,6 +26,7 @@
#include "stab.h"
#include "binfile.h"
#include "util.h"
#include "parse.h"
static int cmd_eval(char **arg)
{

773
util.c
View File

@ -29,458 +29,7 @@
#include <signal.h>
#include <assert.h>
#ifdef USE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
#include "util.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;
}
char *get_arg(char **text)
{
char *start;
char *end;
if (!text)
return NULL;
start = *text;
while (*start && isspace(*start))
start++;
if (!*start)
return NULL;
end = start;
while (*end && !isspace(*end))
end++;
if (*end)
while (*end && isspace(*end))
*(end++) = 0;
*text = end;
return start;
}
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) {
printf("COMMAND: %s\n", cmd->name);
fputs(cmd->help, stdout);
if (opt)
printf("\n");
}
if (opt) {
printf("OPTION: %s (%s)\n", opt->name,
type_text(opt->type));
fputs(opt->help, stdout);
}
if (!(cmd || opt)) {
fprintf(stderr, "help: unknown command: %s\n", topic);
return -1;
}
} else {
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 addr_exp(word, &o->data.numeric);
case OPTION_TEXT:
strncpy(o->data.text, word, sizeof(o->data.text));
o->data.text[sizeof(o->data.text) - 1] = 0;
break;
}
return 0;
}
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 disassembly output.\n"
};
int colorize(const char *text)
{
if (!option_color.data.numeric)
return 0;
return printf("\x1b[%s", text);
}
static volatile int ctrlc_flag;
@ -489,7 +38,7 @@ static void sigint_handler(int signum)
ctrlc_flag = 1;
}
void parse_init(void)
void ctrlc_init(void)
{
const static struct sigaction siga = {
.sa_handler = sigint_handler,
@ -497,12 +46,16 @@ void parse_init(void)
};
sigaction(SIGINT, &siga, NULL);
}
register_option(&option_color);
void ctrlc_reset(void)
{
ctrlc_flag = 0;
}
register_command(&command_help);
register_command(&command_opt);
register_command(&command_read);
int ctrlc_check(void)
{
return ctrlc_flag;
}
void hexdump(int addr, const u_int8_t *data, int len)
@ -596,311 +149,3 @@ int open_serial(const char *device, int rate)
return fd;
}
void ctrlc_reset(void)
{
ctrlc_flag = 0;
}
int ctrlc_check(void)
{
return ctrlc_flag;
}
struct addr_exp_state {
int last_operator;
int data_stack[32];
int data_stack_size;
int op_stack[32];
int op_stack_size;
};
static int addr_exp_data(struct addr_exp_state *s, const char *text)
{
int value;
if (!s->last_operator || s->last_operator == ')') {
fprintf(stderr, "syntax error at token %s\n", text);
return -1;
}
/* Hex value */
if (*text == '0' && text[1] == 'x')
value = strtoul(text + 2, NULL, 16);
else if (isdigit(*text))
value = atoi(text);
else if (stab_get(text, &value) < 0) {
fprintf(stderr, "can't parse token: %s\n", text);
return -1;
}
if (s->data_stack_size + 1 > ARRAY_LEN(s->data_stack)) {
fprintf(stderr, "data stack overflow at token %s\n", text);
return -1;
}
s->data_stack[s->data_stack_size++] = value;
s->last_operator = 0;
return 0;
}
static int addr_exp_pop(struct addr_exp_state *s)
{
char op = s->op_stack[--s->op_stack_size];
int data1 = s->data_stack[--s->data_stack_size];
int data2 = 0;
int result = 0;
if (op != 'N')
data2 = s->data_stack[--s->data_stack_size];
assert (s->op_stack_size >= 0);
assert (s->data_stack_size >= 0);
switch (op) {
case '+':
result = data2 + data1;
break;
case '-':
result = data2 - data1;
break;
case '*':
result = data2 * data1;
break;
case '/':
if (!data1)
goto divzero;
result = data2 / data1;
break;
case '%':
if (!data1)
goto divzero;
result = data2 % data1;
break;
case 'N':
result = -data1;
break;
}
s->data_stack[s->data_stack_size++] = result;
return 0;
divzero:
fprintf(stderr, "divide by zero\n");
return -1;
}
static int can_push(struct addr_exp_state *s, char op)
{
char top;
if (!s->op_stack_size || op == '(')
return 1;
top = s->op_stack[s->op_stack_size - 1];
if (top == '(')
return 1;
switch (op) {
case 'N':
return 1;
case '*':
case '%':
case '/':
return top == '+' || top == '-';
default:
break;
}
return 0;
}
static int addr_exp_op(struct addr_exp_state *s, char op)
{
if (op == '(') {
if (!s->last_operator || s->last_operator == ')')
goto syntax_error;
} else if (op == '-') {
if (s->last_operator && s->last_operator != ')')
op = 'N';
} else {
if (s->last_operator && s->last_operator != ')')
goto syntax_error;
}
if (op == ')') {
/* ) collapses the stack to the last matching ( */
while (s->op_stack_size &&
s->op_stack[s->op_stack_size - 1] != '(')
if (addr_exp_pop(s) < 0)
return -1;
if (!s->op_stack_size) {
fprintf(stderr, "parenthesis mismatch: )\n");
return -1;
}
s->op_stack_size--;
} else {
while (!can_push(s, op))
if (addr_exp_pop(s) < 0)
return -1;
if (s->op_stack_size + 1 > ARRAY_LEN(s->op_stack)) {
fprintf(stderr, "operator stack overflow: %c\n", op);
return -1;
}
s->op_stack[s->op_stack_size++] = op;
}
s->last_operator = op;
return 0;
syntax_error:
fprintf(stderr, "syntax error at operator %c\n", op);
return -1;
}
static int addr_exp_finish(struct addr_exp_state *s, int *ret)
{
if (s->last_operator && s->last_operator != ')') {
fprintf(stderr, "syntax error at end of expression\n");
return -1;
}
while (s->op_stack_size) {
if (s->op_stack[s->op_stack_size - 1] == '(') {
fprintf(stderr, "parenthesis mismatch: (\n");
return -1;
}
if (addr_exp_pop(s) < 0)
return -1;
}
if (s->data_stack_size != 1) {
fprintf(stderr, "no data: stack size is %d\n",
s->data_stack_size);
return -1;
}
if (ret)
*ret = s->data_stack[0];
return 0;
}
int addr_exp(const char *text, int *addr)
{
const char *text_save = text;
int last_cc = 1;
char token_buf[64];
int token_len = 0;
struct addr_exp_state s = {0};
s.last_operator = '(';
for (;;) {
int cc;
/* Figure out what class this character is */
if (*text == '+' || *text == '-' ||
*text == '*' || *text == '/' ||
*text == '%' || *text == '(' ||
*text == ')')
cc = 1;
else if (!*text || isspace(*text))
cc = 2;
else if (isalnum(*text) || *text == '.' || *text == '_' ||
*text == '$' || *text == ':')
cc = 3;
else {
fprintf(stderr, "illegal character in expression: %c\n",
*text);
return -1;
}
/* Accumulate and process token text */
if (cc == 3) {
if (token_len + 1 < sizeof(token_buf))
token_buf[token_len++] = *text;
} else if (token_len) {
token_buf[token_len] = 0;
token_len = 0;
if (addr_exp_data(&s, token_buf) < 0)
goto fail;
}
/* Process operators */
if (cc == 1) {
if (addr_exp_op(&s, *text) < 0)
goto fail;
}
if (!*text)
break;
last_cc = cc;
text++;
}
if (addr_exp_finish(&s, addr) < 0)
goto fail;
return 0;
fail:
fprintf(stderr, "bad address expression: %s\n", text_save);
return -1;
}
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;
}

100
util.h
View File

@ -16,106 +16,11 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef PARSE_H_
#define PARSE_H_
#ifndef UTIL_H_
#define UTIL_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);
/* Retrieve the next word from a pointer to the rest of a command
* argument buffer. Returns NULL if no more words.
*/
char *get_arg(char **text);
/* Process a command, returning -1 on error or 0 if executed
* successfully.
*
* The interactive argument specifies whether or not the command
* should be executed in an interactive context.
*/
int process_command(char *arg, int interactive);
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);
/* Parse an address expression, storing the result in the integer
* pointed to. Returns 0 if parsed successfully, -1 if not.
*/
typedef int (*token_func_t)(const char *text, int *value);
int addr_exp(const char *text, int *value);
/* 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);
@ -125,6 +30,7 @@ int open_serial(const char *device, int rate);
int read_with_timeout(int fd, u_int8_t *data, int len);
int write_all(int fd, const u_int8_t *data, int len);
void ctrlc_init(void);
void ctrlc_reset(void);
int ctrlc_check(void);