breakpoints
This commit is contained in:
parent
541d0c7829
commit
601c21d802
15 changed files with 480 additions and 26 deletions
|
@ -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
|
||||
|
|
10
breakpoint.cpp
Normal file
10
breakpoint.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include "breakpoint.h"
|
||||
|
||||
|
||||
breakpoint::breakpoint(bus *const b) : b(b)
|
||||
{
|
||||
}
|
||||
|
||||
breakpoint::~breakpoint()
|
||||
{
|
||||
}
|
21
breakpoint.h
Normal file
21
breakpoint.h
Normal 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
48
breakpoint_and.cpp
Normal 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
20
breakpoint_and.h
Normal 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
44
breakpoint_or.cpp
Normal 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
20
breakpoint_or.h
Normal 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
107
breakpoint_parser.cpp
Normal 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
8
breakpoint_parser.h
Normal 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
77
breakpoint_register.cpp
Normal 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
25
breakpoint_register.h
Normal 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
34
cpu.cpp
|
@ -44,22 +44,42 @@ void cpu::emulation_start()
|
|||
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;
|
||||
}
|
||||
|
|
11
cpu.h
11
cpu.h
|
@ -12,6 +12,7 @@
|
|||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpoint.h"
|
||||
#include "bus.h"
|
||||
|
||||
|
||||
|
@ -62,7 +63,7 @@ private:
|
|||
#endif
|
||||
std::atomic_bool any_queued_interrupts { false };
|
||||
|
||||
std::set<uint16_t> breakpoints;
|
||||
std::map<int, breakpoint *> 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<uint16_t> list_breakpoints();
|
||||
std::optional<std::string> check_breakpoint();
|
||||
int set_breakpoint(breakpoint *const bp);
|
||||
bool remove_breakpoint(const int bp_id);
|
||||
std::map<int, breakpoint *> list_breakpoints();
|
||||
|
||||
void disassemble(void) const;
|
||||
std::map<std::string, std::vector<std::string> > disassemble(const uint16_t addr) const;
|
||||
|
|
57
debugger.cpp
57
debugger.cpp
|
@ -13,6 +13,7 @@
|
|||
#include <LittleFS.h>
|
||||
#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<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 {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
19
main.cpp
19
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<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, "");
|
||||
|
||||
std::vector<disk_backend *> rk05_files;
|
||||
|
|
Loading…
Add table
Reference in a new issue