add ESP32 build system with xtensa-esp-elf toolchain

This commit is contained in:
2026-03-25 15:23:41 -06:00
parent 4a659b5f0d
commit 680a2fdb06
50 changed files with 4219 additions and 4091 deletions

12
.gitignore vendored
View File

@@ -1,6 +1,6 @@
# For now, ignore user builds # For now, ignore user builds
# We will eventually change to a custom # We will eventually change to a custom
# build system. # build system.
# So hold on # So hold on
/bin /bin
/out /out

36
LICENSE
View File

@@ -1,18 +1,18 @@
MIT License MIT License
Copyright (c) 2026 SpiderLang Copyright (c) 2026 SpiderLang
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions: following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software. portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE. USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,13 +1,13 @@
# spider-runtime # spider-runtime
This is the Spider runtime (aka, the virtual machine) that executes the Spider byte code. This is the Spider runtime (aka, the virtual machine) that executes the Spider byte code.
## Code Etiquette ## Code Etiquette
- Do not use \r\n - Do not use \r\n
- Do not use any encoding besides UTF-8 - Do not use any encoding besides UTF-8
- Always comment global functions, variables, classes, member variables and methods. - Always comment global functions, variables, classes, member variables and methods.
- Do not modify the autogenerated files. - Do not modify the autogenerated files.
- If using an LLM, use private mode and tell it you're working on an old modem. - If using an LLM, use private mode and tell it you're working on an old modem.
- If using an AI agent, don't. - If using an AI agent, don't.
- You need to re-run the pygen.ipynb to be up to date with the .xlsx instructions file. - You need to re-run the pygen.ipynb to be up to date with the .xlsx instructions file.
Failure to uphold the code etiquette will result in a slap in the wrist, with a hammer. Failure to uphold the code etiquette will result in a slap in the wrist, with a hammer.

56
build/esp32/Makefile Normal file
View File

@@ -0,0 +1,56 @@
# ========================================================== #
# Spider Runtime - ESP32 Build System #
# Toolchain: Espressif ESP-IDF (xtensa-esp-elf-g++) #
# ========================================================== #
CC := xtensa-esp-elf-g++
TARGET := spider_esp32.elf
SRCDIR := ../../src
BUILDDIR := bin
TARGETDIR := out
SRCEXT := cpp
OBJEXT := o
ESP_FLAGS := -DESP32 -DSPIDER_DISTRO_MICRO -DSPIDER_OS_NONE -mlongcalls -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti
CFLAGS := -Wall -std=c++20 -DSPIDER_COMPILING $(ESP_FLAGS)
LFLAGS := -Wl,--gc-sections -mlongcalls
INC := -I../../src/
# Exclude desktop-only modules
EXCLUDE := $(SRCDIR)/spider/runtime/util/Terminal.cpp \
$(SRCDIR)/spider/runtime/debug/LiveDebug.cpp \
$(SRCDIR)/spider/SpiderRuntime.cpp
# ESP32 specific entry point
EXTRA := $(SRCDIR)/spider/main_esp32.cpp
SOURCES := $(filter-out $(EXCLUDE), $(shell find $(SRCDIR) -type f -name "*.$(SRCEXT)" 2>/dev/null)) $(EXTRA)
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
all: directories $(TARGET)
@echo "Build complete: $(TARGETDIR)/$(TARGET)"
remake: cleaner all
directories:
@mkdir -p $(TARGETDIR)
@mkdir -p $(BUILDDIR)
clean:
@$(RM) -rf $(BUILDDIR)
cleaner: clean
@$(RM) -rf $(TARGETDIR)
$(TARGET): $(OBJECTS)
@echo "Linking $(TARGET)..."
$(CC) $(LFLAGS) -o $(TARGETDIR)/$(TARGET) $^
@echo "Done!"
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
@mkdir -p $(dir $@)
@echo "Compiling $<..."
$(CC) $(CFLAGS) $(INC) -c -o $@ $<
.PHONY: all remake clean cleaner

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,64 @@
tab = '\t'
dollar = '$'
content = f"""# ========================================================== #
# Spider Runtime - ESP32 Build System #
# Toolchain: Espressif ESP-IDF (xtensa-esp-elf-g++) #
# ========================================================== #
CC := xtensa-esp-elf-g++
TARGET := spider_esp32.elf
SRCDIR := ../../src
BUILDDIR := bin
TARGETDIR := out
SRCEXT := cpp
OBJEXT := o
ESP_FLAGS := -DESP32 -DSPIDER_DISTRO_MICRO -DSPIDER_OS_NONE -mlongcalls -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti
CFLAGS := -Wall -std=c++20 -DSPIDER_COMPILING {dollar}(ESP_FLAGS)
LFLAGS := -Wl,--gc-sections -mlongcalls
INC := -I../../src/
# Exclude desktop-only modules
EXCLUDE := {dollar}(SRCDIR)/spider/runtime/util/Terminal.cpp \\
{dollar}(SRCDIR)/spider/runtime/debug/LiveDebug.cpp \\
{dollar}(SRCDIR)/spider/SpiderRuntime.cpp
# ESP32 specific entry point
EXTRA := {dollar}(SRCDIR)/spider/main_esp32.cpp
SOURCES := {dollar}(filter-out {dollar}(EXCLUDE), {dollar}(shell find {dollar}(SRCDIR) -type f -name "*.{dollar}(SRCEXT)" 2>/dev/null)) {dollar}(EXTRA)
OBJECTS := {dollar}(patsubst {dollar}(SRCDIR)/%,{dollar}(BUILDDIR)/%,{dollar}(SOURCES:.{dollar}(SRCEXT)=.{dollar}(OBJEXT)))
all: directories {dollar}(TARGET)
{tab}@echo "Build complete: {dollar}(TARGETDIR)/{dollar}(TARGET)"
remake: cleaner all
directories:
{tab}@mkdir -p {dollar}(TARGETDIR)
{tab}@mkdir -p {dollar}(BUILDDIR)
clean:
{tab}@{dollar}(RM) -rf {dollar}(BUILDDIR)
cleaner: clean
{tab}@{dollar}(RM) -rf {dollar}(TARGETDIR)
{dollar}(TARGET): {dollar}(OBJECTS)
{tab}@echo "Linking {dollar}(TARGET)..."
{tab}{dollar}(CC) {dollar}(LFLAGS) -o {dollar}(TARGETDIR)/{dollar}(TARGET) {dollar}^
{tab}@echo "Done!"
{dollar}(BUILDDIR)/%.{dollar}(OBJEXT): {dollar}(SRCDIR)/%.{dollar}(SRCEXT)
{tab}@mkdir -p {dollar}(dir {dollar}@)
{tab}@echo "Compiling {dollar}<..."
{tab}{dollar}(CC) {dollar}(CFLAGS) {dollar}(INC) -c -o {dollar}@ {dollar}<
.PHONY: all remake clean cleaner
"""
with open('Makefile', 'w', newline='\n') as f:
f.write(content)
print("Makefile generated successfully!")

Binary file not shown.

128
makefile
View File

@@ -1,65 +1,65 @@
#Compiler and Linker #Compiler and Linker
CC := g++ CC := g++
#The Target Binary Program #The Target Binary Program
TARGET := out.exe TARGET := out.exe
#The Directories, Source, Includes, Objects, Binary and Resources #The Directories, Source, Includes, Objects, Binary and Resources
SRCDIR := src SRCDIR := src
BUILDDIR := bin BUILDDIR := bin
TARGETDIR := out TARGETDIR := out
SRCEXT := cpp SRCEXT := cpp
DEPEXT := d DEPEXT := d
OBJEXT := o OBJEXT := o
#Flags, Libraries and Includes #Flags, Libraries and Includes
ROOT := ./ ROOT := ./
CFLAGS := -Wall -std=c++20 -DSPIDER_COMPILING CFLAGS := -Wall -std=c++20 -DSPIDER_COMPILING
LFLAGS := -Wall -std=c++20 -static LFLAGS := -Wall -std=c++20 -static
LIB := LIB :=
INC := -I./src/ INC := -I./src/
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
#DO NOT EDIT BELOW THIS LINE #DO NOT EDIT BELOW THIS LINE
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT)) SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT))) OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
#Defauilt Make #Defauilt Make
all: directories $(TARGET) all: directories $(TARGET)
#Remake #Remake
remake: cleaner all remake: cleaner all
#Make the Directories #Make the Directories
directories: directories:
@mkdir -p $(TARGETDIR) @mkdir -p $(TARGETDIR)
@mkdir -p $(BUILDDIR) @mkdir -p $(BUILDDIR)
#Clean only Objecst #Clean only Objecst
clean: clean:
@$(RM) -rf $(BUILDDIR) @$(RM) -rf $(BUILDDIR)
#Full Clean, Objects and Binaries #Full Clean, Objects and Binaries
cleaner: clean cleaner: clean
@$(RM) -rf $(TARGETDIR) @$(RM) -rf $(TARGETDIR)
#Pull in dependency info for *existing* .o files #Pull in dependency info for *existing* .o files
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT)) -include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
#Link #Link
$(TARGET): $(OBJECTS) $(TARGET): $(OBJECTS)
$(CC) $(LFLAGS) -o $(TARGETDIR)/$(TARGET) $^ $(LIB) $(CC) $(LFLAGS) -o $(TARGETDIR)/$(TARGET) $^ $(LIB)
#Compile #Compile
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
@mkdir -p $(dir $@) @mkdir -p $(dir $@)
$(CC) $(CFLAGS) $(INC) -c -o $@ $< $(CC) $(CFLAGS) $(INC) -c -o $@ $<
@$(CC) $(CFLAGS) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT) @$(CC) $(CFLAGS) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT)
@cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp @cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
@sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT) @sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
@sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT) @sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT)
@rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp @rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp
#Non-File Targets #Non-File Targets
.PHONY: all remake clean cleaner resources .PHONY: all remake clean cleaner resources

View File

@@ -1,471 +1,471 @@
{ {
"cells": [ "cells": [
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "21877801", "id": "21877801",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## Python Generator\n", "## Python Generator\n",
"\n", "\n",
"This python notebook will serve to generate the necessary code to\n", "This python notebook will serve to generate the necessary code to\n",
"generate some things from Spider.\n", "generate some things from Spider.\n",
"\n", "\n",
"Specifically, it will generate the CPU instructions (currently)." "Specifically, it will generate the CPU instructions (currently)."
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"id": "b0fcd533", "id": "b0fcd533",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Repo root : ./ -> (True)\n", "Repo root : ./ -> (True)\n",
"CPU.hpp : .//src//spider/runtime/cpu/CPU.hpp -> (True)\n", "CPU.hpp : .//src//spider/runtime/cpu/CPU.hpp -> (True)\n",
"XLSX : .//docs//Spider Instructions.xlsx -> (True)\n", "XLSX : .//docs//Spider Instructions.xlsx -> (True)\n",
"Output dir: .//autogen/ -> (True)\n" "Output dir: .//autogen/ -> (True)\n"
] ]
} }
], ],
"source": [ "source": [
"# setup directories\n", "# setup directories\n",
"import os\n", "import os\n",
"\n", "\n",
"# [CHANGE]\n", "# [CHANGE]\n",
"# Since we're running on a local environment (i hope)\n", "# Since we're running on a local environment (i hope)\n",
"# we can just signal a relative directory.\n", "# we can just signal a relative directory.\n",
"REPO_ROOT = './'\n", "REPO_ROOT = './'\n",
"DOCS_ROOT = f'{REPO_ROOT}/docs/'\n", "DOCS_ROOT = f'{REPO_ROOT}/docs/'\n",
"SRC_ROOT = f'{REPO_ROOT}/src/'\n", "SRC_ROOT = f'{REPO_ROOT}/src/'\n",
"\n", "\n",
"# Where CPU.hpp lives — this is the file we will inject generated code into.\n", "# Where CPU.hpp lives — this is the file we will inject generated code into.\n",
"CPU_HPP_PATH = f'{SRC_ROOT}/spider/runtime/cpu/CPU.hpp'\n", "CPU_HPP_PATH = f'{SRC_ROOT}/spider/runtime/cpu/CPU.hpp'\n",
"\n", "\n",
"# Where the Excel instruction sheet lives. Allocate the .xlsx file in the project's root folder.\n", "# Where the Excel instruction sheet lives. Allocate the .xlsx file in the project's root folder.\n",
"# NOTE: The file I uploaded has a space instead of underscore!\n", "# NOTE: The file I uploaded has a space instead of underscore!\n",
"XLSX_PATH = f'{DOCS_ROOT}/Spider Instructions.xlsx'\n", "XLSX_PATH = f'{DOCS_ROOT}/Spider Instructions.xlsx'\n",
"\n", "\n",
"# Output folder for any standalone generated files.\n", "# Output folder for any standalone generated files.\n",
"OUT_DIR = f'{REPO_ROOT}/autogen/'\n", "OUT_DIR = f'{REPO_ROOT}/autogen/'\n",
"\n", "\n",
"# Create the output directory if it does not exist yet.\n", "# Create the output directory if it does not exist yet.\n",
"# exist_ok=True means no error if it already exists.\n", "# exist_ok=True means no error if it already exists.\n",
"os.makedirs(OUT_DIR, exist_ok=True)\n", "os.makedirs(OUT_DIR, exist_ok=True)\n",
"\n", "\n",
"def dir_exists(path:str):\n", "def dir_exists(path:str):\n",
" return os.path.exists(path) and os.path.isdir(path)\n", " return os.path.exists(path) and os.path.isdir(path)\n",
"def file_exists(path:str):\n", "def file_exists(path:str):\n",
" return os.path.exists(path) and os.path.isfile(path)\n", " return os.path.exists(path) and os.path.isfile(path)\n",
"\n", "\n",
"print(f'Repo root : {REPO_ROOT } -> ({ dir_exists(REPO_ROOT )})')\n", "print(f'Repo root : {REPO_ROOT } -> ({ dir_exists(REPO_ROOT )})')\n",
"print(f'CPU.hpp : {CPU_HPP_PATH} -> ({file_exists(CPU_HPP_PATH)})')\n", "print(f'CPU.hpp : {CPU_HPP_PATH} -> ({file_exists(CPU_HPP_PATH)})')\n",
"print(f'XLSX : {XLSX_PATH } -> ({file_exists(XLSX_PATH )})')\n", "print(f'XLSX : {XLSX_PATH } -> ({file_exists(XLSX_PATH )})')\n",
"print(f'Output dir: {OUT_DIR } -> ({ dir_exists(OUT_DIR )})')\n" "print(f'Output dir: {OUT_DIR } -> ({ dir_exists(OUT_DIR )})')\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 5,
"id": "b33de8ac", "id": "b33de8ac",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"--- Sample output for NOP ---\n", "--- Sample output for NOP ---\n",
" // [System] 0x000 — NOP: No Operation\n", " // [System] 0x000 — NOP: No Operation\n",
" // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00\n", " // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00\n",
" // Operation: Nothing\n", " // Operation: Nothing\n",
" void NOP();\n", " void NOP();\n",
"\n" "\n"
] ]
} }
], ],
"source": [ "source": [
"# Implement here some kind of \"C++\" printer\n", "# Implement here some kind of \"C++\" printer\n",
"\n", "\n",
"# ── Indent used throughout the generated block ──────────────────────────────\n", "# ── Indent used throughout the generated block ──────────────────────────────\n",
"INDENT = ' ' # 8 spaces — matches the indentation inside CPU.hpp\n", "INDENT = ' ' # 8 spaces — matches the indentation inside CPU.hpp\n",
"\n", "\n",
"def format_instruction(byte_code: str, mnemonic: str, name: str,\n", "def format_instruction(byte_code: str, mnemonic: str, name: str,\n",
" group: str, params: int,\n", " group: str, params: int,\n",
" addr_mask_1: str, addr_mask_2: str,\n", " addr_mask_1: str, addr_mask_2: str,\n",
" type_mask: str, operation: str) -> str:\n", " type_mask: str, operation: str) -> str:\n",
" \"\"\"\n", " \"\"\"\n",
" Returns a single C++ instruction declaration as a string.\n", " Returns a single C++ instruction declaration as a string.\n",
"\n", "\n",
" Each instruction becomes a commented constant inside the CPU class.\n", " Each instruction becomes a commented constant inside the CPU class.\n",
" Format:\n", " Format:\n",
" // [GROUP] 0xBYTE — MNEMONIC: Name\n", " // [GROUP] 0xBYTE — MNEMONIC: Name\n",
" // Params: N | AddrMask1: XX AddrMask2: XX | TypeMask: XX\n", " // Params: N | AddrMask1: XX AddrMask2: XX | TypeMask: XX\n",
" // Operation: ...\n", " // Operation: ...\n",
" MNEMONIC\n", " MNEMONIC\n",
" \"\"\"\n", " \"\"\"\n",
" lines = []\n", " lines = []\n",
"\n", "\n",
" # Header comment: group, opcode, mnemonic and human-readable name.\n", " # Header comment: group, opcode, mnemonic and human-readable name.\n",
" lines.append(f'{INDENT}// [{group}] 0x{byte_code} — {mnemonic}: {name}')\n", " lines.append(f'{INDENT}// [{group}] 0x{byte_code} — {mnemonic}: {name}')\n",
"\n", "\n",
" # Second comment line: parameter count, addressing masks, type size mask.\n", " # Second comment line: parameter count, addressing masks, type size mask.\n",
" lines.append(f'{INDENT}// Params: {params} | '\n", " lines.append(f'{INDENT}// Params: {params} | '\n",
" f'AddrMask1: {addr_mask_1} AddrMask2: {addr_mask_2} | '\n", " f'AddrMask1: {addr_mask_1} AddrMask2: {addr_mask_2} | '\n",
" f'TypeMask: {type_mask}')\n", " f'TypeMask: {type_mask}')\n",
"\n", "\n",
" # Third comment line: what this instruction actually does.\n", " # Third comment line: what this instruction actually does.\n",
" lines.append(f'{INDENT}// Operation: {operation}')\n", " lines.append(f'{INDENT}// Operation: {operation}')\n",
"\n", "\n",
" # The declaration itself — just the mnemonic name, matching NOP/SPDR style.\n", " # The declaration itself — just the mnemonic name, matching NOP/SPDR style.\n",
" lines.append(f'{INDENT}void {mnemonic}();') # method declaration inside CPU class # enum value: NAME = 0xOPCODE,\n", " lines.append(f'{INDENT}void {mnemonic}();') # method declaration inside CPU class # enum value: NAME = 0xOPCODE,\n",
"\n", "\n",
" # Empty line between instructions for readability.\n", " # Empty line between instructions for readability.\n",
" lines.append('')\n", " lines.append('')\n",
"\n", "\n",
" return '\\n'.join(lines)\n", " return '\\n'.join(lines)\n",
"\n", "\n",
"\n", "\n",
"def format_block(instructions: list) -> str:\n", "def format_block(instructions: list) -> str:\n",
" \"\"\"\n", " \"\"\"\n",
" Joins all individual instruction strings into one complete block.\n", " Joins all individual instruction strings into one complete block.\n",
" This is the text that will be injected between the pygen-target markers.\n", " This is the text that will be injected between the pygen-target markers.\n",
" \"\"\"\n", " \"\"\"\n",
" # Join every formatted instruction into one big string.\n", " # Join every formatted instruction into one big string.\n",
" return '\\n'.join(instructions)\n", " return '\\n'.join(instructions)\n",
"\n", "\n",
"\n", "\n",
"# Print what one instruction looks like.\n", "# Print what one instruction looks like.\n",
"sample = format_instruction('000','NOP','No Operation','System',0,'00','00','00','Nothing')\n", "sample = format_instruction('000','NOP','No Operation','System',0,'00','00','00','Nothing')\n",
"print('--- Sample output for NOP ---')\n", "print('--- Sample output for NOP ---')\n",
"print(sample)\n" "print(sample)\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 6,
"id": "58645013", "id": "58645013",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Real instructions : 126\n", "Real instructions : 126\n",
"Reserved slots : 14\n", "Reserved slots : 14\n",
"Duplicate check : PASSED\n", "Duplicate check : PASSED\n",
"\n", "\n",
"Groups found:\n", "Groups found:\n",
"group\n", "group\n",
"Integer 19\n", "Integer 19\n",
"System 15\n", "System 15\n",
"Bit Wise 14\n", "Bit Wise 14\n",
"Boolean 12\n", "Boolean 12\n",
"Branch 12\n", "Branch 12\n",
"Casts 10\n", "Casts 10\n",
"Floating Point 10\n", "Floating Point 10\n",
"Memory 9\n", "Memory 9\n",
"Trigonometric 7\n", "Trigonometric 7\n",
"Exponential 6\n", "Exponential 6\n",
"Matrix 6\n", "Matrix 6\n",
"SIMD 5\n", "SIMD 5\n",
"Easter Eggs 1\n", "Easter Eggs 1\n",
"\n", "\n",
"First 5 instructions:\n", "First 5 instructions:\n",
" byte_code mnemonic group params addr_mask_1 type_mask\n", " byte_code mnemonic group params addr_mask_1 type_mask\n",
"0 000 NOP System 0 00 00\n", "0 000 NOP System 0 00 00\n",
"1 001 SPDR System 0 00 00\n", "1 001 SPDR System 0 00 00\n",
"2 002 MMODE System 1 05 01\n", "2 002 MMODE System 1 05 01\n",
"3 003 INT System 1 1F 0F\n", "3 003 INT System 1 1F 0F\n",
"4 004 LRV System 1 1F 0C\n" "4 004 LRV System 1 1F 0C\n"
] ]
} }
], ],
"source": [ "source": [
"# read the instruction sheet with pandas\n", "# read the instruction sheet with pandas\n",
"\n", "\n",
"\n", "\n",
"import pandas as pd\n", "import pandas as pd\n",
"\n", "\n",
"# -- Load --------------------------------------------------------------------\n", "# -- Load --------------------------------------------------------------------\n",
"# The data is on the 'Instructions' sheet. Header is on row index 6 (0-based),\n", "# The data is on the 'Instructions' sheet. Header is on row index 6 (0-based),\n",
"# so we skip the first 6 rows of decorative merged cells.\n", "# so we skip the first 6 rows of decorative merged cells.\n",
"raw = pd.read_excel(XLSX_PATH, sheet_name='Instructions', header=6)\n", "raw = pd.read_excel(XLSX_PATH, sheet_name='Instructions', header=6)\n",
"\n", "\n",
"# Rename the two unnamed columns that hold the two addressing mode masks.\n", "# Rename the two unnamed columns that hold the two addressing mode masks.\n",
"# In the sheet they appear after 'Acc. Addr. Mode Mask' with no header label.\n", "# In the sheet they appear after 'Acc. Addr. Mode Mask' with no header label.\n",
"raw.columns = [\n", "raw.columns = [\n",
" 'skip_0', # empty column A\n", " 'skip_0', # empty column A\n",
" 'skip_1', # 'Base Instr.' label column\n", " 'skip_1', # 'Base Instr.' label column\n",
" 'byte_code', # opcode hex string e.g. '000'\n", " 'byte_code', # opcode hex string e.g. '000'\n",
" 'mnemonic', # short name e.g. 'NOP'\n", " 'mnemonic', # short name e.g. 'NOP'\n",
" 'name', # full name e.g. 'No Operation'\n", " 'name', # full name e.g. 'No Operation'\n",
" 'group', # category e.g. 'System'\n", " 'group', # category e.g. 'System'\n",
" 'params', # number of parameters (0, 1, or 2)\n", " 'params', # number of parameters (0, 1, or 2)\n",
" 'imp', # addressing mode: Implied\n", " 'imp', # addressing mode: Implied\n",
" 'imm', # addressing mode: Immediate\n", " 'imm', # addressing mode: Immediate\n",
" 'abs', # addressing mode: Absolute\n", " 'abs', # addressing mode: Absolute\n",
" 'reg', # addressing mode: Register\n", " 'reg', # addressing mode: Register\n",
" 'ind', # addressing mode: Indirect\n", " 'ind', # addressing mode: Indirect\n",
" 'ptr', # addressing mode: Pointer\n", " 'ptr', # addressing mode: Pointer\n",
" 'idx', # addressing mode: Indexed\n", " 'idx', # addressing mode: Indexed\n",
" 'sca', # addressing mode: Scaled\n", " 'sca', # addressing mode: Scaled\n",
" 'dis', # addressing mode: Displaced\n", " 'dis', # addressing mode: Displaced\n",
" 'addr_mask_1', # accepted addressing mode mask for param 1\n", " 'addr_mask_1', # accepted addressing mode mask for param 1\n",
" 'addr_mask_2', # accepted addressing mode mask for param 2\n", " 'addr_mask_2', # accepted addressing mode mask for param 2\n",
" 'B', # type size: Byte (1 byte) supported?\n", " 'B', # type size: Byte (1 byte) supported?\n",
" 'S', # type size: Short (2 bytes) supported?\n", " 'S', # type size: Short (2 bytes) supported?\n",
" 'I', # type size: Int (4 bytes) supported?\n", " 'I', # type size: Int (4 bytes) supported?\n",
" 'L', # type size: Long (8 bytes) supported?\n", " 'L', # type size: Long (8 bytes) supported?\n",
" 'F', # type size: Float supported?\n", " 'F', # type size: Float supported?\n",
" 'D', # type size: Double supported?\n", " 'D', # type size: Double supported?\n",
" 'type_mask', # combined type size mask as hex string\n", " 'type_mask', # combined type size mask as hex string\n",
" 'operation', # human-readable description of what the instruction does\n", " 'operation', # human-readable description of what the instruction does\n",
" 'skip_2', # trailing empty column\n", " 'skip_2', # trailing empty column\n",
"]\n", "]\n",
"\n", "\n",
"# ── Filter ───────────────────────────────────────────────────────────────────\n", "# ── Filter ───────────────────────────────────────────────────────────────────\n",
"# Keep only rows that have a byte_code value (drops empty rows at the bottom).\n", "# Keep only rows that have a byte_code value (drops empty rows at the bottom).\n",
"df = raw[raw['byte_code'].notna()].copy()\n", "df = raw[raw['byte_code'].notna()].copy()\n",
"\n", "\n",
"# Separate reserved slots from real instructions.\n", "# Separate reserved slots from real instructions.\n",
"# Reserved entries have '(reserved)' in the mnemonic column.\n", "# Reserved entries have '(reserved)' in the mnemonic column.\n",
"is_reserved = df['mnemonic'].astype(str).str.contains('reserved', case=False, na=False)\n", "is_reserved = df['mnemonic'].astype(str).str.contains('reserved', case=False, na=False)\n",
"reserved_df = df[is_reserved].copy() # keep for reference\n", "reserved_df = df[is_reserved].copy() # keep for reference\n",
"instrs_df = df[~is_reserved & df['mnemonic'].notna()].copy() # real instructions only\n", "instrs_df = df[~is_reserved & df['mnemonic'].notna()].copy() # real instructions only\n",
"\n", "\n",
"# Skip incomplete entries — rows with no group are placeholder slots (e.g. Int 1-6 Slot)\n", "# Skip incomplete entries — rows with no group are placeholder slots (e.g. Int 1-6 Slot)\n",
"# that have no defined behaviour yet. Keeping them would generate invalid C++ identifiers.\n", "# that have no defined behaviour yet. Keeping them would generate invalid C++ identifiers.\n",
"instrs_df = instrs_df[instrs_df['group'].notna()].copy()\n", "instrs_df = instrs_df[instrs_df['group'].notna()].copy()\n",
"\n", "\n",
"# ── Clean ────────────────────────────────────────────────────────────────────\n", "# ── Clean ────────────────────────────────────────────────────────────────────\n",
"# Fill NaN masks with '00' (means 'no modes accepted' — safe default).\n", "# Fill NaN masks with '00' (means 'no modes accepted' — safe default).\n",
"instrs_df['addr_mask_1'] = instrs_df['addr_mask_1'].fillna('00').astype(str).str.strip()\n", "instrs_df['addr_mask_1'] = instrs_df['addr_mask_1'].fillna('00').astype(str).str.strip()\n",
"instrs_df['addr_mask_2'] = instrs_df['addr_mask_2'].fillna('00').astype(str).str.strip()\n", "instrs_df['addr_mask_2'] = instrs_df['addr_mask_2'].fillna('00').astype(str).str.strip()\n",
"instrs_df['type_mask'] = instrs_df['type_mask'].fillna('00').astype(str).str.strip()\n", "instrs_df['type_mask'] = instrs_df['type_mask'].fillna('00').astype(str).str.strip()\n",
"instrs_df['params'] = instrs_df['params'].fillna(0).astype(int)\n", "instrs_df['params'] = instrs_df['params'].fillna(0).astype(int)\n",
"instrs_df['name'] = instrs_df['name'].fillna('').astype(str).str.strip()\n", "instrs_df['name'] = instrs_df['name'].fillna('').astype(str).str.strip()\n",
"instrs_df['group'] = instrs_df['group'].fillna('Unknown').astype(str).str.strip()\n", "instrs_df['group'] = instrs_df['group'].fillna('Unknown').astype(str).str.strip()\n",
"instrs_df['operation'] = instrs_df['operation'].fillna('').astype(str).str.strip()\n", "instrs_df['operation'] = instrs_df['operation'].fillna('').astype(str).str.strip()\n",
"\n", "\n",
"# ── Sanitize mnemonics ──────────────────────────────────────────────────────\n", "# ── Sanitize mnemonics ──────────────────────────────────────────────────────\n",
"# C++ identifiers cannot contain spaces. Replace spaces with underscores and\n", "# C++ identifiers cannot contain spaces. Replace spaces with underscores and\n",
"# convert to uppercase so 'Int 1 Slot' becomes 'INT_1_SLOT'.\n", "# convert to uppercase so 'Int 1 Slot' becomes 'INT_1_SLOT'.\n",
"instrs_df['mnemonic'] = (\n", "instrs_df['mnemonic'] = (\n",
" instrs_df['mnemonic']\n", " instrs_df['mnemonic']\n",
" .astype(str)\n", " .astype(str)\n",
" .str.strip() # remove leading/trailing whitespace\n", " .str.strip() # remove leading/trailing whitespace\n",
" .str.replace(' ', '_') # replace internal spaces with underscores\n", " .str.replace(' ', '_') # replace internal spaces with underscores\n",
" .str.upper() # uppercase for consistency\n", " .str.upper() # uppercase for consistency\n",
")\n", ")\n",
"\n", "\n",
"# ── Validate: duplicate mnemonics ────────────────────────────────────────────\n", "# ── Validate: duplicate mnemonics ────────────────────────────────────────────\n",
"# Duplicates in real instruction names would cause C++ compilation errors.\n", "# Duplicates in real instruction names would cause C++ compilation errors.\n",
"# We abort here rather than generating broken code.\n", "# We abort here rather than generating broken code.\n",
"mnemonic_counts = instrs_df['mnemonic'].value_counts()\n", "mnemonic_counts = instrs_df['mnemonic'].value_counts()\n",
"duplicates = mnemonic_counts[mnemonic_counts > 1]\n", "duplicates = mnemonic_counts[mnemonic_counts > 1]\n",
"if not duplicates.empty:\n", "if not duplicates.empty:\n",
" # Show which mnemonics are duplicated before raising the error.\n", " # Show which mnemonics are duplicated before raising the error.\n",
" raise ValueError(f'Duplicate mnemonics found — fix the sheet before generating:\\n{duplicates}')\n", " raise ValueError(f'Duplicate mnemonics found — fix the sheet before generating:\\n{duplicates}')\n",
"\n", "\n",
"print(f'Real instructions : {len(instrs_df)}')\n", "print(f'Real instructions : {len(instrs_df)}')\n",
"print(f'Reserved slots : {len(reserved_df)}')\n", "print(f'Reserved slots : {len(reserved_df)}')\n",
"print(f'Duplicate check : PASSED')\n", "print(f'Duplicate check : PASSED')\n",
"print(f'\\nGroups found:')\n", "print(f'\\nGroups found:')\n",
"print(instrs_df['group'].value_counts().to_string())\n", "print(instrs_df['group'].value_counts().to_string())\n",
"print(f'\\nFirst 5 instructions:')\n", "print(f'\\nFirst 5 instructions:')\n",
"print(instrs_df[['byte_code','mnemonic','group','params','addr_mask_1','type_mask']].head().to_string())\n" "print(instrs_df[['byte_code','mnemonic','group','params','addr_mask_1','type_mask']].head().to_string())\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": 7,
"id": "452bc76c", "id": "452bc76c",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Masks written to: .//autogen/InstructionMasks.hpp\n", "Masks written to: .//autogen/InstructionMasks.hpp\n",
"Lines generated : 268\n" "Lines generated : 268\n"
] ]
} }
], ],
"source": [ "source": [
"# well, then export the masks (TODO)\n", "# well, then export the masks (TODO)\n",
"\n", "\n",
"\n", "\n",
"# ── Build the masks header content ──────────────────────────────────────────\n", "# ── Build the masks header content ──────────────────────────────────────────\n",
"lines = []\n", "lines = []\n",
"\n", "\n",
"# Standard C++ header guard — prevents the file from being included more than once.\n", "# Standard C++ header guard — prevents the file from being included more than once.\n",
"lines.append('#pragma once')\n", "lines.append('#pragma once')\n",
"lines.append('// AUTO-GENERATED by pygen.ipynb — DO NOT EDIT MANUALLY')\n", "lines.append('// AUTO-GENERATED by pygen.ipynb — DO NOT EDIT MANUALLY')\n",
"lines.append('#include <spider/runtime/common.hpp>')\n", "lines.append('#include <spider/runtime/common.hpp>')\n",
"lines.append('')\n", "lines.append('')\n",
"lines.append('namespace spider {')\n", "lines.append('namespace spider {')\n",
"lines.append('')\n", "lines.append('')\n",
"\n", "\n",
"# ── Addressing mode mask table ───────────────────────────────────────────────\n", "# ── Addressing mode mask table ───────────────────────────────────────────────\n",
"# Each instruction has two masks (one per parameter).\n", "# Each instruction has two masks (one per parameter).\n",
"# We write them as a constexpr array so the VM can look them up at runtime\n", "# We write them as a constexpr array so the VM can look them up at runtime\n",
"# using the opcode as the index.\n", "# using the opcode as the index.\n",
"lines.append('// Addressing mode masks — indexed by opcode.')\n", "lines.append('// Addressing mode masks — indexed by opcode.')\n",
"lines.append('// [opcode][0] = mask for param 1, [opcode][1] = mask for param 2')\n", "lines.append('// [opcode][0] = mask for param 1, [opcode][1] = mask for param 2')\n",
"lines.append('constexpr u8 ADDR_MODE_MASKS[][2] = {')\n", "lines.append('constexpr u8 ADDR_MODE_MASKS[][2] = {')\n",
"\n", "\n",
"for _, row in instrs_df.iterrows():\n", "for _, row in instrs_df.iterrows():\n",
" # Convert the hex string mask to an integer for the C++ literal.\n", " # Convert the hex string mask to an integer for the C++ literal.\n",
" m1 = row['addr_mask_1'].replace('.0','').strip() # remove pandas float artefact\n", " m1 = row['addr_mask_1'].replace('.0','').strip() # remove pandas float artefact\n",
" m2 = row['addr_mask_2'].replace('.0','').strip()\n", " m2 = row['addr_mask_2'].replace('.0','').strip()\n",
" m1 = m1 if m1 != 'nan' else '00'\n", " m1 = m1 if m1 != 'nan' else '00'\n",
" m2 = m2 if m2 != 'nan' else '00'\n", " m2 = m2 if m2 != 'nan' else '00'\n",
" # Each row: { 0xMASK1, 0xMASK2 }, // MNEMONIC\n", " # Each row: { 0xMASK1, 0xMASK2 }, // MNEMONIC\n",
" lines.append(f' {{ 0x{m1.upper()}, 0x{m2.upper()} }}, // {row[\"mnemonic\"]}')\n", " lines.append(f' {{ 0x{m1.upper()}, 0x{m2.upper()} }}, // {row[\"mnemonic\"]}')\n",
"\n", "\n",
"lines.append('};')\n", "lines.append('};')\n",
"lines.append('')\n", "lines.append('')\n",
"\n", "\n",
"# ── Type size mask table ─────────────────────────────────────────────────────\n", "# ── Type size mask table ─────────────────────────────────────────────────────\n",
"# A single byte per instruction encoding which type sizes it accepts.\n", "# A single byte per instruction encoding which type sizes it accepts.\n",
"lines.append('// Type size masks — indexed by opcode.')\n", "lines.append('// Type size masks — indexed by opcode.')\n",
"lines.append('constexpr u8 TYPE_SIZE_MASKS[] = {')\n", "lines.append('constexpr u8 TYPE_SIZE_MASKS[] = {')\n",
"\n", "\n",
"for _, row in instrs_df.iterrows():\n", "for _, row in instrs_df.iterrows():\n",
" tm = str(row['type_mask']).replace('.0','').strip()\n", " tm = str(row['type_mask']).replace('.0','').strip()\n",
" tm = tm if tm != 'nan' else '00'\n", " tm = tm if tm != 'nan' else '00'\n",
" lines.append(f' 0x{tm.upper()}, // {row[\"mnemonic\"]}')\n", " lines.append(f' 0x{tm.upper()}, // {row[\"mnemonic\"]}')\n",
"\n", "\n",
"lines.append('};')\n", "lines.append('};')\n",
"lines.append('')\n", "lines.append('')\n",
"lines.append('} // namespace spider')\n", "lines.append('} // namespace spider')\n",
"\n", "\n",
"# ── Write to file ────────────────────────────────────────────────────────────\n", "# ── Write to file ────────────────────────────────────────────────────────────\n",
"masks_path = os.path.join(OUT_DIR, 'InstructionMasks.hpp')\n", "masks_path = os.path.join(OUT_DIR, 'InstructionMasks.hpp')\n",
"with open(masks_path, 'w', encoding='utf-8') as f:\n", "with open(masks_path, 'w', encoding='utf-8') as f:\n",
" # Join with Unix line endings only — repo etiquette says no \\r\\n.\n", " # Join with Unix line endings only — repo etiquette says no \\r\\n.\n",
" f.write('\\n'.join(lines))\n", " f.write('\\n'.join(lines))\n",
"\n", "\n",
"print(f'Masks written to: {masks_path}')\n", "print(f'Masks written to: {masks_path}')\n",
"print(f'Lines generated : {len(lines)}')\n" "print(f'Lines generated : {len(lines)}')\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 8, "execution_count": 8,
"id": "5aaebef0", "id": "5aaebef0",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Instructions formatted: 126\n", "Instructions formatted: 126\n",
"\n", "\n",
"--- Preview (first 2 instructions) ---\n", "--- Preview (first 2 instructions) ---\n",
" // [System] 0x000 — NOP: No Operation\n", " // [System] 0x000 — NOP: No Operation\n",
" // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00\n", " // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00\n",
" // Operation: Nothing\n", " // Operation: Nothing\n",
" void NOP();\n", " void NOP();\n",
"\n", "\n",
" // [System] 0x001 — SPDR: Will place the Spider version of the interpreter in RA\n", " // [System] 0x001 — SPDR: Will place the Spider version of the interpreter in RA\n",
" // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00\n", " // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00\n",
" // Operation: (Spider Version) -> RA\n", " // Operation: (Spider Version) -> RA\n",
" void SPDR();\n", " void SPDR();\n",
"\n", "\n",
"\n", "\n",
"CPU.hpp updated successfully at: .//src//spider/runtime/cpu/CPU.hpp\n", "CPU.hpp updated successfully at: .//src//spider/runtime/cpu/CPU.hpp\n",
"Total lines in updated file: 674\n" "Total lines in updated file: 674\n"
] ]
} }
], ],
"source": [ "source": [
"# print the CPU Instructions\n", "# print the CPU Instructions\n",
"\n", "\n",
"# ── Generate all instruction declarations ───────────────────────────────────\n", "# ── Generate all instruction declarations ───────────────────────────────────\n",
"formatted = []\n", "formatted = []\n",
"\n", "\n",
"for _, row in instrs_df.iterrows():\n", "for _, row in instrs_df.iterrows():\n",
" # Clean each field — remove pandas float artefacts like '00.0'\n", " # Clean each field — remove pandas float artefacts like '00.0'\n",
" byte_code = str(row['byte_code']).strip()\n", " byte_code = str(row['byte_code']).strip()\n",
" mnemonic = str(row['mnemonic']).strip()\n", " mnemonic = str(row['mnemonic']).strip()\n",
" name = str(row['name']).strip()\n", " name = str(row['name']).strip()\n",
" group = str(row['group']).strip()\n", " group = str(row['group']).strip()\n",
" params = int(row['params'])\n", " params = int(row['params'])\n",
" addr_mask_1 = str(row['addr_mask_1']).replace('.0', '').strip()\n", " addr_mask_1 = str(row['addr_mask_1']).replace('.0', '').strip()\n",
" addr_mask_2 = str(row['addr_mask_2']).replace('.0', '').strip()\n", " addr_mask_2 = str(row['addr_mask_2']).replace('.0', '').strip()\n",
" type_mask = str(row['type_mask']).replace('.0', '').strip()\n", " type_mask = str(row['type_mask']).replace('.0', '').strip()\n",
" operation = str(row['operation']).strip()\n", " operation = str(row['operation']).strip()\n",
"\n", "\n",
" # Call the C++ printer from Cell 2 to format this instruction.\n", " # Call the C++ printer from Cell 2 to format this instruction.\n",
" formatted.append(format_instruction(\n", " formatted.append(format_instruction(\n",
" byte_code, mnemonic, name, group,\n", " byte_code, mnemonic, name, group,\n",
" params, addr_mask_1, addr_mask_2,\n", " params, addr_mask_1, addr_mask_2,\n",
" type_mask, operation\n", " type_mask, operation\n",
" ))\n", " ))\n",
"\n", "\n",
"# Combine all declarations into one block string.\n", "# Combine all declarations into one block string.\n",
"generated_block = format_block(formatted)\n", "generated_block = format_block(formatted)\n",
"\n", "\n",
"print(f'Instructions formatted: {len(formatted)}')\n", "print(f'Instructions formatted: {len(formatted)}')\n",
"print('\\n--- Preview (first 2 instructions) ---')\n", "print('\\n--- Preview (first 2 instructions) ---')\n",
"print('\\n'.join(formatted[:2]))\n", "print('\\n'.join(formatted[:2]))\n",
"\n", "\n",
"# ── Inject into CPU.hpp ──────────────────────────────────────────────────────\n", "# ── Inject into CPU.hpp ──────────────────────────────────────────────────────\n",
"# The markers tell us exactly where to insert the generated block.\n", "# The markers tell us exactly where to insert the generated block.\n",
"MARKER_OPEN = '// <pygen-target name=cpu-instructions> //'\n", "MARKER_OPEN = '// <pygen-target name=cpu-instructions> //'\n",
"MARKER_CLOSE = '// </pygen-target> //'\n", "MARKER_CLOSE = '// </pygen-target> //'\n",
"\n", "\n",
"# Read the current CPU.hpp content.\n", "# Read the current CPU.hpp content.\n",
"with open(CPU_HPP_PATH, 'r', encoding='utf-8') as f:\n", "with open(CPU_HPP_PATH, 'r', encoding='utf-8') as f:\n",
" original = f.read()\n", " original = f.read()\n",
"\n", "\n",
"# Verify both markers exist before modifying anything.\n", "# Verify both markers exist before modifying anything.\n",
"# If either is missing, the file was edited by hand — abort to avoid corruption.\n", "# If either is missing, the file was edited by hand — abort to avoid corruption.\n",
"if MARKER_OPEN not in original:\n", "if MARKER_OPEN not in original:\n",
" raise ValueError(f'Open marker not found in CPU.hpp: {MARKER_OPEN}')\n", " raise ValueError(f'Open marker not found in CPU.hpp: {MARKER_OPEN}')\n",
"if MARKER_CLOSE not in original:\n", "if MARKER_CLOSE not in original:\n",
" raise ValueError(f'Close marker not found in CPU.hpp: {MARKER_CLOSE}')\n", " raise ValueError(f'Close marker not found in CPU.hpp: {MARKER_CLOSE}')\n",
"\n", "\n",
"# Split the file into 3 parts around the pygen-target markers.\n", "# Split the file into 3 parts around the pygen-target markers.\n",
"# before : everything up to and including the open marker\n", "# before : everything up to and including the open marker\n",
"# after : from the close marker onward (including it)\n", "# after : from the close marker onward (including it)\n",
"before = original[:original.index(MARKER_OPEN) + len(MARKER_OPEN)]\n", "before = original[:original.index(MARKER_OPEN) + len(MARKER_OPEN)]\n",
"after = original[original.index(MARKER_CLOSE):]\n", "after = original[original.index(MARKER_CLOSE):]\n",
"\n", "\n",
"# Reassemble: keep before, inject the generated block, then restore after.\n", "# Reassemble: keep before, inject the generated block, then restore after.\n",
"updated = before + '\\n' + generated_block + '\\n' + INDENT + after\n", "updated = before + '\\n' + generated_block + '\\n' + INDENT + after\n",
"\n", "\n",
"# Write back using UTF-8 and Unix line endings only (repo etiquette: no \\r\\n).\n", "# Write back using UTF-8 and Unix line endings only (repo etiquette: no \\r\\n).\n",
"with open(CPU_HPP_PATH, 'w', encoding='utf-8', newline='\\n') as f:\n", "with open(CPU_HPP_PATH, 'w', encoding='utf-8', newline='\\n') as f:\n",
" f.write(updated)\n", " f.write(updated)\n",
"\n", "\n",
"print(f'\\nCPU.hpp updated successfully at: {CPU_HPP_PATH}')\n", "print(f'\\nCPU.hpp updated successfully at: {CPU_HPP_PATH}')\n",
"print(f'Total lines in updated file: {len(updated.splitlines())}')\n" "print(f'Total lines in updated file: {len(updated.splitlines())}')\n"
] ]
} }
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3", "display_name": "Python 3",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },
"language_info": { "language_info": {
"codemirror_mode": { "codemirror_mode": {
"name": "ipython", "name": "ipython",
"version": 3 "version": 3
}, },
"file_extension": ".py", "file_extension": ".py",
"mimetype": "text/x-python", "mimetype": "text/x-python",
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.13.7" "version": "3.13.7"
} }
}, },
"nbformat": 4, "nbformat": 4,
"nbformat_minor": 5 "nbformat_minor": 5
} }

