mirror of
https://github.com/n64dev/cen64.git
synced 2024-06-15 18:48:02 -04:00
Implement gdb debugger
This commit is contained in:
parent
2865d107e4
commit
ee3d2fcc47
|
@ -285,6 +285,11 @@ set(DEVICE_SOURCES
|
|||
${PROJECT_SOURCE_DIR}/device/sha1.c
|
||||
)
|
||||
|
||||
set(GDB_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/gdb/gdb.c
|
||||
${PROJECT_SOURCE_DIR}/gdb/protocol.c
|
||||
)
|
||||
|
||||
set(OS_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/os/common/gl_hints.c
|
||||
${PROJECT_SOURCE_DIR}/os/common/input.c
|
||||
|
@ -438,6 +443,7 @@ add_executable(cen64
|
|||
${COMMON_SOURCES}
|
||||
${DD_SOURCES}
|
||||
${DEVICE_SOURCES}
|
||||
${GDB_SOURCES}
|
||||
${OS_SOURCES}
|
||||
${PI_SOURCES}
|
||||
${RDP_SOURCES}
|
||||
|
|
14
cen64.c
14
cen64.c
|
@ -16,6 +16,7 @@
|
|||
#include "device/options.h"
|
||||
#include "device/sha1.h"
|
||||
#include "device/sha1_sums.h"
|
||||
#include "gdb/gdb.h"
|
||||
#include "os/common/alloc.h"
|
||||
#include "os/common/rom_file.h"
|
||||
#include "os/common/save_file.h"
|
||||
|
@ -179,6 +180,8 @@ int cen64_main(int argc, const char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Allocate memory for and create the device.
|
||||
if (cen64_alloc(&cen64_device_mem, sizeof(*device), false) == NULL) {
|
||||
printf("Failed to allocate enough memory for a device.\n");
|
||||
|
@ -197,9 +200,20 @@ int cen64_main(int argc, const char **argv) {
|
|||
}
|
||||
|
||||
else {
|
||||
struct gdb* debugger = NULL;
|
||||
|
||||
if (options.debugger_addr) {
|
||||
debugger = gdb_alloc();
|
||||
gdb_init(debugger, device, options.debugger_addr);
|
||||
}
|
||||
|
||||
device->multithread = options.multithread;
|
||||
status = run_device(device, options.no_video);
|
||||
device_destroy(device, options.cart_path);
|
||||
|
||||
if (debugger) {
|
||||
gdb_destroy(debugger);
|
||||
}
|
||||
}
|
||||
|
||||
cen64_free(&cen64_device_mem);
|
||||
|
|
260
gdb/gdb.c
Normal file
260
gdb/gdb.c
Normal file
|
@ -0,0 +1,260 @@
|
|||
//
|
||||
// gdb.c: gdb remote debugger implementation
|
||||
//
|
||||
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
|
||||
// Copyright (C) 2015, Tyler J. Stachecki.
|
||||
//
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// 'LICENSE', which is part of this source code package.
|
||||
//
|
||||
|
||||
#include "gdb/gdb.h"
|
||||
#include "gdb/protocol.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
cen64_cold void gdb_handle_breakpoint(void* data, enum vr4300_debug_break_reason reason);
|
||||
|
||||
int gdb_read(struct gdb* gdb) {
|
||||
if (gdb->pending_data >= MAX_GDB_PACKET_SIZE) {
|
||||
// if the gdb client is well behaved this should never happen
|
||||
gdb->pending_data = 0;
|
||||
}
|
||||
|
||||
int bytes_read = recv(gdb->client, gdb->packet_buffer + gdb->pending_data, MAX_GDB_PACKET_SIZE, 0);
|
||||
|
||||
if (bytes_read > 0) {
|
||||
gdb->pending_data += bytes_read;
|
||||
}
|
||||
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
void gdb_mark_read(struct gdb* gdb, int handled_bytes) {
|
||||
if (handled_bytes <= gdb->pending_data) {
|
||||
gdb->pending_data -= handled_bytes;
|
||||
} else {
|
||||
gdb->pending_data = 0;
|
||||
}
|
||||
|
||||
if (gdb->pending_data) {
|
||||
for (int i = 0; i < gdb->pending_data; i++) {
|
||||
gdb->packet_buffer[i] = gdb->packet_buffer[i + handled_bytes];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool gdb_parse_packet(const char* input, int len, const char** command_start, const char** command_end) {
|
||||
const char* string_end = input + len;
|
||||
|
||||
while (input < string_end) {
|
||||
if (*input == '$') {
|
||||
input++;
|
||||
*command_start = input;
|
||||
break;
|
||||
}
|
||||
input++;
|
||||
}
|
||||
|
||||
while (input < string_end) {
|
||||
if (*input == '#') {
|
||||
*command_end = input;
|
||||
return true;
|
||||
} else {
|
||||
input++;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CEN64_THREAD_RETURN_TYPE gdb_thread(void *opaque) {
|
||||
cen64_thread_setname(NULL, "gdb");
|
||||
struct gdb *gdb = (struct gdb *) opaque;
|
||||
|
||||
cen64_mutex_lock(&gdb->client_mutex);
|
||||
|
||||
// wait until first breakpoint is hit before entering loop
|
||||
if (gdb->flags & GDB_FLAGS_INITIAL) {
|
||||
cen64_cv_wait(&gdb->client_semaphore, &gdb->client_mutex);
|
||||
} else {
|
||||
cen64_mutex_lock(&gdb->client_mutex);
|
||||
}
|
||||
|
||||
while (gdb->flags & GDB_FLAGS_CONNECTED) {
|
||||
int bytes_read = gdb_read(gdb);
|
||||
|
||||
debug("rec: %.*s\n", bytes_read, gdb->packet_buffer + gdb->pending_data - bytes_read);
|
||||
|
||||
if (bytes_read <= 0) {
|
||||
gdb->flags &= ~GDB_FLAGS_CONNECTED;
|
||||
printf("Debug socket closed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
const char* command_start;
|
||||
const char* command_end;
|
||||
|
||||
bool did_handle = false;
|
||||
|
||||
do {
|
||||
int handled_bytes = 0;
|
||||
|
||||
int search_end = gdb->pending_data;
|
||||
|
||||
if (gdb_parse_packet(gdb->packet_buffer, gdb->pending_data, &command_start, &command_end)) {
|
||||
send(gdb->client, "+", strlen("+"), 0);
|
||||
gdb_handle_packet(gdb, command_start, command_end);
|
||||
|
||||
// +3, 1 byte for the '#' and 2 additional bytes for the checksum
|
||||
handled_bytes = (command_end - gdb->packet_buffer) + 3;
|
||||
search_end = command_start - gdb->packet_buffer;
|
||||
}
|
||||
|
||||
int at;
|
||||
|
||||
for (at = 0; at < search_end && gdb->packet_buffer[at] != '$'; at++) {
|
||||
if (gdb->packet_buffer[at] == 0x03) {
|
||||
vr4300_signal_break(gdb->device->vr4300);
|
||||
}
|
||||
}
|
||||
|
||||
if (at > handled_bytes) {
|
||||
handled_bytes = at;
|
||||
}
|
||||
|
||||
bytes_read -= handled_bytes;
|
||||
gdb_mark_read(gdb, handled_bytes);
|
||||
} while (did_handle);
|
||||
|
||||
}
|
||||
|
||||
return CEN64_THREAD_RETURN_VAL;
|
||||
}
|
||||
|
||||
cen64_cold void gdb_handle_breakpoint(void* data, enum vr4300_debug_break_reason reason) {
|
||||
struct gdb* gdb = (struct gdb*)data;
|
||||
|
||||
debug("Stopping at 0x%08x\n", (uint32_t)vr4300_get_pc(gdb->device->vr4300));
|
||||
|
||||
if (!(gdb->flags & GDB_FLAGS_CONNECTED)) {
|
||||
return;
|
||||
} else if (gdb->flags & GDB_FLAGS_INITIAL) {
|
||||
gdb->flags &= ~GDB_FLAGS_INITIAL;
|
||||
vr4300_remove_breakpoint(gdb->device->vr4300, 0xFFFFFFFF80000000ULL);
|
||||
cen64_cv_signal(&gdb->client_semaphore);
|
||||
} else {
|
||||
gdb_send_stop_reply(gdb, reason == VR4300_DEBUG_BREAK_REASON_BREAKPOINT);
|
||||
}
|
||||
|
||||
cen64_mutex_lock(&gdb->client_mutex);
|
||||
gdb->flags |= GDB_FLAGS_PAUSED;
|
||||
cen64_cv_wait(&gdb->client_semaphore, &gdb->client_mutex);
|
||||
gdb->flags &= ~GDB_FLAGS_PAUSED;
|
||||
|
||||
if (!(gdb->flags & GDB_FLAGS_CONNECTED)) {
|
||||
vr4300_connect_debugger(gdb->device->vr4300, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
cen64_cold struct gdb* gdb_alloc() {
|
||||
struct gdb* result = malloc(sizeof(struct gdb));
|
||||
memset(result, 0, sizeof(struct gdb));
|
||||
return result;
|
||||
}
|
||||
|
||||
cen64_cold bool gdb_init(struct gdb* gdb, struct cen64_device* device, const char* host) {
|
||||
int port;
|
||||
if (sscanf(host, "localhost:%d", &port) != 1) {
|
||||
printf("debugger error: -debug flag must be followed by locahost:<port number>\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (port < 0 || port > 0xffff) {
|
||||
printf("debugger error: -debug port must be a value between 0-65535\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((gdb->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
|
||||
printf("debugger error: could create socket\n");
|
||||
return false;
|
||||
}
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
//addr.sin_addr.s_addr = IPADDR_ANY;
|
||||
addr.sin_port = htons((unsigned short)port);
|
||||
|
||||
int flag = 1;
|
||||
if (-1 == setsockopt(gdb->socket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag))) {
|
||||
printf("debugger error: could not open port %d\n", (int)port);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(bind(gdb->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
printf("debugger error: could not open port %d\n", (int)port);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(listen(gdb->socket, 1) < 0) {
|
||||
printf("debugger error: could not listen on port %d\n", (int)port);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Waiting for gdb connection on port %d\n", (int)port);
|
||||
|
||||
if((gdb->client = accept(gdb->socket, NULL, NULL)) < 0) {
|
||||
printf("debugger error: could not connect to client on port %d\n", (int)port);
|
||||
return false;
|
||||
}
|
||||
|
||||
gdb->device = device;
|
||||
device_connect_debugger(device, gdb, &gdb_handle_breakpoint);
|
||||
|
||||
gdb->flags = GDB_FLAGS_INITIAL | GDB_FLAGS_CONNECTED;
|
||||
vr4300_set_breakpoint(device->vr4300, 0xFFFFFFFF80000000ULL);
|
||||
|
||||
if (cen64_mutex_create(&gdb->client_mutex)) {
|
||||
printf("Failed to create gdb client semaphore.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cen64_cv_create(&gdb->client_semaphore)) {
|
||||
cen64_mutex_destroy(&gdb->client_mutex);
|
||||
printf("Failed to create gdb client semaphore.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cen64_thread_create(&gdb->thread, gdb_thread, gdb)) {
|
||||
cen64_mutex_destroy(&gdb->client_mutex);
|
||||
cen64_cv_destroy(&gdb->client_semaphore);
|
||||
printf("Failed to create gdb thread.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cen64_cold void gdb_destroy(struct gdb* gdb) {
|
||||
gdb->flags = 0;
|
||||
|
||||
shutdown(gdb->client, 2);
|
||||
shutdown(gdb->socket, 2);
|
||||
|
||||
cen64_thread_join(&gdb->thread);
|
||||
cen64_mutex_destroy(&gdb->client_mutex);
|
||||
cen64_cv_destroy(&gdb->client_semaphore);
|
||||
|
||||
gdb->device = NULL;
|
||||
free(gdb);
|
||||
}
|
42
gdb/gdb.h
Normal file
42
gdb/gdb.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// gdb.h: gdb remote debugger implementation
|
||||
//
|
||||
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
|
||||
// Copyright (C) 2015, Tyler J. Stachecki.
|
||||
//
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// 'LICENSE', which is part of this source code package.
|
||||
//
|
||||
|
||||
#ifndef _gdb_h__
|
||||
#define _gdb_h__
|
||||
#include "common.h"
|
||||
#include "vr4300/interface.h"
|
||||
#include "device/device.h"
|
||||
|
||||
#define MAX_GDB_PACKET_SIZE 0x4000
|
||||
|
||||
enum gdb_flags {
|
||||
GDB_FLAGS_INITIAL = 0x1,
|
||||
GDB_FLAGS_CONNECTED = 0x2,
|
||||
GDB_FLAGS_PAUSED = 0x4,
|
||||
};
|
||||
|
||||
struct gdb {
|
||||
int socket;
|
||||
int client;
|
||||
struct cen64_device* device;
|
||||
int pending_data;
|
||||
char packet_buffer[MAX_GDB_PACKET_SIZE*2];
|
||||
char output_buffer[MAX_GDB_PACKET_SIZE];
|
||||
int flags;
|
||||
cen64_thread thread;
|
||||
cen64_mutex client_mutex;
|
||||
cen64_cv client_semaphore;
|
||||
};
|
||||
|
||||
cen64_cold struct gdb* gdb_alloc();
|
||||
cen64_cold bool gdb_init(struct gdb* gdb, struct cen64_device* device, const char* host);
|
||||
cen64_cold void gdb_destroy(struct gdb* gdb);
|
||||
|
||||
#endif
|
351
gdb/protocol.c
Normal file
351
gdb/protocol.c
Normal file
|
@ -0,0 +1,351 @@
|
|||
//
|
||||
// protocol.c: gdb message parsing and responding
|
||||
//
|
||||
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
|
||||
// Copyright (C) 2015, Tyler J. Stachecki.
|
||||
//
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// 'LICENSE', which is part of this source code package.
|
||||
//
|
||||
|
||||
#include "gdb/protocol.h"
|
||||
#include "gdb/gdb.h"
|
||||
#include "vr4300/cpu.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define GDB_GET_EXC_CODE(cause) (((cause) >> 2) & 0x1f)
|
||||
#define GDB_TRANSLATE_PC(pc) ((uint64_t)(pc) | 0xFFFFFFFF00000000ULL)
|
||||
#define GDB_GLOBAL_THREAD_ID 1
|
||||
#define GDB_STR_STARTS_WITH(str, const_str) (strncmp(str, const_str, sizeof const_str - 1) == 0)
|
||||
|
||||
static int gdb_signals[32] = {
|
||||
2, // SIGINT
|
||||
11, // SIGSEGV
|
||||
11, // SIGSEGV
|
||||
11, // SIGSEGV
|
||||
11, // SIGSEGV
|
||||
11, // SIGSEGV
|
||||
10, // SIGBUS
|
||||
10, // SIGBUS
|
||||
12, // SIGSYS
|
||||
5, // SIGTRAP
|
||||
4, // SIGILL
|
||||
30, // SIGUSR1
|
||||
8, // SIGFPE
|
||||
5, // SIGTRAP
|
||||
0, // reserved
|
||||
8, // SIGFPE
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
5, // SIGTRAP
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
};
|
||||
|
||||
int gdb_read_hex_digit(char character) {
|
||||
if (character >= 'a' && character <= 'f') {
|
||||
return 10 + character - 'a';
|
||||
} else if (character >= 'A' && character <= 'F') {
|
||||
return 10 + character - 'A';
|
||||
} else if (character >= '0' && character <= '9') {
|
||||
return character - '0';
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t gdb_parse_hex(const char* src, int max_bytes) {
|
||||
uint32_t result = 0;
|
||||
int current_char;
|
||||
int max_characters = max_bytes * 2;
|
||||
|
||||
for (current_char = 0; current_char < max_characters; ++current_char) {
|
||||
int digit = gdb_read_hex_digit(*src);
|
||||
|
||||
if (digit != -1) {
|
||||
result = (result << 4) + digit;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
++src;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static char gdb_hex_letters[16] = "0123456789abcdef";
|
||||
|
||||
char* gdb_write_hex64(char* target, uint64_t data, int data_size) {
|
||||
int shift = data_size * 8 - 4;
|
||||
|
||||
while (shift >= 0) {
|
||||
*target++ = gdb_hex_letters[(data >> shift) & 0xF];
|
||||
shift -= 4;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
int gdb_apply_checksum(char* message) {
|
||||
char* message_start = message;
|
||||
if (*message == '$') {
|
||||
++message;
|
||||
}
|
||||
|
||||
unsigned char checksum = 0;
|
||||
while (*message)
|
||||
{
|
||||
if (*message == '#') {
|
||||
++message;
|
||||
break;
|
||||
}
|
||||
|
||||
checksum += (unsigned char)*message;
|
||||
++message;
|
||||
}
|
||||
|
||||
sprintf(message, "%02x", checksum);
|
||||
|
||||
return (message - message_start) + 2;
|
||||
}
|
||||
|
||||
void gdb_send(struct gdb* gdb) {
|
||||
int messageLen = gdb_apply_checksum(gdb->output_buffer);
|
||||
send(gdb->client, gdb->output_buffer, messageLen, 0);
|
||||
debug("send: %.*s\n", messageLen, gdb->output_buffer);
|
||||
}
|
||||
|
||||
void gdb_send_literal(struct gdb* gdb, const char* message) {
|
||||
send(gdb->client, message, strlen(message), 0);
|
||||
debug("send: %s\n", message);
|
||||
}
|
||||
|
||||
void gdb_handle_query(struct gdb* gdb, const char* command_start, const char *command_end) {
|
||||
if (GDB_STR_STARTS_WITH(command_start, "qSupported")) {
|
||||
strcpy(gdb->output_buffer, "$PacketSize=4000;vContSupported+;swbreak+#");
|
||||
gdb_send(gdb);
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "qTStatus")) {
|
||||
gdb_send_literal(gdb, "$#00");
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "qfThreadInfo")) {
|
||||
sprintf(gdb->output_buffer, "$m%x#", GDB_GLOBAL_THREAD_ID);
|
||||
gdb_send(gdb);
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "qsThreadInfo")) {
|
||||
strcpy(gdb->output_buffer, "$l#");
|
||||
gdb_send(gdb);
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "qAttached")) {
|
||||
strcpy(gdb->output_buffer, "$0#");
|
||||
gdb_send(gdb);
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "qC")) {
|
||||
sprintf(gdb->output_buffer, "$QC%x#", GDB_GLOBAL_THREAD_ID);
|
||||
gdb_send(gdb);
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "qTfV")) {
|
||||
gdb_send_literal(gdb, "$#00");
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "qTfP")) {
|
||||
gdb_send_literal(gdb, "$#00");
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "qOffsets")) {
|
||||
sprintf(gdb->output_buffer, "$Text=%x;Data=%x;Bss=%x#", 0, 0, 0);
|
||||
gdb_send(gdb);
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "qSymbol")) {
|
||||
gdb_send_literal(gdb, "$OK#9a");
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "qThreadExtraInfo")) {
|
||||
strcpy(gdb->output_buffer, "$746872656164#");
|
||||
gdb_send(gdb);
|
||||
} else {
|
||||
gdb_send_literal(gdb, "$#00");
|
||||
}
|
||||
}
|
||||
|
||||
void gdb_handle_v(struct gdb* gdb, const char* command_start, const char *command_end) {
|
||||
if (GDB_STR_STARTS_WITH(command_start, "vMustReplyEmpty")) {
|
||||
gdb_send_literal(gdb, "$#00");
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "vCont")) {
|
||||
if (command_start[5] == '?') {
|
||||
strcpy(gdb->output_buffer, "$c;t#");
|
||||
gdb_send(gdb);
|
||||
} else {
|
||||
switch (command_start[6])
|
||||
{
|
||||
case 'c':
|
||||
cen64_cv_signal(&gdb->client_semaphore);
|
||||
break;
|
||||
case 't':
|
||||
vr4300_signal_break(gdb->device->vr4300);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (GDB_STR_STARTS_WITH(command_start, "vKill")) {
|
||||
gdb->flags &= ~GDB_FLAGS_CONNECTED;
|
||||
gdb->flags &= ~GDB_FLAGS_PAUSED;
|
||||
cen64_cv_signal(&gdb->client_semaphore);
|
||||
gdb_send_literal(gdb, "$OK#9a");
|
||||
} else {
|
||||
gdb_send_literal(gdb, "$#00");
|
||||
}
|
||||
}
|
||||
|
||||
void gdb_reply_registers(struct gdb* gdb) {
|
||||
char* current = gdb->output_buffer;
|
||||
*current++ = '$';
|
||||
|
||||
// R0
|
||||
current = gdb_write_hex64(current, 0, sizeof(uint64_t));
|
||||
for (int i = VR4300_REGISTER_AT; i <= VR4300_REGISTER_RA; i++) {
|
||||
current = gdb_write_hex64(current, vr4300_get_register(gdb->device->vr4300, i), sizeof(uint64_t));
|
||||
}
|
||||
|
||||
current = gdb_write_hex64(current, vr4300_get_register(gdb->device->vr4300, VR4300_CP0_REGISTER_STATUS), sizeof(uint64_t));
|
||||
current = gdb_write_hex64(current, vr4300_get_register(gdb->device->vr4300, VR4300_REGISTER_LO), sizeof(uint64_t));
|
||||
current = gdb_write_hex64(current, vr4300_get_register(gdb->device->vr4300, VR4300_REGISTER_HI), sizeof(uint64_t));
|
||||
current = gdb_write_hex64(current, vr4300_get_register(gdb->device->vr4300, VR4300_CP0_REGISTER_BADVADDR), sizeof(uint64_t));
|
||||
current = gdb_write_hex64(current, vr4300_get_register(gdb->device->vr4300, VR4300_CP0_REGISTER_CAUSE), sizeof(uint64_t));
|
||||
current += sprintf(current, "%08x%08x", 0, (int32_t)vr4300_get_pc(gdb->device->vr4300));
|
||||
|
||||
for (int i = VR4300_REGISTER_CP1_0; i <= VR4300_REGISTER_CP1_31; i++) {
|
||||
current = gdb_write_hex64(current, vr4300_get_register(gdb->device->vr4300, i), sizeof(uint64_t));
|
||||
}
|
||||
|
||||
current = gdb_write_hex64(current, vr4300_get_register(gdb->device->vr4300, VR4300_CP1_FCR31), sizeof(uint64_t));
|
||||
|
||||
*current++ = '#';
|
||||
*current++ = '\0';
|
||||
|
||||
gdb_send(gdb);
|
||||
}
|
||||
|
||||
void gdb_reply_memory(struct gdb* gdb, const char* command_start, const char *command_end) {
|
||||
char* current = gdb->output_buffer;
|
||||
*current++ = '$';
|
||||
|
||||
const char* len_text = command_start + 1;
|
||||
|
||||
while (*len_text != ',' && len_text != command_end) {
|
||||
++len_text;
|
||||
}
|
||||
|
||||
int32_t addr = gdb_parse_hex(command_start + 1, 4);
|
||||
int32_t len = gdb_parse_hex(len_text + 1, 4);
|
||||
|
||||
int32_t alignedAddr = addr & ~0x3;
|
||||
int32_t byteShift = addr - alignedAddr;
|
||||
|
||||
for (int curr = 0; curr < len + byteShift; curr += 4) {
|
||||
uint32_t word = 0;
|
||||
int32_t vaddr = alignedAddr + curr;
|
||||
|
||||
if (!vr4300_read_word_vaddr(gdb->device->vr4300, vaddr, &word)) {
|
||||
debug("Bad vaddr %08x\n", vaddr);
|
||||
}
|
||||
|
||||
// if (curr > 0x20) {
|
||||
// word = curr;
|
||||
// }
|
||||
|
||||
current = gdb_write_hex64(current, word, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
if (byteShift) {
|
||||
for (int curr = 0; curr < len * 2; curr++) {
|
||||
gdb->output_buffer[curr + 1] = gdb->output_buffer[curr + 1 + byteShift * 2];
|
||||
}
|
||||
}
|
||||
|
||||
current = gdb->output_buffer + 1 + len * 2;
|
||||
|
||||
*current++ = '#';
|
||||
*current++ = '\0';
|
||||
|
||||
gdb_send(gdb);
|
||||
}
|
||||
|
||||
void gdb_handle_packet(struct gdb* gdb, const char* command_start, const char* command_end) {
|
||||
switch (*command_start) {
|
||||
case 'q':
|
||||
gdb_handle_query(gdb, command_start, command_end);
|
||||
break;
|
||||
case 'v':
|
||||
gdb_handle_v(gdb, command_start, command_end);
|
||||
break;
|
||||
case 'H':
|
||||
gdb_send_literal(gdb, "$OK#9a");
|
||||
break;
|
||||
case '!':
|
||||
gdb_send_literal(gdb, "$#00");
|
||||
break;
|
||||
case '?':
|
||||
gdb_send_stop_reply(gdb, false);
|
||||
break;
|
||||
case 'g':
|
||||
gdb_reply_registers(gdb);
|
||||
break;
|
||||
case 'm':
|
||||
gdb_reply_memory(gdb, command_start, command_end);
|
||||
break;
|
||||
case 'D':
|
||||
gdb->flags &= ~GDB_FLAGS_CONNECTED;
|
||||
gdb_send_literal(gdb, "$OK#9a");;
|
||||
break;
|
||||
case 'z':
|
||||
case 'Z':
|
||||
{
|
||||
if (command_start[1] == '0') {
|
||||
uint64_t addr = GDB_TRANSLATE_PC(gdb_parse_hex(&command_start[3], 4));
|
||||
|
||||
if (*command_start == 'z') {
|
||||
vr4300_remove_breakpoint(gdb->device->vr4300, addr);
|
||||
} else {
|
||||
vr4300_set_breakpoint(gdb->device->vr4300, addr);
|
||||
}
|
||||
|
||||
return gdb_send_literal(gdb, "$OK#9a");
|
||||
} else {
|
||||
gdb_send_literal(gdb, "$#00");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gdb_send_literal(gdb, "$#00");
|
||||
}
|
||||
}
|
||||
|
||||
cen64_cold void gdb_send_stop_reply(struct gdb* gdb, bool is_breakpoint) {
|
||||
char* current = gdb->output_buffer;
|
||||
int exc_code;
|
||||
|
||||
if (is_breakpoint) {
|
||||
exc_code = 9;
|
||||
} else {
|
||||
exc_code = GDB_GET_EXC_CODE(vr4300_get_register(gdb->device->vr4300, VR4300_CP0_REGISTER_CAUSE));
|
||||
}
|
||||
current += sprintf(current, "$T%02x", gdb_signals[exc_code]);
|
||||
if (is_breakpoint) {
|
||||
current += sprintf(current, "swbreak:");
|
||||
}
|
||||
current += sprintf(current, "thread:%d;", GDB_GLOBAL_THREAD_ID);
|
||||
*current++ = '#';
|
||||
*current++ = '\0';
|
||||
gdb_send(gdb);
|
||||
}
|
21
gdb/protocol.h
Normal file
21
gdb/protocol.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// protocol.h: gdb message parsing and responding
|
||||
//
|
||||
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
|
||||
// Copyright (C) 2015, Tyler J. Stachecki.
|
||||
//
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// 'LICENSE', which is part of this source code package.
|
||||
//
|
||||
|
||||
#ifndef _gdb_protocol_h__
|
||||
#define _gdb_protocol_h__
|
||||
#include "common.h"
|
||||
|
||||
struct gdb;
|
||||
|
||||
cen64_cold void gdb_send_stop_reply(struct gdb* gdb, bool is_breakpoint);
|
||||
cen64_cold void gdb_handle_packet(struct gdb* gdb, const char* command_start, const char* command_end);
|
||||
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue