add ESP32 build system with xtensa-esp-elf toolchain
This commit is contained in:
12
.gitignore
vendored
12
.gitignore
vendored
@@ -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
36
LICENSE
@@ -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.
|
||||
|
||||
26
README.md
26
README.md
@@ -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
56
build/esp32/Makefile
Normal 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
|
||||
BIN
build/esp32/bin/spider/main_esp32.o
Normal file
BIN
build/esp32/bin/spider/main_esp32.o
Normal file
Binary file not shown.
BIN
build/esp32/bin/spider/runtime/Runtime.o
Normal file
BIN
build/esp32/bin/spider/runtime/Runtime.o
Normal file
Binary file not shown.
BIN
build/esp32/bin/spider/runtime/cpu/CPU.o
Normal file
BIN
build/esp32/bin/spider/runtime/cpu/CPU.o
Normal file
Binary file not shown.
BIN
build/esp32/bin/spider/runtime/instr/Instr_00-1F.o
Normal file
BIN
build/esp32/bin/spider/runtime/instr/Instr_00-1F.o
Normal file
Binary file not shown.
BIN
build/esp32/bin/spider/runtime/math/Quat.o
Normal file
BIN
build/esp32/bin/spider/runtime/math/Quat.o
Normal file
Binary file not shown.
BIN
build/esp32/bin/spider/runtime/memory/ByteArray.o
Normal file
BIN
build/esp32/bin/spider/runtime/memory/ByteArray.o
Normal file
Binary file not shown.
BIN
build/esp32/bin/spider/runtime/memory/RAM.o
Normal file
BIN
build/esp32/bin/spider/runtime/memory/RAM.o
Normal file
Binary file not shown.
BIN
build/esp32/bin/spider/runtime/reel/InstrReel.o
Normal file
BIN
build/esp32/bin/spider/runtime/reel/InstrReel.o
Normal file
Binary file not shown.
BIN
build/esp32/bin/spider/runtime/reel/InstrReelDyn.o
Normal file
BIN
build/esp32/bin/spider/runtime/reel/InstrReelDyn.o
Normal file
Binary file not shown.
BIN
build/esp32/bin/spider/runtime/reel/InstrReelFixed.o
Normal file
BIN
build/esp32/bin/spider/runtime/reel/InstrReelFixed.o
Normal file
Binary file not shown.
64
build/esp32/gen_makefile.py
Normal file
64
build/esp32/gen_makefile.py
Normal 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!")
|
||||
BIN
build/esp32/out/spider_esp32.elf
Normal file
BIN
build/esp32/out/spider_esp32.elf
Normal file
Binary file not shown.
128
makefile
128
makefile
@@ -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
|
||||
942
pygen.ipynb
942
pygen.ipynb
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
8
src/spider/main_esp32.cpp
Normal file
8
src/spider/main_esp32.cpp
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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.");
|
||||
|
||||
}
|
||||
@@ -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(<, &t);
|
||||
#endif
|
||||
#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS)
|
||||
localtime_r(&t, <);
|
||||
#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(<, &t);
|
||||
#endif
|
||||
#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS)
|
||||
localtime_r(&t, <);
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace spider {
|
||||
|
||||
int liveDebugMain();
|
||||
|
||||
}
|
||||
#pragma once
|
||||
|
||||
namespace spider {
|
||||
|
||||
int liveDebugMain();
|
||||
|
||||
}
|
||||
|
||||
@@ -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 //
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 ================== //
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user