From 57775b39a940da5e5f29b9cb2ce6611ba107c587 Mon Sep 17 00:00:00 2001 From: uvok Date: Fri, 23 Jan 2026 20:32:19 +0100 Subject: Add ncurses and disassembly --- eater_cpu/cpp/CMakeLists.txt | 18 +-- eater_cpu/cpp/Veater_computer__main.cpp | 9 +- eater_cpu/cpp/disas.h | 30 ++++ eater_cpu/cpp/simpc_config.h | 2 +- eater_cpu/cpp/simpc_curses.cpp | 237 ++++++++++++++++++++++++++++++++ eater_cpu/cpp/simpc_term.cpp | 12 +- 6 files changed, 288 insertions(+), 20 deletions(-) create mode 100644 eater_cpu/cpp/disas.h create mode 100644 eater_cpu/cpp/simpc_curses.cpp (limited to 'eater_cpu') diff --git a/eater_cpu/cpp/CMakeLists.txt b/eater_cpu/cpp/CMakeLists.txt index 771db9d..d0349e7 100644 --- a/eater_cpu/cpp/CMakeLists.txt +++ b/eater_cpu/cpp/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.20) project(sim_eater_pc) +set(CMAKE_CXX_STANDARD 23) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) find_package(verilator REQUIRED HINTS $ENV{VERILATOR_ROOT}) @@ -40,12 +42,10 @@ verilate(Veater_computer VERILATOR_ARGS -CFLAGS -I${CMAKE_CURRENT_SOURCE_DIR} ) -# if (USE_NCURSES) -# target_compile_definitions(Veater_computer PRIVATE NCUR) -# target_link_libraries(Veater_computer PRIVATE Curses::Curses) -# target_sources(Veater_computer PUBLIC simpc_ui.cpp) -# else() -# target_sources(Veater_computer PUBLIC simpc_term.cpp) -# endif() - -target_sources(Veater_computer PUBLIC simpc_term.cpp) +if (USE_NCURSES) + target_compile_definitions(Veater_computer PRIVATE NCUR) + target_link_libraries(Veater_computer PRIVATE Curses::Curses) + target_sources(Veater_computer PUBLIC simpc_curses.cpp) +else() + target_sources(Veater_computer PUBLIC simpc_term.cpp) +endif() diff --git a/eater_cpu/cpp/Veater_computer__main.cpp b/eater_cpu/cpp/Veater_computer__main.cpp index 40b39b3..3bdb786 100644 --- a/eater_cpu/cpp/Veater_computer__main.cpp +++ b/eater_cpu/cpp/Veater_computer__main.cpp @@ -94,16 +94,17 @@ int main(int argc, char **argv, char **) { tfp->close(); simpc_ui_finish_message(contextp, topp); - if (VL_LIKELY(!contextp->gotFinish())) { - VL_DEBUG_IF(VL_PRINTF("+ Exiting without $finish; no events left\n");); - } + // if (VL_LIKELY(!contextp->gotFinish())) { + // VL_DEBUG_IF(VL_PRINTF("+ Exiting without $finish; no events left\n");); + // } // Execute 'final' processes topp->final(); // Print statistical summary report - contextp->statsPrintSummary(); + // contextp->statsPrintSummary(); + simpc_ui_confirm_finish(); simpc_ui_cleanup(); return 0; diff --git a/eater_cpu/cpp/disas.h b/eater_cpu/cpp/disas.h new file mode 100644 index 0000000..9685e15 --- /dev/null +++ b/eater_cpu/cpp/disas.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +static std::string disas(uint8_t ins) { + switch (ins >> 4) { + case 0: + return "nop"; + case 1: + return std::format("lda {}", ins & 0x0f); + case 2: + return std::format("add {}", ins & 0x0f); + case 3: + return std::format("sub {}", ins & 0x0f); + case 4: + return std::format("sta {}", ins & 0x0f); + case 5: + return std::format("ldi {}", ins & 0x0f); + case 6: + return std::format("jmp"); + case 14: + return std::format("out"); + case 15: + return std::format("halt"); + default: + return "???"; + } +} diff --git a/eater_cpu/cpp/simpc_config.h b/eater_cpu/cpp/simpc_config.h index 56c9ad8..cc00199 100644 --- a/eater_cpu/cpp/simpc_config.h +++ b/eater_cpu/cpp/simpc_config.h @@ -1,4 +1,4 @@ #pragma once // #define TICKS_PER_CLOCK_PERIOD 1 -// #define NCUR_DELAY_MS 10 +#define NCUR_DELAY_MS 10 diff --git a/eater_cpu/cpp/simpc_curses.cpp b/eater_cpu/cpp/simpc_curses.cpp new file mode 100644 index 0000000..b6dd230 --- /dev/null +++ b/eater_cpu/cpp/simpc_curses.cpp @@ -0,0 +1,237 @@ +#include "disas.h" +#include "simpc_config.h" +#include "simpc_ui.h" + +#include + +#define PRINT_ME(y, x, ...) \ + { \ + mvprintw(y, x, __VA_ARGS__); \ + } +#define PRINT_ME_W(w, y, x, ...) \ + { \ + mvwprintw(w, y, x, __VA_ARGS__); \ + } +#define PRINT_NEXT(dly) \ + { \ + if (resized) { \ + /* ????*/ \ + resized = 0; \ + clear(); \ + } \ + refresh(); \ + wrefresh(status_top); \ + wrefresh(clock_regs); \ + wrefresh(MEM); \ + if (dly) { \ + napms(NCUR_DELAY_MS); \ + } \ + } + +#define SIMPLE_BORDER(w, lr, tb, c) \ + { \ + wborder(w, lr, lr, tb, tb, c, c, c, c); \ + } + +static WINDOW *status_top = NULL; +static WINDOW *clock_regs = NULL; +static WINDOW *MEM = NULL; + +#include "Veater_computer.h" +#include "Veater_computer_eater_alu.h" +#include "Veater_computer_eater_computer.h" +#include "Veater_computer_eater_register.h" +#include "Veater_computer_my_mem__DB10.h" +#include "verilated.h" + +bool paused = true; +static bool resized = false; + +static void handle_key(); + +// void simpc_ui_write(const std::unique_ptr &topp, uint64_t +// &i) { +void simpc_ui_write(const std::unique_ptr &topp, uint64_t i) { + // clk hi->lo doesn't do anything, just skip this. + if (i != 0 && !topp->clk_in) + return; + uint8_t opcode = topp->eater_computer->INS->r_datastore; + uint8_t halt = topp->halt; + + PRINT_ME_W(status_top, 1, 1, "Step: %10lu", i); + PRINT_ME_W(status_top, 2, 1, "%-20s", paused ? "Paused" : "Running"); + + PRINT_ME_W(clock_regs, 1, 1, "CLK: %4d\tPC: 0x%02X\tINS: 0x%02X\tHLT: %6d", + topp->clk_in, + // wrong + // topp->eater_computer->clk_in, + topp->eater_computer->PC_out, opcode, topp->halt); + // const wchar_t flag_on = L'\u26ab'; + // const wchar_t flag_off = L'\u26aa'; + const char flag_on = 'X'; + const char flag_off = ' '; + PRINT_ME_W( + clock_regs, 2, 1, + "| %c | %c | %c %c | %c %c | %c %c | %c %c | %c %c | %c | %c %c %c |", + halt ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__MAR_in ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__RAM_in ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__RAM_out ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__INS_in ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__INS_out ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__A_in ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__A_out ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__ALU_out ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__ALU_subtract_nadd ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__B_in ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__B_out ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__OUT_in ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__PC_count ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__PC_out ? flag_on : flag_off, + topp->eater_computer->flags.__PVT__PC_in ? flag_on : flag_off); + PRINT_ME_W(clock_regs, 3, 1, + "| H | M | R R | I I | A A | A S | B B | O | C C J | \n"); + PRINT_ME_W(clock_regs, 4, 1, + "| L | A | A A | S S | I O | L U | I O | I | E O M | \n"); + PRINT_ME_W(clock_regs, 5, 1, + "| T | I | I O | I O | | O B | | N | P | \n"); + + // PRINT_ME_W( + // clock_regs, 2, 1, "A: 0x%02X\tD: 0x%02X\tM: 0x%02X\tRES: 0x%02X", + // topp->eater_computer->reg_A_int, topp->eater_computer->reg_D_int, + // topp->eater_computer->reg_pA_int, topp->eater_computer->result_int); + // PRINT_ME_W(clock_regs, 3, 1, "%c%8d\t%c %8d\t%c %8d\t%11d", + // topp->eater_computer->store_to_A_int ? '*' : ' ', + // topp->eater_computer->reg_A_int, + // topp->eater_computer->store_to_D_int ? '*' : ' ', + // topp->eater_computer->reg_D_int, + // topp->eater_computer->store_to_pA_int ? '*' : ' ', + // topp->eater_computer->reg_pA_int, + // topp->eater_computer->result_int); + + for (int adr = 0; adr < 16; adr++) { + const int ypos_base = 1; + const int adr_factored = adr % 8; + const int xpos_base = adr < 8 ? 1 : 21; + const char *prefix = adr == topp->eater_computer->MAR_out ? "> " : " "; + { + const uint16_t program_op_code = + topp->eater_computer->RAM->r_datastore[adr]; + std::string disas_code = disas(program_op_code); + PRINT_ME_W(MEM, ypos_base + adr_factored, xpos_base, + "%02X %s%02X %-8s %c", adr, prefix, program_op_code, + disas_code.c_str(), xpos_base == 1 ? '|' : ' '); + // mvchgat in bold + } + } + + // borders at the bottom, because of wrapping logic I don't grasp + SIMPLE_BORDER(MEM, '|', '-', '+'); + PRINT_ME_W(MEM, 0, 1, "--- MEM ---"); + + // PRINT_ME(7 + NCUR_OFFSET, NCUR_X, "ALU"); + // PRINT_ME(8 + NCUR_OFFSET, NCUR_X, "X: %5d\tY: %5d", + // topp->eater_computer->CPU->my_alu->int_op_x, + // topp->eater_computer->CPU->my_alu->int_op_y); + + PRINT_ME(getmaxy(stdscr) - 1, 1, + " q - Quit; p - (Un)pause; s - step (while paused) "); + + PRINT_NEXT(true); + + handle_key(); +} + +void simpc_ui_finish_message(const std::unique_ptr &contextp, + const std::unique_ptr &topp) { + attron(A_BOLD); + auto xpos = 20; + PRINT_ME_W(status_top, 1, xpos, "Simulation finished."); + const char *msg; + if (topp->halt) { + msg = "Halt encountered."; + } else if (!contextp->gotFinish()) { + msg = "Step count exceeded."; + } else { + msg = "Regular finish."; + } + PRINT_ME_W(status_top, 2, xpos, "%s", msg); + PRINT_NEXT(false); + attroff(A_BOLD); +} + +void simpc_ui_init(void) { +#if NCUR + initscr(); + curs_set(0); + + // lines, cols, ypos, xpos + status_top = newwin(4, 50, 0, 0); + wrefresh(status_top); + clock_regs = newwin(7, 80, 3, 0); + wrefresh(clock_regs); + auto numlines = 8 + 2; + auto romwidth = 41; + MEM = newwin(numlines, romwidth, 10, 0); + wrefresh(MEM); + + nodelay(stdscr, TRUE); + noecho(); + cbreak(); + +#endif +} + +void simpc_ui_cleanup(void) { +#if NCUR + nocbreak(); + echo(); + + auto ws = {status_top, clock_regs, MEM}; + + for (auto &w : ws) { + if (w) { + delwin(w); + } + } + + endwin(); +#endif +} + +void simpc_ui_confirm_finish(void) { +#if NCUR + nodelay(stdscr, FALSE); + getch(); +#endif +} + +static void handle_key() { + bool block_here = paused; + + do { + int ch = getch(); + + switch (ch) { + case 'p': + // pass *current* state. + // pass false (currently running) - getch will block (entering step mode). + // pass true (currently paused) - getch will be non-blocking. + nodelay(stdscr, paused); + paused = !paused; + block_here = paused; + break; + case 'q': + simpc_ui_cleanup(); + exit(0); + break; + case 's': + block_here = false; + break; + + case KEY_RESIZE: + resized = 1; + break; + } + } while (block_here); +} diff --git a/eater_cpu/cpp/simpc_term.cpp b/eater_cpu/cpp/simpc_term.cpp index 2ac3895..b0396a1 100644 --- a/eater_cpu/cpp/simpc_term.cpp +++ b/eater_cpu/cpp/simpc_term.cpp @@ -1,5 +1,4 @@ -#include "Veater_computer_eater_register.h" -#include "Veater_computer_my_mem__DB10.h" + #include "simpc_ui.h" #define NCUR_X 5 @@ -25,6 +24,8 @@ #include "Veater_computer.h" #include "Veater_computer_eater_alu.h" #include "Veater_computer_eater_computer.h" +#include "Veater_computer_eater_register.h" +#include "Veater_computer_my_mem__DB10.h" #include "verilated.h" #include #include @@ -38,10 +39,8 @@ void simpc_ui_write(const std::unique_ptr &topp, uint64_t i) { // clk hi->lo doesn't do anything, just skip this. if (i != 0 && !topp->clk_in) return; - // uint16_t opcode = topp->eater_computer->; uint8_t opcode = topp->eater_computer->INS->r_datastore; - // topp->halt - uint8_t halt = topp->eater_computer->flags.__PVT__halt; + uint8_t halt = topp->halt; PRINT_ME_W(status_top, 0, 0, "Step: %10lu", i); PRINT_ME_W(status_top, 1, 0, "%-20s", paused ? "Paused" : "Running"); @@ -113,7 +112,8 @@ void simpc_ui_finish_message(const std::unique_ptr &contextp, for (int idx = 0; idx < topp->eater_computer->RAM->r_datastore.size(); idx++) { - printf("Mem[%2d] = 0X%02X\n", idx, topp->eater_computer->RAM->r_datastore[idx]); + printf("Mem[%2d] = 0X%02X\n", idx, + topp->eater_computer->RAM->r_datastore[idx]); } } -- cgit v1.2.3