Started implementing VM.
h3rald h3rald@h3rald.com
Sat, 29 Nov 2025 17:15:26 +0100
M
Makefile
→
Makefile
@@ -1,8 +1,8 @@
CC = gcc CFLAGS = -Wall -Wextra -g LDFLAGS = -SOURCES = xyw.c xywasm.c -OBJS = xyw.o xywasm.o +SOURCES = xyw.c xywasm.c xywvm.c +OBJS = xyw.o xywasm.o xywvm.o .PHONY: clean@@ -11,6 +11,9 @@ $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o xyw
xyw.o: xyw.c xyw.h $(CC) $(CFLAGS) -c xyw.c -o xyw.o + +xywvm.o: xywvm.c xyw.h + $(CC) $(CFLAGS) -c xywvm.c -o xywvm.o xywasm.o: xywasm.c xyw.h $(CC) $(CFLAGS) -c xywasm.c -o xywasm.o
A
xyw
M
xyw.c
→
xyw.c
@@ -2,110 +2,7 @@ #include <stdio.h>
#include <string.h> #include "xyw.h" -#define MEMORY_SIZE 0xffff -#define STACK_SIZE 0xff -#define DEVICE_AREA_START 0x0000 -#define SYSTEM_STACK_START 0x0100 -#define USER_STACK_START 0x0200 -#define USER_MEMORY_START 0x0300 - -/* system device */ -#define SYSTEM_STATE 0x00 -#define SYSTEM_ERROR 0x01 -#define SYSTEM_SSP 0x02 -#define SYSTEM_USP 0x03 -#define SYSTEM_X 0x04 -#define SYSTEM_Y 0x05 -#define SYSTEM_XW 0x06 -#define SYSTEM_YW 0x08 -#define SYSTEM_PC 0x0A -#define SYSTEM_PAGE 0x0C -#define SYSTEM_ON_ERROR 0x0D - -/* console device */ -#define CONSOLE_BASE 0x10 -#define CONSOLE_INPUT 0x10 -#define CONSOLE_OUTPUT 0x11 -#define CONSOLE_ERROR 0x12 -#define CONSOLE_ON_KEYPRESS 0x13 - -/* clock device */ -#define CLOCK_YEAR 0x20 -#define CLOCK_MONTH 0x22 -#define CLOCK_WEEK 0x23 -#define CLOCK_MONTHDAY 0x24 -#define CLOCK_WEEKDAY 0x25 -#define CLOCK_HOUR 0x26 -#define CLOCK_MINUTE 0x27 -#define CLOCK_SECOND 0x28 -#define CLOCK_TIMER 0x29 -#define CLOCK_ON_TIMER_ELAPSED 0x2B - -/* file device */ -#define FILE_PATH 0x30 -#define FILE_OPERATION 0x32 -#define FILE_STATE 0x33 -#define FILE_TYPE 0x34 -#define FILE_READ 0x35 -#define FILE_WRITE 0x36 -#define FILE_APPEND 0x37 - -/* display device */ -#define DISPLAY_WIDTH 0x40 -#define DISPLAY_HEIGHT 0x41 -#define DISPLAY_FILL 0x42 -#define DISPLAY_X 0x43 -#define DISPLAY_Y 0x44 -#define DISPLAY_ON_RENDER 0x45 - -/* display device */ -#define BUZZER_STATE 0x50 -#define BUZZER_SAMPLES 0x51 -#define BUZZER_N_SAMPLES 0x53 -#define BUZZER_ON_STOP 0x55 - int xyw_debug = 0; - -xyw_byte xyw_memory[MEMORY_SIZE]; -xyw_byte system_stack[STACK_SIZE]; -xyw_byte user_stack[STACK_SIZE]; - -//// 1-byte pointers -xyw_byte *ssp = &xyw_memory[SYSTEM_SSP]; -xyw_byte *usp = &xyw_memory[SYSTEM_USP]; -xyw_byte *x = &xyw_memory[SYSTEM_X]; -xyw_byte *y = &xyw_memory[SYSTEM_Y]; - -//// Helpers to manage 2-byte values to memory (big-endian) - -static inline xyw_word mget(xyw_word addr) -{ - return ((xyw_word)xyw_memory[addr] << 8) | xyw_memory[addr + 1]; -} - -static inline void mset(xyw_word addr, xyw_word val) -{ - xyw_memory[addr] = (val >> 8) & 0xFF; - xyw_memory[addr + 1] = val & 0xFF; -} - -static inline void madd(xyw_word addr, int delta) -{ - xyw_word val = mget(addr); - mset(addr, (xyw_word)(val + delta)); -} - -//// Helpers for common registers - -static inline xyw_word pc(void) { return mget(SYSTEM_PC); } -static inline void pc_set(xyw_word val) { mset(SYSTEM_PC, val); } -static inline void pc_add(int delta) { madd(SYSTEM_PC, delta); } -static inline xyw_word xw(void) { return mget(SYSTEM_XW); } -static inline void xw_set(xyw_word val) { mset(SYSTEM_XW, val); } -static inline void xw_add(int delta) { madd(SYSTEM_XW, delta); } -static inline xyw_word yw(void) { return mget(SYSTEM_YW); } -static inline void yw_set(xyw_word val) { mset(SYSTEM_YW, val); } -static inline void yw_add(int delta) { madd(SYSTEM_YW, delta); } //// Main Function int main(int argc, char *argv[])
M
xyw.h
→
xyw.h
@@ -1,10 +1,15 @@
#ifndef XYW_H #define XYW_H +#define XYW_MODE_X 0x20 +#define XYW_MODE_Y 0x40 +#define XYW_MODE_W 0x80 + typedef unsigned char xyw_byte; typedef unsigned short xyw_word; extern int xyw_debug; int xyw_assemble(const char *input, const char *output); +int run(); #endif // XYW_H
M
xywasm.c
→
xywasm.c
@@ -7,10 +7,6 @@ #define STRING_MAX_LENGTH 256
#define MAX_SYMBOLS 0x500 #define MAX_REFS 0x1000 -#define MODE_X 0x20 -#define MODE_Y 0x40 -#define MODE_W 0x80 - #define error(msg) fprintf(stderr, "Error - %s [%s]\n", msg, ctx->file) #define token_error(msg) fprintf(stderr, "Error - %s: %s [%s - Ln %d, Col %d]\n", token, msg, ctx->file, ctx->line, ctx->column) #define symbol_error(symbol, msg) fprintf(stderr, "Error - %s: %s [%s - Ln %d, Col %d]\n", symbol, msg, ctx->file, ctx->line, ctx->column)@@ -68,7 +64,7 @@ TOKEN_END_INVALID
} xyw_token_terminator; // Instruction mnemonics -static char instructions[][4] = { +static char xyw_instructions[][4] = { "BRK", "PSH", "POP", "NOT", "LDR", "STR", "LDD", "STD", "ILD", "LDI", "DLD", "LDD",@@ -267,7 +263,7 @@
// Encode a push instruction static inline void push(xyw_word val) { - (val > 0xff) ? append(PSH | MODE_W) : append(PSH); + (val > 0xff) ? append(PSH | XYW_MODE_W) : append(PSH); append(val); }@@ -425,9 +421,9 @@ {
(void)ctx; xyw_byte opcode = 0; // Match token against instruction mnemonics - for (unsigned int i = 0; i < sizeof(instructions) / sizeof(instructions[0]); i++) + for (unsigned int i = 0; i < sizeof(xyw_instructions) / sizeof(xyw_instructions[0]); i++) { - if (strcmpn(token, instructions[i], 3) == 0) + if (strcmpn(token, xyw_instructions[i], 3) == 0) { opcode = (xyw_byte)i; // There could be 0 to 3 modes (x y z) after the mnemonic in any order@@ -439,17 +435,17 @@ char mode_char = token[j];
int mode_found = 0; if (mode_char == 'x' || mode_char == 'X') { - opcode |= MODE_X; + opcode |= XYW_MODE_X; mode_found = 1; } else if (mode_char == 'y' || mode_char == 'Y') { - opcode |= MODE_Y; + opcode |= XYW_MODE_Y; mode_found = 1; } else if (mode_char == 'w' || mode_char == 'W') { - opcode |= MODE_W; + opcode |= XYW_MODE_W; mode_found = 1; } if (!mode_found)
A
xywvm.c
@@ -0,0 +1,366 @@
+#include "xyw.h" + +#define MEMORY_SIZE 0xffff +#define STACK_SIZE 0xff +#define DEVICE_AREA_START 0x0000 +#define SYSTEM_STACK_START 0x0100 +#define USER_STACK_START 0x0200 +#define USER_MEMORY_START 0x0300 + +typedef enum +{ + XYW_ERROR_NONE = 0, + XYW_ERROR_INVALID_INSTRUCTION, + XYW_ERROR_STACK_UNDERFLOW, + XYW_ERROR_STACK_OVERFLOW, + XYW_ERROR_DEVICE_ERROR +} xyw_error; + +/* system device */ +#define SYSTEM_STATE 0x00 +#define SYSTEM_ERROR 0x01 +#define SYSTEM_SSP 0x02 +#define SYSTEM_USP 0x03 +#define SYSTEM_X 0x04 +#define SYSTEM_Y 0x05 +#define SYSTEM_XW 0x06 +#define SYSTEM_YW 0x08 +#define SYSTEM_PC 0x0A +#define SYSTEM_PAGE 0x0C +#define SYSTEM_ON_ERROR 0x0D + +/* console device */ +#define CONSOLE_BASE 0x10 +#define CONSOLE_INPUT 0x10 +#define CONSOLE_OUTPUT 0x11 +#define CONSOLE_ERROR 0x12 +#define CONSOLE_ON_KEYPRESS 0x13 + +/* clock device */ +#define CLOCK_YEAR 0x20 +#define CLOCK_MONTH 0x22 +#define CLOCK_WEEK 0x23 +#define CLOCK_MONTHDAY 0x24 +#define CLOCK_WEEKDAY 0x25 +#define CLOCK_HOUR 0x26 +#define CLOCK_MINUTE 0x27 +#define CLOCK_SECOND 0x28 +#define CLOCK_TIMER 0x29 +#define CLOCK_ON_TIMER_ELAPSED 0x2B + +/* file device */ +#define FILE_PATH 0x30 +#define FILE_OPERATION 0x32 +#define FILE_STATE 0x33 +#define FILE_TYPE 0x34 +#define FILE_READ 0x35 +#define FILE_WRITE 0x36 +#define FILE_APPEND 0x37 + +/* display device */ +#define DISPLAY_WIDTH 0x40 +#define DISPLAY_HEIGHT 0x41 +#define DISPLAY_FILL 0x42 +#define DISPLAY_X 0x43 +#define DISPLAY_Y 0x44 +#define DISPLAY_ON_RENDER 0x45 + +/* display device */ +#define BUZZER_STATE 0x50 +#define BUZZER_SAMPLES 0x51 +#define BUZZER_N_SAMPLES 0x53 +#define BUZZER_ON_STOP 0x55 + +/// Main memory and stacks +xyw_byte xyw_memory[MEMORY_SIZE]; +xyw_byte system_stack[STACK_SIZE]; +xyw_byte user_stack[STACK_SIZE]; + +//// 1-byte pointers +xyw_byte *ssp = &xyw_memory[SYSTEM_SSP]; +xyw_byte *usp = &xyw_memory[SYSTEM_USP]; +xyw_byte *x = &xyw_memory[SYSTEM_X]; +xyw_byte *y = &xyw_memory[SYSTEM_Y]; + +//// Helpers to manage 2-byte values to memory (big-endian) + +static inline xyw_word mget(xyw_word addr) +{ + return ((xyw_word)xyw_memory[addr] << 8) | xyw_memory[addr + 1]; +} + +static inline void mset(xyw_word addr, xyw_word val) +{ + xyw_memory[addr] = (val >> 8) & 0xFF; + xyw_memory[addr + 1] = val & 0xFF; +} + +static inline void madd(xyw_word addr, int delta) +{ + xyw_word val = mget(addr); + mset(addr, (xyw_word)(val + delta)); +} + +//// Helpers for common registers + +static inline xyw_word pc(void) { return mget(SYSTEM_PC); } +// static inline void pc_set(xyw_word val) { mset(SYSTEM_PC, val); } +// static inline void pc_add(int delta) { madd(SYSTEM_PC, delta); } +static inline xyw_word xw(void) { return mget(SYSTEM_XW); } +// static inline void xw_set(xyw_word val) { mset(SYSTEM_XW, val); } +// static inline void xw_add(int delta) { madd(SYSTEM_XW, delta); } +static inline xyw_word yw(void) { return mget(SYSTEM_YW); } +// static inline void yw_set(xyw_word val) { mset(SYSTEM_YW, val); } +// static inline void yw_add(int delta) { madd(SYSTEM_YW, delta); } + +//// Mode helpers + +static int is_x() { return xyw_memory[pc()] & XYW_MODE_X; } +static int is_y() { return xyw_memory[pc()] & XYW_MODE_Y; } +static int is_w() { return xyw_memory[pc()] & XYW_MODE_W; } + +//// System stack management + +/* +static xyw_word ss_popw() +{ + if (*ssp < 2) + { + xyw_memory[SYSTEM_ERROR] = XYW_ERROR_STACK_UNDERFLOW; + return -1; + } + xyw_word val = mget(SYSTEM_STACK_START + (*ssp) - 2); + mset(SYSTEM_STACK_START + (*ssp) - 2, 0); + (*ssp) -= 2; + return val; +} + +static void ss_pushw(xyw_word val) +{ + if (*ssp > STACK_SIZE - 2) + { + xyw_memory[SYSTEM_ERROR] = XYW_ERROR_STACK_OVERFLOW; + return; + } + mset(SYSTEM_STACK_START + *ssp, val); + (*ssp) += 2; +} +*/ + +//// Instructions + +static void PSH() +{ + if (usp > STACK_SIZE - 1) + { + xyw_memory[SYSTEM_ERROR] = XYW_ERROR_STACK_OVERFLOW; + return; + } + user_stack[(*usp)++] = xyw_memory[pc()]; +} + +static void PSHw() +{ + if (usp > STACK_SIZE - 2) + { + xyw_memory[SYSTEM_ERROR] = XYW_ERROR_STACK_OVERFLOW; + return; + } + mset(USER_STACK_START + *usp, mget(pc())); + (*usp) += 2; +} + +static xyw_byte POP(xyw_byte first) +{ + // First operand, get value from X if set + if (first && is_x()) + { + return *x; + } + // Othwerwise try Y + if (first && is_y()) + { + return *y; + } + // For the second operand, get value from Y if both X and Y modes are set + if (!first && is_x() && is_y()) + { + return *y; + } + // If no mode is set, pop from stack + if (usp < 1) + { + xyw_memory[SYSTEM_ERROR] = XYW_ERROR_STACK_UNDERFLOW; + return -1; + } + xyw_byte val = user_stack[*usp]; + user_stack[*usp] = 0; + (*usp)--; + return val; +} + +static xyw_word POPw(xyw_byte first) +{ + // First operand, get value from X if set + if (first && is_x()) + { + return xw(); + } + // Othwerwise try Y + if (first && is_y()) + { + return yw(); + } + // For the second operand, get value from Y if both X and Y modes are set + if (!first && is_x() && is_y()) + { + return yw(); + } + // If no mode is set, pop from stack + if (usp < 2) + { + xyw_memory[SYSTEM_ERROR] = XYW_ERROR_STACK_UNDERFLOW; + return -1; + } + xyw_word val = mget(USER_STACK_START + (*usp) - 1); + mset(USER_STACK_START + (*usp) - 1, 0); + (*usp) -= 2; + return val; +} + +static xyw_byte LDR(xyw_word addr) +{ + return xyw_memory[addr]; +} + +static xyw_word LDRw(xyw_word addr) +{ + return mget(addr); +} + +static xyw_byte LDP(xyw_byte addr) +{ + return xyw_memory[((xyw_word)xyw_memory[SYSTEM_PAGE] << 8) | addr]; +} + +static xyw_word LDPw(xyw_byte addr) +{ + return mget(((xyw_word)xyw_memory[SYSTEM_PAGE] << 8) | addr); +} + +static xyw_byte ILD(xyw_word addr) +{ + madd(addr, 1); + return xyw_memory[mget(addr)]; +} + +static xyw_word ILDw(xyw_word addr) +{ + madd(addr, 1); + return mget(mget(addr)); +} + +static xyw_byte LDI(xyw_word addr) +{ + xyw_byte r = xyw_memory[addr]; + madd(addr, 1); + return r; +} + +static xyw_word LDIw(xyw_word addr) +{ + xyw_word r = mget(addr); + madd(addr, 1); + return r; +} + +static xyw_byte DLD(xyw_word addr) +{ + madd(addr, -1); + return xyw_memory[mget(addr)]; +} + +static xyw_word DLDw(xyw_word addr) +{ + madd(addr, -1); + return mget(mget(addr)); +} + +static xyw_byte LDD(xyw_word addr) +{ + xyw_byte r = xyw_memory[addr]; + madd(addr, -1); + return r; +} + +static xyw_word LDDw(xyw_word addr) +{ + xyw_word r = mget(addr); + madd(addr, -1); + return r; +} + +//// Macro helpers + +// Infix operation +#define IOP(op) is_w() ? PSHw((POPw(1) op POPw(0))) : PSH((POP(1) op POP(0))) +// Binary operation +#define BOP(op) is_w() ? PSHw(op(POPw(1), POPw(0))) : PSH(op(POP(1), POP(0))) +// Unary operation +#define UOP(op) is_w() ? PSHw(op(POPw(1))) : PSH(op(POP(1))) + +int run() +{ + xyw_word current; + while (1) + { + current = pc(); + madd(SYSTEM_PC, 1); + switch (xyw_memory[current]) + { + // clang-format off + /* BRK */ case 0x00: break; + /* PSH */ case 0x01: is_w() ? PSHw() : PSH(); break; + /* POP */ case 0x02: is_w() ? POPw(1) : POP(1); break; + /* NOT */ case 0x03: UOP(!); break; + + /* LDR */ case 0x04: is_w() ? UOP(LDRw) : UOP(LDR); break; + /* STR */ case 0x05: break; // TODO + /* LDP */ case 0x06: is_w() ? UOP(LDPw) : UOP(LDP); break; + /* STP */ case 0x07: break; // TODO + + /* ILD */ case 0x08: is_w() ? UOP(ILD) : UOP(ILDw); break; + /* LDI */ case 0x09: is_w() ? UOP(LDI) : UOP(LDIw); break; + /* DLD */ case 0x0A: is_w() ? UOP(DLD) : UOP(DLDw); break; + /* LDD */ case 0x0B: is_w() ? UOP(LDD) : UOP(LDDw); break; + + /* ADD */ case 0x0C: IOP(+); break; + /* SUB */ case 0x0D: IOP(-); break; + /* MUL */ case 0x0E: IOP(*); break; + /* DIV */ case 0x0F: IOP(/); break; + + /* EQU */ case 0x10: IOP(==); break; + /* NEQ */ case 0x11: IOP(!=); break; + /* GTH */ case 0x12: IOP(>); break; + /* LTH */ case 0x13: IOP(<); break; + + /* AND */ case 0x14: IOP(&); break; + /* IOR */ case 0x15: IOP(|); break; + /* XOR */ case 0x16: IOP(^); break; + /* SFT */ case 0x17: break; // TODO from here + + /* SWP */ case 0x18: break; + /* DUP */ case 0x19: break; + /* OVR */ case 0x1A: break; + /* ROT */ case 0x1B: break; + + /* JMP */ case 0x1C: break; + /* JCN */ case 0x1D: break; + /* JSR */ case 0x1E: break; + /* RTS */ case 0x1F: break; + // clang-format on + } + } + + return 0; +}