Implement breakpoints

This commit is contained in:
Konstantin Nazarov 2024-12-09 21:21:47 +00:00
parent d0ff238cb0
commit 184c379e96
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
5 changed files with 61 additions and 11 deletions

View file

@ -2,6 +2,12 @@
#include <netinet/tcp.h>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>
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

View file

@ -6,12 +6,7 @@
#include <atomic>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <thread>
#include <vector>
#include <unordered_map>
#include "vm.hpp"
@ -49,4 +44,6 @@ class GDBStub {
std::string read_memory(size_t addr, size_t length);
VM &vm;
std::unordered_map<uint32_t, uint32_t> breakpoints;
};

View file

@ -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;

View file

@ -52,6 +52,12 @@ std::vector<uint8_t> 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;
@ -304,7 +310,8 @@ void VM::step() {
break;
}
case 0x73: { // EBREAK
running = false; // Flag to stop the emulator
pc -= 4;
throw EbreakException();
break;
}
default:
@ -313,7 +320,7 @@ void VM::step() {
}
void VM::eval() {
while (running) {
while (true) {
step();
}
}

View file

@ -5,6 +5,8 @@
#include <string>
#include <vector>
class EbreakException : std::exception {};
const int NUM_REGISTERS = 32; // Standard RISC-V has 32 registers
std::vector<uint8_t> load_program(const std::string& filename,
@ -18,6 +20,9 @@ class VM {
void eval();
std::vector<uint8_t> 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;
};