diff --git a/bsl.c b/bsl.c index 5c6f448..0979e24 100644 --- a/bsl.c +++ b/bsl.c @@ -222,7 +222,13 @@ static device_status_t bsl_poll(device_t dev_base) return DEVICE_STATUS_HALTED; } -static int bsl_breakpoint(device_t dev_base, int enabled, uint16_t addr) +static int bsl_setbrk(device_t dev_base, int n, int enabled, uint16_t addr) +{ + fprintf(stderr, "bsl: breakpoints are not implemented\n"); + return -1; +} + +static int bsl_getbrk(device_t dev_base, int n, int *enabled, uint16_t *addr) { fprintf(stderr, "bsl: breakpoints are not implemented\n"); return -1; @@ -321,12 +327,14 @@ device_t bsl_open(const char *device) return NULL; } + dev->base.max_breakpoints = 0; dev->base.destroy = bsl_destroy; dev->base.readmem = bsl_readmem; dev->base.writemem = bsl_writemem; dev->base.getregs = bsl_getregs; dev->base.setregs = bsl_setregs; - dev->base.breakpoint = bsl_breakpoint; + dev->base.setbrk = bsl_setbrk; + dev->base.getbrk = bsl_getbrk; dev->base.ctl = bsl_ctl; dev->base.poll = bsl_poll; diff --git a/devcmd.c b/devcmd.c index 5da1f49..b2dde40 100644 --- a/devcmd.c +++ b/devcmd.c @@ -183,31 +183,12 @@ static int cmd_step(cproc_t cp, char **arg) static int cmd_run(cproc_t cp, char **arg) { device_t dev = cproc_device(cp); - stab_t stab = cproc_stab(cp); - char *bp_text = get_arg(arg); - int bp_addr; device_status_t status; - if (bp_text) { - if (expr_eval(stab, bp_text, &bp_addr) < 0) { - fprintf(stderr, "run: can't parse breakpoint: %s\n", - bp_text); - return -1; - } - - dev->breakpoint(dev, 1, bp_addr); - } else { - dev->breakpoint(dev, 0, 0); - } - if (dev->ctl(dev, DEVICE_CTL_RUN) < 0) return -1; - if (bp_text) - printf("Running to 0x%04x.", bp_addr); - else - printf("Running."); - printf(" Press Ctrl+C to interrupt...\n"); + printf("Running. Press Ctrl+C to interrupt...\n"); do { status = dev->poll(dev); @@ -572,7 +553,130 @@ static int cmd_prog(cproc_t cp, char **arg) return 0; } +static int cmd_setbreak(cproc_t cp, char **arg) +{ + device_t dev = cproc_device(cp); + stab_t stab = cproc_stab(cp); + char *addr_text = get_arg(arg); + char *index_text = get_arg(arg); + int index; + int addr; + + if (!addr_text) { + fprintf(stderr, "setbreak: address required\n"); + return -1; + } + + if (expr_eval(stab, addr_text, &addr) < 0) { + fprintf(stderr, "setbreak: invalid address\n"); + return -1; + } + + if (index_text) { + index = atoi(index_text); + } else { + int i; + + for (i = 0; i < dev->max_breakpoints; i++) { + int enabled; + + if (!dev->getbrk(dev, i, &enabled, NULL) && + !enabled) + break; + } + + if (i >= dev->max_breakpoints) { + fprintf(stderr, "setbreak: all breakpoint slots are " + "occupied\n"); + return -1; + } + + index = i; + } + + printf("Setting breakpoint %d\n", index); + dev->setbrk(dev, index, 1, addr); + + return 0; +} + +static int cmd_delbreak(cproc_t cp, char **arg) +{ + device_t dev = cproc_device(cp); + char *index_text = get_arg(arg); + int ret = 0; + + if (index_text) { + int index = atoi(index_text); + + printf("Clearing breakpoint %d\n", index); + ret = dev->setbrk(dev, index, 0, 0); + } else { + int i; + + printf("Clearing all breakpoints...\n"); + for (i = 0; i < dev->max_breakpoints; i++) + if (dev->setbrk(dev, i, 0, 0) < 0) + ret = -1; + } + + return ret; +} + +static int cmd_break(cproc_t cp, char **arg) +{ + device_t dev = cproc_device(cp); + stab_t stab = cproc_stab(cp); + int i; + + printf("%d breakpoints available:\n", dev->max_breakpoints); + for (i = 0; i < dev->max_breakpoints; i++) { + int enabled; + uint16_t addr; + + if (!dev->getbrk(dev, i, &enabled, &addr) && enabled) { + char name[128]; + uint16_t offset; + + printf(" %d. 0x%04x", i, addr); + if (!stab_nearest(stab, addr, name, + sizeof(name), &offset)) { + printf(" (%s", name); + if (offset) + printf("+0x%x", offset); + printf(")"); + } + printf("\n"); + } + } + + return 0; +} + static const struct cproc_command commands[] = { + { + .name = "setbreak", + .func = cmd_setbreak, + .help = +"setbreak [index]\n" +" Set a breakpoint. If no index is specified, the first available\n" +" slot will be used.\n" + }, + { + .name = "delbreak", + .func = cmd_delbreak, + .help = +"delbreak [index]\n" +" Delete a breakpoint. If no index is specified, then all active\n" +" breakpoints are cleared.\n" + }, + { + .name = "break", + .func = cmd_break, + .help = +"break\n" +" List active breakpoints.\n" + }, { .name = "regs", .func = cmd_regs, @@ -629,9 +733,9 @@ static const struct cproc_command commands[] = { .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" +"run\n" +" Run the CPU to until a breakpoint is reached or the command is\n" +" interrupted.\n" }, { .name = "set", diff --git a/device.h b/device.h index d29d0a2..4497504 100644 --- a/device.h +++ b/device.h @@ -42,6 +42,8 @@ typedef enum { #define DEVICE_NUM_REGS 16 struct device { + int max_breakpoints; + /* Close the connection to the device and destroy the driver object */ void (*destroy)(device_t dev); @@ -56,7 +58,8 @@ struct device { int (*setregs)(device_t dev, const uint16_t *regs); /* Breakpoint control */ - int (*breakpoint)(device_t dev, int enabled, uint16_t addr); + int (*setbrk)(device_t dev, int n, int enabled, uint16_t addr); + int (*getbrk)(device_t dev, int n, int *enabled, uint16_t *addr); /* CPU control */ int (*ctl)(device_t dev, device_ctl_t op); diff --git a/fet.c b/fet.c index d442521..f05cd6b 100644 --- a/fet.c +++ b/fet.c @@ -33,6 +33,12 @@ #include "fet_db.h" #define MAX_PARAMS 16 +#define MAX_BREAKPOINTS 16 + +struct fet_bp { + int enabled; + uint16_t addr; +}; struct fet_device { struct device base; @@ -40,7 +46,9 @@ struct fet_device { transport_t transport; int proto_flags; int version; - int have_breakpoint; + + /* Breakpoint array */ + struct fet_bp bps[MAX_BREAKPOINTS]; /* Device-specific information */ u_int16_t code_start; @@ -475,6 +483,13 @@ static int xfer(struct fet_device *dev, * MSP430 high-level control functions */ +static void show_dev_info(const char *name, const struct fet_device *dev) +{ + printf("Device: %s\n", name); + printf("Code memory starts at 0x%04x\n", dev->code_start); + printf("Number of breakpoints: %d\n", dev->base.max_breakpoints); +} + static int identify_old(struct fet_device *dev) { char idtext[64]; @@ -491,9 +506,9 @@ static int identify_old(struct fet_device *dev) idtext[32] = 0; dev->code_start = LE_WORD(dev->fet_reply.data, 0x24); + dev->base.max_breakpoints = LE_WORD(dev->fet_reply.data, 0x2a); - printf("Device: %s\n", idtext); - printf("Code memory starts at 0x%04x\n", dev->code_start); + show_dev_info(idtext, dev); return 0; } @@ -529,9 +544,9 @@ static int identify_new(struct fet_device *dev, const char *force_id) } dev->code_start = LE_WORD(r->msg29_data, 0); + dev->base.max_breakpoints = LE_WORD(r->msg29_data, 0x14); - printf("Device: %s\n", r->name); - printf("Code memory starts at 0x%04x\n", dev->code_start); + show_dev_info(r->name, dev); if (xfer(dev, C_IDENT3, r->msg2b_data, r->msg2b_len, 0) < 0) fprintf(stderr, "fet: warning: message C_IDENT3 failed\n"); @@ -625,8 +640,7 @@ static int fet_ctl(device_t dev_base, device_ctl_t action) break; case DEVICE_CTL_RUN: - return do_run(dev, dev->have_breakpoint ? - FET_RUN_BREAKPOINT : FET_RUN_FREE); + return do_run(dev, FET_RUN_BREAKPOINT); case DEVICE_CTL_HALT: if (xfer(dev, C_STATE, NULL, 0, 1, 1) < 0) { @@ -769,21 +783,40 @@ static int fet_setregs(device_t dev_base, const uint16_t *regs) return 0; } -static int fet_breakpoint(device_t dev_base, int enabled, uint16_t addr) +static int fet_setbrk(device_t dev_base, int n, int enabled, uint16_t addr) { struct fet_device *dev = (struct fet_device *)dev_base; - if (enabled) { - dev->have_breakpoint = 1; - - if (xfer(dev, C_BREAKPOINT, NULL, 0, 2, 0, addr) < 0) { - fprintf(stderr, "fet: set breakpoint failed\n"); - return -1; - } - } else { - dev->have_breakpoint = 0; + if (n < 0 || n > dev_base->max_breakpoints) { + fprintf(stderr, "fet: invalid breakpoint number: %d\n", n); + return -1; } + if (xfer(dev, C_BREAKPOINT, NULL, 0, 2, n, enabled ? addr : 0) < 0) { + fprintf(stderr, "fet: set breakpoint failed\n"); + return -1; + } + + dev->bps[n].enabled = enabled; + dev->bps[n].addr = addr; + + return 0; +} + +static int fet_getbrk(device_t dev_base, int n, int *enabled, uint16_t *addr) +{ + struct fet_device *dev = (struct fet_device *)dev_base; + + if (n < 0 || n > dev_base->max_breakpoints) { + fprintf(stderr, "fet: invalid breakpoint number: %d\n", n); + return -1; + } + + if (enabled) + *enabled = dev->bps[n].enabled; + if (addr) + *addr = dev->bps[n].addr; + return 0; } @@ -823,6 +856,7 @@ device_t fet_open(transport_t transport, int proto_flags, int vcc_mv, const char *force_id) { struct fet_device *dev = malloc(sizeof(*dev)); + int i; if (!dev) { perror("fet: failed to allocate memory"); @@ -834,13 +868,15 @@ device_t fet_open(transport_t transport, int proto_flags, int vcc_mv, dev->base.writemem = fet_writemem; dev->base.getregs = fet_getregs; dev->base.setregs = fet_setregs; - dev->base.breakpoint = fet_breakpoint; + dev->base.setbrk = fet_setbrk; + dev->base.getbrk = fet_getbrk; dev->base.ctl = fet_ctl; dev->base.poll = fet_poll; + memset(dev->bps, 0, sizeof(dev->bps)); + dev->transport = transport; dev->proto_flags = proto_flags; - dev->have_breakpoint = 0; dev->fet_len = 0; @@ -881,6 +917,12 @@ device_t fet_open(transport_t transport, int proto_flags, int vcc_mv, goto fail; } + printf("Resetting all breakpoints...\n"); + for (i = 0; i < dev->base.max_breakpoints; i++) + xfer(dev, C_BREAKPOINT, NULL, 0, 2, i, 0); + if (dev->base.max_breakpoints > MAX_BREAKPOINTS) + dev->base.max_breakpoints = MAX_BREAKPOINTS; + return (device_t)dev; fail: diff --git a/gdb.c b/gdb.c index 91bbc07..4f01a25 100644 --- a/gdb.c +++ b/gdb.c @@ -49,12 +49,6 @@ struct gdb_data { int outlen; device_t device; - - /* The underlying device driver supports only one breakpoint at - * a time. We keep track of whether or not it's in use and report - * an error to gdb if more than one is set. - */ - int have_breakpoint; }; static void gdb_printf(struct gdb_data *data, const char *fmt, ...) @@ -441,6 +435,50 @@ static int run(struct gdb_data *data, char *buf) return run_final_status(data); } +static int add_breakpoint(device_t dev, int addr) +{ + int i; + int avail = -1; + + for (i = 0; i < dev->max_breakpoints; i++) { + int enabled; + uint16_t ba; + + if (!dev->getbrk(dev, i, &enabled, &ba)) { + if (!enabled && avail < 0) + avail = i; + if (enabled && addr == ba) { + fprintf(stderr, "warning: gdb: breakpoint at " + "0x%04x already set", addr); + return 0; + } + } + } + + if (avail < 0) { + fprintf(stderr, "gdb: no breakpoint slots available\n"); + return -1; + } + + return dev->setbrk(dev, avail, 1, addr); +} + +static int remove_breakpoint(device_t dev, int addr) +{ + int i; + + for (i = 0; i < dev->max_breakpoints; i++) { + int enabled; + uint16_t ba; + + if (!dev->getbrk(dev, i, &enabled, &ba) && enabled && + ba == addr && dev->setbrk(dev, i, 0, 0) < 0) + return -1; + } + + return 0; +} + static int set_breakpoint(struct gdb_data *data, int enable, char *buf) { char *parts[2]; @@ -475,23 +513,23 @@ static int set_breakpoint(struct gdb_data *data, int enable, char *buf) /* Parse the breakpoint address */ addr = strtoul(parts[1], NULL, 16); - /* Only one breakpoint at a time is allowed */ - if (enable && data->have_breakpoint) { - fprintf(stderr, "gdb: only one breakpoint allowed " - "at a time\n"); - return gdb_send(data, "E00"); - } + if (enable) { + if (add_breakpoint(data->device, addr) < 0) { + fprintf(stderr, "gdb: can't add breakpoint at " + "0x%04x\n", addr); + return gdb_send(data, "E00"); + } - /* Set the breakpoint */ - if (data->device->breakpoint(data->device, enable, addr) < 0) - return gdb_send(data, "E00"); - - data->have_breakpoint = enable; - - if (enable) printf("Breakpoint set at 0x%04x\n", addr); - else - printf("Breakpoint cleared\n"); + } else { + if (remove_breakpoint(data->device, addr) < 0) { + fprintf(stderr, "gdb: can't remove breakpoint at " + "0x%04x\n", addr); + return gdb_send(data, "E00"); + } + + printf("Breakpoint cleared at 0x%04x\n", addr); + } return gdb_send(data, "OK"); } @@ -605,6 +643,7 @@ static int gdb_server(device_t device, int port) socklen_t len; int arg; struct gdb_data data; + int i; sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) { @@ -654,13 +693,12 @@ static int gdb_server(device_t device, int port) data.device = device; /* Put the hardware breakpoint setting into a known state. */ - data.have_breakpoint = 0; - device->breakpoint(device, 0, 0); + printf("Clearing all breakpoints...\n"); + for (i = 0; i < device->max_breakpoints; i++) + device->setbrk(device, i, 0, 0); gdb_reader_loop(&data); - device->breakpoint(device, 0, 0); - return data.error ? -1 : 0; } diff --git a/mspdebug.man b/mspdebug.man index e5b43f5..3395b40 100644 --- a/mspdebug.man +++ b/mspdebug.man @@ -121,6 +121,10 @@ question. See the section marked \fBADDRESS EXPRESSIONS\fR for more information on the syntax of expressions. +.IP "\fBbreak\fR" +Show a list of active breakpoints. Breakpoints can be added and removed +with the \fBsetbreak\fR and \fBdelbreak\fR commands. Each breakpoint is +numbered with an integer index starting at 0. .IP "\fBcgraph\fR \fIaddress\fR \fIlength\fR [\fIaddress\fR]" Construct the call graph of all functions contained or referenced in the given range of memory. If a particular function is specified, then @@ -133,6 +137,9 @@ is considered a possible function start. Callers and callee names are shown prefixed by a "*" where the transition is a tail-call type transition. +.IP "\fBdelbreak\fR [\fIindex\fR]" +Delete one or all breakpoints. If an index is given, the selected breakpoint +is deleted. Otherwise, all breakpoints are cleared. .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 @@ -274,14 +281,11 @@ processed. Show the current value of all CPU registers in the device under test. .IP "\fBreset\fR" Reset (and halt) the CPU of the device under test. -.IP "\fBrun\fR [\fIbreakpoint\fR]" -Run the CPU, optionally specifying a breakpoint. The breakpoint can be -specified as an address expression. - -The interactive command prompt is blocked when the CPU is started and -the prompt will not appear again until the CPU halts. The CPU will halt -if it encounters the specified breakpoint, or if Ctrl\-C is pressed by -the user. +.IP "\fBrun\fR" +Start running the CPU. The interactive command prompt is blocked when +the CPU is started and the prompt will not appear again until the CPU +halts. The CPU will halt if it encounters a breakpoint, or if Ctrl\-C +is pressed by 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 @@ -291,6 +295,11 @@ 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 "\fBsetbreak\fR \fIaddress\fR [\fIindex\fR]" +Add a new breakpoint. The breakpoint location is an address expression. An +optional index may be specified, indicating that this new breakpoint should +overwrite an existing slot. If no index is specified, then the breakpoint +will be stored in the next unused slot. .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 diff --git a/sim.c b/sim.c index 578f8b0..701abc5 100644 --- a/sim.c +++ b/sim.c @@ -26,7 +26,14 @@ #include "sim.h" #define MEM_SIZE 65536 -#define MEM_IO_END 0x200 +#define MEM_IO_END 0x200 + +#define NUM_BREAKPOINTS 8 + +struct sim_bp { + int enabled; + uint16_t addr; +}; struct sim_device { struct device base; @@ -35,13 +42,13 @@ struct sim_device { sim_store_func_t store_func; void *user_data; - uint8_t memory[MEM_SIZE]; - uint16_t regs[DEVICE_NUM_REGS]; + uint8_t memory[MEM_SIZE]; + uint16_t regs[DEVICE_NUM_REGS]; int running; - uint16_t current_insn; - int have_breakpoint; - uint16_t breakpoint_addr; + uint16_t current_insn; + + struct sim_bp bps[NUM_BREAKPOINTS]; }; #define MEM_GETB(dev, offset) ((dev)->memory[offset]) @@ -482,12 +489,35 @@ static int sim_setregs(device_t dev_base, const uint16_t *regs) return 0; } -static int sim_breakpoint(device_t dev_base, int enabled, uint16_t addr) +static int sim_setbrk(device_t dev_base, int n, int enabled, uint16_t addr) { struct sim_device *dev = (struct sim_device *)dev_base; - dev->have_breakpoint = enabled; - dev->breakpoint_addr = addr; + if (n < 0 || n > NUM_BREAKPOINTS) { + fprintf(stderr, "sim: invalid breakpoint number: %d\n", n); + return -1; + } + + dev->bps[n].enabled = enabled; + dev->bps[n].addr = addr; + + return 0; +} + +static int sim_getbrk(device_t dev_base, int n, int *enabled, uint16_t *addr) +{ + struct sim_device *dev = (struct sim_device *)dev_base; + + if (n < 0 || n > NUM_BREAKPOINTS) { + fprintf(stderr, "sim: invalid breakpoint number: %d\n", n); + return -1; + } + + if (enabled) + *enabled = dev->bps[n].enabled; + if (addr) + *addr = dev->bps[n].addr; + return 0; } @@ -525,19 +555,27 @@ static device_status_t sim_poll(device_t dev_base) struct sim_device *dev = (struct sim_device *)dev_base; int count = 1000000; + if (!dev->running) + return DEVICE_STATUS_HALTED; + ctrlc_reset(); - while (dev->running && count > 0) { - if (dev->have_breakpoint && - dev->regs[MSP430_REG_PC] == dev->breakpoint_addr) { - printf("Breakpoint reached\n"); - dev->running = 0; - break; + while (count > 0) { + int i; + + for (i = 0; i < NUM_BREAKPOINTS; i++) { + struct sim_bp *bp = &dev->bps[i]; + + if (bp->enabled && + dev->regs[MSP430_REG_PC] == bp->addr) { + dev->running = 0; + return DEVICE_STATUS_HALTED; + } } if (dev->regs[MSP430_REG_SR] & MSP430_SR_CPUOFF) { printf("CPU disabled\n"); dev->running = 0; - break; + return DEVICE_STATUS_HALTED; } if (step_cpu(dev) < 0) { @@ -551,7 +589,7 @@ static device_status_t sim_poll(device_t dev_base) count--; } - return dev->running ? DEVICE_STATUS_RUNNING : DEVICE_STATUS_HALTED; + return DEVICE_STATUS_RUNNING; } device_t sim_open(sim_fetch_func_t fetch_func, @@ -565,12 +603,14 @@ device_t sim_open(sim_fetch_func_t fetch_func, return NULL; } + dev->base.max_breakpoints = NUM_BREAKPOINTS; dev->base.destroy = sim_destroy; dev->base.readmem = sim_readmem; dev->base.writemem = sim_writemem; dev->base.getregs = sim_getregs; dev->base.setregs = sim_setregs; - dev->base.breakpoint = sim_breakpoint; + dev->base.setbrk = sim_setbrk; + dev->base.getbrk = sim_getbrk; dev->base.ctl = sim_ctl; dev->base.poll = sim_poll; @@ -583,8 +623,8 @@ device_t sim_open(sim_fetch_func_t fetch_func, dev->running = 0; dev->current_insn = 0; - dev->have_breakpoint = 0; - dev->breakpoint_addr = 0; + + memset(dev->bps, 0, sizeof(dev->bps)); printf("Simulation started, 0x%x bytes of RAM\n", MEM_SIZE); return (device_t)dev;