diff --git a/blog-esp32.html b/blog-esp32.html new file mode 100644 index 0000000..34ba8da --- /dev/null +++ b/blog-esp32.html @@ -0,0 +1,442 @@ + + + + + +Running Spider on ESP32 — Diego López + + + + + + +
+
+
Spider Lang · Internship 2026
+

Running Spider
on ESP32

+

+ How the Spider Runtime was cross-compiled for the Xtensa architecture + and flashed onto physical hardware using ESP-IDF. +

+
Diego Alberto López  ·  Build System  ·  April 2026
+
+
+ +
+ + +
+script.spider + ↓ Spider Compiler +bytecode.spd ← Spider's own format, not x86, not Xtensa + +Spider Runtime (C++) + ↓ xtensa-esp-elf-g++ ← ESP-IDF toolchain +spider_esp32.bin + ↓ esptool.py +ESP32 flash memory ← Runtime lives here permanently +
+ +

+ Spider's bytecode is a neutral format that belongs to no specific processor. + The toolchain's job is not to convert bytecode — it compiles the + C++ source of the Runtime itself into Xtensa machine code. + Once flashed, the Runtime lives in the ESP32's flash memory and + interprets Spider bytecode at runtime. +

+ + +
+
01
+

Prerequisites

+
+ +

Install ESP-IDF v5.x and add the Xtensa toolchain to your PATH:

+ +
# Add to ~/.bashrc to make it permanent
+export PATH=$PATH:/c/Espressif/tools/xtensa-esp-elf/esp-14.2.0_20251107/xtensa-esp-elf/bin
+ + +
+
02
+

Compile flags for ESP32

+
+ +

+ The Runtime uses compile-time platform detection via distro_mcu.hpp. + These flags tell it which hardware it is targeting: +

+ +
+
-DESP32
+
Activates ESP32 detection in distro_mcu.hpp
+
-DSPIDER_DISTRO_MICRO
+
Enables microcontroller mode (reduced memory footprint)
+
-DSPIDER_OS_NONE
+
Declares bare-metal environment, no OS
+
-mlongcalls
+
Required for Xtensa's memory layout and call distances
+
-fno-exceptions -fno-rtti
+
Disable C++ features unavailable on bare-metal
+
-O0
+
No optimizations — faster compilation during development
+
+ + +
+
03
+

Build with ESP-IDF (CMake)

+
+ +

+ The bare-metal Makefile approach caused boot loops because the ESP32 bootloader + expects a specific binary format. The correct approach is to use ESP-IDF's + CMake build system, which generates the bootloader, partition table, and app + binary automatically. +

+ +
# From spider-micro-buildtools/esp32/idf_project/
+idf.py set-target esp32
+idf.py build
+idf.py -p COM6 flash monitor
+ +

The CMakeLists.txt registers the Spider Runtime source files as a component:

+ +
set(SPIDER_RUNTIME_SRC
+    "${CMAKE_CURRENT_SOURCE_DIR}/../../../../spider-runtime/src"
+)
+
+idf_component_register(
+    SRCS
+        "main.cpp"
+        "${SPIDER_RUNTIME_SRC}/spider/runtime/Runtime.cpp"
+        "${SPIDER_RUNTIME_SRC}/spider/runtime/cpu/CPU.cpp"
+        ... (all Runtime source files)
+    PRIV_INCLUDE_DIRS "${SPIDER_RUNTIME_SRC}"
+)
+ +
+ Note: Relative paths using CMAKE_CURRENT_SOURCE_DIR + are used to avoid exposing personal directory structure in the repository. +
+ + +
+
04
+

app_main — The ESP32 entry point

+
+ +

+ ESP-IDF requires app_main() + instead of the standard main(). + The Spider Runtime is initialized here with a fixed amount of RAM: +

