diff --git a/external/gdbstub.h b/external/gdbstub.h deleted file mode 100644 index 98ffdc2d..00000000 --- a/external/gdbstub.h +++ /dev/null @@ -1,546 +0,0 @@ -// -// gdbstub version 1.1.1 -// -// MIT License -// -// Copyright (c) 2020 Stephen Lane-Walsh -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -#ifndef GDBSTUB_H -#define GDBSTUB_H - -#include -#include - -typedef void (*gdbstub_connected_t)(void * user_data); -typedef void (*gdbstub_disconnected_t)(void * user_data); -typedef void (*gdbstub_start_t)(void * user_data); -typedef void (*gdbstub_stop_t)(void * user_data); -typedef void (*gdbstub_step_t)(void * user_data); -typedef void (*gdbstub_set_breakpoint_t)(void * user_data, uint32_t address); -typedef void (*gdbstub_clear_breakpoint_t)(void * user_data, uint32_t address); -typedef ssize_t (*gdbstub_get_memory_t)(void * user_data, char * buffer, size_t buffer_length, uint32_t address, size_t length); -typedef ssize_t (*gdbstub_get_register_value_t)(void * user_data, char * buffer, size_t buffer_length, int reg); -typedef ssize_t (*gdbstub_get_general_registers_t)(void * user_data, char * buffer, size_t buffer_length); - -typedef struct gdbstub_config gdbstub_config_t; - -struct gdbstub_config -{ - uint16_t port; - - void * user_data; - - gdbstub_connected_t connected; - - gdbstub_disconnected_t disconnected; - - gdbstub_start_t start; - - gdbstub_stop_t stop; - - gdbstub_step_t step; - - gdbstub_set_breakpoint_t set_breakpoint; - - gdbstub_clear_breakpoint_t clear_breakpoint; - - gdbstub_get_memory_t get_memory; - - gdbstub_get_register_value_t get_register_value; - - gdbstub_get_general_registers_t get_general_registers; - - const char * target_config; - size_t target_config_length; - - const char * memory_map; - size_t memory_map_length; -}; - -typedef struct gdbstub gdbstub_t; - -gdbstub_t * gdbstub_init(gdbstub_config_t config); - -void gdbstub_term(gdbstub_t * gdb); - -void gdbstub_tick(gdbstub_t * gdb); - -void gdbstub_breakpoint_hit(gdbstub_t * gdb); - -#endif // GDBSTUB_H - -#ifdef GDBSTUB_IMPLEMENTATION - -#include -#include -#include -#include -#include - -#if defined(WIN32) -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#pragma comment (lib, "Ws2_32.lib") -#define close closesocket -#else -#include -#include -#endif - -#define GDBSTUB_BUFFER_LENGTH 4096 -#define GDBSTUB_PACKET_LENGTH 2048 - -typedef enum -{ - GDB_STATE_NO_PACKET, - GDB_STATE_IN_PACKET, - GDB_STATE_IN_CHECKSUM, - -} gdbstate_t; - -struct gdbstub -{ - gdbstub_config_t config; - - int server; - int client; - - char buffer[GDBSTUB_BUFFER_LENGTH]; - ssize_t buffer_length; - - gdbstate_t state; - char packet[GDBSTUB_BUFFER_LENGTH]; - ssize_t packet_length; - uint8_t packet_checksum; - - char checksum[2]; - ssize_t checksum_length; - -}; - -void _gdbstub_recv(gdbstub_t * gdb); - -void _gdbstub_send(gdbstub_t * gdb, const char * data, size_t data_length); - -void _gdbstub_send_paged(gdbstub_t * gdb, int offset, int length, const char * data, size_t data_length); - -void _gdbstub_process_packet(gdbstub_t * gdb); - -gdbstub_t * gdbstub_init(gdbstub_config_t config) -{ - gdbstub_t * gdb = (gdbstub_t *)malloc(sizeof(gdbstub_t)); - if (!gdb) { - fprintf(stderr, "out of memory\n"); - return NULL; - } - - gdb->config = config; - gdb->server = -1; - gdb->client = -1; - gdb->state = GDB_STATE_NO_PACKET; - gdb->packet_length = 0; - gdb->packet_checksum = 0; - gdb->checksum_length = 0; - - int result; - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(gdb->config.port); - addr.sin_addr.s_addr = INADDR_ANY; - - gdb->server = socket(AF_INET, SOCK_STREAM, 0); - if (gdb->server < 0) { - perror("socket failed"); - free(gdb); - return NULL; - } - - result = fcntl(gdb->server, F_SETFL, fcntl(gdb->server, F_GETFL, 0) | O_NONBLOCK); - if (result < 0) { - perror("fcntl O_NONBLOCK failed"); - free(gdb); - return NULL; - } - - int reuse = 1; - result = setsockopt(gdb->server, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)); - if (result < 0) { - perror("setsockopt SO_REUSEADDR failed"); - free(gdb); - return NULL; - } - -#ifdef SO_REUSEPORT - result = setsockopt(gdb->server, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)); - if (result < 0) { - perror("setsockopt SO_REUSEPORT failed"); - free(gdb); - return NULL; - } -#endif - - result = bind(gdb->server, (struct sockaddr *)&addr, sizeof(addr)); - if (result < 0) { - perror("bind failed"); - free(gdb); - return NULL; - } - - result = listen(gdb->server, 1); - if (result < 0) { - perror("listen failed"); - free(gdb); - return NULL; - } - - printf("listening for gdb on port %hu\n", gdb->config.port); - return gdb; -} - -void gdbstub_term(gdbstub_t * gdb) -{ - if (gdb) { - if (gdb->client >= 0) { - close(gdb->client); - gdb->client = -1; - } - - if (gdb->server >= 0) { - close(gdb->server); - gdb->server = -1; - } - - free(gdb); - } -} - -void gdbstub_tick(gdbstub_t * gdb) -{ - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - - if (gdb->client < 0) { - gdb->client = accept(gdb->server, (struct sockaddr *)&addr, &addrlen); - if (gdb->client >= 0) { - printf("accepted gdb connection\n"); - fcntl(gdb->client, F_SETFL, fcntl(gdb->client, F_GETFL, 0) | O_NONBLOCK); - - if (gdb->config.connected) { - gdb->config.connected(gdb->config.user_data); - } - } - } - else { - _gdbstub_recv(gdb); - } -} - -void gdbstub_breakpoint_hit(gdbstub_t * gdb) { - _gdbstub_send(gdb, "T05", 3); -} - -void _gdbstub_send(gdbstub_t * gdb, const char * data, size_t data_length) -{ - uint8_t checksum = 0; - for (size_t i = 0; i < data_length; ++i) { - checksum += data[i]; - } - - gdb->packet_length = snprintf(gdb->packet, GDBSTUB_PACKET_LENGTH, "$%s#%02x", data, checksum); - - int bytes = send(gdb->client, gdb->packet, gdb->packet_length, 0); - -# if defined(GDBSTUB_DEBUG) - printf("gdbstub sent '%s'\n", gdb->packet); -# endif - - if (bytes < 0) { - perror("lost gdb connection"); - close(gdb->client); - gdb->client = -1; - gdb->state = GDB_STATE_NO_PACKET; - - if (gdb->config.disconnected) { - gdb->config.disconnected(gdb->config.user_data); - } - } -} - -void _gdbstub_send_paged(gdbstub_t * gdb, int offset, int length, const char * data, size_t data_length) -{ - if (length > GDBSTUB_PACKET_LENGTH - 6) { - length = GDBSTUB_PACKET_LENGTH - 6; - } - - if (length < (data_length - offset)) { - // Any page but the last - gdb->buffer_length = snprintf(gdb->buffer, GDBSTUB_BUFFER_LENGTH, "m%.*s", length, data + offset); - _gdbstub_send(gdb, gdb->buffer, gdb->buffer_length); - } - else if (offset != 0) { - // The last page - gdb->buffer_length = snprintf(gdb->buffer, GDBSTUB_BUFFER_LENGTH, "l%.*s", length, data + offset); - _gdbstub_send(gdb, gdb->buffer, gdb->buffer_length); - } - else { - // The only page - gdb->buffer_length = snprintf(gdb->buffer, GDBSTUB_BUFFER_LENGTH, "l%s", data); - _gdbstub_send(gdb, gdb->buffer, gdb->buffer_length); - } -} - -void _gdbstub_recv(gdbstub_t * gdb) -{ - gdb->buffer_length = recv(gdb->client, gdb->buffer, GDBSTUB_BUFFER_LENGTH, 0); - if (gdb->buffer_length < 0 && errno != EAGAIN) { - perror("lost gdb connection"); - close(gdb->client); - gdb->client = -1; - gdb->state = GDB_STATE_NO_PACKET; - - if (gdb->config.disconnected) { - gdb->config.disconnected(gdb->config.user_data); - } - return; - } - - for (ssize_t i = 0; i < gdb->buffer_length; ++i) { - char c = gdb->buffer[i]; - - switch (gdb->state) - { - case GDB_STATE_NO_PACKET: - if (c == '$') { - gdb->state = GDB_STATE_IN_PACKET; - gdb->packet_length = 0; - gdb->packet_checksum = 0; - } - else if (c == 3) { - // TODO: investigate - } - break; - case GDB_STATE_IN_PACKET: - if (c == '#') { - gdb->state = GDB_STATE_IN_CHECKSUM; - gdb->checksum_length = 0; - } - else { - gdb->packet[gdb->packet_length++] = c; - gdb->packet_checksum += c; - } - break; - case GDB_STATE_IN_CHECKSUM: - gdb->checksum[gdb->checksum_length++] = c; - if (gdb->checksum_length == 2) { - int checksum; - sscanf(gdb->checksum, "%2x", &checksum); - if (gdb->packet_checksum != checksum) { - // freak out? - } - - send(gdb->client, "+", 1, 0); - - gdb->packet[gdb->packet_length] = '\0'; -# if defined(GDBSTUB_DEBUG) - printf("gdbstub received '$%s#%c%c'\n", gdb->packet, gdb->checksum[0], gdb->checksum[1]); -# endif - - _gdbstub_process_packet(gdb); - - gdb->state = GDB_STATE_NO_PACKET; - } - } - } -} - -void _gdbstub_process_packet(gdbstub_t * gdb) -{ - switch (gdb->packet[0]) - { - case 'c': - // Continue execution - if (gdb->config.start) { - gdb->config.start(gdb->config.user_data); - } - return; - case 'D': - // Disconnect - printf("gdb disconnected\n"); - _gdbstub_send(gdb, "OK", 2); - close(gdb->client); - gdb->client = -1; - gdb->state = GDB_STATE_NO_PACKET; - - if (gdb->config.disconnected) { - gdb->config.disconnected(gdb->config.user_data); - } - return; - case 'g': - // Get general registers - if (gdb->config.get_general_registers) { - gdb->buffer_length = gdb->config.get_general_registers(gdb->config.user_data, gdb->buffer, GDBSTUB_BUFFER_LENGTH); - _gdbstub_send(gdb, gdb->buffer, gdb->buffer_length); - } - else { - _gdbstub_send(gdb, "", 0); - } - return; - case 'H': - // Set active thread - _gdbstub_send(gdb, "OK", 2); - return; - case 'm': - // Read memory - if (gdb->config.get_memory) { - int address, length; - sscanf(gdb->packet + 1, "%x,%x", &address, &length); - gdb->buffer_length = gdb->config.get_memory(gdb->config.user_data, gdb->buffer, GDBSTUB_BUFFER_LENGTH, address, length); - _gdbstub_send(gdb, gdb->buffer, gdb->buffer_length); - } - else { - _gdbstub_send(gdb, "", 0); - } - return; - case 'p': - // Read the value of register n - if (gdb->config.get_register_value) { - int reg; - sscanf(gdb->packet + 1, "%x", ®); - gdb->buffer_length = gdb->config.get_register_value(gdb->config.user_data, gdb->buffer, GDBSTUB_BUFFER_LENGTH, reg); - _gdbstub_send(gdb, gdb->buffer, gdb->buffer_length); - } - else { - _gdbstub_send(gdb, "", 0); - } - return; - case 'q': - // Check for available features - if (strncmp(gdb->packet, "qSupported", 10) == 0) { - strcpy(gdb->buffer, "PacketSize=1024"); - gdb->buffer_length = 15; - - if (gdb->config.target_config) { - strcpy(gdb->buffer + gdb->buffer_length, ";qXfer:features:read+"); - gdb->buffer_length += 21; - } - - if (gdb->config.memory_map) { - strcpy(gdb->buffer + gdb->buffer_length, ";qXfer:memory-map:read+"); - gdb->buffer_length += 23; - } - - _gdbstub_send(gdb, gdb->buffer, gdb->buffer_length); - return; - } - // We have no thread ID - else if (gdb->packet[1] == 'C') { - _gdbstub_send(gdb, "QC00", 4); - return; - } - // We are always "attached" to an existing process - else if (strncmp(gdb->packet, "qAttached", 9) == 0) { - _gdbstub_send(gdb, "1", 1); - return; - } - // There is no trace running - else if (strncmp(gdb->packet, "qTStatus", 8) == 0) { - _gdbstub_send(gdb, "T0", 2); - return; - } - // Target configuration XML - else if (strncmp(gdb->packet, "qXfer:features:read:target.xml:", 31) == 0) { - int offset, length; - sscanf(gdb->packet + 31, "%x,%x", &offset, &length); - _gdbstub_send_paged(gdb, offset, length, gdb->config.target_config, gdb->config.target_config_length); - return; - } - // Memory map XML - else if (strncmp(gdb->packet, "qXfer:memory-map:read::", 23) == 0) { - int offset, length; - sscanf(gdb->packet + 23, "%x,%x", &offset, &length); - _gdbstub_send_paged(gdb, offset, length, gdb->config.memory_map, gdb->config.memory_map_length); - return; - } - // Trace control operations - else if (strncmp(gdb->packet, "qTfP", 4) == 0) { - _gdbstub_send(gdb, "", 0); - return; - } - else if (strncmp(gdb->packet, "qTfV", 4) == 0) { - _gdbstub_send(gdb, "", 0); - return; - } - else if (strncmp(gdb->packet, "qTsP", 4) == 0) { - _gdbstub_send(gdb, "", 0); - return; - } - // Thread #0 - else if (strncmp(gdb->packet, "qfThreadInfo", 12) == 0) { - _gdbstub_send(gdb, "lm0", 3); - return; - } - break; - case 's': - // Single execution step - if (gdb->config.step) { - gdb->config.step(gdb->config.user_data); - } - _gdbstub_send(gdb, "T05", 3); - return; - case 'v': - // Various remote operations, not supported - _gdbstub_send(gdb, "", 0); - return; - case 'z': - // Remove breakpoint - if (gdb->config.clear_breakpoint) { - uint32_t address; - sscanf(gdb->packet, "z0,%x", &address); - gdb->config.clear_breakpoint(gdb->config.user_data, address); - } - _gdbstub_send(gdb, "OK", 2); - break; - case 'Z': - // Add breakpoint - if (gdb->config.set_breakpoint) { - uint32_t address; - sscanf(gdb->packet, "Z0,%x", &address); - gdb->config.set_breakpoint(gdb->config.user_data, address); - } - _gdbstub_send(gdb, "OK", 2); - break; - case '?': - // Break immediately - if (gdb->config.stop) { - gdb->config.stop(gdb->config.user_data); - } - // TODO: improve - _gdbstub_send(gdb, "S00", 3); - return; - } - - fprintf(stderr, "unknown gdb command '%s'\n", gdb->packet); -} - -#endif // GDBSTUB_IMPLEMENTATION \ No newline at end of file diff --git a/src/frontend/imgui/Window.cpp b/src/frontend/imgui/Window.cpp index 67dce8bd..5e408416 100644 --- a/src/frontend/imgui/Window.cpp +++ b/src/frontend/imgui/Window.cpp @@ -141,4 +141,5 @@ ImDrawData* Window::Present(n64::Core& core) { void Window::Render(n64::Core& core) { MainMenuBar(core); + DebuggerWindow(core); } diff --git a/src/frontend/imgui/Window.hpp b/src/frontend/imgui/Window.hpp index 80660f58..b76ddf41 100644 --- a/src/frontend/imgui/Window.hpp +++ b/src/frontend/imgui/Window.hpp @@ -21,7 +21,7 @@ private: void InitImgui(); void Render(n64::Core& core); void MainMenuBar(n64::Core& core); - void DebuggerWindow(n64::Core& core); + void DebuggerWindow(n64::Core& core) const; VkPhysicalDevice physicalDevice{}; VkDevice device{}; diff --git a/src/frontend/imgui/debugger.hpp b/src/frontend/imgui/debugger.hpp index 10017275..04247fa6 100644 --- a/src/frontend/imgui/debugger.hpp +++ b/src/frontend/imgui/debugger.hpp @@ -1,149 +1,226 @@ #pragma once -#define GDBSTUB_IMPLEMENTATION -#include #include +#include +#include struct Core; -#define GDB_CPU_PORT 1337 - -struct Breakpoint { - u32 addr; - Breakpoint* next; +const std::string gprStr[32] = { + "zero", "at", "v0", "v1", + "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", + "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", + "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", + "gp", "sp", "s8", "ra" }; -struct DebuggerState { - gdbstub_t* gdb; - bool broken; - int steps; - Breakpoint* breakpoints; - bool enabled; -}; - -inline bool CheckBreakpoint(DebuggerState& state, u32 addr) { - Breakpoint* cur = state.breakpoints; - while (cur != NULL) { - if (cur->addr == addr) { - util::print("Hit breakpoint at 0x{:08X}\n", addr); - return true; - } - cur = cur->next; +std::string special(u32 instr) { + u8 mask = (instr & 0x3F); + u8 sa = (instr >> 6) & 0x1f; + switch (mask) { // TODO: named constants for clearer code + case 0: + if (mask != 0) { + return fmt::format("sll {}, {}, {:02X}", gprStr[RD(instr)], gprStr[RT(instr)], sa); + } + return "nop"; + case 0x02: return fmt::format("srl {}, {}, {:02X}", gprStr[RD(instr)], gprStr[RT(instr)], sa); + case 0x03: return fmt::format("sra {}, {}, {:02X}", gprStr[RD(instr)], gprStr[RT(instr)], sa); + case 0x04: return fmt::format("sllv {}, {}, {}", gprStr[RD(instr)], gprStr[RT(instr)], gprStr[RS(instr)]); + case 0x06: return fmt::format("srlv {}, {}, {}", gprStr[RD(instr)], gprStr[RT(instr)], gprStr[RS(instr)]); + case 0x07: return fmt::format("srav {}, {}, {}", gprStr[RD(instr)], gprStr[RT(instr)], gprStr[RS(instr)]); + case 0x08: return fmt::format("jr {}", gprStr[RS(instr)]); + case 0x09: return fmt::format("jalr {}, {}", gprStr[RD(instr)], gprStr[RS(instr)]); + case 0x0C: return fmt::format("syscall"); + case 0x0D: return fmt::format("break"); + case 0x0F: return fmt::format("sync"); + case 0x10: return fmt::format("mfhi {}", gprStr[RD(instr)]); + case 0x11: return fmt::format("mthi {}", gprStr[RS(instr)]); + case 0x12: return fmt::format("mflo {}", gprStr[RD(instr)]); + case 0x13: return fmt::format("mtlo {}", gprStr[RS(instr)]); + case 0x14: return fmt::format("dsllv {}, {}, {}", gprStr[RD(instr)], gprStr[RT(instr)], gprStr[RS(instr)]); + case 0x16: return fmt::format("dsrlv {}, {}, {}", gprStr[RD(instr)], gprStr[RT(instr)], gprStr[RS(instr)]); + case 0x17: return fmt::format("dsrav {}, {}, {}", gprStr[RD(instr)], gprStr[RT(instr)], gprStr[RS(instr)]); + case 0x18: return fmt::format("mult {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x19: return fmt::format("multu {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x1A: return fmt::format("div {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x1B: return fmt::format("divu {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x1C: return fmt::format("dmult {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x1D: return fmt::format("dmultu {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x1E: return fmt::format("ddiv {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x1F: return fmt::format("ddivu {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x20: return fmt::format("add {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x21: return fmt::format("addu {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x22: return fmt::format("sub {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x23: return fmt::format("subu {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x24: return fmt::format("and {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x25: return fmt::format("or {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x26: return fmt::format("xor {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x27: return fmt::format("nor {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x2A: return fmt::format("slt {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x2B: return fmt::format("sltu {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x2C: return fmt::format("dadd {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x2D: return fmt::format("daddu {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x2E: return fmt::format("dsub {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x2F: return fmt::format("dsubu {}, {}, {}", gprStr[RD(instr)], gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x34: return fmt::format("teq {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + case 0x38: return fmt::format("dsll {}, {}, {:02X}", gprStr[RD(instr)], gprStr[RS(instr)], sa); + case 0x3A: return fmt::format("dsrl {}, {}, {:02X}", gprStr[RD(instr)], gprStr[RS(instr)], sa); + case 0x3B: return fmt::format("dsra {}, {}, {:02X}", gprStr[RD(instr)], gprStr[RS(instr)], sa); + case 0x3C: return fmt::format("dsll32 {}, {}, {:02X}", gprStr[RD(instr)], gprStr[RS(instr)], sa); + case 0x3E: return fmt::format("dsrl32 {}, {}, {:02X}", gprStr[RD(instr)], gprStr[RS(instr)], sa); + case 0x3F: return fmt::format("dsra32 {}, {}, {:02X}", gprStr[RD(instr)], gprStr[RS(instr)], sa); + default: + return fmt::format("INVALID ({:08X})\n", instr); } - return false; } -const char* target_xml = - "" - "" - "" - "mips:4000" - "none" - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - "" - " " - " " - " " - " " - "" - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; +std::string regimm(u32 instr) { + u8 mask = ((instr >> 16) & 0x1F); + // 000r_rccc + switch (mask) { // TODO: named constants for clearer code + case 0x00: return fmt::format("bltz {}", gprStr[RS(instr)]); + case 0x01: return fmt::format("bgez {}", gprStr[RS(instr)]); + case 0x02: return fmt::format("bltzl {}", gprStr[RS(instr)]); + case 0x03: return fmt::format("bgezl {}", gprStr[RS(instr)]); + case 0x10: return fmt::format("bltzal {}", gprStr[RS(instr)]); + case 0x11: return fmt::format("bgezal {}", gprStr[RS(instr)]); + case 0x12: return fmt::format("bltzall {}", gprStr[RS(instr)]); + case 0x13: return fmt::format("bgezall {}", gprStr[RS(instr)]); + default: return fmt::format("INVALID {:08X}", instr); + } +} -const char* memory_map = - "" - "" - "" - "" +const std::map> cpuInstr = { + {0b000000, special}, + {0b000001, regimm}, + {0b000010, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b000011, [](u32 instr) { + return fmt::format("jal {:08X}", instr & 0x3FFFFFF); + }}, {0b000100, [](u32 instr) { + return fmt::format("beq {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b000101, [](u32 instr) { + return fmt::format("bne {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b000110, [](u32 instr) { + return fmt::format("blez {}", gprStr[RS(instr)]); + }}, {0b000111, [](u32 instr) { + return fmt::format("bgtz {}", gprStr[RS(instr)]); + }}, {0b001000, [](u32 instr) { + return fmt::format("addi {}, {:04X}", gprStr[RS(instr)], instr & 0xFFFF); + }}, {0b001001, [](u32 instr) { + return fmt::format("addiu {}, {:04X}", gprStr[RS(instr)], instr & 0x3FFFFFF); + }}, {0b001010, [](u32 instr) { + return fmt::format("slti {}, {}, {:04X}", gprStr[RT(instr)], gprStr[RS(instr)], instr & 0xFFFF); + }}, {0b001011, [](u32 instr) { + return fmt::format("sltiu {}, {}, {:04X}", gprStr[RT(instr)], gprStr[RS(instr)], instr & 0xFFFF); + }}, {0b001100, [](u32 instr) { + return fmt::format("andi {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b001101, [](u32 instr) { + return fmt::format("ori {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b001110, [](u32 instr) { + return fmt::format("xori {}", gprStr[RS(instr)]); + }}, {0b001111, [](u32 instr) { + return fmt::format("lui {}", gprStr[RS(instr)]); + }}, {0b010000, /*cop0decode*/ }, + {0b010001, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b010010, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b010011, [](u32 instr) { + return fmt::format("jal {:08X}", instr & 0x3FFFFFF); + }}, {0b010100, [](u32 instr) { + return fmt::format("beq {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b010101, [](u32 instr) { + return fmt::format("bne {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b010110, [](u32 instr) { + return fmt::format("blez {}", gprStr[RS(instr)]); + }}, {0b010111, [](u32 instr) { + return fmt::format("bgtz {}", gprStr[RS(instr)]); + }}, {0b011000, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b011001, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b011010, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b011011, [](u32 instr) { + return fmt::format("jal {:08X}", instr & 0x3FFFFFF); + }}, {0b011100, [](u32 instr) { + return fmt::format("beq {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b011101, [](u32 instr) { + return fmt::format("bne {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b011110, [](u32 instr) { + return fmt::format("blez {}", gprStr[RS(instr)]); + }}, {0b011111, [](u32 instr) { + return fmt::format("bgtz {}", gprStr[RS(instr)]); + }}, {0b100000, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b100001, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b100010, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b100011, [](u32 instr) { + return fmt::format("jal {:08X}", instr & 0x3FFFFFF); + }}, {0b100100, [](u32 instr) { + return fmt::format("beq {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b100101, [](u32 instr) { + return fmt::format("bne {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b100110, [](u32 instr) { + return fmt::format("blez {}", gprStr[RS(instr)]); + }}, {0b100111, [](u32 instr) { + return fmt::format("bgtz {}", gprStr[RS(instr)]); + }}, {0b101000, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b101001, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b101010, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b101011, [](u32 instr) { + return fmt::format("jal {:08X}", instr & 0x3FFFFFF); + }}, {0b101100, [](u32 instr) { + return fmt::format("beq {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b101101, [](u32 instr) { + return fmt::format("bne {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b101110, [](u32 instr) { + return fmt::format("blez {}", gprStr[RS(instr)]); + }}, {0b101111, [](u32 instr) { + return fmt::format("bgtz {}", gprStr[RS(instr)]); + }}, {0b110000, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b110001, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b110010, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b110011, [](u32 instr) { + return fmt::format("jal {:08X}", instr & 0x3FFFFFF); + }}, {0b110100, [](u32 instr) { + return fmt::format("beq {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b110101, [](u32 instr) { + return fmt::format("bne {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b110110, [](u32 instr) { + return fmt::format("blez {}", gprStr[RS(instr)]); + }}, {0b110111, [](u32 instr) { + return fmt::format("bgtz {}", gprStr[RS(instr)]); + }}, {0b111000, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b111001, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b111010, [](u32 instr) { + return fmt::format("j {:08X}", instr & 0x3FFFFFF); + }}, {0b111011, [](u32 instr) { + return fmt::format("jal {:08X}", instr & 0x3FFFFFF); + }}, {0b111100, [](u32 instr) { + return fmt::format("beq {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b111101, [](u32 instr) { + return fmt::format("bne {}, {}", gprStr[RS(instr)], gprStr[RT(instr)]); + }}, {0b111110, [](u32 instr) { + return fmt::format("blez {}", gprStr[RS(instr)]); + }}, {0b111111, [](u32 instr) { + return fmt::format("bgtz {}", gprStr[RS(instr)]); + }} +}; - "" // TODO finish - "" // RDRAM - "" // RSP DMEM - "" // RSP IMEM - "" // PIF ROM +struct Debugger { - "" // TODO finish - "" // RDRAM - "" // RSP DMEM - "" // RSP IMEM - "" // PIF ROM - ""; - - -void DebugStart(void* user_data); -void DebugStop(void* user_data); -void DebugStep(void* user_data); -void DebugSetBreakpoint(void* user_data, u32 address); -void DebugClearBreakpoint(void* user_data, u32 address); -ssize_t DebugGetMemory(void* user_data, char* buffer, size_t length, u32 address, size_t bytes); -ssize_t DebugGetRegisterValue(void* user_data, char * buffer, size_t buffer_length, int reg); -ssize_t DebugGetGeneralRegisters(void* user_data, char * buffer, size_t buffer_length); \ No newline at end of file +}; \ No newline at end of file diff --git a/src/frontend/imgui/widgets.cpp b/src/frontend/imgui/widgets.cpp index 05493915..f8cbedb8 100644 --- a/src/frontend/imgui/widgets.cpp +++ b/src/frontend/imgui/widgets.cpp @@ -38,7 +38,7 @@ void Window::MainMenuBar(n64::Core& core) { ImGui::PopFont(); } -void Window::DebuggerWindow(n64::Core& core) { +void Window::DebuggerWindow(n64::Core& core) const { ImGui::PushFont(uiFont); ImGui::Begin("Debugger"); if(ImGui::Button("Step")) { diff --git a/src/n64/Core.cpp b/src/n64/Core.cpp index dcff8e85..af085757 100644 --- a/src/n64/Core.cpp +++ b/src/n64/Core.cpp @@ -56,16 +56,6 @@ void Core::Run(Window& window) { } for(;cycles <= mmio.vi.cyclesPerHalfline; cycles++) { - #ifndef NDEBUG - if (debuggerState.enabled && CheckBreakpoint(debuggerState, cpu.regs.pc)) { - DebuggerBreakpointHit(); - } - - while (debuggerState.broken) { - SDL_Delay(1000); - DebuggerTick(); - } - #endif cpu.Step(mem); mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); @@ -170,49 +160,4 @@ void Core::UpdateController(const u8* state) { } } } - -void Core::DebuggerInit() { - gdbstub_config_t config; - memset(&config, 0, sizeof(gdbstub_config_t)); - config.port = GDB_CPU_PORT; - config.user_data = this; - config.start = (gdbstub_start_t) DebugStart; - config.stop = (gdbstub_stop_t) DebugStop; - config.step = (gdbstub_step_t) DebugStep; - config.set_breakpoint = (gdbstub_set_breakpoint_t) DebugSetBreakpoint; - config.clear_breakpoint = (gdbstub_clear_breakpoint_t) DebugClearBreakpoint; - config.get_memory = (gdbstub_get_memory_t) DebugGetMemory; - config.get_register_value = (gdbstub_get_register_value_t) DebugGetRegisterValue; - config.get_general_registers = (gdbstub_get_general_registers_t) DebugGetGeneralRegisters; - - config.target_config = target_xml; - config.target_config_length = strlen(target_xml); - - printf("Sizeof target: %zu\n", config.target_config_length); - - config.memory_map = memory_map; - config.memory_map_length = strlen(memory_map); - - printf("Sizeof memory map: %zu\n", config.memory_map_length); - - debuggerState.gdb = gdbstub_init(config); - if (!debuggerState.gdb) { - util::panic("Failed to initialize GDB stub!"); - } -} - -void Core::DebuggerTick() const { - gdbstub_tick(debuggerState.gdb); -} - -void Core::DebuggerBreakpointHit() { - debuggerState.broken = true; - gdbstub_breakpoint_hit(debuggerState.gdb); -} - -void Core::DebuggerCleanup() const { - if (debuggerState.enabled) { - gdbstub_term(debuggerState.gdb); - } -} } diff --git a/src/n64/Core.hpp b/src/n64/Core.hpp index 6a5fe294..5155f721 100644 --- a/src/n64/Core.hpp +++ b/src/n64/Core.hpp @@ -19,11 +19,6 @@ struct Core { void TogglePause() { pause = !pause; } VI& GetVI() { return mem.mmio.vi; } - void DebuggerInit(); - void DebuggerTick() const; - void DebuggerBreakpointHit(); - void DebuggerCleanup() const; - bool pause = true; bool romLoaded = false; SDL_GameController* gamepad;