#include "LiveDebug.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace spider { void drawHead(Terminal& t) { t.move(1, 1) .style(Terminal::FG_YELLOW) .print(" Spider Runtime Live Debug ") .style(Terminal::RESET).print(" | ") .style(Terminal::FG_B_CYAN).print(" Sintek Analytics @ 2026 ") .style(Terminal::RESET).print(" | ") .style(Terminal::FG_B_BLACK).print("Press ESC to exit") .style(Terminal::FG_BLACK) .style(Terminal::BG_YELLOW) .move(3, 1).print(" // __ \\\\").print(" ") // 27 .move(4, 1).print(" \\\\( )//").print(" SPIDER v0.1 ") .move(5, 1).print(" //()\\\\ ").print(" alpha ") .move(6, 1).print(" \\\\ // ").print(" ") .style(Terminal::RESET) .style(Terminal::FG_B_BLACK) // 4x8 for the menu .move(3, 28).print("[ STEP ]") .move(4, 28).print("[ STOP ]") .move(5, 28).print("[ RUN ]") .move(6, 28).print("[ MENU ]") .style(Terminal::RESET) ; } void drawCPUTempl(Terminal& t) { i32 r = 8, c = 1; i32 w = 35, h = 31; t.drawBox(r, c, w, h, "CPU"); const std::string regs[] = { "RA", "RB", "RC", "RD", "RX", "RY", "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "RF", "RI", "RS", "RZ", "RE", "RN", "RV", "RM", "ALU0", "ALU1" }; const std::string alt[] = { Terminal::FG_WHITE, Terminal::FG_B_BLACK, }; r++; c++; t.move(r++, c); t.style(Terminal::FG_B_YELLOW); t.print_center(w - 2, "GP Registers"); t.style(Terminal::RESET); for (i32 i = 0; i < 8; i++) { t.style(alt[i & 1]); t.move(r + i * 2, c); t.print(regs[i * 2]); t.move(r + i * 2, c + 17); t.print(regs[i * 2 + 1]); } t.move(r += 16, c); t.style(Terminal::FG_B_CYAN); t.print_center(w - 2, "System Registers"); t.style(Terminal::RESET); r++; for (i32 j = 0, i = 8; i < 12; j++, i++) { t.style(alt[j & 1]); t.move(r + j * 2, c); t.print(regs[i * 2]); t.move(r + j * 2, c + 17); t.print(regs[i * 2 + 1]); } t.move(r += 8, c); t.style(Terminal::FG_GREEN); t.print_center(w - 2, "Extra Registers"); t.style(Terminal::RESET); r++; for (i32 j = 0, i = 12; i < 13; j++, i++) { t.style(alt[j & 1]); t.move(r + j * 2, c); t.print(regs[i * 2]); t.move(r + j * 2, c + 17); t.print(regs[i * 2 + 1]); } t.flush(); } void printU64Hex(u64 n) { std::ios state(nullptr); state.copyfmt(std::cout); std::cout << std::hex << std::uppercase << std::setfill('0') << std::setw(16) << n; std::cout.copyfmt(state); } void drawCPU(Terminal& t, CPU& cpu) { i32 r = 8, c = 1; const register_t* regs[] = { &cpu.RA, &cpu.RB, &cpu.RC, &cpu.RD, &cpu.RX, &cpu.RY, &cpu.R0, &cpu.R1, &cpu.R2, &cpu.R3, &cpu.R4, &cpu.R5, &cpu.R6, &cpu.R7, &cpu.R8, &cpu.R9, //&cpu.RF, &cpu.RI, &cpu.RS, &cpu.RZ, //&cpu.RE, &cpu.RN, &cpu.RV, &cpu.RM, &cpu.ALU0, &cpu.ALU1 }; const u64* sys_regs[] = { &cpu.RF, &cpu.RI, &cpu.RS, &cpu.RZ, &cpu.RE, &cpu.RN, &cpu.RV, &cpu.RM, }; const std::string alt[] = { Terminal::FG_WHITE, Terminal::FG_B_BLACK, }; r++; c++; t.move(r++, c); t.style(Terminal::RESET); r++; for (i32 i = 0; i < 8; i++) { t.style(alt[i & 1]); t.move(r + i * 2, c); printU64Hex(regs[i * 2]->_u64); t.move(r + i * 2, c + 17); printU64Hex(regs[i * 2 + 1]->_u64); } t.move(r += 16, c); r++; for (i32 j = 0; j < 4; j++) { t.style(alt[j & 1]); t.move(r + j * 2, c); printU64Hex(*sys_regs[j * 2]); t.move(r + j * 2, c + 17); printU64Hex(*sys_regs[j * 2 + 1]); } t.move(r += 8, c); r++; for (i32 j = 0; j < 1; j++) { t.style(alt[j & 1]); t.move(r + j * 2, c); printU64Hex(regs[16 + j * 2]->_u64); t.move(r + j * 2, c + 17); printU64Hex(regs[16 + j * 2 + 1]->_u64); } t.flush(); } u32 addressWidth(isize ramSize) { if (ramSize == 0) return 1; isize maxAddr = ramSize - 1; u32 digits = 0; // Shift by increments of 4 (one hex nibble) // We use a do-while to ensure at least 1 digit is returned for small RAMs do { digits++; maxAddr >>= 4; } while (maxAddr > 0); return digits; } /** * Draws a vertical scrollbar * @param x The column where the bar should be placed (usually box_x + width - 1) * @param y The starting row of the track (usually box_y + 1) * @param trackHeight The internal height of the box (box_height - 2) * @param progress The current progress * @param total The total */ void drawScrollThumb(Terminal& term, u32 x, u32 y, u32 trackHeight, isize progress, isize total) { if (total == 0 || trackHeight <= 0) return; // 1. Draw the background track (Light Shade: ░) term.style(Terminal::FG_B_BLACK); // Dim the track for (u32 i = 0; i < trackHeight; ++i) { term.move(i32(y + i), i32(x)).print("░"); } // 2. Calculate Thumb Position // Cap progress to total to avoid overflow if (progress > total) progress = total; // Calculate ratio (0.0 to 1.0) f64 ratio = f64(progress) / f64(total); // Map to track coordinates u32 thumbOffset = u32(ratio * (trackHeight - 1)); // 3. Draw the Thumb (Full Block: █) term.move(i32(y + thumbOffset), i32(x)); term.style(Terminal::FG_WHITE).print("█"); term.style(Terminal::RESET); } /** * Draws a hex dump of memory within a styled terminal box. * @param term Reference to your Terminal instance * @param ram The RAM * @param scrollPos The starting address to display * @param x Starting column * @param y Starting row * @param width Width of the box * @param height Height of the box */ void drawRAM(Terminal& term, RAM& ram, u64 scrollPos) { // 1. Draw the container box u32 y = 3; u32 height = 36; // 2. Configuration for the hex layout u32 addrWidth = addressWidth(ram.size()); u32 bytesPerRow = 8; u32 displayRows = height - 2; // Subtract top/bottom borders u32 width = (2 + 2 + 16 + 7 + 3 + 8 + 4) + addrWidth; u32 x = 37; // create box term.drawBox(i32(y), i32(x), i32(width), i32(height), "RAM"); drawScrollThumb(term, x + width - 2, y + 1, height - 2, scrollPos, ram.size()); // Ensure scrollPos is within bounds and aligned //if (scrollPos < 0) scrollPos = 0; if (scrollPos > ram.size()) scrollPos = ram.size(); for (u32 i = 0; i < displayRows; ++i) { isize currentRowAddr = scrollPos + (i * bytesPerRow); // address lock if (currentRowAddr >= ram.size()) { term.move(i32(y + 1 + i), i32(x + 1)); term.print(std::string(width - 3, ' ')); continue; } std::stringstream ssaddr; std::stringstream ss; // setup ss ssaddr << std::setfill('0') << std::uppercase << std::hex; ss << std::setfill('0') << std::uppercase << std::hex; // address ssaddr << std::setw(i32(addrWidth)) << currentRowAddr << " "; // Hex Bytes std::string asciiPart = ""; for (u32 j = 0; j < bytesPerRow; ++j) { isize targetAddr = currentRowAddr + j; if (targetAddr >= ram.size()) { ss << ""; // Padding for end of memory asciiPart += ""; continue; } u8 byte = ram[targetAddr]; ss << std::setfill('0') << std::setw(2) << std::hex << u32(byte) << " "; asciiPart += (std::isprint(byte) ? char(byte) : '.'); } // --- Combine and Print --- term.move(i32(y + 1 + i), i32(x + 2)); // Move inside the box term.style(Terminal::FG_B_CYAN).print(ssaddr.str()); // Hex part in Cyan term.style(Terminal::FG_WHITE).print(ss.str()); term.style(Terminal::FG_B_YELLOW).print(" | "); term.style(Terminal::FG_WHITE).print(asciiPart); // ASCII part in White } term.style(Terminal::RESET); term.flush(); } std::string getTimestamp() { std::time_t t = std::time(nullptr); std::tm lt; #if defined(SPIDER_OS_WINDOWS) localtime_s(<, &t); #endif #if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS) localtime_r(&t, <); #endif return std::format("{:02}:{:02}:{:02} {:02}/{:02}/{}", lt.tm_hour, lt.tm_min, lt.tm_sec, lt.tm_mday, lt.tm_mon + 1, lt.tm_year + 1900); } void drawTime(Terminal& t) { //auto now = std::chrono::system_clock::now(); //auto now_l = std::chrono::current_zone()->to_local(now); //auto now_s = std::chrono::floor(now_l); //std::string time_str = std::format("{:%H:%M:%S}", now_s); // Format: HH:mm:ss //std::string date_str = std::format("{:%d/%m/%Y}", now_s); // Format: dd/MM/YYYY t.move(1, 76); t.style(Terminal::RESET); t.print(" | ").style(Terminal::FG_GREEN).print(getTimestamp()); } void redraw(Terminal& t, Runtime& r, u64 scroll) { // draw CPU, RAM drawCPU(t, r.cpu); drawRAM(t, r.ram, scroll); } int liveDebugMain() { Terminal t; Runtime runtime(1024); InstrReelFixed fix(100); runtime.hookReel(&fix, false); bool running = true, update = true; u64 ramScroll = 0; u8 key = Terminal::UNKNOWN; t.println("Starting Spider live debug..."); t.altbuff(true).cursor(false); drawTime(t); drawHead(t); drawCPUTempl(t); // delay for time auto last_exec = std::chrono::steady_clock::now(); auto delay = std::chrono::milliseconds(1000); while (running) { // draw time auto now = std::chrono::steady_clock::now(); if (now - last_exec >= delay) { drawTime(t); last_exec = now; } // redraw something if it updated if (update) { redraw(t, runtime, ramScroll); update = false; } // Handle Input key = t.getKeyNb(); switch (key) { case Terminal::ESC: running = false; break; case Terminal::UP: if (ramScroll >= 16) ramScroll -= 16; update = true; break; case Terminal::DOWN: if (runtime.ram.size() >= 16 && ramScroll <= runtime.ram.size() - 16) ramScroll += 16; update = true; break; case Terminal::ENTER: update = true; runtime.cpu.fetchInstr(); runtime.cpu.execute(); // looks up instrMap[_opcode] & calls the correct instruction method (e.g. FMUL) break; default: break; } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } t.altbuff(false).println("Stopped Spider live debug.").flush(); return 0; } }