begin breaking up into modules, add wasm
This commit is contained in:
parent
244baf55f1
commit
bb9e30a1c1
|
@ -102,4 +102,6 @@ Mkfile.old
|
||||||
dkms.conf
|
dkms.conf
|
||||||
|
|
||||||
zre
|
zre
|
||||||
|
zre.wasm
|
||||||
memory_dump.bin
|
memory_dump.bin
|
||||||
|
src/build/
|
78
src/Makefile
78
src/Makefile
|
@ -1,39 +1,69 @@
|
||||||
# Compiler and flags
|
# Compiler configurations
|
||||||
CC = gcc
|
# -----------------------
|
||||||
CFLAGS += -std=c89 -g -Wall -Wextra -Werror -Wno-unused-parameter
|
# Native build (gcc)
|
||||||
LDFLAGS +=
|
CC_NATIVE = gcc
|
||||||
LDLIBS +=
|
CFLAGS_NATIVE = -std=c89 -Wall -Wextra -Werror -Wno-unused-parameter
|
||||||
|
LDFLAGS_NATIVE =
|
||||||
|
LDLIBS_NATIVE =
|
||||||
|
|
||||||
|
# WASM build (emscripten)
|
||||||
|
CC_WASM = emcc
|
||||||
|
CFLAGS_WASM = -std=c89 -Wall -Wextra -Werror -Wno-unused-parameter #-s WASM=1
|
||||||
|
LDFLAGS_WASM = #-s WASM=1
|
||||||
|
LDLIBS_WASM =
|
||||||
|
|
||||||
# Source and build configuration
|
# Source and build configuration
|
||||||
|
# ----------------------------
|
||||||
SRC = $(wildcard *.c)
|
SRC = $(wildcard *.c)
|
||||||
OBJ = $(SRC:.c=.o)
|
EXEC_NATIVE = zre
|
||||||
EXEC = zre
|
EXEC_WASM = zre.wasm
|
||||||
|
|
||||||
|
# Build directories
|
||||||
|
OBJ_DIR_NATIVE = build/native/obj
|
||||||
|
OBJ_DIR_WASM = build/wasm/obj
|
||||||
|
|
||||||
|
# Create output paths
|
||||||
|
OBJ_NATIVE = $(addprefix $(OBJ_DIR_NATIVE)/,$(notdir $(SRC:.c=.o)))
|
||||||
|
OBJ_WASM = $(addprefix $(OBJ_DIR_WASM)/,$(notdir $(SRC:.c=.o)))
|
||||||
|
|
||||||
# Phony targets
|
# Phony targets
|
||||||
.PHONY: all clean test install
|
# -------------
|
||||||
|
.PHONY: all clean install wasm
|
||||||
|
|
||||||
# Default target
|
# Default target builds both versions
|
||||||
all: $(EXEC)
|
all: native wasm
|
||||||
|
|
||||||
# Main build rule
|
# Native build rules
|
||||||
$(EXEC): $(OBJ)
|
# ------------------
|
||||||
$(CC) $(LDFLAGS) $(OBJ) $(LDLIBS) -o $@
|
native: $(EXEC_NATIVE)
|
||||||
|
|
||||||
# Pattern rule for object files
|
$(EXEC_NATIVE): $(OBJ_NATIVE)
|
||||||
%.o: %.c
|
$(CC_NATIVE) $(LDFLAGS_NATIVE) $^ $(LDLIBS_NATIVE) -o $@
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
# Test target with dependency
|
# WASM build rules
|
||||||
test: $(EXEC)
|
# ----------------
|
||||||
@echo "Running tests..."
|
wasm: $(EXEC_WASM)
|
||||||
./$(EXEC)
|
|
||||||
@echo "Tests completed successfully."
|
$(EXEC_WASM): $(OBJ_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 $@
|
||||||
|
|
||||||
# Clean build artifacts
|
# Clean build artifacts
|
||||||
|
# ---------------------
|
||||||
clean:
|
clean:
|
||||||
$(RM) $(OBJ) $(EXEC)
|
$(RM) -r $(OBJ_DIR_NATIVE) $(OBJ_DIR_WASM) $(EXEC_NATIVE) $(EXEC_WASM)
|
||||||
|
|
||||||
# Install target (example)
|
# Install target (example)
|
||||||
install: $(EXEC)
|
# ------------------------
|
||||||
|
install: native
|
||||||
install -d $(DESTDIR)/usr/local/bin
|
install -d $(DESTDIR)/usr/local/bin
|
||||||
install $(EXEC) $(DESTDIR)/usr/local/bin/
|
install $(EXEC_NATIVE) $(DESTDIR)/usr/local/bin/
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef ZRE_COMMON_H
|
||||||
|
#define ZRE_COMMON_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include "vm.h"
|
||||||
|
|
||||||
|
/* #define MEMORY_SIZE 65536 /\* 64KB memory (adjustable) *\/ */
|
||||||
|
#define MEMORY_SIZE 1024
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Data memory[MEMORY_SIZE]; /* Memory array */
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
memory[i++].u = OP_ADD_F32;
|
||||||
|
memory[i++].u = 102;
|
||||||
|
memory[i++].u = 103;
|
||||||
|
memory[i++].u = 103;
|
||||||
|
memory[i++].u = OP_SUB;
|
||||||
|
memory[i++].u = 100;
|
||||||
|
memory[i++].u = 101;
|
||||||
|
memory[i++].u = 100;
|
||||||
|
memory[i++].u = OP_JGZ;
|
||||||
|
memory[i++].u = 100;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = OP_F32_TO_INT;
|
||||||
|
memory[i++].u = 103;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = 103;
|
||||||
|
memory[i++].u = OP_INT_TO_STRING;
|
||||||
|
memory[i++].u = 103;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = 104;
|
||||||
|
memory[i++].u = OP_PRINT_STRING;
|
||||||
|
memory[i++].u = 105;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = OP_READ_STRING;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = 109;
|
||||||
|
memory[i++].u = OP_PRINT_STRING;
|
||||||
|
memory[i++].u = 110;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = OP_HALT;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[i++].u = 0;
|
||||||
|
memory[100].u = 5;
|
||||||
|
memory[101].u = 1;
|
||||||
|
memory[102].f = 5.f;
|
||||||
|
memory[103].f = 5.f;
|
||||||
|
|
||||||
|
run_vm(memory, MEMORY_SIZE);
|
||||||
|
|
||||||
|
return core_dump(memory, MEMORY_SIZE);
|
||||||
|
}
|
120
src/vm.c
120
src/vm.c
|
@ -1,49 +1,17 @@
|
||||||
#include <stdbool.h>
|
#include "vm.h"
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
/* #define MEMORY_SIZE 65536 /\* 64KB memory (adjustable) *\/ */
|
int core_dump(Data *memory, uint32_t memory_size) {
|
||||||
#define MEMORY_SIZE 1024
|
|
||||||
typedef union {
|
|
||||||
float f;
|
|
||||||
uint32_t u;
|
|
||||||
} Data;
|
|
||||||
Data memory[MEMORY_SIZE]; /* Memory array */
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
OP_HALT, /* terminate execution */
|
|
||||||
OP_ADD, /* dest = src1 + src2 */
|
|
||||||
OP_SUB, /* dest = src1 - src2 */
|
|
||||||
OP_MUL, /* dest = src1 * src2 */
|
|
||||||
OP_DIV, /* dest = src1 / src2 */
|
|
||||||
OP_ADD_F32, /* dest = src1 + src2 */
|
|
||||||
OP_SUB_F32, /* dest = src1 - src2 */
|
|
||||||
OP_MUL_F32, /* dest = src1 * src2 */
|
|
||||||
OP_DIV_F32, /* dest = src1 / src2 */
|
|
||||||
OP_F32_TO_INT, /* dest = src1 as int */
|
|
||||||
OP_INT_TO_F32, /* dest = src1 as f32 */
|
|
||||||
OP_MOV, /* dest = src1 */
|
|
||||||
OP_JMP, /* jump to address src1 unconditionally */
|
|
||||||
OP_JGZ, /* jump to address dest if src1 > 0 */
|
|
||||||
OP_INT_TO_STRING, /* dest = src1 as str */
|
|
||||||
OP_F32_TO_STRING, /* dest = src2 as str */
|
|
||||||
OP_READ_STRING,
|
|
||||||
OP_PRINT_STRING,
|
|
||||||
} Opcode;
|
|
||||||
|
|
||||||
int core_dump() {
|
|
||||||
FILE *file = fopen("memory_dump.bin", "wb");
|
FILE *file = fopen("memory_dump.bin", "wb");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
perror("Failed to open file");
|
perror("Failed to open file");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
size_t written = fwrite(memory, 1, MEMORY_SIZE, file);
|
size_t written = fwrite(memory, 1, memory_size, file);
|
||||||
if (written != MEMORY_SIZE) {
|
if (written != memory_size) {
|
||||||
fprintf(stderr, "Incomplete write: %zu bytes written out of %u\n", written,
|
fprintf(stderr, "Incomplete write: %zu bytes written out of %u\n", written,
|
||||||
MEMORY_SIZE);
|
memory_size);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +29,7 @@ uint32_t set_char(uint32_t word, int index, uint8_t ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pack string into union-based memory */
|
/* Pack string into union-based memory */
|
||||||
void pack_string(const char *str, uint32_t length, uint32_t dest_addr) {
|
void pack_string(Data *memory, const char *str, uint32_t length, uint32_t dest_addr) {
|
||||||
memory[dest_addr].u = length;
|
memory[dest_addr].u = length;
|
||||||
uint32_t buffer_addr = dest_addr + 1;
|
uint32_t buffer_addr = dest_addr + 1;
|
||||||
int word_index = 0;
|
int word_index = 0;
|
||||||
|
@ -89,10 +57,10 @@ void pack_string(const char *str, uint32_t length, uint32_t dest_addr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_vm() {
|
void run_vm(Data *memory, uint32_t memory_size) {
|
||||||
uint32_t pc = 0; /* Program counter */
|
uint32_t pc = 0; /* Program counter */
|
||||||
|
|
||||||
while (pc < MEMORY_SIZE - 4) {
|
while (pc < memory_size - 4) {
|
||||||
|
|
||||||
Opcode opcode = memory[pc].u;
|
Opcode opcode = memory[pc].u;
|
||||||
uint32_t src1_addr = memory[pc + 1].u;
|
uint32_t src1_addr = memory[pc + 1].u;
|
||||||
|
@ -100,8 +68,8 @@ void run_vm() {
|
||||||
uint32_t dest_addr = memory[pc + 3].u;
|
uint32_t dest_addr = memory[pc + 3].u;
|
||||||
pc += 4; /* Advance to next instruction */
|
pc += 4; /* Advance to next instruction */
|
||||||
|
|
||||||
if (src1_addr >= MEMORY_SIZE || src2_addr >= MEMORY_SIZE ||
|
if (src1_addr >= memory_size || src2_addr >= memory_size ||
|
||||||
dest_addr >= MEMORY_SIZE) {
|
dest_addr >= memory_size) {
|
||||||
printf("Invalid memory address!\n");
|
printf("Invalid memory address!\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -173,14 +141,14 @@ void run_vm() {
|
||||||
int32_t a = (int32_t)memory[src1_addr].u;
|
int32_t a = (int32_t)memory[src1_addr].u;
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
sprintf(buffer, "%d", a);
|
sprintf(buffer, "%d", a);
|
||||||
pack_string(buffer, strlen(buffer), dest_addr);
|
pack_string(memory, buffer, strlen(buffer), dest_addr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OP_F32_TO_STRING: {
|
case OP_F32_TO_STRING: {
|
||||||
float a = memory[src1_addr].f;
|
float a = memory[src1_addr].f;
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
sprintf(buffer, "%f", a);
|
sprintf(buffer, "%f", a);
|
||||||
pack_string(buffer, strlen(buffer), dest_addr);
|
pack_string(memory, buffer, strlen(buffer), dest_addr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OP_PRINT_STRING: {
|
case OP_PRINT_STRING: {
|
||||||
|
@ -229,6 +197,26 @@ void run_vm() {
|
||||||
memory[dest_addr].u = length;
|
memory[dest_addr].u = length;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OP_CMP_STRING: {
|
||||||
|
uint32_t addr1 = src1_addr;
|
||||||
|
uint32_t addr2 = src2_addr;
|
||||||
|
uint32_t length1 = memory[src1_addr - 1].u;
|
||||||
|
int equal = 1;
|
||||||
|
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < length1;) {
|
||||||
|
uint32_t word1 = memory[addr1 + i].u;
|
||||||
|
uint32_t word2 = memory[addr2 + i].u;
|
||||||
|
if (word1 != word2) {
|
||||||
|
equal = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((word1 & 0xFF) == '\0' && (word2 & 0xFF) == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memory[dest_addr].u = equal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
printf("Unknown opcode: %d\n", opcode);
|
printf("Unknown opcode: %d\n", opcode);
|
||||||
return;
|
return;
|
||||||
|
@ -236,47 +224,3 @@ void run_vm() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
|
||||||
int i = 0;
|
|
||||||
memory[i++].u = OP_ADD_F32;
|
|
||||||
memory[i++].u = 102;
|
|
||||||
memory[i++].u = 103;
|
|
||||||
memory[i++].u = 103;
|
|
||||||
memory[i++].u = OP_SUB;
|
|
||||||
memory[i++].u = 100;
|
|
||||||
memory[i++].u = 101;
|
|
||||||
memory[i++].u = 100;
|
|
||||||
memory[i++].u = OP_JGZ;
|
|
||||||
memory[i++].u = 100;
|
|
||||||
memory[i++].u = 0;
|
|
||||||
memory[i++].u = 0;
|
|
||||||
memory[i++].u = OP_F32_TO_INT;
|
|
||||||
memory[i++].u = 103;
|
|
||||||
memory[i++].u = 0;
|
|
||||||
memory[i++].u = 103;
|
|
||||||
memory[i++].u = OP_INT_TO_STRING;
|
|
||||||
memory[i++].u = 103;
|
|
||||||
memory[i++].u = 0;
|
|
||||||
memory[i++].u = 104;
|
|
||||||
memory[i++].u = OP_PRINT_STRING;
|
|
||||||
memory[i++].u = 105;
|
|
||||||
memory[i++].u = 0;
|
|
||||||
memory[i++].u = 0;
|
|
||||||
memory[i++].u = OP_READ_STRING;
|
|
||||||
memory[i++].u = 0;
|
|
||||||
memory[i++].u = 0;
|
|
||||||
memory[i++].u = 109;
|
|
||||||
memory[i++].u = OP_PRINT_STRING;
|
|
||||||
memory[i++].u = 110;
|
|
||||||
memory[i++].u = 0;
|
|
||||||
memory[i++].u = 0;
|
|
||||||
memory[i++].u = OP_HALT;
|
|
||||||
memory[100].u = 5;
|
|
||||||
memory[101].u = 1;
|
|
||||||
memory[102].f = 5.f;
|
|
||||||
memory[103].f = 5.f;
|
|
||||||
|
|
||||||
run_vm();
|
|
||||||
|
|
||||||
return core_dump();
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef ZRE_VM_H
|
||||||
|
#define ZRE_VM_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
float f;
|
||||||
|
uint32_t u;
|
||||||
|
} Data;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OP_HALT, /* terminate execution */
|
||||||
|
OP_ADD, /* dest = src1 + src2 */
|
||||||
|
OP_SUB, /* dest = src1 - src2 */
|
||||||
|
OP_MUL, /* dest = src1 * src2 */
|
||||||
|
OP_DIV, /* dest = src1 / src2 */
|
||||||
|
OP_ADD_F32, /* dest = src1 + src2 */
|
||||||
|
OP_SUB_F32, /* dest = src1 - src2 */
|
||||||
|
OP_MUL_F32, /* dest = src1 * src2 */
|
||||||
|
OP_DIV_F32, /* dest = src1 / src2 */
|
||||||
|
OP_F32_TO_INT, /* dest = src1 as int */
|
||||||
|
OP_INT_TO_F32, /* dest = src1 as f32 */
|
||||||
|
OP_MOV, /* dest = src1 */
|
||||||
|
OP_JMP, /* jump to address src1 unconditionally */
|
||||||
|
OP_JGZ, /* jump to address dest if src1 > 0 */
|
||||||
|
OP_INT_TO_STRING, /* dest = src1 as str */
|
||||||
|
OP_F32_TO_STRING, /* dest = src2 as str */
|
||||||
|
OP_READ_STRING,
|
||||||
|
OP_PRINT_STRING,
|
||||||
|
OP_CMP_STRING,
|
||||||
|
} Opcode;
|
||||||
|
|
||||||
|
void run_vm(Data *memory, uint32_t memory_size);
|
||||||
|
int core_dump(Data *memory, uint32_t memory_size);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue