breakpoints

This commit is contained in:
folkert van heusden 2024-04-16 10:52:12 +02:00
parent 541d0c7829
commit 601c21d802
Signed by untrusted user who does not match committer: folkert
GPG key ID: 6B6455EDFEED3BD1
15 changed files with 480 additions and 26 deletions

View file

@ -16,6 +16,11 @@ if (NOT WIN32)
add_executable( add_executable(
kek kek
breakpoint.cpp
breakpoint_and.cpp
breakpoint_or.cpp
breakpoint_parser.cpp
breakpoint_register.cpp
bus.cpp bus.cpp
console.cpp console.cpp
console_ncurses.cpp console_ncurses.cpp

10
breakpoint.cpp Normal file
View file

@ -0,0 +1,10 @@
#include "breakpoint.h"
breakpoint::breakpoint(bus *const b) : b(b)
{
}
breakpoint::~breakpoint()
{
}

21
breakpoint.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include <optional>
#include <string>
#include "bus.h"
class breakpoint
{
protected:
bus *const b { nullptr };
public:
breakpoint(bus *const b);
virtual ~breakpoint();
virtual std::optional<std::string> is_triggered() const = 0;
virtual std::string emit() const = 0;
};

48
breakpoint_and.cpp Normal file
View file

@ -0,0 +1,48 @@
#include "breakpoint_and.h"
#include "utils.h"
breakpoint_and::breakpoint_and(bus *const b, const std::vector<breakpoint *> & triggers):
breakpoint(b),
triggers(triggers)
{
}
breakpoint_and::~breakpoint_and()
{
for(auto & bp: triggers)
delete bp;
}
std::optional<std::string> 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;
}

20
breakpoint_and.h Normal file
View file

@ -0,0 +1,20 @@
#include <optional>
#include <string>
#include <vector>
#include "breakpoint.h"
class breakpoint_and : public breakpoint
{
private:
const std::vector<breakpoint *> triggers;
public:
breakpoint_and(bus *const b, const std::vector<breakpoint *> & triggers);
virtual ~breakpoint_and();
virtual std::optional<std::string> is_triggered() const override;
virtual std::string emit() const override;
};

44
breakpoint_or.cpp Normal file
View file

@ -0,0 +1,44 @@
#include "breakpoint_or.h"
#include "utils.h"
breakpoint_or::breakpoint_or(bus *const b, const std::vector<breakpoint *> & triggers):
breakpoint(b),
triggers(triggers)
{
}
breakpoint_or::~breakpoint_or()
{
for(auto & bp: triggers)
delete bp;
}
std::optional<std::string> 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;
}

20
breakpoint_or.h Normal file
View file

@ -0,0 +1,20 @@
#include <optional>
#include <string>
#include <vector>
#include "breakpoint.h"
class breakpoint_or : public breakpoint
{
private:
const std::vector<breakpoint *> triggers;
public:
breakpoint_or(bus *const b, const std::vector<breakpoint *> & triggers);
virtual ~breakpoint_or();
virtual std::optional<std::string> is_triggered() const override;
virtual std::string emit() const override;
};

107
breakpoint_parser.cpp Normal file
View file

@ -0,0 +1,107 @@
#include <optional>
#include <string>
#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<breakpoint *> & parsed)
{
for(auto & p: parsed)
delete p;
}
std::pair<breakpoint *, std::optional<std::string> > parse_breakpoint(bus *const b, const std::string & in)
{
auto parts = split(in, " ");
std::vector<breakpoint *> parsed;
enum { combine_not_set, combine_single, combine_and, combine_or } combine { combine_not_set };
for(size_t i=0; i<parts.size(); i++) {
std::pair<breakpoint *, std::optional<std::string> > current;
if (parts[i][0] == '(') {
int depth = 0;
std::optional<size_t> end_index;
for(size_t j=i; j<parts.size(); j++) {
if (parts[j][0] == '(')
depth++;
for(size_t count=0; count<parts[j].size(); count++) {
if (parts[j][count] == ')')
depth--;
}
if (depth == 0) {
end_index = j;
break;
}
}
if (depth != 0) {
delete_parsed(parsed);
return { nullptr, "( and ) unbalanced: " + in };
}
std::string temp;
for(size_t j=i; j<=end_index.value(); j++)
temp += (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), { } };
}

8
breakpoint_parser.h Normal file
View file

@ -0,0 +1,8 @@
#include <optional>
#include <string>
#include "breakpoint.h"
#include "bus.h"
std::pair<breakpoint *, std::optional<std::string> > parse_breakpoint(bus *const b, const std::string & in);

77
breakpoint_register.cpp Normal file
View file

@ -0,0 +1,77 @@
#include <ctype.h>
#include "breakpoint_register.h"
#include "cpu.h"
#include "utils.h"
breakpoint_register::breakpoint_register(bus *const b, const int register_nr, const std::set<uint16_t> & values) :
breakpoint(b),
c(b->getCpu()),
register_nr(register_nr), values(values)
{
}
breakpoint_register::~breakpoint_register()
{
}
std::optional<std::string> 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 *, std::optional<std::string> > 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<uint16_t> 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;
}

