Commands are now registered by each module.
This commit is contained in:
parent
f3c66c5aa4
commit
ceb78ff053
2
Makefile
2
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 device.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS)
|
||||
|
||||
.c.o:
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
/* Callback for binary image data */
|
||||
typedef int (*imgfunc_t)(u_int16_t addr, const u_int8_t *data, int len);
|
||||
|
||||
/* Callback for symbol data */
|
||||
typedef int (*symfunc_t)(const char *name, int value);
|
||||
|
||||
/* Intel HEX file support */
|
||||
int ihex_check(FILE *in);
|
||||
int ihex_extract(FILE *in, imgfunc_t cb);
|
||||
|
@ -32,10 +35,10 @@ int ihex_extract(FILE *in, imgfunc_t cb);
|
|||
/* ELF32 file support */
|
||||
int elf32_check(FILE *in);
|
||||
int elf32_extract(FILE *in, imgfunc_t cb);
|
||||
int elf32_syms(FILE *in);
|
||||
int elf32_syms(FILE *in, symfunc_t cb);
|
||||
|
||||
/* *.map file support */
|
||||
int symmap_check(FILE *in);
|
||||
int symmap_syms(FILE *in);
|
||||
int symmap_syms(FILE *in, symfunc_t cb);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,668 @@
|
|||
/* MSPDebug - debugging tool for the eZ430
|
||||
* Copyright (C) 2009, 2010 Daniel Beer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "device.h"
|
||||
#include "binfile.h"
|
||||
#include "stab.h"
|
||||
#include "util.h"
|
||||
#include "dis.h"
|
||||
|
||||
static const struct device *msp430_dev;
|
||||
|
||||
#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("(r%02d: %04x) ", k, regs[k]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_regs(char **arg)
|
||||
{
|
||||
u_int16_t regs[DEVICE_NUM_REGS];
|
||||
u_int8_t code[16];
|
||||
|
||||
if (msp430_dev->getregs(regs) < 0)
|
||||
return -1;
|
||||
show_regs(regs);
|
||||
|
||||
/* Try to disassemble the instruction at PC */
|
||||
if (msp430_dev->readmem(regs[0], code, sizeof(code)) < 0)
|
||||
return 0;
|
||||
|
||||
disassemble(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)
|
||||
{
|
||||
char *off_text = get_arg(arg);
|
||||
char *len_text = get_arg(arg);
|
||||
int offset = 0;
|
||||
int length = 0x40;
|
||||
|
||||
if (!off_text) {
|
||||
fprintf(stderr, "md: offset must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_exp(off_text, &offset) < 0) {
|
||||
fprintf(stderr, "md: can't parse offset: %s\n", off_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len_text) {
|
||||
if (addr_exp(len_text, &length) < 0) {
|
||||
fprintf(stderr, "md: can't parse length: %s\n",
|
||||
len_text);
|
||||
return -1;
|
||||
}
|
||||
} else if (offset + length > 0x10000) {
|
||||
length = 0x10000 - offset;
|
||||
}
|
||||
|
||||
if (offset < 0 || length <= 0 || (offset + length) > 0x10000) {
|
||||
fprintf(stderr, "md: memory out of range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (length) {
|
||||
u_int8_t buf[128];
|
||||
int blen = length > sizeof(buf) ? sizeof(buf) : length;
|
||||
|
||||
if (msp430_dev->readmem(offset, buf, blen) < 0)
|
||||
return -1;
|
||||
hexdump(offset, buf, blen);
|
||||
|
||||
offset += blen;
|
||||
length -= blen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct command command_md = {
|
||||
.name = "md",
|
||||
.func = cmd_md,
|
||||
.help =
|
||||
"md <address> [length]\n"
|
||||
" Read the specified number of bytes from memory at the given\n"
|
||||
" address, and display a hexdump.\n"
|
||||
};
|
||||
|
||||
static int cmd_mw(char **arg)
|
||||
{
|
||||
char *off_text = get_arg(arg);
|
||||
char *byte_text;
|
||||
int offset = 0;
|
||||
int length = 0;
|
||||
u_int8_t buf[1024];
|
||||
|
||||
if (!off_text) {
|
||||
fprintf(stderr, "md: offset must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_exp(off_text, &offset) < 0) {
|
||||
fprintf(stderr, "md: can't parse offset: %s\n", off_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((byte_text = get_arg(arg))) {
|
||||
if (length >= sizeof(buf)) {
|
||||
fprintf(stderr, "md: maximum length exceeded\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[length++] = strtoul(byte_text, NULL, 16);
|
||||
}
|
||||
|
||||
if (!length)
|
||||
return 0;
|
||||
|
||||
if (offset < 0 || (offset + length) > 0x10000) {
|
||||
fprintf(stderr, "md: memory out of range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msp430_dev->writemem(offset, buf, length) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct command command_mw = {
|
||||
.name = "mw",
|
||||
.func = cmd_mw,
|
||||
.help =
|
||||
"mw <address> bytes ...\n"
|
||||
" Write a sequence of bytes to a memory address. Byte values are\n"
|
||||
" two-digit hexadecimal numbers.\n"
|
||||
};
|
||||
|
||||
static int cmd_reset(char **arg)
|
||||
{
|
||||
return msp430_dev->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)
|
||||
{
|
||||
if (msp430_dev->control(DEVICE_CTL_HALT) < 0)
|
||||
return -1;
|
||||
|
||||
printf("Erasing...\n");
|
||||
return msp430_dev->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)
|
||||
{
|
||||
char *count_text = get_arg(arg);
|
||||
int count = 1;
|
||||
|
||||
if (count_text)
|
||||
count = atoi(count_text);
|
||||
|
||||
while (count > 0) {
|
||||
if (msp430_dev->control(DEVICE_CTL_STEP) < 0)
|
||||
return -1;
|
||||
count--;
|
||||
}
|
||||
|
||||
return cmd_regs(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)
|
||||
{
|
||||
char *bp_text = get_arg(arg);
|
||||
int bp_addr;
|
||||
device_status_t status;
|
||||
|
||||
if (bp_text) {
|
||||
if (addr_exp(bp_text, &bp_addr) < 0) {
|
||||
fprintf(stderr, "run: can't parse breakpoint: %s\n",
|
||||
bp_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
msp430_dev->breakpoint(bp_addr);
|
||||
}
|
||||
|
||||
if (msp430_dev->control(bp_text ?
|
||||
DEVICE_CTL_RUN_BP : 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");
|
||||
|
||||
status = msp430_dev->wait(1);
|
||||
if (status == DEVICE_STATUS_INTR)
|
||||
printf("\n");
|
||||
|
||||
if (msp430_dev->control(DEVICE_CTL_HALT) < 0)
|
||||
return -1;
|
||||
|
||||
return cmd_regs(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)
|
||||
{
|
||||
char *reg_text = get_arg(arg);
|
||||
char *val_text = get_arg(arg);
|
||||
int reg;
|
||||
int value = 0;
|
||||
u_int16_t regs[DEVICE_NUM_REGS];
|
||||
|
||||
if (!(reg_text && val_text)) {
|
||||
fprintf(stderr, "set: must specify a register and a value\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (*reg_text && !isdigit(*reg_text))
|
||||
reg_text++;
|
||||
reg = atoi(reg_text);
|
||||
|
||||
if (addr_exp(val_text, &value) < 0) {
|
||||
fprintf(stderr, "set: can't parse value: %s\n", val_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (reg < 0 || reg >= DEVICE_NUM_REGS) {
|
||||
fprintf(stderr, "set: register out of range: %d\n", reg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msp430_dev->getregs(regs) < 0)
|
||||
return -1;
|
||||
regs[reg] = value;
|
||||
if (msp430_dev->setregs(regs) < 0)
|
||||
return -1;
|
||||
|
||||
show_regs(regs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct command command_set = {
|
||||
.name = "set",
|
||||
.func = cmd_set,
|
||||
.help =
|
||||
"set <register> <value>\n"
|
||||
" Change the value of a CPU register.\n"
|
||||
};
|
||||
|
||||
static int cmd_dis(char **arg)
|
||||
{
|
||||
char *off_text = get_arg(arg);
|
||||
char *len_text = get_arg(arg);
|
||||
int offset = 0;
|
||||
int length = 0x40;
|
||||
u_int8_t buf[4096];
|
||||
|
||||
if (!off_text) {
|
||||
fprintf(stderr, "dis: offset must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_exp(off_text, &offset) < 0) {
|
||||
fprintf(stderr, "dis: can't parse offset: %s\n", off_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len_text) {
|
||||
if (addr_exp(len_text, &length) < 0) {
|
||||
fprintf(stderr, "dis: can't parse length: %s\n",
|
||||
len_text);
|
||||
return -1;
|
||||
}
|
||||
} else if (offset + length > 0x10000) {
|
||||
length = 0x10000 - offset;
|
||||
}
|
||||
|
||||
if (offset < 0 || length <= 0 || length > sizeof(buf) ||
|
||||
(offset + length) > 0x10000) {
|
||||
fprintf(stderr, "dis: memory out of range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msp430_dev->readmem(offset, buf, length) < 0)
|
||||
return -1;
|
||||
|
||||
disassemble(offset, (u_int8_t *)buf, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct command command_dis = {
|
||||
.name = "dis",
|
||||
.func = cmd_dis,
|
||||
.help =
|
||||
"dis <address> [length]\n"
|
||||
" Disassemble a section of memory.\n"
|
||||
};
|
||||
|
||||
static FILE *hexout_file;
|
||||
static u_int16_t hexout_addr;
|
||||
static u_int8_t hexout_buf[16];
|
||||
static int hexout_len;
|
||||
|
||||
static int hexout_start(const char *filename)
|
||||
{
|
||||
hexout_file = fopen(filename, "w");
|
||||
if (!hexout_file) {
|
||||
perror("hexout: couldn't open output file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hexout_flush(void)
|
||||
{
|
||||
int i;
|
||||
int cksum = 0;
|
||||
|
||||
if (!hexout_len)
|
||||
return 0;
|
||||
|
||||
if (fprintf(hexout_file, ":%02X%04X00", hexout_len, hexout_addr) < 0)
|
||||
goto fail;
|
||||
cksum += hexout_len;
|
||||
cksum += hexout_addr & 0xff;
|
||||
cksum += hexout_addr >> 8;
|
||||
|
||||
for (i = 0; i < hexout_len; i++) {
|
||||
if (fprintf(hexout_file, "%02X", hexout_buf[i]) < 0)
|
||||
goto fail;
|
||||
cksum += hexout_buf[i];
|
||||
}
|
||||
|
||||
if (fprintf(hexout_file, "%02X\n", ~(cksum - 1) & 0xff) < 0)
|
||||
goto fail;
|
||||
|
||||
hexout_len = 0;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
perror("hexout: can't write HEX data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int hexout_feed(u_int16_t addr, const u_int8_t *buf, int len)
|
||||
{
|
||||
while (len) {
|
||||
int count;
|
||||
|
||||
if ((hexout_addr + hexout_len != addr ||
|
||||
hexout_len >= sizeof(hexout_buf)) &&
|
||||
hexout_flush() < 0)
|
||||
return -1;
|
||||
|
||||
if (!hexout_len)
|
||||
hexout_addr = addr;
|
||||
|
||||
count = sizeof(hexout_buf) - hexout_len;
|
||||
if (count > len)
|
||||
count = len;
|
||||
|
||||
memcpy(hexout_buf + hexout_len, buf, count);
|
||||
hexout_len += count;
|
||||
|
||||
addr += count;
|
||||
buf += count;
|
||||
len -= count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_hexout(char **arg)
|
||||
{
|
||||
char *off_text = get_arg(arg);
|
||||
char *len_text = get_arg(arg);
|
||||
char *filename = *arg;
|
||||
int off;
|
||||
int length;
|
||||
|
||||
if (!(off_text && len_text && *filename)) {
|
||||
fprintf(stderr, "hexout: need offset, length and filename\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_exp(off_text, &off) < 0 ||
|
||||
addr_exp(len_text, &length) < 0)
|
||||
return -1;
|
||||
|
||||
if (hexout_start(filename) < 0)
|
||||
return -1;
|
||||
|
||||
while (length) {
|
||||
u_int8_t buf[128];
|
||||
int count = length;
|
||||
|
||||
if (count > sizeof(buf))
|
||||
count = sizeof(buf);
|
||||
|
||||
printf("Reading %d bytes from 0x%04x...\n", count, off);
|
||||
if (msp430_dev->readmem(off, buf, count) < 0) {
|
||||
perror("hexout: can't read memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (hexout_feed(off, buf, count) < 0)
|
||||
goto fail;
|
||||
|
||||
length -= count;
|
||||
off += count;
|
||||
}
|
||||
|
||||
if (hexout_flush() < 0)
|
||||
goto fail;
|
||||
if (fclose(hexout_file) < 0) {
|
||||
perror("hexout: error on close");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
fclose(hexout_file);
|
||||
unlink(filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct command command_hexout = {
|
||||
.name = "hexout",
|
||||
.func = cmd_hexout,
|
||||
.help =
|
||||
"hexout <address> <length> <filename.hex>\n"
|
||||
" Save a region of memory into a HEX file.\n"
|
||||
};
|
||||
|
||||
static u_int8_t prog_buf[128];
|
||||
static u_int16_t prog_addr;
|
||||
static int prog_len;
|
||||
static int prog_have_erased;
|
||||
|
||||
static void prog_init(void)
|
||||
{
|
||||
prog_len = 0;
|
||||
prog_have_erased = 0;
|
||||
}
|
||||
|
||||
static int prog_flush(void)
|
||||
{
|
||||
while (prog_len) {
|
||||
int wlen = prog_len;
|
||||
|
||||
/* Writing across this address seems to cause a hang */
|
||||
if (prog_addr < 0x999a && wlen + prog_addr > 0x999a)
|
||||
wlen = 0x999a - prog_addr;
|
||||
|
||||
if (!prog_have_erased) {
|
||||
printf("Erasing...\n");
|
||||
if (device_active()->control(DEVICE_CTL_ERASE) < 0)
|
||||
return -1;
|
||||
prog_have_erased = 1;
|
||||
}
|
||||
|
||||
printf("Writing %3d bytes to %04x...\n", wlen, prog_addr);
|
||||
if (device_active()->writemem(prog_addr, prog_buf, wlen) < 0)
|
||||
return -1;
|
||||
|
||||
memmove(prog_buf, prog_buf + wlen, prog_len - wlen);
|
||||
prog_len -= wlen;
|
||||
prog_addr += wlen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prog_feed(u_int16_t addr, const u_int8_t *data, int len)
|
||||
{
|
||||
/* Flush if this section is discontiguous */
|
||||
if (prog_len && prog_addr + prog_len != addr && prog_flush() < 0)
|
||||
return -1;
|
||||
|
||||
if (!prog_len)
|
||||
prog_addr = addr;
|
||||
|
||||
/* Add the buffer in piece by piece, flushing when it gets
|
||||
* full.
|
||||
*/
|
||||
while (len) {
|
||||
int count = sizeof(prog_buf) - prog_len;
|
||||
|
||||
if (count > len)
|
||||
count = len;
|
||||
|
||||
if (!count) {
|
||||
if (prog_flush() < 0)
|
||||
return -1;
|
||||
} else {
|
||||
memcpy(prog_buf + prog_len, data, count);
|
||||
prog_len += count;
|
||||
data += count;
|
||||
len -= count;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_prog(char **arg)
|
||||
{
|
||||
FILE *in;
|
||||
int result = 0;
|
||||
|
||||
if (modify_prompt(MODIFY_SYMS))
|
||||
return 0;
|
||||
|
||||
in = fopen(*arg, "r");
|
||||
if (!in) {
|
||||
fprintf(stderr, "prog: %s: %s\n", *arg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (device_active()->control(DEVICE_CTL_HALT) < 0) {
|
||||
fclose(in);
|
||||
return -1;
|
||||
}
|
||||
|
||||
prog_init();
|
||||
|
||||
if (elf32_check(in)) {
|
||||
result = elf32_extract(in, prog_feed);
|
||||
stab_clear();
|
||||
elf32_syms(in, stab_set);
|
||||
} else if (ihex_check(in)) {
|
||||
result = ihex_extract(in, prog_feed);
|
||||
} else {
|
||||
fprintf(stderr, "prog: %s: unknown file type\n", *arg);
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
|
||||
if (prog_flush() < 0)
|
||||
return -1;
|
||||
|
||||
if (device_active()->control(DEVICE_CTL_RESET) < 0) {
|
||||
fprintf(stderr, "prog: failed to reset after programming\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
modify_clear(MODIFY_SYMS);
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct command command_prog = {
|
||||
.name = "prog",
|
||||
.func = cmd_prog,
|
||||
.help =
|
||||
"prog <filename>\n"
|
||||
" Erase the device and flash the data contained in a binary file.\n"
|
||||
" This command also loads symbols from the file, if available.\n"
|
||||
};
|
||||
|
||||
void device_init(const struct device *dev)
|
||||
{
|
||||
msp430_dev = dev;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const struct device *device_active(void)
|
||||
{
|
||||
return msp430_dev;
|
||||
}
|
||||
|
||||
void device_exit(void)
|
||||
{
|
||||
if (msp430_dev)
|
||||
msp430_dev->close();
|
||||
}
|
5
device.h
5
device.h
|
@ -64,4 +64,9 @@ const struct device *bsl_open(const char *device);
|
|||
/* Dummy/simulation implementation. */
|
||||
const struct device *sim_open(void);
|
||||
|
||||
/* Register device commands */
|
||||
void device_init(const struct device *dev);
|
||||
void device_exit(void);
|
||||
const struct device *device_active(void);
|
||||
|
||||
#endif
|
||||
|
|
9
elf32.c
9
elf32.c
|
@ -21,7 +21,6 @@
|
|||
#include <string.h>
|
||||
#include <elf.h>
|
||||
#include "binfile.h"
|
||||
#include "stab.h"
|
||||
|
||||
#define EM_MSP430 0x69
|
||||
|
||||
|
@ -265,7 +264,7 @@ static int syms_load_strings(FILE *in, Elf32_Shdr *s)
|
|||
|
||||
#define N_SYMS 128
|
||||
|
||||
static int syms_load_syms(FILE *in, Elf32_Shdr *s)
|
||||
static int syms_load_syms(FILE *in, Elf32_Shdr *s, symfunc_t cb)
|
||||
{
|
||||
Elf32_Sym syms[N_SYMS];
|
||||
int len = s->sh_size / sizeof(syms[0]);
|
||||
|
@ -299,7 +298,7 @@ static int syms_load_syms(FILE *in, Elf32_Shdr *s)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (stab_set(string_tab + y->st_name, y->st_value) < 0)
|
||||
if (cb(string_tab + y->st_name, y->st_value) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -309,7 +308,7 @@ static int syms_load_syms(FILE *in, Elf32_Shdr *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int elf32_syms(FILE *in)
|
||||
int elf32_syms(FILE *in, symfunc_t cb)
|
||||
{
|
||||
Elf32_Shdr *s;
|
||||
|
||||
|
@ -331,7 +330,7 @@ int elf32_syms(FILE *in)
|
|||
string_len = 0;
|
||||
|
||||
if (syms_load_strings(in, &file_shdrs[s->sh_link]) < 0 ||
|
||||
syms_load_syms(in, s) < 0) {
|
||||
syms_load_syms(in, s, cb) < 0) {
|
||||
if (string_tab)
|
||||
free(string_tab);
|
||||
return -1;
|
||||
|
|
69
gdb.c
69
gdb.c
|
@ -28,10 +28,9 @@
|
|||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "device.h"
|
||||
#include "util.h"
|
||||
#include "gdb.h"
|
||||
|
||||
static const struct device *gdb_device;
|
||||
|
||||
/************************************************************************
|
||||
* GDB IO routines
|
||||
*/
|
||||
|
@ -208,7 +207,7 @@ static int read_registers(void)
|
|||
int i;
|
||||
|
||||
printf("Reading registers\n");
|
||||
if (gdb_device->getregs(regs) < 0)
|
||||
if (device_active()->getregs(regs) < 0)
|
||||
return gdb_send("E00");
|
||||
|
||||
gdb_packet_start();
|
||||
|
@ -230,11 +229,11 @@ static int monitor_command(char *buf)
|
|||
|
||||
if (!strcasecmp(cmd, "reset")) {
|
||||
printf("Resetting device\n");
|
||||
if (gdb_device->control(DEVICE_CTL_RESET) < 0)
|
||||
if (device_active()->control(DEVICE_CTL_RESET) < 0)
|
||||
return gdb_send_hex("Reset failed\n");
|
||||
} else if (!strcasecmp(cmd, "erase")) {
|
||||
printf("Erasing device\n");
|
||||
if (gdb_device->control(DEVICE_CTL_ERASE) < 0)
|
||||
if (device_active()->control(DEVICE_CTL_ERASE) < 0)
|
||||
return gdb_send_hex("Erase failed\n");
|
||||
}
|
||||
|
||||
|
@ -258,7 +257,7 @@ static int write_registers(char *buf)
|
|||
buf += 4;
|
||||
}
|
||||
|
||||
if (gdb_device->setregs(regs) < 0)
|
||||
if (device_active()->setregs(regs) < 0)
|
||||
return gdb_send("E00");
|
||||
|
||||
return gdb_send("OK");
|
||||
|
@ -286,7 +285,7 @@ static int read_memory(char *text)
|
|||
|
||||
printf("Reading %d bytes from 0x%04x\n", length, addr);
|
||||
|
||||
if (gdb_device->readmem(addr, buf, length) < 0)
|
||||
if (device_active()->readmem(addr, buf, length) < 0)
|
||||
return gdb_send("E00");
|
||||
|
||||
gdb_packet_start();
|
||||
|
@ -329,7 +328,7 @@ static int write_memory(char *text)
|
|||
|
||||
printf("Writing %d bytes to 0x%04x\n", buflen, addr);
|
||||
|
||||
if (gdb_device->writemem(addr, buf, buflen) < 0)
|
||||
if (device_active()->writemem(addr, buf, buflen) < 0)
|
||||
return gdb_send("E00");
|
||||
|
||||
return gdb_send("OK");
|
||||
|
@ -342,11 +341,11 @@ static int run_set_pc(char *buf)
|
|||
if (!*buf)
|
||||
return 0;
|
||||
|
||||
if (gdb_device->getregs(regs) < 0)
|
||||
if (device_active()->getregs(regs) < 0)
|
||||
return -1;
|
||||
|
||||
regs[0] = strtoul(buf, NULL, 16);
|
||||
return gdb_device->setregs(regs);
|
||||
return device_active()->setregs(regs);
|
||||
}
|
||||
|
||||
static int run_final_status(void)
|
||||
|
@ -354,7 +353,7 @@ static int run_final_status(void)
|
|||
u_int16_t regs[DEVICE_NUM_REGS];
|
||||
int i;
|
||||
|
||||
if (gdb_device->getregs(regs) < 0)
|
||||
if (device_active()->getregs(regs) < 0)
|
||||
return gdb_send("E00");
|
||||
|
||||
gdb_packet_start();
|
||||
|
@ -371,7 +370,7 @@ static int single_step(char *buf)
|
|||
printf("Single stepping\n");
|
||||
|
||||
if (run_set_pc(buf) < 0 ||
|
||||
gdb_device->control(DEVICE_CTL_STEP) < 0)
|
||||
device_active()->control(DEVICE_CTL_STEP) < 0)
|
||||
gdb_send("E00");
|
||||
|
||||
return run_final_status();
|
||||
|
@ -382,13 +381,13 @@ static int run(char *buf)
|
|||
printf("Running\n");
|
||||
|
||||
if (run_set_pc(buf) < 0 ||
|
||||
gdb_device->control(DEVICE_CTL_RUN) < 0) {
|
||||
device_active()->control(DEVICE_CTL_RUN) < 0) {
|
||||
gdb_send("E00");
|
||||
return run_final_status();
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
device_status_t status = gdb_device->wait(0);
|
||||
device_status_t status = device_active()->wait(0);
|
||||
|
||||
if (status == DEVICE_STATUS_ERROR) {
|
||||
gdb_send("E00");
|
||||
|
@ -417,13 +416,13 @@ static int run(char *buf)
|
|||
}
|
||||
|
||||
out:
|
||||
if (gdb_device->control(DEVICE_CTL_HALT) < 0)
|
||||
if (device_active()->control(DEVICE_CTL_HALT) < 0)
|
||||
gdb_send("E00");
|
||||
|
||||
return run_final_status();
|
||||
}
|
||||
|
||||
static int process_command(char *buf, int len)
|
||||
static int process_gdb_command(char *buf, int len)
|
||||
{
|
||||
switch (buf[0]) {
|
||||
case '?': /* Return target halt reason */
|
||||
|
@ -457,7 +456,7 @@ static int process_command(char *buf, int len)
|
|||
return gdb_send("");
|
||||
}
|
||||
|
||||
static void reader_loop(void)
|
||||
static void gdb_reader_loop(void)
|
||||
{
|
||||
for (;;) {
|
||||
char buf[1024];
|
||||
|
@ -515,12 +514,12 @@ static void reader_loop(void)
|
|||
if (gdb_flush() < 0)
|
||||
return;
|
||||
|
||||
if (len && process_command(buf, len) < 0)
|
||||
if (len && process_gdb_command(buf, len) < 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int gdb_server(const struct device *dev, int port)
|
||||
static int gdb_server(int port)
|
||||
{
|
||||
int sock;
|
||||
int client;
|
||||
|
@ -573,9 +572,37 @@ int gdb_server(const struct device *dev, int port)
|
|||
gdb_head = 0;
|
||||
gdb_tail = 0;
|
||||
gdb_outlen = 0;
|
||||
gdb_device = dev;
|
||||
|
||||
reader_loop();
|
||||
gdb_reader_loop();
|
||||
|
||||
return gdb_errno ? -1 : 0;
|
||||
}
|
||||
|
||||
static int cmd_gdb(char **arg)
|
||||
{
|
||||
char *port_text = get_arg(arg);
|
||||
int port = 2000;
|
||||
|
||||
if (port_text)
|
||||
port = atoi(port_text);
|
||||
|
||||
if (port <= 0 || port > 65535) {
|
||||
fprintf(stderr, "gdb: invalid port: %d\n", port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return gdb_server(port);
|
||||
}
|
||||
|
||||
static struct command command_gdb = {
|
||||
.name = "gdb",
|
||||
.func = cmd_gdb,
|
||||
.help =
|
||||
"gdb [port]\n"
|
||||
" Run a GDB remote stub on the given TCP/IP port.\n"
|
||||
};
|
||||
|
||||
void gdb_init(void)
|
||||
{
|
||||
register_command(&command_gdb);
|
||||
}
|
||||
|
|
8
gdb.h
8
gdb.h
|
@ -19,11 +19,7 @@
|
|||
#ifndef GDB_H_
|
||||
#define GDB_H_
|
||||
|
||||
/* Start a GDB remote stub, listening on the given TCP/IP port. It will
|
||||
* wait for GDB to connect, and then return once GDB has disconnected.
|
||||
*
|
||||
* Returns 0 for success, or -1 in the case of an error.
|
||||
*/
|
||||
int gdb_server(const struct device *dev, int port);
|
||||
/* Register the "gdb" command */
|
||||
void gdb_init(void);
|
||||
|
||||
#endif
|
||||
|
|
883
main.c
883
main.c
|
@ -30,872 +30,6 @@
|
|||
#include "util.h"
|
||||
#include "gdb.h"
|
||||
|
||||
static const struct device *msp430_dev;
|
||||
|
||||
/**********************************************************************
|
||||
* Modification tracking and prompting
|
||||
*/
|
||||
|
||||
static int syms_are_modified;
|
||||
|
||||
static int syms_modify_check(voiv)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
if (!syms_are_modified)
|
||||
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;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Command definitions
|
||||
*/
|
||||
|
||||
#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("(r%02d: %04x) ", k, regs[k]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_md(char **arg)
|
||||
{
|
||||
char *off_text = get_arg(arg);
|
||||
char *len_text = get_arg(arg);
|
||||
int offset = 0;
|
||||
int length = 0x40;
|
||||
|
||||
if (!off_text) {
|
||||
fprintf(stderr, "md: offset must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_exp(off_text, &offset) < 0) {
|
||||
fprintf(stderr, "md: can't parse offset: %s\n", off_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len_text) {
|
||||
if (addr_exp(len_text, &length) < 0) {
|
||||
fprintf(stderr, "md: can't parse length: %s\n",
|
||||
len_text);
|
||||
return -1;
|
||||
}
|
||||
} else if (offset + length > 0x10000) {
|
||||
length = 0x10000 - offset;
|
||||
}
|
||||
|
||||
if (offset < 0 || length <= 0 || (offset + length) > 0x10000) {
|
||||
fprintf(stderr, "md: memory out of range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (length) {
|
||||
u_int8_t buf[128];
|
||||
int blen = length > sizeof(buf) ? sizeof(buf) : length;
|
||||
|
||||
if (msp430_dev->readmem(offset, buf, blen) < 0)
|
||||
return -1;
|
||||
hexdump(offset, buf, blen);
|
||||
|
||||
offset += blen;
|
||||
length -= blen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_mw(char **arg)
|
||||
{
|
||||
char *off_text = get_arg(arg);
|
||||
char *byte_text;
|
||||
int offset = 0;
|
||||
int length = 0;
|
||||
u_int8_t buf[1024];
|
||||
|
||||
if (!off_text) {
|
||||
fprintf(stderr, "md: offset must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_exp(off_text, &offset) < 0) {
|
||||
fprintf(stderr, "md: can't parse offset: %s\n", off_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((byte_text = get_arg(arg))) {
|
||||
if (length >= sizeof(buf)) {
|
||||
fprintf(stderr, "md: maximum length exceeded\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[length++] = strtoul(byte_text, NULL, 16);
|
||||
}
|
||||
|
||||
if (!length)
|
||||
return 0;
|
||||
|
||||
if (offset < 0 || (offset + length) > 0x10000) {
|
||||
fprintf(stderr, "md: memory out of range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msp430_dev->writemem(offset, buf, length) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_dis(char **arg)
|
||||
{
|
||||
char *off_text = get_arg(arg);
|
||||
char *len_text = get_arg(arg);
|
||||
int offset = 0;
|
||||
int length = 0x40;
|
||||
u_int8_t buf[4096];
|
||||
|
||||
if (!off_text) {
|
||||
fprintf(stderr, "md: offset must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_exp(off_text, &offset) < 0) {
|
||||
fprintf(stderr, "dis: can't parse offset: %s\n", off_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len_text) {
|
||||
if (addr_exp(len_text, &length) < 0) {
|
||||
fprintf(stderr, "dis: can't parse length: %s\n",
|
||||
len_text);
|
||||
return -1;
|
||||
}
|
||||
} else if (offset + length > 0x10000) {
|
||||
length = 0x10000 - offset;
|
||||
}
|
||||
|
||||
if (offset < 0 || length <= 0 || length > sizeof(buf) ||
|
||||
(offset + length) > 0x10000) {
|
||||
fprintf(stderr, "dis: memory out of range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msp430_dev->readmem(offset, buf, length) < 0)
|
||||
return -1;
|
||||
|
||||
disassemble(offset, (u_int8_t *)buf, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FILE *hexout_file;
|
||||
static u_int16_t hexout_addr;
|
||||
static u_int8_t hexout_buf[16];
|
||||
static int hexout_len;
|
||||
|
||||
static int hexout_start(const char *filename)
|
||||
{
|
||||
hexout_file = fopen(filename, "w");
|
||||
if (!hexout_file) {
|
||||
perror("hexout: couldn't open output file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hexout_flush(void)
|
||||
{
|
||||
int i;
|
||||
int cksum = 0;
|
||||
|
||||
if (!hexout_len)
|
||||
return 0;
|
||||
|
||||
if (fprintf(hexout_file, ":%02X%04X00", hexout_len, hexout_addr) < 0)
|
||||
goto fail;
|
||||
cksum += hexout_len;
|
||||
cksum += hexout_addr & 0xff;
|
||||
cksum += hexout_addr >> 8;
|
||||
|
||||
for (i = 0; i < hexout_len; i++) {
|
||||
if (fprintf(hexout_file, "%02X", hexout_buf[i]) < 0)
|
||||
goto fail;
|
||||
cksum += hexout_buf[i];
|
||||
}
|
||||
|
||||
if (fprintf(hexout_file, "%02X\n", ~(cksum - 1) & 0xff) < 0)
|
||||
goto fail;
|
||||
|
||||
hexout_len = 0;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
perror("hexout: can't write HEX data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int hexout_feed(u_int16_t addr, const u_int8_t *buf, int len)
|
||||
{
|
||||
while (len) {
|
||||
int count;
|
||||
|
||||
if ((hexout_addr + hexout_len != addr ||
|
||||
hexout_len >= sizeof(hexout_buf)) &&
|
||||
hexout_flush() < 0)
|
||||
return -1;
|
||||
|
||||
if (!hexout_len)
|
||||
hexout_addr = addr;
|
||||
|
||||
count = sizeof(hexout_buf) - hexout_len;
|
||||
if (count > len)
|
||||
count = len;
|
||||
|
||||
memcpy(hexout_buf + hexout_len, buf, count);
|
||||
hexout_len += count;
|
||||
|
||||
addr += count;
|
||||
buf += count;
|
||||
len -= count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_hexout(char **arg)
|
||||
{
|
||||
char *off_text = get_arg(arg);
|
||||
char *len_text = get_arg(arg);
|
||||
char *filename = *arg;
|
||||
int off;
|
||||
int length;
|
||||
|
||||
if (!(off_text && len_text && *filename)) {
|
||||
fprintf(stderr, "hexout: need offset, length and filename\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_exp(off_text, &off) < 0 ||
|
||||
addr_exp(len_text, &length) < 0)
|
||||
return -1;
|
||||
|
||||
if (hexout_start(filename) < 0)
|
||||
return -1;
|
||||
|
||||
while (length) {
|
||||
u_int8_t buf[128];
|
||||
int count = length;
|
||||
|
||||
if (count > sizeof(buf))
|
||||
count = sizeof(buf);
|
||||
|
||||
printf("Reading %d bytes from 0x%04x...\n", count, off);
|
||||
if (msp430_dev->readmem(off, buf, count) < 0) {
|
||||
perror("hexout: can't read memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (hexout_feed(off, buf, count) < 0)
|
||||
goto fail;
|
||||
|
||||
length -= count;
|
||||
off += count;
|
||||
}
|
||||
|
||||
if (hexout_flush() < 0)
|
||||
goto fail;
|
||||
if (fclose(hexout_file) < 0) {
|
||||
perror("hexout: error on close");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
fclose(hexout_file);
|
||||
unlink(filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cmd_reset(char **arg)
|
||||
{
|
||||
return msp430_dev->control(DEVICE_CTL_RESET);
|
||||
}
|
||||
|
||||
static int cmd_regs(char **arg)
|
||||
{
|
||||
u_int16_t regs[DEVICE_NUM_REGS];
|
||||
u_int8_t code[16];
|
||||
|
||||
if (msp430_dev->getregs(regs) < 0)
|
||||
return -1;
|
||||
show_regs(regs);
|
||||
|
||||
/* Try to disassemble the instruction at PC */
|
||||
if (msp430_dev->readmem(regs[0], code, sizeof(code)) < 0)
|
||||
return 0;
|
||||
|
||||
disassemble(regs[0], (u_int8_t *)code, sizeof(code));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_run(char **arg)
|
||||
{
|
||||
char *bp_text = get_arg(arg);
|
||||
int bp_addr;
|
||||
device_status_t status;
|
||||
|
||||
if (bp_text) {
|
||||
if (addr_exp(bp_text, &bp_addr) < 0) {
|
||||
fprintf(stderr, "run: can't parse breakpoint: %s\n",
|
||||
bp_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
msp430_dev->breakpoint(bp_addr);
|
||||
}
|
||||
|
||||
if (msp430_dev->control(bp_text ?
|
||||
DEVICE_CTL_RUN_BP : 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");
|
||||
|
||||
status = msp430_dev->wait(1);
|
||||
if (status == DEVICE_STATUS_INTR)
|
||||
printf("\n");
|
||||
|
||||
if (msp430_dev->control(DEVICE_CTL_HALT) < 0)
|
||||
return -1;
|
||||
|
||||
return cmd_regs(NULL);
|
||||
}
|
||||
|
||||
static int cmd_set(char **arg)
|
||||
{
|
||||
char *reg_text = get_arg(arg);
|
||||
char *val_text = get_arg(arg);
|
||||
int reg;
|
||||
int value = 0;
|
||||
u_int16_t regs[DEVICE_NUM_REGS];
|
||||
|
||||
if (!(reg_text && val_text)) {
|
||||
fprintf(stderr, "set: must specify a register and a value\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (*reg_text && !isdigit(*reg_text))
|
||||
reg_text++;
|
||||
reg = atoi(reg_text);
|
||||
|
||||
if (addr_exp(val_text, &value) < 0) {
|
||||
fprintf(stderr, "set: can't parse value: %s\n", val_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (reg < 0 || reg >= DEVICE_NUM_REGS) {
|
||||
fprintf(stderr, "set: register out of range: %d\n", reg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msp430_dev->getregs(regs) < 0)
|
||||
return -1;
|
||||
regs[reg] = value;
|
||||
if (msp430_dev->setregs(regs) < 0)
|
||||
return -1;
|
||||
|
||||
show_regs(regs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_step(char **arg)
|
||||
{
|
||||
char *count_text = get_arg(arg);
|
||||
int count = 1;
|
||||
|
||||
if (count_text)
|
||||
count = atoi(count_text);
|
||||
|
||||
while (count > 0) {
|
||||
if (msp430_dev->control(DEVICE_CTL_STEP) < 0)
|
||||
return -1;
|
||||
count--;
|
||||
}
|
||||
|
||||
return cmd_regs(NULL);
|
||||
}
|
||||
|
||||
static u_int8_t prog_buf[128];
|
||||
static u_int16_t prog_addr;
|
||||
static int prog_len;
|
||||
static int prog_have_erased;
|
||||
|
||||
static void prog_init(void)
|
||||
{
|
||||
prog_len = 0;
|
||||
prog_have_erased = 0;
|
||||
}
|
||||
|
||||
static int prog_flush(void)
|
||||
{
|
||||
while (prog_len) {
|
||||
int wlen = prog_len;
|
||||
|
||||
/* Writing across this address seems to cause a hang */
|
||||
if (prog_addr < 0x999a && wlen + prog_addr > 0x999a)
|
||||
wlen = 0x999a - prog_addr;
|
||||
|
||||
if (!prog_have_erased) {
|
||||
printf("Erasing...\n");
|
||||
if (msp430_dev->control(DEVICE_CTL_ERASE) < 0)
|
||||
return -1;
|
||||
prog_have_erased = 1;
|
||||
}
|
||||
|
||||
printf("Writing %3d bytes to %04x...\n", wlen, prog_addr);
|
||||
if (msp430_dev->writemem(prog_addr, prog_buf, wlen) < 0)
|
||||
return -1;
|
||||
|
||||
memmove(prog_buf, prog_buf + wlen, prog_len - wlen);
|
||||
prog_len -= wlen;
|
||||
prog_addr += wlen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prog_feed(u_int16_t addr, const u_int8_t *data, int len)
|
||||
{
|
||||
/* Flush if this section is discontiguous */
|
||||
if (prog_len && prog_addr + prog_len != addr && prog_flush() < 0)
|
||||
return -1;
|
||||
|
||||
if (!prog_len)
|
||||
prog_addr = addr;
|
||||
|
||||
/* Add the buffer in piece by piece, flushing when it gets
|
||||
* full.
|
||||
*/
|
||||
while (len) {
|
||||
int count = sizeof(prog_buf) - prog_len;
|
||||
|
||||
if (count > len)
|
||||
count = len;
|
||||
|
||||
if (!count) {
|
||||
if (prog_flush() < 0)
|
||||
return -1;
|
||||
} else {
|
||||
memcpy(prog_buf + prog_len, data, count);
|
||||
prog_len += count;
|
||||
data += count;
|
||||
len -= count;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_erase(char **arg)
|
||||
{
|
||||
if (msp430_dev->control(DEVICE_CTL_HALT) < 0)
|
||||
return -1;
|
||||
|
||||
printf("Erasing...\n");
|
||||
return msp430_dev->control(DEVICE_CTL_ERASE);
|
||||
}
|
||||
|
||||
static int cmd_prog(char **arg)
|
||||
{
|
||||
FILE *in;
|
||||
int result = 0;
|
||||
|
||||
if (is_interactive() && syms_modify_check())
|
||||
return 0;
|
||||
|
||||
in = fopen(*arg, "r");
|
||||
if (!in) {
|
||||
fprintf(stderr, "prog: %s: %s\n", *arg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msp430_dev->control(DEVICE_CTL_HALT) < 0) {
|
||||
fclose(in);
|
||||
return -1;
|
||||
}
|
||||
|
||||
prog_init();
|
||||
|
||||
if (elf32_check(in)) {
|
||||
result = elf32_extract(in, prog_feed);
|
||||
stab_clear();
|
||||
elf32_syms(in);
|
||||
} else if (ihex_check(in)) {
|
||||
result = ihex_extract(in, prog_feed);
|
||||
} else {
|
||||
fprintf(stderr, "prog: %s: unknown file type\n", *arg);
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
|
||||
if (prog_flush() < 0)
|
||||
return -1;
|
||||
|
||||
if (msp430_dev->control(DEVICE_CTL_RESET) < 0) {
|
||||
fprintf(stderr, "prog: failed to reset after programming\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
syms_are_modified = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int cmd_eval(char **arg)
|
||||
{
|
||||
int addr;
|
||||
u_int16_t offset;
|
||||
char name[64];
|
||||
|
||||
if (addr_exp(*arg, &addr) < 0) {
|
||||
fprintf(stderr, "=: can't parse: %s\n", *arg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("0x%04x", addr);
|
||||
if (!stab_nearest(addr, name, sizeof(name), &offset)) {
|
||||
printf(" = %s", name);
|
||||
if (offset)
|
||||
printf("+0x%x", offset);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_sym_load_add(int clear, char **arg)
|
||||
{
|
||||
FILE *in;
|
||||
int result = 0;
|
||||
|
||||
if (clear && is_interactive() && syms_modify_check())
|
||||
return 0;
|
||||
|
||||
in = fopen(*arg, "r");
|
||||
if (!in) {
|
||||
fprintf(stderr, "sym: %s: %s\n", *arg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (clear)
|
||||
stab_clear();
|
||||
|
||||
if (elf32_check(in))
|
||||
result = elf32_syms(in);
|
||||
else if (symmap_check(in))
|
||||
result = symmap_syms(in);
|
||||
else
|
||||
fprintf(stderr, "sym: %s: unknown file type\n", *arg);
|
||||
|
||||
fclose(in);
|
||||
|
||||
syms_are_modified = !clear;
|
||||
return result;
|
||||
}
|
||||
|
||||
static FILE *savemap_out;
|
||||
|
||||
static int savemap_write(const char *name, u_int16_t value)
|
||||
{
|
||||
if (fprintf(savemap_out, "%08x t %s\n", value, name) < 0) {
|
||||
fprintf(stderr, "sym: error writing symbols: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_sym_savemap(char **arg)
|
||||
{
|
||||
char *fname = get_arg(arg);
|
||||
|
||||
if (!fname) {
|
||||
fprintf(stderr, "sym: filename required to save map\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
savemap_out = fopen(fname, "w");
|
||||
if (!savemap_out) {
|
||||
fprintf(stderr, "sym: couldn't write to %s: %s\n", fname,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stab_enum(savemap_write) < 0)
|
||||
return -1;
|
||||
|
||||
if (fclose(savemap_out) < 0) {
|
||||
fprintf(stderr, "sym: error closing %s: %s\n", fname,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
syms_are_modified = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int printsym(const char *name, u_int16_t value)
|
||||
{
|
||||
printf("0x%04x: %s\n", value, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_sym(char **arg)
|
||||
{
|
||||
char *subcmd = get_arg(arg);
|
||||
|
||||
if (!subcmd) {
|
||||
fprintf(stderr, "sym: need to specify a subcommand "
|
||||
"(try \"help sym\")\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcasecmp(subcmd, "clear")) {
|
||||
if (is_interactive() && syms_modify_check())
|
||||
return 0;
|
||||
stab_clear();
|
||||
syms_are_modified = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcasecmp(subcmd, "set")) {
|
||||
char *name = get_arg(arg);
|
||||
char *val_text = get_arg(arg);
|
||||
int value;
|
||||
|
||||
if (!(name && val_text)) {
|
||||
fprintf(stderr, "sym: need a name and value to set "
|
||||
"symbol table entries\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_exp(val_text, &value) < 0) {
|
||||
fprintf(stderr, "sym: can't parse value: %s\n",
|
||||
val_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stab_set(name, value) < 0)
|
||||
return -1;
|
||||
|
||||
syms_are_modified = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcasecmp(subcmd, "del")) {
|
||||
char *name = get_arg(arg);
|
||||
|
||||
if (!name) {
|
||||
fprintf(stderr, "sym: need a name to delete "
|
||||
"symbol table entries\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stab_del(name) < 0)
|
||||
return -1;
|
||||
|
||||
syms_are_modified = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcasecmp(subcmd, "load"))
|
||||
return cmd_sym_load_add(1, arg);
|
||||
if (!strcasecmp(subcmd, "add"))
|
||||
return cmd_sym_load_add(0, arg);
|
||||
|
||||
if (!strcasecmp(subcmd, "savemap"))
|
||||
return cmd_sym_savemap(arg);
|
||||
|
||||
if (!strcasecmp(subcmd, "find")) {
|
||||
char *expr = get_arg(arg);
|
||||
|
||||
if (!expr) {
|
||||
stab_enum(printsym);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return stab_re_search(expr, printsym) < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "sym: unknown subcommand: %s\n", subcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cmd_gdb(char **arg)
|
||||
{
|
||||
char *port_text = get_arg(arg);
|
||||
int port = 2000;
|
||||
|
||||
if (port_text)
|
||||
port = atoi(port_text);
|
||||
|
||||
if (port <= 0 || port > 65535) {
|
||||
fprintf(stderr, "gdb: invalid port: %d\n", port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return gdb_server(msp430_dev, port);
|
||||
}
|
||||
|
||||
static int cmd_read(char **arg)
|
||||
{
|
||||
char *filename = get_arg(arg);
|
||||
FILE *in;
|
||||
char buf[1024];
|
||||
|
||||
if (!filename) {
|
||||
fprintf(stderr, "read: filename must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
while (*cmd && isspace(*cmd))
|
||||
cmd++;
|
||||
|
||||
if (*cmd == '#')
|
||||
continue;
|
||||
|
||||
if (process_command(cmd, 0) < 0) {
|
||||
fprintf(stderr, "read: error processing %s\n",
|
||||
filename);
|
||||
fclose(in);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct command all_commands[] = {
|
||||
{"=", cmd_eval,
|
||||
"= <expression>\n"
|
||||
" Evaluate an expression using the symbol table.\n"},
|
||||
{"dis", cmd_dis,
|
||||
"dis <address> [length]\n"
|
||||
" Disassemble a section of memory.\n"},
|
||||
{"erase", cmd_erase,
|
||||
"erase\n"
|
||||
" Erase the device under test.\n"},
|
||||
{"gdb", cmd_gdb,
|
||||
"gdb [port]\n"
|
||||
" Run a GDB remote stub on the given TCP/IP port.\n"},
|
||||
{"help", cmd_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"},
|
||||
{"hexout", cmd_hexout,
|
||||
"hexout <address> <length> <filename.hex>\n"
|
||||
" Save a region of memory into a HEX file.\n"},
|
||||
{"md", cmd_md,
|
||||
"md <address> [length]\n"
|
||||
" Read the specified number of bytes from memory at the given\n"
|
||||
" address, and display a hexdump.\n"},
|
||||
{"mw", cmd_mw,
|
||||
"mw <address> 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 <filename>\n"
|
||||
" Erase the device and flash the data contained in a binary file.\n"
|
||||
" This command also loads symbols from the file, if available.\n"},
|
||||
{"read", cmd_read,
|
||||
"read <filename>\n"
|
||||
" Read commands from a file and evaluate them.\n"},
|
||||
{"regs", cmd_regs,
|
||||
"regs\n"
|
||||
" Read and display the current register contents.\n"},
|
||||
{"reset", cmd_reset,
|
||||
"reset\n"
|
||||
" Reset (and halt) the CPU.\n"},
|
||||
{"run", cmd_run,
|
||||
"run [breakpoint]\n"
|
||||
" Run the CPU until either a specified breakpoint occurs or the\n"
|
||||
" command is interrupted.\n"},
|
||||
{"set", cmd_set,
|
||||
"set <register> <value>\n"
|
||||
" Change the value of a CPU register.\n"},
|
||||
{"step", cmd_step,
|
||||
"step [count]\n"
|
||||
" Single-step the CPU, and display the register state.\n"},
|
||||
{"sym", cmd_sym,
|
||||
"sym clear\n"
|
||||
" Clear the symbol table.\n"
|
||||
"sym set <name> <value>\n"
|
||||
" Set or overwrite the value of a symbol.\n"
|
||||
"sym del <name>\n"
|
||||
" Delete a symbol from the symbol table.\n"
|
||||
"sym load <filename>\n"
|
||||
" Load symbols from the given file.\n"
|
||||
"sym add <filename>\n"
|
||||
" Load additional symbols from the given file.\n"
|
||||
"sym savemap <filename>\n"
|
||||
" Save the current symbols to a BSD-style symbol file.\n"
|
||||
"sym find <regex>\n"
|
||||
" Search for symbols by regular expression.\n"},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
static void usage(const char *progname)
|
||||
{
|
||||
fprintf(stderr,
|
||||
|
@ -934,6 +68,7 @@ int main(int argc, char **argv)
|
|||
const struct fet_transport *trans;
|
||||
const char *uif_device = NULL;
|
||||
const char *bsl_device = NULL;
|
||||
const struct device *msp430_dev = NULL;
|
||||
int opt;
|
||||
int flags = 0;
|
||||
int want_jtag = 0;
|
||||
|
@ -999,6 +134,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
parse_init();
|
||||
gdb_init();
|
||||
if (stab_init() < 0)
|
||||
return -1;
|
||||
|
||||
|
@ -1026,23 +162,22 @@ int main(int argc, char **argv)
|
|||
msp430_dev = fet_open(trans, flags, vcc_mv);
|
||||
}
|
||||
|
||||
if (!msp430_dev)
|
||||
if (!msp430_dev) {
|
||||
stab_exit();
|
||||
return -1;
|
||||
}
|
||||
|
||||
device_init(msp430_dev);
|
||||
|
||||
/* Process commands */
|
||||
if (optind < argc) {
|
||||
while (optind < argc)
|
||||
process_command(argv[optind++], 0);
|
||||
} else {
|
||||
printf("\n");
|
||||
cmd_help(NULL);
|
||||
|
||||
do {
|
||||
reader_loop();
|
||||
} while (syms_modify_check());
|
||||
reader_loop();
|
||||
}
|
||||
|
||||
msp430_dev->close();
|
||||
device_exit();
|
||||
stab_exit();
|
||||
|
||||
return 0;
|
||||
|
|
276
stab.c
276
stab.c
|
@ -22,7 +22,10 @@
|
|||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <regex.h>
|
||||
#include <errno.h>
|
||||
#include "stab.h"
|
||||
#include "util.h"
|
||||
#include "binfile.h"
|
||||
#include "btree.h"
|
||||
|
||||
struct sym_key {
|
||||
|
@ -100,33 +103,37 @@ static const struct btree_def addr_table_def = {
|
|||
static btree_t sym_table;
|
||||
static btree_t addr_table;
|
||||
|
||||
/************************************************************************
|
||||
* Public interface
|
||||
*/
|
||||
|
||||
int stab_init(void)
|
||||
static int cmd_eval(char **arg)
|
||||
{
|
||||
sym_table = btree_alloc(&sym_table_def);
|
||||
if (!sym_table) {
|
||||
fprintf(stderr, "stab: failed to allocate symbol table\n");
|
||||
int addr;
|
||||
u_int16_t offset;
|
||||
char name[64];
|
||||
|
||||
if (addr_exp(*arg, &addr) < 0) {
|
||||
fprintf(stderr, "=: can't parse: %s\n", *arg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr_table = btree_alloc(&addr_table_def);
|
||||
if (!addr_table) {
|
||||
fprintf(stderr, "stab: failed to allocate address table\n");
|
||||
btree_free(sym_table);
|
||||
return -1;
|
||||
printf("0x%04x", addr);
|
||||
if (!stab_nearest(addr, name, sizeof(name), &offset)) {
|
||||
printf(" = %s", name);
|
||||
if (offset)
|
||||
printf("+0x%x", offset);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stab_exit(void)
|
||||
{
|
||||
btree_free(sym_table);
|
||||
btree_free(addr_table);
|
||||
}
|
||||
static struct command command_eval = {
|
||||
.name = "=",
|
||||
.func = cmd_eval,
|
||||
.help =
|
||||
"= <expression>\n"
|
||||
" Evaluate an expression using the symbol table.\n"
|
||||
};
|
||||
|
||||
typedef int (*stab_callback_t)(const char *name, u_int16_t value);
|
||||
|
||||
void stab_clear(void)
|
||||
{
|
||||
|
@ -134,10 +141,11 @@ void stab_clear(void)
|
|||
btree_clear(addr_table);
|
||||
}
|
||||
|
||||
int stab_set(const char *name, u_int16_t addr)
|
||||
int stab_set(const char *name, int value)
|
||||
{
|
||||
struct sym_key skey;
|
||||
struct addr_key akey;
|
||||
u_int16_t addr = value;
|
||||
u_int16_t old_addr;
|
||||
|
||||
sym_key_init(&skey, name);
|
||||
|
@ -161,7 +169,7 @@ int stab_set(const char *name, u_int16_t addr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int stab_get(const char *name, u_int16_t *value)
|
||||
static int stab_get(const char *name, u_int16_t *value)
|
||||
{
|
||||
struct sym_key skey;
|
||||
|
||||
|
@ -172,7 +180,7 @@ int stab_get(const char *name, u_int16_t *value)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int stab_del(const char *name)
|
||||
static int stab_del(const char *name)
|
||||
{
|
||||
struct sym_key skey;
|
||||
u_int16_t value;
|
||||
|
@ -192,7 +200,7 @@ int stab_del(const char *name)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int stab_enum(stab_callback_t cb)
|
||||
static int stab_enum(stab_callback_t cb)
|
||||
{
|
||||
struct sym_key skey;
|
||||
u_int16_t value;
|
||||
|
@ -210,7 +218,7 @@ int stab_enum(stab_callback_t cb)
|
|||
return count;
|
||||
}
|
||||
|
||||
int stab_re_search(const char *regex, stab_callback_t cb)
|
||||
static int stab_re_search(const char *regex, stab_callback_t cb)
|
||||
{
|
||||
struct sym_key skey;
|
||||
u_int16_t value;
|
||||
|
@ -261,3 +269,225 @@ int stab_nearest(u_int16_t addr, char *ret_name, int max_len,
|
|||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cmd_sym_load_add(int clear, char **arg)
|
||||
{
|
||||
FILE *in;
|
||||
int result = 0;
|
||||
|
||||
if (clear && modify_prompt(MODIFY_SYMS))
|
||||
return 0;
|
||||
|
||||
in = fopen(*arg, "r");
|
||||
if (!in) {
|
||||
fprintf(stderr, "sym: %s: %s\n", *arg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (clear)
|
||||
stab_clear();
|
||||
|
||||
if (elf32_check(in))
|
||||
result = elf32_syms(in, stab_set);
|
||||
else if (symmap_check(in))
|
||||
result = symmap_syms(in, stab_set);
|
||||
else
|
||||
fprintf(stderr, "sym: %s: unknown file type\n", *arg);
|
||||
|
||||
fclose(in);
|
||||
|
||||
if (clear)
|
||||
modify_clear(MODIFY_SYMS);
|
||||
else
|
||||
modify_set(MODIFY_SYMS);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static FILE *savemap_out;
|
||||
|
||||
static int savemap_write(const char *name, u_int16_t value)
|
||||
{
|
||||
if (fprintf(savemap_out, "%08x t %s\n", value, name) < 0) {
|
||||
fprintf(stderr, "sym: error writing symbols: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_sym_savemap(char **arg)
|
||||
{
|
||||
char *fname = get_arg(arg);
|
||||
|
||||
if (!fname) {
|
||||
fprintf(stderr, "sym: filename required to save map\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
savemap_out = fopen(fname, "w");
|
||||
if (!savemap_out) {
|
||||
fprintf(stderr, "sym: couldn't write to %s: %s\n", fname,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stab_enum(savemap_write) < 0)
|
||||
return -1;
|
||||
|
||||
if (fclose(savemap_out) < 0) {
|
||||
fprintf(stderr, "sym: error closing %s: %s\n", fname,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
modify_clear(MODIFY_SYMS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int printsym(const char *name, u_int16_t value)
|
||||
{
|
||||
printf("0x%04x: %s\n", value, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_sym(char **arg)
|
||||
{
|
||||
char *subcmd = get_arg(arg);
|
||||
|
||||
if (!subcmd) {
|
||||
fprintf(stderr, "sym: need to specify a subcommand "
|
||||
"(try \"help sym\")\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcasecmp(subcmd, "clear")) {
|
||||
if (modify_prompt(MODIFY_SYMS))
|
||||
return 0;
|
||||
stab_clear();
|
||||
modify_clear(MODIFY_SYMS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcasecmp(subcmd, "set")) {
|
||||
char *name = get_arg(arg);
|
||||
char *val_text = get_arg(arg);
|
||||
int value;
|
||||
|
||||
if (!(name && val_text)) {
|
||||
fprintf(stderr, "sym: need a name and value to set "
|
||||
"symbol table entries\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_exp(val_text, &value) < 0) {
|
||||
fprintf(stderr, "sym: can't parse value: %s\n",
|
||||
val_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stab_set(name, value) < 0)
|
||||
return -1;
|
||||
|
||||
modify_set(MODIFY_SYMS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcasecmp(subcmd, "del")) {
|
||||
char *name = get_arg(arg);
|
||||
|
||||
if (!name) {
|
||||
fprintf(stderr, "sym: need a name to delete "
|
||||
"symbol table entries\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stab_del(name) < 0)
|
||||
return -1;
|
||||
|
||||
modify_set(MODIFY_SYMS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcasecmp(subcmd, "load"))
|
||||
return cmd_sym_load_add(1, arg);
|
||||
if (!strcasecmp(subcmd, "add"))
|
||||
return cmd_sym_load_add(0, arg);
|
||||
|
||||
if (!strcasecmp(subcmd, "savemap"))
|
||||
return cmd_sym_savemap(arg);
|
||||
|
||||
if (!strcasecmp(subcmd, "find")) {
|
||||
char *expr = get_arg(arg);
|
||||
|
||||
if (!expr) {
|
||||
stab_enum(printsym);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return stab_re_search(expr, printsym) < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
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 <name> <value>\n"
|
||||
" Set or overwrite the value of a symbol.\n"
|
||||
"sym del <name>\n"
|
||||
" Delete a symbol from the symbol table.\n"
|
||||
"sym load <filename>\n"
|
||||
" Load symbols from the given file.\n"
|
||||
"sym add <filename>\n"
|
||||
" Load additional symbols from the given file.\n"
|
||||
"sym savemap <filename>\n"
|
||||
" Save the current symbols to a BSD-style symbol file.\n"
|
||||
"sym find <regex>\n"
|
||||
" Search for symbols by regular expression.\n"
|
||||
};
|
||||
|
||||
static int lookup_token(const char *name, int *value)
|
||||
{
|
||||
u_int16_t val;
|
||||
|
||||
if (stab_get(name, &val) < 0)
|
||||
return -1;
|
||||
|
||||
*value = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stab_init(void)
|
||||
{
|
||||
sym_table = btree_alloc(&sym_table_def);
|
||||
if (!sym_table) {
|
||||
fprintf(stderr, "stab: failed to allocate symbol table\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr_table = btree_alloc(&addr_table_def);
|
||||
if (!addr_table) {
|
||||
fprintf(stderr, "stab: failed to allocate address table\n");
|
||||
btree_free(sym_table);
|
||||
return -1;
|
||||
}
|
||||
|
||||
set_token_func(lookup_token);
|
||||
register_command(&command_eval);
|
||||
register_command(&command_sym);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stab_exit(void)
|
||||
{
|
||||
btree_free(sym_table);
|
||||
btree_free(addr_table);
|
||||
}
|
||||
|
|
33
stab.h
33
stab.h
|
@ -31,38 +31,7 @@ void stab_exit(void);
|
|||
void stab_clear(void);
|
||||
|
||||
/* Set a symbol in the table. Returns 0 on success, or -1 on error. */
|
||||
int stab_set(const char *name, u_int16_t value);
|
||||
|
||||
/* Fetch the value of a symbol. Returns 0 on success, or -1 if no such
|
||||
* symbol exists.
|
||||
*/
|
||||
int stab_get(const char *name, u_int16_t *value);
|
||||
|
||||
/* Delete a symbol from the symbol table.
|
||||
*
|
||||
* Returns 0 if successful, -1 if no such symbol exists.
|
||||
*/
|
||||
int stab_del(const char *name);
|
||||
|
||||
/* Enumerate all symbols. Returns total symbol count, or -1 if a callback
|
||||
* invocation returns an error.
|
||||
*/
|
||||
typedef int (*stab_callback_t)(const char *name, u_int16_t value);
|
||||
|
||||
int stab_enum(stab_callback_t cb);
|
||||
|
||||
/* Search for a symbol by supplying a regular expression. The given
|
||||
* callback is invoked for each symbol matching the regex. Returns the
|
||||
* total number of symbols found, or -1 if an error occurs.
|
||||
*/
|
||||
int stab_re_search(const char *regex, stab_callback_t cb);
|
||||
|
||||
/* Parse an address expression and return an address. The text may be an
|
||||
* address, a symbol name or a combination (using + or -).
|
||||
*
|
||||
* Returns 0 if parsed successfully, -1 if an error occurs.
|
||||
*/
|
||||
int stab_parse(const char *text, int *addr);
|
||||
int stab_set(const char *name, int value);
|
||||
|
||||
/* Take an address and find the nearest symbol and offset (always
|
||||
* non-negative).
|
||||
|
|
5
symmap.c
5
symmap.c
|
@ -20,7 +20,6 @@
|
|||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "binfile.h"
|
||||
#include "stab.h"
|
||||
|
||||
int symmap_check(FILE *in)
|
||||
{
|
||||
|
@ -46,7 +45,7 @@ int symmap_check(FILE *in)
|
|||
return spc_count >= 2;
|
||||
}
|
||||
|
||||
int symmap_syms(FILE *in)
|
||||
int symmap_syms(FILE *in, symfunc_t cb)
|
||||
{
|
||||
rewind(in);
|
||||
char buf[128];
|
||||
|
@ -61,7 +60,7 @@ int symmap_syms(FILE *in)
|
|||
if (addr && name) {
|
||||
int addr_val = strtoul(addr, NULL, 16);
|
||||
|
||||
if (stab_set(name, addr_val) < 0)
|
||||
if (cb(name, addr_val) < 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
528
util.c
528
util.c
|
@ -33,10 +33,10 @@
|
|||
#include <readline/history.h>
|
||||
#endif
|
||||
|
||||
#include "stab.h"
|
||||
#include "util.h"
|
||||
|
||||
static struct option *option_list;
|
||||
static struct command *command_list;
|
||||
|
||||
void register_option(struct option *o)
|
||||
{
|
||||
|
@ -55,7 +55,24 @@ static struct option *find_option(const char *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int interactive_call;
|
||||
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)
|
||||
{
|
||||
|
@ -89,17 +106,6 @@ char *get_arg(char **text)
|
|||
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;
|
||||
|
@ -131,6 +137,141 @@ int process_command(char *arg, int interactive)
|
|||
return 0;
|
||||
}
|
||||
|
||||
const char *type_text(option_type_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case OPTION_BOOLEAN:
|
||||
return "boolean";
|
||||
|
||||
case OPTION_NUMERIC:
|
||||
return "numeric";
|
||||
|
||||
case OPTION_TEXT:
|
||||
return "text";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static const char *name_list[128];
|
||||
static int num_names;
|
||||
static int name_max_len;
|
||||
|
||||
static void name_start(void)
|
||||
{
|
||||
num_names = 0;
|
||||
name_max_len = 0;
|
||||
}
|
||||
|
||||
static void name_push(const char *text)
|
||||
{
|
||||
if (num_names < ARRAY_LEN(name_list)) {
|
||||
int len = strlen(text);
|
||||
|
||||
name_list[num_names++] = text;
|
||||
if (len > name_max_len)
|
||||
name_max_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
static int compare_name(const void *left, const void *right)
|
||||
{
|
||||
return strcasecmp(*(const char *const *)left,
|
||||
*(const char *const *)right);
|
||||
}
|
||||
|
||||
static void name_list_show(void)
|
||||
{
|
||||
int i;
|
||||
int max_len = name_max_len + 2;
|
||||
int rows, cols;
|
||||
|
||||
qsort(name_list, num_names, sizeof(name_list[0]),
|
||||
compare_name);
|
||||
|
||||
cols = 72 / max_len;
|
||||
rows = (num_names + cols - 1) / cols;
|
||||
|
||||
for (i = 0; i < rows; i++) {
|
||||
int j;
|
||||
|
||||
printf(" ");
|
||||
for (j = 0; j < cols; j++) {
|
||||
int k = j * rows + i;
|
||||
|
||||
if (k >= num_names)
|
||||
break;
|
||||
|
||||
printf("%s", name_list[k]);
|
||||
for (k = strlen(name_list[k]); k < max_len; k++)
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_help(char **arg)
|
||||
{
|
||||
const char *topic = get_arg(arg);
|
||||
|
||||
if (topic) {
|
||||
const struct command *cmd = find_command(topic);
|
||||
const struct option *opt = find_option(topic);
|
||||
|
||||
if (cmd) {
|
||||
printf("COMMAND: %s\n", cmd->name);
|
||||
fputs(cmd->help, stdout);
|
||||
if (opt)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (opt) {
|
||||
printf("OPTION: %s (%s)\n", opt->name,
|
||||
type_text(opt->type));
|
||||
fputs(opt->help, stdout);
|
||||
}
|
||||
|
||||
if (!(cmd || opt)) {
|
||||
fprintf(stderr, "help: unknown command: %s\n", topic);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
const struct command *cmd;
|
||||
const struct option *opt;
|
||||
|
||||
name_start();
|
||||
for (cmd = command_list; cmd; cmd = cmd->next)
|
||||
name_push(cmd->name);
|
||||
|
||||
printf("Available commands:\n");
|
||||
name_list_show();
|
||||
printf("\n");
|
||||
|
||||
name_start();
|
||||
for (opt = option_list; opt; opt = opt->next)
|
||||
name_push(opt->name);
|
||||
|
||||
printf("Available options:\n");
|
||||
name_list_show();
|
||||
printf("\n");
|
||||
|
||||
printf("Type \"help <topic>\" for more information.\n");
|
||||
printf("Press Ctrl+D to quit.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct command command_help = {
|
||||
.func = cmd_help,
|
||||
.name = "help",
|
||||
.help =
|
||||
"help [command]\n"
|
||||
" Without arguments, displays a list of commands. With a command\n"
|
||||
" name as an argument, displays help for that command.\n"
|
||||
};
|
||||
|
||||
#ifndef USE_READLINE
|
||||
#define LINE_BUF_SIZE 128
|
||||
|
||||
|
@ -165,177 +306,26 @@ static char *readline(const char *prompt)
|
|||
|
||||
void reader_loop(void)
|
||||
{
|
||||
for (;;) {
|
||||
char *buf = readline("(mspdebug) ");
|
||||
printf("\n");
|
||||
cmd_help(NULL);
|
||||
printf("\n");
|
||||
|
||||
if (!buf)
|
||||
break;
|
||||
do {
|
||||
for (;;) {
|
||||
char *buf = readline("(mspdebug) ");
|
||||
|
||||
add_history(buf);
|
||||
process_command(buf, 1);
|
||||
free(buf);
|
||||
}
|
||||
if (!buf)
|
||||
break;
|
||||
|
||||
add_history(buf);
|
||||
process_command(buf, 1);
|
||||
free(buf);
|
||||
}
|
||||
} while (modify_prompt(MODIFY_ALL));
|
||||
|
||||
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 <command>\" 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);
|
||||
|
@ -379,7 +369,7 @@ static int parse_option(struct option *o, const char *word)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int cmd_opt(char **arg)
|
||||
static int cmd_opt(char **arg)
|
||||
{
|
||||
const char *opt_text = get_arg(arg);
|
||||
struct option *opt = NULL;
|
||||
|
@ -411,6 +401,62 @@ int cmd_opt(char **arg)
|
|||
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"
|
||||
};
|
||||
|
||||
static int cmd_read(char **arg)
|
||||
{
|
||||
char *filename = get_arg(arg);
|
||||
FILE *in;
|
||||
char buf[1024];
|
||||
|
||||
if (!filename) {
|
||||
fprintf(stderr, "read: filename must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
while (*cmd && isspace(*cmd))
|
||||
cmd++;
|
||||
|
||||
if (*cmd == '#')
|
||||
continue;
|
||||
|
||||
if (process_command(cmd, 0) < 0) {
|
||||
fprintf(stderr, "read: error processing %s\n",
|
||||
filename);
|
||||
fclose(in);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct command command_read = {
|
||||
.name = "read",
|
||||
.func = cmd_read,
|
||||
.help =
|
||||
"read <filename>\n"
|
||||
" Read commands from a file and evaluate them.\n"
|
||||
};
|
||||
|
||||
static struct option option_color = {
|
||||
.name = "color",
|
||||
.type = OPTION_BOOLEAN,
|
||||
|
@ -432,7 +478,6 @@ static void sigint_handler(int signum)
|
|||
ctrlc_flag = 1;
|
||||
}
|
||||
|
||||
|
||||
void parse_init(void)
|
||||
{
|
||||
const static struct sigaction siga = {
|
||||
|
@ -441,7 +486,12 @@ void parse_init(void)
|
|||
};
|
||||
|
||||
sigaction(SIGINT, &siga, NULL);
|
||||
|
||||
register_option(&option_color);
|
||||
|
||||
register_command(&command_help);
|
||||
register_command(&command_opt);
|
||||
register_command(&command_read);
|
||||
}
|
||||
|
||||
void hexdump(int addr, const u_int8_t *data, int len)
|
||||
|
@ -605,3 +655,121 @@ int ctrlc_check(void)
|
|||
{
|
||||
return ctrlc_flag;
|
||||
}
|
||||
|
||||
static char token_buf[64];
|
||||
static int token_len;
|
||||
static int token_mult;
|
||||
static int token_sum;
|
||||
|
||||
static token_func_t token_func;
|
||||
|
||||
static int token_add(void)
|
||||
{
|
||||
int i;
|
||||
int 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 (token_func && !token_func(token_buf, &value)) {
|
||||
token_sum += token_mult * value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "unknown token: %s\n", token_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void set_token_func(token_func_t func)
|
||||
{
|
||||
token_func = func;
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
|
34
util.h
34
util.h
|
@ -27,10 +27,12 @@ struct command {
|
|||
const char *name;
|
||||
int (*func)(char **arg);
|
||||
const char *help;
|
||||
|
||||
struct command *next;
|
||||
};
|
||||
|
||||
/* The global command table, defined in main.c */
|
||||
extern const struct command all_commands[];
|
||||
/* Add a command to the global command table */
|
||||
void register_command(struct command *c);
|
||||
|
||||
/* Retrieve the next word from a pointer to the rest of a command
|
||||
* argument buffer. Returns NULL if no more words.
|
||||
|
@ -52,12 +54,9 @@ int process_command(char *arg, int interactive);
|
|||
*/
|
||||
void reader_loop(void);
|
||||
|
||||
/* Help command. Displays a command list with no argument, or help
|
||||
* for a particular command.
|
||||
/* Print an ANSI colour code, if the colour option has been set by
|
||||
* the user.
|
||||
*/
|
||||
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. */
|
||||
|
@ -66,7 +65,22 @@ int is_interactive(void);
|
|||
/* Parse an address expression, storing the result in the integer
|
||||
* pointed to. Returns 0 if parsed successfully, -1 if not.
|
||||
*/
|
||||
typedef int (*token_func_t)(const char *text, int *value);
|
||||
|
||||
int addr_exp(const char *text, int *value);
|
||||
void set_token_func(token_func_t func);
|
||||
|
||||
/* 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.
|
||||
|
@ -99,14 +113,10 @@ struct option {
|
|||
*/
|
||||
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);
|
||||
|
||||
/* Display a canonical hexdump */
|
||||
void hexdump(int addr, const u_int8_t *data, int len);
|
||||
|
||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
|
Loading…
Reference in New Issue