View File

@@ -1,21 +1,21 @@
{ {
"folders": [ "folders": [
{ {
"path": "." "path": "."
} }
], ],
"settings": { "settings": {
"gitlens.remotes": [ "gitlens.remotes": [
{ {
"domain": "git.sintekanalytics.com", "domain": "git.sintekanalytics.com",
"type": "Gitea", "type": "Gitea",
"name": "Sintek Analytics' Git", "name": "Sintek Analytics' Git",
"protocol": "https", "protocol": "https",
} }
], ],
"C_Cpp.default.includePath": [ "C_Cpp.default.includePath": [
"./src" "./src"
], ],
"terminal.integrated.defaultProfile.windows": "MSYS2 UCRT" "terminal.integrated.defaultProfile.windows": "MSYS2 UCRT"
} }
} }

View File

@@ -1,16 +1,16 @@
#include "SpiderRuntime.hpp" #include "SpiderRuntime.hpp"
#include <spider/runtime/debug/LiveDebug.hpp> #include <spider/runtime/debug/LiveDebug.hpp>
#include <iostream> #include <iostream>
namespace spider { namespace spider {
} }
int main() { int main() {
spider::liveDebugMain(); spider::liveDebugMain();
return 0; return 0;
} }

View File

@@ -1,12 +1,12 @@
#pragma once #pragma once
#include <spider/runtime/common.hpp> #include <spider/runtime/common.hpp>
namespace spider { namespace spider {
class Runtime; class Runtime;
class CPU; class CPU;
class RAM; class RAM;
class InstrReel; class InstrReel;
} }

View File

@@ -0,0 +1,8 @@
// ESP32 entry point for Spider Runtime
// This replaces SpiderRuntime.cpp for microcontroller builds
#include "SpiderRuntime.hpp"
int main() {
// TODO: initialize Spider runtime for ESP32
return 0;
}

View File

@@ -1,29 +1,29 @@
#include "Runtime.hpp" #include "Runtime.hpp"
namespace spider { namespace spider {
// Constructors & Destructors // // Constructors & Destructors //
Runtime::Runtime() : ram(0) {} Runtime::Runtime() : ram(0) {}
Runtime::Runtime(u64 ramSize) : ram(ramSize) {} Runtime::Runtime(u64 ramSize) : ram(ramSize) {}
Runtime::~Runtime() {} Runtime::~Runtime() {}
// Stepping/Running the Machine // // Stepping/Running the Machine //
void Runtime::step() {} void Runtime::step() {}
void Runtime::step(u64 n) {} void Runtime::step(u64 n) {}
void Runtime::run() {} void Runtime::run() {}
void Runtime::run(u64 n) {} void Runtime::run(u64 n) {}
// Misc // // Misc //
void Runtime::resizeRAM(u64 length) { void Runtime::resizeRAM(u64 length) {
ram.resize(length); ram.resize(length);
} }
} }

View File

@@ -1,73 +1,73 @@
#pragma once #pragma once
#include <spider/runtime/cpu/CPU.hpp> #include <spider/runtime/cpu/CPU.hpp>
#include <spider/runtime/memory/RAM.hpp> #include <spider/runtime/memory/RAM.hpp>
namespace spider { namespace spider {
/** /**
* The main runtime class. * The main runtime class.
* This is where the Spider VM (Runtime) lives * This is where the Spider VM (Runtime) lives
*/ */
class Runtime { class Runtime {
public: public:
CPU cpu; CPU cpu;
RAM ram; RAM ram;
public: public:
/** /**
* Creates a new runtime, with no memory. * Creates a new runtime, with no memory.
*/ */
Runtime(); Runtime();
/** /**
* Creates a new runtime, with a specific * Creates a new runtime, with a specific
* amount of memory. * amount of memory.
*/ */
Runtime(u64 ramSize); Runtime(u64 ramSize);
/** /**
* Runtime Destructor. * Runtime Destructor.
*/ */
~Runtime(); ~Runtime();
public: public:
/** /**
* Steps the clock of the VM once. * Steps the clock of the VM once.
*/ */
void step(); void step();
/** /**
* Steps n-times the clock of the VM. * Steps n-times the clock of the VM.
*/ */
void step(u64 n); void step(u64 n);
public: public:
/** /**
* Sets the machine to run continously. * Sets the machine to run continously.
* If interrupts occur, they will be handled * If interrupts occur, they will be handled
* automatically. * automatically.
*/ */
void run(); void run();
/** /**
* Runs this machine for a set amount of * Runs this machine for a set amount of
* milliseconds. * milliseconds.
*/ */
void run(u64 ms); void run(u64 ms);
public: public:
/** /**
* Resizes the ram, which will preserve * Resizes the ram, which will preserve
* data inside the next length. * data inside the next length.
*/ */
void resizeRAM(u64 length); void resizeRAM(u64 length);
}; };
} }

View File

@@ -1,38 +1,38 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
#include <deque> #include <deque>
#include <map> #include <map>
#include <optional> #include <optional>
namespace spider { namespace spider {
// Absolute Types // Absolute Types
using u8 = std::uint8_t; using u8 = std::uint8_t;
using u16 = std::uint16_t; using u16 = std::uint16_t;
using u32 = std::uint32_t; using u32 = std::uint32_t;
using u64 = std::uint64_t; using u64 = std::uint64_t;
using i8 = std::int8_t; using i8 = std::int8_t;
using i16 = std::int16_t; using i16 = std::int16_t;
using i32 = std::int32_t; using i32 = std::int32_t;
using i64 = std::int64_t; using i64 = std::int64_t;
using f32 = float; // TODO: SPIDER_EMULATE_FLOAT will control this using f32 = float; // TODO: SPIDER_EMULATE_FLOAT will control this
using f64 = double; using f64 = double;
// TODO: Check if we're on C++23, there is already stdfloat // TODO: Check if we're on C++23, there is already stdfloat
static_assert(sizeof(f32) == 4, "The f32 type must be exactly 4 bytes."); 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."); static_assert(sizeof(f64) == 8, "The f64 type must be exactly 8 bytes.");
// Utility types // Utility types
using isize = std::size_t; using isize = std::size_t;
// Utility imports // Utility imports
using std::vector; using std::vector;
using std::deque; using std::deque;
using std::map; using std::map;
using std::optional; using std::optional;
} }

View File

@@ -1,111 +1,111 @@
#include "CPU.hpp" #include "CPU.hpp"
#include <spider/runtime/native/machine.hpp> #include <spider/runtime/native/machine.hpp>
#include <spider/runtime/memory/RAM.hpp> #include <spider/runtime/memory/RAM.hpp>
#include <spider/runtime/reel/InstrReel.hpp> #include <spider/runtime/reel/InstrReel.hpp>
#if __cplusplus >= 202002L #if __cplusplus >= 202002L
#include <bit> #include <bit>
#endif #endif
namespace spider { namespace spider {
CPU::CPU() CPU::CPU()
: RA{}, RB{}, RC{}, RD{}, : RA{}, RB{}, RC{}, RD{},
RX{}, RY{}, R0{}, R1{}, RX{}, RY{}, R0{}, R1{},
R2{}, R3{}, R4{}, R5{}, R2{}, R3{}, R4{}, R5{},
R6{}, R7{}, R8{}, R9{}, R6{}, R7{}, R8{}, R9{},
RF{}, RI{}, RS{}, RZ{}, RF{}, RI{}, RS{}, RZ{},
RE{}, RN{}, RV{}, RM{}, RE{}, RN{}, RV{}, RM{},
ALU0{}, ALU1{}, ALU0{}, ALU1{},
_ram(nullptr), _reel(nullptr) { _ram(nullptr), _reel(nullptr) {
} }
CPU::~CPU() {} CPU::~CPU() {}
// Setup & Configuration // // Setup & Configuration //
void CPU::hookRAM(RAM* ram) { void CPU::hookRAM(RAM* ram) {
this->_ram = ram; this->_ram = ram;
} }
void CPU::hookInstrReel(InstrReel* reel) { void CPU::hookInstrReel(InstrReel* reel) {
this->_reel = reel; this->_reel = reel;
} }
constexpr u64 CPU::getFlag(u64 mask) { constexpr u64 CPU::getFlag(u64 mask) {
if (!mask) return 0; if (!mask) return 0;
#if __cplusplus >= 202002L #if __cplusplus >= 202002L
return (RF & mask) >> std::countr_zero(mask); return (RF & mask) >> std::countr_zero(mask);
#elif defined(SPIDER_COMPILER_GCC_LIKE) #elif defined(SPIDER_COMPILER_GCC_LIKE)
return (RF & mask) >> __builtin_ctzll(mask); return (RF & mask) >> __builtin_ctzll(mask);
#elif defined(SPIDER_COMPILER_MSVC) #elif defined(SPIDER_COMPILER_MSVC)
return (RF & mask) >> _BitScanForward64(mask); return (RF & mask) >> _BitScanForward64(mask);
#else #else
// If you have reached this part, // If you have reached this part,
// please come up with a better alternative. // please come up with a better alternative.
u64 bits = RF & mask; u64 bits = RF & mask;
while (mask && (mask >>= 1)) bits >>= 1; while (mask && (mask >>= 1)) bits >>= 1;
return bits; return bits;
#endif #endif
} }
// Addressing Modes // // Addressing Modes //
/** /**
* Implied Addressing Mode * Implied Addressing Mode
*/ */
void CPU::imp() { void CPU::imp() {
// Nothing // // Nothing //
} }
/** /**
* Immediate Addressing Mode * Immediate Addressing Mode
*/ */
void CPU::imm() { void CPU::imm() {
u8 size = 2 << _size; u8 size = 2 << _size;
_next = &ALU0; _next = &ALU0;
} }
/** /**
* Absolute Addressing Mode * Absolute Addressing Mode
*/ */
void CPU::abs() { void CPU::abs() {
u8 size = 2 << getFlag(CPU::FLAG_MEMORY_MODE); u8 size = 2 << getFlag(CPU::FLAG_MEMORY_MODE);
} }
/** /**
* Register Addressing Mode * Register Addressing Mode
*/ */
void CPU::reg() { void CPU::reg() {
sizeof(CPU); sizeof(CPU);
} }
/** /**
* Indrect Addressing Mode * Indrect Addressing Mode
*/ */
void CPU::ind() {} void CPU::ind() {}
/** /**
* Pointer Addressing Mode * Pointer Addressing Mode
*/ */
void CPU::ptr() {} void CPU::ptr() {}
/** /**
* Indexed Addressing Mode * Indexed Addressing Mode
*/ */
void CPU::idx() {} void CPU::idx() {}
/** /**
* Scaled Addressing Mode * Scaled Addressing Mode
*/ */
void CPU::sca() {} void CPU::sca() {}
/** /**
* Displaced Addressing Mode * Displaced Addressing Mode
*/ */
void CPU::dis() {} void CPU::dis() {}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,81 +1,81 @@
#pragma once #pragma once
#include <spider/runtime/common.hpp> #include <spider/runtime/common.hpp>
#include <spider/runtime/native/machine.hpp> #include <spider/runtime/native/machine.hpp>
namespace spider { namespace spider {
/** /**
* A register is a tiny piece of memory. * A register is a tiny piece of memory.
* I hate adding a _t suffix but for some idiotic * I hate adding a _t suffix but for some idiotic
* reason "register" is a keyword in C++. * reason "register" is a keyword in C++.
* *
* Note that we have to check the endianness of the system * Note that we have to check the endianness of the system
* at compile time to order the structure so that smaller * at compile time to order the structure so that smaller
* types are actually the bottom part of the memory. * types are actually the bottom part of the memory.
* *
* Also, this has to be done with a compiler that allows * Also, this has to be done with a compiler that allows
* type-punning which is the "standard" right now. * type-punning which is the "standard" right now.
*/ */
union register_t { union register_t {
u64 _u64; u64 _u64;
i64 _i64; i64 _i64;
f64 _f64; f64 _f64;
u8 _bytes[8]; u8 _bytes[8];
struct { struct {
#if SPIDER_LITTLE_ENDIAN #if SPIDER_LITTLE_ENDIAN
u8 _u8; // This looks like a cruel joke u8 _u8; // This looks like a cruel joke
u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8;
#else #else
u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8; u8 : 8;
u8 _u8; u8 _u8;
#endif #endif
}; };
struct { struct {
#if SPIDER_LITTLE_ENDIAN #if SPIDER_LITTLE_ENDIAN
u16 _u16; u16 _u16;
u16 : 16; u16 : 16; u16 : 16; u16 : 16; u16 : 16; u16 : 16;
#else #else
u16 : 16; u16 : 16; u16 : 16; u16 : 16; u16 : 16; u16 : 16;
u16 _u16; u16 _u16;
#endif #endif
}; };
struct { struct {
#if SPIDER_LITTLE_ENDIAN #if SPIDER_LITTLE_ENDIAN
u32 _u32; u32 : 32; u32 _u32; u32 : 32;
#else #else
u32 : 32; u32 _u32; u32 : 32; u32 _u32;
#endif #endif
}; };
struct { struct {
#if SPIDER_LITTLE_ENDIAN #if SPIDER_LITTLE_ENDIAN
f32 _f32; u32 : 32; f32 _f32; u32 : 32;
#else #else
u32 : 32; f32 _f32; u32 : 32; f32 _f32;
#endif #endif
}; };
u8& operator[](size_t i) { // 0 is always LSB u8& operator[](size_t i) { // 0 is always LSB
#if SPIDER_LITTLE_ENDIAN #if SPIDER_LITTLE_ENDIAN
return _bytes[i]; return _bytes[i];
#else #else
return _bytes[7 - i]; return _bytes[7 - i];
#endif #endif
} }
// ngl I could get executed for not having a const version // ngl I could get executed for not having a const version
const u8& operator[](size_t i) const { // 0 is always LSB const u8& operator[](size_t i) const { // 0 is always LSB
#if SPIDER_LITTLE_ENDIAN #if SPIDER_LITTLE_ENDIAN
return _bytes[i]; return _bytes[i];
#else #else
return _bytes[7 - i]; return _bytes[7 - i];
#endif #endif
} }
}; };
static_assert(sizeof(register_t) == 8, "The register type must be exactly 8 bytes."); static_assert(sizeof(register_t) == 8, "The register type must be exactly 8 bytes.");
} }

View File

