// (C) 2018-2024 by Folkert van Heusden // Released under MIT license #include #include #include #include #include "bus.h" #include "console_ncurses.h" #include "cpu.h" #include "error.h" #include "gen.h" #include "utils.h" console_ncurses::console_ncurses(std::atomic_uint32_t *const stop_event): console(stop_event) { init_ncurses(true); } console_ncurses::~console_ncurses() { stop_thread(); if (th_panel) { th_panel->join(); delete th_panel; } std::unique_lock lck(ncurses_mutex); wattron(w_main->win, A_BOLD); wprintw(w_main->win, "\n\n *** PRESS ENTER TO TERMINATE ***\n"); mydoupdate(); while(getch() != 13) { } delete_window(w_panel_b); delete_window(w_panel); delete_window(w_main_b); delete_window(w_main); endwin(); } void console_ncurses::begin() { resize_terminal(); th_panel = new std::thread(&console_ncurses::panel_update_thread, this); } int console_ncurses::wait_for_char_ll(const short timeout) { struct pollfd fds[] = { { STDIN_FILENO, POLLIN, 0 } }; if (poll(fds, 1, timeout) == 1 && fds[0].revents) { std::unique_lock lck(ncurses_mutex); int c = getch(); if (c == ERR) return -1; return c; } return -1; } void console_ncurses::put_char_ll(const char c) { if ((c >= 32 && c < 127) || c == 10) { std::unique_lock lck(ncurses_mutex); wprintw(w_main->win, "%c", c); getyx(w_main->win, ty, tx); mydoupdate(); } } 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); determine_terminal_size(); if (ERR == resizeterm(max_y, max_x)) error_exit(true, "problem resizing terminal"); wresize(stdscr, max_y, max_x); endwin(); refresh(); init_pair(1, COLOR_RED, COLOR_BLACK); init_pair(2, COLOR_YELLOW, COLOR_BLACK); init_pair(3, COLOR_BLUE, COLOR_BLACK); init_pair(4, COLOR_GREEN, COLOR_BLACK); init_pair(5, COLOR_CYAN, COLOR_BLACK); wclear(stdscr); delete_window(w_main_b); delete_window(w_main); create_win_border(0, 0, 80, 25, "terminal", &w_main_b, &w_main, false); create_win_border(0, 27, 100, 5, "panel", &w_panel_b, &w_panel, false); scrollok(w_main -> win, TRUE); mydoupdate(); } 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(); constexpr int refresh_rate = 50; while(*stop_event != EVENT_TERMINATE && stop_panel == false) { myusleep(1000000 / refresh_rate); // note that these are approximately as there's no mutex on the emulation try { uint16_t current_PSW = c->getPSW(); int run_mode = current_PSW >> 14; uint16_t current_PC = c->getPC(); memory_addresses_t rc = b->getMMU()->calculate_physical_address(run_mode, current_PC); auto current_instr = b->peek_word(run_mode, current_PC); auto data = c->disassemble(current_PC); std::unique_lock lck(ncurses_mutex); werase(w_panel->win); // wattron(w_panel->win, COLOR_PAIR(1 + run_mode)); for(uint8_t b=0; b<22; b++) mvwprintw(w_panel->win, 0, 1 + 22 - b, "%c", rc.physical_instruction & (1 << b) ? '1' : '0'); wattron(w_panel->win, COLOR_PAIR(1)); for(uint8_t b=0; b<16; b++) mvwprintw(w_panel->win, 1, 1 + 16 - b, "%c", current_PSW & (1 << b) ? '1' : '0'); if (current_instr.has_value()) { for(uint8_t b=0; b<16; b++) mvwprintw(w_panel->win, 1, 1 + 16 - b + 17, "%c", current_instr.value() & (1 << b) ? '1' : '0'); } else { for(uint8_t b=0; b<16; b++) mvwprintw(w_panel->win, 1, 1 + 16 - b + 17, "-"); } mvwprintw(w_panel->win, 4, 1, "LEDs:"); uint16_t leds = b->get_console_leds(); for(uint8_t b=0; b<16; b++) mvwprintw(w_panel->win, 4, 1 + 22 - b, "%c", leds & (1 << b) ? '1' : '0'); wattron(w_panel->win, COLOR_PAIR(5)); mvwprintw(w_panel->win, 1, 1 + 35, "%c%c%c", running_flag ? '+' : '-', disk_read_activity_flag ? '*' : 'o', disk_write_activity_flag ? '*' : 'o'); wattron(w_panel->win, COLOR_PAIR(0)); // disassembler 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); mvwprintw(w_panel->win, 2, 1, "R0: %s, R1: %s, R2: %s, R3: %s, R4: %s, R5: %s, SP: %s, PC: %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()); mvwprintw(w_panel->win, 3, 1, "PSW: %s, instr: %s", psw.c_str(), instruction_values.c_str()); mvwprintw(w_panel->win, 3, 46, "%s - %s", instruction.c_str(), work_values.c_str()); } catch(int trap) { std::unique_lock lck(ncurses_mutex); werase(w_panel->win); } { std::unique_lock lck(ncurses_mutex); // speed uint64_t cur_instr_cnt = c->get_instructions_executed_count(); mvwprintw(w_panel->win, 1, 1 + 39, "%8ld", (cur_instr_cnt - prev_instr_cnt) * refresh_rate); prev_instr_cnt = cur_instr_cnt; // ncurses wmove(w_main->win, ty, tx); mydoupdate(); } } } void console_ncurses::refresh_virtual_terminal() { std::unique_lock lck(ncurses_mutex); wclear(w_main->win); int offset = 0; for(int row=0; rowwin, row + 1, col, "%c", screen_buffer[offset]); } } mydoupdate(); }