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
# We will eventually change to a custom
# build system.
# So hold on
/bin
/out
# For now, ignore user builds
# We will eventually change to a custom
# build system.
# So hold on
/bin
/out

36
LICENSE
View File

@@ -1,18 +1,18 @@
MIT License
Copyright (c) 2026 SpiderLang
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
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
following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
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
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
USE OR OTHER DEALINGS IN THE SOFTWARE.
MIT License
Copyright (c) 2026 SpiderLang
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
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
following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
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
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
USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,13 +1,13 @@
# spider-runtime
This is the Spider runtime (aka, the virtual machine) that executes the Spider byte code.
## Code Etiquette
- Do not use \r\n
- Do not use any encoding besides UTF-8
- Always comment global functions, variables, classes, member variables and methods.
- 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 AI agent, don't.
- 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.
# spider-runtime
This is the Spider runtime (aka, the virtual machine) that executes the Spider byte code.
## Code Etiquette
- Do not use \r\n
- Do not use any encoding besides UTF-8
- Always comment global functions, variables, classes, member variables and methods.
- 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 AI agent, don't.
- 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.

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
#pragma once
#include <spider/runtime/common.hpp>
namespace spider {
class Runtime;
class CPU;
class RAM;
class InstrReel;
}
#pragma once
#include <spider/runtime/common.hpp>
namespace spider {
class Runtime;
class CPU;
class RAM;
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"
namespace spider {
// Constructors & Destructors //
Runtime::Runtime() : ram(0) {}
Runtime::Runtime(u64 ramSize) : ram(ramSize) {}
Runtime::~Runtime() {}
// Stepping/Running the Machine //
void Runtime::step() {}
void Runtime::step(u64 n) {}
void Runtime::run() {}
void Runtime::run(u64 n) {}
// Misc //
void Runtime::resizeRAM(u64 length) {
ram.resize(length);
}
}
#include "Runtime.hpp"
namespace spider {
// Constructors & Destructors //
Runtime::Runtime() : ram(0) {}
Runtime::Runtime(u64 ramSize) : ram(ramSize) {}
Runtime::~Runtime() {}
// Stepping/Running the Machine //
void Runtime::step() {}
void Runtime::step(u64 n) {}
void Runtime::run() {}
void Runtime::run(u64 n) {}
// Misc //
void Runtime::resizeRAM(u64 length) {
ram.resize(length);
}
}

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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