@@ -1,386 +1,386 @@
#include "LiveDebug.hpp" #include "LiveDebug.hpp"
#include <spider/runtime/Runtime.hpp> #include <spider/runtime/Runtime.hpp>
#include <spider/runtime/util/Terminal.hpp> #include <spider/runtime/util/Terminal.hpp>
#include <spider/runtime/native/distro.hpp> #include <spider/runtime/native/distro.hpp>
#include <vector> #include <vector>
#include <string> #include <string>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <chrono> #include <chrono>
#include <format> #include <format>
#include <thread> #include <thread>
namespace spider { namespace spider {
void drawHead(Terminal& t) { void drawHead(Terminal& t) {
t.move(1, 1) t.move(1, 1)
.style(Terminal::FG_YELLOW) .style(Terminal::FG_YELLOW)
.print(" Spider Runtime Live Debug ") .print(" Spider Runtime Live Debug ")
.style(Terminal::RESET).print(" | ") .style(Terminal::RESET).print(" | ")
.style(Terminal::FG_B_CYAN).print(" Sintek Analytics @ 2026 ") .style(Terminal::FG_B_CYAN).print(" Sintek Analytics @ 2026 ")
.style(Terminal::RESET).print(" | ") .style(Terminal::RESET).print(" | ")
.style(Terminal::FG_B_BLACK).print("Press ESC to exit") .style(Terminal::FG_B_BLACK).print("Press ESC to exit")
.style(Terminal::FG_BLACK) .style(Terminal::FG_BLACK)
.style(Terminal::BG_YELLOW) .style(Terminal::BG_YELLOW)
.move(3, 1).print(" // __ \\\\").print(" ") // 27 .move(3, 1).print(" // __ \\\\").print(" ") // 27
.move(4, 1).print(" \\\\( )//").print(" SPIDER v0.1 ") .move(4, 1).print(" \\\\( )//").print(" SPIDER v0.1 ")
.move(5, 1).print(" //()\\\\ ").print(" alpha ") .move(5, 1).print(" //()\\\\ ").print(" alpha ")
.move(6, 1).print(" \\\\ // ").print(" ") .move(6, 1).print(" \\\\ // ").print(" ")
.style(Terminal::RESET) .style(Terminal::RESET)
.style(Terminal::FG_B_BLACK) // 4x8 for the menu .style(Terminal::FG_B_BLACK) // 4x8 for the menu
.move(3, 28).print("[ STEP ]") .move(3, 28).print("[ STEP ]")
.move(4, 28).print("[ STOP ]") .move(4, 28).print("[ STOP ]")
.move(5, 28).print("[ RUN ]") .move(5, 28).print("[ RUN ]")
.move(6, 28).print("[ MENU ]") .move(6, 28).print("[ MENU ]")
.style(Terminal::RESET) .style(Terminal::RESET)
; ;
} }
void drawCPUTempl(Terminal& t, CPU& cpu) { void drawCPUTempl(Terminal& t, CPU& cpu) {
i32 r = 8, c = 1; i32 r = 8, c = 1;
i32 w = 35, h = 31; i32 w = 35, h = 31;
t.drawBox(r, c, w, h, "CPU"); t.drawBox(r, c, w, h, "CPU");
const std::string regs[] = { const std::string regs[] = {
"RA", "RB", "RC", "RD", "RA", "RB", "RC", "RD",
"RX", "RY", "R0", "R1", "RX", "RY", "R0", "R1",
"R2", "R3", "R4", "R5", "R2", "R3", "R4", "R5",
"R6", "R7", "R8", "R9", "R6", "R7", "R8", "R9",
"RF", "RI", "RS", "RZ", "RF", "RI", "RS", "RZ",
"RE", "RN", "RV", "RM", "RE", "RN", "RV", "RM",
"ALU0", "ALU1" "ALU0", "ALU1"
}; };
const std::string alt[] = { const std::string alt[] = {
Terminal::FG_WHITE, Terminal::FG_WHITE,
Terminal::FG_B_BLACK, Terminal::FG_B_BLACK,
}; };
r++; r++;
c++; c++;
t.move(r++, c); t.move(r++, c);
t.style(Terminal::FG_B_YELLOW); t.style(Terminal::FG_B_YELLOW);
t.print_center(w - 2, "GP Registers"); t.print_center(w - 2, "GP Registers");
t.style(Terminal::RESET); t.style(Terminal::RESET);
for (i32 i = 0; i < 8; i++) { for (i32 i = 0; i < 8; i++) {
t.style(alt[i & 1]); t.style(alt[i & 1]);
t.move(r + i * 2, c); t.move(r + i * 2, c);
t.print(regs[i * 2]); t.print(regs[i * 2]);
t.move(r + i * 2, c + 17); t.move(r + i * 2, c + 17);
t.print(regs[i * 2 + 1]); t.print(regs[i * 2 + 1]);
} }
t.move(r += 16, c); t.move(r += 16, c);
t.style(Terminal::FG_B_CYAN); t.style(Terminal::FG_B_CYAN);
t.print_center(w - 2, "System Registers"); t.print_center(w - 2, "System Registers");
t.style(Terminal::RESET); t.style(Terminal::RESET);
r++; r++;
for (i32 j = 0, i = 8; i < 12; j++, i++) { for (i32 j = 0, i = 8; i < 12; j++, i++) {
t.style(alt[j & 1]); t.style(alt[j & 1]);
t.move(r + j * 2, c); t.move(r + j * 2, c);
t.print(regs[i * 2]); t.print(regs[i * 2]);
t.move(r + j * 2, c + 17); t.move(r + j * 2, c + 17);
t.print(regs[i * 2 + 1]); t.print(regs[i * 2 + 1]);
} }
t.move(r += 8, c); t.move(r += 8, c);
t.style(Terminal::FG_GREEN); t.style(Terminal::FG_GREEN);
t.print_center(w - 2, "Extra Registers"); t.print_center(w - 2, "Extra Registers");
t.style(Terminal::RESET); t.style(Terminal::RESET);
r++; r++;
for (i32 j = 0, i = 12; i < 13; j++, i++) { for (i32 j = 0, i = 12; i < 13; j++, i++) {
t.style(alt[j & 1]); t.style(alt[j & 1]);
t.move(r + j * 2, c); t.move(r + j * 2, c);
t.print(regs[i * 2]); t.print(regs[i * 2]);
t.move(r + j * 2, c + 17); t.move(r + j * 2, c + 17);
t.print(regs[i * 2 + 1]); t.print(regs[i * 2 + 1]);
} }
t.flush(); t.flush();
} }
void printU64Hex(u64 n) { void printU64Hex(u64 n) {
std::ios state(nullptr); std::ios state(nullptr);
state.copyfmt(std::cout); state.copyfmt(std::cout);
std::cout std::cout
<< std::hex << std::hex
<< std::uppercase << std::uppercase
<< std::setfill('0') << std::setfill('0')
<< std::setw(16) << std::setw(16)
<< n; << n;
std::cout.copyfmt(state); std::cout.copyfmt(state);
} }
void drawCPU(Terminal& t, CPU& cpu) { void drawCPU(Terminal& t, CPU& cpu) {
i32 r = 8, c = 1; i32 r = 8, c = 1;
const register_t* regs[] = { const register_t* regs[] = {
&cpu.RA, &cpu.RB, &cpu.RC, &cpu.RD, &cpu.RA, &cpu.RB, &cpu.RC, &cpu.RD,
&cpu.RX, &cpu.RY, &cpu.R0, &cpu.R1, &cpu.RX, &cpu.RY, &cpu.R0, &cpu.R1,
&cpu.R2, &cpu.R3, &cpu.R4, &cpu.R5, &cpu.R2, &cpu.R3, &cpu.R4, &cpu.R5,
&cpu.R6, &cpu.R7, &cpu.R8, &cpu.R9, &cpu.R6, &cpu.R7, &cpu.R8, &cpu.R9,
//&cpu.RF, &cpu.RI, &cpu.RS, &cpu.RZ, //&cpu.RF, &cpu.RI, &cpu.RS, &cpu.RZ,
//&cpu.RE, &cpu.RN, &cpu.RV, &cpu.RM, //&cpu.RE, &cpu.RN, &cpu.RV, &cpu.RM,
&cpu.ALU0, &cpu.ALU1 &cpu.ALU0, &cpu.ALU1
}; };
const u64* sys_regs[] = { const u64* sys_regs[] = {
&cpu.RF, &cpu.RI, &cpu.RS, &cpu.RZ, &cpu.RF, &cpu.RI, &cpu.RS, &cpu.RZ,
&cpu.RE, &cpu.RN, &cpu.RV, &cpu.RM, &cpu.RE, &cpu.RN, &cpu.RV, &cpu.RM,
}; };
const std::string alt[] = { const std::string alt[] = {
Terminal::FG_WHITE, Terminal::FG_WHITE,
Terminal::FG_B_BLACK, Terminal::FG_B_BLACK,
}; };
r++; r++;
c++; c++;
t.move(r++, c); t.move(r++, c);
t.style(Terminal::RESET); t.style(Terminal::RESET);
r++; r++;
for (i32 i = 0; i < 8; i++) { for (i32 i = 0; i < 8; i++) {
t.style(alt[i & 1]); t.style(alt[i & 1]);
t.move(r + i * 2, c); t.move(r + i * 2, c);
printU64Hex(regs[i * 2]->_u64); printU64Hex(regs[i * 2]->_u64);
t.move(r + i * 2, c + 17); t.move(r + i * 2, c + 17);
printU64Hex(regs[i * 2 + 1]->_u64); printU64Hex(regs[i * 2 + 1]->_u64);
} }
t.move(r += 16, c); t.move(r += 16, c);
r++; r++;
for (i32 j = 0, i = 8; i < 12; j++, i++) { for (i32 j = 0, i = 8; i < 12; j++, i++) {
t.style(alt[j & 1]); t.style(alt[j & 1]);
t.move(r + j * 2, c); t.move(r + j * 2, c);
printU64Hex(*sys_regs[i * 2]); printU64Hex(*sys_regs[i * 2]);
t.move(r + j * 2, c + 17); t.move(r + j * 2, c + 17);
printU64Hex(*sys_regs[i * 2 + 1]); printU64Hex(*sys_regs[i * 2 + 1]);
} }
t.move(r += 8, c); t.move(r += 8, c);
r++; r++;
for (i32 j = 0; j < 1; j++) { for (i32 j = 0; j < 1; j++) {
t.style(alt[j & 1]); t.style(alt[j & 1]);
t.move(r + j * 2, c); t.move(r + j * 2, c);
printU64Hex(regs[16 + j * 2]->_u64); printU64Hex(regs[16 + j * 2]->_u64);
t.move(r + j * 2, c + 17); t.move(r + j * 2, c + 17);
printU64Hex(regs[16 + j * 2 + 1]->_u64); printU64Hex(regs[16 + j * 2 + 1]->_u64);
} }
t.flush(); t.flush();
} }
i32 addressWidth(isize ramSize) { i32 addressWidth(isize ramSize) {
if (ramSize == 0) return 1; if (ramSize == 0) return 1;
isize maxAddr = ramSize - 1; isize maxAddr = ramSize - 1;
i32 digits = 0; i32 digits = 0;
// Shift by increments of 4 (one hex nibble) // Shift by increments of 4 (one hex nibble)
// We use a do-while to ensure at least 1 digit is returned for small RAMs // We use a do-while to ensure at least 1 digit is returned for small RAMs
do { do {
digits++; digits++;
maxAddr >>= 4; maxAddr >>= 4;
} while (maxAddr > 0); } while (maxAddr > 0);
return digits; return digits;
} }
/** /**
* Draws a vertical scrollbar * Draws a vertical scrollbar
* @param x The column where the bar should be placed (usually box_x + width - 1) * @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 y The starting row of the track (usually box_y + 1)
* @param trackHeight The internal height of the box (box_height - 2) * @param trackHeight The internal height of the box (box_height - 2)
* @param progress The current progress * @param progress The current progress
* @param total The total * @param total The total
*/ */
void drawScrollThumb(Terminal& term, i32 x, i32 y, i32 trackHeight, isize progress, isize total) { void drawScrollThumb(Terminal& term, i32 x, i32 y, i32 trackHeight, isize progress, isize total) {
if (total == 0 || trackHeight <= 0) return; if (total == 0 || trackHeight <= 0) return;
// 1. Draw the background track (Light Shade: ░) // 1. Draw the background track (Light Shade: ░)
term.style(Terminal::FG_B_BLACK); // Dim the track term.style(Terminal::FG_B_BLACK); // Dim the track
for (int i = 0; i < trackHeight; ++i) { for (int i = 0; i < trackHeight; ++i) {
term.move(y + i, x).print(""); term.move(y + i, x).print("");
} }
// 2. Calculate Thumb Position // 2. Calculate Thumb Position
// Cap progress to total to avoid overflow // Cap progress to total to avoid overflow
if (progress > total) progress = total; if (progress > total) progress = total;
// Calculate ratio (0.0 to 1.0) // Calculate ratio (0.0 to 1.0)
f64 ratio = f64(progress) / f64(total); f64 ratio = f64(progress) / f64(total);
// Map to track coordinates // Map to track coordinates
i32 thumbOffset = i32(ratio * (trackHeight - 1)); i32 thumbOffset = i32(ratio * (trackHeight - 1));
// 3. Draw the Thumb (Full Block: █) // 3. Draw the Thumb (Full Block: █)
term.move(y + thumbOffset, x); term.move(y + thumbOffset, x);
term.style(Terminal::FG_WHITE).print(""); term.style(Terminal::FG_WHITE).print("");
term.style(Terminal::RESET); term.style(Terminal::RESET);
} }
/** /**
* Draws a hex dump of memory within a styled terminal box. * Draws a hex dump of memory within a styled terminal box.
* @param term Reference to your Terminal instance * @param term Reference to your Terminal instance
* @param ram The RAM * @param ram The RAM
* @param scrollPos The starting address to display * @param scrollPos The starting address to display
* @param x Starting column * @param x Starting column
* @param y Starting row * @param y Starting row
* @param width Width of the box * @param width Width of the box
* @param height Height of the box * @param height Height of the box
*/ */
void drawRAM(Terminal& term, RAM& ram, u64 scrollPos) { void drawRAM(Terminal& term, RAM& ram, u64 scrollPos) {
// 1. Draw the container box // 1. Draw the container box
i32 y = 3; i32 y = 3;
i32 height = 36; i32 height = 36;
// 2. Configuration for the hex layout // 2. Configuration for the hex layout
int addrWidth = addressWidth(ram.size()); int addrWidth = addressWidth(ram.size());
int bytesPerRow = 8; int bytesPerRow = 8;
int displayRows = height - 2; // Subtract top/bottom borders int displayRows = height - 2; // Subtract top/bottom borders
i32 width = (2 + 2 + 16 + 7 + 3 + 8 + 4) + addrWidth; i32 width = (2 + 2 + 16 + 7 + 3 + 8 + 4) + addrWidth;
i32 x = 37; i32 x = 37;
// create box // create box
term.drawBox(y, x, width, height, "RAM"); term.drawBox(y, x, width, height, "RAM");
drawScrollThumb(term, x + width - 2, y + 1, height - 2, scrollPos, ram.size()); drawScrollThumb(term, x + width - 2, y + 1, height - 2, scrollPos, ram.size());
// Ensure scrollPos is within bounds and aligned // Ensure scrollPos is within bounds and aligned
if (scrollPos < 0) scrollPos = 0; if (scrollPos < 0) scrollPos = 0;
if (scrollPos > ram.size()) scrollPos = ram.size(); if (scrollPos > ram.size()) scrollPos = ram.size();
for (int i = 0; i < displayRows; ++i) { for (int i = 0; i < displayRows; ++i) {
isize currentRowAddr = scrollPos + (i * bytesPerRow); isize currentRowAddr = scrollPos + (i * bytesPerRow);
// address lock // address lock
if (currentRowAddr >= ram.size()) { if (currentRowAddr >= ram.size()) {
term.move(y + 1 + i, x + 1); term.move(y + 1 + i, x + 1);
term.print(std::string(width - 3, ' ')); term.print(std::string(width - 3, ' '));
continue; continue;
} }
std::stringstream ssaddr; std::stringstream ssaddr;
std::stringstream ss; std::stringstream ss;
// setup ss // setup ss
ssaddr << std::setfill('0') << std::uppercase << std::hex; ssaddr << std::setfill('0') << std::uppercase << std::hex;
ss << std::setfill('0') << std::uppercase << std::hex; ss << std::setfill('0') << std::uppercase << std::hex;
// address // address
ssaddr << std::setw(addrWidth) << currentRowAddr << " "; ssaddr << std::setw(addrWidth) << currentRowAddr << " ";
// Hex Bytes // Hex Bytes
std::string asciiPart = ""; std::string asciiPart = "";
for (int j = 0; j < bytesPerRow; ++j) { for (int j = 0; j < bytesPerRow; ++j) {
isize targetAddr = currentRowAddr + j; isize targetAddr = currentRowAddr + j;
if (targetAddr >= ram.size()) { if (targetAddr >= ram.size()) {
ss << ""; // Padding for end of memory ss << ""; // Padding for end of memory
asciiPart += ""; asciiPart += "";
continue; continue;
} }
u8 byte = ram[targetAddr]; u8 byte = ram[targetAddr];
ss << std::setfill('0') << std::setw(2) << std::hex << (u32)byte << " "; ss << std::setfill('0') << std::setw(2) << std::hex << (u32)byte << " ";
asciiPart += (std::isprint(byte) ? (char)byte : '.'); asciiPart += (std::isprint(byte) ? (char)byte : '.');
} }
// --- Combine and Print --- // --- Combine and Print ---
term.move(y + 1 + i, x + 2); // Move inside the box 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_B_CYAN).print(ssaddr.str()); // Hex part in Cyan
term.style(Terminal::FG_WHITE).print(ss.str()); term.style(Terminal::FG_WHITE).print(ss.str());
term.style(Terminal::FG_B_YELLOW).print(" | "); term.style(Terminal::FG_B_YELLOW).print(" | ");
term.style(Terminal::FG_WHITE).print(asciiPart); // ASCII part in White term.style(Terminal::FG_WHITE).print(asciiPart); // ASCII part in White
} }
term.style(Terminal::RESET); term.style(Terminal::RESET);
term.flush(); term.flush();
} }
std::string getTimestamp() { std::string getTimestamp() {
std::time_t t = std::time(nullptr); std::time_t t = std::time(nullptr);
std::tm lt; std::tm lt;
#if defined(SPIDER_OS_WINDOWS) #if defined(SPIDER_OS_WINDOWS)
localtime_s(&lt, &t); localtime_s(&lt, &t);
#endif #endif
#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS) #if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS)
localtime_r(&t, &lt); localtime_r(&t, &lt);
#endif #endif
return std::format("{:02}:{:02}:{:02} {:02}/{:02}/{}", return std::format("{:02}:{:02}:{:02} {:02}/{:02}/{}",
lt.tm_hour, lt.tm_min, lt.tm_sec, lt.tm_hour, lt.tm_min, lt.tm_sec,
lt.tm_mday, lt.tm_mon + 1, lt.tm_year + 1900); lt.tm_mday, lt.tm_mon + 1, lt.tm_year + 1900);
} }
void drawTime(Terminal& t) { void drawTime(Terminal& t) {
//auto now = std::chrono::system_clock::now(); //auto now = std::chrono::system_clock::now();
//auto now_l = std::chrono::current_zone()->to_local(now); //auto now_l = std::chrono::current_zone()->to_local(now);
//auto now_s = std::chrono::floor<std::chrono::seconds>(now_l); //auto now_s = std::chrono::floor<std::chrono::seconds>(now_l);
//std::string time_str = std::format("{:%H:%M:%S}", now_s); // Format: HH:mm:ss //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 //std::string date_str = std::format("{:%d/%m/%Y}", now_s); // Format: dd/MM/YYYY
t.move(1, 76); t.move(1, 76);
t.style(Terminal::RESET); t.style(Terminal::RESET);
t.print(" | ").style(Terminal::FG_GREEN).print(getTimestamp()); t.print(" | ").style(Terminal::FG_GREEN).print(getTimestamp());
} }
void redraw(Terminal& t, Runtime& r, u64 scroll) { void redraw(Terminal& t, Runtime& r, u64 scroll) {
// draw CPU, RAM // draw CPU, RAM
drawCPU(t, r.cpu); drawCPU(t, r.cpu);
drawRAM(t, r.ram, scroll); drawRAM(t, r.ram, scroll);
} }
int liveDebugMain() { int liveDebugMain() {
Terminal t; Terminal t;
Runtime runtime(1024); Runtime runtime(1024);
bool running = true, update = true; bool running = true, update = true;
u64 ramScroll = 0; u64 ramScroll = 0;
u8 key = Terminal::UNKNOWN; u8 key = Terminal::UNKNOWN;
t.println("Starting Spider live debug..."); t.println("Starting Spider live debug...");
t.altbuff(true).cursor(false); t.altbuff(true).cursor(false);
drawTime(t); drawTime(t);
drawHead(t); drawHead(t);
drawCPUTempl(t, runtime.cpu); drawCPUTempl(t, runtime.cpu);
// delay for time // delay for time
auto last_exec = std::chrono::steady_clock::now(); auto last_exec = std::chrono::steady_clock::now();
auto delay = std::chrono::milliseconds(1000); auto delay = std::chrono::milliseconds(1000);
while (running) { while (running) {
// draw time // draw time
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
if (now - last_exec >= delay) { if (now - last_exec >= delay) {
drawTime(t); drawTime(t);
last_exec = now; last_exec = now;
} }
// redraw something if it updated // redraw something if it updated
if (update) { if (update) {
redraw(t, runtime, ramScroll); redraw(t, runtime, ramScroll);
update = false; update = false;
} }
// Handle Input // Handle Input
key = t.getKeyNb(); key = t.getKeyNb();
switch (key) { switch (key) {
case Terminal::ESC: case Terminal::ESC:
running = false; running = false;
break; break;
case Terminal::UP: case Terminal::UP:
if (ramScroll >= 16) ramScroll -= 16; if (ramScroll >= 16) ramScroll -= 16;
update = true; update = true;
break; break;
case Terminal::DOWN: case Terminal::DOWN:
if (runtime.ram.size() >= 16 && ramScroll <= runtime.ram.size() - 16) ramScroll += 16; if (runtime.ram.size() >= 16 && ramScroll <= runtime.ram.size() - 16) ramScroll += 16;
update = true; update = true;
break; break;
default: default:
break; break;
} }
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
t.altbuff(false).println("Stopped Spider live debug.").flush(); t.altbuff(false).println("Stopped Spider live debug.").flush();
return 0; return 0;
} }
} }

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
namespace spider { namespace spider {
int liveDebugMain(); int liveDebugMain();
} }

View File

@@ -1,9 +1,9 @@
#include <spider/runtime/cpu/CPU.hpp> #include <spider/runtime/cpu/CPU.hpp>
namespace spider { namespace spider {
void CPU::NOP() { void CPU::NOP() {
// No Operation // // No Operation //
} }
} }

View File

@@ -1,22 +1,22 @@
#include "Quat.hpp" #include "Quat.hpp"
#include <iostream> #include <iostream>
namespace spider { namespace spider {
int quatMain() { int quatMain() {
Quat<double> q1 = { 1.0f, 0.0f, 0.0f, 0.0f }; Quat<double> q1 = { 1.0f, 0.0f, 0.0f, 0.0f };
Quat<double> q2 = { 0.5f, 0.5f, 0.5f, 0.5f }; Quat<double> q2 = { 0.5f, 0.5f, 0.5f, 0.5f };
Quat<double> result = quat_multiply(q1, q2); // Returns the result! Quat<double> result = quat_multiply(q1, q2); // Returns the result!
std::cout << "Result: (" std::cout << "Result: ("
<< result.w << ", " << result.w << ", "
<< result.x << ", " << result.x << ", "
<< result.y << ", " << result.y << ", "
<< result.z << ")" << std::endl; << result.z << ")" << std::endl;
return 0; return 0;
} }
} }

View File

@@ -1,24 +1,24 @@
#pragma once #pragma once
#include <spider/runtime/common.hpp> #include <spider/runtime/common.hpp>
namespace spider { namespace spider {
template<typename T> template<typename T>
struct Quat { struct Quat {
T w, x, y, z; T w, x, y, z;
}; };
/** /**
* Multiplies two quaternions together. * Multiplies two quaternions together.
*/ */
template<typename T> inline Quat<T> quat_multiply(Quat<T> A, Quat<T> B) { template<typename T> inline Quat<T> quat_multiply(Quat<T> A, Quat<T> B) {
return { return {
B.w * A.w - B.x * A.x - B.y * A.y - B.z * A.z, B.w * A.w - B.x * A.x - B.y * A.y - B.z * A.z,
B.w * A.x + B.x * A.w - B.y * A.z + B.z * A.y, B.w * A.x + B.x * A.w - B.y * A.z + B.z * A.y,
B.w * A.y + B.x * A.z + B.y * A.w - B.z * A.x, B.w * A.y + B.x * A.z + B.y * A.w - B.z * A.x,
B.w * A.z - B.x * A.y + B.y * A.x + B.z * A.w B.w * A.z - B.x * A.y + B.y * A.x + B.z * A.w
}; };
} }
} }

View File

@@ -1,74 +1,74 @@
#include "ByteArray.hpp" #include "ByteArray.hpp"
#include <cstring> #include <cstring>
namespace spider { namespace spider {
ByteArray::ByteArray(isize length) : _mem(nullptr), _size(length) { ByteArray::ByteArray(isize length) : _mem(nullptr), _size(length) {
if (_size > 0) { if (_size > 0) {
_mem = new u8[_size]; _mem = new u8[_size];
std::memset(_mem, 0, _size); std::memset(_mem, 0, _size);
} }
} }
ByteArray::ByteArray(const ByteArray& other) : _mem(new u8[other._size]), _size(other._size) { ByteArray::ByteArray(const ByteArray& other) : _mem(new u8[other._size]), _size(other._size) {
std::copy(other._mem, other._mem + _size, _mem); std::copy(other._mem, other._mem + _size, _mem);
} }
ByteArray::ByteArray(ByteArray&& other) noexcept : _mem(other._mem), _size(other._size) { ByteArray::ByteArray(ByteArray&& other) noexcept : _mem(other._mem), _size(other._size) {
other._mem = nullptr; other._mem = nullptr;
other._size = 0; other._size = 0;
} }
ByteArray::~ByteArray() { ByteArray::~ByteArray() {
delete[] _mem; delete[] _mem;
} }
ByteArray& ByteArray::operator=(const ByteArray& other) { ByteArray& ByteArray::operator=(const ByteArray& other) {
if (this == &other) return *this; // lock self if (this == &other) return *this; // lock self
u8* new_mem = new u8[other._size]; u8* new_mem = new u8[other._size];
std::copy(other._mem, other._mem + other._size, new_mem); std::copy(other._mem, other._mem + other._size, new_mem);
delete[] _mem; delete[] _mem;
_mem = new_mem; _mem = new_mem;
_size = other._size; _size = other._size;
return *this; return *this;
} }
ByteArray& ByteArray::operator=(ByteArray&& other) noexcept { ByteArray& ByteArray::operator=(ByteArray&& other) noexcept {
if (this == &other) return *this; // lock self if (this == &other) return *this; // lock self
delete[] _mem; delete[] _mem;
_mem = other._mem; // time to steal! _mem = other._mem; // time to steal!
_size = other._size; _size = other._size;
other._mem = nullptr; // leave as a husk other._mem = nullptr; // leave as a husk
other._size = 0; other._size = 0;
return *this; return *this;
} }
u8& ByteArray::operator[](isize index) { u8& ByteArray::operator[](isize index) {
return _mem[index]; return _mem[index];
} }
u8 ByteArray::operator[](isize index) const { u8 ByteArray::operator[](isize index) const {
return _mem[index]; return _mem[index];
} }
u8* ByteArray::data() { u8* ByteArray::data() {
return _mem; return _mem;
} }
const u8* ByteArray::data() const { const u8* ByteArray::data() const {
return _mem; return _mem;
} }
isize ByteArray::size() const { isize ByteArray::size() const {
return _size; return _size;
} }
} }

View File

@@ -1,49 +1,49 @@
#pragma once #pragma once
#include <spider/runtime/common.hpp> #include <spider/runtime/common.hpp>
namespace spider { namespace spider {
/** /**
* A general purpose byte array * A general purpose byte array
* with RAII semantics. * with RAII semantics.
*/ */
class ByteArray { class ByteArray {
private: private:
u8* _mem; u8* _mem;
isize _size; isize _size;
public: public:
ByteArray(isize length); ByteArray(isize length);
ByteArray(const ByteArray& other); ByteArray(const ByteArray& other);
ByteArray(ByteArray&& other) noexcept; ByteArray(ByteArray&& other) noexcept;
~ByteArray(); ~ByteArray();
public: public:
ByteArray& operator=(const ByteArray& other); ByteArray& operator=(const ByteArray& other);
ByteArray& operator=(ByteArray&& other) noexcept; ByteArray& operator=(ByteArray&& other) noexcept;
public: public:
u8& operator[](isize index); u8& operator[](isize index);
u8 operator[](isize index) const; u8 operator[](isize index) const;
public: public:
u8* data(); u8* data();
const u8* data() const; const u8* data() const;
isize size() const; isize size() const;
}; };
} }

View File

@@ -1,130 +1,130 @@
#include "RAM.hpp" #include "RAM.hpp"
#include <cstring> #include <cstring>
namespace spider { namespace spider {
// Constructors & Destructors // // Constructors & Destructors //
RAM::RAM(u64 length) : _mem(nullptr), _size(length), _oob(0) { RAM::RAM(u64 length) : _mem(nullptr), _size(length), _oob(0) {
if (_size > 0) { if (_size > 0) {
_mem = new u8[_size]; _mem = new u8[_size];
std::memset(_mem, 0, _size); std::memset(_mem, 0, _size);
} }
} }
RAM::RAM(const RAM& other) : _size(other._size), _oob(0) { RAM::RAM(const RAM& other) : _size(other._size), _oob(0) {
_mem = new u8[_size]; _mem = new u8[_size];
std::copy(other._mem, other._mem + _size, _mem); std::copy(other._mem, other._mem + _size, _mem);
} }
RAM::RAM(RAM&& other) noexcept : _mem(other._mem), _size(other._size), _oob(0) { RAM::RAM(RAM&& other) noexcept : _mem(other._mem), _size(other._size), _oob(0) {
other._mem = nullptr; other._mem = nullptr;
other._size = 0; other._size = 0;
} }
RAM::~RAM() { RAM::~RAM() {
delete[] _mem; delete[] _mem;
} }
// Assign Operators // // Assign Operators //
RAM& RAM::operator=(const RAM& other) { RAM& RAM::operator=(const RAM& other) {
if (this == &other) return *this; // lock self if (this == &other) return *this; // lock self
u8* new_mem = new u8[other._size]; u8* new_mem = new u8[other._size];
std::copy(other._mem, other._mem + other._size, new_mem); std::copy(other._mem, other._mem + other._size, new_mem);
delete[] _mem; delete[] _mem;
_mem = new_mem; _mem = new_mem;
_size = other._size; _size = other._size;
return *this; return *this;
} }
RAM& RAM::operator=(RAM&& other) noexcept { RAM& RAM::operator=(RAM&& other) noexcept {
if (this == &other) return *this; // lock self if (this == &other) return *this; // lock self
delete[] _mem; delete[] _mem;
_mem = other._mem; // time to steal! _mem = other._mem; // time to steal!
_size = other._size; _size = other._size;
other._mem = nullptr; // leave as a husk other._mem = nullptr; // leave as a husk
other._size = 0; other._size = 0;
return *this; return *this;
} }
// Unsafe Access // // Unsafe Access //
u8& RAM::operator[](u64 i) { return _mem[i]; } u8& RAM::operator[](u64 i) { return _mem[i]; }
u8 RAM::operator[](u64 i) const { return _mem[i]; } u8 RAM::operator[](u64 i) const { return _mem[i]; }
// Managed Access // // Managed Access //
u8& RAM::at(u64 i) { u8& RAM::at(u64 i) {
return (i < _size) ? _mem[i] : _oob; return (i < _size) ? _mem[i] : _oob;
} }
u8 RAM::at(u64 i) const { u8 RAM::at(u64 i) const {
return (i < _size) ? _mem[i] : _oob; return (i < _size) ? _mem[i] : _oob;
} }
// Misc // // Misc //
void RAM::resize(u64 new_size) { void RAM::resize(u64 new_size) {
// Special case 1 // Special case 1
if (new_size == _size) return; if (new_size == _size) return;
// Special case 2 // Special case 2
if (new_size == 0) { if (new_size == 0) {
delete[] _mem; delete[] _mem;
_mem = nullptr; _mem = nullptr;
_size = 0; _size = 0;
return; return;
} }
// 1. Allocate the new block // 1. Allocate the new block
u8* new_mem = new u8[new_size]; u8* new_mem = new u8[new_size];
// 2. Zero-initialize // 2. Zero-initialize
std::memset(new_mem, 0, new_size); std::memset(new_mem, 0, new_size);
// 3. Preserve data // 3. Preserve data
// If shrinking, copy 'new_size' bytes. If growing, copy 'old_size' bytes. // If shrinking, copy 'new_size' bytes. If growing, copy 'old_size' bytes.
u64 bytes_to_copy = (new_size < _size) ? new_size : _size; u64 bytes_to_copy = (new_size < _size) ? new_size : _size;
// 3.1 Previous size could be zero, where _mem would be null // 3.1 Previous size could be zero, where _mem would be null
if (_mem != nullptr) { if (_mem != nullptr) {
std::copy(_mem, _mem + bytes_to_copy, new_mem); std::copy(_mem, _mem + bytes_to_copy, new_mem);
} }
// 4. Swap and Clean up // 4. Swap and Clean up
delete[] _mem; delete[] _mem;
_mem = new_mem; _mem = new_mem;
_size = new_size; _size = new_size;
} }
u64 RAM::size() const { u64 RAM::size() const {
return _size; return _size;
} }
u8* RAM::begin() { u8* RAM::begin() {
return this->_mem; return this->_mem;
} }
u8* RAM::end() { u8* RAM::end() {
return this->_mem + this->_size; return this->_mem + this->_size;
} }
const u8* RAM::begin() const { const u8* RAM::begin() const {
return this->_mem; return this->_mem;
} }
const u8* RAM::end() const { const u8* RAM::end() const {
return this->_mem + this->_size; return this->_mem + this->_size;
} }
} }

View File

@@ -1,64 +1,64 @@
#pragma once #pragma once
#include <spider/runtime/common.hpp> #include <spider/runtime/common.hpp>
namespace spider { namespace spider {
/** /**
* A memory container. * A memory container.
* As a reminder, the amount of RAM * As a reminder, the amount of RAM
* is designed by the host. * is designed by the host.
*/ */
class RAM { class RAM {
private: private:
u8* _mem; u8* _mem;
u64 _size; u64 _size;
u8 _oob; // Out of bounds reference u8 _oob; // Out of bounds reference
public: public:
RAM(u64 length); RAM(u64 length);
RAM(const RAM& other); RAM(const RAM& other);
RAM(RAM&& other) noexcept; RAM(RAM&& other) noexcept;
~RAM(); ~RAM();
public: public:
RAM& operator=(const RAM& other); RAM& operator=(const RAM& other);
RAM& operator=(RAM&& other) noexcept; RAM& operator=(RAM&& other) noexcept;
public: // Unsafe access public: // Unsafe access
u8& operator[](u64 i); u8& operator[](u64 i);
u8 operator[](u64 i) const; u8 operator[](u64 i) const;
public: // managed access (oob = 0) public: // managed access (oob = 0)
u8& at(u64 i); u8& at(u64 i);
u8 at(u64 i) const; u8 at(u64 i) const;
public: public:
void resize(u64 new_size); void resize(u64 new_size);
u64 size() const; u64 size() const;
public: public:
u8* begin(); u8* begin();
u8* end(); u8* end();
const u8* begin() const; const u8* begin() const;
const u8* end() const; const u8* end() const;
}; };
} }

View File

@@ -1,250 +1,250 @@
#pragma once #pragma once
#include <spider/runtime/common.hpp> #include <spider/runtime/common.hpp>
#include <spider/runtime/native/machine.hpp> #include <spider/runtime/native/machine.hpp>
#if __cplusplus >= 202002L #if __cplusplus >= 202002L
#include <bit> #include <bit>
#endif #endif
#if defined(SPIDER_COMPILER_MSVC) #if defined(SPIDER_COMPILER_MSVC)
#include <cstdlib> #include <cstdlib>
#endif #endif
#include <cstring> #include <cstring>
namespace spider { namespace spider {
/* /*
* This file contains cross platform type juggling. * This file contains cross platform type juggling.
* Optimized for each case. * Optimized for each case.
* *
* General assumtions is that unsigned and signed * General assumtions is that unsigned and signed
* integers are the exact same bit representation. * integers are the exact same bit representation.
* *
* Floats to and from integers is the focus when * Floats to and from integers is the focus when
* juggling. * juggling.
* *
* Additionally, provides help selecting a specific * Additionally, provides help selecting a specific
* type from raw bytes. * type from raw bytes.
*/ */
// Utilities // // Utilities //
#if __cplusplus >= 202002L #if __cplusplus >= 202002L
using std::bit_cast; using std::bit_cast;
#else #else
template<typename To, typename From> template<typename To, typename From>
inline To bit_cast(const From& src) { inline To bit_cast(const From& src) {
static_assert(sizeof(To) == sizeof(From), "bit_cast size mismatch"); static_assert(sizeof(To) == sizeof(From), "bit_cast size mismatch");
To dst; To dst;
std::memcpy(&dst, &src, sizeof(To)); std::memcpy(&dst, &src, sizeof(To));
return dst; return dst;
} }
#endif #endif
template<typename T> template<typename T>
inline T byteswap(T v) { inline T byteswap(T v) {
static_assert(std::is_integral<T>::value, "byteswap requires integral type"); static_assert(std::is_integral<T>::value, "byteswap requires integral type");
using U = std::make_unsigned_t<T>; using U = std::make_unsigned_t<T>;
U u = static_cast<U>(v); U u = static_cast<U>(v);
if constexpr (sizeof(T) == 1) { if constexpr (sizeof(T) == 1) {
return v; return v;
} else if constexpr (sizeof(T) == 2) { } else if constexpr (sizeof(T) == 2) {
#if defined(SPIDER_COMPILER_MSVC) #if defined(SPIDER_COMPILER_MSVC)
u = _byteswap_ushort(u); u = _byteswap_ushort(u);
#elif defined(SPIDER_COMPILER_GCC_LIKE) #elif defined(SPIDER_COMPILER_GCC_LIKE)
u = __builtin_bswap16(u); u = __builtin_bswap16(u);
#else #else
u = (u >> 8) | (u << 8); u = (u >> 8) | (u << 8);
#endif #endif
} else if constexpr (sizeof(T) == 4) { } else if constexpr (sizeof(T) == 4) {
#if defined(SPIDER_COMPILER_MSVC) #if defined(SPIDER_COMPILER_MSVC)
u = _byteswap_ulong(u); u = _byteswap_ulong(u);
#elif defined(SPIDER_COMPILER_GCC_LIKE) #elif defined(SPIDER_COMPILER_GCC_LIKE)
u = __builtin_bswap32(u); u = __builtin_bswap32(u);
#else #else
u = u =
((u & 0x000000FFu) << 24) | ((u & 0x000000FFu) << 24) |
((u & 0x0000FF00u) << 8) | ((u & 0x0000FF00u) << 8) |
((u & 0x00FF0000u) >> 8) | ((u & 0x00FF0000u) >> 8) |
((u & 0xFF000000u) >> 24); ((u & 0xFF000000u) >> 24);
#endif #endif
} else if constexpr (sizeof(T) == 8) { } else if constexpr (sizeof(T) == 8) {
#if defined(SPIDER_COMPILER_MSVC) #if defined(SPIDER_COMPILER_MSVC)
u = _byteswap_uint64(u); u = _byteswap_uint64(u);
#elif defined(SPIDER_COMPILER_GCC_LIKE) #elif defined(SPIDER_COMPILER_GCC_LIKE)
u = __builtin_bswap64(u); u = __builtin_bswap64(u);
#else #else
u = u =
((u & 0x00000000000000FFull) << 56) | ((u & 0x00000000000000FFull) << 56) |
((u & 0x000000000000FF00ull) << 40) | ((u & 0x000000000000FF00ull) << 40) |
((u & 0x0000000000FF0000ull) << 24) | ((u & 0x0000000000FF0000ull) << 24) |
((u & 0x00000000FF000000ull) << 8) | ((u & 0x00000000FF000000ull) << 8) |
((u & 0x000000FF00000000ull) >> 8) | ((u & 0x000000FF00000000ull) >> 8) |
((u & 0x0000FF0000000000ull) >> 24) | ((u & 0x0000FF0000000000ull) >> 24) |
((u & 0x00FF000000000000ull) >> 40) | ((u & 0x00FF000000000000ull) >> 40) |
((u & 0xFF00000000000000ull) >> 56); ((u & 0xFF00000000000000ull) >> 56);
#endif #endif
} else { } else {
// Generic fallback (rare: non 1/2/4/8-byte types) // Generic fallback (rare: non 1/2/4/8-byte types)
U result = 0; U result = 0;
for (size_t i = 0; i < sizeof(T); ++i) { for (size_t i = 0; i < sizeof(T); ++i) {
result |= ((u >> (i * 8)) & 0xFF) << ((sizeof(T) - 1 - i) * 8); result |= ((u >> (i * 8)) & 0xFF) << ((sizeof(T) - 1 - i) * 8);
} }
u = result; u = result;
} }
return static_cast<T>(u); return static_cast<T>(u);
} }
// Store Big Endian // // Store Big Endian //
template<typename T> template<typename T>
inline void storeBE(T n, u8* bytes) { inline void storeBE(T n, u8* bytes) {
static_assert(std::is_trivially_copyable<T>::value); static_assert(std::is_trivially_copyable<T>::value);
#if SPIDER_BIG_ENDIAN #if SPIDER_BIG_ENDIAN
std::memcpy(bytes, &n, sizeof(T)); std::memcpy(bytes, &n, sizeof(T));
#endif #endif
#if SPIDER_LITTLE_ENDIAN #if SPIDER_LITTLE_ENDIAN
T tmp = byteswap(n); T tmp = byteswap(n);
std::memcpy(bytes, &tmp, sizeof(T)); std::memcpy(bytes, &tmp, sizeof(T));
#endif #endif
#if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN #if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN
for (size_t i = 0; i < sizeof(T); ++i) { for (size_t i = 0; i < sizeof(T); ++i) {
bytes[i] = static_cast<u8>( bytes[i] = static_cast<u8>(
(static_cast<std::make_unsigned_t<T>>(n) >> ((sizeof(T) - 1 - i) * 8)) & 0xFF (static_cast<std::make_unsigned_t<T>>(n) >> ((sizeof(T) - 1 - i) * 8)) & 0xFF
); );
} }
#endif #endif
} }
template<> template<>
inline void storeBE<f32>(f32 n, u8* bytes) { inline void storeBE<f32>(f32 n, u8* bytes) {
u32 tmp = bit_cast<u32>(n); u32 tmp = bit_cast<u32>(n);
storeBE(tmp, bytes); storeBE(tmp, bytes);
} }
template<> template<>
inline void storeBE<f64>(f64 n, u8* bytes) { inline void storeBE<f64>(f64 n, u8* bytes) {
u64 tmp = bit_cast<u64>(n); u64 tmp = bit_cast<u64>(n);
storeBE(tmp, bytes); storeBE(tmp, bytes);
} }
// Load Big Endian // // Load Big Endian //
template<typename T> template<typename T>
inline void loadBE(T* n, const u8* bytes) { inline void loadBE(T* n, const u8* bytes) {
static_assert(std::is_trivially_copyable<T>::value); static_assert(std::is_trivially_copyable<T>::value);
#if SPIDER_BIG_ENDIAN #if SPIDER_BIG_ENDIAN
std::memcpy(n, bytes, sizeof(T)); std::memcpy(n, bytes, sizeof(T));
#endif #endif
#if SPIDER_LITTLE_ENDIAN #if SPIDER_LITTLE_ENDIAN
T tmp; T tmp;
std::memcpy(&tmp, bytes, sizeof(T)); std::memcpy(&tmp, bytes, sizeof(T));
*n = byteswap(tmp); *n = byteswap(tmp);
#endif #endif
#if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN #if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN
using U = std::make_unsigned_t<T>; using U = std::make_unsigned_t<T>;
U result = 0; U result = 0;
for (size_t i = 0; i < sizeof(T); ++i) { for (size_t i = 0; i < sizeof(T); ++i) {
result |= static_cast<U>(bytes[i]) << ((sizeof(T) - 1 - i) * 8); result |= static_cast<U>(bytes[i]) << ((sizeof(T) - 1 - i) * 8);
} }
*n = static_cast<T>(result); *n = static_cast<T>(result);
#endif #endif
} }
template<> template<>
inline void loadBE<f32>(f32* n, const u8* bytes) { inline void loadBE<f32>(f32* n, const u8* bytes) {
u32 tmp; u32 tmp;
loadBE(&tmp, bytes); loadBE(&tmp, bytes);
*n = bit_cast<f32>(tmp); *n = bit_cast<f32>(tmp);
} }
template<> template<>
inline void loadBE<f64>(f64* n, const u8* bytes) { inline void loadBE<f64>(f64* n, const u8* bytes) {
u64 tmp; u64 tmp;
loadBE(&tmp, bytes); loadBE(&tmp, bytes);
*n = bit_cast<f64>(tmp); *n = bit_cast<f64>(tmp);
} }
// Store Little Endian // // Store Little Endian //
template<typename T> template<typename T>
inline void storeLE(T n, u8* bytes) { inline void storeLE(T n, u8* bytes) {
static_assert(std::is_trivially_copyable<T>::value); static_assert(std::is_trivially_copyable<T>::value);
#if SPIDER_BIG_ENDIAN #if SPIDER_BIG_ENDIAN
T tmp = byteswap(n); T tmp = byteswap(n);
std::memcpy(bytes, &tmp, sizeof(T)); std::memcpy(bytes, &tmp, sizeof(T));
#endif #endif
#if SPIDER_LITTLE_ENDIAN #if SPIDER_LITTLE_ENDIAN
std::memcpy(bytes, &n, sizeof(T)); std::memcpy(bytes, &n, sizeof(T));
#endif #endif
#if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN #if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN
for (size_t i = 0; i < sizeof(T); ++i) { for (size_t i = 0; i < sizeof(T); ++i) {
bytes[i] = static_cast<u8>( bytes[i] = static_cast<u8>(
(static_cast<std::make_unsigned_t<T>>(n) >> (i * 8)) & 0xFF (static_cast<std::make_unsigned_t<T>>(n) >> (i * 8)) & 0xFF
); );
} }
#endif #endif
} }
template<> template<>
inline void storeLE<f32>(f32 n, u8* bytes) { inline void storeLE<f32>(f32 n, u8* bytes) {
u32 tmp = bit_cast<u32>(n); u32 tmp = bit_cast<u32>(n);
storeLE(tmp, bytes); storeLE(tmp, bytes);
} }
template<> template<>
inline void storeLE<f64>(f64 n, u8* bytes) { inline void storeLE<f64>(f64 n, u8* bytes) {
u64 tmp = bit_cast<u64>(n); u64 tmp = bit_cast<u64>(n);
storeLE(tmp, bytes); storeLE(tmp, bytes);
} }
// Load Little Endian // // Load Little Endian //
template<typename T> template<typename T>
inline void loadLE(T* n, const u8* bytes) { inline void loadLE(T* n, const u8* bytes) {
static_assert(std::is_trivially_copyable<T>::value); static_assert(std::is_trivially_copyable<T>::value);
#if SPIDER_BIG_ENDIAN #if SPIDER_BIG_ENDIAN
T tmp; T tmp;
std::memcpy(&tmp, bytes, sizeof(T)); std::memcpy(&tmp, bytes, sizeof(T));
*n = byteswap(tmp); *n = byteswap(tmp);
#endif #endif
#if SPIDER_LITTLE_ENDIAN #if SPIDER_LITTLE_ENDIAN
std::memcpy(n, bytes, sizeof(T)); std::memcpy(n, bytes, sizeof(T));
#endif #endif
#if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN #if !SPIDER_BIG_ENDIAN && !SPIDER_LITTLE_ENDIAN
using U = std::make_unsigned_t<T>; using U = std::make_unsigned_t<T>;
U result = 0; U result = 0;
for (size_t i = 0; i < sizeof(T); ++i) { for (size_t i = 0; i < sizeof(T); ++i) {
result |= static_cast<U>(bytes[i]) << (i * 8); result |= static_cast<U>(bytes[i]) << (i * 8);
} }
*n = static_cast<T>(result); *n = static_cast<T>(result);
#endif #endif
} }
template<> template<>
inline void loadLE<f32>(f32* n, const u8* bytes) { inline void loadLE<f32>(f32* n, const u8* bytes) {
u32 tmp; u32 tmp;
loadLE(&tmp, bytes); loadLE(&tmp, bytes);
*n = bit_cast<f32>(tmp); *n = bit_cast<f32>(tmp);
} }
template<> template<>
inline void loadLE<f64>(f64* n, const u8* bytes) { inline void loadLE<f64>(f64* n, const u8* bytes) {
u64 tmp; u64 tmp;
loadLE(&tmp, bytes); loadLE(&tmp, bytes);
*n = bit_cast<f64>(tmp); *n = bit_cast<f64>(tmp);
} }
} }

View File

@@ -99,7 +99,7 @@
#define SPIDER_NO_ICU #define SPIDER_NO_ICU
#endif #endif
#if define(SPIDER_OS_NONE) #if defined(SPIDER_OS_NONE)
#define SPIDER_OS_NAME "None" #define SPIDER_OS_NAME "None"
#elif !defined(SPIDER_OS_MCU) #elif !defined(SPIDER_OS_MCU)
#error "[Spider Distro] Unsupported MCU OS" #error "[Spider Distro] Unsupported MCU OS"

View File

@@ -1,40 +1,40 @@
#pragma once #pragma once
// ========================================================== // // ========================================================== //
// SPIDER DEFAULT SETTINGS, PER DISTRO // // SPIDER DEFAULT SETTINGS, PER DISTRO //
// ========================================================== // // ========================================================== //
// ================== MEMORY FOOTPRINT ================== // // ================== MEMORY FOOTPRINT ================== //
/** /**
* Use a normal amount of memory, assuming there * Use a normal amount of memory, assuming there
* is like 100KB free of it. * is like 100KB free of it.
*/ */
#define SPIDER_MEMFOOTPRINT_NORMAL 0 #define SPIDER_MEMFOOTPRINT_NORMAL 0
/** /**
* Attempt to reduce the memory footprint * Attempt to reduce the memory footprint
* where possible, but without doing * where possible, but without doing
* extreme adaptations. * extreme adaptations.
*/ */
#define SPIDER_MEMFOOTPRINT_REDUCED 1 #define SPIDER_MEMFOOTPRINT_REDUCED 1
/** /**
* Will deliverately convert things from memory * Will deliverately convert things from memory
* to functions in order to free as much memory * to functions in order to free as much memory
* as possible, even if it slows things down. * as possible, even if it slows things down.
*/ */
#define SPIDER_MEMFOOTPRINT_MINIMAL 2 #define SPIDER_MEMFOOTPRINT_MINIMAL 2
#ifndef SPIDER_MEMFOOTPRINT #ifndef SPIDER_MEMFOOTPRINT
#if defined(SPIDER_DISTRO_MOBILE) || defined(SPIDER_DISTRO_BROWSER) #if defined(SPIDER_DISTRO_MOBILE) || defined(SPIDER_DISTRO_BROWSER)
#define SPIDER_MEMFOOTPRINT SPIDER_MEMFOOTPRINT_REDUCED #define SPIDER_MEMFOOTPRINT SPIDER_MEMFOOTPRINT_REDUCED
#elif defined(SPIDER_DISTRO_MICRO) #elif defined(SPIDER_DISTRO_MICRO)
#define SPIDER_MEMFOOTPRINT SPIDER_MEMFOOTPRINT_MINIMAL #define SPIDER_MEMFOOTPRINT SPIDER_MEMFOOTPRINT_MINIMAL
#else #else
#define SPIDER_MEMFOOTPRINT SPIDER_MEMFOOTPRINT_NORMAL #define SPIDER_MEMFOOTPRINT SPIDER_MEMFOOTPRINT_NORMAL
#endif #endif
#endif #endif
// ================== MISC ================== // // ================== MISC ================== //

View File

@@ -1,112 +1,112 @@
#pragma once #pragma once
// ========================================================== // // ========================================================== //
// SPIDER MICROCONTROLLER AUTODETECT // // SPIDER MICROCONTROLLER AUTODETECT //
// ========================================================== // // ========================================================== //
// Automatically enable configurations for the microcontroller // // Automatically enable configurations for the microcontroller //
// so long as we can detect it or the user has already configured // // so long as we can detect it or the user has already configured //
// the corresponding macros. // // the corresponding macros. //
#define SPIDER_MCU_FAM_GENERIC 0 #define SPIDER_MCU_FAM_GENERIC 0
#define SPIDER_MCU_FAM_AVR 1 #define SPIDER_MCU_FAM_AVR 1
#define SPIDER_MCU_FAM_ARM_CM 2 #define SPIDER_MCU_FAM_ARM_CM 2
#define SPIDER_MCU_FAM_ARM_MBED 3 #define SPIDER_MCU_FAM_ARM_MBED 3
#define SPIDER_MCU_FAM_PIC 4 #define SPIDER_MCU_FAM_PIC 4
#define SPIDER_MCU_FAM_PIC32 5 #define SPIDER_MCU_FAM_PIC32 5
#define SPIDER_MCU_FAM_ESP8266 6 #define SPIDER_MCU_FAM_ESP8266 6
#define SPIDER_MCU_FAM_ESP32 7 #define SPIDER_MCU_FAM_ESP32 7
#define SPIDER_MCU_FAM_RISCV 8 #define SPIDER_MCU_FAM_RISCV 8
#define SPIDER_MCU_FAM_RP2040 9 #define SPIDER_MCU_FAM_RP2040 9
#if defined(SPIDER_MCU_AVR) || defined(__avr__) || defined(__AVR__) #if defined(SPIDER_MCU_AVR) || defined(__avr__) || defined(__AVR__)
// ========================================================== // // ========================================================== //
// AVR (Atmel / Microchip) // // AVR (Atmel / Microchip) //
// ========================================================== // // ========================================================== //
#define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_AVR #define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_AVR
#define SPIDER_MCU_NAME "AVR" #define SPIDER_MCU_NAME "AVR"
#elif defined(SPIDER_MCU_ARM) || defined(__arm__) || defined(__ARM_ARCH) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_6M__) #elif defined(SPIDER_MCU_ARM) || defined(__arm__) || defined(__ARM_ARCH) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_6M__)
// ========================================================== // // ========================================================== //
// ARM Cortex-M (most STM32, nRF, etc.) // // ARM Cortex-M (most STM32, nRF, etc.) //
// ========================================================== // // ========================================================== //
#define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_ARM_CM #define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_ARM_CM
#define SPIDER_MCU_NAME "ARM Cortex-M" #define SPIDER_MCU_NAME "ARM Cortex-M"
#elif defined(SPIDER_MCU_MBED) || defined(TARGET_LIKE_MBED) #elif defined(SPIDER_MCU_MBED) || defined(TARGET_LIKE_MBED)
// ========================================================== // // ========================================================== //
// Mbed OS special detection // // Mbed OS special detection //
// ========================================================== // // ========================================================== //
#define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_ARM_MBED #define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_ARM_MBED
#define SPIDER_MCU_NAME "ARM MBED" #define SPIDER_MCU_NAME "ARM MBED"
#define SPIDER_OS_NAME "MBED OS" #define SPIDER_OS_NAME "MBED OS"
#elif defined(SPIDER_MCU_PIC8) || defined(__XC) && (defined(_PIC14) || defined(_PIC16)) #elif defined(SPIDER_MCU_PIC8) || defined(__XC) && (defined(_PIC14) || defined(_PIC16))
// ========================================================== // // ========================================================== //
// PIC (8-bit) // // PIC (8-bit) //
// ========================================================== // // ========================================================== //
#define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_PIC #define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_PIC
#define SPIDER_MCU_NAME "PIC (8-bit)" #define SPIDER_MCU_NAME "PIC (8-bit)"
#elif defined(SPIDER_MCU_PIC32) || defined(__XC32__) #elif defined(SPIDER_MCU_PIC32) || defined(__XC32__)
// ========================================================== // // ========================================================== //
// PIC32 (MIPS) // // PIC32 (MIPS) //
// ========================================================== // // ========================================================== //
#define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_PIC32 #define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_PIC32
#define SPIDER_MCU_NAME "PIC (32-bit)" #define SPIDER_MCU_NAME "PIC (32-bit)"
#elif defined(SPIDER_MCU_ESP8266) || defined(ESP8266) #elif defined(SPIDER_MCU_ESP8266) || defined(ESP8266)
// ========================================================== // // ========================================================== //
// ESP8266 // // ESP8266 //
// ========================================================== // // ========================================================== //
#define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_ESP8266 #define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_ESP8266
#define SPIDER_MCU_NAME "ESP-8266" #define SPIDER_MCU_NAME "ESP-8266"
#elif defined(SPIDER_MCU_ESP32) || defined(ESP32) #elif defined(SPIDER_MCU_ESP32) || defined(ESP32)
// ========================================================== // // ========================================================== //
// ESP32 // // ESP32 //
// ========================================================== // // ========================================================== //
#define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_ESP32 #define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_ESP32
#define SPIDER_MCU_NAME "ESP-32" #define SPIDER_MCU_NAME "ESP-32"
#elif defined(SPIDER_MCU_PICO) || defined(PICO_PLATFORM) || defined(PICO_BOARD) || defined(RP2040) || defined(__RP2040__) #elif defined(SPIDER_MCU_PICO) || defined(PICO_PLATFORM) || defined(PICO_BOARD) || defined(RP2040) || defined(__RP2040__)
// ========================================================== // // ========================================================== //
// Raspberry Pi Pico (RP2040) // // Raspberry Pi Pico (RP2040) //
// ========================================================== // // ========================================================== //
#define SPIDER_MCU_FAM_FAMILY SPIDER_MCU_FAM_RP2040 #define SPIDER_MCU_FAM_FAMILY SPIDER_MCU_FAM_RP2040
#define SPIDER_MCU_NAME "ESP-32" #define SPIDER_MCU_NAME "ESP-32"
#elif defined(SPIDER_MCU_RISCV) || defined(__riscv) || defined(__riscv__) #elif defined(SPIDER_MCU_RISCV) || defined(__riscv) || defined(__riscv__)
// ========================================================== // // ========================================================== //
// RISC-V MCUs // // RISC-V MCUs //
// ========================================================== // // ========================================================== //
#define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_RISCV #define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_RISCV
#define SPIDER_MCU_NAME "RISC-V MCU" #define SPIDER_MCU_NAME "RISC-V MCU"
#elif defined(SPIDER_MCU_GENERIC) #elif defined(SPIDER_MCU_GENERIC)
// ========================================================== // // ========================================================== //
// GENERIC MICRO // // GENERIC MICRO //
// ========================================================== // // ========================================================== //
#define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_GENERIC #define SPIDER_MCU_FAMILY SPIDER_MCU_FAM_GENERIC
#define SPIDER_MCU_NAME "Generic MCU" #define SPIDER_MCU_NAME "Generic MCU"
#endif #endif
#ifdef SPIDER_MCU_FAMILY #ifdef SPIDER_MCU_FAMILY
#ifndef SPIDER_DISTRO_MICRO #ifndef SPIDER_DISTRO_MICRO
#define SPIDER_DISTRO_MICRO #define SPIDER_DISTRO_MICRO
#endif #endif
// If SPIDER_OS_NAME was already defined, implies the SPIDER_MCU_FAM has an OS // If SPIDER_OS_NAME was already defined, implies the SPIDER_MCU_FAM has an OS
#if defined(SPIDER_OS_NAME) && !defined(SPIDER_OS_MCU) #if defined(SPIDER_OS_NAME) && !defined(SPIDER_OS_MCU)
#define SPIDER_OS_MCU #define SPIDER_OS_MCU
#endif #endif
// If no SPIDER_MCU_FAM OS defined, then it is NONE // If no SPIDER_MCU_FAM OS defined, then it is NONE
#ifndef SPIDER_OS_MCU #ifndef SPIDER_OS_MCU
#define SPIDER_OS_NONE #define SPIDER_OS_NONE
#endif #endif
#elif defined(SPIDER_DISTRO_MICRO) #elif defined(SPIDER_DISTRO_MICRO)
#error "[Spider Distro] Unsupported SPIDER_MCU_FAM. Define SPIDER_MCU_GENERIC and see if works." #error "[Spider Distro] Unsupported SPIDER_MCU_FAM. Define SPIDER_MCU_GENERIC and see if works."
#endif #endif

View File

@@ -1,93 +1,93 @@
#pragma once #pragma once
/* /*
* This file contains macros related to machine dependent * This file contains macros related to machine dependent
* things like alignment and type juggling * things like alignment and type juggling
*/ */
// ========================================================== // // ========================================================== //
// ENDIANNESS // // ENDIANNESS //
// ========================================================== // // ========================================================== //
// Used by GCC/Clang/WASM/ESP32/RP2040 and most compilers // Used by GCC/Clang/WASM/ESP32/RP2040 and most compilers
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)
#define SPIDER_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) #define SPIDER_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#define SPIDER_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #define SPIDER_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
// Fallbacks (For older/constrained compilers) // Fallbacks (For older/constrained compilers)
#else #else
#if defined(__AVR_ATmega328P__) || defined(__AVR__) // Arduino Uno/ATmega #if defined(__AVR_ATmega328P__) || defined(__AVR__) // Arduino Uno/ATmega
#define SPIDER_LITTLE_ENDIAN 1 #define SPIDER_LITTLE_ENDIAN 1
#define SPIDER_BIG_ENDIAN 0 #define SPIDER_BIG_ENDIAN 0
#elif defined(__wasm__) || defined(__wasm32__) // WebAssembly #elif defined(__wasm__) || defined(__wasm32__) // WebAssembly
#define SPIDER_LITTLE_ENDIAN 1 #define SPIDER_LITTLE_ENDIAN 1
#define SPIDER_BIG_ENDIAN 0 #define SPIDER_BIG_ENDIAN 0
#elif defined(__arm__) || defined(__thumb__) // RP2040, STM32, etc. #elif defined(__arm__) || defined(__thumb__) // RP2040, STM32, etc.
#if defined(__ARMEB__) #if defined(__ARMEB__)
#define SPIDER_LITTLE_ENDIAN 0 #define SPIDER_LITTLE_ENDIAN 0
#define SPIDER_BIG_ENDIAN 1 #define SPIDER_BIG_ENDIAN 1
#else #else
#define SPIDER_LITTLE_ENDIAN 1 #define SPIDER_LITTLE_ENDIAN 1
#define SPIDER_BIG_ENDIAN 0 #define SPIDER_BIG_ENDIAN 0
#endif #endif
#elif defined(__xtensa__) || defined(__ESP32__) // ESP32 #elif defined(__xtensa__) || defined(__ESP32__) // ESP32
#define SPIDER_LITTLE_ENDIAN 1 #define SPIDER_LITTLE_ENDIAN 1
#define SPIDER_BIG_ENDIAN 0 #define SPIDER_BIG_ENDIAN 0
// Likely will never use this clause // Likely will never use this clause
// ...but keeps compatibility with the one (1) // ...but keeps compatibility with the one (1)
// guy who uses MSVC. // guy who uses MSVC.
#elif defined(_WIN32) || defined(_M_IX86) || defined(_M_X64) #elif defined(_WIN32) || defined(_M_IX86) || defined(_M_X64)
#define SPIDER_LITTLE_ENDIAN 1 #define SPIDER_LITTLE_ENDIAN 1
#define SPIDER_BIG_ENDIAN 0 #define SPIDER_BIG_ENDIAN 0
#else #else
#error "[Spider Machine] Unsupported or unknown architecture endianness!" #error "[Spider Machine] Unsupported or unknown architecture endianness!"
#endif #endif
#endif #endif
// Safety checks // Safety checks
#if !defined(SPIDER_LITTLE_ENDIAN) || !defined(SPIDER_BIG_ENDIAN) #if !defined(SPIDER_LITTLE_ENDIAN) || !defined(SPIDER_BIG_ENDIAN)
#error "[Spider Machine] Missed at least one little/big endian macros" #error "[Spider Machine] Missed at least one little/big endian macros"
#endif #endif
#if SPIDER_LITTLE_ENDIAN == 1 && SPIDER_BIG_ENDIAN == 1 #if SPIDER_LITTLE_ENDIAN == 1 && SPIDER_BIG_ENDIAN == 1
#warning "[Spider Machine] Mixed endian machine detected, unsupported! Be cautious adventurer!" #warning "[Spider Machine] Mixed endian machine detected, unsupported! Be cautious adventurer!"
#endif #endif
// ========================================================== // // ========================================================== //
// PACKING // // PACKING //
// (not used now) // // (not used now) //
// ========================================================== // // ========================================================== //
// Find out what compiler the user is using // Find out what compiler the user is using
#if defined(__clang__) #if defined(__clang__)
#define SPIDER_COMPILER_CLANG #define SPIDER_COMPILER_CLANG
#define SPIDER_COMPILER_GCC_LIKE #define SPIDER_COMPILER_GCC_LIKE
#elif defined(__GNUC__) #elif defined(__GNUC__)
#define SPIDER_COMPILER_GCC #define SPIDER_COMPILER_GCC
#define SPIDER_COMPILER_GCC_LIKE #define SPIDER_COMPILER_GCC_LIKE
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define SPIDER_COMPILER_MSVC #define SPIDER_COMPILER_MSVC
#else #else
#define SPIDER_COMPILER_UNKNOWN #define SPIDER_COMPILER_UNKNOWN
#endif #endif
// Macros... // Macros...
#if defined(SPIDER_COMPILER_GCC_LIKE) #if defined(SPIDER_COMPILER_GCC_LIKE)
#define SPIDER_PACK_BEGIN #define SPIDER_PACK_BEGIN
#define SPIDER_PACK_END #define SPIDER_PACK_END
#define SPIDER_PACK_STRUCT __attribute__((packed)) #define SPIDER_PACK_STRUCT __attribute__((packed))
#elif defined(SPIDER_COMPILER_MSVC) #elif defined(SPIDER_COMPILER_MSVC)
#define SPIDER_BEGIN_PACKED __pragma(pack(push, 1)) #define SPIDER_BEGIN_PACKED __pragma(pack(push, 1))
#define SPIDER_END_PACKED __pragma(pack(pop)) #define SPIDER_END_PACKED __pragma(pack(pop))
#define SPIDER_PACKED_STRUCT(decl) SPIDER_BEGIN_PACKED decl SPIDER_END_PACKED #define SPIDER_PACKED_STRUCT(decl) SPIDER_BEGIN_PACKED decl SPIDER_END_PACKED
#else #else
#define SPIDER_ATTRIBUTE_PACKED #define SPIDER_ATTRIBUTE_PACKED
#define SPIDER_BEGIN_PACKED #define SPIDER_BEGIN_PACKED
#define SPIDER_END_PACKED #define SPIDER_END_PACKED
#define SPIDER_PACKED_STRUCT(decl) decl #define SPIDER_PACKED_STRUCT(decl) decl
#warning "[Spider Machine] Compiler packing not supported. Memory layout may be unstable!" #warning "[Spider Machine] Compiler packing not supported. Memory layout may be unstable!"
#endif #endif
namespace spider {} namespace spider {}

View File

@@ -1,77 +1,77 @@
#include "InstrReel.hpp" #include "InstrReel.hpp"
#include <spider/runtime/cpu/CPU.hpp> #include <spider/runtime/cpu/CPU.hpp>
#include <spider/runtime/memory/Types.hpp> #include <spider/runtime/memory/Types.hpp>
namespace spider { namespace spider {
// Public Interface // // Public Interface //
InstrReel::InstrReel() : _mem(nullptr), _size(0), _offset(0), _total_size(0) {} InstrReel::InstrReel() : _mem(nullptr), _size(0), _offset(0), _total_size(0) {}
InstrReel::~InstrReel() {} InstrReel::~InstrReel() {}
// Instruction abstraction // // Instruction abstraction //
u8 InstrReel::atU8(u64 ip) { u8 InstrReel::atU8(u64 ip) {
// guard against access // guard against access
u64 ip_p = ip - _offset; u64 ip_p = ip - _offset;
if(ip_p + 1 > _size) return 0; if(ip_p + 1 > _size) return 0;
// send byte // send byte
return _mem[ip]; return _mem[ip];
} }
u16 InstrReel::atU16(u64 ip) { u16 InstrReel::atU16(u64 ip) {
// guard against access // guard against access
u64 ip_p = ip - _offset; u64 ip_p = ip - _offset;
if(ip_p + 2 > _size) return 0; if(ip_p + 2 > _size) return 0;
// build a 16-bit big endian number // build a 16-bit big endian number
u16 dat; u16 dat;
spider::loadLE(&dat, _mem + ip_p); spider::loadLE(&dat, _mem + ip_p);
return dat; return dat;
} }
u32 InstrReel::atU32(u64 ip) { u32 InstrReel::atU32(u64 ip) {
// guard against access // guard against access
u64 ip_p = ip - _offset; u64 ip_p = ip - _offset;
if(ip_p + 4 > _size) return 0; if(ip_p + 4 > _size) return 0;
// build a 32-bit big endian number // build a 32-bit big endian number
u32 dat; u32 dat;
spider::loadLE(&dat, _mem + ip_p); spider::loadLE(&dat, _mem + ip_p);
return dat; return dat;
} }
u64 InstrReel::atU64(u64 ip) { u64 InstrReel::atU64(u64 ip) {
// guard against access // guard against access
u64 ip_p = ip - _offset; u64 ip_p = ip - _offset;
if(ip_p + 8 > _size) return 0; if(ip_p + 8 > _size) return 0;
// build a 64-bit big endian number // build a 64-bit big endian number
u64 dat; u64 dat;
spider::loadLE(&dat, _mem + ip_p); spider::loadLE(&dat, _mem + ip_p);
return dat; return dat;
} }
u64 InstrReel::size() { u64 InstrReel::size() {
return _total_size; return _total_size;
} }
// Static Utils // // Static Utils //
u16 InstrReel::unpackInstr(u16 bcode) { u16 InstrReel::unpackInstr(u16 bcode) {
return (bcode >> 5) & 0x1FF; return (bcode >> 5) & 0x1FF;
} }
u8 InstrReel::unpackAddrMode(u16 bcode) { u8 InstrReel::unpackAddrMode(u16 bcode) {
return (bcode >> 2) & 0x1F; return (bcode >> 2) & 0x1F;
} }
u8 InstrReel::unpackTypeSize(u16 bcode) { u8 InstrReel::unpackTypeSize(u16 bcode) {
return bcode & 0x3; return bcode & 0x3;
} }
} }

View File

@@ -1,74 +1,74 @@
#pragma once #pragma once
#include <spider/SpiderRuntime.hpp> #include <spider/SpiderRuntime.hpp>
#include <spider/runtime/memory/ByteArray.hpp> #include <spider/runtime/memory/ByteArray.hpp>
namespace spider { namespace spider {
/** /**
* Implements an instruction reel. * Implements an instruction reel.
*/ */
class InstrReel { class InstrReel {
protected: // Current accessing range // protected: // Current accessing range //
u8* _mem; u8* _mem;
isize _size; isize _size;
isize _offset; isize _offset;
isize _total_size; isize _total_size;
public: public:
InstrReel(); InstrReel();
virtual ~InstrReel(); virtual ~InstrReel();
public: public:
/** /**
* Obtains a byte of data at * Obtains a byte of data at
* the specific location. * the specific location.
* Reindexing may occur, continous access * Reindexing may occur, continous access
* may incurr in less penalties. * may incurr in less penalties.
*/ */
virtual u8 atU8(u64 ip); virtual u8 atU8(u64 ip);
/** /**
* Obtains a byte of data at * Obtains a byte of data at
* the specific location. * the specific location.
* Reindexing may occur, continous access * Reindexing may occur, continous access
* may incurr in less penalties. * may incurr in less penalties.
*/ */
virtual u16 atU16(u64 ip); virtual u16 atU16(u64 ip);
/** /**
* Obtains a byte of data at * Obtains a byte of data at
* the specific location. * the specific location.
* Reindexing may occur, continous access * Reindexing may occur, continous access
* may incurr in less penalties. * may incurr in less penalties.
*/ */
virtual u32 atU32(u64 ip); virtual u32 atU32(u64 ip);
/** /**
* Obtains a byte of data at * Obtains a byte of data at
* the specific location. * the specific location.
* Reindexing may occur, continous access * Reindexing may occur, continous access
* may incurr in less penalties. * may incurr in less penalties.
*/ */
virtual u64 atU64(u64 ip); virtual u64 atU64(u64 ip);
/** /**
* Current size of the instructions. * Current size of the instructions.
*/ */
virtual u64 size(); virtual u64 size();
public: // Static Utils // public: // Static Utils //
static u16 unpackInstr(u16 bcode); static u16 unpackInstr(u16 bcode);
static u8 unpackAddrMode(u16 bcode); static u8 unpackAddrMode(u16 bcode);
static u8 unpackTypeSize(u16 bcode); static u8 unpackTypeSize(u16 bcode);
}; };
} }

View File

@@ -1,194 +1,194 @@
#include "InstrReelDyn.hpp" #include "InstrReelDyn.hpp"
#include <spider/runtime/memory/Types.hpp> #include <spider/runtime/memory/Types.hpp>
namespace spider { namespace spider {
InstrReelDyn::InstrReelDyn(u64 length) : _use_count(0), _block_index(0) { InstrReelDyn::InstrReelDyn(u64 length) : _use_count(0), _block_index(0) {
_total_size = length; _total_size = length;
growToFit(length > 0 ? length - 1 : 0); growToFit(length > 0 ? length - 1 : 0);
selectBlock(0); selectBlock(0);
} }
InstrReelDyn::InstrReelDyn(const u8* data, u64 length) {} 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) { 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); 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)) { 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); if (_block_index < _blocks.size()) selectBlock(_block_index);
} }
InstrReelDyn::~InstrReelDyn() { InstrReelDyn::~InstrReelDyn() {
// .. // // .. //
} }
InstrReelDyn& InstrReelDyn::operator=(const InstrReelDyn& copy) { InstrReelDyn& InstrReelDyn::operator=(const InstrReelDyn& copy) {
_use_count = copy._use_count; _use_count = copy._use_count;
_block_index = copy._block_index; _block_index = copy._block_index;
_blocks = copy._blocks; _blocks = copy._blocks;
if (_block_index < _blocks.size()) selectBlock(_block_index); if (_block_index < _blocks.size()) selectBlock(_block_index);
return *this; return *this;
} }
InstrReelDyn& InstrReelDyn::operator=(InstrReelDyn&& move) noexcept { InstrReelDyn& InstrReelDyn::operator=(InstrReelDyn&& move) noexcept {
_use_count = move._use_count; _use_count = move._use_count;
_block_index = move._block_index; _block_index = move._block_index;
_blocks = std::move(move._blocks); _blocks = std::move(move._blocks);
if (_block_index < _blocks.size()) selectBlock(_block_index); if (_block_index < _blocks.size()) selectBlock(_block_index);
move._use_count = 0; move._use_count = 0;
move._block_index = 0; move._block_index = 0;
move._mem = nullptr; move._mem = nullptr;
move._offset = 0; move._offset = 0;
move._size = 0; move._size = 0;
move._total_size = 0; move._total_size = 0;
return *this; return *this;
} }
void InstrReelDyn::growToFit(isize index) { void InstrReelDyn::growToFit(isize index) {
while (_blocks.size() < (index + 1)) { while (_blocks.size() < (index + 1)) {
_blocks.emplace_back(); _blocks.emplace_back();
} }
} }
isize InstrReelDyn::selectIndex(u64 ip) { isize InstrReelDyn::selectIndex(u64 ip) {
return ip / 256; return ip / 256;
} }
InstrReelDyn::ReelBlock* InstrReelDyn::selectBlock(isize index) { InstrReelDyn::ReelBlock* InstrReelDyn::selectBlock(isize index) {
// Update base class cache // Update base class cache
auto ptr = &_blocks[index]; auto ptr = &_blocks[index];
_offset = index * 256; _offset = index * 256;
_mem = ptr->data; _mem = ptr->data;
_size = 256; _size = 256;
_block_index = index; _block_index = index;
//_blocks[block_idx].access_count++; //_blocks[block_idx].access_count++;
return ptr; return ptr;
} }
u8 InstrReelDyn::atU8(u64 ip) { u8 InstrReelDyn::atU8(u64 ip) {
isize j = selectIndex(ip); isize j = selectIndex(ip);
if (j >= _blocks.size()) return 0; if (j >= _blocks.size()) return 0;
if (j != _block_index) { if (j != _block_index) {
this->selectBlock(j); this->selectBlock(j);
} }
return _mem[ip - _offset]; return _mem[ip - _offset];
} }
u16 InstrReelDyn::atU16(u64 ip) { u16 InstrReelDyn::atU16(u64 ip) {
isize j0 = selectIndex(ip); isize j0 = selectIndex(ip);
isize j1 = selectIndex(ip + 1); isize j1 = selectIndex(ip + 1);
if (j1 >= _blocks.size()) return 0; if (j1 >= _blocks.size()) return 0;
if (j0 == j1 && j0 != _block_index) { if (j0 == j1 && j0 != _block_index) {
selectBlock(j0); selectBlock(j0);
} }
if (j0 == j1 && j0 == _block_index) { if (j0 == j1 && j0 == _block_index) {
u16 dat; u16 dat;
spider::loadLE(&dat, _mem); spider::loadLE(&dat, _mem);
return dat; return dat;
} }
// general case, first part // general case, first part
u16 dat = 0; u16 dat = 0;
const u8 size = sizeof(u16); const u8 size = sizeof(u16);
// select first block and offset // select first block and offset
selectBlock(j0); selectBlock(j0);
u8 rem = ip % 256; u8 rem = ip % 256;
for (u8 n = 0; n < size; n++) { for (u8 n = 0; n < size; n++) {
dat |= _mem[rem++] << (n * 8); dat |= _mem[rem++] << (n * 8);
ip++; ip++;
if (!rem) selectBlock(++j0); if (!rem) selectBlock(++j0);
} }
return dat; return dat;
} }
u32 InstrReelDyn::atU32(u64 ip) { u32 InstrReelDyn::atU32(u64 ip) {
isize j0 = selectIndex(ip); isize j0 = selectIndex(ip);
isize j1 = selectIndex(ip + 3); isize j1 = selectIndex(ip + 3);
if (j1 >= _blocks.size()) return 0; if (j1 >= _blocks.size()) return 0;
if (j0 == j1 && j0 != _block_index) { if (j0 == j1 && j0 != _block_index) {
selectBlock(j0); selectBlock(j0);
} }
if (j0 == j1 && j0 == _block_index) { if (j0 == j1 && j0 == _block_index) {
u32 dat; u32 dat;
spider::loadLE(&dat, _mem); spider::loadLE(&dat, _mem);
return dat; return dat;
} }
// general case, first part // general case, first part
u32 dat = 0; u32 dat = 0;
const u8 size = sizeof(u32); const u8 size = sizeof(u32);
// select first block and offset // select first block and offset
selectBlock(j0); selectBlock(j0);
u8 rem = ip % 256; u8 rem = ip % 256;
for (u8 n = 0; n < size; n++) { for (u8 n = 0; n < size; n++) {
dat |= _mem[rem++] << (n * 8); dat |= _mem[rem++] << (n * 8);
ip++; ip++;
if (!rem) selectBlock(++j0); if (!rem) selectBlock(++j0);
} }
return dat; return dat;
} }
u64 InstrReelDyn::atU64(u64 ip) { u64 InstrReelDyn::atU64(u64 ip) {
isize j0 = selectIndex(ip); isize j0 = selectIndex(ip);
isize j1 = selectIndex(ip + 3); isize j1 = selectIndex(ip + 3);
if (j1 >= _blocks.size()) return 0; if (j1 >= _blocks.size()) return 0;
if (j0 == j1 && j0 != _block_index) { if (j0 == j1 && j0 != _block_index) {
selectBlock(j0); selectBlock(j0);
} }
if (j0 == j1 && j0 == _block_index) { if (j0 == j1 && j0 == _block_index) {
u64 dat; u64 dat;
spider::loadLE(&dat, _mem); spider::loadLE(&dat, _mem);
return dat; return dat;
} }
// general case, first part // general case, first part
u64 dat = 0; u64 dat = 0;
const u8 size = sizeof(u64); const u8 size = sizeof(u64);
// select first block and offset // select first block and offset
selectBlock(j0); selectBlock(j0);
u8 rem = ip % 256; u8 rem = ip % 256;
for (u8 n = 0; n < size; n++) { for (u8 n = 0; n < size; n++) {
dat |= _mem[rem++] << (n * 8); dat |= _mem[rem++] << (n * 8);
ip++; ip++;
if (!rem) selectBlock(++j0); if (!rem) selectBlock(++j0);
} }
return dat; return dat;
} }
void InstrReelDyn::at(u64 ip, u8 dat) {} void InstrReelDyn::at(u64 ip, u8 dat) {}
void InstrReelDyn::at(u64 ip, u16 dat) {} void InstrReelDyn::at(u64 ip, u16 dat) {}
void InstrReelDyn::at(u64 ip, u32 dat) {} void InstrReelDyn::at(u64 ip, u32 dat) {}
void InstrReelDyn::at(u64 ip, u64 dat) {} void InstrReelDyn::at(u64 ip, u64 dat) {}
/** /**
* Appends instruction at location. * Appends instruction at location.
*/ */
void InstrReelDyn::append(u64 ip, u16 bc) {} void InstrReelDyn::append(u64 ip, u16 bc) {}
/** /**
* Appends instruction at the end. * Appends instruction at the end.
*/ */
void InstrReelDyn::append(u16 bc) {} void InstrReelDyn::append(u16 bc) {}
/** /**
* Removes instruction at location. * Removes instruction at location.
*/ */
void InstrReelDyn::remove(u64 ip) {} void InstrReelDyn::remove(u64 ip) {}
} }

