Refactor to varied length opcodes, update structure, fix Makefile, WIP: number toS
This commit is contained in:
parent
94a1375d51
commit
dd7cc62a3e
|
@ -104,6 +104,6 @@ dkms.conf
|
|||
zre
|
||||
zre.wasm
|
||||
memory_dump.bin
|
||||
src/build/
|
||||
build/
|
||||
.gdb_history
|
||||
.vscode
|
|
@ -0,0 +1,153 @@
|
|||
# --- CONFIGURABLE DEFAULTS ---
|
||||
PLATFORM ?= linux
|
||||
BUILD_MODE ?= debug # 'debug' or 'deploy'
|
||||
|
||||
# --- DIRECTORIES ---
|
||||
SRC_DIR := src
|
||||
BUILD_DIR := build/$(PLATFORM)
|
||||
ARCH_DIR := $(SRC_DIR)/arch/$(PLATFORM)
|
||||
|
||||
# --- COMPILERS ---
|
||||
ifeq ($(PLATFORM), emscripten)
|
||||
CC := emcc
|
||||
else
|
||||
CC ?= gcc
|
||||
endif
|
||||
|
||||
# --- SHARED FLAGS ---
|
||||
BASE_CFLAGS := -I$(SRC_DIR) -Wall -Wextra -Werror -pedantic
|
||||
|
||||
# --- CORE-SPECIFIC FLAGS (freestanding, no stdlib) ---
|
||||
CORE_CFLAGS := $(BASE_CFLAGS) -std=c89 -ffreestanding -nostdlib -fno-builtin
|
||||
|
||||
# --- PLATFORM-SPECIFIC FLAGS ---
|
||||
PLATFORM_CFLAGS := $(BASE_CFLAGS)
|
||||
|
||||
# --- MODE & PLATFORM SPECIFIC FLAGS ---
|
||||
ifeq ($(BUILD_MODE), deploy)
|
||||
CORE_CFLAGS += -O2 -DNDEBUG
|
||||
PLATFORM_CFLAGS += -O2 -DNDEBUG
|
||||
LDFLAGS += -s
|
||||
TARGET_SUFFIX := -release
|
||||
else
|
||||
CORE_CFLAGS += -g -DDEBUG
|
||||
PLATFORM_CFLAGS += -g -DDEBUG
|
||||
TARGET_SUFFIX := -debug
|
||||
endif
|
||||
|
||||
# --- LINUX-SPECIFIC (SDL, libc) ---
|
||||
ifeq ($(PLATFORM), linux)
|
||||
PLATFORM_CFLAGS += $(shell pkg-config --cflags sdl2 2>/dev/null)
|
||||
LDFLAGS += $(shell pkg-config --libs sdl2 2>/dev/null) -lm
|
||||
TARGET = $(BUILD_DIR)/zre-$(PLATFORM)$(TARGET_SUFFIX)
|
||||
endif
|
||||
|
||||
# --- EMSCRIPTEN-SPECIFIC ---
|
||||
ifeq ($(PLATFORM), emscripten)
|
||||
# Core still compiled freestanding (emcc supports this)
|
||||
# Platform layer gets Emscripten/SDL flags
|
||||
PLATFORM_CFLAGS += -s USE_SDL=2 -s WASM=1
|
||||
LDFLAGS += -s USE_SDL=2 -s WASM=1
|
||||
|
||||
# For deploy: optimize, strip debug, minimize size
|
||||
ifeq ($(BUILD_MODE), deploy)
|
||||
PLATFORM_CFLAGS += -O3
|
||||
LDFLAGS += -O3 --closure 1
|
||||
else
|
||||
# For debug: preserve names, generate source maps
|
||||
PLATFORM_CFLAGS += -gsource-map
|
||||
LDFLAGS += -gsource-map --source-map-base .
|
||||
endif
|
||||
|
||||
# Output: HTML + JS + WASM
|
||||
TARGET = $(BUILD_DIR)/zre.html
|
||||
endif
|
||||
|
||||
# --- FALLBACK TARGET (if no platform matched) ---
|
||||
ifndef TARGET
|
||||
TARGET = $(BUILD_DIR)/zre-$(PLATFORM)$(TARGET_SUFFIX)
|
||||
endif
|
||||
|
||||
# --- CORE SOURCES (flat in src/) ---
|
||||
CORE_SOURCES := \
|
||||
$(SRC_DIR)/str.c \
|
||||
$(SRC_DIR)/vm.c \
|
||||
$(SRC_DIR)/device.c \
|
||||
$(SRC_DIR)/test.c
|
||||
|
||||
# --- PLATFORM SOURCE ---
|
||||
PLATFORM_SOURCE := $(ARCH_DIR)/main.c \
|
||||
$(ARCH_DIR)/devices.c
|
||||
|
||||
# --- OBJECT FILES ---
|
||||
CORE_OBJS := $(CORE_SOURCES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
|
||||
PLATFORM_OBJ := $(PLATFORM_SOURCE:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
|
||||
|
||||
# --- DEPENDENCIES ---
|
||||
DEPS := $(CORE_OBJS:.o=.d) $(PLATFORM_OBJ:.o=.d)
|
||||
|
||||
.PHONY: all debug deploy clean clean-all help
|
||||
|
||||
# Default target
|
||||
all: $(TARGET)
|
||||
|
||||
# 'debug' target — just set BUILD_MODE and build
|
||||
debug: BUILD_MODE = debug
|
||||
debug: $(TARGET)
|
||||
|
||||
# 'deploy' target — just set BUILD_MODE and build
|
||||
deploy: BUILD_MODE = deploy
|
||||
deploy: $(TARGET)
|
||||
|
||||
# Main build rule
|
||||
$(TARGET): $(CORE_OBJS) $(PLATFORM_OBJ)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $(PLATFORM_CFLAGS) $(LDFLAGS) -o $@ $^
|
||||
@echo "Built: $@"
|
||||
@if [ "$(PLATFORM)" = "emscripten" ]; then \
|
||||
echo "Open in browser: file://$(shell pwd)/$@"; \
|
||||
fi
|
||||
|
||||
# --- COMPILE CORE (freestanding) ---
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $(CORE_CFLAGS) -MMD -MP -c $< -o $@
|
||||
|
||||
# --- COMPILE PLATFORM ---
|
||||
$(BUILD_DIR)/arch/$(PLATFORM)/%.o: $(ARCH_DIR)/%.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $(PLATFORM_CFLAGS) -MMD -MP -c $< -o $@
|
||||
|
||||
# --- AUTO-DEPENDENCIES ---
|
||||
-include $(DEPS)
|
||||
|
||||
# --- CLEAN ---
|
||||
clean:
|
||||
@echo "Cleaning build artifacts for $(PLATFORM)..."
|
||||
@rm -rf $(BUILD_DIR)
|
||||
@echo "Cleaned."
|
||||
|
||||
clean-all:
|
||||
@echo "Cleaning ALL build artifacts..."
|
||||
@rm -rf build/
|
||||
@echo "All cleaned."
|
||||
|
||||
# --- HELP ---
|
||||
help:
|
||||
@echo "Undar VM"
|
||||
@echo ""
|
||||
@echo "Targets:"
|
||||
@echo " make -> debug build (default: linux)"
|
||||
@echo " make debug -> same as above"
|
||||
@echo " make deploy -> optimized, stripped build"
|
||||
@echo " make clean -> clean current platform"
|
||||
@echo " make clean-all→ clean all platforms"
|
||||
@echo ""
|
||||
@echo "Platforms:"
|
||||
@echo " make PLATFORM=linux -> GCC + SDL2"
|
||||
@echo " make PLATFORM=emscripten -> Emscripten + SDL2 for Web"
|
||||
@echo " make PLATFORM=avr -> (example) AVR-GCC"
|
||||
@echo ""
|
||||
@echo "Output:"
|
||||
@echo " Linux: build/linux/zre-linux-<debug|release>"
|
||||
@echo " Web: build/emscripten/zre.html (+ .js, .wasm)"
|
|
@ -1,5 +1,5 @@
|
|||
local a = 5.0
|
||||
for i = 50000, 0, -1 do
|
||||
for i = 5000, 0, -1 do
|
||||
a = a + 5.0
|
||||
end
|
||||
local b = math.floor(a)
|
|
@ -1,5 +1,5 @@
|
|||
my $a = 5.0;
|
||||
for (my $i = 50000; $i >= 0; $i--) {
|
||||
for (my $i = 5000; $i >= 0; $i--) {
|
||||
$a += 5.0;
|
||||
}
|
||||
my $b = int($a);
|
|
@ -1,5 +1,5 @@
|
|||
a = 5.0
|
||||
for i in range(50000, -1, -1):
|
||||
for i in range(5000, -1, -1):
|
||||
a += 5.0
|
||||
b = int(a)
|
||||
user_string = input("Enter a string:")
|
|
@ -1,9 +1,9 @@
|
|||
real a = 5.0;
|
||||
do (int i = 50000, 0, -1) {
|
||||
do (int i = 5000, 0, -1) {
|
||||
a = a + 5.0;
|
||||
}
|
||||
int b = a as nat;
|
||||
print "Enter a string:");
|
||||
print("Enter a string:");
|
||||
str user_string = read();
|
||||
print a.str;
|
||||
print b.str;
|
|
@ -1,5 +1,5 @@
|
|||
let a = 5.0;
|
||||
for (let i = 50000; i > 0; i = i - 1) {
|
||||
for (let i = 5000; i > 0; i = i - 1) {
|
||||
a = a + 5.0;
|
||||
}
|
||||
let b = a;
|
91
src/Makefile
91
src/Makefile
|
@ -1,91 +0,0 @@
|
|||
# Compiler configurations
|
||||
# -----------------------
|
||||
# Native build (gcc)
|
||||
CC_NATIVE = gcc
|
||||
CFLAGS_NATIVE = -g -std=c89 -Wall -Wextra -Werror -Wno-unused-parameter -I. #-O2
|
||||
LDFLAGS_NATIVE =
|
||||
LDLIBS_NATIVE = -lSDL2
|
||||
|
||||
# WASM build (emscripten)
|
||||
CC_WASM = emcc
|
||||
CFLAGS_WASM = -g -std=c89 -Wall -Wextra -Werror -Wno-unused-parameter -I.
|
||||
LDFLAGS_WASM = -s WASM=1 -g -s USE_SDL=2
|
||||
LDLIBS_WASM =
|
||||
|
||||
# Source and build configuration
|
||||
# ----------------------------
|
||||
COMMON_SRC = $(wildcard *.c)
|
||||
|
||||
# Separate architecture sources
|
||||
ARCH_SRC_LINUX = $(wildcard arch/linux/*.c)
|
||||
ARCH_SRC_EMSCRIPTEN = $(wildcard arch/emscripten/*.c)
|
||||
|
||||
# Object directories
|
||||
OBJ_DIR_NATIVE = build/native/obj
|
||||
OBJ_DIR_WASM = build/wasm/obj
|
||||
|
||||
# Common objects from root
|
||||
OBJ_NATIVE = $(addprefix $(OBJ_DIR_NATIVE)/,$(notdir $(COMMON_SRC:.c=.o)))
|
||||
OBJ_WASM = $(addprefix $(OBJ_DIR_WASM)/,$(notdir $(COMMON_SRC:.c=.o)))
|
||||
|
||||
# Architecture-specific objects
|
||||
OBJ_ARCH_NATIVE = $(addprefix $(OBJ_DIR_NATIVE)/,$(notdir $(ARCH_SRC_LINUX:.c=.o)))
|
||||
OBJ_ARCH_WASM = $(addprefix $(OBJ_DIR_WASM)/,$(notdir $(ARCH_SRC_EMSCRIPTEN:.c=.o)))
|
||||
|
||||
# Executables
|
||||
EXEC_NATIVE = zre
|
||||
EXEC_WASM = zre.wasm
|
||||
|
||||
# Phony targets
|
||||
.PHONY: all clean install wasm native emscripten linux macos
|
||||
|
||||
# Default target builds the native version
|
||||
all: native
|
||||
|
||||
# Native build rules
|
||||
# ------------------
|
||||
native: linux
|
||||
|
||||
linux: $(EXEC_NATIVE)
|
||||
|
||||
$(EXEC_NATIVE): $(OBJ_NATIVE) $(OBJ_ARCH_NATIVE)
|
||||
$(CC_NATIVE) $(LDFLAGS_NATIVE) $^ $(LDLIBS_NATIVE) -o $@
|
||||
|
||||
# WASM build rules
|
||||
# ----------------
|
||||
wasm: emscripten
|
||||
|
||||
emscripten: $(EXEC_WASM)
|
||||
|
||||
$(EXEC_WASM): $(OBJ_WASM) $(OBJ_ARCH_WASM)
|
||||
$(CC_WASM) $(LDFLAGS_WASM) $^ $(LDLIBS_WASM) -o $@
|
||||
|
||||
# Object file rules
|
||||
# -----------------
|
||||
$(OBJ_DIR_NATIVE)/%.o: %.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC_NATIVE) $(CFLAGS_NATIVE) -c $< -o $@
|
||||
|
||||
$(OBJ_DIR_WASM)/%.o: %.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC_WASM) $(CFLAGS_WASM) -c $< -o $@
|
||||
|
||||
# Architecture-specific source rules
|
||||
$(OBJ_DIR_NATIVE)/%.o: arch/linux/%.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC_NATIVE) $(CFLAGS_NATIVE) -c $< -o $@
|
||||
|
||||
$(OBJ_DIR_WASM)/%.o: arch/emscripten/%.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC_WASM) $(CFLAGS_WASM) -c $< -o $@
|
||||
|
||||
# Clean build artifacts
|
||||
# ---------------------
|
||||
clean:
|
||||
rm -rf $(OBJ_DIR_NATIVE) $(OBJ_DIR_WASM) $(EXEC_NATIVE) $(EXEC_WASM)
|
||||
|
||||
# Install target (example)
|
||||
# ------------------------
|
||||
install: native
|
||||
install -d $(DESTDIR)/usr/local/bin
|
||||
install $(EXEC_NATIVE) $(DESTDIR)/usr/local/bin/
|
|
@ -1,6 +1,43 @@
|
|||
#include "devices.h"
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
int screen_open(void *data, uint32_t mode) {
|
||||
i32 console_open(void *data, u32 mode) {
|
||||
USED(mode);
|
||||
USED(data);
|
||||
/* Nothing to open — stdin/stdout are always available */
|
||||
return 0; /* Success */
|
||||
}
|
||||
|
||||
i32 console_read(void *data, u8 *buffer, u32 size) {
|
||||
USED(data);
|
||||
ssize_t result = read(STDIN_FILENO, buffer, size);
|
||||
if (result < 0) return -1; /* Error */
|
||||
return (i32)result; /* Bytes read */
|
||||
}
|
||||
|
||||
i32 console_write(void *data, const u8 *buffer, u32 size) {
|
||||
USED(data);
|
||||
ssize_t result = write(STDOUT_FILENO, buffer, size);
|
||||
if (result < 0) return -1; /* Error */
|
||||
return (i32)result; /* Bytes written */
|
||||
}
|
||||
|
||||
i32 console_close(void *data) {
|
||||
USED(data);
|
||||
/* Nothing to close — stdin/stdout are process-owned */
|
||||
return 0;
|
||||
}
|
||||
|
||||
i32 console_ioctl(void *data, u32 cmd, void *args) {
|
||||
USED(data);
|
||||
USED(cmd);
|
||||
USED(args);
|
||||
return -1; /* Unsupported */
|
||||
}
|
||||
|
||||
i32 screen_open(void *data, u32 mode) {
|
||||
USED(mode);
|
||||
ScreenDeviceData *screen = (ScreenDeviceData *)data;
|
||||
|
||||
|
||||
|
@ -27,12 +64,17 @@ int screen_open(void *data, uint32_t mode) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int screen_read(void *data, uint8_t *buffer, uint32_t size) { return -1; }
|
||||
i32 screen_read(void *data, u8 *buffer, u32 size) {
|
||||
USED(data);
|
||||
USED(buffer);
|
||||
USED(size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int screen_write(void *data, const uint8_t *buffer, uint32_t size) {
|
||||
i32 screen_write(void *data, const u8 *buffer, u32 size) {
|
||||
ScreenDeviceData *screen = (ScreenDeviceData *)data;
|
||||
|
||||
if (size > screen->framebuffer_size * sizeof(uint8_t)) {
|
||||
if (size > screen->framebuffer_size * sizeof(u8)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -40,7 +82,7 @@ int screen_write(void *data, const uint8_t *buffer, uint32_t size) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int screen_close(void *data) {
|
||||
i32 screen_close(void *data) {
|
||||
ScreenDeviceData *screen = (ScreenDeviceData *)data;
|
||||
|
||||
if (screen->texture) {
|
||||
|
@ -59,15 +101,19 @@ int screen_close(void *data) {
|
|||
}
|
||||
|
||||
/* MOUSE */
|
||||
int mouse_open(void *data, uint32_t mode) { return 0; }
|
||||
i32 mouse_open(void *data, u32 mode) {
|
||||
USED(data);
|
||||
USED(mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mouse_read(void *data, uint8_t *buffer, uint32_t size) {
|
||||
i32 mouse_read(void *data, u8 *buffer, u32 size) {
|
||||
MouseDeviceData *mouse = (MouseDeviceData *)data;
|
||||
|
||||
if (size < 3 * sizeof(uint32_t))
|
||||
if (size < 3 * sizeof(u32))
|
||||
return -1;
|
||||
|
||||
uint32_t *out = (uint32_t *)buffer;
|
||||
u32 *out = (u32 *)buffer;
|
||||
out[0] = mouse->x;
|
||||
out[1] = mouse->y;
|
||||
out[2] = (mouse->btn1 | (mouse->btn2 << 1) | (mouse->btn3 << 2) |
|
||||
|
@ -75,24 +121,42 @@ int mouse_read(void *data, uint8_t *buffer, uint32_t size) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int mouse_write(void *data, const uint8_t *buffer, uint32_t size) { return -1; }
|
||||
i32 mouse_write(void *data, const u8 *buffer, u32 size) {
|
||||
USED(data);
|
||||
USED(buffer);
|
||||
USED(size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mouse_close(void *data) { return 0; }
|
||||
i32 mouse_close(void *data) {
|
||||
USED(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int keyboard_open(void *data, uint32_t mode) { return 0; }
|
||||
i32 keyboard_open(void *data, u32 mode) {
|
||||
USED(data);
|
||||
USED(mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int keyboard_read(void *data, uint8_t *buffer, uint32_t size) {
|
||||
i32 keyboard_read(void *data, u8 *buffer, u32 size) {
|
||||
KeyboardDeviceData *kbd = (KeyboardDeviceData *)data;
|
||||
|
||||
if (size < (uint32_t)kbd->key_count)
|
||||
if (size < (u32)kbd->key_count)
|
||||
return -1;
|
||||
|
||||
memcpy(buffer, kbd->keys, kbd->key_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int keyboard_write(void *data, const uint8_t *buffer, uint32_t size) {
|
||||
i32 keyboard_write(void *data, const u8 *buffer, u32 size) {
|
||||
USED(data);
|
||||
USED(buffer);
|
||||
USED(size);
|
||||
return -1; /* not writable */
|
||||
}
|
||||
|
||||
int keyboard_close(void *data) { return 0; }
|
||||
i32 keyboard_close(void *data) {
|
||||
USED(data);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
/* Screen device data */
|
||||
typedef struct screen_device_data_s {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t framebuffer_pos;
|
||||
uint32_t framebuffer_size;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 framebuffer_pos;
|
||||
u32 framebuffer_size;
|
||||
VM* vm;
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
|
@ -16,32 +16,37 @@ typedef struct screen_device_data_s {
|
|||
|
||||
/* Mouse device data */
|
||||
typedef struct mouse_device_data_s {
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
uint8_t btn1;
|
||||
uint8_t btn2;
|
||||
uint8_t btn3;
|
||||
uint8_t btn4;
|
||||
u32 x;
|
||||
u32 y;
|
||||
u8 btn1;
|
||||
u8 btn2;
|
||||
u8 btn3;
|
||||
u8 btn4;
|
||||
} MouseDeviceData;
|
||||
|
||||
/* Keyboard device data */
|
||||
typedef struct keyboard_device_data_s {
|
||||
const uint8_t *keys;
|
||||
int32_t key_count;
|
||||
const u8 *keys;
|
||||
i32 key_count;
|
||||
} KeyboardDeviceData;
|
||||
|
||||
int screen_open(void *data, uint32_t mode);
|
||||
int screen_read(void *data, uint8_t *buffer, uint32_t size);
|
||||
int screen_write(void *data, const uint8_t *buffer, uint32_t size);
|
||||
int screen_close(void *data);
|
||||
i32 screen_open(void *data, u32 mode);
|
||||
i32 screen_read(void *data, u8 *buffer, u32 size);
|
||||
i32 screen_write(void *data, const u8 *buffer, u32 size);
|
||||
i32 screen_close(void *data);
|
||||
|
||||
int mouse_open(void *data, uint32_t mode);
|
||||
int mouse_read(void *data, uint8_t *buffer, uint32_t size);
|
||||
int mouse_write(void *data, const uint8_t *buffer, uint32_t size);
|
||||
int mouse_close(void *data);
|
||||
i32 mouse_open(void *data, u32 mode);
|
||||
i32 mouse_read(void *data, u8 *buffer, u32 size);
|
||||
i32 mouse_write(void *data, const u8 *buffer, u32 size);
|
||||
i32 mouse_close(void *data);
|
||||
|
||||
int keyboard_open(void *data, uint32_t mode);
|
||||
int keyboard_read(void *data, uint8_t *buffer, uint32_t size);
|
||||
int keyboard_write(void *data, const uint8_t *buffer, uint32_t size);
|
||||
int keyboard_close(void *data);
|
||||
i32 keyboard_open(void *data, u32 mode);
|
||||
i32 keyboard_read(void *data, u8 *buffer, u32 size);
|
||||
i32 keyboard_write(void *data, const u8 *buffer, u32 size);
|
||||
i32 keyboard_close(void *data);
|
||||
|
||||
i32 console_open(void *data, u32 mode);
|
||||
i32 console_read(void *data, u8 *buffer, u32 size);
|
||||
i32 console_write(void *data, const u8 *buffer, u32 size);
|
||||
i32 console_close(void *data);
|
||||
i32 console_ioctl(void *data, u32 cmd, void *args);
|
|
@ -26,11 +26,21 @@ static DeviceOps keyboard_ops = {.open = keyboard_open,
|
|||
.close = keyboard_close,
|
||||
.ioctl = NULL};
|
||||
|
||||
static DeviceOps console_device_ops = {
|
||||
.open = console_open,
|
||||
.read = console_read,
|
||||
.write = console_write,
|
||||
.close = console_close,
|
||||
.ioctl = console_ioctl,
|
||||
};
|
||||
|
||||
static ScreenDeviceData screen_data = {0};
|
||||
static MouseDeviceData mouse_data = {0};
|
||||
static KeyboardDeviceData keyboard_data = {0};
|
||||
|
||||
void compileFile(const char *path, VM *vm) {
|
||||
USED(vm);
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
perror("fopen");
|
||||
|
@ -84,13 +94,13 @@ enum FlagType {
|
|||
#define MAX_INPUT_FILES 16 /* Adjust based on your system's constraints */
|
||||
|
||||
struct CompilerConfig {
|
||||
uint32_t flags;
|
||||
u32 flags;
|
||||
char *input_files[MAX_INPUT_FILES];
|
||||
int input_file_count;
|
||||
i32 input_file_count;
|
||||
};
|
||||
|
||||
int parse_arguments(int argc, char *argv[], struct CompilerConfig *config) {
|
||||
int i;
|
||||
i32 parse_arguments(i32 argc, char *argv[], struct CompilerConfig *config) {
|
||||
i32 i;
|
||||
|
||||
/* Initialize config */
|
||||
config->flags = 0;
|
||||
|
@ -131,6 +141,9 @@ int parse_arguments(int argc, char *argv[], struct CompilerConfig *config) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This needs to be done dynamically eventually
|
||||
*/
|
||||
void register_sdl_devices(VM *vm) {
|
||||
screen_data.vm = vm;
|
||||
screen_data.width = 640;
|
||||
|
@ -156,7 +169,7 @@ void register_sdl_devices(VM *vm) {
|
|||
&keyboard_ops);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
i32 main(i32 argc, char *argv[]) {
|
||||
struct CompilerConfig config = {0};
|
||||
|
||||
if (parse_arguments(argc, argv, &config) != 0) {
|
||||
|
@ -172,7 +185,7 @@ int main(int argc, char *argv[]) {
|
|||
if (config.flags & FLAG_TEST_MODE) {
|
||||
compile_internal_test(config.input_files[0], &vm);
|
||||
} else {
|
||||
int j;
|
||||
i32 j;
|
||||
for (j = 0; j < config.input_file_count; j++) {
|
||||
compileFile(config.input_files[j], &vm);
|
||||
}
|
||||
|
@ -206,9 +219,10 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
bool running = true;
|
||||
vm_register_device(&vm, "/dev/term/0", "terminal", nil, &console_device_ops);
|
||||
|
||||
if (config.flags & FLAG_GUI_MODE) {
|
||||
uint32_t i;
|
||||
u32 i;
|
||||
register_sdl_devices(&vm);
|
||||
while (running) {
|
||||
for (i = 0; i < vm.dc; i++) {
|
||||
|
@ -260,7 +274,7 @@ int main(int argc, char *argv[]) {
|
|||
if (screen->texture && screen->renderer) {
|
||||
SDL_UpdateTexture(screen->texture, NULL,
|
||||
&vm.memory[screen->framebuffer_pos],
|
||||
screen->width * sizeof(uint32_t));
|
||||
screen->width * sizeof(u32));
|
||||
|
||||
SDL_RenderClear(screen->renderer);
|
||||
|
||||
|
@ -271,9 +285,9 @@ int main(int argc, char *argv[]) {
|
|||
float scale = SDL_min(scale_x, scale_y);
|
||||
|
||||
SDL_Rect dstrect = {
|
||||
(int)((output_rect.w - screen->width * scale) / 2),
|
||||
(int)((output_rect.h - screen->height * scale) / 2),
|
||||
(int)(screen->width * scale), (int)(screen->height * scale)};
|
||||
(i32)((output_rect.w - screen->width * scale) / 2),
|
||||
(i32)((output_rect.h - screen->height * scale) / 2),
|
||||
(i32)(screen->width * scale), (i32)(screen->height * scale)};
|
||||
|
||||
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &dstrect);
|
||||
SDL_RenderPresent(screen->renderer);
|
||||
|
|
23
src/common.h
23
src/common.h
|
@ -1,22 +1,19 @@
|
|||
#ifndef ZRE_COMMON_H
|
||||
#define ZRE_COMMON_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef signed char i8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short i16;
|
||||
typedef unsigned long u32;
|
||||
typedef signed long i32;
|
||||
typedef uint8_t u8;
|
||||
typedef int8_t i8;
|
||||
typedef uint16_t u16;
|
||||
typedef int16_t i16;
|
||||
typedef uint32_t u32;
|
||||
typedef int32_t i32;
|
||||
|
||||
/* For 64-bit, use compiler extensions or split into hi/lo u32 on pure C89 platforms */
|
||||
#if defined(__GNUC__) || defined(_MSC_VER)
|
||||
typedef unsigned long long u64;
|
||||
typedef signed long long i64;
|
||||
#endif
|
||||
#define nil NULL
|
||||
|
||||
#define USED(x) ((void)(x))
|
||||
|
||||
#endif
|
||||
|
|
70
src/device.c
70
src/device.c
|
@ -1,53 +1,57 @@
|
|||
#include "device.h"
|
||||
#include "str.h"
|
||||
|
||||
#include <string.h>
|
||||
i32 vm_register_device(VM *vm, const char *path, const char *type, void *data,
|
||||
DeviceOps *ops) {
|
||||
Device *dev;
|
||||
|
||||
int vm_register_device(VM *vm, const char *path, const char *type, void *data, DeviceOps *ops) {
|
||||
if (vm->dc >= DEVICES_SIZE) return -1;
|
||||
if (vm->dc >= DEVICES_SIZE)
|
||||
return -1;
|
||||
|
||||
Device *dev = &vm->devices[vm->dc++];
|
||||
dev = &vm->devices[vm->dc++];
|
||||
|
||||
strncpy(dev->path, path, DEVICE_PATH_MAX_LENGTH);
|
||||
dev->path[DEVICE_PATH_MAX_LENGTH - 1] = '\0';
|
||||
strcopy(dev->path, path, DEVICE_PATH_MAX_LENGTH);
|
||||
dev->path[DEVICE_PATH_MAX_LENGTH - 1] = '\0';
|
||||
|
||||
strncpy(dev->type, type, DEVICE_TYPE_MAX_LENGTH);
|
||||
dev->type[DEVICE_TYPE_MAX_LENGTH - 1] = '\0';
|
||||
strcopy(dev->type, type, DEVICE_TYPE_MAX_LENGTH);
|
||||
dev->type[DEVICE_TYPE_MAX_LENGTH - 1] = '\0';
|
||||
|
||||
dev->data = data;
|
||||
dev->ops = ops;
|
||||
dev->flags = 0;
|
||||
return 0;
|
||||
dev->data = data;
|
||||
dev->ops = ops;
|
||||
dev->flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find device by path */
|
||||
Device* find_device_by_path(VM *vm, const char *path) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < vm->dc; i++) {
|
||||
if (strcmp(vm->devices[i].path, path) == 0) {
|
||||
return &vm->devices[i];
|
||||
}
|
||||
Device *find_device_by_path(VM *vm, const char *path) {
|
||||
u32 i;
|
||||
for (i = 0; i < vm->dc; i++) {
|
||||
if (streq(vm->devices[i].path, path)) {
|
||||
return &vm->devices[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find device by type (useful for checking capabilities) */
|
||||
Device* find_device_by_type(VM *vm, const char *type) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < vm->dc; i++) {
|
||||
if (strcmp(vm->devices[i].type, type) == 0) {
|
||||
return &vm->devices[i];
|
||||
}
|
||||
Device *find_device_by_type(VM *vm, const char *type) {
|
||||
u32 i;
|
||||
for (i = 0; i < vm->dc; i++) {
|
||||
if (streq(vm->devices[i].type, type)) {
|
||||
return &vm->devices[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find all devices of a type */
|
||||
int find_devices_by_type(VM *vm, const char *type, Device **results, uint32_t max_results) {
|
||||
uint32_t i, count = 0;
|
||||
for (i = 0; i < vm->dc && count < max_results; i++) {
|
||||
if (strcmp(vm->devices[i].type, type) == 0) {
|
||||
results[count++] = &vm->devices[i];
|
||||
}
|
||||
i32 find_devices_by_type(VM *vm, const char *type, Device **results,
|
||||
u32 max_results) {
|
||||
u32 i, count = 0;
|
||||
for (i = 0; i < vm->dc && count < max_results; i++) {
|
||||
if (streq(vm->devices[i].type, type)) {
|
||||
results[count++] = &vm->devices[i];
|
||||
}
|
||||
return count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
#include "opcodes.h"
|
||||
|
||||
int vm_register_device(VM *vm, const char *path, const char *type, void *data, DeviceOps *ops);
|
||||
i32 vm_register_device(VM *vm, const char *path, const char *type, void *data, DeviceOps *ops);
|
||||
Device* find_device_by_path(VM *vm, const char *path);
|
||||
Device* find_device_by_type(VM *vm, const char *type);
|
||||
int find_devices_by_type(VM *vm, const char *type, Device **results, uint32_t max_results);
|
||||
i32 find_devices_by_type(VM *vm, const char *type, Device **results, uint32_t max_results);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,2 @@
|
|||
gcc math_gen.c -o math_gen -lm
|
||||
./math_gen > ../math.h
|
|
@ -0,0 +1,31 @@
|
|||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("#ifndef ZRE_TRIG_TABLES_H\n#define ZRE_TRIG_TABLES_H\n#include <stdint.h>\n\n");
|
||||
|
||||
// Generate SINE table (256 entries, Q16.16 format)
|
||||
printf("const int32_t sin_table[256] = {\n");
|
||||
for (int i = 0; i < 256; i++) {
|
||||
double angle = i * 2 * M_PI / 256.0; // 0-360° in radians
|
||||
double value = sin(angle) * 65536.0; // Scale to Q16.16
|
||||
int32_t fixed = (int32_t)(value + 0.5); // Round to nearest
|
||||
printf(" %d,", fixed);
|
||||
if (i % 8 == 7) printf("\n");
|
||||
}
|
||||
printf("};\n\n");
|
||||
|
||||
// Generate COSINE table (256 entries, Q16.16 format)
|
||||
printf("const int32_t cos_table[256] = {\n");
|
||||
for (int i = 0; i < 256; i++) {
|
||||
double angle = i * 2 * M_PI / 256.0; // 0-360° in radians
|
||||
double value = cos(angle) * 65536.0; // Scale to Q16.16
|
||||
int32_t fixed = (int32_t)(value + 0.5); // Round to nearest
|
||||
printf(" %d,", fixed);
|
||||
if (i % 8 == 7) printf("\n");
|
||||
}
|
||||
printf("};\n");
|
||||
printf("#endif\n");
|
||||
return 0;
|
||||
}
|
136
src/opcodes.h
136
src/opcodes.h
|
@ -2,27 +2,18 @@
|
|||
#define ZRE_OPCODES_H
|
||||
|
||||
#include "common.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
OP_HALT, /* halt : terminate execution */
|
||||
OP_JMP, /* jump : jump to address dest unconditionally */
|
||||
OP_GET_PC, /* pc : dest = current program counter */
|
||||
OP_CALL, /* call : creates a new frame */
|
||||
OP_RETURN, /* retn : returns from a frame to the parent frame */
|
||||
OP_LOAD, /* load : dest = &[next memory location] */
|
||||
OP_STORE, /* stor : next memory location = src1 as float */
|
||||
OP_HALT, /* halt : terminate execution */
|
||||
OP_JMP, /* jump : jump to address dest unconditionally */
|
||||
OP_CALL, /* call : creates a new frame */
|
||||
OP_RETURN, /* retn : returns from a frame to the parent frame */
|
||||
OP_LOAD, /* load : dest = &[next memory location] */
|
||||
OP_LOAD_IMM, /* load : dest = &[next memory location] */
|
||||
OP_STORE, /* stor : next memory location = src1 as float */
|
||||
OP_PUSH, /* push : push str ref from register onto the stack and copy str */
|
||||
OP_POP, /* pop : pop int from stack onto the register */
|
||||
OP_REG_MOV, /* rmov : dest = src1 */
|
||||
OP_REG_SWAP, /* rswp : dest = src1, src1 = dest */
|
||||
OP_GET_ACC, /* gacc : dest = accumulator */
|
||||
OP_MEM_SWAP, /* mswp : &dest = &src1, &src1 = &dest */
|
||||
OP_MEM_MOV, /* mmov : &dest = &src1 */
|
||||
OP_MEM_ALLOC, /* aloc : dest [next memory location as size] */
|
||||
OP_GET, /* get : dest = ptr : dest = memory[ptr] */
|
||||
OP_PUT, /* put : ptr = src1 : memory[ptr] = src */
|
||||
OP_OFFSET, /* offs : dest = ptr + src1 : dest = p + o */
|
||||
OP_SYSCALL, /* sysc : */
|
||||
OP_ADD_INT, /* addi : dest = src1 + src2 */
|
||||
OP_SUB_INT, /* subi : dest = src1 - src2 */
|
||||
|
@ -58,36 +49,17 @@ typedef enum {
|
|||
OP_INT_TO_STRING, /* itos : dest = src1 as str */
|
||||
OP_UINT_TO_STRING, /* utos : dest = src1 as str */
|
||||
OP_REAL_TO_STRING, /* rtos : dest = src1 as str */
|
||||
OP_CMP_STRING, /* cmps : dest = (str == src2) as bool */
|
||||
OP_STRING_TO_INT, /* stoi : dest = src1 as int */
|
||||
OP_STRING_TO_UINT, /* stou : dest = src1 as uint */
|
||||
OP_STRING_TO_REAL, /* stor : dest = src1 as real */
|
||||
/* to remove (replace with device), just for testing for now */
|
||||
OP_DBG_PRINT_STRING, /* puts : write a string to stdout */
|
||||
OP_DBG_READ_STRING, /* gets : read a string from stdin */
|
||||
OP_STRING_TO_REAL /* stor : dest = src1 as real */
|
||||
} Opcode;
|
||||
|
||||
/* defines a uint32 opcode */
|
||||
#define OP(opcode, dest, src1, src2) \
|
||||
((opcode << 24) | (dest << 16) | (src1 << 8) | (src2))
|
||||
|
||||
typedef union value_u {
|
||||
int32_t i; /* Integers */
|
||||
float f; /* Float */
|
||||
uint32_t u; /* Unsigned integers, also used for pointer address */
|
||||
char c[4]; /* 4 Byte char array for string packing */
|
||||
} Value;
|
||||
|
||||
typedef struct slice_s {
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
} Slice;
|
||||
|
||||
#define MAX_REGS 32
|
||||
typedef struct frame_s {
|
||||
Value registers[MAX_REGS]; /* R0-R31 */
|
||||
uint32_t rp; /* register pointer (last unused) */
|
||||
Slice allocated; /* start and end of global allocated block */
|
||||
u32 registers[MAX_REGS]; /* R0-R31 */
|
||||
u32 rp; /* register pointer (last unused) */
|
||||
u32 start; /* start and end of global allocated block */
|
||||
u32 end;
|
||||
} Frame;
|
||||
|
||||
typedef enum {
|
||||
|
@ -96,16 +68,15 @@ typedef enum {
|
|||
SYSCALL_DEVICE_READ,
|
||||
SYSCALL_DEVICE_WRITE,
|
||||
SYSCALL_DEVICE_CLOSE,
|
||||
SYSCALL_DEVICE_IOCTL,
|
||||
SYSCALL_DEVICE_IOCTL
|
||||
} SyscallID;
|
||||
|
||||
typedef struct device_ops_s {
|
||||
int (*open)(void *device_data, uint32_t mode);
|
||||
int (*read)(void *device_data, uint8_t *buffer, uint32_t size);
|
||||
int (*write)(void *device_data, const uint8_t *buffer, uint32_t size);
|
||||
int (*close)(void *device_data);
|
||||
int (*ioctl)(void *device_data, uint32_t cmd,
|
||||
void *args); /* optional control */
|
||||
i32 (*open)(void *device_data, u32 mode);
|
||||
i32 (*read)(void *device_data, u8 *buffer, u32 size);
|
||||
i32 (*write)(void *device_data, const u8 *buffer, u32 size);
|
||||
i32 (*close)(void *device_data);
|
||||
i32 (*ioctl)(void *device_data, u32 cmd, void *args); /* optional control */
|
||||
} DeviceOps;
|
||||
|
||||
#define DEVICE_TYPE_MAX_LENGTH 24 /* 23 chars + null terminator */
|
||||
|
@ -117,7 +88,7 @@ typedef struct device_s {
|
|||
etc. */
|
||||
void *data; /* device-specific data */
|
||||
DeviceOps *ops; /* operations vtable */
|
||||
uint32_t flags; /* permissions, status, etc. */
|
||||
u32 flags; /* permissions, status, etc. */
|
||||
} Device;
|
||||
|
||||
#define MEMORY_SIZE (640 * 480 + 65536)
|
||||
|
@ -126,20 +97,61 @@ typedef struct device_s {
|
|||
#define STACK_SIZE 256
|
||||
#define DEVICES_SIZE 8
|
||||
typedef struct vm_s {
|
||||
uint32_t pc; /* program counter */
|
||||
uint32_t cp; /* code pointer (last allocated opcode) */
|
||||
uint32_t fp; /* frame pointer (current frame) */
|
||||
uint32_t sp; /* stack pointer (top of stack) */
|
||||
uint32_t rp; /* return stack pointer (top of stack) */
|
||||
uint32_t mp; /* memory pointer (last allocated value) */
|
||||
uint32_t dc; /* device count */
|
||||
uint32_t acc; /* accumulator (temporary results like SYSCALL status) */
|
||||
Device devices[DEVICES_SIZE]; /* device definitions */
|
||||
Frame frames[FRAMES_SIZE]; /* function call frames */
|
||||
Value stack[STACK_SIZE]; /* main stack */
|
||||
Value return_stack[STACK_SIZE]; /* return stack (for call recursion) */
|
||||
Value code[CODE_SIZE]; /* code block */
|
||||
Value memory[MEMORY_SIZE]; /* memory block */
|
||||
u32 pc; /* program counter */
|
||||
u32 cp; /* code pointer (last allocated opcode) */
|
||||
u32 fp; /* frame pointer (current frame) */
|
||||
u32 sp; /* stack pointer (top of stack) */
|
||||
u32 rp; /* return stack pointer (top of stack) */
|
||||
u32 mp; /* memory pointer (last allocated value) */
|
||||
u32 dc; /* device count */
|
||||
u32 acc; /* accumulator (temporary results like SYSCALL status) */
|
||||
Frame frames[FRAMES_SIZE]; /* function call frames */
|
||||
u32 stack[STACK_SIZE]; /* main stack */
|
||||
u32 return_stack[STACK_SIZE]; /* return stack (for call recursion) */
|
||||
Device devices[DEVICES_SIZE]; /* device definitions */
|
||||
u8 code[CODE_SIZE]; /* code block */
|
||||
u8 memory[MEMORY_SIZE]; /* memory block */
|
||||
} VM;
|
||||
|
||||
#define AS_INT(v) ((i32)(v))
|
||||
#define AS_UINT(v) ((u32)(v))
|
||||
#define AS_FIXED(v) ((float)(i32)(v) / 65536.0f)
|
||||
#define TO_FIXED(f) ((u32)((i32)( \
|
||||
((f) >= 0.0f) ? ((f) * 65536.0f + 0.5f) : ((f) * 65536.0f - 0.5f) \
|
||||
)))
|
||||
|
||||
#define read_u8(vm, location, addr) ((vm)->location[addr])
|
||||
#define read_u16(vm, location, addr) \
|
||||
(((uint16_t)(vm)->location[(addr)] << 8) | \
|
||||
((uint16_t)(vm)->location[(addr) + 1]))
|
||||
#define read_u32(vm, location, addr) \
|
||||
(((u32)(vm)->location[(addr)] << 24) | \
|
||||
((u32)(vm)->location[(addr) + 1] << 16) | \
|
||||
((u32)(vm)->location[(addr) + 2] << 8) | ((u32)(vm)->location[(addr) + 3]))
|
||||
|
||||
#define write_u8(vm, location, addr, value) \
|
||||
do { \
|
||||
if ((addr) < sizeof((vm)->location)) { \
|
||||
(vm)->location[(addr)] = (value); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define write_u16(vm, location, addr, value) \
|
||||
do { \
|
||||
if ((addr) + 1 < sizeof((vm)->location)) { \
|
||||
(vm)->location[(addr)] = ((value) >> 8) & 0xFF; \
|
||||
(vm)->location[(addr) + 1] = (value) & 0xFF; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define write_u32(vm, location, addr, value) \
|
||||
do { \
|
||||
if ((addr) + 3 < sizeof((vm)->location)) { \
|
||||
(vm)->location[(addr)] = ((value) >> 24) & 0xFF; \
|
||||
(vm)->location[(addr) + 1] = ((value) >> 16) & 0xFF; \
|
||||
(vm)->location[(addr) + 2] = ((value) >> 8) & 0xFF; \
|
||||
(vm)->location[(addr) + 3] = (value) & 0xFF; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
#include "str.h"
|
||||
|
||||
i32 strcopy(char *to, const char *from, u32 length) {
|
||||
u32 i;
|
||||
if (to == nil || from == nil) return -1;
|
||||
if (length == 0) {return 0;}
|
||||
for (i = 0; i < length - 1 && from[i] != '\0'; i++) {
|
||||
to[i] = from[i];
|
||||
}
|
||||
to[i] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool streq(const char *s1, const char *s2) {
|
||||
if (s1 == nil && s2 == nil) return true;
|
||||
if (s1 == nil || s2 == nil) return false;
|
||||
|
||||
while (*s1 && *s2) {
|
||||
if (*s1 != *s2) return false;
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
|
||||
return (*s1 == '\0' && *s2 == '\0');
|
||||
}
|
||||
|
||||
|
||||
u32 strlen(const char *str) {
|
||||
u32 i;
|
||||
if (str == nil) {return 0;}
|
||||
for (i = 0; str[i] != '\0'; i++) {
|
||||
; /* twiddle thumbs, 'i' is doing all the work*/
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
u32 strnlen(const char *str, u32 max_len) {
|
||||
u32 i;
|
||||
if (str == nil) {return 0;}
|
||||
for (i = 0; i < max_len && str[i] != '\0'; i++) {
|
||||
; /* twiddle thumbs, 'i' is doing all the work*/
|
||||
}
|
||||
return i;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef ZRE_STR_H
|
||||
#define ZRE_STR_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
i32 strcopy(char* to, const char *from, u32 length);
|
||||
u32 strlen(const char* str);
|
||||
bool streq(const char *s1, const char *s2);
|
||||
|
||||
#endif
|
255
src/test.c
255
src/test.c
|
@ -1,194 +1,135 @@
|
|||
#include "test.h"
|
||||
#include "opcodes.h"
|
||||
#include "str.h"
|
||||
#include "vm.h"
|
||||
#include <string.h>
|
||||
|
||||
uint32_t real_alloc(VM *vm, float v) {
|
||||
uint32_t addr = vm->mp;
|
||||
vm->memory[vm->mp++].f = v;
|
||||
vm->frames[vm->fp].allocated.end++;
|
||||
u32 real_alloc(VM *vm, float v) {
|
||||
i32 fixed = TO_FIXED(v);
|
||||
u32 addr = vm->mp;
|
||||
write_u32(vm, memory, vm->mp, fixed);
|
||||
vm->mp += 4;
|
||||
vm->frames[vm->fp].end += 4;
|
||||
return addr;
|
||||
}
|
||||
|
||||
uint32_t nat_alloc(VM *vm, uint32_t v) {
|
||||
uint32_t addr = vm->mp;
|
||||
vm->memory[vm->mp++].u = v;
|
||||
vm->frames[vm->fp].allocated.end++;
|
||||
u32 nat_alloc(VM *vm, u32 v) {
|
||||
u32 addr = vm->mp;
|
||||
write_u32(vm, memory, vm->mp, v);
|
||||
vm->mp += 4;
|
||||
vm->frames[vm->fp].end += 4;
|
||||
return addr;
|
||||
}
|
||||
|
||||
uint32_t int_alloc(VM *vm, int32_t v) {
|
||||
uint32_t addr = vm->mp;
|
||||
vm->memory[vm->mp++].i = v;
|
||||
vm->frames[vm->fp].allocated.end++;
|
||||
u32 int_alloc(VM *vm, i32 v) {
|
||||
u32 addr = vm->mp;
|
||||
write_u32(vm, memory, vm->mp, v);
|
||||
vm->mp += 4;
|
||||
vm->frames[vm->fp].end += 4;
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Array of test mappings */
|
||||
struct TestMapping internal_tests[] = {
|
||||
{"simple.ul", test_simple_compile},
|
||||
{"loop.ul", test_loop_compile},
|
||||
{"add.ul", test_add_function_compile},
|
||||
{"fib.ul", test_recursive_function_compile},
|
||||
{"window.ul", test_window_click_compile},
|
||||
/* Add more test mappings here */
|
||||
{NULL, NULL} /* Sentinel to mark end of array */
|
||||
{NULL, NULL} /* Sentinel to mark end of array */
|
||||
};
|
||||
|
||||
bool compile_internal_test(const char* filename, VM* vm) {
|
||||
int i;
|
||||
for (i = 0; internal_tests[i].filename != NULL; i++) {
|
||||
if (strcmp(internal_tests[i].filename, filename) == 0) {
|
||||
return internal_tests[i].test_func(vm);
|
||||
}
|
||||
bool compile_internal_test(const char *filename, VM *vm) {
|
||||
i32 i;
|
||||
for (i = 0; internal_tests[i].filename != NULL; i++) {
|
||||
if (streq(internal_tests[i].filename, filename)) {
|
||||
return internal_tests[i].test_func(vm);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool test_simple_compile(VM *vm) {
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0);
|
||||
vm->code[vm->cp++].u = nat_alloc(vm, 1);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 1, 0, 0);
|
||||
vm->code[vm->cp++].u = nat_alloc(vm, 2);
|
||||
vm->code[vm->cp++].u = OP(OP_ADD_UINT, 2, 1, 0); /* let sum = 1 + 2; */
|
||||
vm->code[vm->cp++].u = OP(OP_UINT_TO_STRING, 3, 2, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_DBG_PRINT_STRING, 0, 3, 0); /* print(sum.toS()); */
|
||||
vm->code[vm->cp++].u = OP(OP_HALT, 0, 0, 0); /* explicit halt */
|
||||
return true;
|
||||
}
|
||||
u32 ptr;
|
||||
u32 terminal_path_addr = str_alloc(vm, "/dev/term/0", 12);
|
||||
vm->code[vm->cp++] = OP_LOAD;
|
||||
vm->code[vm->cp++] = 0;
|
||||
ptr = real_alloc(vm, 1.0f);
|
||||
write_u32(vm, code, vm->cp, ptr); vm->cp+=4;
|
||||
|
||||
bool test_loop_compile(VM *vm) {
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0); /* let a = 5.0 */
|
||||
vm->code[vm->cp++].u = real_alloc(vm, 5.0f);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 1, 0, 0); /* do (i = 50000, 0, -1) { */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 10000);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 2, 0, 0); /* loop check value */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 3, 0, 0); /* loop incriment value */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, -1);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 5, 0, 0); /* a */
|
||||
vm->code[vm->cp++].u = real_alloc(vm, 5.0f);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 4, 0, 0); /* loop start */
|
||||
uint32_t addr = vm->cp + 1;
|
||||
vm->code[vm->cp++].u = int_alloc(vm, addr);
|
||||
vm->code[vm->cp++].u = OP(OP_ADD_REAL, 0, 0, 5); /* a += 5.0; */
|
||||
vm->code[vm->cp++].u = OP(OP_ADD_INT, 1, 1, 3); /* (implied by loop) i = i + (-1) */
|
||||
vm->code[vm->cp++].u = OP(OP_JGE_INT, 4, 1, 2); /* } */
|
||||
vm->code[vm->cp++].u = OP(OP_REAL_TO_UINT, 1, 0, 0); /* let b = a as nat; */
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 6, 0, 0);
|
||||
uint32_t str_addr = str_alloc(vm, "Enter a string:", 16);
|
||||
vm->code[vm->cp++].u = nat_alloc(vm, str_addr);
|
||||
vm->code[vm->cp++].u = OP(OP_DBG_PRINT_STRING, 0, 6, 0); /* print("Enter a string: "); */
|
||||
vm->code[vm->cp++].u = OP(OP_DBG_READ_STRING, 2, 0, 0); /* let user_string = gets(); */
|
||||
vm->code[vm->cp++].u = OP(OP_UINT_TO_STRING, 3, 1, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_DBG_PRINT_STRING, 0, 3, 0); /* print(a.toS()); */
|
||||
vm->code[vm->cp++].u = OP(OP_REAL_TO_STRING, 3, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_DBG_PRINT_STRING, 0, 3, 0); /* print(b.toS()); */
|
||||
vm->code[vm->cp++].u = OP(OP_DBG_PRINT_STRING, 0, 2, 0); /* print(user_string); */
|
||||
vm->code[vm->cp++].u = OP(OP_HALT, 0, 0, 0); /* program done */
|
||||
return true;
|
||||
}
|
||||
vm->code[vm->cp++] = OP_LOAD;
|
||||
vm->code[vm->cp++] = 1;
|
||||
ptr = real_alloc(vm, 2.0f);
|
||||
write_u32(vm, code, vm->cp, ptr); vm->cp+=4;
|
||||
|
||||
bool test_add_function_compile(VM *vm) {
|
||||
/* fn main() */
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0); /* 1 */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 1);
|
||||
vm->code[vm->cp++].u = OP(OP_PUSH, 0, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0); /* 1 */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 1);
|
||||
vm->code[vm->cp++].u = OP(OP_PUSH, 0, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_CALL, 0, 0, 0); /* ); */
|
||||
vm->code[vm->cp++].u = 12;
|
||||
vm->code[vm->cp++].u = OP(OP_POP, 0, 0, 0); /* get return value */
|
||||
vm->code[vm->cp++].u = OP(OP_INT_TO_STRING, 1, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_DBG_PRINT_STRING, 0, 1, 0); /* print(sum.toS()); */
|
||||
vm->code[vm->cp++].u = OP(OP_HALT, 0, 0, 0);
|
||||
/* fn add() */
|
||||
vm->code[vm->cp++].u = OP(OP_POP, 0, 0, 0); /* a int */
|
||||
vm->code[vm->cp++].u = OP(OP_POP, 1, 0, 0); /* b int */
|
||||
vm->code[vm->cp++].u = OP(OP_ADD_INT, 2, 1, 0); /* a + b */
|
||||
vm->code[vm->cp++].u = OP(OP_PUSH, 2, 0, 0); /* push on stack */
|
||||
vm->code[vm->cp++].u = OP(OP_RETURN, 0, 0, 0);
|
||||
return true;
|
||||
}
|
||||
vm->code[vm->cp++] = OP_ADD_UINT;
|
||||
vm->code[vm->cp++] = 2;
|
||||
vm->code[vm->cp++] = 1;
|
||||
vm->code[vm->cp++] = 0; /* let sum = 1 + 2; */
|
||||
|
||||
bool test_recursive_function_compile(VM *vm) {
|
||||
/* fn main() */
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0); /* 35 */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 35);
|
||||
vm->code[vm->cp++].u = OP(OP_PUSH, 0, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_CALL, 0, 0, 0); /* ); */
|
||||
vm->code[vm->cp++].u = 9;
|
||||
vm->code[vm->cp++].u = OP(OP_POP, 0, 0, 0); /* get return value */
|
||||
vm->code[vm->cp++].u = OP(OP_INT_TO_STRING, 1, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_DBG_PRINT_STRING, 0, 1, 0); /* print(fib(35).toS()); */
|
||||
vm->code[vm->cp++].u = OP(OP_HALT, 0, 0, 0);
|
||||
/* fn fib() */
|
||||
vm->code[vm->cp++].u = OP(OP_POP, 0, 0, 0); /* n int */
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 1, 0, 0); /* 2 */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 2);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 2, 0, 0); /* &fib */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 32);
|
||||
vm->code[vm->cp++].u = OP(OP_JLT_INT, 2, 0, 1);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 3, 0, 0); /* 2 */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 2);
|
||||
vm->code[vm->cp++].u = OP(OP_SUB_INT, 4, 0, 3);
|
||||
vm->code[vm->cp++].u = OP(OP_PUSH, 4, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_CALL, 0, 0, 0); /* fib(n - 2) */
|
||||
vm->code[vm->cp++].u = 9;
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 3, 0, 0); /* 1 */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 1);
|
||||
vm->code[vm->cp++].u = OP(OP_SUB_INT, 4, 0, 3);
|
||||
vm->code[vm->cp++].u = OP(OP_PUSH, 4, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_CALL, 0, 0, 0); /* fib(n - 1) */
|
||||
vm->code[vm->cp++].u = 9;
|
||||
vm->code[vm->cp++].u = OP(OP_POP, 4, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_POP, 5, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_ADD_INT, 6, 5, 4);
|
||||
vm->code[vm->cp++].u = OP(OP_PUSH, 6, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_RETURN, 0, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_PUSH, 0, 0, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_RETURN, 0, 0, 0);
|
||||
vm->code[vm->cp++] = OP_REAL_TO_STRING;
|
||||
vm->code[vm->cp++] = 4;
|
||||
vm->code[vm->cp++] = 2;
|
||||
|
||||
vm->code[vm->cp++] = OP_LOAD_IMM;
|
||||
vm->code[vm->cp++] = 3;
|
||||
write_u32(vm, code, vm->cp, terminal_path_addr); vm->cp+=4;
|
||||
|
||||
vm->code[vm->cp++] = OP_SYSCALL;
|
||||
write_u32(vm, code, vm->cp, AS_UINT(SYSCALL_DEVICE_WRITE)); vm->cp+=4;
|
||||
write_u32(vm, code, vm->cp, AS_UINT(2)); vm->cp+=4;
|
||||
vm->code[vm->cp++] = 3;
|
||||
/* syscall_id=WRITE, arg_count=2, start_reg=3 ; print(sum.toS()); */
|
||||
vm->code[vm->cp++] = OP_HALT; /* explicit halt */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_window_click_compile(VM *vm) {
|
||||
uint32_t screen_path_addr = str_alloc(vm, "/dev/screen/0", 14);
|
||||
/*uint32_t mouse_path_addr = str_alloc(vm, "/dev/mouse/0", 12);*/
|
||||
u32 test_pixel_addr, loop_start, screen_path_addr = str_alloc(vm, "/dev/screen/0", 14);
|
||||
|
||||
/* Open screen device: R0=path, R1=mode */
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0); /* R0 = screen path */
|
||||
vm->code[vm->cp++].u = nat_alloc(vm, screen_path_addr);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 1, 0, 0); /* R1 = mode (0) */
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 0);
|
||||
vm->code[vm->cp++].u = OP(OP_SYSCALL, SYSCALL_DEVICE_OPEN, 0, 2); /* syscall_id, first_reg=0, arg_count=2 */
|
||||
vm->code[vm->cp++] = OP_LOAD; /* R0 = screen path */
|
||||
vm->code[vm->cp++] = 0;
|
||||
vm->code[vm->cp++] = nat_alloc(vm, screen_path_addr);
|
||||
|
||||
/* Check if open succeeded (result in vm->acc) */
|
||||
vm->code[vm->cp++].u = OP(OP_GET_ACC, 2, 0, 0); /* Move result to R2 */
|
||||
vm->code[vm->cp++] = OP_LOAD; /* R1 = mode (0) */
|
||||
vm->code[vm->cp++] = 1;
|
||||
vm->code[vm->cp++] = int_alloc(vm, 0);
|
||||
|
||||
/* Create simple test pixel data */
|
||||
uint32_t test_pixel_addr = vm->mp;
|
||||
vm->memory[vm->mp].c[0] = (char)((255 / 32) << 5) | ((0 / 32) << 2) | (0 / 64);
|
||||
vm->memory[vm->mp].c[1] = (char)((255 / 32) << 5) | ((0 / 32) << 2) | (0 / 64);
|
||||
vm->memory[vm->mp].c[2] = (char)((255 / 32) << 5) | ((0 / 32) << 2) | (0 / 64);
|
||||
vm->memory[vm->mp++].c[3] = (char)((255 / 32) << 5) | ((0 / 32) << 2) | (0 / 64);
|
||||
vm->frames[vm->fp].allocated.end++;
|
||||
vm->code[vm->cp++] = OP_SYSCALL;
|
||||
vm->code[vm->cp++] = SYSCALL_DEVICE_OPEN;
|
||||
vm->code[vm->cp++] = 0; /* first_reg */
|
||||
vm->code[vm->cp++] = 2; /* arg_count */
|
||||
|
||||
/* Main loop to check for mouse input */
|
||||
uint32_t loop_start = vm->cp;
|
||||
test_pixel_addr = vm->mp;
|
||||
vm->memory[vm->mp++] = (char)((255 / 32) << 5) | ((0 / 32) << 2) | (0 / 64);
|
||||
vm->memory[vm->mp++] = (char)((255 / 32) << 5) | ((0 / 32) << 2) | (0 / 64);
|
||||
vm->memory[vm->mp++] = (char)((255 / 32) << 5) | ((0 / 32) << 2) | (0 / 64);
|
||||
vm->memory[vm->mp++] = (char)((255 / 32) << 5) | ((0 / 32) << 2) | (0 / 64);
|
||||
vm->frames[vm->fp].end += 4;
|
||||
|
||||
/* Write to screen: R0=path, R1=buffer, R2=size */
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0);
|
||||
vm->code[vm->cp++].u = nat_alloc(vm, screen_path_addr);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 1, 0, 0);
|
||||
vm->code[vm->cp++].u = nat_alloc(vm, test_pixel_addr);
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 2, 0, 0);
|
||||
vm->code[vm->cp++].u = int_alloc(vm, 1);
|
||||
vm->code[vm->cp++].u = OP(OP_SYSCALL, SYSCALL_DEVICE_WRITE, 0, 3); /* syscall_id, first_reg=0, arg_count=3 */
|
||||
loop_start = vm->cp;
|
||||
|
||||
vm->code[vm->cp++].u = OP(OP_LOAD, 3, 0, 0);
|
||||
vm->code[vm->cp++].u = nat_alloc(vm, loop_start);
|
||||
vm->code[vm->cp++].u = OP(OP_JMP, 3, 0, 0); /* Infinite loop for testing */
|
||||
vm->code[vm->cp++].u = OP(OP_HALT, 0, 0, 0);
|
||||
vm->code[vm->cp++] = OP_LOAD;
|
||||
vm->code[vm->cp++] = 0;
|
||||
vm->code[vm->cp++] = nat_alloc(vm, screen_path_addr);
|
||||
|
||||
return true;
|
||||
vm->code[vm->cp++] = OP_LOAD;
|
||||
vm->code[vm->cp++] = 1;
|
||||
vm->code[vm->cp++] = nat_alloc(vm, test_pixel_addr);
|
||||
|
||||
vm->code[vm->cp++] = OP_LOAD;
|
||||
vm->code[vm->cp++] = 2;
|
||||
vm->code[vm->cp++] = int_alloc(vm, 1);
|
||||
|
||||
vm->code[vm->cp++] = OP_SYSCALL;
|
||||
vm->code[vm->cp++] = SYSCALL_DEVICE_WRITE;
|
||||
vm->code[vm->cp++] = 0; /* first_reg */
|
||||
vm->code[vm->cp++] = 3; /* arg_count */
|
||||
|
||||
vm->code[vm->cp++] = OP_LOAD;
|
||||
vm->code[vm->cp++] = 3;
|
||||
vm->code[vm->cp++] = nat_alloc(vm, loop_start);
|
||||
|
||||
vm->code[vm->cp++] = OP_JMP;
|
||||
vm->code[vm->cp++] = 3;
|
||||
|
||||
vm->code[vm->cp++] = OP_HALT;
|
||||
|
||||
return true;
|
||||
}
|
688
src/vm.c
688
src/vm.c
|
@ -1,56 +1,50 @@
|
|||
#include "vm.h"
|
||||
#include "device.h"
|
||||
#include "opcodes.h"
|
||||
#include <string.h>
|
||||
#include "str.h"
|
||||
|
||||
/* no inline fn in ANSI C :( */
|
||||
#define COMPARE_AND_JUMP(type, accessor, op) \
|
||||
#define COMPARE_AND_JUMP(type, op) \
|
||||
do { \
|
||||
type value = vm->frames[vm->fp].registers[src1].accessor; \
|
||||
type value2 = vm->frames[vm->fp].registers[src2].accessor; \
|
||||
vm->pc = \
|
||||
(value op value2) ? vm->frames[vm->fp].registers[dest].u : vm->pc; \
|
||||
type value, value2; \
|
||||
dest = read_u8(vm, code, vm->pc); \
|
||||
vm->pc++; \
|
||||
src1 = read_u8(vm, code, vm->pc); \
|
||||
vm->pc++; \
|
||||
src2 = read_u8(vm, code, vm->pc); \
|
||||
vm->pc++; \
|
||||
value = vm->frames[vm->fp].registers[src1]; \
|
||||
value2 = vm->frames[vm->fp].registers[src2]; \
|
||||
vm->pc = (value op value2) ? vm->frames[vm->fp].registers[dest] : vm->pc; \
|
||||
return true; \
|
||||
} while (0)
|
||||
|
||||
#define MATH_OP(accessor, op) \
|
||||
#define MATH_OP(type, op) \
|
||||
do { \
|
||||
vm->frames[vm->fp].registers[dest].accessor = \
|
||||
vm->frames[vm->fp] \
|
||||
.registers[src1] \
|
||||
.accessor op vm->frames[vm->fp] \
|
||||
.registers[src2] \
|
||||
.accessor; \
|
||||
dest = read_u8(vm, code, vm->pc); \
|
||||
vm->pc++; \
|
||||
src1 = read_u8(vm, code, vm->pc); \
|
||||
vm->pc++; \
|
||||
src2 = read_u8(vm, code, vm->pc); \
|
||||
vm->pc++; \
|
||||
vm->frames[vm->fp].registers[dest] = (type)vm->frames[vm->fp] \
|
||||
.registers[src1] op(type) \
|
||||
vm->frames[vm->fp] \
|
||||
.registers[src2]; \
|
||||
return true; \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Embeds a string into the VM
|
||||
*/
|
||||
uint32_t str_alloc(VM *vm, const char *str, uint32_t length) {
|
||||
uint32_t str_addr = vm->mp++;
|
||||
uint32_t temp = 0;
|
||||
|
||||
while (temp < length) {
|
||||
uint32_t idx = temp % 4;
|
||||
vm->memory[vm->mp].c[idx] = str[temp];
|
||||
temp++;
|
||||
|
||||
if (idx == 3)
|
||||
vm->mp++;
|
||||
u32 str_alloc(VM *vm, const char *str, u32 length) {
|
||||
u32 str_addr = vm->mp;
|
||||
u32 i = 0;
|
||||
vm->mp += 4;
|
||||
while (i < length) {
|
||||
vm->memory[vm->mp++] = str[i++];
|
||||
}
|
||||
vm->memory[vm->mp++] = '\0';
|
||||
|
||||
uint32_t i, rem = temp % 4;
|
||||
if (rem != 0) {
|
||||
for (i = rem; i < 4; i++) {
|
||||
vm->memory[vm->mp].c[i] = '\0';
|
||||
}
|
||||
vm->mp++;
|
||||
}
|
||||
|
||||
vm->memory[str_addr].u = length;
|
||||
vm->frames[vm->fp].allocated.end = vm->mp;
|
||||
|
||||
write_u32(vm, memory, str_addr, length);
|
||||
vm->frames[vm->fp].end = vm->mp;
|
||||
return str_addr;
|
||||
}
|
||||
|
||||
|
@ -58,143 +52,112 @@ uint32_t str_alloc(VM *vm, const char *str, uint32_t length) {
|
|||
* Step to the next opcode in the vm.
|
||||
*/
|
||||
bool step_vm(VM *vm) {
|
||||
/* Get current instruction & Advance to next instruction */
|
||||
uint32_t instruction = vm->code[vm->pc++].u;
|
||||
u8 opcode, dest, src1, src2;
|
||||
u32 v, ptr;
|
||||
i32 value;
|
||||
|
||||
uint8_t opcode = (instruction >> 24) & 0xFF;
|
||||
uint8_t dest = (instruction >> 16) & 0xFF;
|
||||
uint8_t src1 = (instruction >> 8) & 0xFF;
|
||||
uint8_t src2 = instruction & 0xFF;
|
||||
/* Get current instruction & Advance to next instruction */
|
||||
opcode = vm->code[vm->pc++];
|
||||
|
||||
switch (opcode) {
|
||||
case OP_HALT: {
|
||||
return false;
|
||||
}
|
||||
case OP_CALL: {
|
||||
uint32_t jmp = vm->code[vm->pc++].u; /* location of function in code */
|
||||
vm->return_stack[vm->rp++].u = vm->pc; /* set return address */
|
||||
vm->fp++; /* increment to the next free frame */
|
||||
vm->frames[vm->fp].allocated.start =
|
||||
vm->mp; /* set start of new memory block */
|
||||
u32 jmp = read_u32(vm, code, vm->pc); /* location of function in code */
|
||||
vm->pc += 4;
|
||||
vm->return_stack[vm->rp++] = vm->pc; /* set return address */
|
||||
vm->fp++; /* increment to the next free frame */
|
||||
vm->frames[vm->fp].start = vm->mp; /* set start of new memory block */
|
||||
vm->pc = jmp;
|
||||
return true;
|
||||
}
|
||||
case OP_RETURN: {
|
||||
vm->frames[vm->fp].rp = 0; /* reset register ptr */
|
||||
vm->pc = vm->return_stack[--vm->rp].u; /* set pc to return address */
|
||||
vm->frames[vm->fp].rp = 0; /* reset register ptr */
|
||||
vm->pc = vm->return_stack[--vm->rp]; /* set pc to return address */
|
||||
vm->mp =
|
||||
vm->frames[vm->fp--].allocated.start; /* reset memory pointer to start
|
||||
vm->frames[vm->fp--].start; /* reset memory pointer to start
|
||||
of old slice, pop the frame */
|
||||
return true;
|
||||
}
|
||||
case OP_LOAD_IMM: {
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
v = read_u32(vm, code, vm->pc);
|
||||
vm->pc += 4;
|
||||
vm->frames[vm->fp].registers[dest] = v;
|
||||
return true;
|
||||
}
|
||||
case OP_LOAD: {
|
||||
uint32_t ptr = vm->code[vm->pc++].u;
|
||||
Value v = vm->memory[ptr];
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
ptr = read_u32(vm, code, vm->pc);
|
||||
vm->pc += 4;
|
||||
v = read_u32(vm, memory, ptr);
|
||||
vm->frames[vm->fp].registers[dest] = v;
|
||||
return true;
|
||||
}
|
||||
case OP_STORE: {
|
||||
uint32_t ptr = vm->code[vm->pc++].u;
|
||||
vm->memory[ptr] = vm->frames[vm->fp].registers[src1];
|
||||
return true;
|
||||
}
|
||||
case OP_PUT: {
|
||||
uint32_t addr = vm->frames[vm->fp].registers[dest].u;
|
||||
vm->memory[addr] = vm->frames[vm->fp].registers[src1];
|
||||
return true;
|
||||
}
|
||||
case OP_GET: {
|
||||
uint32_t addr = vm->frames[vm->fp].registers[src1].u;
|
||||
vm->frames[vm->fp].registers[dest] = vm->memory[addr];
|
||||
return true;
|
||||
}
|
||||
case OP_OFFSET: {
|
||||
uint32_t ptr = vm->frames[vm->fp].registers[src1].u;
|
||||
uint32_t offset = vm->frames[vm->fp].registers[src2].u;
|
||||
uint32_t result = ptr + offset;
|
||||
vm->frames[vm->fp].registers[dest].u = result;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
ptr = read_u32(vm, code, vm->pc);
|
||||
vm->pc += 4;
|
||||
v = vm->frames[vm->fp].registers[src1];
|
||||
write_u32(vm, memory, ptr, v);
|
||||
return true;
|
||||
}
|
||||
case OP_PUSH: {
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
vm->stack[++vm->sp] = vm->frames[vm->fp].registers[dest];
|
||||
return true;
|
||||
}
|
||||
case OP_POP: {
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
vm->frames[vm->fp].registers[dest] = vm->stack[vm->sp--];
|
||||
return true;
|
||||
}
|
||||
case OP_MEM_ALLOC: {
|
||||
uint32_t mem_dest = (uint32_t)vm->frames[vm->fp]
|
||||
.allocated.end; /* get start of unallocated */
|
||||
vm->frames[vm->fp].registers[dest].u =
|
||||
mem_dest; /* store ptr of array to dest register */
|
||||
uint32_t size = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
||||
vm->memory[mem_dest].u = size;
|
||||
vm->frames[vm->fp].allocated.end +=
|
||||
size; /* increment to end of allocated */
|
||||
return true;
|
||||
}
|
||||
case OP_MEM_MOV: {
|
||||
uint32_t dest_addr = (uint32_t)vm->frames[vm->fp].registers[dest].u;
|
||||
uint32_t src_addr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
||||
vm->memory[dest_addr] = vm->memory[src_addr];
|
||||
return true;
|
||||
}
|
||||
case OP_MEM_SWAP: {
|
||||
uint32_t dest_addr = (uint32_t)vm->frames[vm->fp].registers[dest].u;
|
||||
uint32_t src_addr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
||||
vm->memory[dest_addr].u ^= vm->memory[src_addr].u;
|
||||
vm->memory[src_addr].u ^= vm->memory[dest_addr].u;
|
||||
vm->memory[dest_addr].u ^= vm->memory[src_addr].u;
|
||||
return true;
|
||||
}
|
||||
case OP_REG_SWAP: {
|
||||
vm->frames[vm->fp].registers[dest].u ^=
|
||||
vm->frames[vm->fp].registers[src1].u;
|
||||
vm->frames[vm->fp].registers[src1].u ^=
|
||||
vm->frames[vm->fp].registers[dest].u;
|
||||
vm->frames[vm->fp].registers[dest].u ^=
|
||||
vm->frames[vm->fp].registers[src1].u;
|
||||
return true;
|
||||
}
|
||||
case OP_REG_MOV: {
|
||||
vm->frames[vm->fp].registers[dest].i = vm->frames[vm->fp].registers[src1].i;
|
||||
return true;
|
||||
}
|
||||
case OP_GET_ACC: {
|
||||
vm->frames[vm->fp].registers[dest].u = vm->acc;
|
||||
return true;
|
||||
}
|
||||
case OP_GET_PC: {
|
||||
vm->frames[vm->fp].registers[dest].u = vm->pc;
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
vm->frames[vm->fp].registers[dest] = vm->frames[vm->fp].registers[src1];
|
||||
return true;
|
||||
}
|
||||
case OP_JMP: {
|
||||
vm->pc = vm->frames[vm->fp].registers[dest].u; /* Jump to address */
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
vm->pc = vm->frames[vm->fp].registers[dest]; /* Jump to address */
|
||||
return true;
|
||||
}
|
||||
case OP_SYSCALL: {
|
||||
uint32_t syscall_id = dest;
|
||||
uint32_t arg_count = src2;
|
||||
uint32_t first_reg = src1;
|
||||
u32 syscall_id, arg_count;
|
||||
u8 first_reg;
|
||||
|
||||
syscall_id = read_u32(vm, code, vm->pc);
|
||||
vm->pc += 4;
|
||||
arg_count = read_u32(vm, code, vm->pc);
|
||||
vm->pc += 4;
|
||||
first_reg = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
|
||||
switch (syscall_id) {
|
||||
case SYSCALL_DEVICE_OPEN: {
|
||||
if (arg_count >= 2) {
|
||||
uint32_t path_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg].u; /* R0: path pointer */
|
||||
uint32_t mode =
|
||||
vm->frames[vm->fp].registers[first_reg + 1].u; /* R1: mode */
|
||||
/* uint32_t length = vm->memory[path_ptr].u; */
|
||||
uint32_t str_src = path_ptr + 1;
|
||||
const char *path = (const char *)&vm->memory[str_src];
|
||||
Device *dev;
|
||||
u32 path_ptr, mode, str_src;
|
||||
path_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg]; /* R0: path pointer */
|
||||
mode = vm->frames[vm->fp].registers[first_reg + 1]; /* R1: mode */
|
||||
str_src = path_ptr + 1;
|
||||
|
||||
Device *dev = find_device_by_path(vm, path);
|
||||
dev = find_device_by_path(vm, (const char *)&vm->memory[str_src]);
|
||||
|
||||
if (dev) {
|
||||
if (dev->ops->open) {
|
||||
int result = dev->ops->open(dev->data, mode);
|
||||
vm->acc = result;
|
||||
vm->acc = dev->ops->open(dev->data, mode);
|
||||
} else {
|
||||
vm->acc = 1; /* success, no open needed */
|
||||
}
|
||||
|
@ -209,23 +172,21 @@ bool step_vm(VM *vm) {
|
|||
|
||||
case SYSCALL_DEVICE_READ: {
|
||||
if (arg_count >= 3) {
|
||||
uint32_t path_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg].u; /* R0: path pointer */
|
||||
uint32_t buffer_ptr = vm->frames[vm->fp]
|
||||
.registers[first_reg + 1]
|
||||
.u; /* R1: buffer pointer */
|
||||
uint32_t size =
|
||||
vm->frames[vm->fp].registers[first_reg + 2].u; /* R2: size */
|
||||
/* uint32_t length = vm->memory[path_ptr].u; */
|
||||
uint32_t str_src = path_ptr + 1;
|
||||
Device *dev;
|
||||
u32 path_ptr, buffer_ptr, size, str_src;
|
||||
|
||||
const char *path = (const char *)&vm->memory[str_src];
|
||||
Device *dev = find_device_by_path(vm, path);
|
||||
path_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg]; /* R0: path pointer */
|
||||
buffer_ptr = vm->frames[vm->fp]
|
||||
.registers[first_reg + 1]; /* R1: buffer pointer */
|
||||
size = vm->frames[vm->fp].registers[first_reg + 2]; /* R2: size */
|
||||
str_src = path_ptr + 1;
|
||||
|
||||
dev = find_device_by_path(vm, (const char *)&vm->memory[str_src]);
|
||||
|
||||
if (dev && dev->ops->read) {
|
||||
int result = dev->ops->read(dev->data,
|
||||
(uint8_t *)&vm->memory[buffer_ptr], size);
|
||||
vm->acc = result;
|
||||
vm->acc =
|
||||
dev->ops->read(dev->data, (u8 *)&vm->memory[buffer_ptr], size);
|
||||
} else {
|
||||
vm->acc = 0;
|
||||
}
|
||||
|
@ -236,24 +197,20 @@ bool step_vm(VM *vm) {
|
|||
}
|
||||
|
||||
case SYSCALL_DEVICE_WRITE: {
|
||||
if (arg_count >= 3) {
|
||||
uint32_t path_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg].u; /* R0: path pointer */
|
||||
uint32_t buffer_ptr = vm->frames[vm->fp]
|
||||
.registers[first_reg + 1]
|
||||
.u; /* R1: buffer pointer */
|
||||
uint32_t size =
|
||||
vm->frames[vm->fp].registers[first_reg + 2].u; /* R2: size */
|
||||
/* uint32_t length = vm->memory[path_ptr].u; */
|
||||
uint32_t str_src = path_ptr + 1;
|
||||
|
||||
const char *path = (const char *)&vm->memory[str_src];
|
||||
Device *dev = find_device_by_path(vm, path);
|
||||
if (arg_count >= 2) {
|
||||
Device *dev;
|
||||
u32 path_ptr, buffer_ptr, size, str_src;
|
||||
path_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg]; /* R0: path pointer */
|
||||
buffer_ptr = vm->frames[vm->fp]
|
||||
.registers[first_reg + 1]; /* R1: buffer pointer */
|
||||
size = read_u32(vm, memory, buffer_ptr); /* R2: size */
|
||||
str_src = path_ptr += 4;
|
||||
|
||||
dev = find_device_by_path(vm, (const char *)&vm->memory[str_src]);
|
||||
if (dev && dev->ops->write) {
|
||||
int result = dev->ops->write(
|
||||
dev->data, (const uint8_t *)&vm->memory[buffer_ptr], size);
|
||||
vm->acc = result;
|
||||
vm->acc = dev->ops->write(dev->data,
|
||||
(const u8 *)&vm->memory[buffer_ptr], size);
|
||||
} else {
|
||||
vm->acc = 0;
|
||||
}
|
||||
|
@ -265,16 +222,16 @@ bool step_vm(VM *vm) {
|
|||
|
||||
case SYSCALL_DEVICE_CLOSE: {
|
||||
if (arg_count >= 1) {
|
||||
uint32_t path_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg].u; /* R0: path pointer */
|
||||
/* uint32_t length = vm->memory[path_ptr].u; */
|
||||
uint32_t str_src = path_ptr + 1;
|
||||
Device *dev;
|
||||
u32 path_ptr, str_src;
|
||||
path_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg]; /* R0: path pointer */
|
||||
str_src = path_ptr + 1;
|
||||
|
||||
const char *path = (const char *)&vm->memory[str_src];
|
||||
Device *dev = find_device_by_path(vm, path);
|
||||
dev = find_device_by_path(vm, (const char *)&vm->memory[str_src]);
|
||||
|
||||
if (dev && dev->ops->close) {
|
||||
int result = dev->ops->close(dev->data);
|
||||
i32 result = dev->ops->close(dev->data);
|
||||
vm->acc = result;
|
||||
} else {
|
||||
vm->acc = 0;
|
||||
|
@ -287,22 +244,20 @@ bool step_vm(VM *vm) {
|
|||
|
||||
case SYSCALL_DEVICE_IOCTL: {
|
||||
if (arg_count >= 3) {
|
||||
uint32_t path_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg].u; /* R0: device path */
|
||||
uint32_t cmd = vm->frames[vm->fp]
|
||||
.registers[first_reg + 1]
|
||||
.u; /* R1: ioctl command */
|
||||
uint32_t args_ptr = vm->frames[vm->fp]
|
||||
.registers[first_reg + 2]
|
||||
.u; /* R2: args pointer */
|
||||
/* uint32_t length = vm->memory[path_ptr].u; */
|
||||
uint32_t str_src = path_ptr + 1;
|
||||
Device *dev;
|
||||
u32 path_ptr, args_ptr, cmd, str_src;
|
||||
path_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg]; /* R0: device path */
|
||||
cmd =
|
||||
vm->frames[vm->fp].registers[first_reg + 1]; /* R1: ioctl command */
|
||||
args_ptr =
|
||||
vm->frames[vm->fp].registers[first_reg + 2]; /* R2: args pointer */
|
||||
str_src = path_ptr + 1;
|
||||
|
||||
const char *path = (const char *)&vm->memory[str_src];
|
||||
Device *dev = find_device_by_path(vm, path);
|
||||
dev = find_device_by_path(vm, (const char *)&vm->memory[str_src]);
|
||||
|
||||
if (dev && dev->ops && dev->ops->ioctl) {
|
||||
int result = dev->ops->ioctl(dev->data, cmd, &vm->memory[args_ptr]);
|
||||
i32 result = dev->ops->ioctl(dev->data, cmd, &vm->memory[args_ptr]);
|
||||
vm->acc = result;
|
||||
} else {
|
||||
vm->acc = 0; /* error or no ioctl support */
|
||||
|
@ -325,210 +280,309 @@ bool step_vm(VM *vm) {
|
|||
return true;
|
||||
}
|
||||
case OP_ADD_INT:
|
||||
MATH_OP(i, +);
|
||||
MATH_OP(i32, +);
|
||||
case OP_SUB_INT:
|
||||
MATH_OP(i, -);
|
||||
MATH_OP(i32, -);
|
||||
case OP_MUL_INT:
|
||||
MATH_OP(i, *);
|
||||
MATH_OP(i32, *);
|
||||
case OP_DIV_INT:
|
||||
MATH_OP(i, /);
|
||||
MATH_OP(i32, /);
|
||||
case OP_ADD_UINT:
|
||||
MATH_OP(u, +);
|
||||
MATH_OP(u32, +);
|
||||
case OP_SUB_UINT:
|
||||
MATH_OP(u, -);
|
||||
MATH_OP(u32, -);
|
||||
case OP_MUL_UINT:
|
||||
MATH_OP(u, *);
|
||||
MATH_OP(u32, *);
|
||||
case OP_DIV_UINT:
|
||||
MATH_OP(u, /);
|
||||
case OP_ADD_REAL:
|
||||
MATH_OP(f, +);
|
||||
case OP_SUB_REAL:
|
||||
MATH_OP(f, -);
|
||||
case OP_MUL_REAL:
|
||||
MATH_OP(f, *);
|
||||
case OP_DIV_REAL:
|
||||
MATH_OP(f, /);
|
||||
MATH_OP(u32, /);
|
||||
case OP_MUL_REAL: {
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src2 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
vm->frames[vm->fp].registers[dest] = (vm->frames[vm->fp].registers[src1] *
|
||||
vm->frames[vm->fp].registers[src2]) >>
|
||||
16;
|
||||
return true;
|
||||
}
|
||||
|
||||
case OP_DIV_REAL: {
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src2 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
vm->frames[vm->fp].registers[dest] =
|
||||
(vm->frames[vm->fp].registers[src1] << 16) /
|
||||
vm->frames[vm->fp].registers[src2];
|
||||
return true;
|
||||
}
|
||||
|
||||
case OP_ADD_REAL: {
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src2 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
vm->frames[vm->fp].registers[dest] =
|
||||
vm->frames[vm->fp].registers[src1] + vm->frames[vm->fp].registers[src2];
|
||||
return true;
|
||||
}
|
||||
|
||||
case OP_SUB_REAL: {
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src2 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
vm->frames[vm->fp].registers[dest] =
|
||||
vm->frames[vm->fp].registers[src1] - vm->frames[vm->fp].registers[src2];
|
||||
return true;
|
||||
}
|
||||
case OP_REAL_TO_INT: {
|
||||
vm->frames[vm->fp].registers[dest].i =
|
||||
(int32_t)(vm->frames[vm->fp].registers[src1].f);
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
value = vm->frames[vm->fp].registers[src1];
|
||||
|
||||
if (value >= 0) {
|
||||
vm->frames[vm->fp].registers[dest] = value >> 16;
|
||||
} else {
|
||||
vm->frames[vm->fp].registers[dest] = -((-value) >> 16);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
case OP_INT_TO_REAL: {
|
||||
vm->frames[vm->fp].registers[dest].f =
|
||||
(float)(vm->frames[vm->fp].registers[src1].i);
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
vm->frames[vm->fp].registers[dest] =
|
||||
(vm->frames[vm->fp].registers[src1] << 16);
|
||||
return true;
|
||||
}
|
||||
case OP_REAL_TO_UINT: {
|
||||
vm->frames[vm->fp].registers[dest].u =
|
||||
(uint32_t)(vm->frames[vm->fp].registers[src1].f);
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
value = vm->frames[vm->fp].registers[src1];
|
||||
if (value < 0) {
|
||||
vm->frames[vm->fp].registers[dest] = 0;
|
||||
} else {
|
||||
vm->frames[vm->fp].registers[dest] = AS_UINT(value >> 16);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case OP_UINT_TO_REAL: {
|
||||
vm->frames[vm->fp].registers[dest].f =
|
||||
(float)(vm->frames[vm->fp].registers[src1].u);
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
vm->frames[vm->fp].registers[dest] =
|
||||
AS_INT(vm->frames[vm->fp].registers[src1] << 16);
|
||||
return true;
|
||||
}
|
||||
case OP_JEQ_UINT: {
|
||||
COMPARE_AND_JUMP(uint32_t, u, ==);
|
||||
COMPARE_AND_JUMP(u32, ==);
|
||||
}
|
||||
case OP_JGT_UINT: {
|
||||
COMPARE_AND_JUMP(uint32_t, u, >);
|
||||
COMPARE_AND_JUMP(u32, >);
|
||||
}
|
||||
case OP_JLT_UINT: {
|
||||
COMPARE_AND_JUMP(uint32_t, u, <);
|
||||
COMPARE_AND_JUMP(u32, <);
|
||||
}
|
||||
case OP_JLE_UINT: {
|
||||
COMPARE_AND_JUMP(uint32_t, u, <=);
|
||||
COMPARE_AND_JUMP(u32, <=);
|
||||
}
|
||||
case OP_JGE_UINT: {
|
||||
COMPARE_AND_JUMP(uint32_t, u, >=);
|
||||
COMPARE_AND_JUMP(u32, >=);
|
||||
}
|
||||
case OP_JEQ_INT: {
|
||||
COMPARE_AND_JUMP(int32_t, i, ==);
|
||||
COMPARE_AND_JUMP(i32, ==);
|
||||
}
|
||||
case OP_JGT_INT: {
|
||||
COMPARE_AND_JUMP(int32_t, i, >);
|
||||
COMPARE_AND_JUMP(i32, >);
|
||||
}
|
||||
case OP_JLT_INT: {
|
||||
COMPARE_AND_JUMP(int32_t, i, <);
|
||||
COMPARE_AND_JUMP(i32, <);
|
||||
}
|
||||
case OP_JLE_INT: {
|
||||
COMPARE_AND_JUMP(int32_t, i, <=);
|
||||
COMPARE_AND_JUMP(i32, <=);
|
||||
}
|
||||
case OP_JGE_INT: {
|
||||
COMPARE_AND_JUMP(int32_t, i, >=);
|
||||
COMPARE_AND_JUMP(i32, >=);
|
||||
}
|
||||
case OP_JEQ_REAL: {
|
||||
COMPARE_AND_JUMP(int32_t, i, ==);
|
||||
COMPARE_AND_JUMP(i32, ==);
|
||||
}
|
||||
case OP_JGT_REAL: {
|
||||
COMPARE_AND_JUMP(float, u, >);
|
||||
COMPARE_AND_JUMP(i32, >);
|
||||
}
|
||||
case OP_JLT_REAL: {
|
||||
COMPARE_AND_JUMP(float, u, <);
|
||||
COMPARE_AND_JUMP(i32, <);
|
||||
}
|
||||
case OP_JGE_REAL: {
|
||||
COMPARE_AND_JUMP(float, u, >=);
|
||||
COMPARE_AND_JUMP(i32, >=);
|
||||
}
|
||||
case OP_JLE_REAL: {
|
||||
COMPARE_AND_JUMP(float, u, <=);
|
||||
COMPARE_AND_JUMP(i32, <=);
|
||||
}
|
||||
case OP_INT_TO_STRING: {
|
||||
int32_t a = (int32_t)vm->frames[vm->fp].registers[src1].i; /* get value */
|
||||
char buffer[32];
|
||||
int len = sprintf(buffer, "%d", a);
|
||||
uint32_t ptr = str_alloc(vm, buffer, len); /* copy buffer to dest */
|
||||
vm->frames[vm->fp].registers[dest].u = ptr;
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
int_to_string(AS_INT(vm->frames[vm->fp].registers[src1]), buffer);
|
||||
ptr = str_alloc(vm, buffer, strlen(buffer));
|
||||
vm->frames[vm->fp].registers[dest] = ptr;
|
||||
return true;
|
||||
}
|
||||
case OP_UINT_TO_STRING: {
|
||||
uint32_t a = (uint32_t)vm->frames[vm->fp].registers[src1].u; /* get value */
|
||||
char buffer[32];
|
||||
int len = sprintf(buffer, "%d", a);
|
||||
uint32_t ptr = str_alloc(vm, buffer, len); /* copy buffer to dest */
|
||||
vm->frames[vm->fp].registers[dest].u = ptr;
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
uint_to_string(vm->frames[vm->fp].registers[src1], buffer);
|
||||
ptr = str_alloc(vm, buffer, strlen(buffer));
|
||||
vm->frames[vm->fp].registers[dest] = ptr;
|
||||
return true;
|
||||
}
|
||||
case OP_REAL_TO_STRING: {
|
||||
float a = (float)vm->frames[vm->fp].registers[src1].f; /* get value */
|
||||
char buffer[32];
|
||||
int len = sprintf(buffer, "%f", a);
|
||||
uint32_t ptr = str_alloc(vm, buffer, len); /* copy buffer to dest */
|
||||
vm->frames[vm->fp].registers[dest].u = ptr;
|
||||
dest = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
src1 = read_u8(vm, code, vm->pc);
|
||||
vm->pc++;
|
||||
fixed_to_string(AS_INT(vm->frames[vm->fp].registers[src1]), buffer);
|
||||
ptr = str_alloc(vm, buffer, strlen(buffer)); /* copy buffer to dest */
|
||||
vm->frames[vm->fp].registers[dest] = ptr;
|
||||
return true;
|
||||
}
|
||||
case OP_STRING_TO_INT: {
|
||||
uint32_t src_addr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
||||
uint32_t dest_addr = (uint32_t)vm->frames[vm->fp].registers[dest].u;
|
||||
char *endptr;
|
||||
int32_t value = (int32_t)strtol((char*)&vm->memory[src_addr + 1], &endptr, 10);
|
||||
vm->memory[dest_addr].i = value;
|
||||
return true;
|
||||
/* not implemented yet */
|
||||
return false;
|
||||
}
|
||||
case OP_STRING_TO_UINT: {
|
||||
uint32_t src_addr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
||||
uint32_t dest_addr = (uint32_t)vm->frames[vm->fp].registers[dest].u;
|
||||
long value = atol((char*)&vm->memory[src_addr + 1]);
|
||||
vm->memory[dest_addr].u = value;
|
||||
return true;
|
||||
/* not implemented yet */
|
||||
return false;
|
||||
}
|
||||
case OP_STRING_TO_REAL: {
|
||||
uint32_t src_addr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
||||
uint32_t dest_addr = (uint32_t)vm->frames[vm->fp].registers[dest].u;
|
||||
float value = atof((char*)&vm->memory[src_addr + 1]);
|
||||
vm->memory[dest_addr].u = value;
|
||||
return true;
|
||||
}
|
||||
case OP_DBG_READ_STRING: {
|
||||
uint32_t str_addr = vm->mp++;
|
||||
uint32_t length = 0;
|
||||
int ch;
|
||||
|
||||
while ((ch = getchar()) != '\n' && ch != EOF) {
|
||||
uint32_t idx = length % 4;
|
||||
vm->memory[vm->mp].c[idx] = (char)ch;
|
||||
length++;
|
||||
|
||||
if (idx == 3)
|
||||
vm->mp++;
|
||||
}
|
||||
|
||||
uint32_t i, rem = length % 4;
|
||||
if (rem != 0) {
|
||||
for (i = rem; i < 4; i++) {
|
||||
vm->memory[vm->mp].c[i] = '\0';
|
||||
}
|
||||
vm->mp++;
|
||||
}
|
||||
|
||||
vm->memory[str_addr].u = length;
|
||||
vm->frames[vm->fp].allocated.end = vm->mp;
|
||||
vm->frames[vm->fp].registers[dest].u = str_addr;
|
||||
|
||||
return true;
|
||||
}
|
||||
case OP_DBG_PRINT_STRING: {
|
||||
uint32_t ptr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
||||
uint32_t length = vm->memory[ptr].u;
|
||||
uint32_t str_src = ptr + 1;
|
||||
uint32_t i;
|
||||
for (i = 0; i < length; i++) {
|
||||
uint8_t ch = vm->memory[str_src + (i / 4)].c[i % 4];
|
||||
if (ch == '\0')
|
||||
break;
|
||||
putchar(ch);
|
||||
}
|
||||
putchar('\n');
|
||||
return true;
|
||||
}
|
||||
case OP_CMP_STRING: {
|
||||
uint32_t addr1 = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
||||
uint32_t addr2 = (uint32_t)vm->frames[vm->fp].registers[src2].u;
|
||||
uint32_t length1 = vm->memory[addr1 - 1].u;
|
||||
uint32_t length2 = vm->memory[addr2 - 1].u;
|
||||
uint32_t equal =
|
||||
1; /* we dont have a native boolean type so we use uint32_t */
|
||||
|
||||
if (length1 != length2) {
|
||||
equal = 0;
|
||||
} else {
|
||||
uint32_t i = 0;
|
||||
while (i < length1) {
|
||||
uint32_t char1 = vm->memory[addr1 + i].u;
|
||||
uint32_t char2 = vm->memory[addr2 + i].u;
|
||||
if (char1 != char2) {
|
||||
equal = 0;
|
||||
break;
|
||||
}
|
||||
if ((char1 & 0xFF) == '\0' && (char2 & 0xFF) == '\0') {
|
||||
equal = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
vm->memory[dest].u = equal;
|
||||
return true;
|
||||
/* not implemented yet */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false; /* something bad happened */
|
||||
}
|
||||
|
||||
/* Static digit lookup table (0-9) */
|
||||
const char digits[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
|
||||
|
||||
/* Writes decimal digits of 'value' backwards into 'buf_end' (inclusive),
|
||||
stopping at 'buf_start'. Returns pointer to first written digit. */
|
||||
char *write_digits_backwards(u32 value, char *buf_end, char *buf_start) {
|
||||
char *p = buf_end;
|
||||
if (value == 0) {
|
||||
*--p = '0';
|
||||
} else {
|
||||
u32 num = value;
|
||||
while (num && p > buf_start) {
|
||||
*--p = digits[num % 10];
|
||||
num /= 10;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void uint_to_string(u32 value, char *buffer) {
|
||||
char temp[16];
|
||||
char *start;
|
||||
char *end = temp + sizeof(temp) - 1;
|
||||
*end = '\0';
|
||||
start = write_digits_backwards(value, end, temp);
|
||||
strcopy(buffer, start, end - start + 1); /* +1 for null terminator */
|
||||
}
|
||||
|
||||
void int_to_string(i32 value, char *buffer) {
|
||||
char temp[17]; /* Extra space for '-' */
|
||||
i32 negative = 0;
|
||||
u32 abs_value;
|
||||
char *end = temp + sizeof(temp) - 1;
|
||||
*end = '\0';
|
||||
|
||||
if (value == (-2147483647 - 1)) { /* INT32_MIN */
|
||||
strcopy(buffer, "-2147483648", 12);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == 0) {
|
||||
*--end = '0';
|
||||
} else {
|
||||
if (value < 0) {
|
||||
negative = 1;
|
||||
abs_value = (u32)(-value);
|
||||
} else {
|
||||
abs_value = (u32)value;
|
||||
}
|
||||
|
||||
end = write_digits_backwards(abs_value, end, temp);
|
||||
|
||||
if (negative) {
|
||||
*--end = '-';
|
||||
}
|
||||
}
|
||||
|
||||
strcopy(buffer, end, temp + sizeof(temp) - end);
|
||||
}
|
||||
|
||||
void fixed_to_string(i32 value, char *buffer) {
|
||||
char temp[32];
|
||||
i32 negative;
|
||||
u32 int_part;
|
||||
u32 frac_part, frac_digits;
|
||||
char *end = temp + sizeof(temp) - 1;
|
||||
*end = '\0';
|
||||
|
||||
negative = 0;
|
||||
if (value < 0) {
|
||||
negative = 1;
|
||||
value = -value;
|
||||
}
|
||||
|
||||
int_part = AS_UINT(value >> 16);
|
||||
frac_part = AS_UINT(value & 0xFFFF);
|
||||
|
||||
/* Convert fractional part to 5 decimal digits */
|
||||
frac_digits = (frac_part * 100000U) / 65536U;
|
||||
|
||||
/* Trim trailing zeros */
|
||||
while (frac_digits > 0 && frac_digits % 10 == 0) {
|
||||
frac_digits /= 10;
|
||||
}
|
||||
|
||||
if (frac_digits > 0) {
|
||||
end = write_digits_backwards(frac_digits, end, temp);
|
||||
*--end = '.';
|
||||
}
|
||||
|
||||
if (int_part == 0 && frac_digits == 0) {
|
||||
*--end = '0';
|
||||
} else {
|
||||
end = write_digits_backwards(int_part, end, temp);
|
||||
}
|
||||
|
||||
if (negative) {
|
||||
*--end = '-';
|
||||
}
|
||||
|
||||
strcopy(buffer, end, temp + sizeof(temp) - end);
|
||||
}
|
||||
|
|
5
src/vm.h
5
src/vm.h
|
@ -4,6 +4,9 @@
|
|||
#include "opcodes.h"
|
||||
|
||||
bool step_vm(VM *vm);
|
||||
uint32_t str_alloc(VM *vm, const char *str, uint32_t length);
|
||||
u32 str_alloc(VM *vm, const char *str, u32 length);
|
||||
void fixed_to_string(i32 value, char *buffer);
|
||||
void int_to_string(i32 value, char *buffer);
|
||||
void uint_to_string(u32 value, char *buffer);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
main:
|
||||
lodr $0 5.0
|
||||
lodi $1 50000
|
||||
lodi $1 5000
|
||||
lodi $2 0
|
||||
lodi $3 -1
|
||||
loop_body:
|
||||
|
|
Loading…
Reference in New Issue