diff --git a/.gitignore b/.gitignore index 2269c46..902570b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /.vscode/ /makevars.mak +/bin +/out diff --git a/bin/spider/DrawVM.d b/bin/spider/DrawVM.d index 96619c2..9b04523 100644 --- a/bin/spider/DrawVM.d +++ b/bin/spider/DrawVM.d @@ -1,3 +1,6 @@ -bin/spider/DrawVM.o: src/spider/DrawVM.cpp src/spider/LiveDebug.hpp +bin/spider/DrawVM.o: src/spider/DrawVM.cpp src/spider/LiveDebug.hpp \ + src/spider/RamContent.hpp src/spider/ByteCodeContent.hpp src/spider/DrawVM.cpp: src/spider/LiveDebug.hpp: +src/spider/RamContent.hpp: +src/spider/ByteCodeContent.hpp: diff --git a/bin/spider/DrawVM.o b/bin/spider/DrawVM.o index 5374586..680eacd 100644 Binary files a/bin/spider/DrawVM.o and b/bin/spider/DrawVM.o differ diff --git a/bin/spider/LiveDebug.d b/bin/spider/LiveDebug.d index 04d593f..981f4c5 100644 --- a/bin/spider/LiveDebug.d +++ b/bin/spider/LiveDebug.d @@ -1,3 +1,6 @@ -bin/spider/LiveDebug.o: src/spider/LiveDebug.cpp src/spider/LiveDebug.hpp +bin/spider/LiveDebug.o: src/spider/LiveDebug.cpp src/spider/LiveDebug.hpp \ + src/spider/RamContent.hpp src/spider/ByteCodeContent.hpp src/spider/LiveDebug.cpp: src/spider/LiveDebug.hpp: +src/spider/RamContent.hpp: +src/spider/ByteCodeContent.hpp: diff --git a/bin/spider/LiveDebug.o b/bin/spider/LiveDebug.o index c0cf375..924526d 100644 Binary files a/bin/spider/LiveDebug.o and b/bin/spider/LiveDebug.o differ diff --git a/makefile b/makefile index 7242959..8674412 100644 --- a/makefile +++ b/makefile @@ -25,11 +25,11 @@ CFLAGS := -std=c++20 -O2 \ -Wsign-conversion -Wnull-dereference -Wdouble-promotion \ -Wformat=2 -Wimplicit-fallthrough -Wsuggest-override \ -Wextra-semi -Wduplicated-cond -Wduplicated-branches \ - -Wlogical-op -Wuseless-cast + -Wlogical-op -Wuseless-cast -Wno-unused-parameter LFLAGS := -std=c++20 -static -static-libstdc++ -static-libgcc \ -Wl,--fatal-warnings -Wl,--warn-common LIBDIRS := -L"$(PATH_DESKTOPLIB)/out" -L"$(PATH_SPIDER_RUNTIME)/out" -LIB := -ldesktoplib #-lspider-runtime +LIB := -ldesktoplib -lspider-runtime INC := -I"$(PATH_DESKTOPLIB)/src" -I"$(PATH_SPIDER_RUNTIME)/src" -I./src/ #--------------------------------------------------------------------------------- diff --git a/out/out.exe b/out/out.exe index 1d47660..71db13c 100644 Binary files a/out/out.exe and b/out/out.exe differ diff --git a/src/spider/ByteCodeContent.cpp b/src/spider/ByteCodeContent.cpp new file mode 100644 index 0000000..9698042 --- /dev/null +++ b/src/spider/ByteCodeContent.cpp @@ -0,0 +1,71 @@ +#include "ByteCodeContent.hpp" + +#include "LiveDebug.hpp" + +#include + +namespace spider { + + ByteCodeContent::ByteCodeContent() : input(1), ipX(0), ipY(0) {} + + ByteCodeContent::~ByteCodeContent() {} + + // Content must report how tall it wants to be + int ByteCodeContent::getTotalHeight() const { + return int(std::ceil(float(reel.size() + 1) / 16.f)); + } + + // Render only the rows between startRow and endRow + // relativeY is where the Content starts drawing in terminal space + void ByteCodeContent::render(Terminal& t, pos screenPos, pos viewport, std::pair rows) const { + const u64 reel_size = reel.size(); + char ascii[16]; + + for (int row = rows.first; row < rows.second; ++row) { + // Set terminal cursor position for the start of this row + t << pos(screenPos.x + 1, screenPos.y + (row - rows.first)); + + // Group 8 Bytes + for(int block = 0; block < 2; block++) { + for (int col = 0; col < 8; ++col) { + u64 offset = u64(row * 16 + col + block * 8); + u32 offX = u32(block * 8 + col); + int ascii_index = col + block * 8; + + // Is currently selected thing? + bool selected = offX == ipX && u32(row) == ipY && menuSelected && mainMenuID == 0x13; + if(selected) t << backg::B_GREEN << color::BLACK; + + // Is input currently enabled? + if(input.getCharCount() && selected) { + // draw this instead! + u8 byte = u8(input.getValue()); + drawU8(byte); + ascii[ascii_index] = (byte >= 32 && byte <= 126) ? static_cast(byte) : '.'; + } else if (offset < reel_size) { + u8 byte = reel.readU8(offset); + drawU8(byte); + ascii[ascii_index] = (byte >= 32 && byte <= 126) ? static_cast(byte) : '.'; + } else { + t << " "; + ascii[ascii_index] = ' '; + } + t << style::RESET << color::WHITE; + t << ' '; + } + if(block == 0) t << " "; + } + + t << color::B_BLACK; + t << "| "; + t << color::WHITE; + + // --- ASCII Output Loop --- + for (int i = 0; i < 16; ++i) { + t << ascii[i]; + if (i == 7) t << ' '; + } + } + } + +} diff --git a/src/spider/ByteCodeContent.hpp b/src/spider/ByteCodeContent.hpp new file mode 100644 index 0000000..0ed6f5e --- /dev/null +++ b/src/spider/ByteCodeContent.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include + +namespace spider { + + using namespace ckitty::terminal; + + /** + * Draws a specific part of the RAM. + */ + class ByteCodeContent : public Content { + public: + + ByteInput input; + u64 ipX, ipY; + + public: + + ByteCodeContent(); + + virtual ~ByteCodeContent(); + + public: + + // Content must report how tall it wants to be + virtual int getTotalHeight() const override; + + // Render only the rows between startRow and endRow + // relativeY is where the Content starts drawing in terminal space + virtual void render(Terminal& t, pos screenPos, pos viewport, std::pair rows) const override; + + }; + +} \ No newline at end of file diff --git a/src/spider/ByteInput.cpp b/src/spider/ByteInput.cpp new file mode 100644 index 0000000..69a5a13 --- /dev/null +++ b/src/spider/ByteInput.cpp @@ -0,0 +1,49 @@ +#include "ByteInput.hpp" + +#include + +namespace spider { + + ByteInput::ByteInput(u64 bytes) + : max_bytes(bytes), max_chars(max_bytes * 2), + current_char_count(0), accumulated_value(0) { + } + + u8 ByteInput::hexCharToInt(char ch) const { + if (ch >= '0' && ch <= '9') return u8(ch - '0'); + if (ch >= 'a' && ch <= 'f') return u8(ch - 'a' + 10); + if (ch >= 'A' && ch <= 'F') return u8(ch - 'A' + 10); + return 0; + } + + bool ByteInput::accept(char ch) { + if (current_char_count >= max_chars || !std::isxdigit(int(ch))) { + return false; + } + + // Shift left by 4 bits (1 hex nibble) and inject the new digit + accumulated_value = (accumulated_value << 4) | hexCharToInt(ch); + current_char_count++; + return true; + } + + u64 ByteInput::getValue() const { + if (current_char_count == 0) { + return 0; + } + + // Pad remaining digits with zeros since entry started at MSB + std::size_t remaining_chars = max_chars - current_char_count; + return accumulated_value << (remaining_chars * 4); + } + + void ByteInput::cancel() { + current_char_count = 0; + accumulated_value = 0; + } + + bool ByteInput::isComplete() const { return current_char_count == max_chars; } + + std::size_t ByteInput::getCharCount() const { return current_char_count; } + +} \ No newline at end of file diff --git a/src/spider/ByteInput.hpp b/src/spider/ByteInput.hpp new file mode 100644 index 0000000..d375b36 --- /dev/null +++ b/src/spider/ByteInput.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include + +#include +#include + +namespace spider { + + class ByteInput { + private: + + u64 max_bytes; + u64 max_chars; + u64 current_char_count; + u64 accumulated_value; + + // Helper to convert a single hex char to its integer value + + + public: + /** + * @brief Construct a new Byte Input object. + * @param bytes The number of bytes to accept (capped at 8 to prevent uint64_t overflow). + */ + explicit ByteInput(u64 bytes); + + private: + + u8 hexCharToInt(char ch) const; + + public: + + /** + * @brief Accepts a character if it is a valid hex digit and capacity remains. + * @param ch The character to process. + * @return true if accepted, false if invalid or full. + */ + bool accept(char ch); + + /** + * @brief Get the numeric value of the current input. + * @return std::uint64_t The value, padded with trailing zeros if incomplete. + */ + u64 getValue() const; + + /** + * @brief Resets the input buffer and index. + */ + void cancel(); + + // Inline getters for quick state checking + bool isComplete() const; + + u64 getCharCount() const; + + }; + +} \ No newline at end of file diff --git a/src/spider/DrawVM.cpp b/src/spider/DrawVM.cpp index c6d384f..af88022 100644 --- a/src/spider/DrawVM.cpp +++ b/src/spider/DrawVM.cpp @@ -7,17 +7,81 @@ namespace spider { + const char hex_chars[] = { + '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'A', 'B', + 'C', 'D', 'E', 'F', + }; + void drawU64(u64 n) { - const char chars[] = { - '0', '1', '2', '3', - '4', '5', '6', '7', - '8', '9', 'A', 'B', - 'C', 'D', 'E', 'F', - }; Terminal& t = *term; for (int i = 15; i >= 0; --i) { u64 m = n >> (i * 4); - t << chars[m & 0xF]; + t << hex_chars[m & 0xF]; + } + } + + void drawU8(u8 n) { + Terminal& t = *term; + t << hex_chars[(n >> 4) & 0xF]; + t << hex_chars[ n & 0xF]; + } + + void drawValue(register_t r) { + //u64: 18446744073709551615 | u64: 18446744073709551615// + } + + void clearValue() { + } + + void drawCPUValues() { + Terminal& t = *term; + CPU& cpu = runtime.cpu; + + i32 c = 1, r = 8; + c++; + r += 3; + + const color alt[] = { + color::WHITE, + color::B_BLACK, + }; + + t << style::RESET; + for (i32 i = 0; i < 8; i++) { + t << alt[i & 1]; + t << pos(c, r + i * 2); + drawU64(cpu.GPR[i * 2]._u64); + t << pos(c + 17, r + i * 2); + drawU64(cpu.GPR[i * 2 + 1]._u64); + } + + r += 17; + // "RF", "RI", "RS", "RZ", + // "RE", "RN", "RV", "RM", + const u64* sr[] = { + &cpu.RF, &cpu.RI, &cpu.RS, &cpu.RZ, + &cpu.RE, &cpu.RN, &cpu.RV, &cpu.RM, + }; + t << style::RESET; + for (i32 i = 0; i < 4; i++) { + t << alt[i & 1]; + t << pos(c, r + i * 2); + drawU64(*sr[i * 2]); + t << pos(c + 17, r + i * 2); + drawU64(*sr[i * 2 + 1]); + } + + r += 9; + const register_t* cr[] = { &cpu.ALU0, &cpu.ALU1 }; + t << style::RESET; + for (i32 i = 0; i < 1; i++) { + t << alt[i & 1]; + t << pos(c, r + i * 2); + drawU64(cr[i * 2]->_u64); + t << pos(c + 17, r + i * 2); + drawU64(cr[i * 2 + 1]->_u64); } } @@ -96,12 +160,19 @@ namespace spider { t.flush(); } + void drawRAMValues() { + if(updateRAM) { + updateRAM = false; + ram_scroll->render(*term); + } + } + void drawRAMFace() { Terminal& t = *term; // FF FF FF FF FF FF FF FF | XXXX XXXX #// - i32 c = 37, r = 8; - i32 w = 40, h = 31; + i32 c = ram_scroll->position.x - 1, r = ram_scroll->position.y - 1; + i32 w = ram_scroll->width + 2, h = ram_scroll->height + 2; Box myBox({ c, r }, w, h, " RAM "); myBox.charset = BoxSet::DOUBLE; myBox.borderFg = color::RED; @@ -109,12 +180,19 @@ namespace spider { t << myBox; } + void drawByteCodeValues() { + if(updateReel) { + updateReel = false; + reel_scroll->render(*term); + } + } + void drawByteCodeFace() { Terminal& t = *term; - // FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF | XXXXXXXX XXXXXXXX #// + // 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ........ ........ #// - i32 c = 78, r = 8; - i32 w = 71, h = 31; + i32 c = reel_scroll->position.x - 1, r = reel_scroll->position.y - 1; + i32 w = reel_scroll->width + 2, h = reel_scroll->height + 2; Box myBox({ c, r }, w, h, " Byte Code "); myBox.charset = BoxSet::DOUBLE; myBox.borderFg = color::GREEN; @@ -136,6 +214,20 @@ namespace spider { lt.tm_mday, lt.tm_mon + 1, lt.tm_year + 1900); } + void drawPanel() { + Terminal& t = *term; + t + << pos(46, 3) << color::B_YELLOW << cyclesRan + ; + } + + void drawValues() { + drawPanel(); + drawCPUValues(); + drawRAMValues(); + drawByteCodeValues(); + } + void drawFaceplate() { Terminal& t = *term; t @@ -164,7 +256,7 @@ namespace spider { drawCPUFace(); drawRAMFace(); drawByteCodeFace(); - t << pos(101, 1) << color::YELLOW; + t << pos(104, 1) << color::YELLOW; for(int i = 0; i < 47; i++) t << "═"; t << "╗"; for(int i = 0; i < 5; i++) { @@ -182,7 +274,7 @@ namespace spider { << pos(29, 6) << color::B_BLACK << "[ MENU ]" << pos(4, 8) << color::CYAN << " CPU " << pos(40, 8) << color::RED << " RAM " - << pos(81, 8) << color::GREEN << " Byte Code " + << pos(82, 8) << color::GREEN << " Byte Code " ; switch(mainMenuID) { case 0x1: // step @@ -208,7 +300,7 @@ namespace spider { else t << "> RAM <"; break; case 0x13: // instr - t << pos(81, 8) << color::GREEN << style::BOLD; + t << pos(82, 8) << color::GREEN << style::BOLD; if(menuSelected) t << "[ Byte Code ]"; else t << "> Byte Code <"; break; diff --git a/src/spider/LiveDebug.cpp b/src/spider/LiveDebug.cpp index 855c108..c7a329b 100644 --- a/src/spider/LiveDebug.cpp +++ b/src/spider/LiveDebug.cpp @@ -1,18 +1,30 @@ #include "LiveDebug.hpp" #include +#include namespace spider { // Resources // + Runtime runtime; + InstrReelDyn reel(256); + Terminal* term; MenuMap* mainmenu; + RamContent* ram_content; + InnerScroll* ram_scroll; + + ByteCodeContent reel_content; + InnerScroll* reel_scroll; + bool running; int mainMenuID = 1; int intrMenuID = 1; bool menuSelected = false; + bool updateRAM = true; + bool updateReel = true; int selCPUReg = 0; int selRamPos = 0; int selInstrPos = 0; @@ -30,6 +42,7 @@ namespace spider { .fill(backg::BLACK) ; drawFaceplate(); + drawValues(); } // Live Actions // @@ -49,6 +62,12 @@ namespace spider { // Setup & Loop // void setup() { + runtime.ram.resize(1024); + runtime.hookReel(&reel); + ram_content = new RamContent(&runtime.ram); + ram_scroll = new InnerScroll(pos(38, 9), 39, 29, *ram_content); + reel_scroll = new InnerScroll(pos(80, 9), 71, 29, reel_content); + clearAll(); mainmenu = new MenuMap(3, 5); mainmenu->setWrap(true, false); @@ -63,6 +82,26 @@ namespace spider { mainmenu->fill(2, 4, 1, 3, 0x13); } + /** + * Performs an action according to the + * currently selected action on the panel. + * AKA: STEP, RUN, STOP, MENU + */ + void panelAction() { + switch(mainMenuID) { + case 1: // STEP + //runtime.step(); + cyclesRan++; + break; + case 2: // RUN + break; + case 3: // STOP + break; + case 4: // MENU + break; // TODO: DO THIS + } + } + void mainMenuCtrls(int k) { switch(k) { case key::UP: @@ -77,6 +116,27 @@ namespace spider { case key::RIGHT: mainmenu->moveRight(); break; + case key::ENTER: + // This is a panel action + if(mainMenuID < 0x10) { + panelAction(); + break; + } + // starting action, asap as it's + // selected. Usually just update + // the menu so it comes alive. + menuSelected = mainMenuID; + switch(mainMenuID) { + case 0x11: + break; + case 0x12: + updateRAM = true; + break; + case 0x13: + updateReel = true; + break; + } + break; default: return; } @@ -84,6 +144,77 @@ namespace spider { mainMenuID = r.id; } + void selMenuCtrls(int k) { + switch(mainMenuID) { + case 0x11: // CPU + break; + case 0x12: // RAM + if(k == key::UP) { + ram_scroll->scroll(-1); + updateRAM = true; + } + if(k == key::DOWN) { + ram_scroll->scroll(1); + updateRAM = true; + } + if(k == 'q') updateRAM = true; + break; + case 0x13: // INSTR + if(k == key::UP) { + reel_scroll->scroll(-1); + reel_content.ipY = u64(reel_scroll->rowY); + reel_content.input.cancel(); + updateReel = true; + } + if(k == key::DOWN) { + reel_scroll->scroll(1); + reel_content.ipY = u64(reel_scroll->rowY); + reel_content.input.cancel(); + updateReel = true; + } + if(k == key::LEFT) { + reel_content.ipX = (reel_content.ipX - 1) % 16; + reel_content.input.cancel(); + updateReel = true; + } + if(k == key::RIGHT) { + reel_content.ipX = (reel_content.ipX + 1) % 16; + reel_content.input.cancel(); + updateReel = true; + } + if(std::isxdigit(k)) { + // Input enable. + // Clamp if outside of range. + u64 maxIP = reel.size(); + u64 ip = std::min(reel_content.ipX + reel_content.ipY * 16, maxIP); + reel_content.ipX = ip % 16; + reel_content.ipY = ip / 16; + auto& t = *term; + t << pos(0, 0) << ip << ", " << maxIP; + reel_content.input.accept(char(k)); + updateReel = true; + } + if(k == key::ENTER || reel_content.input.isComplete()) { + // Input enable. + // Clamp if outside of range. + u64 maxIP = reel.size(); + u64 ip = std::min(reel_content.ipX + reel_content.ipY * 16, maxIP); + reel_content.ipX = ip % 16; + reel_content.ipY = ip / 16; + u8 byte = u8(reel_content.input.getValue()); + reel.write(ip, &byte, 1); + reel_content.input.cancel(); + updateReel = true; + } + if(k == 'q') updateReel = true; + break; + } + if(k == 'q') { + menuSelected = false; + drawMainMenuSel(); + } + } + void loop() { // time @79 Terminal& t = *term; @@ -95,11 +226,21 @@ namespace spider { running = false; return; } - mainMenuCtrls(k); - drawMainMenuSel(); + + // if nothing selected, draw the main menu + // selection AND handle input + if(!menuSelected) { + mainMenuCtrls(k); + drawMainMenuSel(); + } else { // inner, selected menu + selMenuCtrls(k); + } + drawValues(); // Favour the thread doing other stuff - if(!runVM) { // IF the vm is not continously running + if(runVM) { // IF the vm is not continously running + std::this_thread::yield(); + } else { std::this_thread::sleep_for(std::chrono::milliseconds(50)); } diff --git a/src/spider/LiveDebug.hpp b/src/spider/LiveDebug.hpp index dcbf345..7ca2bac 100644 --- a/src/spider/LiveDebug.hpp +++ b/src/spider/LiveDebug.hpp @@ -1,21 +1,43 @@ #pragma once #include +#include #include #include #include +#include +#include + +#include "RamContent.hpp" +#include "ByteCodeContent.hpp" namespace spider { using namespace ckitty::terminal; + // Virtual Machine + extern Runtime runtime; + extern InstrReelDyn reel; + // Resources // extern Terminal* term; extern MenuMap* mainmenu; + + extern RamContent* ram_content; + extern InnerScroll* ram_scroll; + + extern ByteCodeContent reel_content; + extern InnerScroll* reel_scroll; + extern bool running; extern int mainMenuID; extern bool menuSelected; + extern bool updateRAM; + extern bool updateReel; + + extern u64 cyclesRan; + extern bool runVM; // UI Actions // @@ -23,8 +45,14 @@ namespace spider { void drawFaceplate(); + void drawValues(); + void drawMainMenuSel(); + void drawU64(u64 n); + + void drawU8(u8 n); + std::string getTime(); // Live Actions // diff --git a/src/spider/RamContent.cpp b/src/spider/RamContent.cpp new file mode 100644 index 0000000..df4a173 --- /dev/null +++ b/src/spider/RamContent.cpp @@ -0,0 +1,76 @@ +#include "RamContent.hpp" +#include "LiveDebug.hpp" + +#include + +namespace spider { + + RamContent::RamContent(RAM* _ram) : ram(_ram) {} + + RamContent::~RamContent() {} + + // Content must report how tall it wants to be + int RamContent::getTotalHeight() const { + return int(std::ceil(float(ram->size()) / 8.f)); + } + + // Render only the rows between startRow and endRow + // relativeY is where the Content starts drawing in terminal space + void RamContent::render(Terminal& t, pos screenPos, pos viewport, std::pair rows) const { + // FF FF FF FF FF FF FF FF | XXXX XXXX #// + const u64 ram_size = ram->size(); + char ascii[8]; + + for (int row = rows.first; row < rows.second; ++row) { + // Set terminal cursor position for the start of this row + t << pos(screenPos.x + 1, screenPos.y + (row - rows.first)); + + // Is this the selected column? + if(ram_scroll->rowY == row && menuSelected && mainMenuID == 0x12) { + t << backg::RED << color::WHITE; + } else { + t << style::RESET << color::WHITE; + } + + // --- Group 1: First 4 bytes --- + for (int col = 0; col < 4; ++col) { + u64 offset = static_cast(row * 8 + col); + if (offset < ram_size) { + u8 byte = ram->at(offset); + drawU8(byte); + ascii[col] = (byte >= 32 && byte <= 126) ? static_cast(byte) : '.'; + } else { + t << " "; + ascii[col] = ' '; + } + t << ' '; + } + + t << ' '; + + // --- Group 2: Next 4 bytes --- + for (int col = 4; col < 8; ++col) { + u64 offset = static_cast(row * 8 + col); + if (offset < ram_size) { + u8 byte = ram->at(offset); + drawU8(byte); + ascii[col] = (byte >= 32 && byte <= 126) ? static_cast(byte) : '.'; + } else { + t << " "; + ascii[col] = ' '; + } + t << " "; + } + t << color::B_BLACK; + t << "| "; + t << color::WHITE; + + // --- ASCII Output Loop --- + for (int i = 0; i < 8; ++i) { + t << ascii[i]; + if (i == 3) t << ' '; + } + } + } + +} diff --git a/src/spider/RamContent.hpp b/src/spider/RamContent.hpp new file mode 100644 index 0000000..c779544 --- /dev/null +++ b/src/spider/RamContent.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +namespace spider { + + using namespace ckitty::terminal; + + /** + * Draws a specific part of the RAM. + */ + class RamContent : public Content { + public: + + RAM* ram; + + public: + + RamContent(RAM* _ram); + + virtual ~RamContent(); + + public: + + // Content must report how tall it wants to be + virtual int getTotalHeight() const override; + + // Render only the rows between startRow and endRow + // relativeY is where the Content starts drawing in terminal space + virtual void render(Terminal& t, pos screenPos, pos viewport, std::pair rows) const override; + + }; + +} \ No newline at end of file