Context background
The Spider VM instruction set is organized in a 9-bit opcode space (512 slots), divided among team members. My assigned range covers 15 instructions: two type cast conversions and thirteen math functions.
Instructions are defined in Instr_060-07F.cpp,
which already contained the previous range (0x060–0x067) implemented by another team member.
My work begins at 0x068.
Assigned Instructions 0x068 → 0x079
Implementation Pattern how it works
Every instruction in the Spider VM follows the same three-step pattern: fetch the operand(s), perform the operation, and call the post-execution hook.
fetchOperDst(); // points _dst to the destination register _dst->_f64 = std::sin(_dst->_f64); (this->*_post)(); // post-execution hook (write-back if needed)
fetchOperDst(); // _dst ← destination fetchOperSrc(); // _src ← source _dst->_f64 = std::atan2(_dst->_f64, _src->_f64); (this->*_post)();
The register_t union
allows each register to be read as any type (_f64,
_u32,
_u64, etc.)
without casting overhead. The type modifier in the instruction header determines
which field is active.
Notable special cases:
LOGAB uses the change-of-base formula: loga(b) = ln(b) / ln(a), where Dst is b and Src is a.
ROOT uses the identity: ⁿ√x = x^(1/n),
implemented as std::pow(dst, 1.0 / src).
The full implementation in C++20:
void CPU::D2I() { fetchOperDst(); _dst->_u32 = static_cast<u32>(_dst->_f64); (this->*_post)(); } void CPU::SIN() { fetchOperDst(); _dst->_f64 = std::sin(_dst->_f64); (this->*_post)(); } void CPU::LOGAB() { fetchOperDst(); fetchOperSrc(); _dst->_f64 = std::log(_dst->_f64) / std::log(_src->_f64); (this->*_post)(); } void CPU::ROOT() { fetchOperDst(); fetchOperSrc(); _dst->_f64 = std::pow(_dst->_f64, 1.0 / _src->_f64); (this->*_post)(); }
Verification test results
A test program was written to call each instruction directly on the CPU
and verify the output against mathematically known values.
Since fetchInstr()
and execute()
are not yet implemented in the Runtime, operands are set manually before each call.
[PASS] D2L (1e12) = 1e+12 (expected 1e+12) -- Trigonometric Instructions -- [PASS] SIN(pi/2) = 1 (expected 1)
[PASS] COS(0) = 1 (expected 1)
[PASS] TAN(pi/4) = 1 (expected 1)
[PASS] ASIN(1.0) = 1.5708 (expected 1.5708)
[PASS] ACOS(1.0) = 0 (expected 0)
[PASS] ATAN(1.0) = 0.785398 (expected 0.785398)
[PASS] ATAN2(1,1) = 0.785398 (expected 0.785398) -- Exponential Instructions -- [PASS] EXP(1) = 2.71828 (expected 2.71828)
[PASS] LOG(e) = 1 (expected 1)
[PASS] LOGAB(100,10) = 2 (expected 2)
[PASS] POW(2,10) = 1024 (expected 1024)
[PASS] SQRT(9) = 3 (expected 3)
[PASS] ROOT(27,3) = 3 (expected 3)
fetchOperDst(),
fetchOperSrc(),
and imp()
as no-ops, and sets _dst and
_src manually.
This isolates the instruction logic completely from the unimplemented CPU dispatch system.