Compare commits
5 Commits
feature/Q1
...
main
Author | SHA1 | Date |
---|---|---|
|
94a1375d51 | |
|
7634bbe1c9 | |
|
d5479d1b7e | |
|
75ad08dae1 | |
|
770d9d56ff |
|
@ -1 +1,2 @@
|
||||||
*.zrl linguist-language=fortran
|
*.ul linguist-language=fortran
|
||||||
|
*.zl linguist-language=zig
|
132
README.org
132
README.org
|
@ -1,5 +1,4 @@
|
||||||
#+TITLE: A Language for Enduring Realities
|
#+TITLE: The Reality Engine
|
||||||
#+SUBTITLE: "Shape realities that outlast their makers."
|
|
||||||
#+AUTHOR: Zongor
|
#+AUTHOR: Zongor
|
||||||
#+EMAIL: archive@undar-lang.org
|
#+EMAIL: archive@undar-lang.org
|
||||||
#+DATE: [2025-04-05]
|
#+DATE: [2025-04-05]
|
||||||
|
@ -16,28 +15,26 @@
|
||||||
· · · ᚾ]
|
· · · ᚾ]
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
* The Reality Engine
|
The =Reality Engine= is a register-based virtual machine designed to render not just graphics, but persistent, inspectable, reproducible computational worlds.
|
||||||
|
|
||||||
The =Reality Engine= is a register-based virtual machine designed to render not just graphics, but *realities* - persistent, inspectable, reproducible computational worlds.
|
|
||||||
|
|
||||||
It is:
|
It is:
|
||||||
- Written in **C89** for maximum portability
|
- Written in **C89** for maximum portability
|
||||||
- **No dynamic allocation** - memory is static, frame-managed, zero-initialized
|
- **No dynamic allocation** - memory is static, frame-managed, zero-initialized
|
||||||
- **Deterministic by design** - identical input -> identical output, forever
|
- **Deterministic by design** - identical input -> identical output
|
||||||
- **Self-inspectable** - symbol table, memory, and state are always accessible
|
- **Self-inspectable** - symbol table, memory, and state are always accessible
|
||||||
- Inspired by Uxn, Dis VM, Dusk OS, and Plan 9
|
- Inspired by Uxn, Dis VM, Dusk OS, and Plan 9
|
||||||
|
|
||||||
**VM Architecture**
|
**VM Architecture**
|
||||||
|
|
||||||
| Feature | Specification |
|
| Feature | Specification |
|
||||||
|-----------------------|----------------------------------------------------|
|
|--------------------+---------------------------------------------|
|
||||||
| Instruction Format | 1-byte opcode, 3-byte operand (CISC-like) |
|
| Instruction Format | 1-byte opcode, 3-byte operand (CISC-like) |
|
||||||
| Register Set | 32 general-purpose registers (R0-R31) |
|
| Register Set | 32 general-purpose registers (R0-R31) |
|
||||||
| Initialization | **ZII**: Zero Is Initialization |
|
| Initialization | **ZII**: Zero Is Initialization |
|
||||||
| Memory Model | Frame-based arenas (function scope = frame) |
|
| Memory Model | Frame-based arenas (function scope = frame) |
|
||||||
| Heap Behavior | Copy-on-write; allocations append to frame |
|
| Heap Behavior | Copy-on-write; allocations append to frame |
|
||||||
| Frame Exit | Pointer resets on return (stack-GC style) |
|
| Frame Exit | Pointer resets on return (stack-GC style) |
|
||||||
| Error Handling | Returns stub pointers to zeroed memory |
|
| Error Handling | Returns stub pointers to zeroed memory |
|
||||||
|
|
||||||
This ensures:
|
This ensures:
|
||||||
- No =malloc=, no =free=, no GC
|
- No =malloc=, no =free=, no GC
|
||||||
|
@ -45,28 +42,30 @@ This ensures:
|
||||||
- Perfect reproducibility
|
- Perfect reproducibility
|
||||||
- Safe failure modes
|
- Safe failure modes
|
||||||
|
|
||||||
Undar is a permacomputing oriented, statically-typed language with **first-class arrays**, **immediate-mode semantics**, and **symbolic clarity**
|
* Undâr
|
||||||
|
|
||||||
|
Undâr is a permacomputing oriented, statically-typed language with **first-class arrays**, **immediate-mode semantics**, and **symbolic clarity**
|
||||||
- =Constrained systems=: microcontrollers, retro consoles (PS1, N64, Mac Classic)
|
- =Constrained systems=: microcontrollers, retro consoles (PS1, N64, Mac Classic)
|
||||||
- =Portable environments=: Web (Emscripten), embedded, CLI
|
- =Portable environments=: Web (Emscripten), embedded, CLI Tui
|
||||||
- =Permacomputing=: long-term survivability, sustainability, minimalism
|
- =Permacomputing=: long-term survivability, sustainability, minimalism
|
||||||
- =3D world-building=: built-in primitives for PS1/N64-style rendering
|
- =3D world-building=: built-in primitives for PS1/N64-style rendering
|
||||||
- =Live development=: hot reloading, REPL, shadowing, symbol versioning
|
- =Live development=: hot reloading, REPL, shadowing, symbol versioning
|
||||||
|
|
||||||
It runs on the =Reality Engine=, a minimal C89 VM inspired by Uxn, Plan 9, and Forth - but built for =spatial software=, =deterministic execution=, and =software that lasts=.
|
It runs on the =Reality Engine=, a minimal C89 VM inspired by Uxn, Plan 9, and Forth - but built for =spatial software=, =deterministic execution=, and =software that lasts=.
|
||||||
|
|
||||||
Sċieppan is a bytecode assembler that is inspired by Webassemblys WAT format.
|
Sċieppan is a minimal lisp inpsired by sectorlisp.
|
||||||
You can view some examples in the =.lisp= files in =/test=
|
You can view some examples in the =.lisp= files in =/test=
|
||||||
|
|
||||||
**Core Types**
|
**Core Types**
|
||||||
|
|
||||||
| Type | Description |
|
| Type | Description |
|
||||||
|--------|-----------------------------------------------|
|
|--------+-------------------------------------------|
|
||||||
| =int= | 32-bit signed integer |
|
| =int= | 32-bit signed integer |
|
||||||
| =nat= | 32-bit natural number (also used for pointers)|
|
| =nat= | 32-bit natural number |
|
||||||
| =real= | Q16.16 fixed-point real number |
|
| =real= | Float/Q16.16 fixed-point real number |
|
||||||
| =str= | 4-byte packed string or fat pointer |
|
| =str= | 4-byte packed string or fat pointer |
|
||||||
| =bool= | Compile-time flag |
|
| =bool= | Compile-time flag |
|
||||||
| =ref= | Reference type for passing by reference |
|
| =ref= | Reference prefix for passing by reference |
|
||||||
|
|
||||||
**Array Semantics (Fortran-Style)**
|
**Array Semantics (Fortran-Style)**
|
||||||
|
|
||||||
|
@ -104,7 +103,6 @@ A =plex= is a **Platonic form** - a structured definition of a kind of being in
|
||||||
|
|
||||||
#+BEGIN_SRC ul
|
#+BEGIN_SRC ul
|
||||||
plex Player {
|
plex Player {
|
||||||
version 1;
|
|
||||||
str name;
|
str name;
|
||||||
real[3] pos;
|
real[3] pos;
|
||||||
|
|
||||||
|
@ -119,36 +117,8 @@ plex Player {
|
||||||
|
|
||||||
- Not a class: no inheritance, no vtables
|
- Not a class: no inheritance, no vtables
|
||||||
- Methods are functions with implicit =this= argument
|
- Methods are functions with implicit =this= argument
|
||||||
- Instances are **atoms** - persistent, versioned, serializable
|
- Instances are **atoms**
|
||||||
- Stored in the internal graph
|
- A plex defines what a thing is. An atom is its instance.
|
||||||
|
|
||||||
> *"A plex defines what a thing is. An atom is its instance in that reality."*
|
|
||||||
|
|
||||||
* Versioning & Shadowing (Forth-Inspired)
|
|
||||||
|
|
||||||
When you redefine a =plex=, the old version is **shadowed but preserved** - unless explicitly discarded.
|
|
||||||
|
|
||||||
#+BEGIN_SRC ul
|
|
||||||
plex Counter { version 1; nat value; inc() { value += 1; } }
|
|
||||||
plex Counter { version 2; nat value; inc() { value += 2; } } ! shadows v1
|
|
||||||
|
|
||||||
Counter c1 = Counter(); ! uses v2 (latest)
|
|
||||||
Counter c2 = Counter.v1(); ! uses v1 - still available
|
|
||||||
|
|
||||||
discard Counter.v1; ! optional: free memory
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
Internally, plex versions form a **linked version chain**:
|
|
||||||
- =head= -> latest version
|
|
||||||
- =tail= -> oldest retained version
|
|
||||||
- =migrate(obj, Counter)= -> converts data layout
|
|
||||||
- =versions(Counter)= -> list available versions
|
|
||||||
|
|
||||||
This enables:
|
|
||||||
- Non-destructive evolution
|
|
||||||
- Safe refactoring
|
|
||||||
- Historical reproducibility
|
|
||||||
- Code archaeology
|
|
||||||
|
|
||||||
* Graphics & Devices
|
* Graphics & Devices
|
||||||
|
|
||||||
|
@ -198,37 +168,22 @@ if (server.attach(auth)) {
|
||||||
|
|
||||||
**Tunnel Operations**
|
**Tunnel Operations**
|
||||||
|
|
||||||
| Op | Meaning |
|
| Op | Meaning |
|
||||||
|------------|-------------------------|
|
|------------+-----------------------|
|
||||||
| =.attach()= | Authenticate and open |
|
| =.attach()= | Authenticate and open |
|
||||||
| =.open()= | Open resource |
|
| =.open()= | Open resource |
|
||||||
| =.read()= |Transfer data |
|
| =.read()= | Transfer data |
|
||||||
| =.write()= | Transfer data |
|
| =.write()= | Transfer data |
|
||||||
| =.walk()= | Navigate hierarchy |
|
| =.walk()= | Navigate hierarchy |
|
||||||
| =.flush()= | Cancel long operation |
|
| =.flush()= | Cancel long operation |
|
||||||
| =.clunk()= | Close connection |
|
| =.clunk()= | Close connection |
|
||||||
| =.stat()= | Get metadata |
|
| =.stat()= | Get metadata |
|
||||||
| =.version()=| Get protocol version |
|
| =.version()= | Get protocol version |
|
||||||
|
|
||||||
Tunnels make I/O **uniform, composable, and archival**.
|
Tunnels make I/O **uniform, composable, and archival**.
|
||||||
|
|
||||||
* Development Environment
|
|
||||||
|
|
||||||
supports **live coding** and **temporal development**:
|
|
||||||
|
|
||||||
**Live Coding Features**
|
**Live Coding Features**
|
||||||
- Hot module reloading: inject code while VM runs
|
|
||||||
- REPL-style interaction: inspect memory, call functions, test logic
|
- REPL-style interaction: inspect memory, call functions, test logic
|
||||||
- Shadowing: redefine =plex=es without restarting
|
|
||||||
- Symbol table manipulation: runtime introspection and patching
|
|
||||||
|
|
||||||
**Final Binaries**
|
|
||||||
- Are **snapshots** of:
|
|
||||||
- Memory state
|
|
||||||
- Symbol table
|
|
||||||
- Version chains
|
|
||||||
- Can be saved, restored, or archived as =.zbin= files
|
|
||||||
- Are fully deterministic and reproducible
|
|
||||||
|
|
||||||
* Getting Started
|
* Getting Started
|
||||||
|
|
||||||
|
@ -308,16 +263,12 @@ function main(int argc, str[] argv) {
|
||||||
- Versioned plexes: forward/backward compatibility
|
- Versioned plexes: forward/backward compatibility
|
||||||
- Self-documenting syntax: just enough magic
|
- Self-documenting syntax: just enough magic
|
||||||
- Open standard: no vendor lock-in
|
- Open standard: no vendor lock-in
|
||||||
- Archive formats: =.ul=, =.zbin=, =.zatom=
|
- Archive formats: =.ul=, =.ubin=, =.uatom=
|
||||||
|
|
||||||
* License
|
* License
|
||||||
|
|
||||||
**MIT-0** - No restrictions, no warranty.
|
**MIT-0** - No restrictions, no warranty.
|
||||||
|
|
||||||
With an ethical understanding:
|
|
||||||
|
|
||||||
> This software should not be used to accelerate obsolescence, exploit users, or harm ecosystems. Compute only to strengthen what lasts.
|
|
||||||
|
|
||||||
* Inspirations
|
* Inspirations
|
||||||
|
|
||||||
- [[https://wiki.xxiivv.com/site/uxn.html][Uxn]] - Minimalism, elegance
|
- [[https://wiki.xxiivv.com/site/uxn.html][Uxn]] - Minimalism, elegance
|
||||||
|
@ -336,15 +287,12 @@ With an ethical understanding:
|
||||||
* Join the Effort
|
* Join the Effort
|
||||||
|
|
||||||
The Reality Engine is a community project. We welcome:
|
The Reality Engine is a community project. We welcome:
|
||||||
- Compiler contributors
|
|
||||||
- Port developers (Web, Game Boy, etc.)
|
- Port developers (Web, Game Boy, etc.)
|
||||||
- Artists and game designers
|
- Artists and game designers
|
||||||
- Archivists and historians
|
- Archivists and historians
|
||||||
|
|
||||||
> *"We are not making programs. We are writing Ages."*
|
|
||||||
|
|
||||||
* Contact
|
* Contact
|
||||||
|
|
||||||
- Website: https://undar-lang.org
|
- Website: https://undar-lang.org
|
||||||
- Email: archive@undar-lang.org
|
- Email: archive@undar-lang.org
|
||||||
- Repository: https://git.alfrescocavern.com/zongor/reality-engine.git
|
- Repository: https://git.alfrescocavern.com/zongor/reality-engine.git
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,9 @@ function main(int argc, str[] argv) {
|
||||||
nat screen_width = 800;
|
nat screen_width = 800;
|
||||||
nat screen_height = 450;
|
nat screen_height = 450;
|
||||||
|
|
||||||
|
if (argv < 2) {
|
||||||
|
exits("usage: zre client.ul <username> <password>");
|
||||||
|
}
|
||||||
str username = argv[1];
|
str username = argv[1];
|
||||||
str password = argv[2];
|
str password = argv[2];
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
/**
|
!!
|
||||||
* Note that these look like classes but act like structs
|
! Note that these look like classes but act like structs
|
||||||
* the methods actually have a implied struct as their first argument
|
! the methods actually have a implied struct as their first argument
|
||||||
*/
|
!!
|
||||||
|
|
||||||
/**
|
!!
|
||||||
* Camera.
|
! Camera.
|
||||||
*/
|
!!
|
||||||
plex Camera {
|
plex Camera {
|
||||||
init(real[3] pos, real[3] look) {
|
init(real[3] pos, real[3] look) {
|
||||||
this.setting = "CAMERA_PERSPECTIVE";
|
this.setting = "CAMERA_PERSPECTIVE";
|
||||||
|
@ -16,9 +16,9 @@ plex Camera {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
!!
|
||||||
* Player.
|
! Player.
|
||||||
*/
|
!!
|
||||||
plex Player {
|
plex Player {
|
||||||
init(str username, real[3] pos, Color color) {
|
init(str username, real[3] pos, Color color) {
|
||||||
this.client = Client("tcp://localhost:25565");
|
this.client = Client("tcp://localhost:25565");
|
||||||
|
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
@ -2,7 +2,7 @@
|
||||||
# -----------------------
|
# -----------------------
|
||||||
# Native build (gcc)
|
# Native build (gcc)
|
||||||
CC_NATIVE = gcc
|
CC_NATIVE = gcc
|
||||||
CFLAGS_NATIVE = -g -O2 -std=c89 -Wall -Wextra -Werror -Wno-unused-parameter -I.
|
CFLAGS_NATIVE = -g -std=c89 -Wall -Wextra -Werror -Wno-unused-parameter -I. #-O2
|
||||||
LDFLAGS_NATIVE =
|
LDFLAGS_NATIVE =
|
||||||
LDLIBS_NATIVE = -lSDL2
|
LDLIBS_NATIVE = -lSDL2
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ int screen_open(void *data, uint32_t mode) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
screen->texture = SDL_CreateTexture(
|
screen->texture = SDL_CreateTexture(
|
||||||
screen->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING,
|
screen->renderer, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING,
|
||||||
screen->width, screen->height);
|
screen->width, screen->height);
|
||||||
if (!screen->texture)
|
if (!screen->texture)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -32,7 +32,7 @@ int screen_read(void *data, uint8_t *buffer, uint32_t size) { return -1; }
|
||||||
int screen_write(void *data, const uint8_t *buffer, uint32_t size) {
|
int screen_write(void *data, const uint8_t *buffer, uint32_t size) {
|
||||||
ScreenDeviceData *screen = (ScreenDeviceData *)data;
|
ScreenDeviceData *screen = (ScreenDeviceData *)data;
|
||||||
|
|
||||||
if (size > screen->framebuffer_size * sizeof(uint32_t)) {
|
if (size > screen->framebuffer_size * sizeof(uint8_t)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#include "../../compiler.h"
|
|
||||||
#include "../../test.h"
|
#include "../../test.h"
|
||||||
#include "../../vm.h"
|
#include "../../vm.h"
|
||||||
#include "devices.h"
|
#include "devices.h"
|
||||||
|
@ -9,29 +8,23 @@
|
||||||
|
|
||||||
#define MAX_SRC_SIZE 16384
|
#define MAX_SRC_SIZE 16384
|
||||||
|
|
||||||
static DeviceOps screen_ops = {
|
static DeviceOps screen_ops = {.open = screen_open,
|
||||||
.open = screen_open,
|
.read = screen_read,
|
||||||
.read = screen_read,
|
.write = screen_write,
|
||||||
.write = screen_write,
|
.close = screen_close,
|
||||||
.close = screen_close,
|
.ioctl = NULL};
|
||||||
.ioctl = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static DeviceOps mouse_ops = {
|
static DeviceOps mouse_ops = {.open = mouse_open,
|
||||||
.open = mouse_open,
|
.read = mouse_read,
|
||||||
.read = mouse_read,
|
.write = mouse_write,
|
||||||
.write = mouse_write,
|
.close = mouse_close,
|
||||||
.close = mouse_close,
|
.ioctl = NULL};
|
||||||
.ioctl = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static DeviceOps keyboard_ops= {
|
static DeviceOps keyboard_ops = {.open = keyboard_open,
|
||||||
.open = keyboard_open,
|
.read = keyboard_read,
|
||||||
.read = keyboard_read,
|
.write = keyboard_write,
|
||||||
.write = keyboard_write,
|
.close = keyboard_close,
|
||||||
.close = keyboard_close,
|
.ioctl = NULL};
|
||||||
.ioctl = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static ScreenDeviceData screen_data = {0};
|
static ScreenDeviceData screen_data = {0};
|
||||||
static MouseDeviceData mouse_data = {0};
|
static MouseDeviceData mouse_data = {0};
|
||||||
|
@ -56,8 +49,6 @@ void compileFile(const char *path, VM *vm) {
|
||||||
size_t read = fread(source, 1, len, f);
|
size_t read = fread(source, 1, len, f);
|
||||||
source[read] = '\0';
|
source[read] = '\0';
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
compile(source, vm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void repl(VM *vm) {
|
void repl(VM *vm) {
|
||||||
|
@ -75,7 +66,7 @@ void repl(VM *vm) {
|
||||||
vm->pc = 0;
|
vm->pc = 0;
|
||||||
vm->mp = 0;
|
vm->mp = 0;
|
||||||
|
|
||||||
compile(line, vm);
|
/* assemble(line, vm); */
|
||||||
while (step_vm(vm))
|
while (step_vm(vm))
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
@ -126,7 +117,7 @@ int parse_arguments(int argc, char *argv[], struct CompilerConfig *config) {
|
||||||
fprintf(stderr, "Unknown flag: %s\n", argv[i]);
|
fprintf(stderr, "Unknown flag: %s\n", argv[i]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (strstr(argv[i], ".zrl") != NULL) {
|
} else if (strstr(argv[i], ".ul") != NULL) {
|
||||||
/* Collect input files */
|
/* Collect input files */
|
||||||
if (config->input_file_count >= MAX_INPUT_FILES) {
|
if (config->input_file_count >= MAX_INPUT_FILES) {
|
||||||
fprintf(stderr, "Too many input files. Maximum is %d\n",
|
fprintf(stderr, "Too many input files. Maximum is %d\n",
|
||||||
|
@ -146,7 +137,7 @@ void register_sdl_devices(VM *vm) {
|
||||||
screen_data.height = 480;
|
screen_data.height = 480;
|
||||||
screen_data.framebuffer_size = 640 * 480;
|
screen_data.framebuffer_size = 640 * 480;
|
||||||
screen_data.framebuffer_pos = vm->mp;
|
screen_data.framebuffer_pos = vm->mp;
|
||||||
vm->mp += screen_data.framebuffer_size; /* advance memory pointer */
|
vm->mp += screen_data.framebuffer_size / 4; /* advance memory pointer */
|
||||||
|
|
||||||
vm_register_device(vm, "/dev/screen/0", "screen", &screen_data, &screen_ops);
|
vm_register_device(vm, "/dev/screen/0", "screen", &screen_data, &screen_ops);
|
||||||
|
|
||||||
|
@ -169,14 +160,12 @@ int main(int argc, char *argv[]) {
|
||||||
struct CompilerConfig config = {0};
|
struct CompilerConfig config = {0};
|
||||||
|
|
||||||
if (parse_arguments(argc, argv, &config) != 0) {
|
if (parse_arguments(argc, argv, &config) != 0) {
|
||||||
fprintf(stderr,
|
fprintf(stderr, "Usage: %s [-d] [-t] [-g] [-o] <file1.ul> [file2.ul] ...\n",
|
||||||
"Usage: %s [-d] [-t] [-g] [-o] <file1.zrl> [file2.zrl] ...\n",
|
|
||||||
argv[0]);
|
argv[0]);
|
||||||
return 64;
|
return 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
VM vm = {0};
|
VM vm = {0};
|
||||||
|
|
||||||
if (config.input_file_count == 0) {
|
if (config.input_file_count == 0) {
|
||||||
repl(&vm);
|
repl(&vm);
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,9 +207,9 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
bool running = true;
|
bool running = true;
|
||||||
|
|
||||||
register_sdl_devices(&vm);
|
|
||||||
if (config.flags & FLAG_GUI_MODE) {
|
if (config.flags & FLAG_GUI_MODE) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
register_sdl_devices(&vm);
|
||||||
while (running) {
|
while (running) {
|
||||||
for (i = 0; i < vm.dc; i++) {
|
for (i = 0; i < vm.dc; i++) {
|
||||||
Device *dev = &vm.devices[i];
|
Device *dev = &vm.devices[i];
|
||||||
|
@ -269,7 +258,8 @@ int main(int argc, char *argv[]) {
|
||||||
if (strcmp(dev->type, "screen") == 0) {
|
if (strcmp(dev->type, "screen") == 0) {
|
||||||
ScreenDeviceData *screen = (ScreenDeviceData *)dev->data;
|
ScreenDeviceData *screen = (ScreenDeviceData *)dev->data;
|
||||||
if (screen->texture && screen->renderer) {
|
if (screen->texture && screen->renderer) {
|
||||||
SDL_UpdateTexture(screen->texture, NULL, &vm.memory[screen->framebuffer_pos],
|
SDL_UpdateTexture(screen->texture, NULL,
|
||||||
|
&vm.memory[screen->framebuffer_pos],
|
||||||
screen->width * sizeof(uint32_t));
|
screen->width * sizeof(uint32_t));
|
||||||
|
|
||||||
SDL_RenderClear(screen->renderer);
|
SDL_RenderClear(screen->renderer);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef ZRL_COMMON_H
|
#ifndef ZRE_COMMON_H
|
||||||
#define ZRL_COMMON_H
|
#define ZRE_COMMON_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
377
src/compiler.c
377
src/compiler.c
|
@ -1,377 +0,0 @@
|
||||||
#include "compiler.h"
|
|
||||||
#include "vm.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Token current;
|
|
||||||
Token previous;
|
|
||||||
bool hadError;
|
|
||||||
bool panicMode;
|
|
||||||
} Parser;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
PREC_NONE,
|
|
||||||
PREC_ASSIGNMENT, /* = */
|
|
||||||
PREC_OR, /* or */
|
|
||||||
PREC_AND, /* and */
|
|
||||||
PREC_EQUALITY, /* == != */
|
|
||||||
PREC_COMPARISON, /* < > <= >= */
|
|
||||||
PREC_TERM, /* + - */
|
|
||||||
PREC_FACTOR, /* * / */
|
|
||||||
PREC_UNARY, /* not */
|
|
||||||
PREC_CALL, /* . () */
|
|
||||||
PREC_PRIMARY
|
|
||||||
} Precedence;
|
|
||||||
|
|
||||||
typedef void (*ParseFn)(VM *vm);
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ParseFn prefix;
|
|
||||||
ParseFn infix;
|
|
||||||
Precedence precedence;
|
|
||||||
} ParseRule;
|
|
||||||
|
|
||||||
Parser parser;
|
|
||||||
SymbolTable st;
|
|
||||||
|
|
||||||
const char *internalErrorMsg = "FLAGRANT COMPILER ERROR\n\nCompiler over.\nBug = Very Yes.";
|
|
||||||
|
|
||||||
void errorAt(Token *token, const char *message) {
|
|
||||||
if (parser.panicMode)
|
|
||||||
return;
|
|
||||||
parser.panicMode = true;
|
|
||||||
fprintf(stderr, "[line %d] Error", token->line);
|
|
||||||
|
|
||||||
if (token->type == TOKEN_EOF) {
|
|
||||||
fprintf(stderr, " at end");
|
|
||||||
} else if (token->type == TOKEN_ERROR) {
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, " at '%.*s'", token->length, token->start);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, ": %s\n", message);
|
|
||||||
parser.hadError = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void error(const char *message) { errorAt(&parser.previous, message); }
|
|
||||||
|
|
||||||
void errorAtCurrent(const char *message) { errorAt(&parser.current, message); }
|
|
||||||
|
|
||||||
void advance() {
|
|
||||||
parser.previous = parser.current;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
parser.current = nextToken();
|
|
||||||
if (parser.current.type != TOKEN_ERROR)
|
|
||||||
break;
|
|
||||||
|
|
||||||
errorAtCurrent(parser.current.start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void consume(TokenType type, const char *message) {
|
|
||||||
if (parser.current.type == type) {
|
|
||||||
advance();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorAtCurrent(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool check(TokenType type) { return parser.current.type == type; }
|
|
||||||
|
|
||||||
static bool match(TokenType type) {
|
|
||||||
if (!check(type))
|
|
||||||
return false;
|
|
||||||
advance();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitOp(VM *vm, uint8_t opcode, uint8_t dest, uint8_t src1, uint8_t src2) {
|
|
||||||
vm->code[vm->cp++].u = OP(opcode, dest, src1, src2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void expression(VM *vm);
|
|
||||||
void statement(VM *vm);
|
|
||||||
void declaration(VM *vm);
|
|
||||||
ParseRule *getRule(TokenType type);
|
|
||||||
void parsePrecedence(VM *vm, Precedence precedence);
|
|
||||||
|
|
||||||
void number(VM *vm) {
|
|
||||||
if (parser.previous.type == TOKEN_INT_LITERAL) {
|
|
||||||
char *endptr;
|
|
||||||
int32_t value = (int32_t)strtol(parser.previous.start, &endptr, 10);
|
|
||||||
emitOp(vm, OP_LOAD, vm->frames[vm->fp].rp++, 0, 0);
|
|
||||||
vm->code[vm->cp++].u = int_alloc(vm, value);
|
|
||||||
return;
|
|
||||||
} else if (parser.previous.type == TOKEN_UINT_LITERAL) {
|
|
||||||
long value = atol(parser.previous.start);
|
|
||||||
emitOp(vm, OP_LOAD, vm->frames[vm->fp].rp++, 0, 0);
|
|
||||||
vm->code[vm->cp++].u = nat_alloc(vm, value);
|
|
||||||
return;
|
|
||||||
} else if (parser.previous.type == TOKEN_FLOAT_LITERAL) {
|
|
||||||
float value = atof(parser.previous.start);
|
|
||||||
emitOp(vm, OP_LOAD, vm->frames[vm->fp].rp++, 0, 0);
|
|
||||||
vm->code[vm->cp++].u = real_alloc(vm, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
errorAtCurrent("Invalid number format");
|
|
||||||
}
|
|
||||||
|
|
||||||
void string(VM *vm) {
|
|
||||||
uint32_t length = parser.previous.length - 2;
|
|
||||||
uint32_t str_addr = vm->mp;
|
|
||||||
vm->memory[vm->mp++].u = length;
|
|
||||||
uint32_t i, j = 0;
|
|
||||||
for (i = 0; i < length; i++) {
|
|
||||||
vm->memory[vm->mp].c[i % 4] = parser.previous.start[i + 1];
|
|
||||||
if (++j == 4) {
|
|
||||||
j = 0;
|
|
||||||
vm->mp++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vm->frames[vm->fp].allocated.end += length / 4;
|
|
||||||
emitOp(vm, OP_LOAD, vm->frames[vm->fp].rp++, 0, 0);
|
|
||||||
vm->code[vm->cp++].u = str_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void grouping(VM *vm) {
|
|
||||||
expression(vm);
|
|
||||||
consume(TOKEN_RPAREN, "Expect ')' after expression.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void unary(VM *vm) {
|
|
||||||
TokenType operatorType = parser.previous.type;
|
|
||||||
|
|
||||||
parsePrecedence(vm, PREC_UNARY);
|
|
||||||
|
|
||||||
switch (operatorType) {
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void literal(VM *vm) {
|
|
||||||
switch (parser.previous.type) {
|
|
||||||
case TOKEN_KEYWORD_NIL: {
|
|
||||||
emitOp(vm, OP_LOAD, vm->frames[vm->fp].rp++, 0, 0);
|
|
||||||
vm->code[vm->cp++].u = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TOKEN_KEYWORD_FALSE: {
|
|
||||||
emitOp(vm, OP_LOAD, vm->frames[vm->fp].rp++, 0, 0);
|
|
||||||
vm->code[vm->cp++].u = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TOKEN_KEYWORD_TRUE: {
|
|
||||||
emitOp(vm, OP_LOAD, vm->frames[vm->fp].rp++, 0, 0);
|
|
||||||
vm->code[vm->cp++].u = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void binary(VM *vm) {
|
|
||||||
TokenType operatorType = parser.previous.type;
|
|
||||||
ParseRule *rule = getRule(operatorType);
|
|
||||||
parsePrecedence(vm, (Precedence)(rule->precedence + 1));
|
|
||||||
TokenType operandType = parser.previous.type;
|
|
||||||
|
|
||||||
Frame f = vm->frames[vm->fp];
|
|
||||||
uint32_t src1 = f.rp--;
|
|
||||||
uint32_t src2 = f.rp--;
|
|
||||||
uint32_t dest = f.rp++;
|
|
||||||
|
|
||||||
switch (operatorType) {
|
|
||||||
case TOKEN_PLUS:
|
|
||||||
if (operandType == TOKEN_UINT_LITERAL) {
|
|
||||||
emitOp(vm, OP_ADD_UINT, dest, src1, src2);
|
|
||||||
} else if (operandType == TOKEN_INT_LITERAL) {
|
|
||||||
emitOp(vm, OP_ADD_INT, dest, src1, src2);
|
|
||||||
} else if (operandType == TOKEN_FLOAT_LITERAL) {
|
|
||||||
emitOp(vm, OP_ADD_REAL, dest, src1, src2);
|
|
||||||
} else {
|
|
||||||
error("not numeric");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TOKEN_MINUS:
|
|
||||||
if (operandType == TOKEN_UINT_LITERAL) {
|
|
||||||
emitOp(vm, OP_SUB_UINT, dest, src1, src2);
|
|
||||||
} else if (operandType == TOKEN_INT_LITERAL) {
|
|
||||||
emitOp(vm, OP_SUB_INT, dest, src1, src2);
|
|
||||||
} else if (operandType == TOKEN_FLOAT_LITERAL) {
|
|
||||||
emitOp(vm, OP_SUB_REAL, dest, src1, src2);
|
|
||||||
} else {
|
|
||||||
error("not numeric");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TOKEN_STAR:
|
|
||||||
if (operandType == TOKEN_UINT_LITERAL) {
|
|
||||||
emitOp(vm, OP_MUL_UINT, dest, src1, src2);
|
|
||||||
} else if (operandType == TOKEN_INT_LITERAL) {
|
|
||||||
emitOp(vm, OP_MUL_INT, dest, src1, src2);
|
|
||||||
} else if (operandType == TOKEN_FLOAT_LITERAL) {
|
|
||||||
emitOp(vm, OP_MUL_REAL, dest, src1, src2);
|
|
||||||
} else {
|
|
||||||
error("not numeric");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TOKEN_SLASH:
|
|
||||||
if (operandType == TOKEN_UINT_LITERAL) {
|
|
||||||
emitOp(vm, OP_DIV_UINT, dest, src1, src2);
|
|
||||||
} else if (operandType == TOKEN_INT_LITERAL) {
|
|
||||||
emitOp(vm, OP_DIV_INT, dest, src1, src2);
|
|
||||||
} else if (operandType == TOKEN_FLOAT_LITERAL) {
|
|
||||||
emitOp(vm, OP_DIV_REAL, dest, src1, src2);
|
|
||||||
} else {
|
|
||||||
error("not numeric");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return; /* Unreachable. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseRule rules[] = {
|
|
||||||
[TOKEN_LPAREN] = {grouping, NULL, PREC_NONE},
|
|
||||||
[TOKEN_RPAREN] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_LBRACE] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_RBRACE] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_COMMA] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_DOT] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_MINUS] = {NULL, binary, PREC_TERM},
|
|
||||||
[TOKEN_PLUS] = {NULL, binary, PREC_TERM},
|
|
||||||
[TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_SLASH] = {NULL, binary, PREC_FACTOR},
|
|
||||||
[TOKEN_STAR] = {NULL, binary, PREC_FACTOR},
|
|
||||||
[TOKEN_BANG] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_BANG_EQ] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_EQ] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_EQ_EQ] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_GT] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_GTE] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_LT] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_LTE] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_IDENTIFIER] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_STRING_LITERAL] = {string, NULL, PREC_NONE},
|
|
||||||
[TOKEN_INT_LITERAL] = {number, NULL, PREC_NONE},
|
|
||||||
[TOKEN_UINT_LITERAL] = {number, NULL, PREC_NONE},
|
|
||||||
[TOKEN_FLOAT_LITERAL] = {number, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_ELSE] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_FOR] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_FN] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_IF] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_OPERATOR_AND] = {NULL, binary, PREC_NONE},
|
|
||||||
[TOKEN_OPERATOR_OR] = {NULL, binary, PREC_NONE},
|
|
||||||
[TOKEN_OPERATOR_NOT] = {unary, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_NIL] = {literal, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_TRUE] = {literal, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_FALSE] = {literal, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_PRINT] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_RETURN] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_THIS] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_LET] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_KEYWORD_WHILE] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_EOF] = {NULL, NULL, PREC_NONE},
|
|
||||||
};
|
|
||||||
|
|
||||||
ParseRule *getRule(TokenType type) { return &rules[type]; }
|
|
||||||
|
|
||||||
void parsePrecedence(VM *vm, Precedence precedence) {
|
|
||||||
advance();
|
|
||||||
ParseFn prefixRule = getRule(parser.previous.type)->prefix;
|
|
||||||
if (prefixRule == NULL) {
|
|
||||||
error("Expect expression.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
prefixRule(vm);
|
|
||||||
|
|
||||||
while (precedence <= getRule(parser.current.type)->precedence) {
|
|
||||||
advance();
|
|
||||||
ParseFn infixRule = getRule(parser.previous.type)->infix;
|
|
||||||
infixRule(vm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void expression(VM *vm) { parsePrecedence(vm, PREC_ASSIGNMENT); }
|
|
||||||
|
|
||||||
void printStatement(VM *vm) {
|
|
||||||
expression(vm);
|
|
||||||
consume(TOKEN_SEMICOLON, "Expect ';' after value.");
|
|
||||||
Frame f = vm->frames[vm->fp];
|
|
||||||
vm->code[vm->cp++].u = OP(OP_DBG_PRINT_STRING, 0, f.rp--, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void expressionStatement(VM *vm) {
|
|
||||||
expression(vm);
|
|
||||||
consume(TOKEN_SEMICOLON, "Expect ';' after expression.");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void intDeclaration(VM *vm) {
|
|
||||||
/* insert variable name in symbol table */
|
|
||||||
uint32_t length = parser.previous.length - 2;
|
|
||||||
if (length > SYMBOL_NAME_SIZE) {
|
|
||||||
error("Variable names cannot be longer than 24 characters.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
st.symbols[st.sc].type = INT;
|
|
||||||
st.symbols[st.sc].frame = vm->fp;
|
|
||||||
|
|
||||||
Frame f = vm->frames[vm->fp];
|
|
||||||
st.symbols[st.sc].reg = f.rp;
|
|
||||||
|
|
||||||
uint32_t i;
|
|
||||||
for (i = 0; i < length; i++) {
|
|
||||||
st.symbols[st.sc].name[i] = parser.previous.start[i + 1];
|
|
||||||
}
|
|
||||||
st.sc++;
|
|
||||||
|
|
||||||
if (match(TOKEN_EQ)) {
|
|
||||||
expression(vm);
|
|
||||||
} else {
|
|
||||||
/* initialize as zero/null */
|
|
||||||
emitOp(vm, OP_LOAD, vm->frames[vm->fp].rp++, 0, 0);
|
|
||||||
vm->code[vm->cp++].i = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
consume(TOKEN_SEMICOLON, "Expect ';' after expression.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void statement(VM *vm) {
|
|
||||||
if (match(TOKEN_KEYWORD_PRINT)) {
|
|
||||||
printStatement(vm);
|
|
||||||
} else if (match(TOKEN_TYPE_INT)) {
|
|
||||||
intDeclaration(vm);
|
|
||||||
} else {
|
|
||||||
expressionStatement(vm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void declaration(VM *vm) { statement(vm); }
|
|
||||||
|
|
||||||
bool compile(const char *source, VM *vm) {
|
|
||||||
initLexer(source);
|
|
||||||
|
|
||||||
parser.hadError = false;
|
|
||||||
parser.panicMode = false;
|
|
||||||
|
|
||||||
st.sc = 0;
|
|
||||||
st.name[0] = 'm';
|
|
||||||
st.name[1] = 'a';
|
|
||||||
st.name[2] = 'i';
|
|
||||||
st.name[3] = 'n';
|
|
||||||
|
|
||||||
advance();
|
|
||||||
|
|
||||||
while (!match(TOKEN_EOF)) {
|
|
||||||
declaration(vm);
|
|
||||||
}
|
|
||||||
|
|
||||||
emitOp(vm, OP_HALT, 0, 0, 0);
|
|
||||||
|
|
||||||
return !parser.hadError;
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
#ifndef ZRL_COMPILER_H
|
|
||||||
#define ZRL_COMPILER_H
|
|
||||||
|
|
||||||
#include "lexer.h"
|
|
||||||
#include "opcodes.h"
|
|
||||||
|
|
||||||
typedef enum { INT, REAL, NATURAL, POINTER, STRING, ARRAY, PLEX } SymbolType;
|
|
||||||
|
|
||||||
typedef struct plex_def_t {
|
|
||||||
SymbolType subtype;
|
|
||||||
uint32_t size;
|
|
||||||
} PlexDef;
|
|
||||||
|
|
||||||
typedef struct array_def_t {
|
|
||||||
SymbolType subtype;
|
|
||||||
uint32_t length;
|
|
||||||
} ArrayDef;
|
|
||||||
|
|
||||||
#define SYMBOL_NAME_SIZE 24
|
|
||||||
|
|
||||||
typedef struct symbol_t {
|
|
||||||
char name[SYMBOL_NAME_SIZE];
|
|
||||||
SymbolType type;
|
|
||||||
union {
|
|
||||||
PlexDef pd;
|
|
||||||
ArrayDef ad;
|
|
||||||
};
|
|
||||||
int8_t reg;
|
|
||||||
uint8_t flags[3]; /* only use for padding now, might be used later*/
|
|
||||||
uint32_t frame;
|
|
||||||
uint32_t ptr;
|
|
||||||
} Symbol;
|
|
||||||
|
|
||||||
#define MODULE_NAME_SIZE 32
|
|
||||||
#define SYMBOL_COUNT 256
|
|
||||||
|
|
||||||
typedef struct symbol_table_t {
|
|
||||||
char name[MODULE_NAME_SIZE];
|
|
||||||
Symbol symbols[SYMBOL_COUNT];
|
|
||||||
uint32_t sc;
|
|
||||||
} SymbolTable;
|
|
||||||
|
|
||||||
extern SymbolTable st;
|
|
||||||
|
|
||||||
bool compile(const char *source, VM *vm);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef ZRL_DEVICE_H
|
#ifndef ZRE_DEVICE_H
|
||||||
#define ZRL_DEVICE_H
|
#define ZRE_DEVICE_H
|
||||||
|
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
|
|
||||||
|
|
201
src/fixed.c
201
src/fixed.c
|
@ -1,201 +0,0 @@
|
||||||
/* fixed.c - Q16.16 Fixed-Point Math Implementation */
|
|
||||||
|
|
||||||
#include "fixed.h"
|
|
||||||
|
|
||||||
/* Conversion functions */
|
|
||||||
fixed_t int_to_fixed(int32_t i) {
|
|
||||||
return i << 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t fixed_to_int(fixed_t f) {
|
|
||||||
return f >> 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_t float_to_fixed(float f) {
|
|
||||||
return (fixed_t)(f * 65536.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
float fixed_to_float(fixed_t f) {
|
|
||||||
return (float)f / 65536.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_t fixed_add(fixed_t a, fixed_t b) {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_t fixed_sub(fixed_t a, fixed_t b) {
|
|
||||||
return a - b;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_t fixed_mul(fixed_t a, fixed_t b) {
|
|
||||||
/* Extract high and low parts */
|
|
||||||
int32_t a_hi = a >> 16;
|
|
||||||
uint32_t a_lo = (uint32_t)a & 0xFFFFU;
|
|
||||||
int32_t b_hi = b >> 16;
|
|
||||||
uint32_t b_lo = (uint32_t)b & 0xFFFFU;
|
|
||||||
|
|
||||||
/* Compute partial products */
|
|
||||||
int32_t p0 = (int32_t)(a_lo * b_lo) >> 16; /* Low * Low */
|
|
||||||
int32_t p1 = a_hi * (int32_t)b_lo; /* High * Low */
|
|
||||||
int32_t p2 = (int32_t)a_lo * b_hi; /* Low * High */
|
|
||||||
int32_t p3 = (a_hi * b_hi) << 16; /* High * High */
|
|
||||||
|
|
||||||
/* Combine results */
|
|
||||||
return p0 + p1 + p2 + p3;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_t fixed_div(fixed_t a, fixed_t b) {
|
|
||||||
if (b == 0) return 0; /* Handle division by zero */
|
|
||||||
|
|
||||||
/* Determine sign */
|
|
||||||
int negative = ((a < 0) ^ (b < 0));
|
|
||||||
|
|
||||||
/* Work with absolute values */
|
|
||||||
uint32_t ua = (a < 0) ? -a : a;
|
|
||||||
uint32_t ub = (b < 0) ? -b : b;
|
|
||||||
|
|
||||||
/* Perform division using long division in base 2^16 */
|
|
||||||
uint32_t quotient = 0;
|
|
||||||
uint32_t remainder = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < 32; i++) {
|
|
||||||
remainder <<= 1;
|
|
||||||
if (ua & 0x80000000U) {
|
|
||||||
remainder |= 1;
|
|
||||||
}
|
|
||||||
ua <<= 1;
|
|
||||||
|
|
||||||
if (remainder >= ub) {
|
|
||||||
remainder -= ub;
|
|
||||||
quotient |= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < 31) {
|
|
||||||
quotient <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return negative ? -(int32_t)quotient : (int32_t)quotient;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fixed_eq(fixed_t a, fixed_t b) {
|
|
||||||
return a == b;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fixed_ne(fixed_t a, fixed_t b) {
|
|
||||||
return a != b;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fixed_lt(fixed_t a, fixed_t b) {
|
|
||||||
return a < b;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fixed_le(fixed_t a, fixed_t b) {
|
|
||||||
return a <= b;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fixed_gt(fixed_t a, fixed_t b) {
|
|
||||||
return a > b;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fixed_ge(fixed_t a, fixed_t b) {
|
|
||||||
return a >= b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unary operations */
|
|
||||||
fixed_t fixed_neg(fixed_t f) {
|
|
||||||
return -f;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_t fixed_abs(fixed_t f) {
|
|
||||||
return (f < 0) ? -f : f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Square root using Newton-Raphson method */
|
|
||||||
fixed_t fixed_sqrt(fixed_t f) {
|
|
||||||
if (f <= 0) return 0;
|
|
||||||
|
|
||||||
fixed_t x = f;
|
|
||||||
fixed_t prev;
|
|
||||||
|
|
||||||
/* Newton-Raphson iteration: x = (x + f/x) / 2 */
|
|
||||||
do {
|
|
||||||
prev = x;
|
|
||||||
x = fixed_div(fixed_add(x, fixed_div(f, x)), int_to_fixed(2));
|
|
||||||
} while (fixed_gt(fixed_abs(fixed_sub(x, prev)), 1)); /* Precision to 1/65536 */
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sine function using Taylor series */
|
|
||||||
fixed_t fixed_sin(fixed_t f) {
|
|
||||||
/* Normalize angle to [-π, π] */
|
|
||||||
fixed_t pi2 = fixed_mul(FIXED_PI, int_to_fixed(2));
|
|
||||||
while (fixed_gt(f, FIXED_PI)) f = fixed_sub(f, pi2);
|
|
||||||
while (fixed_lt(f, fixed_neg(FIXED_PI))) f = fixed_add(f, pi2);
|
|
||||||
|
|
||||||
/* Taylor series: sin(x) = x - x³/3! + x⁵/5! - x⁷/7! + ... */
|
|
||||||
fixed_t result = f;
|
|
||||||
fixed_t term = f;
|
|
||||||
fixed_t f_squared = fixed_mul(f, f);
|
|
||||||
|
|
||||||
/* Calculate first few terms for reasonable precision */
|
|
||||||
int i;
|
|
||||||
for (i = 3; i <= 11; i += 2) {
|
|
||||||
term = fixed_mul(term, f_squared);
|
|
||||||
term = fixed_div(term, int_to_fixed(i * (i - 1)));
|
|
||||||
|
|
||||||
if ((i / 2) % 2 == 0) {
|
|
||||||
result = fixed_add(result, term);
|
|
||||||
} else {
|
|
||||||
result = fixed_sub(result, term);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cosine function using Taylor series */
|
|
||||||
fixed_t fixed_cos(fixed_t f) {
|
|
||||||
/* cos(x) = 1 - x²/2! + x⁴/4! - x⁶/6! + ... */
|
|
||||||
fixed_t result = FIXED_ONE;
|
|
||||||
fixed_t term = FIXED_ONE;
|
|
||||||
fixed_t f_squared = fixed_mul(f, f);
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 2; i <= 12; i += 2) {
|
|
||||||
term = fixed_mul(term, f_squared);
|
|
||||||
term = fixed_div(term, int_to_fixed(i * (i - 1)));
|
|
||||||
|
|
||||||
if ((i / 2) % 2 == 0) {
|
|
||||||
result = fixed_add(result, term);
|
|
||||||
} else {
|
|
||||||
result = fixed_sub(result, term);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tangent function */
|
|
||||||
fixed_t fixed_tan(fixed_t f) {
|
|
||||||
fixed_t cos_val = fixed_cos(f);
|
|
||||||
if (cos_val == 0) return 0; /* Handle undefined case */
|
|
||||||
return fixed_div(fixed_sin(f), cos_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Utility functions */
|
|
||||||
fixed_t fixed_min(fixed_t a, fixed_t b) {
|
|
||||||
return (a < b) ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_t fixed_max(fixed_t a, fixed_t b) {
|
|
||||||
return (a > b) ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_t fixed_clamp(fixed_t f, fixed_t min_val, fixed_t max_val) {
|
|
||||||
if (f < min_val) return min_val;
|
|
||||||
if (f > max_val) return max_val;
|
|
||||||
return f;
|
|
||||||
}
|
|
55
src/fixed.h
55
src/fixed.h
|
@ -1,55 +0,0 @@
|
||||||
/* fixed.h - Q16.16 Fixed-Point Math Library in C89 */
|
|
||||||
|
|
||||||
#ifndef FIXED_H
|
|
||||||
#define FIXED_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* Q16.16 fixed-point type */
|
|
||||||
typedef int32_t fixed_t;
|
|
||||||
|
|
||||||
/* Constants */
|
|
||||||
#define FIXED_ONE 0x00010000L /* 1.0 in Q16.16 */
|
|
||||||
#define FIXED_ZERO 0x00000000L /* 0.0 in Q16.16 */
|
|
||||||
#define FIXED_HALF 0x00008000L /* 0.5 in Q16.16 */
|
|
||||||
#define FIXED_PI 0x0003243FL /* π ≈ 3.14159 */
|
|
||||||
#define FIXED_E 0x0002B7E1L /* e ≈ 2.71828 */
|
|
||||||
#define FIXED_MAX 0x7FFFFFFFL /* Maximum positive value */
|
|
||||||
#define FIXED_MIN 0x80000000L /* Minimum negative value */
|
|
||||||
|
|
||||||
/* Conversion functions */
|
|
||||||
fixed_t int_to_fixed(int32_t i);
|
|
||||||
int32_t fixed_to_int(fixed_t f);
|
|
||||||
fixed_t float_to_fixed(float f);
|
|
||||||
float fixed_to_float(fixed_t f);
|
|
||||||
|
|
||||||
/* Basic arithmetic operations */
|
|
||||||
fixed_t fixed_add(fixed_t a, fixed_t b);
|
|
||||||
fixed_t fixed_sub(fixed_t a, fixed_t b);
|
|
||||||
fixed_t fixed_mul(fixed_t a, fixed_t b);
|
|
||||||
fixed_t fixed_div(fixed_t a, fixed_t b);
|
|
||||||
|
|
||||||
/* Comparison functions */
|
|
||||||
int fixed_eq(fixed_t a, fixed_t b);
|
|
||||||
int fixed_ne(fixed_t a, fixed_t b);
|
|
||||||
int fixed_lt(fixed_t a, fixed_t b);
|
|
||||||
int fixed_le(fixed_t a, fixed_t b);
|
|
||||||
int fixed_gt(fixed_t a, fixed_t b);
|
|
||||||
int fixed_ge(fixed_t a, fixed_t b);
|
|
||||||
|
|
||||||
/* Unary operations */
|
|
||||||
fixed_t fixed_neg(fixed_t f);
|
|
||||||
fixed_t fixed_abs(fixed_t f);
|
|
||||||
|
|
||||||
/* Advanced math functions */
|
|
||||||
fixed_t fixed_sqrt(fixed_t f);
|
|
||||||
fixed_t fixed_sin(fixed_t f); /* f in radians */
|
|
||||||
fixed_t fixed_cos(fixed_t f); /* f in radians */
|
|
||||||
fixed_t fixed_tan(fixed_t f); /* f in radians */
|
|
||||||
|
|
||||||
/* Utility functions */
|
|
||||||
fixed_t fixed_min(fixed_t a, fixed_t b);
|
|
||||||
fixed_t fixed_max(fixed_t a, fixed_t b);
|
|
||||||
fixed_t fixed_clamp(fixed_t f, fixed_t min, fixed_t max);
|
|
||||||
|
|
||||||
#endif /* FIXED_H */
|
|
248
src/lexer.c
248
src/lexer.c
|
@ -1,248 +0,0 @@
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "lexer.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char *start;
|
|
||||||
const char *current;
|
|
||||||
int line;
|
|
||||||
} Lexer;
|
|
||||||
|
|
||||||
Lexer lexer;
|
|
||||||
|
|
||||||
void initLexer(const char *source) {
|
|
||||||
lexer.start = source;
|
|
||||||
lexer.current = source;
|
|
||||||
lexer.line = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAlpha(char c) {
|
|
||||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isDigit(char c) { return c >= '0' && c <= '9'; }
|
|
||||||
|
|
||||||
static bool isAtEnd() { return *lexer.current == '\0'; }
|
|
||||||
|
|
||||||
static char advance() {
|
|
||||||
lexer.current++;
|
|
||||||
return lexer.current[-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static char peek() { return *lexer.current; }
|
|
||||||
|
|
||||||
static char peekNext() {
|
|
||||||
if (isAtEnd())
|
|
||||||
return '\0';
|
|
||||||
return lexer.current[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool match(char expected) {
|
|
||||||
if (isAtEnd())
|
|
||||||
return false;
|
|
||||||
if (*lexer.current != expected)
|
|
||||||
return false;
|
|
||||||
lexer.current++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token makeToken(TokenType type) {
|
|
||||||
Token token;
|
|
||||||
token.type = type;
|
|
||||||
token.start = lexer.start;
|
|
||||||
token.length = (int)(lexer.current - lexer.start);
|
|
||||||
token.line = lexer.line;
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token errorToken(const char *message) {
|
|
||||||
Token token;
|
|
||||||
token.type = TOKEN_ERROR;
|
|
||||||
token.start = message;
|
|
||||||
token.length = (int)strlen(message);
|
|
||||||
token.line = lexer.line;
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void skipWhitespace() {
|
|
||||||
for (;;) {
|
|
||||||
char c = peek();
|
|
||||||
switch (c) {
|
|
||||||
case ' ':
|
|
||||||
case '\r':
|
|
||||||
case '\t':
|
|
||||||
advance();
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
lexer.line++;
|
|
||||||
advance();
|
|
||||||
break;
|
|
||||||
case '/':
|
|
||||||
if (peekNext() == '/') {
|
|
||||||
/* A comment goes until the end of the line. */
|
|
||||||
while (peek() != '\n' && !isAtEnd())
|
|
||||||
advance();
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static TokenType checkKeyword(int start, int length, const char *rest,
|
|
||||||
TokenType type) {
|
|
||||||
if (lexer.current - lexer.start == start + length &&
|
|
||||||
memcmp(lexer.start + start, rest, length) == 0) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TOKEN_IDENTIFIER;
|
|
||||||
}
|
|
||||||
|
|
||||||
static TokenType identifierType() {
|
|
||||||
switch (lexer.start[0]) {
|
|
||||||
case 'a':
|
|
||||||
return checkKeyword(1, 2, "nd", TOKEN_OPERATOR_AND);
|
|
||||||
case 'e':
|
|
||||||
return checkKeyword(1, 3, "lse", TOKEN_KEYWORD_ELSE);
|
|
||||||
case 'f':
|
|
||||||
if (lexer.current - lexer.start > 1) {
|
|
||||||
switch (lexer.start[1]) {
|
|
||||||
case 'a':
|
|
||||||
return checkKeyword(2, 3, "lse", TOKEN_KEYWORD_FALSE);
|
|
||||||
case 'o':
|
|
||||||
return checkKeyword(2, 1, "r", TOKEN_KEYWORD_FOR);
|
|
||||||
}
|
|
||||||
return checkKeyword(1, 1, "n", TOKEN_KEYWORD_FN);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
return checkKeyword(1, 1, "f", TOKEN_KEYWORD_IF);
|
|
||||||
case 'n':
|
|
||||||
return checkKeyword(1, 2, "il", TOKEN_KEYWORD_NIL);
|
|
||||||
case 'o':
|
|
||||||
return checkKeyword(1, 1, "r", TOKEN_OPERATOR_OR);
|
|
||||||
case 'p':
|
|
||||||
if (lexer.current - lexer.start > 1) {
|
|
||||||
switch (lexer.start[1]) {
|
|
||||||
case 'l':
|
|
||||||
return checkKeyword(2, 2, "ex", TOKEN_KEYWORD_PLEX);
|
|
||||||
case 'r':
|
|
||||||
return checkKeyword(2, 3, "int", TOKEN_KEYWORD_PRINT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
return checkKeyword(1, 5, "eturn", TOKEN_KEYWORD_RETURN);
|
|
||||||
case 't':
|
|
||||||
if (lexer.current - lexer.start > 1) {
|
|
||||||
switch (lexer.start[1]) {
|
|
||||||
case 'h':
|
|
||||||
return checkKeyword(2, 2, "is", TOKEN_KEYWORD_THIS);
|
|
||||||
case 'r':
|
|
||||||
return checkKeyword(2, 2, "ue", TOKEN_KEYWORD_TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
return checkKeyword(1, 2, "et", TOKEN_KEYWORD_LET);
|
|
||||||
case 'w':
|
|
||||||
return checkKeyword(1, 4, "hile", TOKEN_KEYWORD_WHILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TOKEN_IDENTIFIER;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token identifier() {
|
|
||||||
while (isAlpha(peek()) || isDigit(peek()))
|
|
||||||
advance();
|
|
||||||
return makeToken(identifierType());
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token number() {
|
|
||||||
while (isDigit(peek()))
|
|
||||||
advance();
|
|
||||||
|
|
||||||
/* Look for a fractional part. */
|
|
||||||
if (peek() == '.' && isDigit(peekNext())) {
|
|
||||||
/* Consume the ".". */
|
|
||||||
advance();
|
|
||||||
|
|
||||||
while (isDigit(peek()))
|
|
||||||
advance();
|
|
||||||
|
|
||||||
return makeToken(TOKEN_FLOAT_LITERAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return makeToken(TOKEN_INT_LITERAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token string() {
|
|
||||||
while (peek() != '"' && !isAtEnd()) {
|
|
||||||
if (peek() == '\n')
|
|
||||||
lexer.line++;
|
|
||||||
advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAtEnd())
|
|
||||||
return errorToken("Unterminated string.");
|
|
||||||
|
|
||||||
/* The closing quote. */
|
|
||||||
advance();
|
|
||||||
return makeToken(TOKEN_STRING_LITERAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
Token nextToken() {
|
|
||||||
skipWhitespace();
|
|
||||||
lexer.start = lexer.current;
|
|
||||||
|
|
||||||
if (isAtEnd())
|
|
||||||
return makeToken(TOKEN_EOF);
|
|
||||||
|
|
||||||
char c = advance();
|
|
||||||
if (isAlpha(c))
|
|
||||||
return identifier();
|
|
||||||
if (isDigit(c))
|
|
||||||
return number();
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
case '(':
|
|
||||||
return makeToken(TOKEN_LPAREN);
|
|
||||||
case ')':
|
|
||||||
return makeToken(TOKEN_RPAREN);
|
|
||||||
case '{':
|
|
||||||
return makeToken(TOKEN_LBRACE);
|
|
||||||
case '}':
|
|
||||||
return makeToken(TOKEN_RBRACE);
|
|
||||||
case ';':
|
|
||||||
return makeToken(TOKEN_SEMICOLON);
|
|
||||||
case ',':
|
|
||||||
return makeToken(TOKEN_COMMA);
|
|
||||||
case '.':
|
|
||||||
return makeToken(TOKEN_DOT);
|
|
||||||
case '-':
|
|
||||||
return makeToken(TOKEN_MINUS);
|
|
||||||
case '+':
|
|
||||||
return makeToken(TOKEN_PLUS);
|
|
||||||
case '/':
|
|
||||||
return makeToken(TOKEN_SLASH);
|
|
||||||
case '*':
|
|
||||||
return makeToken(TOKEN_STAR);
|
|
||||||
case '!':
|
|
||||||
return makeToken(match('=') ? TOKEN_BANG_EQ : TOKEN_BANG);
|
|
||||||
case '=':
|
|
||||||
return makeToken(match('=') ? TOKEN_EQ_EQ : TOKEN_EQ);
|
|
||||||
case '<':
|
|
||||||
return makeToken(match('=') ? TOKEN_LTE : TOKEN_LT);
|
|
||||||
case '>':
|
|
||||||
return makeToken(match('=') ? TOKEN_GTE : TOKEN_GT);
|
|
||||||
case '"':
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
return errorToken("Unexpected character.");
|
|
||||||
}
|
|
70
src/lexer.h
70
src/lexer.h
|
@ -1,70 +0,0 @@
|
||||||
#ifndef zre_lexer_h
|
|
||||||
#define zre_lexer_h
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
TOKEN_EOF,
|
|
||||||
TOKEN_IDENTIFIER,
|
|
||||||
TOKEN_INT_LITERAL,
|
|
||||||
TOKEN_UINT_LITERAL,
|
|
||||||
TOKEN_FLOAT_LITERAL,
|
|
||||||
TOKEN_STRING_LITERAL,
|
|
||||||
TOKEN_TYPE_INT,
|
|
||||||
TOKEN_TYPE_NAT,
|
|
||||||
TOKEN_TYPE_REAL,
|
|
||||||
TOKEN_TYPE_STR,
|
|
||||||
TOKEN_KEYWORD_PLEX,
|
|
||||||
TOKEN_KEYWORD_FN,
|
|
||||||
TOKEN_KEYWORD_LET,
|
|
||||||
TOKEN_KEYWORD_CONST,
|
|
||||||
TOKEN_KEYWORD_IF,
|
|
||||||
TOKEN_KEYWORD_ELSE,
|
|
||||||
TOKEN_KEYWORD_WHILE,
|
|
||||||
TOKEN_KEYWORD_FOR,
|
|
||||||
TOKEN_KEYWORD_RETURN,
|
|
||||||
TOKEN_KEYWORD_USE,
|
|
||||||
TOKEN_KEYWORD_INIT,
|
|
||||||
TOKEN_KEYWORD_THIS,
|
|
||||||
TOKEN_KEYWORD_PRINT,
|
|
||||||
TOKEN_KEYWORD_NIL,
|
|
||||||
TOKEN_KEYWORD_TRUE,
|
|
||||||
TOKEN_KEYWORD_FALSE,
|
|
||||||
TOKEN_OPERATOR_IS,
|
|
||||||
TOKEN_OPERATOR_NOT,
|
|
||||||
TOKEN_OPERATOR_AND,
|
|
||||||
TOKEN_OPERATOR_OR,
|
|
||||||
TOKEN_BANG,
|
|
||||||
TOKEN_BANG_EQ,
|
|
||||||
TOKEN_EQ,
|
|
||||||
TOKEN_EQ_EQ,
|
|
||||||
TOKEN_GT,
|
|
||||||
TOKEN_LT,
|
|
||||||
TOKEN_GTE,
|
|
||||||
TOKEN_LTE,
|
|
||||||
TOKEN_DOT,
|
|
||||||
TOKEN_COMMA,
|
|
||||||
TOKEN_COLON,
|
|
||||||
TOKEN_SEMICOLON,
|
|
||||||
TOKEN_PLUS,
|
|
||||||
TOKEN_MINUS,
|
|
||||||
TOKEN_STAR,
|
|
||||||
TOKEN_SLASH,
|
|
||||||
TOKEN_LPAREN,
|
|
||||||
TOKEN_RPAREN,
|
|
||||||
TOKEN_LBRACE,
|
|
||||||
TOKEN_RBRACE,
|
|
||||||
TOKEN_LBRACKET,
|
|
||||||
TOKEN_RBRACKET,
|
|
||||||
TOKEN_ERROR
|
|
||||||
} TokenType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
TokenType type;
|
|
||||||
const char *start;
|
|
||||||
int length;
|
|
||||||
int line;
|
|
||||||
} Token;
|
|
||||||
|
|
||||||
void initLexer(const char *source);
|
|
||||||
Token nextToken();
|
|
||||||
|
|
||||||
#endif
|
|
132
src/opcodes.h
132
src/opcodes.h
|
@ -1,83 +1,79 @@
|
||||||
#ifndef ZRL_OPCODES_H
|
#ifndef ZRE_OPCODES_H
|
||||||
#define ZRL_OPCODES_H
|
#define ZRE_OPCODES_H
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "fixed.h"
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OP_HALT, /* halt : terminate execution */
|
OP_HALT, /* halt : terminate execution */
|
||||||
OP_JMP, /* jump : jump to address dest unconditionally */
|
OP_JMP, /* jump : jump to address dest unconditionally */
|
||||||
OP_GET_PC, /* pc : dest = current program counter */
|
OP_GET_PC, /* pc : dest = current program counter */
|
||||||
OP_CALL, /* call : creates a new frame */
|
OP_CALL, /* call : creates a new frame */
|
||||||
OP_RETURN, /* retn : returns from a frame to the parent frame */
|
OP_RETURN, /* retn : returns from a frame to the parent frame */
|
||||||
OP_LOAD, /* load : dest = &[next code location] */
|
OP_LOAD, /* load : dest = &[next memory location] */
|
||||||
OP_STORE, /* stor : next code 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_GET_ACC, /* gacc : dest = accumulator */
|
OP_GET_ACC, /* gacc : dest = accumulator */
|
||||||
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 */
|
||||||
OP_OFFSET, /* offs : dest = ptr + src1 : dest = p + o */
|
OP_OFFSET, /* offs : dest = ptr + src1 : dest = p + o */
|
||||||
OP_SYSCALL, /* sysc : */
|
OP_SYSCALL, /* sysc : */
|
||||||
OP_ADD_INT, /* addi : dest = src1 + src2 */
|
OP_ADD_INT, /* addi : dest = src1 + src2 */
|
||||||
OP_SUB_INT, /* subi : dest = src1 - src2 */
|
OP_SUB_INT, /* subi : dest = src1 - src2 */
|
||||||
OP_MUL_INT, /* muli : dest = src1 * src2 */
|
OP_MUL_INT, /* muli : dest = src1 * src2 */
|
||||||
OP_DIV_INT, /* divi : dest = src1 / src2 */
|
OP_DIV_INT, /* divi : dest = src1 / src2 */
|
||||||
OP_ADD_UINT, /* addu : dest = src1 + src2 */
|
OP_ADD_UINT, /* addu : dest = src1 + src2 */
|
||||||
OP_SUB_UINT, /* subu : dest = src1 - src2 */
|
OP_SUB_UINT, /* subu : dest = src1 - src2 */
|
||||||
OP_MUL_UINT, /* mulu : dest = src1 * src2 */
|
OP_MUL_UINT, /* mulu : dest = src1 * src2 */
|
||||||
OP_DIV_UINT, /* divu : dest = src1 / src2 */
|
OP_DIV_UINT, /* divu : dest = src1 / src2 */
|
||||||
OP_ADD_REAL, /* addr : dest = src1 + src2 */
|
OP_ADD_REAL, /* addr : dest = src1 + src2 */
|
||||||
OP_SUB_REAL, /* subr : dest = src1 - src2 */
|
OP_SUB_REAL, /* subr : dest = src1 - src2 */
|
||||||
OP_MUL_REAL, /* mulr : dest = src1 * src2 */
|
OP_MUL_REAL, /* mulr : dest = src1 * src2 */
|
||||||
OP_DIV_REAL, /* divr : dest = src1 / src2 */
|
OP_DIV_REAL, /* divr : dest = src1 / src2 */
|
||||||
OP_INT_TO_REAL, /* itor : dest = src1 as real */
|
OP_INT_TO_REAL, /* itor : dest = src1 as real */
|
||||||
OP_UINT_TO_REAL, /* utor : dest = src1 as real */
|
OP_UINT_TO_REAL, /* utor : dest = src1 as real */
|
||||||
OP_REAL_TO_INT, /* rtoi : dest = src1 as int */
|
OP_REAL_TO_INT, /* rtoi : dest = src1 as int */
|
||||||
OP_REAL_TO_UINT, /* rtou : dest = src1 as uint */
|
OP_REAL_TO_UINT, /* rtou : dest = src1 as uint */
|
||||||
OP_JEQ_INT, /* jeqi : jump to address dest if src1 as int == src2 as int */
|
OP_JEQ_INT, /* jeqi : jump to address dest if src1 as int == src2 as int */
|
||||||
OP_JGT_INT, /* jgti : jump to address dest if src1 as int > src2 as int*/
|
OP_JGT_INT, /* jgti : jump to address dest if src1 as int > src2 as int*/
|
||||||
OP_JLT_INT, /* jlti : jump to address dest if src1 as int < src2 as int */
|
OP_JLT_INT, /* jlti : jump to address dest if src1 as int < src2 as int */
|
||||||
OP_JLE_INT, /* jlei : jump to address dest if src1 as int <= src2 as int */
|
OP_JLE_INT, /* jlei : jump to address dest if src1 as int <= src2 as int */
|
||||||
OP_JGE_INT, /* jgei : jump to address dest if src1 as int >= src2 as int*/
|
OP_JGE_INT, /* jgei : jump to address dest if src1 as int >= src2 as int*/
|
||||||
OP_JEQ_UINT, /* jequ : jump to address dest if src1 as int == src2 as uint */
|
OP_JEQ_UINT, /* jequ : jump to address dest if src1 as int == src2 as uint */
|
||||||
OP_JGT_UINT, /* jgtu : jump to address dest if src1 as int > src2 as uint*/
|
OP_JGT_UINT, /* jgtu : jump to address dest if src1 as int > src2 as uint*/
|
||||||
OP_JLT_UINT, /* jltu : jump to address dest if src1 as int < src2 as uint */
|
OP_JLT_UINT, /* jltu : jump to address dest if src1 as int < src2 as uint */
|
||||||
OP_JLE_UINT, /* jleu : jump to address dest if src1 as int <= src2 as uint */
|
OP_JLE_UINT, /* jleu : jump to address dest if src1 as int <= src2 as uint */
|
||||||
OP_JGE_UINT, /* jgeu : jump to address dest if src1 as int >= src2 as uint*/
|
OP_JGE_UINT, /* jgeu : jump to address dest if src1 as int >= src2 as uint*/
|
||||||
OP_JEQ_REAL, /* jeqr : jump to address dest if src1 as real == src2 as real */
|
OP_JEQ_REAL, /* jeqr : jump to address dest if src1 as real == src2 as real */
|
||||||
OP_JGE_REAL, /* jgtr : jump to address dest if src1 as real >= src2 as real */
|
OP_JGE_REAL, /* jgtr : jump to address dest if src1 as real >= src2 as real */
|
||||||
OP_JGT_REAL, /* jltr : jump to address dest if src1 as real > src2 as real */
|
OP_JGT_REAL, /* jltr : jump to address dest if src1 as real > src2 as real */
|
||||||
OP_JLT_REAL, /* jler : jump to address dest if src1 as real < src2 as real */
|
OP_JLT_REAL, /* jler : jump to address dest if src1 as real < src2 as real */
|
||||||
OP_JLE_REAL, /* jger : jump to address dest if src1 as real <= src2 as real */
|
OP_JLE_REAL, /* jger : jump to address dest if src1 as real <= src2 as real */
|
||||||
OP_INT_TO_STRING, /* itos : dest = src1 as str */
|
OP_INT_TO_STRING, /* itos : dest = src1 as str */
|
||||||
OP_UINT_TO_STRING,/* utos : dest = src1 as str */
|
OP_UINT_TO_STRING, /* utos : dest = src1 as str */
|
||||||
OP_REAL_TO_STRING,/* rtos : dest = src1 as str */
|
OP_REAL_TO_STRING, /* rtos : dest = src1 as str */
|
||||||
OP_CMP_STRING, /* cmps : dest = (str == src2) as bool */
|
OP_CMP_STRING, /* cmps : dest = (str == src2) as bool */
|
||||||
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 (replace with device), just for testing for now */
|
/* to remove (replace with device), just for testing for now */
|
||||||
OP_DBG_PRINT_STRING,/* puts : write src1 as str to stdout */
|
OP_DBG_PRINT_STRING, /* puts : write a string to stdout */
|
||||||
OP_DBG_READ_STRING, /* gets : read to dest as str from stdin */
|
OP_DBG_READ_STRING, /* gets : read a string from stdin */
|
||||||
} Opcode;
|
} Opcode;
|
||||||
|
|
||||||
/* defines a uint32 opcode */
|
/* defines a uint32 opcode */
|
||||||
#define OP(opcode, dest, src1, src2) \
|
#define OP(opcode, dest, src1, src2) \
|
||||||
((opcode << 24) | (dest << 16) | (src1 << 8) | src2)
|
((opcode << 24) | (dest << 16) | (src1 << 8) | (src2))
|
||||||
|
|
||||||
#define OP_SYSCALL_OPCODE(syscall_id, arg_count, src) \
|
|
||||||
((OP_SYSCALL << 24) | ((syscall_id & 0xFF) << 16) | (arg_count & 0xFF) | src)
|
|
||||||
|
|
||||||
typedef union value_u {
|
typedef union value_u {
|
||||||
int32_t i; /* Integers */
|
int32_t i; /* Integers */
|
||||||
fixed_t f; /* Fixed point */
|
float f; /* Float */
|
||||||
uint32_t u; /* Unsigned integers, also used for pointer address */
|
uint32_t u; /* Unsigned integers, also used for pointer address */
|
||||||
char c[4]; /* 4 Byte char array for string packing */
|
char c[4]; /* 4 Byte char array for string packing */
|
||||||
} Value;
|
} Value;
|
||||||
|
@ -124,7 +120,7 @@ typedef struct device_s {
|
||||||
uint32_t flags; /* permissions, status, etc. */
|
uint32_t flags; /* permissions, status, etc. */
|
||||||
} Device;
|
} Device;
|
||||||
|
|
||||||
#define MEMORY_SIZE ((640 * 480) + 65536)
|
#define MEMORY_SIZE (640 * 480 + 65536)
|
||||||
#define CODE_SIZE 8192
|
#define CODE_SIZE 8192
|
||||||
#define FRAMES_SIZE 128
|
#define FRAMES_SIZE 128
|
||||||
#define STACK_SIZE 256
|
#define STACK_SIZE 256
|
||||||
|
|
65
src/test.c
65
src/test.c
|
@ -2,13 +2,34 @@
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
#include <string.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++;
|
||||||
|
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++;
|
||||||
|
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++;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
/* Array of test mappings */
|
/* Array of test mappings */
|
||||||
struct TestMapping internal_tests[] = {
|
struct TestMapping internal_tests[] = {
|
||||||
{"simple.zrl", test_add_compile},
|
{"simple.ul", test_simple_compile},
|
||||||
{"loop.zrl", test_loop_compile},
|
{"loop.ul", test_loop_compile},
|
||||||
{"add.zrl", test_add_function_compile},
|
{"add.ul", test_add_function_compile},
|
||||||
{"fib.zrl", test_recursive_function_compile},
|
{"fib.ul", test_recursive_function_compile},
|
||||||
{"window.zrl", test_window_click_compile},
|
{"window.ul", test_window_click_compile},
|
||||||
/* Add more test mappings here */
|
/* Add more test mappings here */
|
||||||
{NULL, NULL} /* Sentinel to mark end of array */
|
{NULL, NULL} /* Sentinel to mark end of array */
|
||||||
};
|
};
|
||||||
|
@ -23,10 +44,10 @@ bool compile_internal_test(const char* filename, VM* vm) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_add_compile(VM *vm) {
|
bool test_simple_compile(VM *vm) {
|
||||||
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0);
|
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 = nat_alloc(vm, 1);
|
||||||
vm->code[vm->cp++].u = OP(OP_LOAD, 1, 1, 0);
|
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 = 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_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_UINT_TO_STRING, 3, 2, 0);
|
||||||
|
@ -39,22 +60,23 @@ 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 = 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 = 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 = OP(OP_LOAD, 1, 0, 0); /* do (i = 50000, 0, -1) { */
|
||||||
vm->code[vm->cp++].u = int_alloc(vm, 50000);
|
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 = OP(OP_LOAD, 2, 0, 0); /* loop check value */
|
||||||
vm->code[vm->cp++].u = int_alloc(vm, 0);
|
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 = OP(OP_LOAD, 3, 0, 0); /* loop incriment value */
|
||||||
vm->code[vm->cp++].u = int_alloc(vm, -1);
|
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 */
|
vm->code[vm->cp++].u = OP(OP_LOAD, 4, 0, 0); /* loop start */
|
||||||
uint32_t addr = vm->cp + 1;
|
uint32_t addr = vm->cp + 1;
|
||||||
vm->code[vm->cp++].u = int_alloc(vm, addr);
|
vm->code[vm->cp++].u = int_alloc(vm, addr);
|
||||||
vm->code[vm->cp++].u = OP(OP_LOAD, 5, 0, 0);
|
|
||||||
vm->code[vm->cp++].u = real_alloc(vm, 5.0f);
|
|
||||||
vm->code[vm->cp++].u = OP(OP_ADD_REAL, 0, 0, 5); /* a += 5.0; */
|
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_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_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_REAL_TO_UINT, 1, 0, 0); /* let b = a as nat; */
|
||||||
vm->code[vm->cp++].u = OP(OP_LOAD, 6, 0, 0);
|
vm->code[vm->cp++].u = OP(OP_LOAD, 6, 0, 0);
|
||||||
vm->code[vm->cp++].u = str_alloc(vm, "Enter a string:", 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_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_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_UINT_TO_STRING, 3, 1, 0);
|
||||||
|
@ -69,11 +91,10 @@ bool test_loop_compile(VM *vm) {
|
||||||
bool test_add_function_compile(VM *vm) {
|
bool test_add_function_compile(VM *vm) {
|
||||||
/* fn main() */
|
/* fn main() */
|
||||||
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0); /* 1 */
|
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0); /* 1 */
|
||||||
uint32_t addr = int_alloc(vm, 1);
|
vm->code[vm->cp++].u = int_alloc(vm, 1);
|
||||||
vm->code[vm->cp++].u = addr;
|
|
||||||
vm->code[vm->cp++].u = OP(OP_PUSH, 0, 0, 0);
|
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 = OP(OP_LOAD, 0, 0, 0); /* 1 */
|
||||||
vm->code[vm->cp++].u = addr;
|
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_PUSH, 0, 0, 0);
|
||||||
vm->code[vm->cp++].u = OP(OP_CALL, 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 = 12;
|
||||||
|
@ -136,7 +157,7 @@ bool test_window_click_compile(VM *vm) {
|
||||||
|
|
||||||
/* Open screen device: R0=path, R1=mode */
|
/* 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 = OP(OP_LOAD, 0, 0, 0); /* R0 = screen path */
|
||||||
vm->code[vm->cp++].u = screen_path_addr;
|
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 = OP(OP_LOAD, 1, 0, 0); /* R1 = mode (0) */
|
||||||
vm->code[vm->cp++].u = int_alloc(vm, 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++].u = OP(OP_SYSCALL, SYSCALL_DEVICE_OPEN, 0, 2); /* syscall_id, first_reg=0, arg_count=2 */
|
||||||
|
@ -146,22 +167,26 @@ bool test_window_click_compile(VM *vm) {
|
||||||
|
|
||||||
/* Create simple test pixel data */
|
/* Create simple test pixel data */
|
||||||
uint32_t test_pixel_addr = vm->mp;
|
uint32_t test_pixel_addr = vm->mp;
|
||||||
vm->memory[vm->mp++].u = 0x00FF0000;
|
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++;
|
||||||
|
|
||||||
/* Main loop to check for mouse input */
|
/* Main loop to check for mouse input */
|
||||||
uint32_t loop_start = vm->cp;
|
uint32_t loop_start = vm->cp;
|
||||||
|
|
||||||
/* Write to screen: R0=path, R1=buffer, R2=size */
|
/* 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 = OP(OP_LOAD, 0, 0, 0);
|
||||||
vm->code[vm->cp++].u = screen_path_addr;
|
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 = OP(OP_LOAD, 1, 0, 0);
|
||||||
vm->code[vm->cp++].u = test_pixel_addr;
|
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 = OP(OP_LOAD, 2, 0, 0);
|
||||||
vm->code[vm->cp++].u = int_alloc(vm, 1);
|
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 */
|
vm->code[vm->cp++].u = OP(OP_SYSCALL, SYSCALL_DEVICE_WRITE, 0, 3); /* syscall_id, first_reg=0, arg_count=3 */
|
||||||
|
|
||||||
vm->code[vm->cp++].u = OP(OP_LOAD, 3, 0, 0);
|
vm->code[vm->cp++].u = OP(OP_LOAD, 3, 0, 0);
|
||||||
vm->code[vm->cp++].u = loop_start;
|
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_JMP, 3, 0, 0); /* Infinite loop for testing */
|
||||||
vm->code[vm->cp++].u = OP(OP_HALT, 0, 0, 0);
|
vm->code[vm->cp++].u = OP(OP_HALT, 0, 0, 0);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef ZRL_TEST_H
|
#ifndef ZRE_TEST_H
|
||||||
#define ZRL_TEST_H
|
#define ZRE_TEST_H
|
||||||
|
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ struct TestMapping {
|
||||||
};
|
};
|
||||||
|
|
||||||
bool compile_internal_test(const char* filename, VM* vm);
|
bool compile_internal_test(const char* filename, VM* vm);
|
||||||
bool test_add_compile (VM *vm);
|
bool test_simple_compile (VM *vm);
|
||||||
bool test_loop_compile (VM *vm);
|
bool test_loop_compile (VM *vm);
|
||||||
bool test_add_function_compile(VM *vm);
|
bool test_add_function_compile(VM *vm);
|
||||||
bool test_recursive_function_compile(VM *vm);
|
bool test_recursive_function_compile(VM *vm);
|
||||||
|
|
363
src/vm.c
363
src/vm.c
|
@ -1,8 +1,7 @@
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "fixed.h"
|
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
#include <stdint.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* no inline fn in ANSI C :( */
|
/* no inline fn in ANSI C :( */
|
||||||
#define COMPARE_AND_JUMP(type, accessor, op) \
|
#define COMPARE_AND_JUMP(type, accessor, op) \
|
||||||
|
@ -14,6 +13,17 @@
|
||||||
return true; \
|
return true; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define MATH_OP(accessor, op) \
|
||||||
|
do { \
|
||||||
|
vm->frames[vm->fp].registers[dest].accessor = \
|
||||||
|
vm->frames[vm->fp] \
|
||||||
|
.registers[src1] \
|
||||||
|
.accessor op vm->frames[vm->fp] \
|
||||||
|
.registers[src2] \
|
||||||
|
.accessor; \
|
||||||
|
return true; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Embeds a string into the VM
|
* Embeds a string into the VM
|
||||||
*/
|
*/
|
||||||
|
@ -40,125 +50,10 @@ uint32_t str_alloc(VM *vm, const char *str, uint32_t length) {
|
||||||
|
|
||||||
vm->memory[str_addr].u = length;
|
vm->memory[str_addr].u = length;
|
||||||
vm->frames[vm->fp].allocated.end = vm->mp;
|
vm->frames[vm->fp].allocated.end = vm->mp;
|
||||||
|
|
||||||
return str_addr;
|
return str_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t float_as_real_alloc(VM *vm, float v) {
|
|
||||||
uint32_t addr = vm->mp;
|
|
||||||
vm->memory[vm->mp++].f = float_to_fixed(v);
|
|
||||||
vm->frames[vm->fp].allocated.end++;
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t real_alloc(VM *vm, fixed_t v) {
|
|
||||||
uint32_t addr = vm->mp;
|
|
||||||
vm->memory[vm->mp++].f = v;
|
|
||||||
vm->frames[vm->fp].allocated.end++;
|
|
||||||
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++;
|
|
||||||
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++;
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_LEN_INT32 12 /* -2147483648 plus null terminator */
|
|
||||||
#define MAX_LEN_UINT32 11 /* 4294967295 plus null terminator */
|
|
||||||
#define MAX_LEN_FIXED 20 /* Enough for fixed-point representation */
|
|
||||||
const char radix_set[11] = "0123456789";
|
|
||||||
|
|
||||||
/* Convert int32 to string */
|
|
||||||
uint32_t int_to_string(VM *vm, int32_t v) {
|
|
||||||
char buffer[MAX_LEN_INT32] = {0};
|
|
||||||
int32_t n = v;
|
|
||||||
bool neg = n < 0;
|
|
||||||
if (neg)
|
|
||||||
n = -n;
|
|
||||||
int i = MAX_LEN_INT32;
|
|
||||||
do {
|
|
||||||
buffer[--i] = radix_set[n % 10];
|
|
||||||
n /= 10;
|
|
||||||
} while (n > 0);
|
|
||||||
if (neg)
|
|
||||||
buffer[--i] = '-';
|
|
||||||
/* Ensure at least one digit is written for 0 */
|
|
||||||
if (v == 0)
|
|
||||||
buffer[--i] = '0';
|
|
||||||
|
|
||||||
uint32_t len = MAX_LEN_INT32 - i;
|
|
||||||
return str_alloc(vm, buffer + i, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert uint32 to string */
|
|
||||||
uint32_t nat_to_string(VM *vm, uint32_t v) {
|
|
||||||
char buffer[MAX_LEN_INT32] = {0};
|
|
||||||
uint32_t n = v;
|
|
||||||
int i = MAX_LEN_INT32;
|
|
||||||
do {
|
|
||||||
buffer[--i] = radix_set[n % 10];
|
|
||||||
n /= 10;
|
|
||||||
} while (n > 0);
|
|
||||||
/* Ensure at least one digit is written for 0 */
|
|
||||||
if (v == 0)
|
|
||||||
buffer[--i] = '0';
|
|
||||||
|
|
||||||
uint32_t len = MAX_LEN_INT32 - i;
|
|
||||||
return str_alloc(vm, buffer + i, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert fixed-point to string */
|
|
||||||
uint32_t real_to_string(VM *vm, fixed_t q) {
|
|
||||||
char buffer[MAX_LEN_FIXED] = {0};
|
|
||||||
|
|
||||||
|
|
||||||
/* Extract integer part (top 16 bits) */
|
|
||||||
int32_t int_part = q >> 16;
|
|
||||||
/* Extract fractional part (bottom 16 bits) */
|
|
||||||
int32_t frac_part = q & 0xFFFF;
|
|
||||||
|
|
||||||
|
|
||||||
int32_t n = frac_part;
|
|
||||||
bool neg = n < 0;
|
|
||||||
if (neg)
|
|
||||||
n = -n;
|
|
||||||
int i = MAX_LEN_FIXED;
|
|
||||||
do {
|
|
||||||
buffer[--i] = radix_set[n % 10];
|
|
||||||
n /= 10;
|
|
||||||
} while (n > 0);
|
|
||||||
if (neg)
|
|
||||||
buffer[--i] = '-';
|
|
||||||
/* Ensure at least one digit is written for 0 */
|
|
||||||
if (frac_part == 0)
|
|
||||||
buffer[--i] = '0';
|
|
||||||
|
|
||||||
|
|
||||||
/* Convert integer part to string (reverse order) */
|
|
||||||
do {
|
|
||||||
buffer[--i] = radix_set[int_part % 10];
|
|
||||||
int_part /= 10;
|
|
||||||
} while (int_part > 0);
|
|
||||||
/* Ensure at least one digit is written for 0 */
|
|
||||||
if (int_part == 0)
|
|
||||||
buffer[--i] = '0';
|
|
||||||
|
|
||||||
/* Null-terminate */
|
|
||||||
buffer[i] = '\0';
|
|
||||||
|
|
||||||
int32_t len = (MAX_LEN_INT32 - i);
|
|
||||||
printf("i=%d, len=%d", i, len);
|
|
||||||
return str_alloc(vm, buffer + i, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Step to the next opcode in the vm.
|
* Step to the next opcode in the vm.
|
||||||
*/
|
*/
|
||||||
|
@ -221,13 +116,11 @@ bool step_vm(VM *vm) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_PUSH: {
|
case OP_PUSH: {
|
||||||
Value v = vm->frames[vm->fp].registers[dest];
|
vm->stack[++vm->sp] = vm->frames[vm->fp].registers[dest];
|
||||||
vm->stack[++vm->sp] = v;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_POP: {
|
case OP_POP: {
|
||||||
Value v = vm->stack[vm->sp--];
|
vm->frames[vm->fp].registers[dest] = vm->stack[vm->sp--];
|
||||||
vm->frames[vm->fp].registers[dest] = v;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_MEM_ALLOC: {
|
case OP_MEM_ALLOC: {
|
||||||
|
@ -255,6 +148,31 @@ bool step_vm(VM *vm) {
|
||||||
vm->memory[dest_addr].u ^= vm->memory[src_addr].u;
|
vm->memory[dest_addr].u ^= vm->memory[src_addr].u;
|
||||||
return true;
|
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;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case OP_JMP: {
|
||||||
|
vm->pc = vm->frames[vm->fp].registers[dest].u; /* Jump to address */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
case OP_SYSCALL: {
|
case OP_SYSCALL: {
|
||||||
uint32_t syscall_id = dest;
|
uint32_t syscall_id = dest;
|
||||||
uint32_t arg_count = src2;
|
uint32_t arg_count = src2;
|
||||||
|
@ -406,123 +324,48 @@ bool step_vm(VM *vm) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_ADD_INT: {
|
case OP_ADD_INT:
|
||||||
vm->frames[vm->fp].registers[dest].i =
|
MATH_OP(i, +);
|
||||||
vm->frames[vm->fp].registers[src1].i +
|
case OP_SUB_INT:
|
||||||
vm->frames[vm->fp].registers[src2].i;
|
MATH_OP(i, -);
|
||||||
return true;
|
case OP_MUL_INT:
|
||||||
}
|
MATH_OP(i, *);
|
||||||
case OP_SUB_INT:{
|
case OP_DIV_INT:
|
||||||
vm->frames[vm->fp].registers[dest].i =
|
MATH_OP(i, /);
|
||||||
vm->frames[vm->fp].registers[src1].i -
|
case OP_ADD_UINT:
|
||||||
vm->frames[vm->fp].registers[src2].i;
|
MATH_OP(u, +);
|
||||||
return true;
|
case OP_SUB_UINT:
|
||||||
}
|
MATH_OP(u, -);
|
||||||
case OP_MUL_INT:{
|
case OP_MUL_UINT:
|
||||||
vm->frames[vm->fp].registers[dest].i =
|
MATH_OP(u, *);
|
||||||
vm->frames[vm->fp].registers[src1].i *
|
case OP_DIV_UINT:
|
||||||
vm->frames[vm->fp].registers[src2].i;
|
MATH_OP(u, /);
|
||||||
return true;
|
case OP_ADD_REAL:
|
||||||
}
|
MATH_OP(f, +);
|
||||||
case OP_DIV_INT:{
|
case OP_SUB_REAL:
|
||||||
vm->frames[vm->fp].registers[dest].i =
|
MATH_OP(f, -);
|
||||||
vm->frames[vm->fp].registers[src1].i /
|
case OP_MUL_REAL:
|
||||||
vm->frames[vm->fp].registers[src2].i;
|
MATH_OP(f, *);
|
||||||
return true;
|
case OP_DIV_REAL:
|
||||||
}
|
MATH_OP(f, /);
|
||||||
case OP_ADD_UINT:{
|
|
||||||
vm->frames[vm->fp].registers[dest].u =
|
|
||||||
vm->frames[vm->fp].registers[src1].u +
|
|
||||||
vm->frames[vm->fp].registers[src2].u;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case OP_SUB_UINT:{
|
|
||||||
vm->frames[vm->fp].registers[dest].u =
|
|
||||||
vm->frames[vm->fp].registers[src1].u -
|
|
||||||
vm->frames[vm->fp].registers[src2].u;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case OP_MUL_UINT:{
|
|
||||||
vm->frames[vm->fp].registers[dest].u =
|
|
||||||
vm->frames[vm->fp].registers[src1].u *
|
|
||||||
vm->frames[vm->fp].registers[src2].u;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case OP_DIV_UINT:{
|
|
||||||
vm->frames[vm->fp].registers[dest].u =
|
|
||||||
vm->frames[vm->fp].registers[src1].u /
|
|
||||||
vm->frames[vm->fp].registers[src2].u;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case OP_ADD_REAL:{
|
|
||||||
vm->frames[vm->fp].registers[dest].f =
|
|
||||||
fixed_add(vm->frames[vm->fp].registers[src1].f,
|
|
||||||
vm->frames[vm->fp].registers[src2].f);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case OP_SUB_REAL:{
|
|
||||||
vm->frames[vm->fp].registers[dest].f =
|
|
||||||
fixed_sub(vm->frames[vm->fp].registers[src1].f,
|
|
||||||
vm->frames[vm->fp].registers[src2].f);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case OP_MUL_REAL: {
|
|
||||||
vm->frames[vm->fp].registers[dest].f =
|
|
||||||
fixed_mul(vm->frames[vm->fp].registers[src1].f,
|
|
||||||
vm->frames[vm->fp].registers[src2].f);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case OP_DIV_REAL: {
|
|
||||||
vm->frames[vm->fp].registers[dest].f =
|
|
||||||
fixed_div(vm->frames[vm->fp].registers[src1].f,
|
|
||||||
vm->frames[vm->fp].registers[src2].f);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case OP_REAL_TO_INT: {
|
case OP_REAL_TO_INT: {
|
||||||
vm->frames[vm->fp].registers[dest].i =
|
vm->frames[vm->fp].registers[dest].i =
|
||||||
fixed_to_int(vm->frames[vm->fp].registers[src1].f);
|
(int32_t)(vm->frames[vm->fp].registers[src1].f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_INT_TO_REAL: {
|
case OP_INT_TO_REAL: {
|
||||||
vm->frames[vm->fp].registers[dest].f =
|
vm->frames[vm->fp].registers[dest].f =
|
||||||
int_to_fixed(vm->frames[vm->fp].registers[src1].i);
|
(float)(vm->frames[vm->fp].registers[src1].i);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_REAL_TO_UINT: {
|
case OP_REAL_TO_UINT: {
|
||||||
fixed_t f = vm->frames[vm->fp].registers[src1].f;
|
|
||||||
int32_t i = fixed_to_int(f);
|
|
||||||
vm->frames[vm->fp].registers[dest].u =
|
vm->frames[vm->fp].registers[dest].u =
|
||||||
(uint32_t)i;
|
(uint32_t)(vm->frames[vm->fp].registers[src1].f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_UINT_TO_REAL: {
|
case OP_UINT_TO_REAL: {
|
||||||
vm->frames[vm->fp].registers[dest].f =
|
vm->frames[vm->fp].registers[dest].f =
|
||||||
int_to_fixed(vm->frames[vm->fp].registers[src1].u);
|
(float)(vm->frames[vm->fp].registers[src1].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_JMP: {
|
|
||||||
vm->pc = vm->frames[vm->fp].registers[dest].u; /* Jump to address */
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case OP_GET_PC: {
|
|
||||||
vm->frames[vm->fp].registers[dest].u = vm->pc;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_JEQ_UINT: {
|
case OP_JEQ_UINT: {
|
||||||
|
@ -559,51 +402,63 @@ bool step_vm(VM *vm) {
|
||||||
COMPARE_AND_JUMP(int32_t, i, ==);
|
COMPARE_AND_JUMP(int32_t, i, ==);
|
||||||
}
|
}
|
||||||
case OP_JGT_REAL: {
|
case OP_JGT_REAL: {
|
||||||
fixed_t value = vm->frames[vm->fp].registers[src1].f;
|
COMPARE_AND_JUMP(float, u, >);
|
||||||
fixed_t value2 = vm->frames[vm->fp].registers[src2].f;
|
|
||||||
vm->pc =
|
|
||||||
fixed_gt(value, value2) ? vm->frames[vm->fp].registers[dest].u : vm->pc;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
case OP_JLT_REAL: {
|
case OP_JLT_REAL: {
|
||||||
fixed_t value = vm->frames[vm->fp].registers[src1].f;
|
COMPARE_AND_JUMP(float, u, <);
|
||||||
fixed_t value2 = vm->frames[vm->fp].registers[src2].f;
|
|
||||||
vm->pc =
|
|
||||||
fixed_lt(value, value2) ? vm->frames[vm->fp].registers[dest].u : vm->pc;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
case OP_JGE_REAL: {
|
case OP_JGE_REAL: {
|
||||||
fixed_t value = vm->frames[vm->fp].registers[src1].f;
|
COMPARE_AND_JUMP(float, u, >=);
|
||||||
fixed_t value2 = vm->frames[vm->fp].registers[src2].f;
|
|
||||||
vm->pc =
|
|
||||||
fixed_ge(value, value2) ? vm->frames[vm->fp].registers[dest].u : vm->pc;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
case OP_JLE_REAL: {
|
case OP_JLE_REAL: {
|
||||||
fixed_t value = vm->frames[vm->fp].registers[src1].f;
|
COMPARE_AND_JUMP(float, u, <=);
|
||||||
fixed_t value2 = vm->frames[vm->fp].registers[src2].f;
|
|
||||||
vm->pc =
|
|
||||||
fixed_le(value, value2) ? vm->frames[vm->fp].registers[dest].u : vm->pc;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
case OP_INT_TO_STRING: {
|
case OP_INT_TO_STRING: {
|
||||||
int32_t a = vm->frames[vm->fp].registers[src1].i;
|
int32_t a = (int32_t)vm->frames[vm->fp].registers[src1].i; /* get value */
|
||||||
uint32_t ptr = int_to_string(vm, a);
|
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;
|
vm->frames[vm->fp].registers[dest].u = ptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_UINT_TO_STRING: {
|
case OP_UINT_TO_STRING: {
|
||||||
uint32_t a = vm->frames[vm->fp].registers[src1].u;
|
uint32_t a = (uint32_t)vm->frames[vm->fp].registers[src1].u; /* get value */
|
||||||
uint32_t ptr = nat_to_string(vm, a);
|
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;
|
vm->frames[vm->fp].registers[dest].u = ptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_REAL_TO_STRING: {
|
case OP_REAL_TO_STRING: {
|
||||||
fixed_t a = vm->frames[vm->fp].registers[src1].f;
|
float a = (float)vm->frames[vm->fp].registers[src1].f; /* get value */
|
||||||
uint32_t ptr = real_to_string(vm, a);
|
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;
|
vm->frames[vm->fp].registers[dest].u = ptr;
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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: {
|
case OP_DBG_READ_STRING: {
|
||||||
uint32_t str_addr = vm->mp++;
|
uint32_t str_addr = vm->mp++;
|
||||||
uint32_t length = 0;
|
uint32_t length = 0;
|
||||||
|
@ -633,7 +488,7 @@ bool step_vm(VM *vm) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_DBG_PRINT_STRING: {
|
case OP_DBG_PRINT_STRING: {
|
||||||
uint32_t ptr = vm->frames[vm->fp].registers[src1].u;
|
uint32_t ptr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
||||||
uint32_t length = vm->memory[ptr].u;
|
uint32_t length = vm->memory[ptr].u;
|
||||||
uint32_t str_src = ptr + 1;
|
uint32_t str_src = ptr + 1;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
@ -647,8 +502,8 @@ bool step_vm(VM *vm) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case OP_CMP_STRING: {
|
case OP_CMP_STRING: {
|
||||||
uint32_t addr1 = vm->frames[vm->fp].registers[src1].u;
|
uint32_t addr1 = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
||||||
uint32_t addr2 = vm->frames[vm->fp].registers[src2].u;
|
uint32_t addr2 = (uint32_t)vm->frames[vm->fp].registers[src2].u;
|
||||||
uint32_t length1 = vm->memory[addr1 - 1].u;
|
uint32_t length1 = vm->memory[addr1 - 1].u;
|
||||||
uint32_t length2 = vm->memory[addr2 - 1].u;
|
uint32_t length2 = vm->memory[addr2 - 1].u;
|
||||||
uint32_t equal =
|
uint32_t equal =
|
||||||
|
|
9
src/vm.h
9
src/vm.h
|
@ -1,14 +1,9 @@
|
||||||
#ifndef ZRL_VM_H
|
#ifndef ZRE_VM_H
|
||||||
#define ZRL_VM_H
|
#define ZRE_VM_H
|
||||||
|
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
|
|
||||||
VM* init_vm();
|
|
||||||
bool step_vm(VM *vm);
|
bool step_vm(VM *vm);
|
||||||
uint32_t str_alloc(VM *vm, const char *str, uint32_t length);
|
uint32_t str_alloc(VM *vm, const char *str, uint32_t length);
|
||||||
uint32_t float_as_real_alloc(VM *vm, float v);
|
|
||||||
uint32_t real_alloc(VM *vm, fixed_t v);
|
|
||||||
uint32_t nat_alloc(VM *vm, uint32_t v);
|
|
||||||
uint32_t int_alloc(VM *vm, int32_t v);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
hello:
|
||||||
|
"nuqneH 'u'?"
|
||||||
|
puts &hello
|
||||||
|
halt
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
(puts "nuqneH 'u'?")
|
|
|
@ -1 +1,8 @@
|
||||||
print("nuqneH 'u'?");
|
function main(int argc, str[] argv) {
|
||||||
|
str name = "World";
|
||||||
|
if argc > 1 {
|
||||||
|
name = argv[1];
|
||||||
|
}
|
||||||
|
print("Hello, {name}!");
|
||||||
|
exits("Done");
|
||||||
|
}
|
||||||
|
|
|
@ -37,5 +37,5 @@ print_section "zlc ($FILENAME.zl)"
|
||||||
echo "test input" | time zlc "$FILENAME.zl"
|
echo "test input" | time zlc "$FILENAME.zl"
|
||||||
|
|
||||||
# ZRE Implementation
|
# ZRE Implementation
|
||||||
print_section "zre ($FILENAME.zrl)"
|
print_section "zre ($FILENAME.ul)"
|
||||||
echo "test input" | time ../src/zre -t "$FILENAME.zrl"
|
echo "test input" | time ../src/zre -t "$FILENAME.ul"
|
||||||
|
|
Loading…
Reference in New Issue