# HG changeset patch # User Mike Pavone # Date 1391931429 28800 # Node ID 1495179d67371ebced2ae55d26ba44c7c8a8212e # Parent f66c78cbdcaa90a261edbe2a5c38eb017af13e30 Initial GDB remote debugging support. Lacks some features, but breakpoints and basic inspection of registers and memory work. diff -r f66c78cbdcaa -r 1495179d6737 Makefile --- a/Makefile Fri Feb 07 21:18:05 2014 -0800 +++ b/Makefile Sat Feb 08 23:37:09 2014 -0800 @@ -26,8 +26,8 @@ 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 -r f66c78cbdcaa -r 1495179d6737 blastem.c --- a/blastem.c Fri Feb 07 21:18:05 2014 -0800 +++ b/blastem.c Sat Feb 08 23:37:09 2014 -0800 @@ -10,6 +10,7 @@ #include "vdp.h" #include "render.h" #include "blastem.h" +#include "gdb_remote.h" #include "gst.h" #include "util.h" #include @@ -1585,7 +1586,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ } set_keybindings(); - init_run_cpu(&gen, debug, address_log, statefile); + init_run_cpu(&gen, address_log, statefile, debuggerfun); return 0; } diff -r f66c78cbdcaa -r 1495179d6737 gdb_remote.c --- a/gdb_remote.c Fri Feb 07 21:18:05 2014 -0800 +++ b/gdb_remote.c Sat Feb 08 23:37:09 2014 -0800 @@ -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 #include #include #include #include +#include -#define INITIAL_BUFFER_SIZE 4096 +#define INITIAL_BUFFER_SIZE (16*1024) char * buf = NULL; char * curbuf = NULL; @@ -20,9 +21,261 @@ 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 @@ } 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 @@ 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 @@ partial = 1; break; } + } else { + fprintf(stderr, "Ignoring character %c\n", *curbuf); } } + if (curbuf == end) { + curbuf = NULL; + } } - 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': - - } + return context; } -void gdb_run_commands(genesis_context * gen) +void gdb_remote_init(void) { - 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; - } + buf = malloc(INITIAL_BUFFER_SIZE); + curbuf = NULL; + bufsize = INITIAL_BUFFER_SIZE; } - -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); - } - } else if (numread == 0) { - exit(0); - } - for (curbuf = buf, end = buf+numread; curbuf < end; curbuf++) - { - if (*curbuf = 0x03) - { - curbuf++; - return 1; - } - } - } - return 0; -} - -void gdb_remote_init() -{ - fcntl(STDIN_FILENO, FD_SETFL, O_NONBLOCK); - buf = malloc(INITIAL_BUFFER_SIZE); - curbuf = buf; - bufzie = INITIAL_BUFFER_SIZE; -} diff -r f66c78cbdcaa -r 1495179d6737 vdp.c --- a/vdp.c Fri Feb 07 21:18:05 2014 -0800 +++ b/vdp.c Sat Feb 08 23:37:09 2014 -0800 @@ -197,7 +197,7 @@ 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);