diff --git a/Makefile b/Makefile index e8dc5ef..63d5f61 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ install: mspdebug mspdebug.man .SUFFIXES: .c .o mspdebug: main.o fet.o rf2500.o dis.o uif.o ihex.o elf32.o stab.o util.o \ - bsl.o sim.o symmap.o gdb.o btree.o + bsl.o sim.o symmap.o gdb.o btree.o parse.o $(CC) $(LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS) .c.o: diff --git a/dis.c b/dis.c index 8c3d98e..ba73bc9 100644 --- a/dis.c +++ b/dis.c @@ -23,6 +23,7 @@ #include "dis.h" #include "stab.h" #include "util.h" +#include "parse.h" /**********************************************************************/ /* Disassembler @@ -528,107 +529,211 @@ static const char *const msp430_reg_names[] = { "R12", "R13", "R14", "R15" }; -/* Given an operands addressing mode, value and associated register, - * print the canonical representation of it to stdout. - * - * Returns the number of characters printed. - */ -static int format_operand(char *buf, int max_len, - msp430_amode_t amode, u_int16_t addr, - msp430_reg_t reg) +static int format_addr(msp430_amode_t amode, u_int16_t addr) { - const char *prefix = ""; - const char *suffix = ""; - char rbuf[32]; char name[64]; u_int16_t offset; int numeric = 0; - - assert (reg >= 0 && reg < ARRAY_LEN(msp430_reg_names)); + int len; + int count = 0; + const char *prefix = ""; switch (amode) { - case MSP430_AMODE_REGISTER: - return snprintf(buf, max_len, "%s", msp430_reg_names[reg]); - - case MSP430_AMODE_INDEXED: - snprintf(rbuf, sizeof(rbuf), "(%s)", msp430_reg_names[reg]); - suffix = rbuf; - numeric = 1; - break; - case MSP430_AMODE_SYMBOLIC: + case MSP430_AMODE_REGISTER: + case MSP430_AMODE_INDIRECT: + case MSP430_AMODE_INDIRECT_INC: + return 0; + + case MSP430_AMODE_IMMEDIATE: + prefix = "#"; + case MSP430_AMODE_INDEXED: + numeric = 1; break; case MSP430_AMODE_ABSOLUTE: prefix = "&"; break; - - case MSP430_AMODE_INDIRECT: - return snprintf(buf, max_len, "@%s", msp430_reg_names[reg]); - - case MSP430_AMODE_INDIRECT_INC: - return snprintf(buf, max_len, "@%s+", msp430_reg_names[reg]); - - case MSP430_AMODE_IMMEDIATE: - prefix = "#"; - numeric = 1; - break; - - default: - return snprintf(buf, max_len, "???"); } + len = printf("%s", prefix); + if (len >= 0) + count += len; + if ((!numeric || (addr >= 0x200 && addr < 0xfff0)) && !stab_nearest(addr, name, sizeof(name), &offset) && - !offset) - return snprintf(buf, max_len, "%s%s%s", - prefix, name, suffix); + !offset) { + colorize("1m"); + len = printf("%s", name); + colorize("0m"); + } else { + colorize("32m"); + len = printf(numeric ? "0x%x" : "0x%04x", addr); + colorize("0m"); + } - return snprintf(buf, max_len, - numeric ? "%s0x%x%s" : "%s0x%04x%s", - prefix, addr, suffix); + if (len >= 0) + count += len; + + return count; +} + +static int format_reg(msp430_amode_t amode, msp430_reg_t reg) +{ + const char *prefix = ""; + const char *suffix = ""; + int len; + int count = 0; + + switch (amode) { + case MSP430_AMODE_REGISTER: + break; + + case MSP430_AMODE_INDEXED: + prefix = "("; + suffix = ")"; + break; + + case MSP430_AMODE_IMMEDIATE: + case MSP430_AMODE_SYMBOLIC: + case MSP430_AMODE_ABSOLUTE: + return 0; + + case MSP430_AMODE_INDIRECT_INC: + suffix = "+"; + case MSP430_AMODE_INDIRECT: + prefix = "@"; + break; + } + + assert (reg >= 0 && reg < ARRAY_LEN(msp430_reg_names)); + + len = printf("%s", prefix); + if (len >= 0) + count += len; + colorize("33m"); + len = printf("%s", msp430_reg_names[reg]); + colorize("0m"); + if (len >= 0) + count += len; + len = printf("%s", suffix); + if (len >= 0) + count += len; + + return count; +} + +/* Given an operands addressing mode, value and associated register, + * print the canonical representation of it to stdout. + * + * Returns the number of characters printed. + */ +static int format_operand(msp430_amode_t amode, u_int16_t addr, + msp430_reg_t reg) +{ + int len; + int count = 0; + + len = format_addr(amode, addr); + if (len >= 0) + count += len; + + len = format_reg(amode, reg); + if (len >= 0) + count += len; + + return count; } /* Write assembly language for the instruction to this buffer */ -int dis_format(char *buf, int max_len, - const struct msp430_instruction *insn) +static void dis_format(const struct msp430_instruction *insn) { int count = 0; + int len; - /* Opcode mnemonic */ - count = snprintf(buf, max_len, "%s", msp_op_name(insn->op)); - if (insn->is_byte_op) - count += snprintf(buf + count, max_len - count, ".B"); - while (count < 8 && count + 1 < max_len) - buf[count++] = ' '; + colorize("36m"); + len = printf("%s%s", msp_op_name(insn->op), + insn->is_byte_op ? ".B" : ""); + colorize("0m"); + if (len >= 0) + count += len; + while (count < 8) { + count++; + printf(" "); + } /* Source operand */ if (insn->itype == MSP430_ITYPE_DOUBLE) { - count += format_operand(buf + count, - max_len - count, - insn->src_mode, - insn->src_addr, - insn->src_reg); + len = format_operand(insn->src_mode, + insn->src_addr, + insn->src_reg); + if (len >= 0) + count += len; - if (count + 1 < max_len) - buf[count++] = ','; - - while (count < 19 && count + 1 < max_len) - buf[count++] = ' '; - - if (count + 1 < max_len) - buf[count++] = ' '; + printf(","); + count++; + while (count < 19) { + count++; + printf(" "); + } + printf(" "); + count++; } /* Destination operand */ if (insn->itype != MSP430_ITYPE_NOARG) - count += format_operand(buf + count, - max_len - count, - insn->dst_mode, - insn->dst_addr, - insn->dst_reg); - - buf[count] = 0; - return count; + format_operand(insn->dst_mode, + insn->dst_addr, + insn->dst_reg); +} + +void disassemble(u_int16_t offset, u_int8_t *data, int length) +{ + int first_line = 1; + + while (length) { + struct msp430_instruction insn; + int retval; + int count; + int i; + u_int16_t oboff; + char obname[64]; + + if (!stab_nearest(offset, obname, sizeof(obname), &oboff)) { + colorize("1m"); + if (!oboff) + printf("%s:\n", obname); + else if (first_line) + printf("%s+0x%x:\n", obname, oboff); + colorize("0m"); + } + first_line = 0; + + retval = dis_decode(data, offset, length, &insn); + count = retval > 0 ? retval : 2; + if (count > length) + count = length; + + colorize("36m"); + printf(" %04x:", offset); + colorize("0m"); + + for (i = 0; i < count; i++) + printf(" %02x", data[i]); + + while (i < 7) { + printf(" "); + i++; + } + + if (retval >= 0) + dis_format(&insn); + + printf("\n"); + + offset += count; + length -= count; + data += count; + } } diff --git a/dis.h b/dis.h index c2c549a..89cdf9e 100644 --- a/dis.h +++ b/dis.h @@ -205,8 +205,7 @@ struct msp430_instruction { int dis_decode(u_int8_t *code, u_int16_t offset, u_int16_t len, struct msp430_instruction *insn); -/* Write assembly language for the instruction to this buffer */ -int dis_format(char *buf, int max_len, - const struct msp430_instruction *insn); +/* Print a disassembly on stdout */ +void disassemble(u_int16_t offset, u_int8_t *buf, int length); #endif diff --git a/main.c b/main.c index b45f8e8..77e279e 100644 --- a/main.c +++ b/main.c @@ -23,17 +23,13 @@ #include #include -#ifdef USE_READLINE -#include -#include -#endif - #include "dis.h" #include "device.h" #include "binfile.h" #include "stab.h" #include "util.h" #include "gdb.h" +#include "parse.h" static const struct device *msp430_dev; @@ -42,13 +38,12 @@ static const struct device *msp430_dev; */ static int syms_are_modified; -static int is_interactive; -static int syms_modify_check(void) +static int syms_modify_check(voiv) { char buf[32]; - if (!syms_are_modified || !is_interactive) + if (!syms_are_modified) return 0; for (;;) { @@ -72,80 +67,6 @@ static int syms_modify_check(void) return 0; } -/********************************************************************** - * Command-line interface - */ - -char *get_arg(char **text) -{ - char *start; - char *end; - - if (!text) - return NULL; - - start = *text; - while (*start && isspace(*start)) - start++; - - if (!*start) - return NULL; - - end = start; - while (*end && !isspace(*end)) - end++; - - if (*end) - while (*end && isspace(*end)) - *(end++) = 0; - - *text = end; - return start; -} - -struct command { - const char *name; - int (*func)(char **arg); - const char *help; -}; - -static const struct command all_commands[]; - -const struct command *find_command(const char *name) -{ - int i; - - for (i = 0; all_commands[i].name; i++) - if (!strcasecmp(name, all_commands[i].name)) - return &all_commands[i]; - - return NULL; -} - -static int process_command(char *arg) -{ - const char *cmd_text; - int len = strlen(arg); - - while (len && isspace(arg[len - 1])) - len--; - arg[len] = 0; - - cmd_text = get_arg(&arg); - if (cmd_text) { - const struct command *cmd = find_command(cmd_text); - - if (cmd) - return cmd->func(&arg); - - fprintf(stderr, "unknown command: %s (try \"help\")\n", - cmd_text); - return -1; - } - - return 0; -} - /************************************************************************ * Command definitions */ @@ -183,13 +104,13 @@ static int cmd_md(char **arg) return -1; } - if (stab_parse(off_text, &offset) < 0) { + if (addr_exp(off_text, &offset) < 0) { fprintf(stderr, "md: can't parse offset: %s\n", off_text); return -1; } if (len_text) { - if (stab_parse(len_text, &length) < 0) { + if (addr_exp(len_text, &length) < 0) { fprintf(stderr, "md: can't parse length: %s\n", len_text); return -1; @@ -231,7 +152,7 @@ static int cmd_mw(char **arg) return -1; } - if (stab_parse(off_text, &offset) < 0) { + if (addr_exp(off_text, &offset) < 0) { fprintf(stderr, "md: can't parse offset: %s\n", off_text); return -1; } @@ -259,55 +180,6 @@ static int cmd_mw(char **arg) return 0; } -static void disassemble(u_int16_t offset, u_int8_t *data, int length) -{ - int first_line = 1; - - while (length) { - struct msp430_instruction insn; - int retval; - int count; - int i; - u_int16_t oboff; - char obname[64]; - - if (!stab_nearest(offset, obname, sizeof(obname), &oboff)) { - if (!oboff) - printf("%s:\n", obname); - else if (first_line) - printf("%s+0x%x:\n", obname, oboff); - } - first_line = 0; - - retval = dis_decode(data, offset, length, &insn); - count = retval > 0 ? retval : 2; - if (count > length) - count = length; - printf(" %04x:", offset); - - for (i = 0; i < count; i++) - printf(" %02x", data[i]); - - while (i < 7) { - printf(" "); - i++; - } - - if (retval >= 0) { - char buf[128]; - - dis_format(buf, sizeof(buf), &insn); - printf("%s", buf); - } - - printf("\n"); - - offset += count; - length -= count; - data += count; - } -} - static int cmd_dis(char **arg) { char *off_text = get_arg(arg); @@ -321,13 +193,13 @@ static int cmd_dis(char **arg) return -1; } - if (stab_parse(off_text, &offset) < 0) { + if (addr_exp(off_text, &offset) < 0) { fprintf(stderr, "dis: can't parse offset: %s\n", off_text); return -1; } if (len_text) { - if (stab_parse(len_text, &length) < 0) { + if (addr_exp(len_text, &length) < 0) { fprintf(stderr, "dis: can't parse length: %s\n", len_text); return -1; @@ -437,8 +309,8 @@ static int cmd_hexout(char **arg) return -1; } - if (stab_parse(off_text, &off) < 0 || - stab_parse(len_text, &length) < 0) + if (addr_exp(off_text, &off) < 0 || + addr_exp(len_text, &length) < 0) return -1; if (hexout_start(filename) < 0) @@ -508,7 +380,7 @@ static int cmd_run(char **arg) device_status_t status; if (bp_text) { - if (stab_parse(bp_text, &bp_addr) < 0) { + if (addr_exp(bp_text, &bp_addr) < 0) { fprintf(stderr, "run: can't parse breakpoint: %s\n", bp_text); return -1; @@ -554,7 +426,7 @@ static int cmd_set(char **arg) reg_text++; reg = atoi(reg_text); - if (stab_parse(val_text, &value) < 0) { + if (addr_exp(val_text, &value) < 0) { fprintf(stderr, "set: can't parse value: %s\n", val_text); return -1; } @@ -676,7 +548,7 @@ static int cmd_prog(char **arg) FILE *in; int result = 0; - if (syms_modify_check()) + if (is_interactive() && syms_modify_check()) return 0; in = fopen(*arg, "r"); @@ -722,7 +594,7 @@ static int cmd_eval(char **arg) u_int16_t offset; char name[64]; - if (stab_parse(*arg, &addr) < 0) { + if (addr_exp(*arg, &addr) < 0) { fprintf(stderr, "=: can't parse: %s\n", *arg); return -1; } @@ -743,7 +615,7 @@ static int cmd_sym_load_add(int clear, char **arg) FILE *in; int result = 0; - if (clear && syms_modify_check()) + if (clear && is_interactive() && syms_modify_check()) return 0; in = fopen(*arg, "r"); @@ -827,7 +699,7 @@ static int cmd_sym(char **arg) } if (!strcasecmp(subcmd, "clear")) { - if (syms_modify_check()) + if (is_interactive() && syms_modify_check()) return 0; stab_clear(); syms_are_modified = 0; @@ -845,7 +717,7 @@ static int cmd_sym(char **arg) return -1; } - if (stab_parse(val_text, &value) < 0) { + if (addr_exp(val_text, &value) < 0) { fprintf(stderr, "sym: can't parse value: %s\n", val_text); return -1; @@ -913,70 +785,11 @@ static int cmd_gdb(char **arg) return gdb_server(msp430_dev, port); } -static int cmd_help(char **arg) -{ - char *topic = get_arg(arg); - - if (topic) { - const struct command *cmd = find_command(topic); - - if (!cmd) { - fprintf(stderr, "help: unknown command: %s\n", topic); - return -1; - } - - fputs(cmd->help, stdout); - } else { - int i; - int max_len = 0; - int rows, cols; - int total = 0; - - for (i = 0; all_commands[i].name; i++) { - int len = strlen(all_commands[i].name); - - if (len > max_len) - max_len = len; - total++; - } - - max_len += 2; - cols = 72 / max_len; - rows = (total + cols - 1) / cols; - - printf("Available commands:\n"); - for (i = 0; i < rows; i++) { - int j; - - printf(" "); - for (j = 0; j < cols; j++) { - int k = j * rows + i; - const struct command *cmd = &all_commands[k]; - - if (k >= total) - break; - - printf("%s", cmd->name); - for (k = strlen(cmd->name); k < max_len; k++) - printf(" "); - } - - printf("\n"); - } - - printf("Type \"help \" for more information.\n"); - printf("Press Ctrl+D to quit.\n"); - } - - return 0; -} - static int cmd_read(char **arg) { char *filename = get_arg(arg); FILE *in; char buf[1024]; - int was_interactive = is_interactive; if (!filename) { fprintf(stderr, "read: filename must be specified\n"); @@ -990,7 +803,6 @@ static int cmd_read(char **arg) return -1; } - is_interactive = 0; while (fgets(buf, sizeof(buf), in)) { char *cmd = buf; @@ -1000,21 +812,19 @@ static int cmd_read(char **arg) if (*cmd == '#') continue; - if (process_command(cmd) < 0) { + if (process_command(cmd, 0) < 0) { fprintf(stderr, "read: error processing %s\n", filename); fclose(in); - is_interactive = was_interactive; return -1; } } - is_interactive = was_interactive; fclose(in); return 0; } -static const struct command all_commands[] = { +const struct command all_commands[] = { {"=", cmd_eval, "= \n" " Evaluate an expression using the symbol table.\n"}, @@ -1042,6 +852,10 @@ static const struct command all_commands[] = { "mw
bytes ...\n" " Write a sequence of bytes to a memory address. Byte values are\n" " two-digit hexadecimal numbers.\n"}, + {"opt", cmd_opt, + "opt [name] [value]\n" + " Query or set option variables. With no arguments, displays all\n" + " available options.\n"}, {"prog", cmd_prog, "prog \n" " Erase the device and flash the data contained in a binary file.\n" @@ -1111,59 +925,6 @@ static void usage(const char *progname) progname, progname, progname, progname); } -#ifndef USE_READLINE -#define LINE_BUF_SIZE 128 - -static char *readline(const char *prompt) -{ - char *buf = malloc(LINE_BUF_SIZE); - - if (!buf) { - perror("readline: can't allocate memory"); - return NULL; - } - - for (;;) { - printf("(mspdebug) "); - fflush(stdout); - - if (fgets(buf, LINE_BUF_SIZE, stdin)) - return buf; - - if (feof(stdin)) - break; - - printf("\n"); - } - - free(buf); - return NULL; -} - -#define add_history(x) -#endif - -static void reader_loop(void) -{ - printf("\n"); - cmd_help(NULL); - is_interactive = 1; - - for (;;) { - char *buf = readline("(mspdebug) "); - - if (buf) { - add_history(buf); - process_command(buf); - free(buf); - } else if (!syms_modify_check()) { - break; - } - } - - printf("\n"); -} - #define MODE_RF2500 0x01 #define MODE_UIF 0x02 #define MODE_UIF_BSL 0x04 @@ -1238,6 +999,7 @@ int main(int argc, char **argv) return -1; } + parse_init(); ctrlc_init(); if (stab_init() < 0) return -1; @@ -1272,9 +1034,11 @@ int main(int argc, char **argv) /* Process commands */ if (optind < argc) { while (optind < argc) - process_command(argv[optind++]); + process_command(argv[optind++], 0); } else { - reader_loop(); + do { + reader_loop(); + } while (syms_modify_check()); } msp430_dev->close(); diff --git a/mspdebug.man b/mspdebug.man index b5f9b47..889996d 100644 --- a/mspdebug.man +++ b/mspdebug.man @@ -79,7 +79,7 @@ table formats are still usable in this mode). MSPDebug can accept commands either through an interactive prompt, or non-interactively when specified on the command line. The supported commands are listed below. -.IP "= \fIexpression\fR" +.IP "\fB=\fR \fIexpression\fR" Evaluate an address expression and show both its value, and the result when the value is looked up in reverse in the current symbol table. This result is of the form \fIsymbol\fR+\fIoffset\fR, where @@ -88,28 +88,28 @@ question. See the section marked \fBADDRESS EXPRESSIONS\fR for more information on the syntax of expressions. -.IP "dis \fIaddress\fR [\fIlength\fR]" +.IP "\fBdis\fR \fIaddress\fR [\fIlength\fR]" Dissassemble a section of memory. Both arguments may be address expressions. If no length is specified, a section of the default length (64 bytes) is disassembled and shown. If symbols are available, then all addresses used as operands are translated into \fIsymbol\fR+\fIoffset\fR form. -.IP "erase" +.IP "\fBerase\fR" Erase the device under test. All code memory is erased (but not information or boot memory). -.IP "gdb [\fIport\fR]" +.IP "\fBgdb\fR [\fIport\fR]" Start a GDB remote stub, optionally specifying a TCP port to listen on. If no port is given, the default port is 2000. MSPDebug will wait for a connection on this port, and then act as a GDB remote stub until GDB disconnects. The basic GDB protocol is supported, plus the monitor commands "erase" and "reset". -.IP "help [\fIcommand\fR]" +.IP "\fBhelp\fR [\fIcommand\fR]" Show a brief listing of available commands. If an argument is specified, show the syntax for the given command. The help text shown when no argument is given is also shown when MSPDebug starts up. -.IP "hexout \fIaddress\fR \fIlength\fR \fIfilename\fR" +.IP "\fBhexout\fR \fIaddress\fR \fIlength\fR \fIfilename\fR" Read the specified section of the device memory and save it to an Intel HEX file. The address and length arguments may both be address expressions. @@ -118,7 +118,7 @@ If the specified file already exists, then it will be overwritten. If you need to dump memory from several disjoint memory regions, you can do this by saving each section to a separate file. The resulting files can then be concatenated together to form a single valid HEX file. -.IP "md \fIaddress\fR [\fIlength\fR]" +.IP "\fBmd\fR \fIaddress\fR [\fIlength\fR]" Read the specified section of device memory and display it as a canonical\-style hexdump. Both arguments may be address expressions. If no length is specified, a section of the default length (64 bytes) is @@ -128,14 +128,24 @@ The output is split into three columns. The first column shows the starting address for the line. The second column lists the hexadecimal values of the bytes. The final column shows the ASCII characters corresponding to printable bytes, and . for non-printing characters. -.IP "mw \fIaddress\fR \fIbytes\fR ..." +.IP "\fBmw\fR \fIaddress\fR \fIbytes\fR ..." Write a sequence of bytes at the given memory address. The address given may be an address expression. Bytes values are two-digit hexadecimal numbers separated by spaces. Unless used in the simulation mode, this command can only be used for programming flash memory. -.IP "prog \fIfilename\fR" +.IP "\fBopt\fR [\fIname\fR] [\fIvalue\fR]" +Query, set or list option variables. MSPDebug's behaviour can be configured +using option variables, described below in the section \fBOPTIONS\fR. + +Option variables may be of three types: boolean, numeric or text. Numeric +values may be specified as address expressions. + +With no arguments, this command displays all available option variables. +With just an option name as its argument, it displays the current value +of that option. +.IP "\fBprog\fR \fIfilename\fR" Erase and reprogram the device under test using the binary file supplied. The file format will be auto-detected and may be either Intel HEX or ELF32. @@ -145,16 +155,16 @@ from the file into the symbol table (discarding any existing symbols), if they are present. The CPU is reset and halted before and after programming. -.IP "read \fIfilename\fR" +.IP "\fBread\fR \fIfilename\fR" Read commands from the given file, line by line and process each one. Any lines whose first non-space character is \fB#\fR are ignored. If an error occurs while processing a command, the rest of the file is not processed. -.IP "regs" +.IP "\fBregs\fR" Show the current value of all CPU registers in the device under test. -.IP "reset" +.IP "\fBreset\fR" Reset (and halt) the CPU of the device under test. -.IP "run [\fIbreakpoint\fR]" +.IP "\fBrun\fR [\fIbreakpoint\fR]" Run the CPU, optionally specifying a breakpoint. The breakpoint can be specified as an address expression. @@ -166,41 +176,41 @@ the user. After the CPU halts, the current register values are shown as well as a disassembly of the first few instructions at the address selected by the program counter. -.IP "set \fIregister\fR \fIvalue\fR" +.IP "\fBset\fR \fIregister\fR \fIvalue\fR" Alter the value of a register. Registers are specified as numbers from 0 through 15. Any leading non-numeric characters are ignored (so a register may be specified as, for example, "R12"). The value argument is an address expression. -.IP "step [\fIcount\fR]" +.IP "\fBstep\fR [\fIcount\fR]" Step the CPU through one or more instructions. After stepping, the new register values are displayed, as well as a disassembly of the instructions at the address selected by the program counter. An optional count can be specified to step multiple times. If no argument is given, the CPU steps once. -.IP "sym clear" +.IP "\fBsym clear\fR" Clear the symbol table, deleting all symbols. -.IP "sym set \fIname\fR \fIvalue\fR" +.IP "\fBsym set\fR \fIname\fR \fIvalue\fR" Set or alter the value of a symbol. The value given may be an address expression. -.IP "sym del \fIname\fR" +.IP "\fBsym del\fR \fIname\fR" Delete the given symbol from the symbol table. -.IP "sym load \fIfilename\fR" +.IP "\fBsym load\fR \fIfilename\fR" Load symbols from the specified file and add them to the symbol table. The file format will be auto-detected and may be either ELF32 or a BSD-style symbol listing (like the output from \fBnm\fR(1)). Symbols can be combined from many sources, as the syms command adds to the existing symbol table without discarding existing symbols. -.IP "sym add \fIfilename\fR" +.IP "\fBsym add\fR \fIfilename\fR" This command is similar to \fBsym load\fR, except that the symbol table is not cleared first. By using this command, symbols from multiple sources can be combined. -.IP "sym savemap \fIfilename\fR" +.IP "\fBsym savemap\fR \fIfilename\fR" Save all symbols currently defined to the given file. The symbols are saved as a BSD-style symbol table. Note that symbol types are not stored by MSPDebug, and all symbols are saved as type \fBt\fR. -.IP "sym find [\fIregex\fR]" +.IP "\fBsym find\fR [\fIregex\fR]" Search for symbols. If a regular expression is given, then all symbols matching the expression are printed. If no expression is specified, then the entire symbol table is listed. @@ -224,6 +234,10 @@ The following are all valid examples of address expressions: .B __bss_end-__bss_start .SH SEE ALSO \fBnm\fR(1), \fBgdb\fR(1), \fBobjcopy\fR(1) +.SH OPTIONS +MSPDebug's behaviour can be configured via the following variables: +.IP "\fBcolor\fR (boolean)" +If true, MSPDebug will colorize disassembly output. .SH BUGS If you find any bugs, you should report them to the author at daniel@tortek.co.nz. It would help if you could include a transcript diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..55fa6dd --- /dev/null +++ b/parse.c @@ -0,0 +1,428 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009, 2010 Daniel Beer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#ifdef USE_READLINE +#include +#include +#endif + +#include "stab.h" +#include "parse.h" + +static struct option *option_list; + +void register_option(struct option *o) +{ + o->next = option_list; + option_list = o; +} + +static struct option *find_option(const char *name) +{ + struct option *o; + + for (o = option_list; o; o = o->next) + if (!strcasecmp(o->name, name)) + return o; + + return NULL; +} + +static int interactive_call; + +int is_interactive(void) +{ + return interactive_call; +} + +char *get_arg(char **text) +{ + char *start; + char *end; + + if (!text) + return NULL; + + start = *text; + while (*start && isspace(*start)) + start++; + + if (!*start) + return NULL; + + end = start; + while (*end && !isspace(*end)) + end++; + + if (*end) + while (*end && isspace(*end)) + *(end++) = 0; + + *text = end; + return start; +} + +const struct command *find_command(const char *name) +{ + int i; + + for (i = 0; all_commands[i].name; i++) + if (!strcasecmp(name, all_commands[i].name)) + return &all_commands[i]; + + return NULL; +} + +int process_command(char *arg, int interactive) +{ + const char *cmd_text; + int len = strlen(arg); + + while (len && isspace(arg[len - 1])) + len--; + arg[len] = 0; + + cmd_text = get_arg(&arg); + if (cmd_text) { + const struct command *cmd = find_command(cmd_text); + + if (cmd) { + int old = interactive_call; + int ret; + + interactive_call = interactive; + ret = cmd->func(&arg); + interactive_call = old; + return 0; + } + + fprintf(stderr, "unknown command: %s (try \"help\")\n", + cmd_text); + return -1; + } + + return 0; +} + +#ifndef USE_READLINE +#define LINE_BUF_SIZE 128 + +static char *readline(const char *prompt) +{ + char *buf = malloc(LINE_BUF_SIZE); + + if (!buf) { + perror("readline: can't allocate memory"); + return NULL; + } + + for (;;) { + printf("(mspdebug) "); + fflush(stdout); + + if (fgets(buf, LINE_BUF_SIZE, stdin)) + return buf; + + if (feof(stdin)) + break; + + printf("\n"); + } + + free(buf); + return NULL; +} + +#define add_history(x) +#endif + +void reader_loop(void) +{ + printf("\n"); + cmd_help(NULL); + + for (;;) { + char *buf = readline("(mspdebug) "); + + if (!buf) + break; + + add_history(buf); + process_command(buf, 1); + free(buf); + } + + printf("\n"); +} + +const char *type_text(option_type_t type) +{ + switch (type) { + case OPTION_BOOLEAN: + return "boolean"; + + case OPTION_NUMERIC: + return "numeric"; + + case OPTION_TEXT: + return "text"; + } + + return "unknown"; +} + +int cmd_help(char **arg) +{ + char *topic = get_arg(arg); + + if (topic) { + const struct command *cmd = find_command(topic); + const struct option *opt = find_option(topic); + + if (cmd) { + printf("COMMAND: %s\n", cmd->name); + fputs(cmd->help, stdout); + if (opt) + printf("\n"); + } + + if (opt) { + printf("OPTION: %s (%s)\n", opt->name, + type_text(opt->type)); + fputs(opt->help, stdout); + } + + if (!(cmd || opt)) { + fprintf(stderr, "help: unknown command: %s\n", topic); + return -1; + } + } else { + int i; + int max_len = 0; + int rows, cols; + int total = 0; + + for (i = 0; all_commands[i].name; i++) { + int len = strlen(all_commands[i].name); + + if (len > max_len) + max_len = len; + total++; + } + + max_len += 2; + cols = 72 / max_len; + rows = (total + cols - 1) / cols; + + printf("Available commands:\n"); + for (i = 0; i < rows; i++) { + int j; + + printf(" "); + for (j = 0; j < cols; j++) { + int k = j * rows + i; + const struct command *cmd = &all_commands[k]; + + if (k >= total) + break; + + printf("%s", cmd->name); + for (k = strlen(cmd->name); k < max_len; k++) + printf(" "); + } + + printf("\n"); + } + + printf("Type \"help \" for more information.\n"); + printf("Press Ctrl+D to quit.\n"); + } + + return 0; +} + +static char token_buf[64]; +static int token_len; +static int token_mult; +static int token_sum; + +static int token_add(void) +{ + int i; + u_int16_t value; + + if (!token_len) + return 0; + + token_buf[token_len] = 0; + token_len = 0; + + /* Is it a decimal? */ + i = 0; + while (token_buf[i] && isdigit(token_buf[i])) + i++; + if (!token_buf[i]) { + token_sum += token_mult * atoi(token_buf); + return 0; + } + + /* Is it hex? */ + if (token_buf[0] == '0' && tolower(token_buf[1]) == 'x') { + token_sum += token_mult * strtol(token_buf + 2, NULL, 16); + return 0; + } + + /* Look up the name in the symbol table */ + if (!stab_get(token_buf, &value)) { + token_sum += token_mult * value; + return 0; + } + + fprintf(stderr, "unknown token: %s\n", token_buf); + return -1; +} + +int addr_exp(const char *text, int *addr) +{ + token_len = 0; + token_mult = 1; + token_sum = 0; + + while (*text) { + if (isalnum(*text) || *text == '_' || *text == '$' || + *text == '.' || *text == ':') { + if (token_len + 1 < sizeof(token_buf)) + token_buf[token_len++] = *text; + } else { + if (token_add() < 0) + return -1; + if (*text == '+') + token_mult = 1; + if (*text == '-') + token_mult = -1; + } + + text++; + } + + if (token_add() < 0) + return -1; + + *addr = token_sum & 0xffff; + return 0; +} + +static void display_option(const struct option *o) +{ + printf("%32s = ", o->name); + + switch (o->type) { + case OPTION_BOOLEAN: + printf("%s", o->data.numeric ? "true" : "false"); + break; + + case OPTION_NUMERIC: + printf("0x%x (%d)", o->data.numeric, + o->data.numeric); + break; + + case OPTION_TEXT: + printf("%s", o->data.text); + break; + } + + printf("\n"); +} + +static int parse_option(struct option *o, const char *word) +{ + switch (o->type) { + case OPTION_BOOLEAN: + o->data.numeric = (isdigit(word[0]) && word[0] > '0') || + word[0] == 't' || word[0] == 'y' || + (word[0] == 'o' && word[1] == 'n'); + break; + + case OPTION_NUMERIC: + return addr_exp(word, &o->data.numeric); + + case OPTION_TEXT: + strncpy(o->data.text, word, sizeof(o->data.text)); + o->data.text[sizeof(o->data.text) - 1] = 0; + break; + } + + return 0; +} + +int cmd_opt(char **arg) +{ + const char *opt_text = get_arg(arg); + struct option *opt = NULL; + + if (opt_text) { + opt = find_option(opt_text); + if (!opt) { + fprintf(stderr, "opt: no such option: %s\n", + opt_text); + return -1; + } + } + + if (**arg) { + if (parse_option(opt, *arg) < 0) { + fprintf(stderr, "opt: can't parse option: %s\n", + *arg); + return -1; + } + } else if (opt_text) { + display_option(opt); + } else { + struct option *o; + + for (o = option_list; o; o = o->next) + display_option(o); + } + + return 0; +} + +static struct option option_color = { + .name = "color", + .type = OPTION_BOOLEAN, + .help = "Colorize disassembly output.\n" +}; + +int colorize(const char *text) +{ + if (!option_color.data.numeric) + return 0; + + return printf("\x1b[%s", text); +} + +void parse_init(void) +{ + register_option(&option_color); +} diff --git a/parse.h b/parse.h new file mode 100644 index 0000000..301089f --- /dev/null +++ b/parse.h @@ -0,0 +1,109 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009, 2010 Daniel Beer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PARSE_H_ +#define PARSE_H_ + +typedef int (*command_func_t)(char **arg); + +struct command { + const char *name; + int (*func)(char **arg); + const char *help; +}; + +/* The global command table, defined in main.c */ +extern const struct command all_commands[]; + +/* Retrieve the next word from a pointer to the rest of a command + * argument buffer. Returns NULL if no more words. + */ +char *get_arg(char **text); + +/* Process a command, returning -1 on error or 0 if executed + * successfully. + * + * The interactive argument specifies whether or not the command + * should be executed in an interactive context. + */ +int process_command(char *arg, int interactive); + +/* Run the reader loop, exiting when the user presses Ctrl+D. + * + * Commands executed by the reader loop are executed in interactive + * context. + */ +void reader_loop(void); + +/* Help command. Displays a command list with no argument, or help + * for a particular command. + */ +int cmd_help(char **arg); + +/* Colourized output has been requested by the user. */ +int colorize(const char *text); + +/* Return non-zero if executing in an interactive context. */ +int is_interactive(void); + +/* Parse an address expression, storing the result in the integer + * pointed to. Returns 0 if parsed successfully, -1 if not. + */ +int addr_exp(const char *text, int *value); + +/* Options interface. Options may be declared by any module and + * registered with the parser. + * + * They can then be manipulated by the "set" command (function + * declared below. + */ + +typedef enum { + OPTION_BOOLEAN, + OPTION_NUMERIC, + OPTION_TEXT +} option_type_t; + +struct option { + const char *name; + option_type_t type; + const char *help; + + union { + char text[128]; + int numeric; + } data; + + struct option *next; +}; + +/* Add an option to the parser's list. Options can't be removed from + * the list once added. + */ +void register_option(struct option *o); + +/* Command function for settings options. With no arguments, displays + * a list of available options. + */ +int cmd_opt(char **arg); + +/* Initialise the parser, and register built-ins. */ +void parse_init(void); + +#endif + diff --git a/sim.c b/sim.c index 651281f..386ae57 100644 --- a/sim.c +++ b/sim.c @@ -24,6 +24,7 @@ #include "dis.h" #include "util.h" #include "stab.h" +#include "parse.h" #define MEM_SIZE 65536 @@ -88,7 +89,7 @@ static int fetch_io(u_int16_t addr, int is_byte, u_int32_t *data_ret) len--; text[len] = 0; - if (!stab_parse(text, &data)) { + if (!addr_exp(text, &data)) { if (data_ret) *data_ret = data; return 0; diff --git a/stab.c b/stab.c index a4acce1..79279f2 100644 --- a/stab.c +++ b/stab.c @@ -166,10 +166,8 @@ int stab_get(const char *name, u_int16_t *value) struct sym_key skey; sym_key_init(&skey, name); - if (btree_get(sym_table, &skey, value)) { - fprintf(stderr, "stab: can't find symbol: %s\n", name); + if (btree_get(sym_table, &skey, value)) return -1; - } return 0; } @@ -243,79 +241,6 @@ int stab_re_search(const char *regex, stab_callback_t cb) return count; } -static char token_buf[64]; -static int token_len; -static int token_mult; -static int token_sum; - -static int token_add(void) -{ - int i; - struct sym_key skey; - u_int16_t value; - - if (!token_len) - return 0; - - token_buf[token_len] = 0; - token_len = 0; - - /* Is it a decimal? */ - i = 0; - while (token_buf[i] && isdigit(token_buf[i])) - i++; - if (!token_buf[i]) { - token_sum += token_mult * atoi(token_buf); - return 0; - } - - /* Is it hex? */ - if (token_buf[0] == '0' && tolower(token_buf[1]) == 'x') { - token_sum += token_mult * strtol(token_buf + 2, NULL, 16); - return 0; - } - - /* Look up the name in the symbol table */ - sym_key_init(&skey, token_buf); - if (!btree_get(sym_table, &skey, &value)) { - token_sum += token_mult * (int)value; - return 0; - } - - fprintf(stderr, "stab: unknown token: %s\n", token_buf); - return -1; -} - -int stab_parse(const char *text, int *addr) -{ - token_len = 0; - token_mult = 1; - token_sum = 0; - - while (*text) { - if (isalnum(*text) || *text == '_' || *text == '$' || - *text == '.' || *text == ':') { - if (token_len + 1 < sizeof(token_buf)) - token_buf[token_len++] = *text; - } else { - if (token_add() < 0) - return -1; - if (*text == '+') - token_mult = 1; - if (*text == '-') - token_mult = -1; - } - - text++; - } - - if (token_add() < 0) - return -1; - - *addr = token_sum & 0xffff; - return 0; -} - int stab_nearest(u_int16_t addr, char *ret_name, int max_len, u_int16_t *ret_offset) {