summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pavone <pavone@retrodev.com>2014-02-08 23:37:09 -0800
committerMike Pavone <pavone@retrodev.com>2014-02-08 23:37:09 -0800
commit4afb917f03758c286ca85ad9a45be13e852b6a84 (patch)
tree3dd074e2a98cac5921b2cf5aec06db470f6214de
parent9fb4d34b6a61b35380b2840a1e5ed94199dbbada (diff)
Initial GDB remote debugging support. Lacks some features, but breakpoints and basic inspection of registers and memory work.
-rw-r--r--Makefile4
-rw-r--r--blastem.c20
-rw-r--r--gdb_remote.c381
-rw-r--r--vdp.c2
4 files changed, 287 insertions, 120 deletions
diff --git a/Makefile b/Makefile
index 6e3b652..53d1e48 100644
--- a/Makefile
+++ b/Makefile
@@ -26,8 +26,8 @@ CONFIGOBJS=config.o tern.o util.o
all : dis zdis stateview vgmplay blastem
-blastem : blastem.o vdp.o render_sdl.o io.o $(CONFIGOBJS) gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS)
- $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o $(CONFIGOBJS) gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS)
+blastem : blastem.o gdb_remote.o vdp.o render_sdl.o io.o $(CONFIGOBJS) gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS)
+ $(CC) -ggdb -o blastem blastem.o gdb_remote.o vdp.o render_sdl.o io.o $(CONFIGOBJS) gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS)
dis : dis.o 68kinst.o
$(CC) -o dis dis.o 68kinst.o
diff --git a/blastem.c b/blastem.c
index e50c79b..8e8f785 100644
--- a/blastem.c
+++ b/blastem.c
@@ -10,6 +10,7 @@
#include "vdp.h"
#include "render.h"
#include "blastem.h"
+#include "gdb_remote.h"
#include "gst.h"
#include "util.h"
#include <stdio.h>
@@ -1585,7 +1586,7 @@ void save_sram()
printf("Saved SRAM to %s\n", sram_filename);
}
-void init_run_cpu(genesis_context * gen, int debug, FILE * address_log, char * statefile)
+void init_run_cpu(genesis_context * gen, FILE * address_log, char * statefile, uint8_t * debugger)
{
m68k_context context;
x86_68k_options opts;
@@ -1706,15 +1707,15 @@ void init_run_cpu(genesis_context * gen, int debug, FILE * address_log, char * s
exit(1);
}
printf("Loaded %s\n", statefile);
- if (debug) {
- insert_breakpoint(&context, pc, (uint8_t *)debugger);
+ if (debugger) {
+ insert_breakpoint(&context, pc, debugger);
}
adjust_int_cycle(gen->m68k, gen->vdp);
gen->z80->native_pc = z80_get_native_address_trans(gen->z80, gen->z80->pc);
start_68k_context(&context, pc);
} else {
- if (debug) {
- insert_breakpoint(&context, address, (uint8_t *)debugger);
+ if (debugger) {
+ insert_breakpoint(&context, address, debugger);
}
m68k_reset(&context);
}
@@ -1808,6 +1809,7 @@ int main(int argc, char ** argv)
char * romfname = NULL;
FILE *address_log = NULL;
char * statefile = NULL;
+ uint8_t * debuggerfun = NULL;
uint8_t fullscreen = 0, use_gl = 1;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
@@ -1822,7 +1824,11 @@ int main(int argc, char ** argv)
exit_after = atoi(argv[i]);
break;
case 'd':
- debug = 1;
+ debuggerfun = (uint8_t *)debugger;
+ break;
+ case 'D':
+ gdb_remote_init();
+ debuggerfun = (uint8_t *)gdb_debug_enter;
break;
case 'f':
fullscreen = 1;
@@ -1980,6 +1986,6 @@ int main(int argc, char ** argv)
}
set_keybindings();
- init_run_cpu(&gen, debug, address_log, statefile);
+ init_run_cpu(&gen, address_log, statefile, debuggerfun);
return 0;
}
diff --git a/gdb_remote.c b/gdb_remote.c
index 772a44a..31de3e2 100644
--- a/gdb_remote.c
+++ b/gdb_remote.c
@@ -3,14 +3,15 @@
This file is part of BlastEm.
BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
*/
-#include "blastem.h"
+#include "gdb_remote.h"
#include <unistd.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
-#define INITIAL_BUFFER_SIZE 4096
+#define INITIAL_BUFFER_SIZE (16*1024)
char * buf = NULL;
char * curbuf = NULL;
@@ -20,9 +21,261 @@ int cont = 0;
int expect_break_response=0;
uint32_t resume_pc;
-void gdb_debug_enter(genesis_context * gen, uint32_t pc)
+
+void hex_32(uint32_t num, char * out)
+{
+ for (int32_t shift = 28; shift >= 0; shift -= 4)
+ {
+ uint8_t nibble = num >> shift & 0xF;
+ *(out++) = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
+ }
+}
+
+void hex_16(uint16_t num, char * out)
+{
+ for (int16_t shift = 14; shift >= 0; shift -= 4)
+ {
+ uint8_t nibble = num >> shift & 0xF;
+ *(out++) = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
+ }
+}
+
+void hex_8(uint8_t num, char * out)
+{
+ uint8_t nibble = num >> 4;
+ *(out++) = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
+ nibble = num & 0xF;
+ *out = nibble > 9 ? nibble - 0xA + 'A' : nibble + '0';
+}
+
+void gdb_calc_checksum(char * command, char *out)
+{
+ uint8_t checksum = 0;
+ while (*command)
+ {
+ checksum += *(command++);
+ }
+ hex_8(checksum, out);
+}
+
+void write_or_die(int fd, const void *buf, size_t count)
+{
+ if (write(fd, buf, count) < count) {
+ fputs("Error writing to stdout\n", stderr);
+ exit(1);
+ }
+}
+
+void gdb_send_command(char * command)
+{
+ char end[3];
+ write_or_die(STDOUT_FILENO, "$", 1);
+ write_or_die(STDOUT_FILENO, command, strlen(command));
+ end[0] = '#';
+ gdb_calc_checksum(command, end+1);
+ write_or_die(STDOUT_FILENO, end, 3);
+ fprintf(stderr, "Sent $%s#%c%c\n", command, end[1], end[2]);
+}
+
+uint32_t calc_status(m68k_context * context)
+{
+ uint32_t status = context->status << 3;
+ for (int i = 0; i < 5; i++)
+ {
+ status <<= 1;
+ status |= context->flags[i];
+ }
+ return status;
+}
+
+uint8_t read_byte(m68k_context * context, uint32_t address)
+{
+ uint16_t * word;
+ //TODO: Use generated read/write functions so that memory map is properly respected
+ if (address < 0x400000) {
+ word = context->mem_pointers[0] + address/2;
+ } else if (address >= 0xE00000) {
+ word = context->mem_pointers[1] + (address & 0xFFFF)/2;
+ } else {
+ return 0;
+ }
+ if (address & 1) {
+ return *word;
+ }
+ return *word >> 8;
+}
+
+void gdb_run_command(m68k_context * context, uint32_t pc, char * command)
{
- fcntl(STDIN_FILENO, FD_SETFL, 0);
+ char send_buf[512];
+ fprintf(stderr, "Received command %s\n", command);
+ switch(*command)
+ {
+
+ case 'c':
+ if (*(command+1) != 0) {
+ //TODO: implement resuming at an arbitrary address
+ goto not_impl;
+ }
+ cont = 1;
+ expect_break_response = 1;
+ break;
+ case 'H':
+ if (command[1] == 'g' || command[1] == 'c') {;
+ //no thread suport, just acknowledge
+ gdb_send_command("OK");
+ } else {
+ goto not_impl;
+ }
+ break;
+ case 'Z': {
+ uint8_t type = command[1];
+ if (type < '2') {
+ uint32_t address = strtoul(command+3, NULL, 16);
+ insert_breakpoint(context, address, (uint8_t *)gdb_debug_enter);
+ gdb_send_command("OK");
+ } else {
+ //watchpoints are not currently supported
+ gdb_send_command("");
+ }
+ break;
+ }
+ case 'z': {
+ uint8_t type = command[1];
+ if (type < '2') {
+ uint32_t address = strtoul(command+3, NULL, 16);
+ remove_breakpoint(context, address);
+ gdb_send_command("OK");
+ } else {
+ //watchpoints are not currently supported
+ gdb_send_command("");
+ }
+ break;
+ }
+ case 'g': {
+ char * cur = send_buf;
+ for (int i = 0; i < 8; i++)
+ {
+ hex_32(context->dregs[i], cur);
+ cur += 8;
+ }
+ for (int i = 0; i < 8; i++)
+ {
+ hex_32(context->aregs[i], cur);
+ cur += 8;
+ }
+ hex_32(calc_status(context), cur);
+ cur += 8;
+ hex_32(pc, cur);
+ cur += 8;
+ *cur = 0;
+ gdb_send_command(send_buf);
+ break;
+ }
+ case 'm': {
+ char * rest;
+ uint32_t address = strtoul(command+1, &rest, 16);
+ uint32_t size = strtoul(rest+1, NULL, 16);
+ if (size > sizeof(send_buf-1)/2) {
+ size = sizeof(send_buf-1)/2;
+ }
+ char *cur = send_buf;
+ while (size)
+ {
+ hex_8(read_byte(context, address), cur);
+ cur += 2;
+ address++;
+ size--;
+ }
+ *cur = 0;
+ gdb_send_command(send_buf);
+ break;
+ }
+ case 'p': {
+ unsigned long reg = strtoul(command+1, NULL, 16);
+
+ if (reg < 8) {
+ hex_32(context->dregs[reg], send_buf);
+ } else if (reg < 16) {
+ hex_32(context->aregs[reg-8], send_buf);
+ } else if (reg == 16) {
+ hex_32(calc_status(context), send_buf);
+ } else if (reg == 17) {
+ hex_32(pc, send_buf);
+ } else {
+ send_buf[0] = 0;
+ }
+ send_buf[8] = 0;
+ gdb_send_command(send_buf);
+ break;
+ }
+ case 'q':
+ if (!memcmp("Supported", command+1, strlen("Supported"))) {
+ sprintf(send_buf, "PacketSize=%X", (int)bufsize);
+ gdb_send_command(send_buf);
+ } else if (!memcmp("Attached", command+1, strlen("Supported"))) {
+ //not really meaningful for us, but saying we spawned a new process
+ //is probably closest to the truth
+ gdb_send_command("0");
+ } else if (!memcmp("Offsets", command+1, strlen("Offsets"))) {
+ //no relocations, so offsets are all 0
+ gdb_send_command("Text=0;Data=0;Bss=0");
+ } else if (!memcmp("Symbol", command+1, strlen("Symbol"))) {
+ gdb_send_command("");
+ } else if (!memcmp("TStatus", command+1, strlen("TStatus"))) {
+ //TODO: actual tracepoint support
+ gdb_send_command("T0;tnotrun:0");
+ } else if (!memcmp("TfV", command+1, strlen("TfV")) || !memcmp("TfP", command+1, strlen("TfP"))) {
+ //TODO: actual tracepoint support
+ gdb_send_command("");
+ } else if (command[1] == 'C') {
+ //we only support a single thread currently, so send 1
+ gdb_send_command("1");
+ } else {
+ goto not_impl;
+ }
+ break;
+ case 'v':
+ if (!memcmp("Cont?", command+1, strlen("Cont?"))) {
+ gdb_send_command("vCont;c;C;s;S");
+ } else if (!memcmp("Cont;", command+1, strlen("Cont;"))) {
+ switch (*(command + 1 + strlen("Cont;")))
+ {
+ case 'c':
+ case 'C':
+ //might be interesting to have continue with signal fire a
+ //trap exception or something, but for no we'll treat it as
+ //a normal continue
+ cont = 1;
+ expect_break_response = 1;
+ break;
+ default:
+ goto not_impl;
+ }
+ } else {
+ goto not_impl;
+ }
+ break;
+ case '?':
+ gdb_send_command("S05");
+ break;
+ default:
+ goto not_impl;
+
+ }
+ return;
+not_impl:
+ fprintf(stderr, "Command %s is not implemented, exiting...\n", command);
+ exit(1);
+}
+
+m68k_context * gdb_debug_enter(m68k_context * context, uint32_t pc)
+{
+ fprintf(stderr, "Entered debugger at address %X\n", pc);
+ if (expect_break_response) {
+ gdb_send_command("S05");
+ expect_break_response = 0;
+ }
resume_pc = pc;
cont = 0;
uint8_t partial = 0;
@@ -35,7 +288,7 @@ void gdb_debug_enter(genesis_context * gen, uint32_t pc)
} else if (partial) {
if (curbuf != buf) {
memmove(curbuf, buf, end-curbuf);
- end -= cufbuf - buf;
+ end -= curbuf - buf;
}
int numread = read(STDIN_FILENO, end, bufsize - (end-buf));
end += numread;
@@ -55,10 +308,13 @@ void gdb_debug_enter(genesis_context * gen, uint32_t pc)
if (end-curbuf >= 2) {
//TODO: verify checksum
//Null terminate payload
- *curbuf = 0
+ *curbuf = 0;
//send acknowledgement
- write(FILENO_STDOUT, "+", 1);
- gdb_run_command(genesis_context * gen, start);
+ if (write(STDOUT_FILENO, "+", 1) < 1) {
+ fputs("Error writing to stdout\n", stderr);
+ exit(1);
+ }
+ gdb_run_command(context, pc, start);
curbuf += 2;
}
} else {
@@ -66,115 +322,20 @@ void gdb_debug_enter(genesis_context * gen, uint32_t pc)
partial = 1;
break;
}
- }
- }
- }
- fcntl(STDIN_FILENO, FD_SETFL, O_NONBLOCK);
-}
-
-void gdb_run_command(genesis_context * gen, char * command)
-{
- switch(*command)
- {
- case 'c':
- if (*(command+1) != 0) {
- resume_pc =
- }
- cont = 1;
- expect_break_response = 1;
- break;
- case 's':
-
- }
-}
-
-void gdb_run_commands(genesis_context * gen)
-{
- int enter_debugger = 0;
- char * cur = buf;
- while(cur < curbuf);
- {
- if(*cur == '$') {
- cur++
- char * start = cur;
- while (cur < curbuf && *cur != '#') {
- cur++;
- }
- if (*cur == '#') {
- //check to make sure we've received the checksum bytes
- if (curbuf-cur >= 2) {
- //TODO: verify checksum
- //Null terminate payload
- //send acknowledgement
- write(FILENO_STDOUT, "+", 1);
- gdb_run_command(genesis_context * gen, start);
- cur += 2;
- } else {
- cur = start - 1;
- break;
- }
- } else {
- cur = start - 1;
- break;
- }
- } else {
- if (*cur == 0x03) {
- enter_debugger = 1;
- }
- cur++;
- }
- }
-
- //FIXME
- if (consumed == curbuf-buf) {
- curbuf = buf;
- } else if (consumed > 0) {
- memmove(buf, buf + consumed, curbuf - buf - consumed);
- curbuf -= consumed;
- }
-}
-
-int gdb_command_poll(genesis_context * gen)
-{
- for(;;)
- {
- if (curbuf == buf + bufsize) {
- //buffer is full, expand it
- bufsize *= 2;
- buf = realloc(buf, bufsize);
- if (!buf) {
- fprintf(stderr, "Failed to grow GDB command buffer to %d bytes\n", (int)bufsize);
- exit(1);
- }
- curbuf = buf + bufsize/2;
- }
- int numread = read(STDIN_FILENO, buf, bufsize);
- if (numread < 0) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- return 0;
} else {
- fprintf(stderr, "Error %d while reading GDB commands from stdin", errno);
- exit(1);
+ fprintf(stderr, "Ignoring character %c\n", *curbuf);
}
- } else if (numread == 0) {
- exit(0);
}
- for (curbuf = buf, end = buf+numread; curbuf < end; curbuf++)
- {
- if (*curbuf = 0x03)
- {
- curbuf++;
- return 1;
- }
+ if (curbuf == end) {
+ curbuf = NULL;
}
}
- return 0;
+ return context;
}
-void gdb_remote_init()
+void gdb_remote_init(void)
{
- fcntl(STDIN_FILENO, FD_SETFL, O_NONBLOCK);
buf = malloc(INITIAL_BUFFER_SIZE);
- curbuf = buf;
- bufzie = INITIAL_BUFFER_SIZE;
+ curbuf = NULL;
+ bufsize = INITIAL_BUFFER_SIZE;
}
diff --git a/vdp.c b/vdp.c
index 103ed63..5cf5797 100644
--- a/vdp.c
+++ b/vdp.c
@@ -197,7 +197,7 @@ void vdp_print_sprite_table(vdp_context * context)
uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3;
uint8_t pri = context->vdpmem[address + 4] >> 7;
uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5;
- //printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern);
+ printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern);
current_index = link;
count++;
} while (current_index != 0 && count < 80);