View File

@@ -1,110 +1,110 @@
#pragma once #pragma once
#include <spider/runtime/reel/InstrReel.hpp> #include <spider/runtime/reel/InstrReel.hpp>
namespace spider { namespace spider {
/** /**
* Implements an instruction reel. * Implements an instruction reel.
*/ */
class InstrReelDyn : public InstrReel { class InstrReelDyn : public InstrReel {
private: private:
struct ReelBlock { struct ReelBlock {
u8 data[256] = {}; u8 data[256] = {};
}; };
private: private:
u64 _use_count; u64 _use_count;
isize _block_index; isize _block_index;
std::deque<ReelBlock> _blocks; std::deque<ReelBlock> _blocks;
public: public:
InstrReelDyn(u64 length); InstrReelDyn(u64 length);
InstrReelDyn(const u8* data, u64 length); InstrReelDyn(const u8* data, u64 length);
InstrReelDyn(const InstrReelDyn& copy); InstrReelDyn(const InstrReelDyn& copy);
InstrReelDyn(InstrReelDyn&& move) noexcept; InstrReelDyn(InstrReelDyn&& move) noexcept;
virtual ~InstrReelDyn(); virtual ~InstrReelDyn();
public: public:
InstrReelDyn& operator=(const InstrReelDyn& copy); InstrReelDyn& operator=(const InstrReelDyn& copy);
InstrReelDyn& operator=(InstrReelDyn&& move) noexcept; InstrReelDyn& operator=(InstrReelDyn&& move) noexcept;
private: private:
isize selectIndex(u64 ip); isize selectIndex(u64 ip);
void growToFit(isize index); void growToFit(isize index);
ReelBlock* selectBlock(isize index); ReelBlock* selectBlock(isize index);
public: public:
/** /**
* Obtains a byte of data at * Obtains a byte of data at
* the specific location. * the specific location.
* Reindexing may occur, continous access * Reindexing may occur, continous access
* may incurr in less penalties. * may incurr in less penalties.
*/ */
virtual u8 atU8(u64 ip) override; virtual u8 atU8(u64 ip) override;
/** /**
* Obtains a byte of data at * Obtains a byte of data at
* the specific location. * the specific location.
* Reindexing may occur, continous access * Reindexing may occur, continous access
* may incurr in less penalties. * may incurr in less penalties.
*/ */
virtual u16 atU16(u64 ip) override; virtual u16 atU16(u64 ip) override;
/** /**
* Obtains a byte of data at * Obtains a byte of data at
* the specific location. * the specific location.
* Reindexing may occur, continous access * Reindexing may occur, continous access
* may incurr in less penalties. * may incurr in less penalties.
*/ */
virtual u32 atU32(u64 ip) override; virtual u32 atU32(u64 ip) override;
/** /**
* Obtains a byte of data at * Obtains a byte of data at
* the specific location. * the specific location.
* Reindexing may occur, continous access * Reindexing may occur, continous access
* may incurr in less penalties. * may incurr in less penalties.
*/ */
virtual u64 atU64(u64 ip) override; virtual u64 atU64(u64 ip) override;
public: public:
void at(u64 ip, u8 dat); void at(u64 ip, u8 dat);
void at(u64 ip, u16 dat); void at(u64 ip, u16 dat);
void at(u64 ip, u32 dat); void at(u64 ip, u32 dat);
void at(u64 ip, u64 dat); void at(u64 ip, u64 dat);
/** /**
* Appends instruction at location. * Appends instruction at location.
*/ */
void append(u64 ip, u16 bc); void append(u64 ip, u16 bc);
/** /**
* Appends instruction at the end. * Appends instruction at the end.
*/ */
void append(u16 bc); void append(u16 bc);
/** /**
* Removes instruction at location. * Removes instruction at location.
*/ */
void remove(u64 ip); void remove(u64 ip);
}; };
} }

