diff --git a/simavr/sim/sim_avr.c b/simavr/sim/sim_avr.c
index 6e43bf91f..4c2602ca9 100644
--- a/simavr/sim/sim_avr.c
+++ b/simavr/sim/sim_avr.c
@@ -179,6 +179,7 @@ avr_reset(
port->reset(port);
port = port->next;
}
+ avr->cycle = 0; // Prevent crash
}
void
@@ -286,7 +287,7 @@ void
avr_callback_run_gdb(
avr_t * avr)
{
- avr_gdb_processor(avr, avr->state == cpu_Stopped);
+ avr_gdb_processor(avr, avr->state == cpu_Stopped ? 50000 : 0);
if (avr->state == cpu_Stopped)
return ;
@@ -331,7 +332,6 @@ avr_callback_run_gdb(
// if we were stepping, use this state to inform remote gdb
if (step)
avr->state = cpu_StepDone;
-
}
/*
diff --git a/simavr/sim/sim_avr.h b/simavr/sim/sim_avr.h
index 710e12b1b..e9a97cde2 100644
--- a/simavr/sim/sim_avr.h
+++ b/simavr/sim/sim_avr.h
@@ -152,6 +152,8 @@ typedef void (*avr_run_t)(
#define AVR_FUSE_HIGH 1
#define AVR_FUSE_EXT 2
+#define REG_NAME_COUNT (256 + 32) // Size of reg_names table.
+
/*
* Main AVR instance. Some of these fields are set by the AVR "Core" definition files
* the rest is runtime data (as little as possible)
diff --git a/simavr/sim/sim_core.c b/simavr/sim/sim_core.c
index 4bb254334..a1a9ac926 100644
--- a/simavr/sim/sim_core.c
+++ b/simavr/sim/sim_core.c
@@ -164,7 +164,8 @@ uint8_t avr_core_watch_read(avr_t *avr, uint16_t addr)
"CORE: *** Wrapping read address "
"PC=%04x SP=%04x O=%04x Address %04x %% %04x --> %04x\n"
FONT_DEFAULT,
- avr->pc, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc), addr, (avr->ramend + 1), addr % (avr->ramend + 1));
+ avr->pc, _avr_sp_get(avr), _avr_flash_read16le(avr, avr->pc),
+ addr, (avr->ramend + 1), addr % (avr->ramend + 1));
addr = addr % (avr->ramend + 1);
}
@@ -321,7 +322,7 @@ avr_flashaddr_t _avr_pop_addr(avr_t * avr)
/*
* "Pretty" register names
*/
-const char * reg_names[255] = {
+const char * reg_names[REG_NAME_COUNT] = {
[R_XH] = "XH", [R_XL] = "XL",
[R_YH] = "YH", [R_YL] = "YL",
[R_ZH] = "ZH", [R_ZL] = "ZL",
@@ -330,7 +331,7 @@ const char * reg_names[255] = {
};
-const char * avr_regname(uint8_t reg)
+const char * avr_regname(unsigned int reg)
{
if (!reg_names[reg]) {
char tt[16];
@@ -938,12 +939,9 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
case 0x9598: { // BREAK -- 1001 0101 1001 1000
STATE("break\n");
if (avr->gdb) {
- // if gdb is on, we break here as in here
- // and we do so until gdb restores the instruction
- // that was here before
- avr->state = cpu_StepDone;
- new_pc = avr->pc;
- cycle = 0;
+ // if gdb is on, break here.
+ avr->state = cpu_Stopped;
+ avr_gdb_handle_break(avr);
}
} break;
case 0x95a8: { // WDR -- Watchdog Reset -- 1001 0101 1010 1000
diff --git a/simavr/sim/sim_gdb.c b/simavr/sim/sim_gdb.c
index 5ea74a126..ec950e698 100644
--- a/simavr/sim/sim_gdb.c
+++ b/simavr/sim/sim_gdb.c
@@ -33,6 +33,7 @@
#include "avr_eeprom.h"
#include "sim_gdb.h"
+// For debug printfs: "#define DBG(w) w"
#define DBG(w)
#define WATCH_LIMIT (32)
@@ -48,11 +49,16 @@ typedef struct {
typedef struct avr_gdb_t {
avr_t * avr;
- int listen; // listen socket
- int s; // current gdb connection
+ int listen; // listen socket
+ int s; // current gdb connection
avr_gdb_watchpoints_t breakpoints;
avr_gdb_watchpoints_t watchpoints;
+
+ // These are used by gdb's "info io_registers" command.
+
+ uint16_t ior_base;
+ uint8_t ior_count, mad;
} avr_gdb_t;
@@ -71,7 +77,6 @@ gdb_watch_find(
return i;
}
}
-
return -1;
}
@@ -87,11 +92,11 @@ gdb_watch_find_range(
for (int i = 0; i < w->len; i++) {
if (w->points[i].addr > addr) {
return -1;
- } else if (w->points[i].addr <= addr && addr < w->points[i].addr + w->points[i].size) {
+ } else if (w->points[i].addr <= addr &&
+ addr < w->points[i].addr + w->points[i].size) {
return i;
}
}
-
return -1;
}
@@ -105,6 +110,9 @@ gdb_watch_add_or_update(
uint32_t addr,
uint32_t size )
{
+ if (kind == AVR_GDB_WATCH_ACCESS)
+ kind |= AVR_GDB_WATCH_WRITE | AVR_GDB_WATCH_READ;
+
/* If the watchpoint exists, update it. */
int i = gdb_watch_find(w, addr);
if (i != -1) {
@@ -193,21 +201,41 @@ gdb_send_reply(
send(g->s, reply, dst - reply + 3, 0);
}
+static void
+gdb_send_stop_status(
+ avr_gdb_t * g,
+ uint8_t signal,
+ const char * reason,
+ uint32_t * pp )
+{
+ avr_t * avr;
+ uint8_t sreg;
+ int n;
+ char cmd[64];
+
+ avr = g->avr;
+ READ_SREG_INTO(avr, sreg);
+
+ n = sprintf(cmd, "T%02x20:%02x;21:%02x%02x;22:%02x%02x%02x00;",
+ signal, sreg,
+ avr->data[R_SPL], avr->data[R_SPH],
+ avr->pc & 0xff, (avr->pc >> 8) & 0xff,
+ (avr->pc >> 16) & 0xff);
+ if (reason) {
+ if (pp)
+ sprintf(cmd + n, "%s:%x;", reason, *pp);
+ else
+ sprintf(cmd + n, "%s:;", reason);
+ }
+ gdb_send_reply(g, cmd);
+}
+
static void
gdb_send_quick_status(
avr_gdb_t * g,
uint8_t signal )
{
- char cmd[64];
- uint8_t sreg;
-
- READ_SREG_INTO(g->avr, sreg);
-
- sprintf(cmd, "T%02x20:%02x;21:%02x%02x;22:%02x%02x%02x00;",
- signal ? signal : 5, sreg,
- g->avr->data[R_SPL], g->avr->data[R_SPH],
- g->avr->pc & 0xff, (g->avr->pc>>8)&0xff, (g->avr->pc>>16)&0xff);
- gdb_send_reply(g, cmd);
+ gdb_send_stop_status(g, signal, NULL, NULL);
}
static int
@@ -218,14 +246,14 @@ gdb_change_breakpoint(
uint32_t addr,
uint32_t size )
{
- DBG(printf("set %d kind %d addr %08x len %d\n", set, kind, addr, len);)
+ DBG(printf("%s kind %d addr %08x len %d\n", set ? "Set" : "Clear",
+ kind, addr, size);)
if (set) {
return gdb_watch_add_or_update(w, kind, addr, size);
} else {
return gdb_watch_rm(w, kind, addr);
}
-
return -1;
}
@@ -281,10 +309,231 @@ gdb_read_register(
return strlen(rep);
}
+static int tohex(const char *in, char *out, unsigned int len)
+{
+ int n = 0;
+
+ while (*in && n + 2 < len)
+ n += sprintf(out + n, "%02x", (uint8_t)*in++);
+ return n;
+}
+
+/* Send a message to the user. Gdb must be expecting a reply, otherwise this
+ * is ignored.
+ */
+
+static void message(avr_gdb_t * g, const char *m)
+{
+ char buff[256];
+
+ buff[0] = 'O';
+ tohex(m, buff + 1, sizeof buff - 1);
+ gdb_send_reply(g, buff);
+}
+
+static int
+handle_monitor(avr_t * avr, avr_gdb_t * g, char * cmd)
+{
+ char *ip, *op;
+ unsigned int c1, c2;
+ char dehex[128];
+
+ if (*cmd++ != ',')
+ return 1; // Bad format
+ for (op = dehex; op < dehex + (sizeof dehex - 1); ++op) {
+ if (!*cmd)
+ break;
+ if (sscanf(cmd, "%1x%1x", &c1, &c2) != 2)
+ return 2; // Bad format
+ *op = (c1 << 4) + c2;
+ cmd += 2;
+ }
+ *op = '\0';
+ if (*cmd)
+ return 3; // Too long
+ ip = dehex;
+ while (*ip) {
+ while (*ip == ' ' || *ip == '\t')
+ ++ip;
+
+ if (strncmp(ip, "reset", 5) == 0) {
+ avr_reset(avr);
+ avr->state = cpu_Stopped;
+ ip += 5;
+ } else if (strncmp(ip, "halt", 4) == 0) {
+ avr->state = cpu_Stopped;
+ ip += 4;
+ } else if (strncmp(ip, "ior", 3) == 0) {
+ unsigned int base;
+ int n, m, count;
+
+ // Format is "ior
+ // or just "ior" to reset.
+
+ ip += 3;
+ m = sscanf(ip, "%x %i%n", &base, &count, &n);
+ if (m <= 0) {
+ // Reset values.
+
+ g->ior_base = g->ior_count = 0;
+ n = 0;
+ } else if (m != 2) {
+ return 1;
+ } else {
+ if (count <= 0 || base + count + 32 > REG_NAME_COUNT ||
+ base + count + 32 > avr->ioend) {
+ return 4; // bad value
+ }
+ g->ior_base = base;
+ g->ior_count = count;
+ }
+ ip += n;
+ DBG(
+ } else if (strncmp(ip, "say ", 4) == 0) {
+ // Put a message in the debug output.
+ printf("Say: %s\n", ip + 4);
+ ip += strlen(ip);
+ )
+ } else {
+ tohex("Monitor subcommands are: ior halt reset" DBG(" say") "\n",
+ dehex, sizeof dehex);
+ gdb_send_reply(g, dehex);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void
+handle_io_registers(avr_t * avr, avr_gdb_t * g, char * cmd)
+{
+ extern const char *avr_regname(unsigned int); // sim_core.c
+ char * params;
+ char * reply;
+ unsigned int addr, count;
+ char buff[1024];
+
+ if (g->mad) {
+ /* For this command, gdb employs a streaming protocol,
+ * with the command being repeated until the stub sends
+ * an empy packet as terminator. That makes no sense,
+ * as the requests are sized to ensure the reply will
+ * fit in a single packet.
+ */
+
+ reply = "";
+ g->mad = 0;
+ } else {
+ params = cmd + 11;
+ if (sscanf(params, ":%x,%x", &addr, &count) == 2) {
+ int i;
+
+ // Send names and values.
+ addr += 32;
+ if (addr + count > avr->ioend)
+ count = avr->ioend + 1 - addr;
+ reply = buff;
+ for (i = 0; i < count; ++i) {
+ const char *name;
+
+ name = avr_regname(addr + i);
+ reply += sprintf(reply, "%s,%x;",
+ name, avr->data[addr + i]);
+ if (reply > buff + sizeof buff - 20)
+ break;
+ }
+ } else {
+ // Send register count.
+
+ count = g->ior_count ? g->ior_count :
+ avr->ioend > REG_NAME_COUNT ?
+ REG_NAME_COUNT - 32 : avr->ioend - 32;
+ sprintf(buff, "%x", count);
+ }
+ reply = buff;
+ g->mad = 1;
+ }
+ gdb_send_reply(g, reply);
+}
+
+static void
+handle_v(avr_t * avr, avr_gdb_t * g, char * cmd, int length)
+{
+ uint32_t addr;
+ uint8_t *src = NULL;
+ int len, err = -1;
+
+ if (strncmp(cmd, "FlashErase", 10) == 0) {
+
+ sscanf(cmd, "%*[^:]:%x,%x", &addr, &len);
+ if (addr < avr->flashend) {
+ src = avr->flash + addr;
+ if (addr + len > avr->flashend)
+ len = avr->flashend - addr;
+ memset(src, 0xff, len);
+ DBG(printf("FlashErase: %x,%x\n", addr, len);) //Remove
+ } else {
+ err = 1;
+ }
+ } else if (strncmp(cmd, "FlashWrite", 10) == 0) {
+ if (sscanf(cmd, "%*[^:]:%x:%n", &addr, &len) != 1) {
+ err = 2;
+ } else {
+ if (len >= length) {
+ err = 99;
+ } else if (addr < avr->flashend) {
+ int escaped;
+ char *end;
+ uint8_t *limit;
+
+ end = cmd + length - 1; // Ignore final '#'.
+ cmd += len;
+ src = avr->flash + addr;
+ limit = avr->flash + avr->flashend;
+ for (escaped = 0; cmd < end && src < limit; ++cmd) {
+ if (escaped) {
+ *src++ = *cmd ^ 0x20;
+ escaped = 0;
+ } else if (*cmd == '}') {
+ escaped = 1;
+ } else {
+ *src++ = *cmd;
+ }
+ }
+ DBG(printf("FlashWrite %x, %ld bytes\n", addr,
+ (src - avr->flash) - addr);)
+ addr = src - avr->flash; // Address of end.
+ if (addr > avr->codeend) // Checked by sim_core.c
+ avr->codeend = addr;
+ if (cmd != end) {
+ DBG(printf("FlashWrite %ld bytes left!\n", end - cmd));
+ }
+ } else {
+ err = 1;
+ }
+ }
+ } else if (strncmp(cmd, "FlashDone", 9) == 0) {
+ DBG(printf("FlashDone\n");) //Remove
+ } else {
+ gdb_send_reply(g, "");
+ return;
+ }
+
+ if (err < 0) {
+ gdb_send_reply(g, "OK");
+ } else {
+ char b[32];
+
+ sprintf(b, "E %.2d", err);
+ gdb_send_reply(g, b);
+ }
+}
+
static void
gdb_handle_command(
avr_gdb_t * g,
- char * cmd )
+ char * cmd,
+ int length)
{
avr_t * avr = g->avr;
char rep[1024];
@@ -294,9 +543,9 @@ gdb_handle_command(
if (strncmp(cmd, "Supported", 9) == 0) {
/* If GDB asked what features we support, report back
* the features we support, which is just memory layout
- * information for now.
+ * information and stop reasons for now.
*/
- gdb_send_reply(g, "qXfer:memory-map:read+");
+ gdb_send_reply(g, "qXfer:memory-map:read+;swbreak+;hwbreak+");
break;
} else if (strncmp(cmd, "Attached", 8) == 0) {
/* Respond that we are attached to an existing process..
@@ -349,25 +598,18 @@ gdb_handle_command(
// all available registers.
}
} else if (strncmp(cmd, "Rcmd", 4) == 0) { // monitor command
- char * args = strchr(cmd, ',');
- if (args != NULL) {
- args++;
- while (args != 0x00) {
- printf("%s",args);
- if (strncmp(args, "7265736574", 10) == 0) { // reset matched
- avr->state = cpu_StepDone;
- avr_reset(avr);
- args += 10;
- } else if (strncmp(args, "68616c74", 8) == 0) { // halt matched
- avr->state = cpu_Stopped;
- args += 8;
- } else if (strncmp(args, "20", 2) == 0) { // space matched
- args += 2;
- } else // no match - end
- break;
- }
+ int err = handle_monitor(avr, g, cmd + 4);
+ if (err > 0) {
+ snprintf(rep, sizeof rep,
+ "E%02x", err);
+ gdb_send_reply(g, rep);
+ } else if (err == 0) {
+ gdb_send_reply(g, "OK");
}
- gdb_send_reply(g, "OK");
+ break;
+ } else if (strncmp(cmd, "Ravr.io_reg", 11) == 0) {
+ handle_io_registers(avr, g, cmd);
+ break;
}
gdb_send_reply(g, "");
break;
@@ -478,8 +720,8 @@ gdb_handle_command(
avr->state = cpu_Step;
} break;
case 'r': { // deprecated, suggested for AVRStudio compatibility
- avr->state = cpu_StepDone;
avr_reset(avr);
+ avr->state = cpu_Stopped;
} break;
case 'Z': // set clear break/watchpoint
case 'z': {
@@ -516,11 +758,22 @@ gdb_handle_command(
break;
}
} break;
- case 'K': // kill
- case 'D': { // detach
+ case 'D': // detach
+#ifdef DETACHABLE
+ if (avr->state = cpu_Stopped)
+ avr->state = cpu_Running;
+ gdb_send_reply(g, "OK");
+ close(g->s);
+ g->s = -1;
+ break;
+#endif
+ case 'k': // kill
avr->state = cpu_Done;
gdb_send_reply(g, "OK");
- } break;
+ break;
+ case 'v':
+ handle_v(avr, g, cmd, length);
+ break;
default:
gdb_send_reply(g, "");
break;
@@ -560,7 +813,7 @@ gdb_network_handler(
int i = 1;
setsockopt (g->s, IPPROTO_TCP, TCP_NODELAY, &i, sizeof (i));
g->avr->state = cpu_Stopped;
- printf("%s connection opened\n", __FUNCTION__);
+ DBG(printf("%s connection opened\n", __FUNCTION__);)
}
if (g->s != -1 && FD_ISSET(g->s, &read_set)) {
@@ -569,7 +822,7 @@ gdb_network_handler(
ssize_t r = recv(g->s, buffer, sizeof(buffer)-1, 0);
if (r == 0) {
- printf("%s connection closed\n", __FUNCTION__);
+ DBG(printf("%s connection closed\n", __FUNCTION__);)
close(g->s);
gdb_watch_clear(&g->breakpoints);
gdb_watch_clear(&g->watchpoints);
@@ -583,35 +836,53 @@ gdb_network_handler(
return 1;
}
buffer[r] = 0;
- // printf("%s: received %d bytes\n'%s'\n", __FUNCTION__, r, buffer);
- // hdump("gdb", buffer, r);
uint8_t * src = buffer;
while (*src == '+' || *src == '-')
src++;
+ DBG(
+ if (!strncmp("$vFlashWrite", (char *)src, 12)) {
+ printf("%s: received Flashwrite command %ld bytes\n",
+ __FUNCTION__, r);
+ } else {
+ printf("%s: received command %ld bytes\n'%s'\n",
+ __FUNCTION__, r, buffer);
+ })
+ // hdump("gdb", buffer, r);
// control C -- lets send the guy a nice status packet
if (*src == 3) {
src++;
- g->avr->state = cpu_StepDone;
+ gdb_send_quick_status(g, 2); // SIGINT
+ g->avr->state = cpu_Stopped;
printf("GDB hit control-c\n");
- }
- if (*src == '$') {
+ } else if (*src == '$') {
// strip checksum
uint8_t * end = buffer + r - 1;
while (end > src && *end != '#')
*end-- = 0;
*end = 0;
src++;
- DBG(printf("GDB command = '%s'\n", src);)
-
+ DBG(
+ if (strncmp("vFlashWrite", (char *)src, 11))
+ printf("GDB command = '%s'\n", src);)
send(g->s, "+", 1, 0);
-
- gdb_handle_command(g, (char*)src);
+ if (end > src)
+ gdb_handle_command(g, (char*)src, end - src);
}
}
return 1;
}
+/* Called on a hardware break instruction. */
+void avr_gdb_handle_break(avr_t *avr)
+{
+ avr_gdb_t *g = avr->gdb;
+
+ message(g, "Simavr executed 'break' instruction.\n");
+ //gdb_send_stop_status(g, 5, "swbreak", NULL); Correct but ignored!
+ gdb_send_quick_status(g, 5);
+}
+
/**
* If an applicable watchpoint exists for addr, stop the cpu and send a status report.
* type is one of AVR_GDB_WATCH_READ, AVR_GDB_WATCH_WRITE depending on the type of access.
@@ -623,6 +894,7 @@ avr_gdb_handle_watchpoints(
enum avr_gdb_watch_type type )
{
avr_gdb_t *g = avr->gdb;
+ uint32_t false_addr;
int i = gdb_watch_find_range(&g->watchpoints, addr);
if (i == -1) {
@@ -630,21 +902,17 @@ avr_gdb_handle_watchpoints(
}
int kind = g->watchpoints.points[i].kind;
+ DBG(printf("Addr %04x found watchpoint %d size %d type %x wanted %x\n",
+ addr, i, g->watchpoints.points[i].size, kind, type);)
if (kind & type) {
/* Send gdb reply (see GDB user manual appendix E.3). */
- char cmd[78];
- uint8_t sreg;
-
- READ_SREG_INTO(g->avr, sreg);
- sprintf(cmd, "T%02x20:%02x;21:%02x%02x;22:%02x%02x%02x00;%s:%06x;",
- 5, sreg,
- g->avr->data[R_SPL], g->avr->data[R_SPH],
- g->avr->pc & 0xff, (g->avr->pc>>8)&0xff, (g->avr->pc>>16)&0xff,
- kind & AVR_GDB_WATCH_ACCESS ? "awatch" :
- kind & AVR_GDB_WATCH_WRITE ? "watch" : "rwatch",
- addr | 0x800000);
- gdb_send_reply(g, cmd);
+ const char * what;
+
+ what = (kind & AVR_GDB_WATCH_ACCESS) ? "awatch" :
+ (kind & AVR_GDB_WATCH_WRITE) ? "watch" : "rwatch";
+ false_addr = addr + 0x800000;
+ gdb_send_stop_status(g, 5, what, &false_addr);
avr->state = cpu_Stopped;
}
}
@@ -661,7 +929,7 @@ avr_gdb_processor(
if (avr->state == cpu_Running &&
gdb_watch_find(&g->breakpoints, avr->pc) != -1) {
DBG(printf("avr_gdb_processor hit breakpoint at %08x\n", avr->pc);)
- gdb_send_quick_status(g, 0);
+ gdb_send_stop_status(g, 5, "hwbreak", NULL);
avr->state = cpu_Stopped;
} else if (avr->state == cpu_StepDone) {
gdb_send_quick_status(g, 0);
diff --git a/simavr/sim/sim_gdb.h b/simavr/sim/sim_gdb.h
index 225291bb0..2936df79e 100644
--- a/simavr/sim/sim_gdb.h
+++ b/simavr/sim/sim_gdb.h
@@ -34,7 +34,7 @@ enum avr_gdb_watch_type {
AVR_GDB_WATCH_WRITE = 1 << 2,
AVR_GDB_WATCH_READ = 1 << 3,
- AVR_GDB_WATCH_ACCESS = AVR_GDB_WATCH_WRITE | AVR_GDB_WATCH_READ,
+ AVR_GDB_WATCH_ACCESS = 1 << 4
};
int avr_gdb_init(avr_t * avr);
@@ -46,6 +46,7 @@ int avr_gdb_processor(avr_t * avr, int sleep);
// Called from sim_core.c
void avr_gdb_handle_watchpoints(avr_t * g, uint16_t addr, enum avr_gdb_watch_type type);
+void avr_gdb_handle_break(avr_t *);
#ifdef __cplusplus
};