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