From ba6e3366ff1cd20920de8d2f788fe43752b34c9e Mon Sep 17 00:00:00 2001 From: Daniel Beer Date: Wed, 21 Apr 2010 16:56:56 +1200 Subject: [PATCH] Command processor is now an object. --- Makefile | 2 +- cproc.c | 604 +++++++++++++++++++++++++++++++++++++++++++++++++++ cproc.h | 129 +++++++++++ cproc_util.c | 302 ++++++++++++++++++++++++++ cproc_util.h | 36 +++ devcmd.c | 255 +++++++++------------- devcmd.h | 4 +- dis.c | 272 ++++------------------- dis.h | 13 +- gdb.c | 9 +- gdb.h | 4 +- main.c | 27 ++- parse.c | 545 ---------------------------------------------- parse.h | 110 ---------- rtools.c | 20 +- rtools.h | 4 +- sim.c | 1 - sym.c | 117 +++++----- sym.h | 4 +- util.c | 22 ++ util.h | 3 + vector.c | 2 +- vector.h | 2 +- 23 files changed, 1356 insertions(+), 1131 deletions(-) create mode 100644 cproc.c create mode 100644 cproc.h create mode 100644 cproc_util.c create mode 100644 cproc_util.h delete mode 100644 parse.c delete mode 100644 parse.h diff --git a/Makefile b/Makefile index 0bab559..3b267d1 100644 --- a/Makefile +++ b/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: diff --git a/cproc.c b/cproc.c new file mode 100644 index 0000000..f74bf43 --- /dev/null +++ b/cproc.c @@ -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 +#include +#include +#include +#include +#include + +#ifdef USE_READLINE +#include +#include +#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 \" 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 \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; +} diff --git a/cproc.h b/cproc.h new file mode 100644 index 0000000..9c2c6c3 --- /dev/null +++ b/cproc.h @@ -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 diff --git a/cproc_util.c b/cproc_util.c new file mode 100644 index 0000000..96e7322 --- /dev/null +++ b/cproc_util.c @@ -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 +#include + +#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); + } +} diff --git a/cproc_util.h b/cproc_util.h new file mode 100644 index 0000000..a6e5761 --- /dev/null +++ b/cproc_util.h @@ -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 +#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 diff --git a/devcmd.c b/devcmd.c index c581a03..e08fbb4 100644 --- a/devcmd.c +++ b/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
[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
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 \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
[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
\n" - " Save a region of memory into a HEX file.\n" -}; - static u_int8_t prog_buf[128]; static u_int16_t prog_addr; static int prog_len; @@ -589,12 +481,12 @@ static int prog_feed(u_int16_t addr, const u_int8_t *data, int len) return 0; } -static int cmd_prog(char **arg) +static int cmd_prog(cproc_t cp, char **arg) { FILE *in; int result = 0; - if (modify_prompt(MODIFY_SYMS)) + if (cproc_prompt_abort(cp, CPROC_MODIFY_SYMS)) return 0; in = fopen(*arg, "r"); @@ -630,30 +522,95 @@ static int cmd_prog(char **arg) return -1; } - modify_clear(MODIFY_SYMS); + cproc_unmodify(cp, CPROC_MODIFY_SYMS); return result; } -static struct command command_prog = { - .name = "prog", - .func = cmd_prog, - .help = - "prog \n" - " Erase the device and flash the data contained in a binary file.\n" - " This command also loads symbols from the file, if available.\n" +static const struct cproc_command commands[] = { + { + .name = "regs", + .func = cmd_regs, + .help = +"regs\n" +" Read and display the current register contents.\n" + }, + { + .name = "prog", + .func = cmd_prog, + .help = +"prog \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
[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
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 \n" +" Change the value of a CPU register.\n" + }, + { + .name = "dis", + .func = cmd_dis, + .help = +"dis
[length]\n" +" Disassemble a section of memory.\n" + }, + { + .name = "hexout", + .func = cmd_hexout, + .help = +"hexout
\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)); } diff --git a/devcmd.h b/devcmd.h index c5a3815..4505223 100644 --- a/devcmd.h +++ b/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 diff --git a/dis.c b/dis.c index 2fa404c..f89a7c8 100644 --- a/dis.c +++ b/dis.c @@ -17,12 +17,12 @@ */ #include +#include #include #include +#include -#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; } diff --git a/dis.h b/dis.h index 0d435bf..ad1cbbe 100644 --- a/dis.h +++ b/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 diff --git a/gdb.c b/gdb.c index fc1f39e..7ddc7b6 100644 --- a/gdb.c +++ b/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); } diff --git a/gdb.h b/gdb.h index 0d5ac1c..c020743 100644 --- a/gdb.h +++ b/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 diff --git a/main.c b/main.c index 7207a8a..fa430a9 100644 --- a/main.c +++ b/main.c @@ -24,7 +24,6 @@ #include #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(); diff --git a/parse.c b/parse.c deleted file mode 100644 index 16020ee..0000000 --- a/parse.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef USE_READLINE -#include -#include -#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 \" 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 \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; - } -} diff --git a/parse.h b/parse.h deleted file mode 100644 index 0f6346d..0000000 --- a/parse.h +++ /dev/null @@ -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 - -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 diff --git a/rtools.c b/rtools.c index e027aa3..5c04615 100644 --- a/rtools.c +++ b/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); } diff --git a/rtools.h b/rtools.h index f718ebd..6d5ff32 100644 --- a/rtools.h +++ b/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 diff --git a/sim.c b/sim.c index aa3593c..770bef0 100644 --- a/sim.c +++ b/sim.c @@ -24,7 +24,6 @@ #include "dis.h" #include "util.h" #include "stab.h" -#include "parse.h" #define MEM_SIZE 65536 diff --git a/sym.c b/sym.c index 44e086d..925f408 100644 --- a/sym.c +++ b/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 = - "= \n" - " Evaluate an expression using the symbol table.\n" -}; - -static int cmd_sym_load_add(int clear, char **arg) +static int cmd_sym_load_add(cproc_t cp, int clear, char **arg) { FILE *in; int result = 0; - if (clear && modify_prompt(MODIFY_SYMS)) + if (clear && cproc_prompt_abort(cp, CPROC_MODIFY_SYMS)) return 0; in = fopen(*arg, "r"); @@ -86,9 +78,9 @@ static int cmd_sym_load_add(int clear, char **arg) fclose(in); if (clear) - modify_clear(MODIFY_SYMS); + cproc_unmodify(cp, CPROC_MODIFY_SYMS); else - modify_set(MODIFY_SYMS); + cproc_modify(cp, CPROC_MODIFY_SYMS); return result; } @@ -105,7 +97,7 @@ static int savemap_cb(const char *name, u_int16_t value) return 0; } -static int cmd_sym_savemap(char **arg) +static int cmd_sym_savemap(cproc_t cp, char **arg) { char *fname = get_arg(arg); @@ -131,7 +123,7 @@ static int cmd_sym_savemap(char **arg) return -1; } - modify_clear(MODIFY_SYMS); + cproc_unmodify(cp, CPROC_MODIFY_SYMS); return 0; } @@ -151,7 +143,7 @@ static int find_sym(const char *name, u_int16_t value) return 0; } -static int cmd_sym_find(char **arg) +static int cmd_sym_find(cproc_t cp, char **arg) { char *expr = get_arg(arg); @@ -215,9 +207,6 @@ static int renames_do(const char *replace) count++; } - if (count) - modify_set(MODIFY_SYMS); - printf("%d symbols renamed\n", count); return 0; } @@ -241,7 +230,7 @@ static int find_renames(const char *name, u_int16_t value) return 0; } -static int cmd_sym_rename(char **arg) +static int cmd_sym_rename(cproc_t cp, char **arg) { const char *expr = get_arg(arg); const char *replace = get_arg(arg); @@ -269,10 +258,14 @@ static int cmd_sym_rename(char **arg) regfree(&find_preg); ret = renames_do(replace); vector_destroy(&renames_vec); - return ret; + + if (ret > 0) + cproc_modify(cp, CPROC_MODIFY_SYMS); + + return ret >= 0 ? 0 : -1; } -static int cmd_sym_del(char **arg) +static int cmd_sym_del(cproc_t cp, char **arg) { char *name = get_arg(arg); @@ -288,11 +281,11 @@ static int cmd_sym_del(char **arg) return -1; } - modify_set(MODIFY_SYMS); + cproc_modify(cp, CPROC_MODIFY_SYMS); return 0; } -static int cmd_sym(char **arg) +static int cmd_sym(cproc_t cp, char **arg) { char *subcmd = get_arg(arg); @@ -303,10 +296,10 @@ static int cmd_sym(char **arg) } if (!strcasecmp(subcmd, "clear")) { - if (modify_prompt(MODIFY_SYMS)) + if (cproc_prompt_abort(cp, CPROC_MODIFY_SYMS)) return 0; stab_clear(); - modify_clear(MODIFY_SYMS); + cproc_unmodify(cp, CPROC_MODIFY_SYMS); return 0; } @@ -330,53 +323,59 @@ static int cmd_sym(char **arg) if (stab_set(name, value) < 0) return -1; - modify_set(MODIFY_SYMS); + cproc_modify(cp, CPROC_MODIFY_SYMS); return 0; } if (!strcasecmp(subcmd, "del")) - return cmd_sym_del(arg); + return cmd_sym_del(cp, arg); if (!strcasecmp(subcmd, "import")) - return cmd_sym_load_add(1, arg); + return cmd_sym_load_add(cp, 1, arg); if (!strcasecmp(subcmd, "import+")) - return cmd_sym_load_add(0, arg); + return cmd_sym_load_add(cp, 0, arg); if (!strcasecmp(subcmd, "export")) - return cmd_sym_savemap(arg); + return cmd_sym_savemap(cp, arg); if (!strcasecmp(subcmd, "rename")) - return cmd_sym_rename(arg); + return cmd_sym_rename(cp, arg); if (!strcasecmp(subcmd, "find")) - return cmd_sym_find(arg); + return cmd_sym_find(cp, arg); fprintf(stderr, "sym: unknown subcommand: %s\n", subcmd); return -1; } -static struct command command_sym = { - .name = "sym", - .func = cmd_sym, - .help = - "sym clear\n" - " Clear the symbol table.\n" - "sym set \n" - " Set or overwrite the value of a symbol.\n" - "sym del \n" - " Delete a symbol from the symbol table.\n" - "sym import \n" - " Load symbols from the given file.\n" - "sym import+ \n" - " Load additional symbols from the given file.\n" - "sym export \n" - " Save the current symbols to a BSD-style symbol file.\n" - "sym find \n" - " Search for symbols by regular expression.\n" - "sym rename \n" - " Replace every occurance of a pattern in symbol names.\n" +static const struct cproc_command commands[] = { + { + .name = "=", + .func = cmd_eval, + .help = +"= \n" +" Evaluate an expression using the symbol table.\n" + }, + { + .name = "sym", + .func = cmd_sym, + .help = +"sym clear\n" +" Clear the symbol table.\n" +"sym set \n" +" Set or overwrite the value of a symbol.\n" +"sym del \n" +" Delete a symbol from the symbol table.\n" +"sym import \n" +" Load symbols from the given file.\n" +"sym import+ \n" +" Load additional symbols from the given file.\n" +"sym export \n" +" Save the current symbols to a BSD-style symbol file.\n" +"sym find \n" +" Search for symbols by regular expression.\n" +"sym rename \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)); } diff --git a/sym.h b/sym.h index 5561709..cd7abe9 100644 --- a/sym.h +++ b/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 diff --git a/util.c b/util.c index 27d1c26..beefbe1 100644 --- a/util.c +++ b/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; +} diff --git a/util.h b/util.h index 3573ebd..fc6b644 100644 --- a/util.h +++ b/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 diff --git a/vector.c b/vector.c index 9786d30..c748a0c 100644 --- a/vector.c +++ b/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; diff --git a/vector.h b/vector.h index 5114620..a1ed0d7 100644 --- a/vector.h +++ b/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: