From 429596af861823eaf460f7abd8cfb436a85b2a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20De=20Gante=20P=C3=A9rez?= Date: Mon, 6 Apr 2026 12:33:43 -0600 Subject: [PATCH] Implement instructions 0x03C-0x053, add flag constants and fix execute dispatch --- src/spider/runtime/Runtime.cpp | 4 +- src/spider/runtime/cpu/CPU.cpp | 6 +- src/spider/runtime/cpu/CPU.hpp | 8 + src/spider/runtime/instr/Instr_020-03F.cpp | 63 ++++++- src/spider/runtime/instr/Instr_040-05F.cpp | 185 +++++++++++++++++++-- 5 files changed, 244 insertions(+), 22 deletions(-) diff --git a/src/spider/runtime/Runtime.cpp b/src/spider/runtime/Runtime.cpp index ddb008e..7188517 100644 --- a/src/spider/runtime/Runtime.cpp +++ b/src/spider/runtime/Runtime.cpp @@ -17,8 +17,10 @@ namespace spider { // Stepping/Running the Machine // void Runtime::step() { + // fetchInstr() decodes the opcode, addressing mode and type siz cpu.fetchInstr(); - // TODO: Call instruction + // execute() completes the fetch-decode-execute cycle by calling the correct instruction method based on the opcode. + cpu.execute(); } void Runtime::step(u64 n) { diff --git a/src/spider/runtime/cpu/CPU.cpp b/src/spider/runtime/cpu/CPU.cpp index 1a796b1..f43506d 100644 --- a/src/spider/runtime/cpu/CPU.cpp +++ b/src/spider/runtime/cpu/CPU.cpp @@ -95,8 +95,12 @@ namespace spider { _addrm++; } + /** + instrMap[] is the correct 512-entry dispatch + table that maps operation codes to instruction methods. + */ void CPU::execute() { - (this->*(CPU::addrModes[_opcode]))(); + (this->*(CPU::instrMap[_opcode]))(); } // Addressing Modes // diff --git a/src/spider/runtime/cpu/CPU.hpp b/src/spider/runtime/cpu/CPU.hpp index a2e8aa5..948ec2d 100644 --- a/src/spider/runtime/cpu/CPU.hpp +++ b/src/spider/runtime/cpu/CPU.hpp @@ -15,6 +15,14 @@ namespace spider { static constexpr const u64 FLAG_INTERRUPT_REQUEST = 0b0000000000000000000000000000000000000000000000000000000000000100; static constexpr const u64 FLAG_EXCEPTION = 0b0000000000000000000000000000000000000000000000000000000000001000; static constexpr const u64 FLAG_MEMORY_MODE = 0b0000000000000000000000000000000000000000000000000000000000110000; + static constexpr const u64 FLAG_EXT_INT_DISABLE = 0b0000000000000000000000000000000000000000000000000000000010000000; // bit 7 + static constexpr const u64 FLAG_EQUAL = 0b0000000000000000000000000000000000000000000000000000010000000000; // bit 10 + static constexpr const u64 FLAG_EPSILON_ENABLE = 0b0000000000000000000000000000000000000000000000000001000000000000; // bit 12 + static constexpr const u64 FLAG_HOTSWAP_SIGNAL = 0b0000000000000000000000000000000000000000000000010000000000000000; // bit 16 + static constexpr const u64 FLAG_USER_A = 0b0000000000000000000000000000000000000000000100000000000000000000; // bit 20 + static constexpr const u64 FLAG_USER_B = 0b0000000000000000000000000000000000000000001000000000000000000000; // bit 21 + static constexpr const u64 FLAG_USER_C = 0b0000000000000000000000000000000000000000010000000000000000000000; // bit 22 + static constexpr const u64 FLAG_USER_D = 0b0000000000000000000000000000000000000000100000000000000000000000; // bit 23 public: // Map of addressing modes & Instructions diff --git a/src/spider/runtime/instr/Instr_020-03F.cpp b/src/spider/runtime/instr/Instr_020-03F.cpp index 40166f7..3fcb420 100644 --- a/src/spider/runtime/instr/Instr_020-03F.cpp +++ b/src/spider/runtime/instr/Instr_020-03F.cpp @@ -154,20 +154,75 @@ namespace spider { // TODO: Implement JIF } + /** + * 0x03C — Jump Relative. + * Adds a signed offset (Dst) to the instruction register. + */ void CPU::JMR() { - // TODO: Implement JMR + fetchOperDst(); + i64 offset; + switch (_size) { + case 0b00: offset = static_cast(_dst->_i8); break; // 1 byte + case 0b01: offset = static_cast(_dst->_i16); break; // 2 bytes + case 0b10: offset = static_cast(_dst->_i32); break; // 4 bytes + case 0b11: offset = _dst->_i64; break; // 8 bytes + } + RI = static_cast(static_cast(RI) + offset); } + /** + * 0x03D — Jump Relative if Equal. + * Adds a signed offset (Dst) to RI only if the Equal flag (bit 10) is set. + */ void CPU::JER() { - // TODO: Implement JER + fetchOperDst(); + if (RF & CPU::FLAG_EQUAL) { + i64 offset; + switch (_size) { + case 0b00: offset = static_cast(_dst->_i8); break; + case 0b01: offset = static_cast(_dst->_i16); break; + case 0b10: offset = static_cast(_dst->_i32); break; + case 0b11: offset = _dst->_i64; break; + } + RI = static_cast(static_cast(RI) + offset); + } } + /** + * 0x03E — Jump Relative if Not Equal. + * Adds a signed offset (Dst) to RI only if the Equal flag (bit 10) is cleared. + */ void CPU::JNR() { - // TODO: Implement JNR + fetchOperDst(); + if (!(RF & CPU::FLAG_EQUAL)) { + i64 offset; + switch (_size) { + case 0b00: offset = static_cast(_dst->_i8); break; + case 0b01: offset = static_cast(_dst->_i16); break; + case 0b10: offset = static_cast(_dst->_i32); break; + case 0b11: offset = _dst->_i64; break; + } + RI = static_cast(static_cast(RI) + offset); + } } + /** + * 0x03F — Jump Relative if Src is true. + * Adds a signed offset (Dst) to RI only if Src is booleanly true (non-zero). + */ void CPU::JIR() { - // TODO: Implement JIR + fetchOperSrc(); + fetchOperDst(); + if (_src->_u64 != 0) { + i64 offset; + switch (_size) { + case 0b00: offset = static_cast(_dst->_i8); break; + case 0b01: offset = static_cast(_dst->_i16); break; + case 0b10: offset = static_cast(_dst->_i32); break; + case 0b11: offset = _dst->_i64; break; + } + RI = static_cast(static_cast(RI) + offset); + } } } diff --git a/src/spider/runtime/instr/Instr_040-05F.cpp b/src/spider/runtime/instr/Instr_040-05F.cpp index f0c8829..de96aa6 100644 --- a/src/spider/runtime/instr/Instr_040-05F.cpp +++ b/src/spider/runtime/instr/Instr_040-05F.cpp @@ -4,71 +4,224 @@ */ #include +#include namespace spider { +// ── 0x040 — SFB: Store (User) Flag Bit ───────────────────────────── + // bool(Src) -> User Flag at index (Dst & 0x3) + // Flags A-D are bits 20-23 of RF. void CPU::SFB() { - // TODO: Implement SFB + fetchOperSrc(); + fetchOperDst(); + u8 flag_idx = _dst->_u8 & 0x3; + u64 flag_bit = CPU::FLAG_USER_A << flag_idx; + if (_src->_u64 != 0) { + RF |= flag_bit; + } else { + RF &= ~flag_bit; + } } + // ── 0x041 — LFB: Load (User) Flag Bit ────────────────────────────── + // User Flag at index (Src & 0x3) -> Dst void CPU::LFB() { - // TODO: Implement LFB + fetchOperSrc(); + fetchOperDst(); + u8 flag_idx = _src->_u8 & 0x3; + u64 flag_bit = CPU::FLAG_USER_A << flag_idx; + _dst->_u64 = (RF & flag_bit) ? 1 : 0; + (this->*_post)(); } + // ── 0x042 — JUF: Jump absolute if User Flag is true ──────────────── + // Dst -> RI IF User Flag at index (Src & 0x3) is set void CPU::JUF() { - // TODO: Implement JUF + fetchOperSrc(); + fetchOperDst(); + u8 flag_idx = _src->_u8 & 0x3; + u64 flag_bit = CPU::FLAG_USER_A << flag_idx; + if (RF & flag_bit) { + RI = _dst->_u64; + } } + // ── 0x043 — JUR: Jump relative if User Flag is true ──────────────── + // Dst + RI -> RI IF User Flag at index (Src & 0x3) is set void CPU::JUR() { - // TODO: Implement JUR + fetchOperSrc(); + fetchOperDst(); + u8 flag_idx = _src->_u8 & 0x3; + u64 flag_bit = CPU::FLAG_USER_A << flag_idx; + if (RF & flag_bit) { + i64 offset; + switch (_size) { + case 0b00: offset = static_cast(_dst->_i8); break; + case 0b01: offset = static_cast(_dst->_i16); break; + case 0b10: offset = static_cast(_dst->_i32); break; + case 0b11: offset = _dst->_i64; break; + } + RI = static_cast(static_cast(RI) + offset); + } } + // ── 0x044 — PUSH: Push to stack ───────────────────────────────────── + // Dst -> RAM[RS], RS += (1 << _size) + // The stack grows upward from the bottom of memory. void CPU::PUSH() { - // TODO: Implement PUSH + fetchOperDst(); + u8 bytes = 1 << _size; + for (u8 i = 0; i < bytes; i++) { + _ram->at(RS + i) = (*_dst)[i]; + } + RS += bytes; } + // ── 0x045 — POP: Pop from stack ───────────────────────────────────── + // RS -= (1 << _size), RAM[RS] -> Dst void CPU::POP() { - // TODO: Implement POP + fetchOperDst(); + u8 bytes = 1 << _size; + RS -= bytes; + _ram->loadRegister(RS, _size, _dst); + (this->*_post)(); } + // ── 0x046 — ALLOC: Allocate to heap ───────────────────────────────── + // Stub: returns 0 (null) until proper heap management is implemented. void CPU::ALLOC() { - // TODO: Implement ALLOC + fetchOperDst(); + // TODO: Proper heap allocation with gap tracking. + _dst->_u64 = 0; + (this->*_post)(); } + // ── 0x047 — HFREE: Delete from heap ───────────────────────────────── + // Stub: no-op until proper heap management is implemented. void CPU::HFREE() { - // TODO: Implement HFREE + fetchOperDst(); + // TODO: Proper heap deallocation. } + //──────────────────────────────────────────────────────── + + // ── 0x04A — CALL: Call function at instruction index ──────────────── + // Minimal version: saves RZ and RI to the stack, + // updates the stack base, then jumps to Dst. + // The calling convention (parameter passing, caller-saved + // registers) is the compiler's responsibility. void CPU::CALL() { - // TODO: Implement CALL + fetchOperDst(); + u64 target = _dst->_u64; + + // Push old stack base (RZ) — always 8 bytes + register_t rz_save; + rz_save._u64 = RZ; + for (u8 i = 0; i < 8; i++) { + _ram->at(RS + i) = rz_save[i]; + } + RS += 8; + + // Push return address (RI) — always 8 bytes + register_t ri_save; + ri_save._u64 = RI; + for (u8 i = 0; i < 8; i++) { + _ram->at(RS + i) = ri_save[i]; + } + RS += 8; + + // New stack base is the current stack top + RZ = RS; + + // Jump to target + RI = target; } + // ── 0x04B — RET: Return from a function ───────────────────────────── + // Undoes what CALL did: restores RI and RZ from the stack. void CPU::RET() { - // TODO: Implement RET + // Wind the stack back to the current frame base + RS = RZ; + + // Pop return address + RS -= 8; + register_t ri_restore; + _ram->loadRegister(RS, 0b11, &ri_restore); + RI = ri_restore._u64; + + // Pop previous stack base + RS -= 8; + register_t rz_restore; + _ram->loadRegister(RS, 0b11, &rz_restore); + RZ = rz_restore._u64; } + // ── 0x04C — EDI: Enable/Disable External Interrupts ──────────────── + // bool(Dst) == true -> enable (clear the disable bit) + // bool(Dst) == false -> disable (set the disable bit) void CPU::EDI() { - // TODO: Implement EDI + fetchOperDst(); + if (_dst->_u64 != 0) { + RF &= ~CPU::FLAG_EXT_INT_DISABLE; + } else { + RF |= CPU::FLAG_EXT_INT_DISABLE; + } } + // ── 0x04D — SHSS: Set Hotswap Signal Bit ──────────────────────────── + // bool(Dst) -> Hotswap Signal flag (bit 16 of RF) void CPU::SHSS() { - // TODO: Implement SHSS + fetchOperDst(); + if (_dst->_u64 != 0) { + RF |= CPU::FLAG_HOTSWAP_SIGNAL; + } else { + RF &= ~CPU::FLAG_HOTSWAP_SIGNAL; + } } + //──────────────────────────────────────────────────────────────────────────────────── + + // ── 0x050 — FLI: Float Load Immediate ─────────────────────────────── + // The addressing mode already loads the raw bytes into Dst. + // _size == 0b10 for f32, 0b11 for f64. void CPU::FLI() { - // TODO: Implement FLI + fetchOperDst(); + (this->*_post)(); } + // ── 0x051 — FNEG: Float negate ────────────────────────────────────── + // -Dst -> Dst void CPU::FNEG() { - // TODO: Implement FNEG + fetchOperDst(); + switch (_size) { + case 0b10: _dst->_f32 = -_dst->_f32; break; + case 0b11: _dst->_f64 = -_dst->_f64; break; + } + (this->*_post)(); } + // ── 0x052 — FADD: Float add ───────────────────────────────────────── + // Dst + Src -> Dst void CPU::FADD() { - // TODO: Implement FADD + fetchOperSrc(); + fetchOperDst(); + switch (_size) { + case 0b10: _dst->_f32 += _src->_f32; break; + case 0b11: _dst->_f64 += _src->_f64; break; + } + (this->*_post)(); } + // ── 0x053 — FSUB: Float subtract ──────────────────────────────────── + // Dst - Src -> Dst void CPU::FSUB() { - // TODO: Implement FSUB + fetchOperSrc(); + fetchOperDst(); + switch (_size) { + case 0b10: _dst->_f32 -= _src->_f32; break; + case 0b11: _dst->_f64 -= _src->_f64; break; + } + (this->*_post)(); } void CPU::FMUL() {