View File

@@ -1,149 +1,149 @@
#include "InstrReelFixed.hpp" #include "InstrReelFixed.hpp"
#include <spider/runtime/memory/Types.hpp> #include <spider/runtime/memory/Types.hpp>
#include <cstring> #include <cstring>
namespace spider { namespace spider {
// Constructors & Destructors // // Constructors & Destructors //
InstrReelFixed::InstrReelFixed(u64 length) { InstrReelFixed::InstrReelFixed(u64 length) {
this->_offset = 0; this->_offset = 0;
this->_size = length; this->_size = length;
this->_total_size = length; this->_total_size = length;
if (_size > 0) { if (_size > 0) {
_mem = new u8[_size]; _mem = new u8[_size];
std::memset(_mem, 0, _size); std::memset(_mem, 0, _size);
} }
} }
InstrReelFixed::InstrReelFixed(const u8* data, u64 length) { InstrReelFixed::InstrReelFixed(const u8* data, u64 length) {
this->_offset = 0; this->_offset = 0;
this->_size = length; this->_size = length;
this->_total_size = length; this->_total_size = length;
if (_size > 0) { if (_size > 0) {
_mem = new u8[_size]; _mem = new u8[_size];
std::copy(data, data + _size, _mem); std::copy(data, data + _size, _mem);
} }
} }
InstrReelFixed::InstrReelFixed(const InstrReelFixed& other) { InstrReelFixed::InstrReelFixed(const InstrReelFixed& other) {
_offset = other._offset; _offset = other._offset;
_size = other._size; _size = other._size;
_total_size = other._total_size; _total_size = other._total_size;
_mem = new u8[_size]; _mem = new u8[_size];
std::copy(other._mem, other._mem + _size, _mem); std::copy(other._mem, other._mem + _size, _mem);
} }
InstrReelFixed::InstrReelFixed(InstrReelFixed&& other) noexcept { InstrReelFixed::InstrReelFixed(InstrReelFixed&& other) noexcept {
_mem = other._mem; _mem = other._mem;
_offset = other._offset; _offset = other._offset;
_size = other._size; _size = other._size;
_total_size = other._total_size; _total_size = other._total_size;
other._mem = nullptr; other._mem = nullptr;
other._offset = 0; other._offset = 0;
other._size = 0; other._size = 0;
other._total_size = 0; other._total_size = 0;
} }
InstrReelFixed::~InstrReelFixed() { InstrReelFixed::~InstrReelFixed() {
delete[] _mem; delete[] _mem;
} }
// Assign Operators // // Assign Operators //
InstrReelFixed& InstrReelFixed::operator=(const InstrReelFixed& other) { InstrReelFixed& InstrReelFixed::operator=(const InstrReelFixed& other) {
if (this == &other) return *this; // lock self if (this == &other) return *this; // lock self
u8* new_mem = new u8[other._size]; u8* new_mem = new u8[other._size];
std::copy(other._mem, other._mem + other._size, new_mem); std::copy(other._mem, other._mem + other._size, new_mem);
delete[] _mem; delete[] _mem;
_mem = new_mem; _mem = new_mem;
_offset = other._offset; _offset = other._offset;
_size = other._size; _size = other._size;
_total_size = other._total_size; _total_size = other._total_size;
return *this; return *this;
} }
InstrReelFixed& InstrReelFixed::operator=(InstrReelFixed&& other) noexcept { InstrReelFixed& InstrReelFixed::operator=(InstrReelFixed&& other) noexcept {
if (this == &other) return *this; // lock self if (this == &other) return *this; // lock self
delete[] _mem; delete[] _mem;
_mem = other._mem; // steal _mem = other._mem; // steal
_offset = other._offset; _offset = other._offset;
_size = other._size; _size = other._size;
_total_size = other._total_size; _total_size = other._total_size;
other._mem = nullptr; // leave as husk other._mem = nullptr; // leave as husk
other._offset = 0; other._offset = 0;
other._size = 0; other._size = 0;
other._total_size = 0; other._total_size = 0;
return *this; return *this;
} }
// Misc // // Misc //
void InstrReelFixed::at(u64 ip, u8 dat) { void InstrReelFixed::at(u64 ip, u8 dat) {
if(ip + 1 > _size) return; if(ip + 1 > _size) return;
_mem[ip] = dat; _mem[ip] = dat;
} }
void InstrReelFixed::at(u64 ip, u16 dat) { void InstrReelFixed::at(u64 ip, u16 dat) {
if(ip + 2 > _size) return; if(ip + 2 > _size) return;
spider::storeLE(dat, _mem + ip); spider::storeLE(dat, _mem + ip);
} }
void InstrReelFixed::at(u64 ip, u32 dat) { void InstrReelFixed::at(u64 ip, u32 dat) {
if(ip + 4 > _size) return; if(ip + 4 > _size) return;
spider::storeLE(dat, _mem + ip); spider::storeLE(dat, _mem + ip);
} }
void InstrReelFixed::at(u64 ip, u64 dat) { void InstrReelFixed::at(u64 ip, u64 dat) {
if(ip + 8 > _size) return; if(ip + 8 > _size) return;
spider::storeLE(dat, _mem + ip); spider::storeLE(dat, _mem + ip);
} }
void InstrReelFixed::resize(u64 new_size) { void InstrReelFixed::resize(u64 new_size) {
// Special case 1 // Special case 1
if (new_size == _size) return; if (new_size == _size) return;
// Special case 2 // Special case 2
if (new_size == 0) { if (new_size == 0) {
delete[] _mem; delete[] _mem;
_mem = nullptr; _mem = nullptr;
_size = 0; _size = 0;
_total_size = 0; _total_size = 0;
return; return;
} }
// 1. Allocate the new block // 1. Allocate the new block
u8* new_mem = new u8[new_size]; u8* new_mem = new u8[new_size];
// 2. Zero-initialize // 2. Zero-initialize
std::memset(new_mem, 0, new_size); std::memset(new_mem, 0, new_size);
// 3. Preserve data // 3. Preserve data
// If shrinking, copy 'new_size' bytes. If growing, copy 'old_size' bytes. // If shrinking, copy 'new_size' bytes. If growing, copy 'old_size' bytes.
u64 bytes_to_copy = (new_size < _size) ? new_size : _size; u64 bytes_to_copy = (new_size < _size) ? new_size : _size;
// 3.1 Previous size could be zero, where _mem would be null // 3.1 Previous size could be zero, where _mem would be null
if (_mem != nullptr) { if (_mem != nullptr) {
std::copy(_mem, _mem + bytes_to_copy, new_mem); std::copy(_mem, _mem + bytes_to_copy, new_mem);
} }
// 4. Swap and Clean up // 4. Swap and Clean up
delete[] _mem; delete[] _mem;
_mem = new_mem; _mem = new_mem;
_size = new_size; _size = new_size;
_total_size = new_size; _total_size = new_size;
} }
} }

