#include <poll.h>
#include <pthread.h>
#include "sim_avr.h"
+#include "sim_core.h" // for SET_SREG_FROM, READ_SREG_INTO
#include "sim_hex.h"
#include "avr_eeprom.h"
#include "sim_gdb.h"
int s; // current gdb connection
avr_gdb_watchpoints_t breakpoints;
+ avr_gdb_watchpoints_t watchpoints;
} avr_gdb_t;
static int gdb_watch_find(const avr_gdb_watchpoints_t * w, uint32_t addr)
{
for (int i = 0; i < w->len; i++) {
- if (w->points[i].addr == addr) {
+ if (w->points[i].addr > addr) {
+ return -1;
+ } else if (w->points[i].addr == addr) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Contrary to gdb_watch_find, this actually checks the address against
+ * a watched memory _range_.
+ */
+static int gdb_watch_find_range(const avr_gdb_watchpoints_t * w, uint32_t addr)
+{
+ 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) {
return i;
}
}
return -1;
}
- w->points[w->len].kind = kind;
- w->points[w->len].addr = addr;
- w->points[w->len].size = size;
+ /* Find the insertion point. */
+ for (i = 0; i < w->len; i++) {
+ if (w->points[i].addr > addr) {
+ break;
+ }
+ }
w->len++;
+ /* Make space for new element. */
+ for (int j = i + 1; j < w->len; j++) {
+ w->points[j] = w->points[j - 1];
+ }
+
+ /* Insert it. */
+ w->points[i].kind = kind;
+ w->points[i].addr = addr;
+ w->points[i].size = size;
+
return 0;
}
return 1;
case 32:
g->avr->data[R_SREG] = *src;
+ SET_SREG_FROM(g->avr, *src);
return 1;
case 33:
g->avr->data[R_SPL] = src[0];
case 0 ... 31:
sprintf(rep, "%02x", g->avr->data[regi]);
break;
- case 32:
- sprintf(rep, "%02x", g->avr->data[R_SREG]);
+ case 32: {
+ uint8_t sreg;
+ READ_SREG_INTO(g->avr, sreg);
+ sprintf(rep, "%02x", sreg);
+ }
break;
case 33:
sprintf(rep, "%02x%02x", g->avr->data[R_SPL], g->avr->data[R_SPH]);
gdb_send_reply(g, "OK");
break;
- // TODO
case 2: // write watchpoint
case 3: // read watchpoint
case 4: // access watchpoint
+ /* Mask out the offset applied to SRAM addresses. */
+ addr &= ~0x800000;
+ if (addr > avr->ramend ||
+ gdb_change_breakpoint(&g->watchpoints, set, 1 << kind, addr, len) == -1) {
+ gdb_send_reply(g, "E01");
+ break;
+ }
+
+ gdb_send_reply(g, "OK");
+ break;
default:
gdb_send_reply(g, "");
- }
+ break;
+ }
} break;
default:
gdb_send_reply(g, "");
printf("%s connection closed\n", __FUNCTION__);
close(g->s);
gdb_watch_clear(&g->breakpoints);
+ gdb_watch_clear(&g->watchpoints);
g->avr->state = cpu_Running; // resume
g->s = -1;
return 1;
return 1;
}
+/**
+ * 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.
+ */
+void avr_gdb_handle_watchpoints(avr_t * avr, uint16_t addr, enum avr_gdb_watch_type type)
+{
+ avr_gdb_t *g = avr->gdb;
+
+ int i = gdb_watch_find_range(&g->watchpoints, addr);
+ if (i == -1) {
+ return;
+ }
+
+ int kind = g->watchpoints.points[i].kind;
+ if (kind & type) {
+ /* Send gdb reply (see GDB user manual appendix E.3). */
+ char cmd[78];
+ sprintf(cmd, "T%02x20:%02x;21:%02x%02x;22:%02x%02x%02x00;%s:%06x;",
+ 5, g->avr->data[R_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);
+
+ avr->state = cpu_Stopped;
+ }
+}
+
int avr_gdb_processor(avr_t * avr, int sleep)
{
if (!avr || !avr->gdb)