add initial device implementation
This commit is contained in:
parent
744f2d526b
commit
ab5bc87908
|
@ -66,6 +66,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
uint32_t buffer_size = 640 * 480 * sizeof(uint32_t);
|
uint32_t buffer_size = 640 * 480 * sizeof(uint32_t);
|
||||||
|
|
||||||
|
/*
|
||||||
Device screen;
|
Device screen;
|
||||||
screen.type = SCREEN;
|
screen.type = SCREEN;
|
||||||
screen.s = (Screen){.width = (uint8_t)480,
|
screen.s = (Screen){.width = (uint8_t)480,
|
||||||
|
@ -86,7 +87,6 @@ int main(int argc, char **argv) {
|
||||||
keyboard.k = (Keyboard){.length = SDL_NUM_SCANCODES, .keys = state};
|
keyboard.k = (Keyboard){.length = SDL_NUM_SCANCODES, .keys = state};
|
||||||
vm.devices[vm.dp++] = keyboard;
|
vm.devices[vm.dp++] = keyboard;
|
||||||
|
|
||||||
/* Create window and renderer */
|
|
||||||
SDL_Window *window =
|
SDL_Window *window =
|
||||||
SDL_CreateWindow("Reality Engine VM", SDL_WINDOWPOS_CENTERED,
|
SDL_CreateWindow("Reality Engine VM", SDL_WINDOWPOS_CENTERED,
|
||||||
SDL_WINDOWPOS_CENTERED, screen.s.width, screen.s.height,
|
SDL_WINDOWPOS_CENTERED, screen.s.width, screen.s.height,
|
||||||
|
@ -95,13 +95,11 @@ int main(int argc, char **argv) {
|
||||||
SDL_Renderer *renderer = SDL_CreateRenderer(
|
SDL_Renderer *renderer = SDL_CreateRenderer(
|
||||||
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||||
|
|
||||||
/* Create texture for 640x480 buffer */
|
|
||||||
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
|
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
|
||||||
SDL_TEXTUREACCESS_STREAMING,
|
SDL_TEXTUREACCESS_STREAMING,
|
||||||
screen.s.width, screen.s.height);
|
screen.s.width, screen.s.height);
|
||||||
|
|
||||||
/* Enable nearest-neighbor scaling (preserves pixel art) */
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
|
||||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); /* "0" = nearest-neighbor */
|
|
||||||
|
|
||||||
bool running = true;
|
bool running = true;
|
||||||
while (running) {
|
while (running) {
|
||||||
|
@ -138,7 +136,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
/* (Scales to fit screen while preserving pixel grid) */
|
|
||||||
SDL_Rect output_rect;
|
SDL_Rect output_rect;
|
||||||
SDL_RenderGetViewport(renderer, &output_rect);
|
SDL_RenderGetViewport(renderer, &output_rect);
|
||||||
float scale_x = (float)output_rect.w / screen.s.width;
|
float scale_x = (float)output_rect.w / screen.s.width;
|
||||||
|
@ -153,5 +151,6 @@ int main(int argc, char **argv) {
|
||||||
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
|
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
|
||||||
SDL_RenderPresent(renderer);
|
SDL_RenderPresent(renderer);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int vm_register_device(VM *vm, const char *path, const char *type, void *data, DeviceOps *ops) {
|
||||||
|
if (vm->dc >= DEVICES_SIZE) return -1;
|
||||||
|
|
||||||
|
Device *dev = &vm->devices[vm->dc++];
|
||||||
|
|
||||||
|
strncpy(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';
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef ZRL_DEVICE_H
|
||||||
|
#define ZRL_DEVICE_H
|
||||||
|
|
||||||
|
#include "opcodes.h"
|
||||||
|
|
||||||
|
int 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);
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,6 +2,7 @@
|
||||||
#define ZRL_OPCODES_H
|
#define ZRL_OPCODES_H
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OP_HALT, /* halt : terminate execution */
|
OP_HALT, /* halt : terminate execution */
|
||||||
|
@ -12,11 +13,11 @@ typedef enum {
|
||||||
OP_LOAD, /* load : dest = &[next memory location] */
|
OP_LOAD, /* load : dest = &[next memory location] */
|
||||||
OP_STORE, /* stor : next memory location = src1 as float */
|
OP_STORE, /* stor : next memory location = src1 as float */
|
||||||
OP_PUSH, /* push : push str ref from register onto the stack and copy str */
|
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_POP, /* pop : pop int from stack onto the register */
|
||||||
OP_REG_MOV, /* rmov : dest = src1 */
|
OP_REG_MOV, /* rmov : dest = src1 */
|
||||||
OP_REG_SWAP, /* rswp : dest = src1, src1 = dest */
|
OP_REG_SWAP, /* rswp : dest = src1, src1 = dest */
|
||||||
OP_MEM_SWAP, /* mswp : &dest = &src1, &src1 = &dest */
|
OP_MEM_SWAP, /* mswp : &dest = &src1, &src1 = &dest */
|
||||||
OP_MEM_MOV, /* mmov : &dest = &src1 */
|
OP_MEM_MOV, /* mmov : &dest = &src1 */
|
||||||
OP_MEM_ALLOC, /* aloc : dest [next memory location as size] */
|
OP_MEM_ALLOC, /* aloc : dest [next memory location as size] */
|
||||||
OP_GET, /* get : dest = ptr : dest = memory[ptr] */
|
OP_GET, /* get : dest = ptr : dest = memory[ptr] */
|
||||||
OP_PUT, /* put : ptr = src1 : memory[ptr] = src */
|
OP_PUT, /* put : ptr = src1 : memory[ptr] = src */
|
||||||
|
@ -60,7 +61,7 @@ typedef enum {
|
||||||
OP_STRING_TO_INT, /* stoi : dest = src1 as int */
|
OP_STRING_TO_INT, /* stoi : dest = src1 as int */
|
||||||
OP_STRING_TO_UINT, /* stou : dest = src1 as uint */
|
OP_STRING_TO_UINT, /* stou : dest = src1 as uint */
|
||||||
OP_STRING_TO_REAL, /* stor : dest = src1 as real */
|
OP_STRING_TO_REAL, /* stor : dest = src1 as real */
|
||||||
/* to remove, just for testing for now */
|
/* to remove (replace with device), just for testing for now */
|
||||||
OP_DBG_PRINT_STRING,
|
OP_DBG_PRINT_STRING,
|
||||||
OP_DBG_READ_STRING,
|
OP_DBG_READ_STRING,
|
||||||
} Opcode;
|
} Opcode;
|
||||||
|
@ -88,42 +89,32 @@ typedef struct frame_s {
|
||||||
Slice allocated; /* start and end of global allocated block */
|
Slice allocated; /* start and end of global allocated block */
|
||||||
} Frame;
|
} Frame;
|
||||||
|
|
||||||
typedef struct screen_t {
|
|
||||||
uint8_t width;
|
|
||||||
uint8_t height;
|
|
||||||
Slice allocated;
|
|
||||||
Value *buffer;
|
|
||||||
} Screen;
|
|
||||||
|
|
||||||
typedef struct mouse_t {
|
|
||||||
uint32_t x;
|
|
||||||
uint32_t y;
|
|
||||||
uint8_t btn1;
|
|
||||||
uint8_t btn2;
|
|
||||||
uint8_t btn3;
|
|
||||||
} Mouse;
|
|
||||||
|
|
||||||
typedef struct keyboard_t {
|
|
||||||
uint32_t length;
|
|
||||||
const uint8_t *keys;
|
|
||||||
} Keyboard;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SCREEN,
|
SYSCALL_EXIT = 0,
|
||||||
MOUSE,
|
SYSCALL_DEVICE_OPEN,
|
||||||
KEYBOARD,
|
SYSCALL_DEVICE_READ,
|
||||||
CONTROLLER,
|
SYSCALL_DEVICE_WRITE,
|
||||||
AUDIO,
|
SYSCALL_DEVICE_CLOSE,
|
||||||
TUNNEL,
|
SYSCALL_DEVICE_IOCTL,
|
||||||
} Devicecode;
|
} SyscallID;
|
||||||
|
|
||||||
typedef union device_u {
|
typedef struct device_ops_s {
|
||||||
uint8_t type;
|
int (*open)(void *device_data, uint32_t mode);
|
||||||
Screen s;
|
int (*read)(void *device_data, uint8_t *buffer, uint32_t size);
|
||||||
Mouse m;
|
int (*write)(void *device_data, const uint8_t *buffer, uint32_t size);
|
||||||
Keyboard k;
|
int (*close)(void *device_data);
|
||||||
/* Audio a; */
|
int (*ioctl)(void *device_data, uint32_t cmd, void *args); /* optional control */
|
||||||
/* Tunnel t; */
|
} DeviceOps;
|
||||||
|
|
||||||
|
#define DEVICE_TYPE_MAX_LENGTH 24 /* 23 chars + null terminator */
|
||||||
|
#define DEVICE_PATH_MAX_LENGTH 64 /* 63 chars + null terminator */
|
||||||
|
|
||||||
|
typedef struct device_s {
|
||||||
|
char type[DEVICE_TYPE_MAX_LENGTH]; /* e.g., "screen", "mouse", "gpio" */
|
||||||
|
char path[DEVICE_PATH_MAX_LENGTH]; /* "/dev/screen", "/dev/input/mouse/0", etc. */
|
||||||
|
void *data; /* device-specific data */
|
||||||
|
DeviceOps *ops; /* operations vtable */
|
||||||
|
uint32_t flags; /* permissions, status, etc. */
|
||||||
} Device;
|
} Device;
|
||||||
|
|
||||||
#define MEMORY_SIZE 65536
|
#define MEMORY_SIZE 65536
|
||||||
|
@ -138,8 +129,8 @@ typedef struct vm_s {
|
||||||
uint32_t sp; /* stack pointer (top of stack) */
|
uint32_t sp; /* stack pointer (top of stack) */
|
||||||
uint32_t rp; /* return stack pointer (top of stack) */
|
uint32_t rp; /* return stack pointer (top of stack) */
|
||||||
uint32_t mp; /* memory pointer (last allocated value) */
|
uint32_t mp; /* memory pointer (last allocated value) */
|
||||||
uint32_t dp; /* device pointer (last allocated device) */
|
uint32_t dc; /* device count */
|
||||||
uint8_t devices_size;
|
uint32_t device_size;
|
||||||
Device devices[DEVICES_SIZE];
|
Device devices[DEVICES_SIZE];
|
||||||
uint32_t frames_size;
|
uint32_t frames_size;
|
||||||
Frame frames[FRAMES_SIZE]; /* function call frames */
|
Frame frames[FRAMES_SIZE]; /* function call frames */
|
||||||
|
|
125
src/vm.c
125
src/vm.c
|
@ -1,4 +1,5 @@
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
#include "device.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* no inline fn in ANSI C :( */
|
/* no inline fn in ANSI C :( */
|
||||||
|
@ -158,6 +159,130 @@ bool step_vm(VM *vm) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_SYSCALL: {
|
case OP_SYSCALL: {
|
||||||
|
uint32_t syscall_id = dest;
|
||||||
|
uint32_t arg_ptr = src1;
|
||||||
|
uint32_t arg_count = src2;
|
||||||
|
|
||||||
|
Frame *frame = &vm->frames[vm->fp];
|
||||||
|
Value *regs = frame->registers;
|
||||||
|
|
||||||
|
uint32_t memory_ptr = regs[arg_ptr].u;
|
||||||
|
|
||||||
|
switch (syscall_id) {
|
||||||
|
case SYSCALL_DEVICE_OPEN: {
|
||||||
|
if (arg_count >= 2) {
|
||||||
|
uint32_t path_offset = vm->memory[memory_ptr].u;
|
||||||
|
uint32_t mode = vm->memory[memory_ptr + 1].u;
|
||||||
|
|
||||||
|
const char *path = (const char *)&vm->memory[path_offset];
|
||||||
|
Device *dev = find_device_by_path(vm, path);
|
||||||
|
|
||||||
|
if (dev) {
|
||||||
|
if (dev->ops->open) {
|
||||||
|
int result = dev->ops->open(dev->data, mode);
|
||||||
|
vm->stack[++vm->sp].i = result;
|
||||||
|
} else {
|
||||||
|
vm->stack[++vm->sp].i = 1; /* success, no open needed */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vm->stack[++vm->sp].i = 0; /* error */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SYSCALL_DEVICE_READ: {
|
||||||
|
if (arg_count >= 3) {
|
||||||
|
uint32_t path_offset =
|
||||||
|
vm->memory[memory_ptr].u; /* offset 0: path pointer */
|
||||||
|
uint32_t buffer_ptr =
|
||||||
|
vm->memory[memory_ptr + 1].u; /* offset 1: buffer pointer */
|
||||||
|
uint32_t size = vm->memory[memory_ptr + 2].u; /* offset 2: size */
|
||||||
|
|
||||||
|
const char *path = (const char *)&vm->memory[path_offset];
|
||||||
|
Device *dev = find_device_by_path(vm, path);
|
||||||
|
|
||||||
|
if (dev && dev->ops->read) {
|
||||||
|
int result = dev->ops->read(dev->data,
|
||||||
|
(uint8_t *)&vm->memory[buffer_ptr], size);
|
||||||
|
vm->stack[++vm->sp].i = result;
|
||||||
|
} else {
|
||||||
|
vm->stack[++vm->sp].i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SYSCALL_DEVICE_WRITE: {
|
||||||
|
if (arg_count >= 3) {
|
||||||
|
uint32_t path_offset =
|
||||||
|
vm->memory[memory_ptr].u; /* offset 0: path pointer */
|
||||||
|
uint32_t buffer_ptr =
|
||||||
|
vm->memory[memory_ptr + 1].u; /* offset 1: buffer pointer */
|
||||||
|
uint32_t size = vm->memory[memory_ptr + 2].u; /* offset 2: size */
|
||||||
|
|
||||||
|
const char *path = (const char *)&vm->memory[path_offset];
|
||||||
|
Device *dev = find_device_by_path(vm, path);
|
||||||
|
|
||||||
|
if (dev && dev->ops->write) {
|
||||||
|
int result = dev->ops->write(
|
||||||
|
dev->data, (const uint8_t *)&vm->memory[buffer_ptr], size);
|
||||||
|
vm->stack[++vm->sp].i = result;
|
||||||
|
} else {
|
||||||
|
vm->stack[++vm->sp].i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SYSCALL_DEVICE_CLOSE: {
|
||||||
|
if (arg_count >= 1) {
|
||||||
|
uint32_t path_offset =
|
||||||
|
vm->memory[memory_ptr].u; /* offset 0: path pointer */
|
||||||
|
|
||||||
|
const char *path = (const char *)&vm->memory[path_offset];
|
||||||
|
Device *dev = find_device_by_path(vm, path);
|
||||||
|
|
||||||
|
if (dev && dev->ops->close) {
|
||||||
|
int result = dev->ops->close(dev->data);
|
||||||
|
vm->stack[++vm->sp].i = result;
|
||||||
|
} else {
|
||||||
|
vm->stack[++vm->sp].i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SYSCALL_DEVICE_IOCTL: {
|
||||||
|
if (arg_count >= 3) {
|
||||||
|
uint32_t path_offset = vm->memory[memory_ptr].u; /* device path */
|
||||||
|
uint32_t cmd = vm->memory[memory_ptr + 1].u; /* ioctl command */
|
||||||
|
uint32_t args_ptr = vm->memory[memory_ptr + 2].u; /* args pointer */
|
||||||
|
|
||||||
|
const char *path = (const char *)&vm->memory[path_offset];
|
||||||
|
Device *dev = find_device_by_path(vm, path);
|
||||||
|
|
||||||
|
if (dev && dev->ops && dev->ops->ioctl) {
|
||||||
|
int result = dev->ops->ioctl(dev->data, cmd, &vm->memory[args_ptr]);
|
||||||
|
vm->stack[++vm->sp].i = result;
|
||||||
|
} else {
|
||||||
|
vm->stack[++vm->sp].i = 0; /* error or no ioctl support */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SYSCALL_EXIT: {
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
regs[0].i = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
case OP_ADD_INT:
|
case OP_ADD_INT:
|
||||||
MATH_OP(i, +);
|
MATH_OP(i, +);
|
||||||
|
|
Loading…
Reference in New Issue