diff --git a/src/debug.cpp b/src/debug.cpp index 2d8c73d..a531b12 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -2,6 +2,12 @@ #include +#include +#include +#include +#include +#include + GDBStub::GDBStub(VM &vm, int port) : server_fd(-1), client_fd(-1), running(false), vm(vm) { server_fd = socket(AF_INET, SOCK_STREAM, 0); @@ -174,6 +180,12 @@ void GDBStub::handle_packet(const std::string &packet) { break; case 'c': + try { + while (true) { + vm.step(); + } + } catch (const EbreakException &ex) { + } send_packet("S05"); // TODO: continue execution break; @@ -189,6 +201,36 @@ void GDBStub::handle_packet(const std::string &packet) { send_packet(""); // Empty response } break; + case 'Z': { + // Insert breakpoint + if (packet[1] == '0') { + uint32_t addr = + std::stoul(packet.substr(3, packet.find(',')), nullptr, 16); + + if (breakpoints.count(addr) == 0) { + uint32_t original_instr = vm.read_memory_word(addr); + breakpoints[addr] = original_instr; + vm.write_memory_word(addr, 0x00100073); // 0x00100073 is EBREAK + } + } + send_packet("OK"); + break; + } + case 'z': { + // Delete breakpoint + if (packet[1] == '0') { + uint32_t addr = + std::stoul(packet.substr(3, packet.find(',')), nullptr, 16); + + if (breakpoints.count(addr) > 0) { + // Restore the original instruction + vm.write_memory_word(addr, breakpoints[addr]); + breakpoints.erase(addr); + } + } + send_packet("OK"); + break; + } default: send_packet(""); // Unsupported packet diff --git a/src/debug.hpp b/src/debug.hpp index 03aad05..8fbcc33 100644 --- a/src/debug.hpp +++ b/src/debug.hpp @@ -6,12 +6,7 @@ #include #include -#include -#include -#include -#include -#include -#include +#include #include "vm.hpp" @@ -49,4 +44,6 @@ class GDBStub { std::string read_memory(size_t addr, size_t length); VM &vm; + + std::unordered_map breakpoints; }; diff --git a/src/rve.cpp b/src/rve.cpp index 7147fa8..486671e 100644 --- a/src/rve.cpp +++ b/src/rve.cpp @@ -37,6 +37,7 @@ int main(int argc, char *argv[]) { if (!debug) { try { vm.eval(); + } catch (const EbreakException &e) { } catch (const std::exception &e) { std::cerr << "Emulator error: " << e.what() << std::endl; return 1; diff --git a/src/vm.cpp b/src/vm.cpp index 62e08eb..b42a49d 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -52,6 +52,12 @@ std::vector VM::read_memory(size_t start, size_t size) { memory_.begin() + start + size); } +uint32_t VM::read_memory_word(size_t pos) { return *(uint32_t*)&memory_[pos]; } + +void VM::write_memory_word(size_t pos, uint32_t value) { + *(uint32_t*)&memory_[pos] = value; +} + uint32_t VM::read_register(size_t regnum) { if (regnum == 32) return pc; @@ -303,8 +309,9 @@ void VM::step() { (imm << 12); // Add the immediate (shifted left) to the current PC break; } - case 0x73: { // EBREAK - running = false; // Flag to stop the emulator + case 0x73: { // EBREAK + pc -= 4; + throw EbreakException(); break; } default: @@ -313,7 +320,7 @@ void VM::step() { } void VM::eval() { - while (running) { + while (true) { step(); } } diff --git a/src/vm.hpp b/src/vm.hpp index 092ffe0..11ee55f 100644 --- a/src/vm.hpp +++ b/src/vm.hpp @@ -5,6 +5,8 @@ #include #include +class EbreakException : std::exception {}; + const int NUM_REGISTERS = 32; // Standard RISC-V has 32 registers std::vector load_program(const std::string& filename, @@ -18,6 +20,9 @@ class VM { void eval(); std::vector read_memory(size_t start, size_t size); + uint32_t read_memory_word(size_t pos); + void write_memory_word(size_t pos, uint32_t value); + uint32_t read_register(size_t regnum); private: @@ -25,6 +30,4 @@ class VM { uint32_t registers[NUM_REGISTERS] = {0}; uint32_t pc = 0; - - bool running = true; };