i/o fixes for debugger

This commit is contained in:
folkert van heusden 2022-04-11 22:50:05 +02:00
parent 9c7632fb1c
commit ab268f5ca6
13 changed files with 255 additions and 131 deletions

View file

@ -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; i<timeout / 10; i++) {
if (Serial.available())

View file

@ -6,16 +6,14 @@
class console_esp32 : public console
{
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_esp32(std::atomic_bool *const terminate, bus *const b);
console_esp32(std::atomic_bool *const terminate, std::atomic_bool *const interrupt_emulation, bus *const b);
virtual ~console_esp32();
void start_thread() override;
void put_string_lf(const std::string & what) override;
void resize_terminal() override;

View file

@ -37,9 +37,10 @@ uint32_t start_ts = 0;
SdFat32 sd;
std::atomic_bool terminate { false };
std::atomic_bool terminate { false };
std::atomic_bool interrupt_emulation { false };
std::atomic_bool *running { nullptr };
std::atomic_bool *running { nullptr };
// std::atomic_bool on_wifi { false };
@ -185,7 +186,7 @@ void setup() {
c->setEmulateMFPT(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 <enter> to start"));
for(;;) {
int c = cnsl->wait_for_char(1000);
int c = cnsl->wait_char(1000);
if (c == 13 || c == 10)
break;
}

View file

@ -1,3 +1,4 @@
#include <chrono>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -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<std::mutex> lck(input_lock);
return input_buffer.empty() == false;
}
uint8_t console::get_char()
int console::get_char()
{
std::unique_lock<std::mutex> 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<std::mutex> 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");)

View file

@ -1,6 +1,7 @@
#pragma once
#include <atomic>
#include <condition_variable>
#include <thread>
#include <vector>
@ -14,38 +15,45 @@ class console
{
private:
std::vector<char> 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, ...);

View file

@ -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<std::mutex> 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<std::mutex> 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();

View file

@ -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;

View file

@ -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()

View file

@ -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;

29
cpu.cpp
View file

@ -1922,35 +1922,6 @@ std::map<std::string, std::vector<std::string> > 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++;

113
main.cpp
View file

@ -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)

View file

@ -8,6 +8,7 @@
#include <stdint.h>
#include <string>
#include <string.h>
#include <vector>
#include <sys/time.h>
void setBit(uint16_t & v, const int bit, const bool vb)
@ -74,3 +75,41 @@ void myusleep(uint64_t us)
}
#endif
}
std::vector<std::string> split(std::string in, std::string splitter)
{
std::vector<std::string> 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
}

View file

@ -1,7 +1,8 @@
// (C) 2018 by Folkert van Heusden
// (C) 2018-2022 by Folkert van Heusden
// Released under Apache License v2.0
#include <stdint.h>
#include <string>
#include <vector>
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<std::string> split(std::string in, std::string splitter);
unsigned long get_ms();
void myusleep(uint64_t us);
void set_thread_name(std::string name);