Mercurial > repos > blastem
view gdb_remote.c @ 1374:8f404b1fa572
Go back to resetting the refresh counter after a DMA. Probably not quite correct as it is probably reset on VDP triggered refresh, but this is close enough for now given the general limitations with my refresh code. VDP FIFO Testing seems to be passing 100% reliably again (was occassionally failing still with the last commit)
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 23 May 2017 23:47:40 -0700 |
parents | 85a90964b557 |
children | ded16f3d7eb4 d6d4c006a7b3 |
line wrap: on
line source
/* Copyright 2013 Michael Pavone 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. */ #ifdef _WIN32 #define WINVER 0x501 #include <winsock2.h> #include <ws2tcpip.h> int gdb_sock; #define GDB_IN_FD gdb_sock #define GDB_OUT_FD gdb_sock #define GDB_READ(fd, buf, bufsize) recv(fd, buf, bufsize, 0) #define GDB_WRITE(fd, buf, bufsize) send(fd, buf, bufsize, 0) #else #define GDB_IN_FD STDIN_FILENO #define GDB_OUT_FD STDOUT_FILENO #define GDB_READ read #define GDB_WRITE write #endif #include "gdb_remote.h" #include "68kinst.h" #include "debug.h" #include "util.h" #include <unistd.h> #include <fcntl.h> #include <stddef.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #define INITIAL_BUFFER_SIZE (16*1024) #ifdef DO_DEBUG_PRINT #define dfprintf fprintf #else #define dfprintf #endif char * buf = NULL; char * curbuf = NULL; char * end = NULL; size_t bufsize; int cont = 0; int expect_break_response=0; uint32_t resume_pc; static uint16_t branch_t; static uint16_t branch_f; static bp_def * breakpoints = NULL; static uint32_t bp_index = 0; 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 (GDB_WRITE(fd, buf, count) < count) { fatal_error("Error writing to stdout\n"); } } void gdb_send_command(char * command) { char end[3]; write_or_die(GDB_OUT_FD, "$", 1); write_or_die(GDB_OUT_FD, command, strlen(command)); end[0] = '#'; gdb_calc_checksum(command, end+1); write_or_die(GDB_OUT_FD, end, 3); dfprintf(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; } void update_status(m68k_context * context, uint16_t value) { context->status = value >> 8; for (int i = 4; i >= 0; i--) { context->flags[i] = value & 1; value >>= 1; } } uint8_t read_byte(m68k_context * context, uint32_t address) { genesis_context *gen = context->system; //TODO: Use generated read/write functions to support access to hardware that is not ROM or RAM uint16_t * word = get_native_pointer(address & 0xFFFFFFFE, (void **)context->mem_pointers, &context->options->gen); if (word) { if (address & 1) { return *word; } return *word >> 8; } if (address >= 0xA00000 && address < 0xA04000) { return gen->zram[address & 0x1FFF]; } return 0; } void write_byte(m68k_context * context, uint32_t address, uint8_t value) { genesis_context *gen = context->system; //TODO: Use generated read/write functions so that memory map is properly respected uint16_t * word = get_native_pointer(address & 0xFFFFFFFE, (void **)context->mem_pointers, &context->options->gen); if (word) { if (address & 1) { *word = (*word & 0xFF00) | value; } else { *word = (*word & 0xFF) | value << 8; } //TODO: Deal with this more generally once m68k_handle_code_write can handle it if (address >= 0xE00000) { m68k_handle_code_write(address, context); } return; } if (address >= 0xA00000 && address < 0xA04000) { gen->zram[address & 0x1FFF] = value; genesis_context * gen = context->system; #ifndef NO_Z80 z80_handle_code_write(address & 0x1FFF, gen->z80); #endif return; } else { return; } } void gdb_run_command(m68k_context * context, uint32_t pc, char * command) { char send_buf[512]; dfprintf(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 's': { if (*(command+1) != 0) { //TODO: implement resuming at an arbitrary address goto not_impl; } m68kinst inst; genesis_context *gen = context->system; uint16_t * pc_ptr = get_native_pointer(pc, (void **)context->mem_pointers, &context->options->gen); if (!pc_ptr) { fatal_error("Entered gdb remote debugger stub at address %X\n", pc); } uint16_t * after_pc = m68k_decode(pc_ptr, &inst, pc & 0xFFFFFF); uint32_t after = pc + (after_pc-pc_ptr)*2; if (inst.op == M68K_RTS) { after = (read_dma_value(context->aregs[7]/2) << 16) | read_dma_value(context->aregs[7]/2 + 1); } else if (inst.op == M68K_RTE || inst.op == M68K_RTR) { after = (read_dma_value((context->aregs[7]+2)/2) << 16) | read_dma_value((context->aregs[7]+2)/2 + 1); } else if(m68k_is_branch(&inst)) { if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) { branch_f = after; branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; insert_breakpoint(context, branch_t, gdb_debug_enter); } else if(inst.op == M68K_DBCC && inst.extra.cond != COND_FALSE) { branch_t = after; branch_f = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; insert_breakpoint(context, branch_f, gdb_debug_enter); } else { after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; } } insert_breakpoint(context, after, gdb_debug_enter); 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, gdb_debug_enter); bp_def *new_bp = malloc(sizeof(bp_def)); new_bp->next = breakpoints; new_bp->address = address; new_bp->index = bp_index++; breakpoints = new_bp; 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); bp_def **found = find_breakpoint(&breakpoints, address); if (*found) { bp_def * to_remove = *found; *found = to_remove->next; free(to_remove); } 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 'M': { char * rest; uint32_t address = strtoul(command+1, &rest, 16); uint32_t size = strtoul(rest+1, &rest, 16); char *cur = rest+1; while (size) { char tmp[3]; tmp[0] = *(cur++); tmp[1] = *(cur++); tmp[2] = 0; write_byte(context, address, strtoul(tmp, NULL, 16)); address++; size--; } gdb_send_command("OK"); break; } case 'X': //binary transfers aren't supported currently as I don't feel like dealing with the escaping gdb_send_command(""); 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 'P': { char *after = NULL; unsigned long reg = strtoul(command+1, &after, 16); uint32_t value = strtoul(after+1, NULL, 16); if (reg < 8) { context->dregs[reg] = value; } else if (reg < 16) { context->aregs[reg-8] = value; } else if (reg == 16) { update_status(context, value); } else { //supporting updates to PC is going to be a pain gdb_send_command("E01"); break; } gdb_send_command("OK"); 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("Attached"))) { //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("QC1"); } else if (!strcmp("fThreadInfo", command + 1)) { //we only support a single thread currently, so send 1 gdb_send_command("m1"); } else if (!strcmp("sThreadInfo", command + 1)) { gdb_send_command("l"); } else { goto not_impl; } break; case 'v': if (!memcmp("Cont?", command+1, strlen("Cont?"))) { gdb_send_command("vCont;c;C;s;S"); } else if (!strcmp("MustReplyEmpty", command + 1)) { gdb_send_command(""); } 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; case 's': case 'S': { m68kinst inst; genesis_context *gen = context->system; uint16_t * pc_ptr = get_native_pointer(pc, (void **)context->mem_pointers, &context->options->gen); if (!pc_ptr) { fatal_error("Entered gdb remote debugger stub at address %X\n", pc); } uint16_t * after_pc = m68k_decode(pc_ptr, &inst, pc & 0xFFFFFF); uint32_t after = pc + (after_pc-pc_ptr)*2; if (inst.op == M68K_RTS) { after = (read_dma_value(context->aregs[7]/2) << 16) | read_dma_value(context->aregs[7]/2 + 1); } else if (inst.op == M68K_RTE || inst.op == M68K_RTR) { after = (read_dma_value((context->aregs[7]+2)/2) << 16) | read_dma_value((context->aregs[7]+2)/2 + 1); } else if(m68k_is_branch(&inst)) { if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) { branch_f = after; branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; insert_breakpoint(context, branch_t, gdb_debug_enter); } else if(inst.op == M68K_DBCC && inst.extra.cond != COND_FALSE) { branch_t = after; branch_f = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; insert_breakpoint(context, branch_f, gdb_debug_enter); } else { after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; } } insert_breakpoint(context, after, gdb_debug_enter); 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: fatal_error("Command %s is not implemented, exiting...\n", command); } void gdb_debug_enter(m68k_context * context, uint32_t pc) { dfprintf(stderr, "Entered debugger at address %X\n", pc); if (expect_break_response) { gdb_send_command("S05"); expect_break_response = 0; } if ((pc & 0xFFFFFF) == branch_t) { bp_def ** f_bp = find_breakpoint(&breakpoints, branch_f); if (!*f_bp) { remove_breakpoint(context, branch_f); } branch_t = branch_f = 0; } else if((pc & 0xFFFFFF) == branch_f) { bp_def ** t_bp = find_breakpoint(&breakpoints, branch_t); if (!*t_bp) { remove_breakpoint(context, branch_t); } branch_t = branch_f = 0; } //Check if this is a user set breakpoint, or just a temporary one bp_def ** this_bp = find_breakpoint(&breakpoints, pc & 0xFFFFFF); if (!*this_bp) { remove_breakpoint(context, pc & 0xFFFFFF); } resume_pc = pc; cont = 0; uint8_t partial = 0; while(!cont) { if (!curbuf) { int numread = GDB_READ(GDB_IN_FD, buf, bufsize); if (numread < 0) { fatal_error("Failed to read on GDB input file descriptor\n"); } dfprintf(stderr, "read %d bytes\n", numread); curbuf = buf; end = buf + numread; } else if (partial) { if (curbuf != buf) { memmove(curbuf, buf, end-curbuf); end -= curbuf - buf; } int numread = GDB_READ(GDB_IN_FD, end, bufsize - (end-buf)); end += numread; curbuf = buf; } for (; curbuf < end; curbuf++) { if (*curbuf == '$') { curbuf++; char * start = curbuf; while (curbuf < end && *curbuf != '#') { curbuf++; } if (*curbuf == '#') { //check to make sure we've received the checksum bytes if (end-curbuf >= 2) { //TODO: verify checksum //Null terminate payload *curbuf = 0; //send acknowledgement if (GDB_WRITE(GDB_OUT_FD, "+", 1) < 1) { fatal_error("Error writing to stdout\n"); } gdb_run_command(context, pc, start); curbuf += 2; } } else { curbuf--; partial = 1; break; } } else { dfprintf(stderr, "Ignoring character %c\n", *curbuf); } } if (curbuf == end) { curbuf = NULL; } } } #ifdef _WIN32 void gdb_cleanup(void) { WSACleanup(); } WSADATA wsa_data; #endif void gdb_remote_init(void) { buf = malloc(INITIAL_BUFFER_SIZE); curbuf = NULL; bufsize = INITIAL_BUFFER_SIZE; #ifdef _WIN32 WSAStartup(MAKEWORD(2,2), &wsa_data); struct addrinfo request, *result; memset(&request, 0, sizeof(request)); request.ai_family = AF_INET; request.ai_socktype = SOCK_STREAM; request.ai_flags = AI_PASSIVE; getaddrinfo("localhost", "1234", &request, &result); int listen_sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (listen_sock < 0) { fatal_error("Failed to open GDB remote debugging socket"); } if (bind(listen_sock, result->ai_addr, result->ai_addrlen) < 0) { fatal_error("Failed to bind GDB remote debugging socket"); } if (listen(listen_sock, 1) < 0) { fatal_error("Failed to listen on GDB remote debugging socket"); } gdb_sock = accept(listen_sock, NULL, NULL); if (gdb_sock < 0) { fatal_error("accept returned an error while listening on GDB remote debugging socket"); } closesocket(listen_sock); #endif }