diff --git a/src/spider/runtime/common.hpp b/src/spider/runtime/common.hpp index 67f8ed8..3507fc0 100644 --- a/src/spider/runtime/common.hpp +++ b/src/spider/runtime/common.hpp @@ -8,6 +8,7 @@ namespace spider { + // Absolute Types using u8 = std::uint8_t; using u16 = std::uint16_t; using u32 = std::uint32_t; @@ -25,6 +26,9 @@ namespace spider { static_assert(sizeof(f32) == 4, "The f32 type must be exactly 4 bytes."); static_assert(sizeof(f64) == 8, "The f64 type must be exactly 8 bytes."); + // Utility types + using isize = std::size_t; + // Utility imports using std::vector; using std::deque; diff --git a/src/spider/runtime/cpu/CPU.cpp b/src/spider/runtime/cpu/CPU.cpp index fb8cdda..e9b456e 100644 --- a/src/spider/runtime/cpu/CPU.cpp +++ b/src/spider/runtime/cpu/CPU.cpp @@ -8,7 +8,8 @@ namespace spider { R2{}, R3{}, R4{}, R5{}, R6{}, R7{}, R8{}, R9{}, RF{}, RI{}, RS{}, RZ{}, - RE{}, RN{}, RV{}, RM{} + RE{}, RN{}, RV{}, RM{}, + ALU0{}, ALU1{} {} CPU::~CPU() {} diff --git a/src/spider/runtime/cpu/InstrReel.cpp b/src/spider/runtime/cpu/InstrReel.cpp index 3a6cfaa..88df155 100644 --- a/src/spider/runtime/cpu/InstrReel.cpp +++ b/src/spider/runtime/cpu/InstrReel.cpp @@ -1,16 +1,64 @@ #include "InstrReel.hpp" +#include + +#include + namespace spider { - InstrReel::InstrReel() {} + // Public Interface // + + InstrReel::InstrReel() : _mem(nullptr), _size(0), _offset(0), _total_size(0) {} InstrReel::~InstrReel() {} - u16 InstrReel::instrAt(u64 ip) const {} + // Instruction abstraction // - u8 InstrReel::dataAt(u64 ip) const {} + u8 InstrReel::atU8(u64 ip) { + // guard against access + u64 ip_p = ip - _offset; + if(ip_p + 1 > _size) return 0; - u8 InstrReel::feedNext(CPU& cpu) {} + // send byte + return _mem[ip]; + } + + u16 InstrReel::atU16(u64 ip) { + // guard against access + u64 ip_p = ip - _offset; + if(ip_p + 2 > _size) return 0; + + // build a 16-bit big endian number + u16 dat; + spider::loadLE(&dat, _mem + ip_p); + return dat; + } + + u32 InstrReel::atU32(u64 ip) { + // guard against access + u64 ip_p = ip - _offset; + if(ip_p + 4 > _size) return 0; + + // build a 32-bit big endian number + u32 dat; + spider::loadLE(&dat, _mem + ip_p); + return dat; + } + + u64 InstrReel::atU64(u64 ip) { + // guard against access + u64 ip_p = ip - _offset; + if(ip_p + 8 > _size) return 0; + + // build a 64-bit big endian number + u64 dat; + spider::loadLE(&dat, _mem + ip_p); + return dat; + } + + u64 InstrReel::size() { + return _total_size; + } // Static Utils // diff --git a/src/spider/runtime/cpu/InstrReel.hpp b/src/spider/runtime/cpu/InstrReel.hpp index 5e2175f..06a3d14 100644 --- a/src/spider/runtime/cpu/InstrReel.hpp +++ b/src/spider/runtime/cpu/InstrReel.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace spider { @@ -8,43 +9,57 @@ namespace spider { * Implements an instruction reel. */ class InstrReel { - private: + protected: // Current accessing range // + + u8* _mem; + isize _size; + isize _offset; + isize _total_size; public: InstrReel(); - ~InstrReel(); + virtual ~InstrReel(); public: - - - public: - - /** - * Returns the two-byte instruction at the - * specific byte location. - */ - u16 instrAt(u64 ip) const; - /** * Obtains a byte of data at * the specific location. + * Reindexing may occur, continous access + * may incurr in less penalties. */ - u8 dataAt(u64 ip) const; - - public: + virtual u8 atU8(u64 ip); /** - * Fetches the data, and then - * feeds the instruction into the - * CPU. - * - * Returns how many steps it should - * move after. + * Obtains a byte of data at + * the specific location. + * Reindexing may occur, continous access + * may incurr in less penalties. */ - u8 feedNext(CPU& cpu); + virtual u16 atU16(u64 ip); + + /** + * Obtains a byte of data at + * the specific location. + * Reindexing may occur, continous access + * may incurr in less penalties. + */ + virtual u32 atU32(u64 ip); + + /** + * Obtains a byte of data at + * the specific location. + * Reindexing may occur, continous access + * may incurr in less penalties. + */ + virtual u64 atU64(u64 ip); + + /** + * Current size of the instructions. + */ + virtual u64 size(); public: // Static Utils // diff --git a/src/spider/runtime/cpu/InstrReelDyn.cpp b/src/spider/runtime/cpu/InstrReelDyn.cpp new file mode 100644 index 0000000..150e0b8 --- /dev/null +++ b/src/spider/runtime/cpu/InstrReelDyn.cpp @@ -0,0 +1,192 @@ +#include "InstrReelDyn.hpp" + +#include + +namespace spider { + + InstrReelDyn::InstrReelDyn(u64 length) : _use_count(0), _block_index(0) { + _total_size = length; + growToFit(length > 0 ? length - 1 : 0); + selectBlock(0); + } + + InstrReelDyn::InstrReelDyn(const u8* data, u64 length) {} + + InstrReelDyn::InstrReelDyn(const InstrReelDyn& copy) : _use_count(copy._use_count), _block_index(copy._block_index), _blocks(copy._blocks) { + if (_block_index < _blocks.size()) selectBlock(_block_index); + } + + InstrReelDyn::InstrReelDyn(InstrReelDyn&& move) noexcept : _use_count(move._use_count), _block_index(move._block_index), _blocks(std::move(move._blocks)) { + if (_block_index < _blocks.size()) selectBlock(_block_index); + } + + InstrReelDyn::~InstrReelDyn() { + // .. // + } + + InstrReelDyn& InstrReelDyn::operator=(const InstrReelDyn& copy) { + _use_count = copy._use_count; + _block_index = copy._block_index; + _blocks = copy._blocks; + if (_block_index < _blocks.size()) selectBlock(_block_index); + } + + InstrReelDyn& InstrReelDyn::operator=(InstrReelDyn&& move) noexcept { + _use_count = move._use_count; + _block_index = move._block_index; + _blocks = std::move(move._blocks); + if (_block_index < _blocks.size()) selectBlock(_block_index); + + move._use_count = 0; + move._block_index = 0; + move._mem = nullptr; + move._offset = 0; + move._size = 0; + move._total_size = 0; + } + + void InstrReelDyn::growToFit(isize index) { + while (_blocks.size() < (index + 1)) { + _blocks.emplace_back(); + } + } + + isize InstrReelDyn::selectIndex(u64 ip) { + return ip / 256; + } + + InstrReelDyn::ReelBlock* InstrReelDyn::selectBlock(isize index) { + // Update base class cache + auto ptr = &_blocks[index]; + _offset = index * 256; + _mem = ptr->data; + _size = 256; + _block_index = index; + + //_blocks[block_idx].access_count++; + return ptr; + } + + u8 InstrReelDyn::atU8(u64 ip) { + isize j = selectIndex(ip); + if (j >= _blocks.size()) return 0; + if (j != _block_index) { + this->selectBlock(j); + } + return _mem[ip - _offset]; + } + + u16 InstrReelDyn::atU16(u64 ip) { + isize j0 = selectIndex(ip); + isize j1 = selectIndex(ip + 1); + if (j1 >= _blocks.size()) return 0; + if (j0 == j1 && j0 != _block_index) { + selectBlock(j0); + } + if (j0 == j1 && j0 == _block_index) { + u16 dat; + spider::loadLE(&dat, _mem); + return dat; + } + + // general case, first part + u16 dat = 0; + const u8 size = sizeof(u16); + + // select first block and offset + selectBlock(j0); + u8 rem = ip % 256; + + for (u8 n = 0; n < size; n++) { + dat |= _mem[rem++] << (n * 8); + ip++; + if (!rem) selectBlock(++j0); + } + + return dat; + } + + u32 InstrReelDyn::atU32(u64 ip) { + isize j0 = selectIndex(ip); + isize j1 = selectIndex(ip + 3); + if (j1 >= _blocks.size()) return 0; + if (j0 == j1 && j0 != _block_index) { + selectBlock(j0); + } + if (j0 == j1 && j0 == _block_index) { + u32 dat; + spider::loadLE(&dat, _mem); + return dat; + } + + // general case, first part + u32 dat = 0; + const u8 size = sizeof(u32); + + // select first block and offset + selectBlock(j0); + u8 rem = ip % 256; + + for (u8 n = 0; n < size; n++) { + dat |= _mem[rem++] << (n * 8); + ip++; + if (!rem) selectBlock(++j0); + } + + return dat; + } + + u64 InstrReelDyn::atU64(u64 ip) { + isize j0 = selectIndex(ip); + isize j1 = selectIndex(ip + 3); + if (j1 >= _blocks.size()) return 0; + if (j0 == j1 && j0 != _block_index) { + selectBlock(j0); + } + if (j0 == j1 && j0 == _block_index) { + u64 dat; + spider::loadLE(&dat, _mem); + return dat; + } + + // general case, first part + u64 dat = 0; + const u8 size = sizeof(u64); + + // select first block and offset + selectBlock(j0); + u8 rem = ip % 256; + + for (u8 n = 0; n < size; n++) { + dat |= _mem[rem++] << (n * 8); + ip++; + if (!rem) selectBlock(++j0); + } + + return dat; + } + + void InstrReelDyn::at(u64 ip, u8 dat) {} + + void InstrReelDyn::at(u64 ip, u16 dat) {} + + void InstrReelDyn::at(u64 ip, u32 dat) {} + + void InstrReelDyn::at(u64 ip, u64 dat) {} + + /** + * Appends instruction at location. + */ + void InstrReelDyn::append(u64 ip, u16 bc) {} + + /** + * Appends instruction at the end. + */ + void InstrReelDyn::append(u16 bc) {} + + /** + * Removes instruction at location. + */ + void InstrReelDyn::remove(u64 ip) {} + +} diff --git a/src/spider/runtime/cpu/InstrReelDyn.hpp b/src/spider/runtime/cpu/InstrReelDyn.hpp new file mode 100644 index 0000000..75912da --- /dev/null +++ b/src/spider/runtime/cpu/InstrReelDyn.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include + +namespace spider { + + /** + * Implements an instruction reel. + */ + class InstrReelDyn : public InstrReel { + private: + + struct ReelBlock { + u8 data[256] = {}; + }; + + private: + + u64 _use_count; + isize _block_index; + std::deque _blocks; + + public: + + InstrReelDyn(u64 length); + + InstrReelDyn(const u8* data, u64 length); + + InstrReelDyn(const InstrReelDyn& copy); + + InstrReelDyn(InstrReelDyn&& move) noexcept; + + virtual ~InstrReelDyn(); + + public: + + InstrReelDyn& operator=(const InstrReelDyn& copy); + + InstrReelDyn& operator=(InstrReelDyn&& move) noexcept; + + private: + + isize selectIndex(u64 ip); + + void growToFit(isize index); + + ReelBlock* selectBlock(isize index); + + public: + + /** + * Obtains a byte of data at + * the specific location. + * Reindexing may occur, continous access + * may incurr in less penalties. + */ + virtual u8 atU8(u64 ip) override; + + /** + * Obtains a byte of data at + * the specific location. + * Reindexing may occur, continous access + * may incurr in less penalties. + */ + virtual u16 atU16(u64 ip) override; + + /** + * Obtains a byte of data at + * the specific location. + * Reindexing may occur, continous access + * may incurr in less penalties. + */ + virtual u32 atU32(u64 ip) override; + + /** + * Obtains a byte of data at + * the specific location. + * Reindexing may occur, continous access + * may incurr in less penalties. + */ + virtual u64 atU64(u64 ip) override; + + public: + + void at(u64 ip, u8 dat); + + void at(u64 ip, u16 dat); + + void at(u64 ip, u32 dat); + + void at(u64 ip, u64 dat); + + /** + * Appends instruction at location. + */ + void append(u64 ip, u16 bc); + + /** + * Appends instruction at the end. + */ + void append(u16 bc); + + /** + * Removes instruction at location. + */ + void remove(u64 ip); + + }; + +} diff --git a/src/spider/runtime/cpu/InstrReelFile.hpp b/src/spider/runtime/cpu/InstrReelFile.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/spider/runtime/cpu/InstrReelFixed.cpp b/src/spider/runtime/cpu/InstrReelFixed.cpp new file mode 100644 index 0000000..0c41e7f --- /dev/null +++ b/src/spider/runtime/cpu/InstrReelFixed.cpp @@ -0,0 +1,149 @@ +#include "InstrReelFixed.hpp" + +#include + +#include + +namespace spider { + + // Constructors & Destructors // + + InstrReelFixed::InstrReelFixed(u64 length) { + this->_offset = 0; + this->_size = length; + this->_total_size = length; + + if (_size > 0) { + _mem = new u8[_size]; + std::memset(_mem, 0, _size); + } + } + + InstrReelFixed::InstrReelFixed(const u8* data, u64 length) { + this->_offset = 0; + this->_size = length; + this->_total_size = length; + + if (_size > 0) { + _mem = new u8[_size]; + std::copy(data, data + _size, _mem); + } + } + + InstrReelFixed::InstrReelFixed(const InstrReelFixed& other) { + _offset = other._offset; + _size = other._size; + _total_size = other._total_size; + _mem = new u8[_size]; + std::copy(other._mem, other._mem + _size, _mem); + } + + InstrReelFixed::InstrReelFixed(InstrReelFixed&& other) noexcept { + _mem = other._mem; + _offset = other._offset; + _size = other._size; + _total_size = other._total_size; + + other._mem = nullptr; + other._offset = 0; + other._size = 0; + other._total_size = 0; + } + + InstrReelFixed::~InstrReelFixed() { + delete[] _mem; + } + + // Assign Operators // + + InstrReelFixed& InstrReelFixed::operator=(const InstrReelFixed& other) { + if (this == &other) return *this; // lock self + + u8* new_mem = new u8[other._size]; + std::copy(other._mem, other._mem + other._size, new_mem); + + delete[] _mem; + _mem = new_mem; + _offset = other._offset; + _size = other._size; + _total_size = other._total_size; + + return *this; + } + + InstrReelFixed& InstrReelFixed::operator=(InstrReelFixed&& other) noexcept { + if (this == &other) return *this; // lock self + + delete[] _mem; + + _mem = other._mem; // steal + _offset = other._offset; + _size = other._size; + _total_size = other._total_size; + + other._mem = nullptr; // leave as husk + other._offset = 0; + other._size = 0; + other._total_size = 0; + + return *this; + } + + // Misc // + + void InstrReelFixed::at(u64 ip, u8 dat) { + if(ip + 1 > _size) return; + _mem[ip] = dat; + } + + void InstrReelFixed::at(u64 ip, u16 dat) { + if(ip + 2 > _size) return; + spider::storeLE(dat, _mem + ip); + } + + void InstrReelFixed::at(u64 ip, u32 dat) { + if(ip + 4 > _size) return; + spider::storeLE(dat, _mem + ip); + } + + void InstrReelFixed::at(u64 ip, u64 dat) { + if(ip + 8 > _size) return; + spider::storeLE(dat, _mem + ip); + } + + void InstrReelFixed::resize(u64 new_size) { + // Special case 1 + if (new_size == _size) return; + + // Special case 2 + if (new_size == 0) { + delete[] _mem; + _mem = nullptr; + _size = 0; + _total_size = 0; + return; + } + + // 1. Allocate the new block + u8* new_mem = new u8[new_size]; + + // 2. Zero-initialize + std::memset(new_mem, 0, new_size); + + // 3. Preserve data + // If shrinking, copy 'new_size' bytes. If growing, copy 'old_size' bytes. + u64 bytes_to_copy = (new_size < _size) ? new_size : _size; + + // 3.1 Previous size could be zero, where _mem would be null + if (_mem != nullptr) { + std::copy(_mem, _mem + bytes_to_copy, new_mem); + } + + // 4. Swap and Clean up + delete[] _mem; + _mem = new_mem; + _size = new_size; + _total_size = new_size; + } + +} diff --git a/src/spider/runtime/cpu/InstrReelFixed.hpp b/src/spider/runtime/cpu/InstrReelFixed.hpp new file mode 100644 index 0000000..dd8f4ec --- /dev/null +++ b/src/spider/runtime/cpu/InstrReelFixed.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +namespace spider { + + /** + * Implements an instruction reel. + */ + class InstrReelFixed : public InstrReel { + public: + + InstrReelFixed(u64 length); + + InstrReelFixed(const u8* data, u64 length); + + InstrReelFixed(const InstrReelFixed& copy); + + InstrReelFixed(InstrReelFixed&& move) noexcept; + + virtual ~InstrReelFixed(); + + public: + + InstrReelFixed& operator=(const InstrReelFixed& copy); + + InstrReelFixed& operator=(InstrReelFixed&& move) noexcept; + + public: + + void at(u64 ip, u8 dat); + + void at(u64 ip, u16 dat); + + void at(u64 ip, u32 dat); + + void at(u64 ip, u64 dat); + + void resize(u64 new_size); + + }; + +} diff --git a/src/spider/runtime/debug/LiveDebug.cpp b/src/spider/runtime/debug/LiveDebug.cpp index 56b23be..f1301d4 100644 --- a/src/spider/runtime/debug/LiveDebug.cpp +++ b/src/spider/runtime/debug/LiveDebug.cpp @@ -2,33 +2,45 @@ #include #include +#include #include #include - +#include +#include +#include +#include +#include namespace spider { void drawHead(Terminal& t) { t.move(1, 1) - .style(Terminal::RESET) - .print(" Spider Runtime Live Debug ") - .style(Terminal::RESET).print(" | ") - .style(Terminal::FG_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(" ") - .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_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, CPU& cpu) { - i32 r = 7, c = 1; - t.drawBox(r, c, 35, 18, "CPU"); + 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", @@ -36,47 +48,335 @@ namespace spider { "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "RF", "RI", "RS", "RZ", - "RE", "RN", "RV", "RM" + "RE", "RN", "RV", "RM", + "ALU0", "ALU1" + }; + const std::string alt[] = { + Terminal::FG_WHITE, + Terminal::FG_B_BLACK, }; r++; c++; - - - for(i32 i = 0; i < 8; i++) { + 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, i = 8; i < 12; j++, i++) { + t.style(alt[j & 1]); + t.move(r + j * 2, c); + printU64Hex(*sys_regs[i * 2]); + t.move(r + j * 2, c + 17); + printU64Hex(*sys_regs[i * 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(); + } + + i32 addressWidth(isize ramSize) { + if (ramSize == 0) return 1; + isize maxAddr = ramSize - 1; + i32 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, i32 x, i32 y, i32 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 (int i = 0; i < trackHeight; ++i) { + term.move(y + i, 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 + i32 thumbOffset = i32(ratio * (trackHeight - 1)); + + // 3. Draw the Thumb (Full Block: █) + term.move(y + thumbOffset, 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 + i32 y = 3; + i32 height = 36; + + // 2. Configuration for the hex layout + int addrWidth = addressWidth(ram.size()); + int bytesPerRow = 8; + int displayRows = height - 2; // Subtract top/bottom borders + i32 width = (2 + 2 + 16 + 7 + 3 + 8 + 4) + addrWidth; + i32 x = 37; + + // create box + term.drawBox(y, x, width, 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 (int i = 0; i < displayRows; ++i) { + isize currentRowAddr = scrollPos + (i * bytesPerRow); + + // address lock + if (currentRowAddr >= ram.size()) { + term.move(y + 1 + i, 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(addrWidth) << currentRowAddr << " "; + + // Hex Bytes + std::string asciiPart = ""; + for (int 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(y + 1 + i, 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); + 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, runtime.cpu); - bool running = true; - int c = 1; + // 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 - u8 k = t.getKey(); - switch (k) { + 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; default: - t.move(5, c++); - t.print("A"); - t.flush(); break; } + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } t.altbuff(false).println("Stopped Spider live debug.").flush(); diff --git a/src/spider/runtime/memory/ByteArray.cpp b/src/spider/runtime/memory/ByteArray.cpp new file mode 100644 index 0000000..507fa67 --- /dev/null +++ b/src/spider/runtime/memory/ByteArray.cpp @@ -0,0 +1,74 @@ +#include "ByteArray.hpp" + +#include + +namespace spider { + + ByteArray::ByteArray(isize length) : _mem(nullptr), _size(length) { + if (_size > 0) { + _mem = new u8[_size]; + std::memset(_mem, 0, _size); + } + } + + ByteArray::ByteArray(const ByteArray& other) : _mem(new u8[other._size]), _size(other._size) { + std::copy(other._mem, other._mem + _size, _mem); + } + + ByteArray::ByteArray(ByteArray&& other) noexcept : _mem(other._mem), _size(other._size) { + other._mem = nullptr; + other._size = 0; + } + + ByteArray::~ByteArray() { + delete[] _mem; + } + + ByteArray& ByteArray::operator=(const ByteArray& other) { + if (this == &other) return *this; // lock self + + u8* new_mem = new u8[other._size]; + std::copy(other._mem, other._mem + other._size, new_mem); + + delete[] _mem; + _mem = new_mem; + _size = other._size; + + return *this; + } + + ByteArray& ByteArray::operator=(ByteArray&& other) noexcept { + if (this == &other) return *this; // lock self + + delete[] _mem; + + _mem = other._mem; // time to steal! + _size = other._size; + + other._mem = nullptr; // leave as a husk + other._size = 0; + + return *this; + } + + u8& ByteArray::operator[](isize index) { + return _mem[index]; + } + + u8 ByteArray::operator[](isize index) const { + return _mem[index]; + } + + u8* ByteArray::data() { + return _mem; + } + + const u8* ByteArray::data() const { + return _mem; + } + + isize ByteArray::size() const { + return _size; + } + +} diff --git a/src/spider/runtime/memory/ByteArray.hpp b/src/spider/runtime/memory/ByteArray.hpp new file mode 100644 index 0000000..69ca2e7 --- /dev/null +++ b/src/spider/runtime/memory/ByteArray.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +namespace spider { + + /** + * A general purpose byte array + * with RAII semantics. + */ + class ByteArray { + private: + + u8* _mem; + isize _size; + + public: + + ByteArray(isize length); + + ByteArray(const ByteArray& other); + + ByteArray(ByteArray&& other) noexcept; + + ~ByteArray(); + + public: + + ByteArray& operator=(const ByteArray& other); + + ByteArray& operator=(ByteArray&& other) noexcept; + + public: + + u8& operator[](isize index); + + u8 operator[](isize index) const; + + public: + + u8* data(); + + const u8* data() const; + + isize size() const; + + }; + +} diff --git a/src/spider/runtime/memory/RAM.cpp b/src/spider/runtime/memory/RAM.cpp index c47d7f2..5c65303 100644 --- a/src/spider/runtime/memory/RAM.cpp +++ b/src/spider/runtime/memory/RAM.cpp @@ -111,4 +111,20 @@ namespace spider { return _size; } + u8* RAM::begin() { + return this->_mem; + } + + u8* RAM::end() { + return this->_mem + this->_size; + } + + const u8* RAM::begin() const { + return this->_mem; + } + + const u8* RAM::end() const { + return this->_mem + this->_size; + } + } diff --git a/src/spider/runtime/memory/RAM.hpp b/src/spider/runtime/memory/RAM.hpp index 6df9cdc..c62cd46 100644 --- a/src/spider/runtime/memory/RAM.hpp +++ b/src/spider/runtime/memory/RAM.hpp @@ -49,6 +49,16 @@ namespace spider { u64 size() const; + public: + + u8* begin(); + + u8* end(); + + const u8* begin() const; + + const u8* end() const; + }; } \ No newline at end of file diff --git a/src/spider/runtime/memory/Types.hpp b/src/spider/runtime/memory/Types.hpp new file mode 100644 index 0000000..7d250ee --- /dev/null +++ b/src/spider/runtime/memory/Types.hpp @@ -0,0 +1,248 @@ +#pragma once + +#include +#include + +#if __cplusplus >= 202002L + #include +#endif + +#if defined(SPIDER_COMPILER_MSVC) + #include +#endif + +namespace spider { + + /* + * This file contains cross platform type juggling. + * Optimized for each case. + * + * General assumtions is that unsigned and signed + * integers are the exact same bit representation. + * + * Floats to and from integers is the focus when + * juggling. + * + * Additionally, provides help selecting a specific + * type from raw bytes. + */ + + // Utilities // + +#if __cplusplus >= 202002L + using std::bit_cast; +#else + template + inline To bit_cast(const From& src) { + static_assert(sizeof(To) == sizeof(From), "bit_cast size mismatch"); + To dst; + std::memcpy(&dst, &src, sizeof(To)); + return dst; + } +#endif + + template + inline T byteswap(T v) { + static_assert(std::is_integral::value, "byteswap requires integral type"); + + using U = std::make_unsigned_t; + U u = static_cast(v); + + if constexpr (sizeof(T) == 1) { + return v; + } else if constexpr (sizeof(T) == 2) { + +#if defined(SPIDER_COMPILER_MSVC) + u = _byteswap_ushort(u); +#elif defined(SPIDER_COMPILER_GCC_LIKE) + u = __builtin_bswap16(u); +#else + u = (u >> 8) | (u << 8); +#endif + + } else if constexpr (sizeof(T) == 4) { + +#if defined(SPIDER_COMPILER_MSVC) + u = _byteswap_ulong(u); +#elif defined(SPIDER_COMPILER_GCC_LIKE) + u = __builtin_bswap32(u); +#else + u = + ((u & 0x000000FFu) << 24) | + ((u & 0x0000FF00u) << 8) | + ((u & 0x00FF0000u) >> 8) | + ((u & 0xFF000000u) >> 24); +#endif + + } else if constexpr (sizeof(T) == 8) { + +#if defined(SPIDER_COMPILER_MSVC) + u = _byteswap_uint64(u); +#elif defined(SPIDER_COMPILER_GCC_LIKE) + u = __builtin_bswap64(u); +#else + u = + ((u & 0x00000000000000FFull) << 56) | + ((u & 0x000000000000FF00ull) << 40) | + ((u & 0x0000000000FF0000ull) << 24) | + ((u & 0x00000000FF000000ull) << 8) | + ((u & 0x000000FF00000000ull) >> 8) | + ((u & 0x0000FF0000000000ull) >> 24) | + ((u & 0x00FF000000000000ull) >> 40) | + ((u & 0xFF00000000000000ull) >> 56); +#endif + + } else { + // Generic fallback (rare: non 1/2/4/8-byte types) + U result = 0; + for (size_t i = 0; i < sizeof(T); ++i) { + result |= ((u >> (i * 8)) & 0xFF) << ((sizeof(T) - 1 - i) * 8); + } + u = result; + } + + return static_cast(u); + } + + // Store Big Endian // + + template + inline void storeBE(T n, u8* bytes) { + static_assert(std::is_trivially_copyable::value); +#if SPIDER_BIG_ENDIAN + std::memcpy(bytes, &n, sizeof(T)); +#endif +#if SPIDER_LITTLE_ENDIAN + T tmp = byteswap(n); + std::memcpy(bytes, &tmp, sizeof(T)); +#endif +#if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN + for (size_t i = 0; i < sizeof(T); ++i) { + bytes[i] = static_cast( + (static_cast>(n) >> ((sizeof(T) - 1 - i) * 8)) & 0xFF + ); + } +#endif + } + + template<> + inline void storeBE(f32 n, u8* bytes) { + u32 tmp = bit_cast(n); + storeBE(tmp, bytes); + } + + template<> + inline void storeBE(f64 n, u8* bytes) { + u64 tmp = bit_cast(n); + storeBE(tmp, bytes); + } + + // Load Big Endian // + + template + inline void loadBE(T* n, const u8* bytes) { + static_assert(std::is_trivially_copyable::value); +#if SPIDER_BIG_ENDIAN + std::memcpy(n, bytes, sizeof(T)); +#endif +#if SPIDER_LITTLE_ENDIAN + T tmp; + std::memcpy(&tmp, bytes, sizeof(T)); + *n = bswap(tmp); +#endif +#if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN + using U = std::make_unsigned_t; + U result = 0; + for (size_t i = 0; i < sizeof(T); ++i) { + result |= static_cast(bytes[i]) << ((sizeof(T) - 1 - i) * 8); + } + *n = static_cast(result); +#endif + } + + template<> + inline void loadBE(f32* n, const u8* bytes) { + u32 tmp; + loadBE(&tmp, bytes); + *n = bit_cast(tmp); + } + + template<> + inline void loadBE(f64* n, const u8* bytes) { + u64 tmp; + loadBE(&tmp, bytes); + *n = bit_cast(tmp); + } + + // Store Little Endian // + + template + inline void storeLE(T n, u8* bytes) { + static_assert(std::is_trivially_copyable::value); +#if SPIDER_BIG_ENDIAN + T tmp = byteswap(n); + std::memcpy(bytes, &tmp, sizeof(T)); +#endif +#if SPIDER_LITTLE_ENDIAN + std::memcpy(bytes, &n, sizeof(T)); +#endif +#if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN + for (size_t i = 0; i < sizeof(T); ++i) { + bytes[i] = static_cast( + (static_cast>(n) >> (i * 8)) & 0xFF + ); + } +#endif + } + + template<> + inline void storeLE(f32 n, u8* bytes) { + u32 tmp = bit_cast(n); + storeLE(tmp, bytes); + } + + template<> + inline void storeLE(f64 n, u8* bytes) { + u64 tmp = bit_cast(n); + storeLE(tmp, bytes); + } + + // Load Little Endian // + + template + inline void loadLE(T* n, const u8* bytes) { + static_assert(std::is_trivially_copyable::value); +#if SPIDER_BIG_ENDIAN + std::memcpy(n, bytes, sizeof(T)); +#endif +#if SPIDER_LITTLE_ENDIAN + T tmp; + std::memcpy(&tmp, bytes, sizeof(T)); + *n = bswap(tmp); +#endif +#if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN + using U = std::make_unsigned_t; + U result = 0; + for (size_t i = 0; i < sizeof(T); ++i) { + result |= static_cast(bytes[i]) << (i * 8); + } + *n = static_cast(result); +#endif + } + + template<> + inline void loadLE(f32* n, const u8* bytes) { + u32 tmp; + loadLE(&tmp, bytes); + *n = bit_cast(tmp); + } + + template<> + inline void loadLE(f64* n, const u8* bytes) { + u64 tmp; + loadLE(&tmp, bytes); + *n = bit_cast(tmp); + } + +} + diff --git a/src/spider/runtime/native/machine.hpp b/src/spider/runtime/native/machine.hpp index 2b6fe1f..d48186c 100644 --- a/src/spider/runtime/native/machine.hpp +++ b/src/spider/runtime/native/machine.hpp @@ -58,7 +58,6 @@ // (not used now) // // ========================================================== // - // Find out what compiler the user is using #if defined(__clang__) #define SPIDER_COMPILER_CLANG diff --git a/src/spider/runtime/util/Terminal.cpp b/src/spider/runtime/util/Terminal.cpp index 0883191..52eb4c8 100644 --- a/src/spider/runtime/util/Terminal.cpp +++ b/src/spider/runtime/util/Terminal.cpp @@ -17,10 +17,45 @@ namespace spider { + // Style // + const char* Terminal::RESET = "\033[0m"; + const char* Terminal::BOLD = "\033[1m"; + const char* Terminal::ITALIC = "\033[3m"; + const char* Terminal::FAINT = "\033[2m"; + const char* Terminal::STRIKE = "\033[9m"; + + // Foreground // + const char* Terminal::FG_BLACK = "\033[30m"; const char* Terminal::FG_B_BLACK = "\033[90m"; + const char* Terminal::FG_RED = "\033[31m"; const char* Terminal::FG_B_RED = "\033[91m"; + const char* Terminal::FG_GREEN = "\033[32m"; const char* Terminal::FG_B_GREEN = "\033[92m"; + const char* Terminal::FG_YELLOW = "\033[33m"; const char* Terminal::FG_B_YELLOW = "\033[93m"; + const char* Terminal::FG_BLUE = "\033[34m"; const char* Terminal::FG_B_BLUE = "\033[94m"; + const char* Terminal::FG_MAGENTA = "\033[35m"; const char* Terminal::FG_B_MAGENTA = "\033[95m"; + const char* Terminal::FG_CYAN = "\033[36m"; const char* Terminal::FG_B_CYAN = "\033[96m"; + const char* Terminal::FG_WHITE = "\033[37m"; const char* Terminal::FG_B_WHITE = "\033[97m"; + + // Background // + const char* Terminal::BG_BLACK = "\033[40m"; const char* Terminal::BG_B_BLACK = "\033[100m"; + const char* Terminal::BG_RED = "\033[41m"; const char* Terminal::BG_B_RED = "\033[101m"; + const char* Terminal::BG_GREEN = "\033[42m"; const char* Terminal::BG_B_GREEN = "\033[102m"; + const char* Terminal::BG_YELLOW = "\033[43m"; const char* Terminal::BG_B_YELLOW = "\033[103m"; + const char* Terminal::BG_BLUE = "\033[44m"; const char* Terminal::BG_B_BLUE = "\033[104m"; + const char* Terminal::BG_MAGENTA = "\033[45m"; const char* Terminal::BG_B_MAGENTA = "\033[105m"; + const char* Terminal::BG_CYAN = "\033[46m"; const char* Terminal::BG_B_CYAN = "\033[106m"; + const char* Terminal::BG_WHITE = "\033[47m"; const char* Terminal::BG_B_WHITE = "\033[107m"; + Terminal::Terminal() { #if defined(SPIDER_OS_WINDOWS) + // Enable UTF-8 SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); + + // enable vtp + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwMode = 0; + GetConsoleMode(hOut, &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(hOut, dwMode); #endif } @@ -93,7 +128,7 @@ namespace spider { Terminal& Terminal::drawBox(i32 startRow, i32 startCol, i32 width, i32 height, std::string_view title) { // 1. Draw the top border move(startRow, startCol); - std::cout << "┌"; + std::cout << "┌"; for (i32 i = 0; i < width - 2; ++i) std::cout << "─"; std::cout << "┐"; @@ -116,7 +151,7 @@ namespace spider { move(startRow, startCol + (width - title.size() - 2) / 2); std::cout << " " << title << " "; } - + std::cout.flush(); return *this; } @@ -137,7 +172,7 @@ namespace spider { return *this; } - std::pair Terminal::size() { + std::pair Terminal::getSize() { std::pair pair; #if defined(SPIDER_OS_WINDOWS) CONSOLE_SCREEN_BUFFER_INFO csbi; @@ -171,6 +206,7 @@ namespace spider { case 80: return Terminal::DOWN; case 75: return Terminal::LEFT; case 77: return Terminal::RIGHT; + default: return Terminal::UNKNOWN; } } if (ch == 13) return Terminal::ENTER; @@ -185,24 +221,54 @@ namespace spider { newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); - u8 chd = Terminal::UNKNOWN; - i32 ch = getchar(); - if (ch == 27) { // ESC sequence - if (getchar() == '[') { - switch (getchar()) { - case 'A': chd = Terminal::UP; break; - case 'B': chd = Terminal::DOWN; break; - case 'D': chd = Terminal::LEFT; break; - case 'C': chd = Terminal::RIGHT; break; + u8 result = Terminal::UNKNOWN; + int ch = getchar(); + + if (ch == 27) { // Potential Escape Sequence + // Use a small timeout or check if more chars are in buffer + // to distinguish between 'Esc' key and 'Arrow' sequence + // Another Win for the Win API + struct timeval tv = { 0, 10000 }; // 10ms wait + fd_set fds; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + + if (select(1, &fds, NULL, NULL, &tv) > 0) { + if (getchar() == '[') { + switch (getchar()) { + case 'A': result = Terminal::UP; break; + case 'B': result = Terminal::DOWN; break; + case 'D': result = Terminal::LEFT; break; + case 'C': result = Terminal::RIGHT; break; + } } + } else { + result = Terminal::ESC; } - } - else if (ch == 10) chd = Terminal::ENTER; - else if (ch == 127) chd = Terminal::BACKSPACE; + } else if (ch == 10) result = Terminal::ENTER; + else if (ch == 127) result = Terminal::BACKSPACE; + else result = (u8)ch; tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + return result; +#endif + } - return chd; + u8 Terminal::getKeyNb() { +#if defined(SPIDER_OS_WINDOWS) + if (_kbhit()) return getKey(); + return Terminal::UNKNOWN; +#endif +#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS) + struct timeval tv = { 0, 0 }; + fd_set fds; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + // select() returns > 0 if there is data to read + if (select(1, &fds, NULL, NULL, &tv) > 0) { + return getKey(); + } + return Terminal::UNKNOWN; #endif } diff --git a/src/spider/runtime/util/Terminal.hpp b/src/spider/runtime/util/Terminal.hpp index 3532722..108491e 100644 --- a/src/spider/runtime/util/Terminal.hpp +++ b/src/spider/runtime/util/Terminal.hpp @@ -5,43 +5,48 @@ #include #include #include -#include +#include namespace spider { class Terminal { public: - // --- Constants --- - static constexpr const char* RESET = "\033[0m"; - static constexpr const char* FG_BLACK = "\033[30m"; static constexpr const char* FG_B_BLACK = "\033[90m"; - static constexpr const char* FG_RED = "\033[31m"; static constexpr const char* FG_B_RED = "\033[91m"; - static constexpr const char* FG_GREEN = "\033[32m"; static constexpr const char* FG_B_GREEN = "\033[92m"; - static constexpr const char* FG_YELLOW = "\033[33m"; static constexpr const char* FG_B_YELLOW = "\033[93m"; - static constexpr const char* FG_BLUE = "\033[34m"; static constexpr const char* FG_B_BLUE = "\033[94m"; - static constexpr const char* FG_MAGENTA = "\033[35m"; static constexpr const char* FG_B_MAGENTA = "\033[95m"; - static constexpr const char* FG_CYAN = "\033[36m"; static constexpr const char* FG_B_CYAN = "\033[96m"; - static constexpr const char* FG_WHITE = "\033[37m"; static constexpr const char* FG_B_WHITE = "\033[97m"; + static const char* RESET; + static const char* BOLD; + static const char* ITALIC; + static const char* FAINT; + static const char* STRIKE; - static constexpr const char* BG_BLACK = "\033[40m"; static constexpr const char* BG_B_BLACK = "\033[100m"; - static constexpr const char* BG_RED = "\033[41m"; static constexpr const char* BG_B_RED = "\033[101m"; - static constexpr const char* BG_GREEN = "\033[42m"; static constexpr const char* BG_B_GREEN = "\033[102m"; - static constexpr const char* BG_YELLOW = "\033[43m"; static constexpr const char* BG_B_YELLOW = "\033[103m"; - static constexpr const char* BG_BLUE = "\033[44m"; static constexpr const char* BG_B_BLUE = "\033[104m"; - static constexpr const char* BG_MAGENTA = "\033[45m"; static constexpr const char* BG_B_MAGENTA = "\033[105m"; - static constexpr const char* BG_CYAN = "\033[46m"; static constexpr const char* BG_B_CYAN = "\033[106m"; - static constexpr const char* BG_WHITE = "\033[47m"; static constexpr const char* BG_B_WHITE = "\033[107m"; + static const char* FG_BLACK; static const char* FG_B_BLACK; + static const char* FG_RED; static const char* FG_B_RED; + static const char* FG_GREEN; static const char* FG_B_GREEN; + static const char* FG_YELLOW; static const char* FG_B_YELLOW; + static const char* FG_BLUE; static const char* FG_B_BLUE; + static const char* FG_MAGENTA; static const char* FG_B_MAGENTA; + static const char* FG_CYAN; static const char* FG_B_CYAN; + static const char* FG_WHITE; static const char* FG_B_WHITE; + + static const char* BG_BLACK; static const char* BG_B_BLACK; + static const char* BG_RED; static const char* BG_B_RED; + static const char* BG_GREEN; static const char* BG_B_GREEN; + static const char* BG_YELLOW; static const char* BG_B_YELLOW; + static const char* BG_BLUE; static const char* BG_B_BLUE; + static const char* BG_MAGENTA; static const char* BG_B_MAGENTA; + static const char* BG_CYAN; static const char* BG_B_CYAN; + static const char* BG_WHITE; static const char* BG_B_WHITE; public: - static constexpr const u8 UP = 0x1; - static constexpr const u8 DOWN = 0x2; - static constexpr const u8 LEFT = 0x3; - static constexpr const u8 RIGHT = 0x4; - static constexpr const u8 ENTER = 0x5; - static constexpr const u8 ESC = 0x6; - static constexpr const u8 BACKSPACE = 0x7; - static constexpr const u8 UNKNOWN = 0x8; + // Key Definitions (ASCII OK) // + static constexpr const u8 UP = 0x80; + static constexpr const u8 DOWN = 0x81; + static constexpr const u8 LEFT = 0x82; + static constexpr const u8 RIGHT = 0x83; + static constexpr const u8 ENTER = 0x84; + static constexpr const u8 ESC = 0x85; + static constexpr const u8 BACKSPACE = 0x86; + static constexpr const u8 UNKNOWN = 0xFF; public: @@ -119,7 +124,12 @@ namespace spider { u8 getKey(); - std::pair size(); + /** + * Get key non blocking + */ + u8 getKeyNb(); + + std::pair getSize(); public: @@ -135,6 +145,26 @@ namespace spider { return *this; } + template + Terminal& print_center(i32 width, const T& msg) { + // to string + std::ostringstream oss; + oss << msg; + std::string s = oss.str(); + + // then print + if (s.length() >= size(width)) { + std::cout << s; + } else { + i32 total_padding = width - s.length(); + i32 left_padding = total_padding / 2; + std::cout << std::string(left_padding, ' '); + std::cout << s; + std::cout << std::string(total_padding - left_padding, ' '); + } + return *this; + } + template Terminal& read(T& var) { std::cin >> var;