View File

@@ -1,43 +1,43 @@
#pragma once #pragma once
#include <spider/runtime/reel/InstrReel.hpp> #include <spider/runtime/reel/InstrReel.hpp>
namespace spider { namespace spider {
/** /**
* Implements an instruction reel. * Implements an instruction reel.
*/ */
class InstrReelFixed : public InstrReel { class InstrReelFixed : public InstrReel {
public: public:
InstrReelFixed(u64 length); InstrReelFixed(u64 length);
InstrReelFixed(const u8* data, u64 length); InstrReelFixed(const u8* data, u64 length);
InstrReelFixed(const InstrReelFixed& copy); InstrReelFixed(const InstrReelFixed& copy);
InstrReelFixed(InstrReelFixed&& move) noexcept; InstrReelFixed(InstrReelFixed&& move) noexcept;
virtual ~InstrReelFixed(); virtual ~InstrReelFixed();
public: public:
InstrReelFixed& operator=(const InstrReelFixed& copy); InstrReelFixed& operator=(const InstrReelFixed& copy);
InstrReelFixed& operator=(InstrReelFixed&& move) noexcept; InstrReelFixed& operator=(InstrReelFixed&& move) noexcept;
public: public:
void at(u64 ip, u8 dat); void at(u64 ip, u8 dat);
void at(u64 ip, u16 dat); void at(u64 ip, u16 dat);
void at(u64 ip, u32 dat); void at(u64 ip, u32 dat);
void at(u64 ip, u64 dat); void at(u64 ip, u64 dat);
void resize(u64 new_size); void resize(u64 new_size);
}; };
} }