25
breakpoint_register.h Normal file
View file

@ -0,0 +1,25 @@
#include <optional>
#include <set>
#include <string>
#include "breakpoint.h"
#include "bus.h"
class breakpoint_register : public breakpoint
{
private:
cpu *const c { nullptr };
int register_nr { -1 };
std::set<uint16_t> values;
public:
breakpoint_register(bus *const b, const int register_nr, const std::set<uint16_t> & values);
virtual ~breakpoint_register();
virtual std::optional<std::string> is_triggered() const override;
static std::pair<breakpoint_register *, std::optional<std::string> > parse(bus *const b, const std::string & in);
virtual std::string emit() const override;
};

34
cpu.cpp
View file

@ -44,22 +44,42 @@ void cpu::emulation_start()
wait_time = 0; wait_time = 0;
} }
bool cpu::check_breakpoint() std::optional<std::string> 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<uint16_t> cpu::list_breakpoints() std::map<int, breakpoint *> cpu::list_breakpoints()
{ {
return breakpoints; return breakpoints;
} }

11
cpu.h
View file

@ -12,6 +12,7 @@
#include <stdint.h> #include <stdint.h>
#include <vector> #include <vector>
#include "breakpoint.h"
#include "bus.h" #include "bus.h"
@ -62,7 +63,7 @@ private:
#endif #endif
std::atomic_bool any_queued_interrupts { false }; std::atomic_bool any_queued_interrupts { false };
std::set<uint16_t> breakpoints; std::map<int, breakpoint *> breakpoints;
bus *const b { nullptr }; bus *const b { nullptr };
@ -98,10 +99,10 @@ public:
explicit cpu(bus *const b, std::atomic_uint32_t *const event); explicit cpu(bus *const b, std::atomic_uint32_t *const event);
~cpu(); ~cpu();
bool check_breakpoint(); std::optional<std::string> check_breakpoint();
void set_breakpoint(const uint16_t addr); int set_breakpoint(breakpoint *const bp);
void remove_breakpoint(const uint16_t addr); bool remove_breakpoint(const int bp_id);
std::set<uint16_t> list_breakpoints(); std::map<int, breakpoint *> list_breakpoints();
void disassemble(void) const; void disassemble(void) const;
std::map<std::string, std::vector<std::string> > disassemble(const uint16_t addr) const; std::map<std::string, std::vector<std::string> > disassemble(const uint16_t addr) const;

View file

@ -13,6 +13,7 @@
#include <LittleFS.h> #include <LittleFS.h>
#endif #endif
#include "breakpoint_parser.h"
#include "bus.h" #include "bus.h"
#include "console.h" #include "console.h"
#include "cpu.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()); 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; 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; *stop_event = EVENT_NONE;
} }
else if ((parts[0] == "sbp" || parts[0] == "cbp") && parts.size() == 2){ else if ((parts[0] == "sbp" || parts[0] == "cbp") && parts.size() >= 2){
uint16_t pc = std::stoi(parts[1].c_str(), nullptr, 8);
if (parts[0] == "sbp") { 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<breakpoint *, std::optional<std::string> > 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 { else {
c->remove_breakpoint(pc); int id = c->set_breakpoint(rc.first);
cnsl->put_string_lf(format("Clear breakpoint at %06o", pc)); cnsl->put_string_lf(format("Breakpoint has id: %d", id));
}
}
else {
if (c->remove_breakpoint(std::stoi(parts[1])))
cnsl->put_string_lf("Breakpoint cleared");
else
cnsl->put_string_lf("Breakpoint not found");
} }
continue; continue;
@ -758,11 +786,11 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("Breakpoints:"); cnsl->put_string_lf("Breakpoints:");
for(auto a : bps) { for(auto a : bps)
cnsl->put_string(format(" %06o> ", a)); 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; 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())) if ((tracing || single_step) && (t_rl.has_value() == false || t_rl.value() == c->getPSW_runmode()))
disassemble(c, single_step ? cnsl : nullptr, c->getPC(), false); disassemble(c, single_step ? cnsl : nullptr, c->getPC(), false);
if (c->check_breakpoint() && !single_step) { auto bp_result = c->check_breakpoint();
cnsl->put_string_lf("Breakpoint"); if (bp_result.has_value() && !single_step) {
cnsl->put_string_lf("Breakpoint: " + bp_result.value());
break; break;
} }

View file

@ -314,8 +314,27 @@ void help()
printf("-M log metrics\n"); printf("-M log metrics\n");
} }
#include "breakpoint_parser.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
#if 0
{
bus *b = new bus();
cpu *c = new cpu(b, &event);
b->add_cpu(c);
std::pair<breakpoint *, std::optional<std::string> > 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, ""); //setlocale(LC_ALL, "");
std::vector<disk_backend *> rk05_files; std::vector<disk_backend *> rk05_files;