diff --git a/ESP32/console_esp32.cpp b/ESP32/console_esp32.cpp index 642f332..161f5d9 100644 --- a/ESP32/console_esp32.cpp +++ b/ESP32/console_esp32.cpp @@ -10,26 +10,17 @@ #define NEOPIXELS_PIN 25 -console_esp32::console_esp32(std::atomic_bool *const terminate, bus *const b) : - console(terminate, b) +console_esp32::console_esp32(std::atomic_bool *const terminate, std::atomic_bool *const interrupt_emulation, bus *const b) : + console(terminate, interrupt_emulation, b) { } console_esp32::~console_esp32() { - if (th) { - th->join(); - - delete th; - } + stop_thread(); } -void console_esp32::start_thread() -{ - th = new std::thread(std::ref(*this)); -} - -int console_esp32::wait_for_char(const short timeout) +int console_esp32::wait_for_char_ll(const short timeout) { for(short i=0; isetEmulateMFPT(true); Serial.println(F("Init console")); - cnsl = new console_esp32(&terminate, b); + cnsl = new console_esp32(&terminate, &interrupt_emulation, b); running = cnsl->get_running_flag(); @@ -215,7 +216,7 @@ void setup() { Serial.println(F("Press to start")); for(;;) { - int c = cnsl->wait_for_char(1000); + int c = cnsl->wait_char(1000); if (c == 13 || c == 10) break; } diff --git a/console.cpp b/console.cpp index ec60240..1751ec4 100644 --- a/console.cpp +++ b/console.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -9,8 +10,9 @@ #include "utils.h" -console::console(std::atomic_bool *const terminate, bus *const b) : +console::console(std::atomic_bool *const terminate, std::atomic_bool *const interrupt_emulation, bus *const b) : terminate(terminate), + interrupt_emulation(interrupt_emulation), b(b) { memset(screen_buffer, ' ', sizeof screen_buffer); @@ -20,15 +22,38 @@ console::~console() { } +void console::start_thread() +{ + stop_thread_flag = false; + + th = new std::thread(std::ref(*this)); +} + +void console::stop_thread() +{ + if (th) { + stop_thread_flag = true; + + th->join(); + delete th; + + th = nullptr; + } +} + bool console::poll_char() { + std::unique_lock lck(input_lock); + return input_buffer.empty() == false; } -uint8_t console::get_char() +int console::get_char() { + std::unique_lock lck(input_lock); + if (input_buffer.empty()) - return 0x00; + return -1; char c = input_buffer.at(0); @@ -37,6 +62,23 @@ uint8_t console::get_char() return c; } +int console::wait_char(const int timeout_ms) +{ + std::unique_lock lck(input_lock); + + using namespace std::chrono_literals; + + if (input_buffer.empty() == false || have_data.wait_for(lck, timeout_ms * 1ms) == std::cv_status::no_timeout) { + int c = input_buffer.at(0); + + input_buffer.erase(input_buffer.begin() + 0); + + return c; + } + + return -1; +} + void console::flush_input() { input_buffer.clear(); @@ -50,7 +92,7 @@ std::string console::read_line(const std::string & prompt) std::string str; for(;;) { - char c = wait_for_char(500); + char c = wait_char(500); if (c == -1) continue; @@ -134,29 +176,29 @@ void console::put_string(const std::string & what) put_char(what.at(x)); } -void console::put_string_lf(const std::string & what) -{ - put_string(what); - - put_string("\n"); -} - void console::operator()() { D(fprintf(stderr, "Console thread started\n");) - while(!*terminate) { - int c = wait_for_char(500); + set_thread_name("kek:console"); + + while(!*terminate && !stop_thread_flag) { + int c = wait_for_char_ll(100); if (c == -1) continue; if (c == 3) // ^c *terminate = true; + else if (c == 5) // ^e + *interrupt_emulation = true; else if (c == 12) // ^l refresh_virtual_terminal(); - else + else { input_buffer.push_back(c); + + have_data.notify_all(); + } } D(fprintf(stderr, "Console thread terminating\n");) diff --git a/console.h b/console.h index d89a519..7bdac7b 100644 --- a/console.h +++ b/console.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -14,38 +15,45 @@ class console { private: std::vector input_buffer; + std::condition_variable have_data; + std::mutex input_lock; protected: - std::atomic_bool *const terminate { nullptr }; + std::atomic_bool *const terminate { nullptr }; + std::atomic_bool *const interrupt_emulation { nullptr }; - bus *const b { nullptr }; - std::thread *th { nullptr }; + bus *const b { nullptr }; + std::thread *th { nullptr }; std::atomic_bool disk_read_activity_flag { false }; std::atomic_bool disk_write_activity_flag { false }; - std::atomic_bool running_flag { false }; + std::atomic_bool running_flag { false }; + + bool stop_thread_flag { false }; char screen_buffer[t_height][t_width]; - uint8_t tx { 0 }; - uint8_t ty { 0 }; + uint8_t tx { 0 }; + uint8_t ty { 0 }; + + virtual int wait_for_char_ll(const short timeout) = 0; virtual void put_char_ll(const char c) = 0; public: - console(std::atomic_bool *const terminate, bus *const b); + console(std::atomic_bool *const terminate, std::atomic_bool *const interrupt_emulation, bus *const b); virtual ~console(); - virtual void start_thread() = 0; - - virtual int wait_for_char(const short timeout) = 0; + void start_thread(); + void stop_thread(); bool poll_char(); - uint8_t get_char(); + int get_char(); + int wait_char(const int timeout_ms); std::string read_line(const std::string & prompt); void flush_input(); void put_char(const char c); void put_string(const std::string & what); - virtual void put_string_lf(const std::string & what); + virtual void put_string_lf(const std::string & what) = 0; void debug(const std::string fmt, ...); diff --git a/console_ncurses.cpp b/console_ncurses.cpp index 81c67ee..66735c4 100644 --- a/console_ncurses.cpp +++ b/console_ncurses.cpp @@ -9,32 +9,26 @@ #include "utils.h" -console_ncurses::console_ncurses(std::atomic_bool *const terminate, bus *const b) : - console(terminate, b) +console_ncurses::console_ncurses(std::atomic_bool *const terminate, std::atomic_bool *const interrupt_emulation, bus *const b) : + console(terminate, interrupt_emulation, b) { init_ncurses(true); resize_terminal(); - th = new std::thread(std::ref(*this)); - th_panel = new std::thread(&console_ncurses::panel_update_thread, this); } console_ncurses::~console_ncurses() { + stop_thread(); + if (th_panel) { th_panel->join(); delete th_panel; } - if (th) { - th->join(); - - delete th; - } - std::unique_lock lck(ncurses_mutex); wattron(w_main->win, A_BOLD); @@ -54,12 +48,7 @@ console_ncurses::~console_ncurses() endwin(); } -void console_ncurses::start_thread() -{ - th = new std::thread(std::ref(*this)); -} - -int console_ncurses::wait_for_char(const short timeout) +int console_ncurses::wait_for_char_ll(const short timeout) { struct pollfd fds[] = { { STDIN_FILENO, POLLIN, 0 } }; @@ -90,6 +79,13 @@ void console_ncurses::put_char_ll(const char c) } } +void console_ncurses::put_string_lf(const std::string & what) +{ + put_string(what); + + put_string("\n"); +} + void console_ncurses::resize_terminal() { std::unique_lock lck(ncurses_mutex); @@ -126,6 +122,8 @@ void console_ncurses::resize_terminal() void console_ncurses::panel_update_thread() { + set_thread_name("kek:c-panel"); + cpu *const c = b->getCpu(); uint64_t prev_instr_cnt = c->get_instructions_executed_count(); diff --git a/console_ncurses.h b/console_ncurses.h index 41025b4..7f910c8 100644 --- a/console_ncurses.h +++ b/console_ncurses.h @@ -21,15 +21,15 @@ private: int ty { 0 }; protected: - int wait_for_char(const short timeout) override; + int wait_for_char_ll(const short timeout) override; void put_char_ll(const char c) override; public: - console_ncurses(std::atomic_bool *const terminate, bus *const b); + console_ncurses(std::atomic_bool *const terminate, std::atomic_bool *const interrupt_emulation, bus *const b); virtual ~console_ncurses(); - void start_thread() override; + void put_string_lf(const std::string & what) override; void resize_terminal() override; diff --git a/console_posix.cpp b/console_posix.cpp index bf51003..eee9f52 100644 --- a/console_posix.cpp +++ b/console_posix.cpp @@ -7,8 +7,8 @@ #include "error.h" -console_posix::console_posix(std::atomic_bool *const terminate, bus *const b) : - console(terminate, b) +console_posix::console_posix(std::atomic_bool *const terminate, std::atomic_bool *const interrupt_emulation, bus *const b) : + console(terminate, interrupt_emulation, b) { if (tcgetattr(STDIN_FILENO, &org_tty_opts) == -1) error_exit(true, "console_posix: tcgetattr failed"); @@ -18,29 +18,17 @@ console_posix::console_posix(std::atomic_bool *const terminate, bus *const b) : if (tcsetattr(STDIN_FILENO, TCSANOW, &tty_opts_raw) == -1) error_exit(true, "console_posix: tcsetattr failed"); - - th = new std::thread(std::ref(*this)); } console_posix::~console_posix() { - if (th) { - th->join(); - - delete th; - } + stop_thread(); if (tcsetattr(STDIN_FILENO, TCSANOW, &org_tty_opts) == -1) error_exit(true, "~console_posix: tcsetattr failed"); - } -void console_posix::start_thread() -{ - th = new std::thread(std::ref(*this)); -} - -int console_posix::wait_for_char(const short timeout) +int console_posix::wait_for_char_ll(const short timeout) { struct pollfd fds[] = { { STDIN_FILENO, POLLIN, timeout } }; @@ -61,7 +49,7 @@ void console_posix::put_string_lf(const std::string & what) { put_string(what); - put_string("\n"); + put_string("\r\n"); } void console_posix::resize_terminal() diff --git a/console_posix.h b/console_posix.h index 5c706e9..25954be 100644 --- a/console_posix.h +++ b/console_posix.h @@ -9,16 +9,14 @@ private: struct termios org_tty_opts { 0 }; protected: - int wait_for_char(const short timeout) override; + int wait_for_char_ll(const short timeout) override; void put_char_ll(const char c) override; public: - console_posix(std::atomic_bool *const terminate, bus *const b); + console_posix(std::atomic_bool *const terminate, std::atomic_bool *const interrupt_emulation, bus *const b); virtual ~console_posix(); - void start_thread() override; - void resize_terminal() override; void put_string_lf(const std::string & what) override; diff --git a/cpu.cpp b/cpu.cpp index 57555c2..14a49bb 100644 --- a/cpu.cpp +++ b/cpu.cpp @@ -1922,35 +1922,6 @@ std::map > cpu::disassemble(const uint16_t return out; } -void cpu::disassemble() const -{ -#if !defined(ESP32) - auto data = 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); - - D(fprintf(stderr, "R0: %s, R1: %s, R2: %s, R3: %s, R4: %s, R5: %s, SP: %s, PC: %s, PSW: %s, instr: %s: %s - %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(), registers[7].c_str(), - psw.c_str(), - instruction_values.c_str(), - instruction.c_str(), - work_values.c_str() - );) -#endif -} - void cpu::step() { instruction_count++; diff --git a/main.cpp b/main.cpp index 3781031..1f63746 100644 --- a/main.cpp +++ b/main.cpp @@ -127,6 +127,40 @@ uint16_t loadTape(bus *const b, const char *const file) return start; } +void disassemble(cpu *const c, console *const cnsl) +{ +#if !defined(ESP32) + auto data = c->disassemble(c->getPC()); + + 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 = format("R0: %s, R1: %s, R2: %s, R3: %s, R4: %s, R5: %s, SP: %s, PC: %s, 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(), registers[7].c_str(), + 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()); +#endif +} + volatile bool sw = false; void sw_handler(int s) { @@ -167,7 +201,7 @@ int main(int argc, char *argv[]) bool debugger = false; bool tracing = false; int opt = -1; - while((opt = getopt(argc, argv, "hm:T:R:p:ndL:")) != -1) + while((opt = getopt(argc, argv, "hm:T:R:p:ndtL:")) != -1) { switch(opt) { case 'h': @@ -220,14 +254,16 @@ int main(int argc, char *argv[]) console *cnsl = nullptr; + std::atomic_bool interrupt_emulation { false }; + if (withUI) - cnsl = new console_ncurses(&terminate, b); + cnsl = new console_ncurses(&terminate, &interrupt_emulation, b); else { fprintf(stderr, "This PDP-11 emulator is called \"kek\" (reason for that is forgotten) and was written by Folkert van Heusden.\n"); fprintf(stderr, "Built on: " __DATE__ " " __TIME__ "\n"); - cnsl = new console_posix(&terminate, b); + cnsl = new console_posix(&terminate, &interrupt_emulation, b); } if (rk05_files.empty() == false) { @@ -252,9 +288,8 @@ int main(int argc, char *argv[]) sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; - if (withUI) { + if (withUI) sigaction(SIGWINCH, &sa, nullptr); - } sigaction(SIGTERM, &sa, nullptr); sigaction(SIGINT , &sa, nullptr); @@ -264,22 +299,72 @@ int main(int argc, char *argv[]) // loadbin(b, 0, "test.dat"); // c->setRegister(7, 0); - cnsl->start_thread(); - c->emulation_start(); // for statistics + cnsl->start_thread(); + if (debugger) { - while(!event && !terminate) { - if (tracing) - c->disassemble(); + bool single_step = false; - if (c->check_breakpoint()) + while(!terminate) { + bool temp = terminate; + std::string cmd = cnsl->read_line(format("%d%d", event, temp)); + auto parts = split(cmd, " "); + + if (cmd == "go") + single_step = false; + else if (cmd == "single" || cmd == "s") + single_step = true; + else if (cmd == "disassemble" || cmd == "d") { + disassemble(c, single_step ? cnsl : nullptr); + continue; + } + else if (cmd == "reset" || cmd == "r") { + terminate = false; + + event = 0; + + c->reset(); + + continue; + } + else if (cmd == "quit" || cmd == "q") break; + else if (cmd == "help") { + cnsl->put_string_lf("disassemble/d - show current instruction"); + 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)"); - c->step(); + continue; + } + else { + cnsl->put_string_lf("?"); + continue; + } + + while(!event && !terminate && !interrupt_emulation) { + if (tracing || single_step) + disassemble(c, single_step ? cnsl : nullptr); + + if (c->check_breakpoint() && !single_step) + break; + + c->step(); + + if (single_step) + break; + } + + if (interrupt_emulation) { + single_step = true; + + event = 0; + + interrupt_emulation = false; + } } - - // TODO: some menu } else { while(!event && !terminate) diff --git a/utils.cpp b/utils.cpp index ff54378..f9356a4 100644 --- a/utils.cpp +++ b/utils.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include void setBit(uint16_t & v, const int bit, const bool vb) @@ -74,3 +75,41 @@ void myusleep(uint64_t us) } #endif } + +std::vector split(std::string in, std::string splitter) +{ + std::vector out; + size_t splitter_size = splitter.size(); + + for(;;) + { + size_t pos = in.find(splitter); + if (pos == std::string::npos) + break; + + std::string before = in.substr(0, pos); + if (!before.empty()) + out.push_back(before); + + size_t bytes_left = in.size() - (pos + splitter_size); + if (bytes_left == 0) + return out; + + in = in.substr(pos + splitter_size); + } + + if (in.size() > 0) + out.push_back(in); + + return out; +} + +void set_thread_name(std::string name) +{ +#if !defined(ESP32) + if (name.length() > 15) + name = name.substr(0, 15); + + pthread_setname_np(pthread_self(), name.c_str()); +#endif +} diff --git a/utils.h b/utils.h index e87cae0..53a9479 100644 --- a/utils.h +++ b/utils.h @@ -1,7 +1,8 @@ -// (C) 2018 by Folkert van Heusden +// (C) 2018-2022 by Folkert van Heusden // Released under Apache License v2.0 #include #include +#include void setBit(uint16_t & v, const int bit, const bool vb); int parity(int v); @@ -10,5 +11,9 @@ int parity(int v); std::string format(const char *const fmt, ...); +std::vector split(std::string in, std::string splitter); + unsigned long get_ms(); void myusleep(uint64_t us); + +void set_thread_name(std::string name);