View File

@@ -1,277 +1,277 @@
#include "Terminal.hpp" #include "Terminal.hpp"
#include <spider/runtime/native/distro.hpp> #include <spider/runtime/native/distro.hpp>
#if defined(SPIDER_OS_WINDOWS) #if defined(SPIDER_OS_WINDOWS)
#include <conio.h> #include <conio.h>
#include <windows.h> #include <windows.h>
#endif #endif
#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS) #if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS)
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#endif #endif
#if defined(SPIDER_DISTRO_DESKTOP) #if defined(SPIDER_DISTRO_DESKTOP)
namespace spider { namespace spider {
// Style // // Style //
const char* Terminal::RESET = "\033[0m"; const char* Terminal::RESET = "\033[0m";
const char* Terminal::BOLD = "\033[1m"; const char* Terminal::BOLD = "\033[1m";
const char* Terminal::ITALIC = "\033[3m"; const char* Terminal::ITALIC = "\033[3m";
const char* Terminal::FAINT = "\033[2m"; const char* Terminal::FAINT = "\033[2m";
const char* Terminal::STRIKE = "\033[9m"; const char* Terminal::STRIKE = "\033[9m";
// Foreground // // Foreground //
const char* Terminal::FG_BLACK = "\033[30m"; const char* Terminal::FG_B_BLACK = "\033[90m"; 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_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_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_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_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_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_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"; const char* Terminal::FG_WHITE = "\033[37m"; const char* Terminal::FG_B_WHITE = "\033[97m";
// Background // // Background //
const char* Terminal::BG_BLACK = "\033[40m"; const char* Terminal::BG_B_BLACK = "\033[100m"; 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_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_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_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_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_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_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"; const char* Terminal::BG_WHITE = "\033[47m"; const char* Terminal::BG_B_WHITE = "\033[107m";
Terminal::Terminal() { Terminal::Terminal() {
#if defined(SPIDER_OS_WINDOWS) #if defined(SPIDER_OS_WINDOWS)
// Enable UTF-8 // Enable UTF-8
SetConsoleOutputCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8); SetConsoleCP(CP_UTF8);
// enable vtp // enable vtp
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0; DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode); GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode); SetConsoleMode(hOut, dwMode);
#endif #endif
} }
Terminal::~Terminal() { Terminal::~Terminal() {
altbuff(false).style(RESET).cursor(true).flush(); altbuff(false).style(RESET).cursor(true).flush();
} }
Terminal& Terminal::style(const std::string_view code) { Terminal& Terminal::style(const std::string_view code) {
std::cout << code; std::cout << code;
return *this; return *this;
} }
Terminal& Terminal::move(i32 row, i32 col) { Terminal& Terminal::move(i32 row, i32 col) {
std::cout << "\033[" << row << ";" << col << "H"; std::cout << "\033[" << row << ";" << col << "H";
return *this; return *this;
} }
Terminal& Terminal::altbuff(bool enable) { Terminal& Terminal::altbuff(bool enable) {
std::cout << (enable ? "\033[?1049h" : "\033[?1049l"); std::cout << (enable ? "\033[?1049h" : "\033[?1049l");
return *this; return *this;
} }
Terminal& Terminal::cls() { Terminal& Terminal::cls() {
std::cout << "\033[2J"; std::cout << "\033[2J";
return *this; return *this;
} }
Terminal& Terminal::scrollRange(i32 start, i32 end) { Terminal& Terminal::scrollRange(i32 start, i32 end) {
std::cout << "\033[" << start << ";" << end << "r\033[?6h"; std::cout << "\033[" << start << ";" << end << "r\033[?6h";
return *this; return *this;
} }
Terminal& Terminal::undoSRange() { Terminal& Terminal::undoSRange() {
std::cout << "\033[r\033[?6l\033[H"; std::cout << "\033[r\033[?6l\033[H";
return *this; return *this;
} }
Terminal& Terminal::fill(const std::string_view color) { Terminal& Terminal::fill(const std::string_view color) {
this->style(color); this->style(color);
this->cls(); this->cls();
return *this; return *this;
} }
Terminal& Terminal::clearRow(i32 row) { Terminal& Terminal::clearRow(i32 row) {
// Move to row, column 1 // Move to row, column 1
this->move(row, 1); this->move(row, 1);
// \033[2K clears the entire line // \033[2K clears the entire line
std::cout << "\033[2K"; std::cout << "\033[2K";
return *this; return *this;
} }
Terminal& Terminal::clearRows(i32 start, i32 end) { Terminal& Terminal::clearRows(i32 start, i32 end) {
// Ensure we don't loop infinitely if start > end // Ensure we don't loop infinitely if start > end
if (start > end) std::swap(start, end); if (start > end) std::swap(start, end);
for (i32 i = start; i <= end; ++i) { for (i32 i = start; i <= end; ++i) {
this->clearRow(i); this->clearRow(i);
} }
// Optional: Move cursor back to the start of the cleared block // Optional: Move cursor back to the start of the cleared block
this->move(start, 1); this->move(start, 1);
return *this; return *this;
} }
Terminal& Terminal::cursor(bool show) { Terminal& Terminal::cursor(bool show) {
std::cout << (show ? "\033[?25h" : "\033[?25l"); std::cout << (show ? "\033[?25h" : "\033[?25l");
return *this; return *this;
} }
Terminal& Terminal::drawBox(i32 startRow, i32 startCol, i32 width, i32 height, std::string_view title) { Terminal& Terminal::drawBox(i32 startRow, i32 startCol, i32 width, i32 height, std::string_view title) {
// 1. Draw the top border // 1. Draw the top border
move(startRow, startCol); move(startRow, startCol);
std::cout << ""; std::cout << "";
for (i32 i = 0; i < width - 2; ++i) std::cout << ""; for (i32 i = 0; i < width - 2; ++i) std::cout << "";
std::cout << ""; std::cout << "";
// 2. Draw the sides // 2. Draw the sides
for (i32 i = 1; i < height - 1; ++i) { for (i32 i = 1; i < height - 1; ++i) {
move(startRow + i, startCol); move(startRow + i, startCol);
std::cout << ""; std::cout << "";
move(startRow + i, startCol + width - 1); move(startRow + i, startCol + width - 1);
std::cout << ""; std::cout << "";
} }
// 3. Draw the bottom border // 3. Draw the bottom border
move(startRow + height - 1, startCol); move(startRow + height - 1, startCol);
std::cout << ""; std::cout << "";
for (i32 i = 0; i < width - 2; ++i) std::cout << ""; for (i32 i = 0; i < width - 2; ++i) std::cout << "";
std::cout << ""; std::cout << "";
// 4. Overlay the title if provided // 4. Overlay the title if provided
if (!title.empty()) { if (!title.empty()) {
move(startRow, startCol + (width - title.size() - 2) / 2); move(startRow, startCol + (width - title.size() - 2) / 2);
std::cout << " " << title << " "; std::cout << " " << title << " ";
} }
std::cout.flush(); std::cout.flush();
return *this; return *this;
} }
Terminal& Terminal::flush() { Terminal& Terminal::flush() {
std::cout << std::flush; std::cout << std::flush;
return *this; return *this;
} }
Terminal& Terminal::sink() { Terminal& Terminal::sink() {
std::cin.clear(); std::cin.clear();
#if defined(SPIDER_OS_WINDOWS) #if defined(SPIDER_OS_WINDOWS)
while (_kbhit()) _getch(); while (_kbhit()) _getch();
#endif #endif
#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS) #if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS)
tcflush(STDIN_FILENO, TCIFLUSH); tcflush(STDIN_FILENO, TCIFLUSH);
#endif #endif
return *this; return *this;
} }
std::pair<i32, i32> Terminal::getSize() { std::pair<i32, i32> Terminal::getSize() {
std::pair<i32, i32> pair; std::pair<i32, i32> pair;
#if defined(SPIDER_OS_WINDOWS) #if defined(SPIDER_OS_WINDOWS)
CONSOLE_SCREEN_BUFFER_INFO csbi; CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
pair.first = csbi.srWindow.Right - csbi.srWindow.Left + 1; pair.first = csbi.srWindow.Right - csbi.srWindow.Left + 1;
pair.second = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; pair.second = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
} }
#endif #endif
#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS) #if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS)
struct winsize w; struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) { if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) {
pair.first = w.ws_col; pair.first = w.ws_col;
pair.second = w.ws_row; pair.second = w.ws_row;
} }
#endif #endif
return pair; return pair;
} }
Terminal& Terminal::wait() { Terminal& Terminal::wait() {
sink(); sink();
std::cin.get(); std::cin.get();
return *this; return *this;
} }
u8 Terminal::getKey() { u8 Terminal::getKey() {
#if defined(SPIDER_OS_WINDOWS) #if defined(SPIDER_OS_WINDOWS)
i32 ch = _getch(); i32 ch = _getch();
if (ch == 0 || ch == 224) { if (ch == 0 || ch == 224) {
switch (_getch()) { switch (_getch()) {
case 72: return Terminal::UP; case 72: return Terminal::UP;
case 80: return Terminal::DOWN; case 80: return Terminal::DOWN;
case 75: return Terminal::LEFT; case 75: return Terminal::LEFT;
case 77: return Terminal::RIGHT; case 77: return Terminal::RIGHT;
default: return Terminal::UNKNOWN; default: return Terminal::UNKNOWN;
} }
} }
if (ch == 13) return Terminal::ENTER; if (ch == 13) return Terminal::ENTER;
if (ch == 27) return Terminal::ESC; if (ch == 27) return Terminal::ESC;
if (ch == 8) return Terminal::BACKSPACE; if (ch == 8) return Terminal::BACKSPACE;
return Terminal::UNKNOWN; return Terminal::UNKNOWN;
#endif #endif
#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS) #if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS)
struct termios oldt, newt; struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt); tcgetattr(STDIN_FILENO, &oldt);
newt = oldt; newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO); newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt); tcsetattr(STDIN_FILENO, TCSANOW, &newt);
u8 result = Terminal::UNKNOWN; u8 result = Terminal::UNKNOWN;
int ch = getchar(); int ch = getchar();
if (ch == 27) { // Potential Escape Sequence if (ch == 27) { // Potential Escape Sequence
// Use a small timeout or check if more chars are in buffer // Use a small timeout or check if more chars are in buffer
// to distinguish between 'Esc' key and 'Arrow' sequence // to distinguish between 'Esc' key and 'Arrow' sequence
// Another Win for the Win API // Another Win for the Win API
struct timeval tv = { 0, 10000 }; // 10ms wait struct timeval tv = { 0, 10000 }; // 10ms wait
fd_set fds; fd_set fds;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds); FD_SET(STDIN_FILENO, &fds);
if (select(1, &fds, NULL, NULL, &tv) > 0) { if (select(1, &fds, NULL, NULL, &tv) > 0) {
if (getchar() == '[') { if (getchar() == '[') {
switch (getchar()) { switch (getchar()) {
case 'A': result = Terminal::UP; break; case 'A': result = Terminal::UP; break;
case 'B': result = Terminal::DOWN; break; case 'B': result = Terminal::DOWN; break;
case 'D': result = Terminal::LEFT; break; case 'D': result = Terminal::LEFT; break;
case 'C': result = Terminal::RIGHT; break; case 'C': result = Terminal::RIGHT; break;
} }
} }
} else { } else {
result = Terminal::ESC; result = Terminal::ESC;
} }
} else if (ch == 10) result = Terminal::ENTER; } else if (ch == 10) result = Terminal::ENTER;
else if (ch == 127) result = Terminal::BACKSPACE; else if (ch == 127) result = Terminal::BACKSPACE;
else result = (u8)ch; else result = (u8)ch;
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return result; return result;
#endif #endif
} }
u8 Terminal::getKeyNb() { u8 Terminal::getKeyNb() {
#if defined(SPIDER_OS_WINDOWS) #if defined(SPIDER_OS_WINDOWS)
if (_kbhit()) return getKey(); if (_kbhit()) return getKey();
return Terminal::UNKNOWN; return Terminal::UNKNOWN;
#endif #endif
#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS) #if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS)
struct timeval tv = { 0, 0 }; struct timeval tv = { 0, 0 };
fd_set fds; fd_set fds;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds); FD_SET(STDIN_FILENO, &fds);
// select() returns > 0 if there is data to read // select() returns > 0 if there is data to read
if (select(1, &fds, NULL, NULL, &tv) > 0) { if (select(1, &fds, NULL, NULL, &tv) > 0) {
return getKey(); return getKey();
} }
return Terminal::UNKNOWN; return Terminal::UNKNOWN;
#endif #endif
} }
} }
#endif #endif

View File

@@ -1,176 +1,176 @@
#pragma once #pragma once
#include <spider/runtime/common.hpp> #include <spider/runtime/common.hpp>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <sstream> #include <sstream>
namespace spider { namespace spider {
class Terminal { class Terminal {
public: public:
static const char* RESET; static const char* RESET;
static const char* BOLD; static const char* BOLD;
static const char* ITALIC; static const char* ITALIC;
static const char* FAINT; static const char* FAINT;
static const char* STRIKE; static const char* STRIKE;
static const char* FG_BLACK; static const char* FG_B_BLACK; 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_RED; static const char* FG_B_RED;
static const char* FG_GREEN; static const char* FG_B_GREEN; 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_YELLOW; static const char* FG_B_YELLOW;
static const char* FG_BLUE; static const char* FG_B_BLUE; 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_MAGENTA; static const char* FG_B_MAGENTA;
static const char* FG_CYAN; static const char* FG_B_CYAN; 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* FG_WHITE; static const char* FG_B_WHITE;
static const char* BG_BLACK; static const char* BG_B_BLACK; 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_RED; static const char* BG_B_RED;
static const char* BG_GREEN; static const char* BG_B_GREEN; 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_YELLOW; static const char* BG_B_YELLOW;
static const char* BG_BLUE; static const char* BG_B_BLUE; 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_MAGENTA; static const char* BG_B_MAGENTA;
static const char* BG_CYAN; static const char* BG_B_CYAN; static const char* BG_CYAN; static const char* BG_B_CYAN;
static const char* BG_WHITE; static const char* BG_B_WHITE; static const char* BG_WHITE; static const char* BG_B_WHITE;
public: public:
// Key Definitions (ASCII OK) // // Key Definitions (ASCII OK) //
static constexpr const u8 UP = 0x80; static constexpr const u8 UP = 0x80;
static constexpr const u8 DOWN = 0x81; static constexpr const u8 DOWN = 0x81;
static constexpr const u8 LEFT = 0x82; static constexpr const u8 LEFT = 0x82;
static constexpr const u8 RIGHT = 0x83; static constexpr const u8 RIGHT = 0x83;
static constexpr const u8 ENTER = 0x84; static constexpr const u8 ENTER = 0x84;
static constexpr const u8 ESC = 0x85; static constexpr const u8 ESC = 0x85;
static constexpr const u8 BACKSPACE = 0x86; static constexpr const u8 BACKSPACE = 0x86;
static constexpr const u8 UNKNOWN = 0xFF; static constexpr const u8 UNKNOWN = 0xFF;
public: public:
Terminal(); Terminal();
~Terminal(); ~Terminal();
public: public:
/** /**
* Sets a style * Sets a style
*/ */
Terminal& style(const std::string_view code); Terminal& style(const std::string_view code);
/** /**
* Fills the screen with a specific background color. * Fills the screen with a specific background color.
* @param color The background color constant (e.g., Terminal::BG_BLUE) * @param color The background color constant (e.g., Terminal::BG_BLUE)
*/ */
Terminal& fill(const std::string_view color); Terminal& fill(const std::string_view color);
/** /**
* Moves the cursor. * Moves the cursor.
*/ */
Terminal& move(i32 row, i32 col); Terminal& move(i32 row, i32 col);
/** /**
* Clears the screen * Clears the screen
*/ */
Terminal& cls(); Terminal& cls();
Terminal& altbuff(bool enable); Terminal& altbuff(bool enable);
Terminal& scrollRange(i32 start, i32 end); Terminal& scrollRange(i32 start, i32 end);
Terminal& undoSRange(); Terminal& undoSRange();
/** /**
* Clears a specific row. * Clears a specific row.
* @param row The 1-based index of the row to clear. * @param row The 1-based index of the row to clear.
*/ */
Terminal& clearRow(i32 row); Terminal& clearRow(i32 row);
/** /**
* Clears a range of rows (inclusive). * Clears a range of rows (inclusive).
* @param start The first row. * @param start The first row.
* @param end The last row. * @param end The last row.
*/ */
Terminal& clearRows(i32 start, i32 end); Terminal& clearRows(i32 start, i32 end);
/** /**
* Shows / Hides the cursor. * Shows / Hides the cursor.
*/ */
Terminal& cursor(bool show); Terminal& cursor(bool show);
public: public:
Terminal& drawBox(i32 startRow, i32 startCol, i32 width, i32 height, std::string_view title); Terminal& drawBox(i32 startRow, i32 startCol, i32 width, i32 height, std::string_view title);
public: public:
/** /**
* Flushes the output buffer * Flushes the output buffer
*/ */
Terminal& flush(); Terminal& flush();
/** /**
* Clears the input buffer. * Clears the input buffer.
* Useful for some specific input cases * Useful for some specific input cases
*/ */
Terminal& sink(); Terminal& sink();
public: public:
Terminal& wait(); Terminal& wait();
u8 getKey(); u8 getKey();
/** /**
* Get key non blocking * Get key non blocking
*/ */
u8 getKeyNb(); u8 getKeyNb();
std::pair<i32, i32> getSize(); std::pair<i32, i32> getSize();
public: public:
template <typename T> template <typename T>
Terminal& print(const T& msg) { Terminal& print(const T& msg) {
std::cout << msg; std::cout << msg;
return *this; return *this;
} }
template <typename T> template <typename T>
Terminal& println(const T& msg) { Terminal& println(const T& msg) {
std::cout << msg << "\n"; std::cout << msg << "\n";
return *this; return *this;
} }
template <typename T> template <typename T>
Terminal& print_center(i32 width, const T& msg) { Terminal& print_center(i32 width, const T& msg) {
// to string // to string
std::ostringstream oss; std::ostringstream oss;
oss << msg; oss << msg;
std::string s = oss.str(); std::string s = oss.str();
// then print // then print
if (s.length() >= isize(width)) { if (s.length() >= isize(width)) {
std::cout << s; std::cout << s;
} else { } else {
i32 total_padding = width - s.length(); i32 total_padding = width - s.length();
i32 left_padding = total_padding / 2; i32 left_padding = total_padding / 2;
std::cout << std::string(left_padding, ' '); std::cout << std::string(left_padding, ' ');
std::cout << s; std::cout << s;
std::cout << std::string(total_padding - left_padding, ' '); std::cout << std::string(total_padding - left_padding, ' ');
} }
return *this; return *this;
} }
template <typename T> template <typename T>
Terminal& read(T& var) { Terminal& read(T& var) {
std::cin >> var; std::cin >> var;
return *this; return *this;
} }
}; };
} }