+ +
#include "esp_log.h"
+#include <spider/runtime/Runtime.hpp>
+
+static const char* TAG = "Spider";
+
+extern "C" void app_main(void) {
+    ESP_LOGI(TAG, "Spider Runtime starting...");
+    spider::Runtime runtime(32 * 1024);   // 32 KB RAM
+    ESP_LOGI(TAG, "Spider Runtime initialized!");
+    ESP_LOGI(TAG, "RAM size: 32KB");
+    while(1) { vTaskDelay(pdMS_TO_TICKS(1000)); }
+}
+ + +
+
+I (274) Spider: Spider Runtime starting...
+I (274) Spider: Spider Runtime initialized!
+I (274) Spider: RAM size: 32KB
+    
+
+ + +
+
05
+

Repository structure

+
+ +

+ The build tools live in spider-micro-buildtools, + separate from the Runtime source. Both repos must be cloned at the same level: +

+ +
+Internship/ +├── spider-runtime/ ← Runtime C++ source +└── spider-micro-buildtools/ + └── esp32/ + ├── Makefile ← bare-metal reference build + ├── gen_makefile.py + ├── main_esp32.cpp + ├── README.md + └── idf_project/ ← ESP-IDF project (use this) + ├── CMakeLists.txt + └── main/ + ├── CMakeLists.txt + └── main.cpp +
+ +
+ + + + + diff --git a/blog-instrucciones.html b/blog-instrucciones.html new file mode 100644 index 0000000..d1e42d1 --- /dev/null +++ b/blog-instrucciones.html @@ -0,0 +1,551 @@ + + + + + +Implementing Spider VM Instructions — Diego López + + + + + + +
+
+ Spider VM · Instruction Set Implementation · 2026 +

Implementing
Spider VM Instructions

+

Cast conversions, trigonometric and exponential functions for the Spider Virtual Machine — opcodes 0x068 through 0x079.

+
Diego Alberto López  ·  spider-runtime  ·  April 2026
+
+
+ +
+ + +
+

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

+ +
+
Cast Conversions
+
+
+
0x068
+
D2I
+
Double (f64) → Integer (i32), truncates toward zero
+
+
+
0x069
+
D2L
+
Double (f64) → Long (i64), truncates toward zero
+
+
+
+ +
+
Trigonometric
+
+
+
0x06C
+
SIN
+
Sine — sin(Dst) → Dst
+
+
+
0x06D
+
COS
+
Cosine — cos(Dst) → Dst
+
+
+
0x06E
+
TAN
+
Tangent — tan(Dst) → Dst
+
+
+
0x06F
+
ASIN
+
Arc sine — asin(Dst) → Dst
+
+
+
0x070
+
ACOS
+
Arc cosine — acos(Dst) → Dst
+
+
+
0x071
+
ATAN
+
Arc tangent — atan(Dst) → Dst
+
+
+
0x072
+
ATAN2
+
Arc tangent 2-argument — atan2(Dst, Src) → Dst
+
+
+
+ +
+
Exponential
+
+
+
0x074
+
EXP
+
Natural exponential — eˣ → Dst
+
+
+
0x075
+
LOG
+
Natural logarithm — ln(Dst) → Dst
+
+
+
0x076
+
LOGAB
+
Logarithm base A of B — log_Src(Dst) → Dst
+
+
+
0x077
+
POW
+
Power — Dst^Src → Dst
+
+
+
0x078
+
SQRT
+
Square root — √Dst → Dst
+
+
+
0x079
+
ROOT
+
General root — Dst^(1/Src) → Dst
+
+
+
+
+ + +
+

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. +

+ +
+
Single-operand pattern
+
fetchOperDst();           // points _dst to the destination register
+_dst->_f64 = std::sin(_dst->_f64);
+(this->*_post)();          // post-execution hook (write-back if needed)
+
+ +
+
Two-operand pattern (ATAN2, LOGAB, POW, ROOT)
+
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. +

+ +
+
+ -- Cast Instructions -- + [PASS] D2I (3.9 -> 3) = 3 (expected 3)
+ [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) +
+
+ +
+ Note on testing approach: The test program uses stub implementations + of 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. +
+
+ +
+ + + + +