summaryrefslogtreecommitdiff
path: root/gdb_remote.c
diff options
context:
space:
mode:
authorMichael Pavone <pavone@retrodev.com>2014-02-11 21:52:15 -0800
committerMichael Pavone <pavone@retrodev.com>2014-02-11 21:52:15 -0800
commit90f9f11749badf66785b6fb416d25e72cdc32c2d (patch)
tree9756acada9554b85f72010e67da1eae1c77b91da /gdb_remote.c
parent6ed58ab3da90a6987bf3b8d51c7db27ed1513460 (diff)
parent04b527cd9945160456e8112afb221acef5e1c4fe (diff)
Merge
Diffstat (limited to 'gdb_remote.c')
-rw-r--r--gdb_remote.c381
1 files changed, 271 insertions, 110 deletions
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;
}