diff --git a/CMakeLists.txt b/CMakeLists.txt index a607c8e..ad215c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable( console_ncurses.cpp console_posix.cpp cpu.cpp + debugger.cpp error.cpp main.cpp memory.cpp diff --git a/ESP32/debugger.cpp b/ESP32/debugger.cpp new file mode 120000 index 0000000..39af298 --- /dev/null +++ b/ESP32/debugger.cpp @@ -0,0 +1 @@ +../debugger.cpp \ No newline at end of file diff --git a/ESP32/debugger.h b/ESP32/debugger.h new file mode 120000 index 0000000..90051ef --- /dev/null +++ b/ESP32/debugger.h @@ -0,0 +1 @@ +../debugger.h \ No newline at end of file diff --git a/ESP32/main.ino b/ESP32/main.ino index d3a30c4..6d79e78 100644 --- a/ESP32/main.ino +++ b/ESP32/main.ino @@ -11,6 +11,7 @@ #include "console_esp32.h" #include "cpu.h" +#include "debugger.h" #include "error.h" #include "esp32.h" #include "memory.h" @@ -36,6 +37,8 @@ std::atomic_bool interrupt_emulation { false }; std::atomic_bool *running { nullptr }; +bool trace_output { false }; + // std::atomic_bool on_wifi { false }; void setBootLoader(bus *const b) { @@ -219,14 +222,6 @@ void setup() { Serial.flush(); - Serial.println(F("Press to start")); - - for(;;) { - int c = cnsl->wait_char(1000); - if (c == 13 || c == 10) - break; - } - cnsl->start_thread(); Serial.println(F("Emulation starting!")); @@ -236,59 +231,8 @@ void setup() { *running = true; } -uint32_t icount = 0; - -void dump_state(bus *const b) { - cpu *const c = b->getCpu(); - - uint32_t now = millis(); - uint32_t t_diff = now - start_ts; - - double mips = icount / (1000.0 * t_diff); - - // see https://retrocomputing.stackexchange.com/questions/6960/what-was-the-clock-speed-and-ips-for-the-original-pdp-11 - constexpr double pdp11_clock_cycle = 150; // ns, for the 11/70 - constexpr double pdp11_mhz = 1000.0 / pdp11_clock_cycle; - constexpr double pdp11_avg_cycles_per_instruction = (1 + 5) / 2.0; - constexpr double pdp11_estimated_mips = pdp11_mhz / pdp11_avg_cycles_per_instruction; - - Serial.print(F("MIPS: ")); - Serial.println(mips); - - Serial.print(F("emulation speed (aproximately): ")); - Serial.print(mips * 100 / pdp11_estimated_mips); - Serial.println('%'); - - Serial.print(F("PC: ")); - Serial.println(c->getPC()); - - Serial.print(F("Uptime (ms): ")); - Serial.println(t_diff); -} - void loop() { - icount++; + debugger(cnsl, b, &interrupt_emulation, false); - c->step(); - - if (event || terminate) { - *running = false; - - Serial.println(F("")); - Serial.println(F(" *** EMULATION STOPPED *** ")); - dump_state(b); - delay(3000); - Serial.println(F(" *** EMULATION RESTARTING *** ")); - - c->reset(); - c->setRegister(7, exec_addr); - - start_ts = millis(); - icount = 0; - - terminate = false; - event = 0; - - *running = true; - } + c->reset(); } diff --git a/ESP32/platformio.ini b/ESP32/platformio.ini index ff9526f..44661d8 100644 --- a/ESP32/platformio.ini +++ b/ESP32/platformio.ini @@ -12,5 +12,5 @@ monitor_speed = 115200 upload_speed = 1000000 lib_deps = greiman/SdFat@^2.1.2 adafruit/Adafruit NeoPixel@^1.10.4 -build_flags = -std=gnu++17 -Ofast -DESP32=1 -ggdb3 +build_flags = -std=gnu++17 -Ofast -DESP32=1 -ggdb3 -D_GLIBCXX_USE_C99 build_unflags = -std=gnu++11 -Os diff --git a/debugger.cpp b/debugger.cpp new file mode 100644 index 0000000..ed249ad --- /dev/null +++ b/debugger.cpp @@ -0,0 +1,189 @@ +#include "bus.h" +#include "console.h" +#include "cpu.h" +#include "utils.h" + + +#if defined(ESP32) +void setBootLoader(bus *const b); +#endif + +extern uint32_t event; +extern std::atomic_bool terminate; + +// returns size of instruction (in bytes) +int disassemble(cpu *const c, console *const cnsl, const int pc, const bool instruction_only) +{ + auto data = c->disassemble(pc); + + auto registers = data["registers"]; + auto psw = data["psw"][0]; + + std::string instruction_values; + for(auto iv : data["instruction-values"]) + instruction_values += (instruction_values.empty() ? "" : ",") + iv; + + std::string work_values; + for(auto wv : data["work-values"]) + work_values += (work_values.empty() ? "" : ",") + wv; + + std::string instruction = data["instruction-text"].at(0); + + std::string result; + + if (instruction_only) + result = format("PC: %06o, instr: %s\t%s\t%s", + pc, + instruction_values.c_str(), + instruction.c_str(), + work_values.c_str() + ); + else + result = format("R0: %s, R1: %s, R2: %s, R3: %s, R4: %s, R5: %s, SP: %s, PC: %06o, PSW: %s, instr: %s: %s - %s", + 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, + psw.c_str(), + instruction_values.c_str(), + instruction.c_str(), + work_values.c_str() + ); + + if (cnsl) + cnsl->debug(result); + else + fprintf(stderr, "%s\n", result.c_str()); + + return data["instruction-values"].size() * 2; +} + +std::map split(const std::vector & kv_array, const std::string & splitter) +{ + std::map out; + + for(auto pair : kv_array) { + auto kv = split(pair, splitter); + + if (kv.size() == 1) + out.insert({ kv[0], "" }); + else if (kv.size() == 2) + out.insert({ kv[0], kv[1] }); + } + + return out; +} + +void debugger(console *const cnsl, bus *const b, std::atomic_bool *const interrupt_emulation, const bool tracing) +{ + cpu *const c = b->getCpu(); + + bool single_step = false; + + while(!terminate) { + bool temp = terminate; + std::string cmd = cnsl->read_line(format("%d%d", event, temp)); + auto parts = split(cmd, " "); + auto kv = split(parts, "="); + + if (parts.empty()) + continue; + + if (cmd == "go") + single_step = false; + else if (cmd == "single" || cmd == "s") + single_step = true; + 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") { + c->set_breakpoint(pc); + + cnsl->put_string_lf(format("Set breakpoint at %06o", pc)); + } + else { + c->remove_breakpoint(pc); + + cnsl->put_string_lf(format("Clear breakpoint at %06o", pc)); + } + + continue; + } + else if (cmd == "lbp") { + auto bps = c->list_breakpoints(); + + cnsl->put_string_lf("Breakpoints:"); + + for(auto pc : bps) { + cnsl->put_string_lf(format(" %06o", pc)); + + pc += disassemble(c, cnsl, pc, true); + } + + continue; + } + else if (parts[0] == "disassemble" || parts[0] == "d") { + int pc = kv.find("pc") != kv.end() ? std::stoi(kv.find("pc")->second, nullptr, 8) : c->getPC(); + int n = kv.find("n") != kv.end() ? std::stoi(kv.find("n") ->second, nullptr, 10) : 1; + + for(int i=0; ireset(); + +#if defined(ESP32) + setBootLoader(b); +#endif + + continue; + } +#if !defined(ESP32) + else if (cmd == "quit" || cmd == "q") + break; +#endif + else if (cmd == "help" || cmd == "h" || cmd == "?") { + cnsl->put_string_lf("disassemble/d - show current instruction (pc=/n=)"); + cnsl->put_string_lf("go - run until trap or ^e"); +#if !defined(ESP32) + cnsl->put_string_lf("quit/q - stop emulator"); +#endif + cnsl->put_string_lf("reset/r - reset cpu/bus/etc"); + cnsl->put_string_lf("single/s - run 1 instruction (implicit 'disassemble' command)"); + cnsl->put_string_lf("sbp/cbp/lbp - set/clear/list breakpoint(s)"); + + continue; + } + else { + cnsl->put_string_lf("?"); + continue; + } + + while(!event && !terminate && !*interrupt_emulation) { + if (tracing || single_step) + disassemble(c, single_step ? cnsl : nullptr, c->getPC(), false); + + if (c->check_breakpoint() && !single_step) { + cnsl->put_string_lf("Breakpoint"); + break; + } + + c->step(); + + if (single_step) + break; + } + + if (*interrupt_emulation) { + single_step = true; + + event = 0; + + *interrupt_emulation = false; + } + } +} diff --git a/debugger.h b/debugger.h new file mode 100644 index 0000000..dd444f9 --- /dev/null +++ b/debugger.h @@ -0,0 +1,4 @@ +#include "bus.h" +#include "console.h" + +void debugger(console *const cnsl, bus *const b, std::atomic_bool *const interrupt_emulation, const bool tracing); diff --git a/main.cpp b/main.cpp index ec63043..31b3a2a 100644 --- a/main.cpp +++ b/main.cpp @@ -11,6 +11,7 @@ #include "console_ncurses.h" #include "console_posix.h" #include "cpu.h" +#include "debugger.h" #include "gen.h" #include "memory.h" #include "terminal.h" @@ -128,67 +129,6 @@ uint16_t loadTape(bus *const b, const char *const file) return start; } -// returns size of instruction (in bytes) -int disassemble(cpu *const c, console *const cnsl, const int pc, const bool instruction_only) -{ - auto data = c->disassemble(pc); - - auto registers = data["registers"]; - auto psw = data["psw"][0]; - - std::string instruction_values; - for(auto iv : data["instruction-values"]) - instruction_values += (instruction_values.empty() ? "" : ",") + iv; - - std::string work_values; - for(auto wv : data["work-values"]) - work_values += (work_values.empty() ? "" : ",") + wv; - - std::string instruction = data["instruction-text"].at(0); - - std::string result; - - if (instruction_only) - result = format("PC: %06o, instr: %s\t%s\t%s", - pc, - instruction_values.c_str(), - instruction.c_str(), - work_values.c_str() - ); - else - result = format("R0: %s, R1: %s, R2: %s, R3: %s, R4: %s, R5: %s, SP: %s, PC: %06o, PSW: %s, instr: %s: %s - %s", - 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, - psw.c_str(), - instruction_values.c_str(), - instruction.c_str(), - work_values.c_str() - ); - - if (cnsl) - cnsl->debug(result); - else - fprintf(stderr, "%s\n", result.c_str()); - - return data["instruction-values"].size() * 2; -} - -std::map split(const std::vector & kv_array, const std::string & splitter) -{ - std::map out; - - for(auto pair : kv_array) { - auto kv = split(pair, splitter); - - if (kv.size() == 1) - out.insert({ kv[0], "" }); - else if (kv.size() == 2) - out.insert({ kv[0], kv[1] }); - } - - return out; -} - volatile bool sw = false; void sw_handler(int s) { @@ -225,10 +165,10 @@ int main(int argc, char *argv[]) c -> setEmulateMFPT(true); std::vector rk05_files; - bool testCases = false; - bool debugger = false; - bool tracing = false; - int opt = -1; + bool testCases = false; + bool run_debugger = false; + bool tracing = false; + int opt = -1; while((opt = getopt(argc, argv, "hm:T:R:p:ndtL:")) != -1) { switch(opt) { @@ -237,7 +177,7 @@ int main(int argc, char *argv[]) return 1; case 'd': - debugger = true; + run_debugger = true; break; case 't': @@ -331,110 +271,8 @@ int main(int argc, char *argv[]) cnsl->start_thread(); - if (debugger) { - bool single_step = false; - - while(!terminate) { - bool temp = terminate; - std::string cmd = cnsl->read_line(format("%d%d", event, temp)); - auto parts = split(cmd, " "); - auto kv = split(parts, "="); - - if (parts.empty()) - continue; - - if (cmd == "go") - single_step = false; - else if (cmd == "single" || cmd == "s") - single_step = true; - 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") { - c->set_breakpoint(pc); - - cnsl->put_string_lf(format("Set breakpoint at %06o", pc)); - } - else { - c->remove_breakpoint(pc); - - cnsl->put_string_lf(format("Clear breakpoint at %06o", pc)); - } - - continue; - } - else if (cmd == "lbp") { - auto bps = c->list_breakpoints(); - - cnsl->put_string_lf("Breakpoints:"); - - for(auto pc : bps) { - cnsl->put_string_lf(format(" %06o", pc)); - - pc += disassemble(c, cnsl, pc, true); - } - - continue; - } - else if (parts[0] == "disassemble" || parts[0] == "d") { - int pc = kv.find("pc") != kv.end() ? std::stoi(kv.find("pc")->second, nullptr, 8) : c->getPC(); - int n = kv.find("n") != kv.end() ? std::stoi(kv.find("n") ->second, nullptr, 10) : 1; - - for(int i=0; ireset(); - - continue; - } - else if (cmd == "quit" || cmd == "q") - break; - else if (cmd == "help") { - cnsl->put_string_lf("disassemble/d - show current instruction (pc=/n=)"); - cnsl->put_string_lf("go - run until trap or ^e"); - cnsl->put_string_lf("quit/q - stop emulator"); - cnsl->put_string_lf("reset/r - reset cpu/bus/etc"); - cnsl->put_string_lf("single/s - run 1 instruction (implicit 'disassemble' command)"); - cnsl->put_string_lf("sbp/cbp/lbp - set/clear/list breakpoint(s)"); - - continue; - } - else { - cnsl->put_string_lf("?"); - continue; - } - - while(!event && !terminate && !interrupt_emulation) { - if (tracing || single_step) - disassemble(c, single_step ? cnsl : nullptr, c->getPC(), false); - - if (c->check_breakpoint() && !single_step) { - cnsl->put_string_lf("Breakpoint"); - break; - } - - c->step(); - - if (single_step) - break; - } - - if (interrupt_emulation) { - single_step = true; - - event = 0; - - interrupt_emulation = false; - } - } - } + if (run_debugger) + debugger(cnsl, b, &interrupt_emulation, tracing); else { while(!event && !terminate) c->step();