Implemented basic gdb support.
Tested only with simulation mode -- real devices need testing.
This commit is contained in:
parent
982ac20147
commit
39a4c5806b
2
Makefile
2
Makefile
|
@ -32,7 +32,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
|
||||
bsl.o sim.o symmap.o gdb.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lusb
|
||||
|
||||
.c.o:
|
||||
|
|
2
bsl.c
2
bsl.c
|
@ -202,7 +202,7 @@ static int bsl_control(device_ctl_t type)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int bsl_wait(void)
|
||||
static int bsl_wait(int blocking)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
2
device.h
2
device.h
|
@ -36,7 +36,7 @@ typedef enum {
|
|||
struct device {
|
||||
void (*close)(void);
|
||||
int (*control)(device_ctl_t action);
|
||||
int (*wait)(void);
|
||||
int (*wait)(int blocking);
|
||||
int (*breakpoint)(u_int16_t addr);
|
||||
int (*getregs)(u_int16_t *regs);
|
||||
int (*setregs)(const u_int16_t *regs);
|
||||
|
|
8
fet.c
8
fet.c
|
@ -602,9 +602,9 @@ static int do_erase(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fet_wait(void)
|
||||
static int fet_wait(int blocking)
|
||||
{
|
||||
for (;;) {
|
||||
do {
|
||||
/* Without this delay, breakpoints can get lost. */
|
||||
if (usleep(500000) < 0)
|
||||
break;
|
||||
|
@ -616,7 +616,7 @@ static int fet_wait(void)
|
|||
|
||||
if (!(fet_reply.argv[0] & FET_POLL_RUNNING))
|
||||
return 0;
|
||||
}
|
||||
} while (blocking);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -639,7 +639,7 @@ static int fet_control(device_ctl_t action)
|
|||
case DEVICE_CTL_STEP:
|
||||
if (do_run(FET_RUN_STEP) < 0)
|
||||
return -1;
|
||||
if (fet_wait() < 0)
|
||||
if (fet_wait(1) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -0,0 +1,575 @@
|
|||
/* MSPDebug - debugging tool for MSP430 MCUs
|
||||
* Copyright (C) 2009, 2010 Daniel Beer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "device.h"
|
||||
#include "gdb.h"
|
||||
|
||||
static const struct device *gdb_device;
|
||||
|
||||
/************************************************************************
|
||||
* GDB IO routines
|
||||
*/
|
||||
|
||||
static int gdb_socket;
|
||||
static int gdb_errno;
|
||||
|
||||
static char gdb_xbuf[1024];
|
||||
static int gdb_head;
|
||||
static int gdb_tail;
|
||||
|
||||
static char gdb_outbuf[1024];
|
||||
static int gdb_outlen;
|
||||
|
||||
static void gdb_printf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(gdb_outbuf + gdb_outlen,
|
||||
sizeof(gdb_outbuf) - gdb_outlen,
|
||||
fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
gdb_outlen += len;
|
||||
}
|
||||
|
||||
static int gdb_flush(void)
|
||||
{
|
||||
if (send(gdb_socket, gdb_outbuf, gdb_outlen, 0) < 0) {
|
||||
gdb_errno = errno;
|
||||
perror("gdb: send");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gdb_outlen = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_read(int blocking)
|
||||
{
|
||||
fd_set r;
|
||||
int len;
|
||||
struct timeval to = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 0
|
||||
};
|
||||
|
||||
FD_ZERO(&r);
|
||||
FD_SET(gdb_socket, &r);
|
||||
|
||||
if (select(gdb_socket + 1, &r, NULL, NULL,
|
||||
blocking ? NULL : &to) < 0) {
|
||||
perror("gdb: select");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!FD_ISSET(gdb_socket, &r))
|
||||
return 0;
|
||||
|
||||
len = recv(gdb_socket, gdb_xbuf, sizeof(gdb_xbuf), 0);
|
||||
|
||||
if (len < 0) {
|
||||
gdb_errno = errno;
|
||||
perror("gdb: recv");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!len) {
|
||||
printf("Connection closed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gdb_head = 0;
|
||||
gdb_tail = len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static int gdb_peek(void)
|
||||
{
|
||||
if (gdb_head == gdb_tail && gdb_read(0) < 0)
|
||||
return -1;
|
||||
|
||||
return gdb_head != gdb_tail;
|
||||
}
|
||||
|
||||
static int gdb_getc(void)
|
||||
{
|
||||
int c;
|
||||
|
||||
/* If the buffer is empty, receive some more data */
|
||||
if (gdb_head == gdb_tail && gdb_read(1) < 0)
|
||||
return -1;
|
||||
|
||||
c = gdb_xbuf[gdb_head];
|
||||
gdb_head++;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static int gdb_flush_ack(void)
|
||||
{
|
||||
int c;
|
||||
|
||||
do {
|
||||
gdb_outbuf[gdb_outlen] = 0;
|
||||
#ifdef DEBUG_GDB
|
||||
printf("-> %s\n", gdb_outbuf);
|
||||
#endif
|
||||
if (gdb_flush() < 0)
|
||||
return -1;
|
||||
|
||||
c = gdb_getc();
|
||||
if (c < 0)
|
||||
return -1;
|
||||
} while (c != '+');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gdb_packet_start(void)
|
||||
{
|
||||
gdb_printf("$");
|
||||
}
|
||||
|
||||
static void gdb_packet_end(void)
|
||||
{
|
||||
int i;
|
||||
int c = 0;
|
||||
|
||||
for (i = 1; i < gdb_outlen; i++)
|
||||
c = (c + gdb_outbuf[i]) & 0xff;
|
||||
gdb_printf("#%02X", c);
|
||||
}
|
||||
|
||||
static void gdb_hexstring(const char *text)
|
||||
{
|
||||
while (*text)
|
||||
gdb_printf("%02X", *(text++));
|
||||
}
|
||||
|
||||
static int hexval(int c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
if (isupper(c))
|
||||
return c - 'A' + 10;
|
||||
if (islower(c))
|
||||
return c - 'a' + 10;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* GDB server
|
||||
*/
|
||||
|
||||
static void read_registers(void)
|
||||
{
|
||||
u_int16_t regs[DEVICE_NUM_REGS];
|
||||
|
||||
printf("Reading registers\n");
|
||||
if (gdb_device->getregs(regs) < 0) {
|
||||
gdb_printf("E00");
|
||||
} else {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DEVICE_NUM_REGS; i++)
|
||||
gdb_printf("%02X%02X", regs[i] & 0xff, regs[i] >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
static void monitor_command(char *buf)
|
||||
{
|
||||
char cmd[128];
|
||||
int len = 0;
|
||||
|
||||
while (len + 1 < sizeof(cmd) && *buf && buf[1]) {
|
||||
cmd[len++] = (hexval(buf[0]) << 4) | hexval(buf[1]);
|
||||
buf += 2;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmd, "reset")) {
|
||||
printf("Resetting device\n");
|
||||
if (gdb_device->control(DEVICE_CTL_RESET) < 0)
|
||||
gdb_hexstring("Reset failed\n");
|
||||
else
|
||||
gdb_printf("OK");
|
||||
} else if (!strcasecmp(cmd, "erase")) {
|
||||
printf("Erasing device\n");
|
||||
if (gdb_device->control(DEVICE_CTL_ERASE) < 0)
|
||||
gdb_hexstring("Erase failed\n");
|
||||
else
|
||||
gdb_printf("OK");
|
||||
}
|
||||
}
|
||||
|
||||
static void write_registers(char *buf)
|
||||
{
|
||||
u_int16_t regs[DEVICE_NUM_REGS];
|
||||
int i;
|
||||
|
||||
printf("Writing registers\n");
|
||||
for (i = 0; i < DEVICE_NUM_REGS; i++) {
|
||||
regs[i] = (hexval(buf[2]) << 12) |
|
||||
(hexval(buf[3]) << 8) |
|
||||
(hexval(buf[0]) << 4) |
|
||||
hexval(buf[1]);
|
||||
buf += 4;
|
||||
}
|
||||
|
||||
if (gdb_device->setregs(regs) < 0)
|
||||
gdb_printf("E00");
|
||||
else
|
||||
gdb_printf("OK");
|
||||
}
|
||||
|
||||
static void read_memory(char *text)
|
||||
{
|
||||
char *length_text = strchr(text, ',');
|
||||
int length, addr;
|
||||
u_int8_t buf[128];
|
||||
|
||||
if (!length_text) {
|
||||
fprintf(stderr, "gdb: malformed memory read request\n");
|
||||
gdb_printf("E00");
|
||||
return;
|
||||
}
|
||||
|
||||
*(length_text++) = 0;
|
||||
|
||||
length = strtoul(length_text, NULL, 16);
|
||||
addr = strtoul(text, NULL, 16);
|
||||
|
||||
if (length > sizeof(buf))
|
||||
length = sizeof(buf);
|
||||
|
||||
printf("Reading %d bytes from 0x%04x\n", length, addr);
|
||||
|
||||
if (gdb_device->readmem(addr, buf, length) < 0) {
|
||||
gdb_printf("E00");
|
||||
} else {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
gdb_printf("%02X", buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_memory(char *text)
|
||||
{
|
||||
char *data_text = strchr(text, ':');
|
||||
char *length_text = strchr(text, ',');
|
||||
int length, addr;
|
||||
u_int8_t buf[128];
|
||||
int buflen = 0;
|
||||
|
||||
if (!(data_text && length_text)) {
|
||||
fprintf(stderr, "gdb: malformed memory write request\n");
|
||||
gdb_printf("E00");
|
||||
return;
|
||||
}
|
||||
|
||||
*(data_text++) = 0;
|
||||
*(length_text++) = 0;
|
||||
|
||||
length = strtoul(length_text, NULL, 16);
|
||||
addr = strtoul(text, NULL, 16);
|
||||
|
||||
while (buflen < sizeof(buf) && *data_text && data_text[1]) {
|
||||
buf[buflen++] = (hexval(data_text[0]) << 4) |
|
||||
hexval(data_text[1]);
|
||||
data_text += 2;
|
||||
}
|
||||
|
||||
if (buflen != length) {
|
||||
fprintf(stderr, "gdb: length mismatch\n");
|
||||
gdb_printf("E00");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Writing %d bytes to 0x%04x\n", buflen, addr);
|
||||
|
||||
if (gdb_device->writemem(addr, buf, buflen) < 0)
|
||||
gdb_printf("E00");
|
||||
else
|
||||
gdb_printf("OK");
|
||||
}
|
||||
|
||||
static void single_step(char *buf)
|
||||
{
|
||||
u_int16_t regs[DEVICE_NUM_REGS];
|
||||
int i;
|
||||
|
||||
printf("Single stepping\n");
|
||||
|
||||
if (*buf) {
|
||||
if (gdb_device->getregs(regs) < 0)
|
||||
goto fail;
|
||||
|
||||
regs[0] = strtoul(buf, NULL, 16);
|
||||
if (gdb_device->setregs(regs) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (gdb_device->control(DEVICE_CTL_STEP) < 0)
|
||||
goto fail;
|
||||
if (gdb_device->getregs(regs) < 0)
|
||||
goto fail;
|
||||
|
||||
gdb_printf("T00");
|
||||
for (i = 0; i < 16; i++)
|
||||
gdb_printf("%02X:%02X%02X;", i, regs[i] & 0xff, regs[i] >> 8);
|
||||
|
||||
return;
|
||||
fail:
|
||||
gdb_printf("E00");
|
||||
}
|
||||
|
||||
static void run(char *buf)
|
||||
{
|
||||
u_int16_t regs[DEVICE_NUM_REGS];
|
||||
int i;
|
||||
|
||||
printf("Running\n");
|
||||
|
||||
if (*buf) {
|
||||
if (gdb_device->getregs(regs) < 0)
|
||||
goto fail;
|
||||
|
||||
regs[0] = strtoul(buf, NULL, 16);
|
||||
if (gdb_device->setregs(regs) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (gdb_device->control(DEVICE_CTL_RUN) < 0)
|
||||
goto fail;
|
||||
|
||||
for (;;) {
|
||||
int status = gdb_device->wait(0);
|
||||
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
if (!status) {
|
||||
printf("Target halted\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (gdb_peek()) {
|
||||
int c = gdb_getc();
|
||||
|
||||
if (c < 0)
|
||||
return;
|
||||
|
||||
if (c == 3) {
|
||||
printf("Interrupted by gdb\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (gdb_device->control(DEVICE_CTL_HALT) < 0)
|
||||
goto fail;
|
||||
if (gdb_device->getregs(regs) < 0)
|
||||
goto fail;
|
||||
|
||||
gdb_printf("T00");
|
||||
for (i = 0; i < 16; i++)
|
||||
gdb_printf("%02X:%02X%02X;", i, regs[i] & 0xff, regs[i] >> 8);
|
||||
|
||||
return;
|
||||
fail:
|
||||
gdb_printf("E00");
|
||||
}
|
||||
|
||||
static int process_command(char *buf, int len)
|
||||
{
|
||||
gdb_packet_start();
|
||||
|
||||
switch (buf[0]) {
|
||||
case '?': /* Return target halt reason */
|
||||
gdb_printf("T00");
|
||||
break;
|
||||
|
||||
case 'g': /* Read registers */
|
||||
read_registers();
|
||||
break;
|
||||
|
||||
case 'G': /* Write registers */
|
||||
if (len >= DEVICE_NUM_REGS * 4)
|
||||
write_registers(buf + 1);
|
||||
else
|
||||
gdb_printf("E00");
|
||||
break;
|
||||
|
||||
case 'q': /* Query */
|
||||
if (!strncmp(buf, "qRcmd,", 6))
|
||||
monitor_command(buf + 6);
|
||||
break;
|
||||
|
||||
case 'm': /* Read memory */
|
||||
read_memory(buf + 1);
|
||||
break;
|
||||
|
||||
case 'M': /* Write memory */
|
||||
write_memory(buf + 1);
|
||||
break;
|
||||
|
||||
case 'c': /* Continue */
|
||||
run(buf + 1);
|
||||
break;
|
||||
|
||||
case 's': /* Single step */
|
||||
single_step(buf + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* For unknown/unsupported packets, return an empty reply */
|
||||
gdb_packet_end();
|
||||
return gdb_flush_ack();
|
||||
}
|
||||
|
||||
static void reader_loop(void)
|
||||
{
|
||||
for (;;) {
|
||||
char buf[1024];
|
||||
int len = 0;
|
||||
int cksum_calc = 0;
|
||||
int cksum_recv = 0;
|
||||
int c;
|
||||
|
||||
/* Wait for packet start */
|
||||
do {
|
||||
c = gdb_getc();
|
||||
if (c < 0)
|
||||
return;
|
||||
} while (c != '$');
|
||||
|
||||
/* Read packet payload */
|
||||
while (len + 1 < sizeof(buf)) {
|
||||
c = gdb_getc();
|
||||
if (c < 0)
|
||||
return;
|
||||
if (c == '#')
|
||||
break;
|
||||
|
||||
buf[len++] = c;
|
||||
cksum_calc = (cksum_calc + c) & 0xff;
|
||||
}
|
||||
buf[len] = 0;
|
||||
|
||||
/* Read packet checksum */
|
||||
c = gdb_getc();
|
||||
if (c < 0)
|
||||
return;
|
||||
cksum_recv = hexval(c);
|
||||
c = gdb_getc();
|
||||
if (c < 0)
|
||||
return;
|
||||
cksum_recv = (cksum_recv << 4) | hexval(c);
|
||||
|
||||
#ifdef DEBUG_GDB
|
||||
printf("<- $%s#%02X\n", buf, cksum_recv);
|
||||
#endif
|
||||
|
||||
if (cksum_recv != cksum_calc) {
|
||||
fprintf(stderr, "gdb: bad checksum (calc = 0x%02x, "
|
||||
"recv = 0x%02x)\n", cksum_calc, cksum_recv);
|
||||
fprintf(stderr, "gdb: packet data was: %s\n", buf);
|
||||
gdb_printf("-");
|
||||
if (gdb_flush() < 0)
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Send acknowledgement */
|
||||
gdb_printf("+");
|
||||
if (gdb_flush() < 0)
|
||||
return;
|
||||
|
||||
if (len && process_command(buf, len) < 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int gdb_server(const struct device *dev, int port)
|
||||
{
|
||||
int sock;
|
||||
int client;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t len;
|
||||
|
||||
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sock < 0) {
|
||||
perror("gdb: can't create socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
fprintf(stderr, "gdb: can't bind to port %d: %s\n",
|
||||
port, strerror(errno));
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(sock, 1) < 0) {
|
||||
perror("gdb: can't listen on socket");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Bound to port %d. Now waiting for connection...\n", port);
|
||||
|
||||
len = sizeof(addr);
|
||||
client = accept(sock, (struct sockaddr *)&addr, &len);
|
||||
if (client < 0) {
|
||||
perror("gdb: failed to accept connection");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
printf("Client connected from %s:%d\n",
|
||||
inet_ntoa(addr.sin_addr), htons(addr.sin_port));
|
||||
|
||||
gdb_socket = client;
|
||||
gdb_errno = 0;
|
||||
gdb_head = 0;
|
||||
gdb_tail = 0;
|
||||
gdb_outlen = 0;
|
||||
gdb_device = dev;
|
||||
|
||||
reader_loop();
|
||||
|
||||
return gdb_errno ? -1 : 0;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/* MSPDebug - debugging tool for MSP430 MCUs
|
||||
* Copyright (C) 2009, 2010 Daniel Beer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef 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);
|
||||
|
||||
#endif
|
29
main.c
29
main.c
|
@ -28,6 +28,7 @@
|
|||
#include "binfile.h"
|
||||
#include "stab.h"
|
||||
#include "util.h"
|
||||
#include "gdb.h"
|
||||
|
||||
static const struct device *msp430_dev;
|
||||
|
||||
|
@ -404,11 +405,9 @@ static int cmd_run(char **arg)
|
|||
printf("Running to 0x%04x.", bp_addr);
|
||||
else
|
||||
printf("Running.");
|
||||
printf(" Press Ctrl+C to interrupt...");
|
||||
fflush(stdout);
|
||||
printf(" Press Ctrl+C to interrupt...\n");
|
||||
|
||||
msp430_dev->wait();
|
||||
printf("\n");
|
||||
msp430_dev->wait(1);
|
||||
|
||||
if (msp430_dev->control(DEVICE_CTL_HALT) < 0)
|
||||
return -1;
|
||||
|
@ -631,6 +630,22 @@ static int cmd_syms(char **arg)
|
|||
return result;
|
||||
}
|
||||
|
||||
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 const struct command all_commands[] = {
|
||||
{"=", cmd_eval,
|
||||
"= <expression>\n"
|
||||
|
@ -638,6 +653,9 @@ static const struct command all_commands[] = {
|
|||
{"dis", cmd_dis,
|
||||
"dis <address> [length]\n"
|
||||
" Disassemble a section of memory.\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 name as\n"
|
||||
|
@ -764,7 +782,6 @@ static void reader_loop(void)
|
|||
{
|
||||
printf("\n");
|
||||
cmd_help(NULL);
|
||||
ctrlc_init();
|
||||
|
||||
for (;;) {
|
||||
char buf[128];
|
||||
|
@ -892,6 +909,8 @@ int main(int argc, char **argv)
|
|||
return -1;
|
||||
}
|
||||
|
||||
ctrlc_init();
|
||||
|
||||
/* Open a device */
|
||||
if (mode == MODE_SIM) {
|
||||
msp430_dev = sim_open();
|
||||
|
|
13
mspdebug.man
13
mspdebug.man
|
@ -22,7 +22,8 @@ registers, set registers, and control the CPU (single step, run and
|
|||
run to breakpoint).
|
||||
|
||||
It supports ELF32, Intel HEX and BSD-style symbol tables (such as the
|
||||
output produced by \fBnm\fR(1)).
|
||||
output produced by \fBnm\fR(1)). It can also be used as a remote stub
|
||||
for \fBgdb\fR(1).
|
||||
.SH OPTIONS
|
||||
Command-line options accepted by MSPDebug are described below. If
|
||||
commands are specified on the end of the command-line, then they are
|
||||
|
@ -93,6 +94,13 @@ length (64 bytes) is disassembled and shown.
|
|||
|
||||
If symbols are available, then all addresses used as operands are
|
||||
translated into \fIsymbol\fR+\fIoffset\fR form.
|
||||
.IP "gdb [\fIport\fR]"
|
||||
Start a GDB remote stub, optionally specifying a TCP port to listen on.
|
||||
If no port is given, the default port is 2000.
|
||||
|
||||
MSPDebug will wait for a connection on this port, and then act as a
|
||||
GDB remote stub until GDB disconnects. The basic GDB protocol is
|
||||
supported, plus the monitor commands "erase" and "reset".
|
||||
.IP "help [\fIcommand\fR]"
|
||||
Show a brief listing of available commands. If an argument is
|
||||
specified, show the syntax for the given command. The help text shown
|
||||
|
@ -183,8 +191,7 @@ The following are all valid examples of address expressions:
|
|||
.br
|
||||
.B __bss_end-__bss_start
|
||||
.SH SEE ALSO
|
||||
.BR nm (1),
|
||||
.BR objcopy (1)
|
||||
\fBnm\fR(1), \fBgdb\fR(1), \fBobjcopy\fR(1)
|
||||
.SH BUGS
|
||||
If you find any bugs, you should report them to the author at
|
||||
daniel@tortek.co.nz. It would help if you could include a transcript
|
||||
|
|
36
sim.c
36
sim.c
|
@ -78,8 +78,10 @@ static u_int16_t fetch_io(u_int16_t addr, int is_byte)
|
|||
|
||||
printf("? ");
|
||||
fflush(stdout);
|
||||
if (!fgets(text, sizeof(text), stdin))
|
||||
if (!fgets(text, sizeof(text), stdin)) {
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = strlen(text);
|
||||
while (len && isspace(text[len - 1]))
|
||||
|
@ -479,13 +481,14 @@ static int sim_control(device_ctl_t action)
|
|||
case DEVICE_CTL_RESET:
|
||||
memset(sim_regs, 0, sizeof(sim_regs));
|
||||
sim_regs[MSP430_REG_PC] = MEM_GETW(0xfffe);
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case DEVICE_CTL_ERASE:
|
||||
memset(memory, 0xff, MEM_SIZE);
|
||||
return 0;
|
||||
|
||||
case DEVICE_CTL_HALT:
|
||||
run_mode = RUN_HALTED;
|
||||
return 0;
|
||||
|
||||
case DEVICE_CTL_STEP:
|
||||
|
@ -503,34 +506,39 @@ static int sim_control(device_ctl_t action)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int sim_wait(void)
|
||||
static int sim_wait(int blocking)
|
||||
{
|
||||
int i = 100000;
|
||||
|
||||
if (run_mode != RUN_HALTED) {
|
||||
printf("\n");
|
||||
ctrlc_reset();
|
||||
|
||||
for (;;) {
|
||||
while (i) {
|
||||
if (run_mode == RUN_TO_BREAKPOINT &&
|
||||
sim_regs[MSP430_REG_PC] == run_breakpoint)
|
||||
break;
|
||||
sim_regs[MSP430_REG_PC] == run_breakpoint) {
|
||||
run_mode = RUN_HALTED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sim_regs[MSP430_REG_SR] & MSP430_SR_CPUOFF) {
|
||||
run_mode = RUN_HALTED;
|
||||
printf("CPU disabled\n");
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctrlc_check()) {
|
||||
run_mode = RUN_HALTED;
|
||||
return 1;
|
||||
}
|
||||
if (ctrlc_check())
|
||||
break;
|
||||
|
||||
if (step_cpu() < 0) {
|
||||
run_mode = RUN_HALTED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!blocking)
|
||||
i--;
|
||||
}
|
||||
|
||||
run_mode = RUN_HALTED;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -551,7 +559,7 @@ static int sim_getregs(u_int16_t *regs)
|
|||
static int sim_setregs(const u_int16_t *regs)
|
||||
{
|
||||
memcpy(sim_regs, regs, sizeof(sim_regs));
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sim_readmem(u_int16_t addr, u_int8_t *mem, int len)
|
||||
|
|
Loading…
Reference in New Issue