From 601c21d802c596ee11c4f74da5f1631fe1655a75 Mon Sep 17 00:00:00 2001 From: folkert van heusden Date: Tue, 16 Apr 2024 10:52:12 +0200 Subject: [PATCH] breakpoints --- CMakeLists.txt | 5 ++ breakpoint.cpp | 10 ++++ breakpoint.h | 21 ++++++++ breakpoint_and.cpp | 48 ++++++++++++++++++ breakpoint_and.h | 20 ++++++++ breakpoint_or.cpp | 44 +++++++++++++++++ breakpoint_or.h | 20 ++++++++ breakpoint_parser.cpp | 107 ++++++++++++++++++++++++++++++++++++++++ breakpoint_parser.h | 8 +++ breakpoint_register.cpp | 77 +++++++++++++++++++++++++++++ breakpoint_register.h | 25 ++++++++++ cpu.cpp | 34 ++++++++++--- cpu.h | 11 +++-- debugger.cpp | 57 +++++++++++++++------ main.cpp | 19 +++++++ 15 files changed, 480 insertions(+), 26 deletions(-) create mode 100644 breakpoint.cpp create mode 100644 breakpoint.h create mode 100644 breakpoint_and.cpp create mode 100644 breakpoint_and.h create mode 100644 breakpoint_or.cpp create mode 100644 breakpoint_or.h create mode 100644 breakpoint_parser.cpp create mode 100644 breakpoint_parser.h create mode 100644 breakpoint_register.cpp create mode 100644 breakpoint_register.h diff --git a/CMakeLists.txt b/CMakeLists.txt index aa10a33..81f8fcb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,11 @@ if (NOT WIN32) add_executable( kek + breakpoint.cpp + breakpoint_and.cpp + breakpoint_or.cpp + breakpoint_parser.cpp + breakpoint_register.cpp bus.cpp console.cpp console_ncurses.cpp diff --git a/breakpoint.cpp b/breakpoint.cpp new file mode 100644 index 0000000..918a9b7 --- /dev/null +++ b/breakpoint.cpp @@ -0,0 +1,10 @@ +#include "breakpoint.h" + + +breakpoint::breakpoint(bus *const b) : b(b) +{ +} + +breakpoint::~breakpoint() +{ +} diff --git a/breakpoint.h b/breakpoint.h new file mode 100644 index 0000000..97028fc --- /dev/null +++ b/breakpoint.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "bus.h" + + +class breakpoint +{ +protected: + bus *const b { nullptr }; + +public: + breakpoint(bus *const b); + virtual ~breakpoint(); + + virtual std::optional is_triggered() const = 0; + + virtual std::string emit() const = 0; +}; diff --git a/breakpoint_and.cpp b/breakpoint_and.cpp new file mode 100644 index 0000000..5d772c1 --- /dev/null +++ b/breakpoint_and.cpp @@ -0,0 +1,48 @@ +#include "breakpoint_and.h" +#include "utils.h" + + +breakpoint_and::breakpoint_and(bus *const b, const std::vector & triggers): + breakpoint(b), + triggers(triggers) +{ +} + +breakpoint_and::~breakpoint_and() +{ + for(auto & bp: triggers) + delete bp; +} + +std::optional breakpoint_and::is_triggered() const +{ + std::string out; + + for(auto & t: triggers) { + auto rc = t->is_triggered(); + if (rc.has_value() == false) + return { }; + + out += (out.empty() ? "" : ", ") + rc.value(); + } + + return out; +} + +std::string breakpoint_and::emit() const +{ + std::string out; + + for(auto & t: triggers) { + if (out.empty()) + out = "("; + else + out += " and "; + + out += t->emit(); + } + + out += ")"; + + return out; +} diff --git a/breakpoint_and.h b/breakpoint_and.h new file mode 100644 index 0000000..89896b7 --- /dev/null +++ b/breakpoint_and.h @@ -0,0 +1,20 @@ +#include +#include +#include + +#include "breakpoint.h" + + +class breakpoint_and : public breakpoint +{ +private: + const std::vector triggers; + +public: + breakpoint_and(bus *const b, const std::vector & triggers); + virtual ~breakpoint_and(); + + virtual std::optional is_triggered() const override; + + virtual std::string emit() const override; +}; diff --git a/breakpoint_or.cpp b/breakpoint_or.cpp new file mode 100644 index 0000000..7615f7e --- /dev/null +++ b/breakpoint_or.cpp @@ -0,0 +1,44 @@ +#include "breakpoint_or.h" +#include "utils.h" + + +breakpoint_or::breakpoint_or(bus *const b, const std::vector & triggers): + breakpoint(b), + triggers(triggers) +{ +} + +breakpoint_or::~breakpoint_or() +{ + for(auto & bp: triggers) + delete bp; +} + +std::optional breakpoint_or::is_triggered() const +{ + for(auto & t: triggers) { + auto rc = t->is_triggered(); + if (rc.has_value()) + return rc; + } + + return { }; +} + +std::string breakpoint_or::emit() const +{ + std::string out; + + for(auto & t: triggers) { + if (out.empty()) + out = "("; + else + out += " or "; + + out += t->emit(); + } + + out += ")"; + + return out; +} diff --git a/breakpoint_or.h b/breakpoint_or.h new file mode 100644 index 0000000..f1fefe3 --- /dev/null +++ b/breakpoint_or.h @@ -0,0 +1,20 @@ +#include +#include +#include + +#include "breakpoint.h" + + +class breakpoint_or : public breakpoint +{ +private: + const std::vector triggers; + +public: + breakpoint_or(bus *const b, const std::vector & triggers); + virtual ~breakpoint_or(); + + virtual std::optional is_triggered() const override; + + virtual std::string emit() const override; +}; diff --git a/breakpoint_parser.cpp b/breakpoint_parser.cpp new file mode 100644 index 0000000..845cab2 --- /dev/null +++ b/breakpoint_parser.cpp @@ -0,0 +1,107 @@ +#include +#include + +#include "breakpoint.h" +#include "breakpoint_and.h" +#include "breakpoint_or.h" +#include "breakpoint_register.h" +#include "bus.h" +#include "utils.h" + + +static void delete_parsed(const std::vector & parsed) +{ + for(auto & p: parsed) + delete p; +} + +std::pair > parse_breakpoint(bus *const b, const std::string & in) +{ + auto parts = split(in, " "); + + std::vector parsed; + + enum { combine_not_set, combine_single, combine_and, combine_or } combine { combine_not_set }; + + for(size_t i=0; i > current; + + if (parts[i][0] == '(') { + int depth = 0; + + std::optional end_index; + for(size_t j=i; j i ? " " : "") + parts.at(j); + + auto rc = parse_breakpoint(b, temp.substr(1, temp.size() - 2)); + if (rc.first == nullptr) { + delete_parsed(parsed); + return rc; + } + + i = end_index.value(); + + parsed.push_back(rc.first); + } + else { + if (parts[i] == "and" || parts[i] == "or") { + if ((combine == combine_and && parts[i] == "or") || (combine == combine_or && parts[i] == "and")) { + delete_parsed(parsed); + return { nullptr, "combining and/or in one definition" }; + } + combine = parts[i] == "and" ? combine_and : combine_or; + continue; + } + else if (combine == combine_single) { + delete_parsed(parsed); + return { nullptr, "and/or missing" }; + } + else { + if (combine == combine_not_set) + combine = combine_single; + + auto rc_reg = breakpoint_register::parse(b, parts[i]); + if (rc_reg.first == nullptr) { + delete_parsed(parsed); + return { nullptr, "not understood" }; + } + + parsed.push_back(rc_reg.first); + } + } + } + + if (combine == combine_and) + return { new breakpoint_and(b, parsed), { } }; + + if (combine == combine_or) + return { new breakpoint_or(b, parsed), { } }; + + if (parsed.size() != 1) { + delete_parsed(parsed); + return { nullptr, "wrong count of items" }; + } + + return { parsed.at(0), { } }; +} diff --git a/breakpoint_parser.h b/breakpoint_parser.h new file mode 100644 index 0000000..5220432 --- /dev/null +++ b/breakpoint_parser.h @@ -0,0 +1,8 @@ +#include +#include + +#include "breakpoint.h" +#include "bus.h" + + +std::pair > parse_breakpoint(bus *const b, const std::string & in); diff --git a/breakpoint_register.cpp b/breakpoint_register.cpp new file mode 100644 index 0000000..ac2a59b --- /dev/null +++ b/breakpoint_register.cpp @@ -0,0 +1,77 @@ +#include + +#include "breakpoint_register.h" +#include "cpu.h" +#include "utils.h" + + +breakpoint_register::breakpoint_register(bus *const b, const int register_nr, const std::set & values) : + breakpoint(b), + c(b->getCpu()), + register_nr(register_nr), values(values) +{ +} + +breakpoint_register::~breakpoint_register() +{ +} + +std::optional breakpoint_register::is_triggered() const +{ + uint16_t v = c->getRegister(register_nr); // TODO run-mode + + auto it = values.find(v); + if (it == values.end()) + return { }; + + return format("R%d=%06o", register_nr, v); +} + +std::pair > breakpoint_register::parse(bus *const b, const std::string & in) +{ + auto parts = split(in, "="); + if (parts.size() != 2) + return { nullptr, "register: key or value missing" }; + + auto values_in = parts.at(1); + auto v_parts = split(values_in, ","); + std::set values; + for(auto & v: v_parts) + values.insert(std::stoi(v, nullptr, 8)); + + auto key = parts.at(0); + if (key.size() < 2) + return { nullptr, "register: register id invalid" }; + + if (toupper(key[0]) == 'R') { + int nr = key[1] - '0'; + if (nr < 0 || nr > 7) + return { nullptr, "register: register id invalid" }; + + return { new breakpoint_register(b, nr, values), { } }; + } + else if (key == "SP" || key == "sp") { + return { new breakpoint_register(b, 6, values), { } }; + } + else if (key == "PC" || key == "pc") { + return { new breakpoint_register(b, 7, values), { } }; + } + + return { nullptr, "register: invalid specification" }; +} + +std::string breakpoint_register::emit() const +{ + std::string out; + + for(auto & v: values) { + if (out.empty()) + out = format("R%d", register_nr) + "="; + else + out += ","; + + out += format("%06o", v); + } + + return out; +} diff --git a/breakpoint_register.h b/breakpoint_register.h new file mode 100644 index 0000000..784cfbb --- /dev/null +++ b/breakpoint_register.h @@ -0,0 +1,25 @@ +#include +#include +#include + +#include "breakpoint.h" +#include "bus.h" + + +class breakpoint_register : public breakpoint +{ +private: + cpu *const c { nullptr }; + int register_nr { -1 }; + std::set values; + +public: + breakpoint_register(bus *const b, const int register_nr, const std::set & values); + virtual ~breakpoint_register(); + + virtual std::optional is_triggered() const override; + + static std::pair > parse(bus *const b, const std::string & in); + + virtual std::string emit() const override; +}; diff --git a/cpu.cpp b/cpu.cpp index d32b6ea..86d8caa 100644 --- a/cpu.cpp +++ b/cpu.cpp @@ -44,22 +44,42 @@ void cpu::emulation_start() wait_time = 0; } -bool cpu::check_breakpoint() +std::optional cpu::check_breakpoint() { - return breakpoints.find(getPC()) != breakpoints.end(); + for(auto & bp: breakpoints) { + auto rc = bp.second->is_triggered(); + if (rc.has_value()) + return rc; + } + + return { }; } -void cpu::set_breakpoint(const uint16_t addr) +int cpu::set_breakpoint(breakpoint *const bp) { - breakpoints.insert(addr); + int id = 0; + + do { + id = rand(); + } while(breakpoints.find(id) != breakpoints.end()); + + breakpoints.insert({ id, bp }); + + return id; } -void cpu::remove_breakpoint(const uint16_t addr) +bool cpu::remove_breakpoint(const int bp_id) { - breakpoints.erase(addr); + auto it = breakpoints.find(bp_id); + if (it == breakpoints.end()) + return false; + + delete it->second; + + return breakpoints.erase(bp_id) == 1; } -std::set cpu::list_breakpoints() +std::map cpu::list_breakpoints() { return breakpoints; } diff --git a/cpu.h b/cpu.h index bca20dc..f7c4935 100644 --- a/cpu.h +++ b/cpu.h @@ -12,6 +12,7 @@ #include #include +#include "breakpoint.h" #include "bus.h" @@ -62,7 +63,7 @@ private: #endif std::atomic_bool any_queued_interrupts { false }; - std::set breakpoints; + std::map breakpoints; bus *const b { nullptr }; @@ -98,10 +99,10 @@ public: explicit cpu(bus *const b, std::atomic_uint32_t *const event); ~cpu(); - bool check_breakpoint(); - void set_breakpoint(const uint16_t addr); - void remove_breakpoint(const uint16_t addr); - std::set list_breakpoints(); + std::optional check_breakpoint(); + int set_breakpoint(breakpoint *const bp); + bool remove_breakpoint(const int bp_id); + std::map list_breakpoints(); void disassemble(void) const; std::map > disassemble(const uint16_t addr) const; diff --git a/debugger.cpp b/debugger.cpp index 617c277..3c137bd 100644 --- a/debugger.cpp +++ b/debugger.cpp @@ -13,6 +13,7 @@ #include #endif +#include "breakpoint_parser.h" #include "bus.h" #include "console.h" #include "cpu.h" @@ -531,6 +532,22 @@ int disassemble(cpu *const c, console *const cnsl, const uint16_t pc, const bool DOLOG(debug, false, "SP: %s", sp.c_str()); +#if 0 + if (c->getPSW_runmode() == 3) { + /* + FILE *fh = fopen("/home/folkert/temp/ramdisk/log-kek.dat", "a+"); + fprintf(fh, "%06o", pc); + for(auto & v: data["instruction-values"]) + fprintf(fh, " %s", v.c_str()); + fprintf(fh, "\n"); + fclose(fh); + */ + FILE *fh = fopen("/home/folkert/temp/ramdisk/da-kek.txt", "a+"); + fprintf(fh, "R0 %s R1 %s R2 %s R3 %s R4 %s R5 %s R6 %s R7 %06o %s\n", registers[0].c_str(), registers[1].c_str(), registers[2].c_str(), registers[3].c_str(), registers[4].c_str(), registers[5].c_str(), registers[6].c_str(), pc, instruction.c_str()); + fclose(fh); + } +#endif + return data["instruction-values"].size() * 2; } @@ -737,18 +754,29 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto *stop_event = EVENT_NONE; } - else if ((parts[0] == "sbp" || parts[0] == "cbp") && parts.size() == 2){ - uint16_t pc = std::stoi(parts[1].c_str(), nullptr, 8); - + else if ((parts[0] == "sbp" || parts[0] == "cbp") && parts.size() >= 2){ if (parts[0] == "sbp") { - c->set_breakpoint(pc); + std::size_t space = cmd.find(" "); - cnsl->put_string_lf(format("Set breakpoint at %06o", pc)); + std::pair > rc = parse_breakpoint(b, cmd.substr(space + 1)); + + if (rc.first == nullptr) { + if (rc.second.has_value()) + cnsl->put_string_lf(rc.second.value()); + else + cnsl->put_string_lf("not set"); + } + else { + int id = c->set_breakpoint(rc.first); + + cnsl->put_string_lf(format("Breakpoint has id: %d", id)); + } } else { - c->remove_breakpoint(pc); - - cnsl->put_string_lf(format("Clear breakpoint at %06o", pc)); + if (c->remove_breakpoint(std::stoi(parts[1]))) + cnsl->put_string_lf("Breakpoint cleared"); + else + cnsl->put_string_lf("Breakpoint not found"); } continue; @@ -758,11 +786,11 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto cnsl->put_string_lf("Breakpoints:"); - for(auto a : bps) { - cnsl->put_string(format(" %06o> ", a)); + for(auto a : bps) + cnsl->put_string_lf(format("%d: %s", a.first, a.second->emit().c_str())); - disassemble(c, cnsl, a, true); - } + if (bps.empty()) + cnsl->put_string_lf("(none)"); continue; } @@ -1046,8 +1074,9 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto if ((tracing || single_step) && (t_rl.has_value() == false || t_rl.value() == c->getPSW_runmode())) disassemble(c, single_step ? cnsl : nullptr, c->getPC(), false); - if (c->check_breakpoint() && !single_step) { - cnsl->put_string_lf("Breakpoint"); + auto bp_result = c->check_breakpoint(); + if (bp_result.has_value() && !single_step) { + cnsl->put_string_lf("Breakpoint: " + bp_result.value()); break; } diff --git a/main.cpp b/main.cpp index 93ce0a4..5d64512 100644 --- a/main.cpp +++ b/main.cpp @@ -314,8 +314,27 @@ void help() printf("-M log metrics\n"); } +#include "breakpoint_parser.h" int main(int argc, char *argv[]) { +#if 0 + { + bus *b = new bus(); + cpu *c = new cpu(b, &event); + b->add_cpu(c); + + std::pair > rc = parse_breakpoint(b, "(pc=0123 and (r0=01456 or r2=1))"); + printf("%p\n", rc.first); + + if (rc.second.has_value()) + printf("%s\n", rc.second.value().c_str()); + delete rc.first; + delete b; + } + + return 0; +#endif + //setlocale(LC_ALL, ""); std::vector rk05_files;