fix minor things, add inital opcode, add mem2mem test
This commit is contained in:
parent
f1f84e0fe5
commit
5d15aa1c57
20
README.org
20
README.org
|
@ -1,13 +1,13 @@
|
|||
#+OPTIONS: toc:nil
|
||||
|
||||
* Zongor's Universe Machine
|
||||
* Zongor's Reality Engine
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: zongors-universe-machine
|
||||
:END:
|
||||
|
||||
* Overview
|
||||
ZTL is a lightweight, portable programming language for permacomputing, game preservation, and indie game development.
|
||||
Built in **C99** for cross-platform compatibility (desktop, microcontrollers, and web via Emscripten).
|
||||
ZRE is a lightweight, portable programming language for permacomputing, game preservation, and indie game development.
|
||||
Built in **C89** for cross-platform compatibility (desktop, microcontrollers, and web via Emscripten).
|
||||
Designed for simplicity, performance, and creative exploration.
|
||||
It is inspired by [[https://wiki.xxiivv.com/site/uxn.html][uxn]], [[http://duskos.org/][Dusk OS]], [[https://lua.org][Lua]], [[https://en.wikipedia.org/wiki/Lisp_(programming_language)][Lisp]], [[https://en.wikipedia.org/wiki/C_(programming_language)][C]], and [[https://ziglang.org/][Zig]].
|
||||
|
||||
|
@ -30,30 +30,32 @@
|
|||
- Emacs major mode included; Vim, VSCode, and Lite XL support planned.
|
||||
|
||||
** Language Design
|
||||
- Dynamically typed with optional type hints in function signatures.
|
||||
- Unified =real= type (=f64= by default) with =byte= for interop (u8, i32, f32, etc.).
|
||||
- Strongly typed with optional type hints in function signatures.
|
||||
- Unified =real= type (=f64= by default) for floating point/fixed point numbers with =byte= for integer numbers (u8, i32, etc.).
|
||||
- 3D math primitives: =vec=, =mat=, =quat=, and more.
|
||||
- Strings with interpolation, =split=, =replace=, =trim=, etc.
|
||||
- Error handling:
|
||||
- Go-style explicit errors.
|
||||
- Null coalescing (=??=) and null checks (=?.=).
|
||||
- Lightweight coroutines with event-loop-driven multitasking.
|
||||
- FFI via C-defined "native functions" (examples provided in VM).
|
||||
- FFI via C-defined "system calls" (examples provided in VM).
|
||||
|
||||
** Modularity & Ecosystem
|
||||
- Unified table type (like Lua) for lists, maps, and tuples.
|
||||
- Higher performance array type.
|
||||
- Standard library: math, networking, 3D rendering, minimal GUI.
|
||||
- =use= keyword for modular code organization via file-based namespaces.
|
||||
|
||||
* Technical Details
|
||||
|
||||
** VM Architecture
|
||||
- Stack-based bytecode interpreter written in C99.
|
||||
- Stack-based bytecode interpreter written in C89.
|
||||
- Portable to new systems, microcontrollers, and web via Emscripten.
|
||||
|
||||
** Memory Management
|
||||
- Reference counting for globally scoped objects.
|
||||
- =weak= keyword to avoid cyclical references.
|
||||
- garbage collection as a system call (choose when to free memory)
|
||||
- Arena allocators for short-lived, scoped memory (e.g., function-local objects).
|
||||
|
||||
** Rendering & I/O
|
||||
|
@ -84,9 +86,9 @@
|
|||
|
||||
* Motivation
|
||||
|
||||
ZTL bridges retro-inspired creativity with modern portability for:
|
||||
ZRE bridges retro-inspired creativity with modern portability for:
|
||||
- Game jams (rapid prototyping + 3D engine).
|
||||
- Indie games (5th/6th-gen aesthetics).
|
||||
- Permacomputing (low-resource, sustainable code).
|
||||
- Education (simple VM/language design).
|
||||
- Education (simple VM/language design), a language which scales to the developers level.
|
||||
|
||||
|
|
|
@ -91,3 +91,38 @@ be something like the backend dac for ORCA.
|
|||
9p filesystem/network by default.
|
||||
|
||||
Have an easy way to network.
|
||||
|
||||
*** opcodes
|
||||
|
||||
|---------------------------------------------------------------------------------------+------------------------------------------------------------------|
|
||||
| opcode | description |
|
||||
|---------------------------------------------------------------------------------------+------------------------------------------------------------------|
|
||||
| nil | noop/nil/eos |
|
||||
| *bbbb call | calls the address as a function |
|
||||
| b syscall | calls a system call based on the opcode |
|
||||
| bbbb pushReal - | allocates Q16.16 bits on the stack |
|
||||
| - popReal bbbb | deallocates Q16.16 bits from the stack |
|
||||
| b *bbbb storeByte - | stores 8 bits from the stack to the address |
|
||||
| bbbb *bbbb storeReal - | stores 64 bits from the stack to the address |
|
||||
| *bbbb loadByte b | loads 8 bits from the address to the stack |
|
||||
| *bbbb loadReal bbbb | loads 64 bits from the address to the stack |
|
||||
| bbbb bbbb addReal bbbb | adds 64 bits from the stack |
|
||||
| bbbb bbbb subReal bbbb | subtracts 64 bits from the stack |
|
||||
| bbbb bbbb mulReal bbbb | multiplies 64 bits from the stack |
|
||||
| bbbb bbbb divReal bbbb | divides 64 bits from the stack |
|
||||
| bbbb [bbbb bbbb bbbb] scalevec3 [bbbb bbbb bbbb] | scales the vec3 by the f64 on the stack |
|
||||
| [bbbb bbbb bbbb] [bbbb bbbb bbbb] addvec3 [bbbb bbbb bbbb] | adds 2 vec3 together from the stack |
|
||||
| [bbbb bbbb bbbb] [bbbb bbbb bbbb] subvec3 [bbbb bbbb bbbb] | subtracts 2 vec3 together from the stack |
|
||||
| [bbbb bbbb bbbb] [bbbb bbbb bbbb] mulvec3 [bbbb bbbb bbbb] | multiplies 2 vec3 together from the stack |
|
||||
| [bbbb bbbb bbbb] [bbbb bbbb bbbb] dotvec3 bbbb | dot product of 2 vec3 together from the stack |
|
||||
| [bbbb bbbb bbbb] [bbbb bbbb bbbb] crossvec3 [bbbb bbbb bbbb] | cross product of 2 vec3 together from the stack |
|
||||
| [bbbb bbbb bbbb] normvec3 [bbbb bbbb bbbb] | normalizes a vec3 |
|
||||
| [[bbbb bbbb bbbb bbbb]*4] invmat4 [[bbbb bbbb bbbb bbbb]*4] | inverts a mat4 from the stack |
|
||||
| [[bbbb bbbb bbbb bbbb]*4] [[bbbb bbbb bbbb bbbb]*4] mulmat4 [[bbbb bbbb bbbb bbbb]*4] | multiplies 2 mat4's from the stack |
|
||||
| *triangle-array-ptr nnnn drawtriangles - | drawn 'n' many triangles using a array of vertices and its color |
|
||||
| *str1 *str2 streq b | checks 2 strings returns a bool of if they are the same |
|
||||
| *str1 *str2 strcat *str3 | concatinates 2 strings and returns the address of the new string |
|
||||
| *str strlen bbbb | pushes the length of the string onto the stack |
|
||||
| | |
|
||||
| | |
|
||||
|---------------------------------------------------------------------------------------+------------------------------------------------------------------|
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
* /ZTL/ (Zongors Transpiler Language) Design parameters
|
||||
* /ZRE/ (Zongors Reality Engine) Design parameters
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: ztl-zongors-transpiler-language-design-parameters
|
||||
:CUSTOM_ID: zre-zongors-transpiler-language-design-parameters
|
||||
:END:
|
||||
** What is /ztl/?
|
||||
** What is /zre/?
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: what-is-ztl
|
||||
:CUSTOM_ID: what-is-zre
|
||||
:END:
|
||||
/ztl/ is an domain specific language for 3d games with C/Lua style syntax.
|
||||
/zre/ is an domain specific language for 3d games with C/Lua style syntax.
|
||||
The compiler is written in C which should make it easy to port to other
|
||||
systems.
|
||||
|
||||
* /ZTL/ Grammar and Specification
|
||||
* /ZRE/ Grammar and Specification
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: ztl-grammar-and-specification
|
||||
:CUSTOM_ID: zre-grammar-and-specification
|
||||
:END:
|
||||
** Types
|
||||
:PROPERTIES:
|
||||
|
@ -23,13 +23,13 @@ systems.
|
|||
are coming from object oriented languages you can think of self as
|
||||
"primitive types"
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
type «token» {
|
||||
init() {
|
||||
// values
|
||||
}
|
||||
}
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
* Basic Types
|
||||
:PROPERTIES:
|
||||
|
@ -40,10 +40,12 @@ type «token» {
|
|||
:CUSTOM_ID: numeric
|
||||
:END:
|
||||
- =real=
|
||||
- 64 bit floating point (Double)
|
||||
- Is it slow and takes up a lot of space? yeah it does,
|
||||
but because it is the only numeric type it makes it so that you do not have to worry about type casting which actually ends up speeding up processing
|
||||
This is also how Lua, Lox, and Wren programming language handles numbers. Also because ZTL is intended to be used for Games, floats are used more for
|
||||
- 64 bit floating point (Double) by default for modern CPUs that have a FPU
|
||||
- 32 bit 16.16 for microcontrollers/hardware without a FPU like a raspberry pi pico, retro hardware like a Mac 68k
|
||||
- 16 bit 12.4 for older retro hardware like PS1 or UXN.
|
||||
- Is it slow and takes up a lot of space? yeah, kinda, maybe,
|
||||
the nice part of a unified numeric type is that makes it so that you do not have to worry about type casting which actually ends up speeding up processing
|
||||
This is also how Lua, Lox, and Wren programming language handles numbers. Also because ZRE is intended to be used for Games, floats are used more for
|
||||
3D graphics than other numeric types.
|
||||
|
||||
** string
|
||||
|
@ -66,7 +68,7 @@ string interpolation
|
|||
:CUSTOM_ID: binary
|
||||
:END:
|
||||
- =byte=
|
||||
- same as uint8 or c char, used for interop
|
||||
- same as uint8 or c char, also used for interop
|
||||
|
||||
** logical
|
||||
:PROPERTIES:
|
||||
|
@ -85,11 +87,11 @@ monad above and is unwrapped in a similar way. You could also think of
|
|||
it as every variable being able to have "the type" and also "error" as a
|
||||
possible value.
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
let rr = err("something borked");
|
||||
let var = rr ?? 0; // value is 0
|
||||
let other_var = rr ?? panic(rr); // will panic
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
** datastructure
|
||||
:PROPERTIES:
|
||||
|
@ -108,13 +110,13 @@ Types that can be indexes are numbers and strings (no objects);
|
|||
syntax (yes I was nice and kept the syntax the same as most C like
|
||||
langs)
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
/* array same as a map of int to «type» */
|
||||
let «variable» = [val1, val2, ...];
|
||||
|
||||
/* or as a map */
|
||||
let «variable» = {key1: val1, key2: val2, ...};
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
*** tunnel
|
||||
:PROPERTIES:
|
||||
|
@ -202,19 +204,19 @@ The following is a list of global operators and their effect:
|
|||
|
||||
let operator
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
let «token» = true;
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
=is=
|
||||
|
||||
checks if a object is of that type
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
if («token» is real) {
|
||||
print("hello yes self is a real?");
|
||||
}
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
also used for letting constants
|
||||
|
||||
|
@ -222,26 +224,26 @@ also used for letting constants
|
|||
|
||||
coerces a type as another type if possible
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
let «token» = 0; /* default is real */
|
||||
some_functon(«token» as byte); /* needs an byte */
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
=in=
|
||||
|
||||
checks if a object's type, or a type impls another type
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
if («token» in Tunnel) {
|
||||
print("im tunnel-able");
|
||||
}
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
also used inside of the for loops
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
for («token» in «collection») { «body» }
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
** Object
|
||||
:PROPERTIES:
|
||||
|
@ -249,9 +251,9 @@ for («token» in «collection») { «body» }
|
|||
:END:
|
||||
An object is an invoked type.
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
let «variable» = «type»(«fields», …);
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
** Tunnel
|
||||
:PROPERTIES:
|
||||
|
@ -305,7 +307,7 @@ connected tunnel
|
|||
=success? : tunnel_object.walk(path_or_endpoint)= -> moves around the
|
||||
filesystem or through the graph
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
/* client */
|
||||
let endpoint = `protocol://path/to/source`;
|
||||
let tunnel = endpoint.attach(user, auth);
|
||||
|
@ -320,7 +322,7 @@ s.bind("/some/resource", fn () str {
|
|||
return "hello world";
|
||||
})
|
||||
server.start();
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
** Functions
|
||||
:PROPERTIES:
|
||||
|
@ -331,11 +333,11 @@ always have a "default type" for all constant values or a developer can
|
|||
use the =as= keyword we do not have to define all values like in C,
|
||||
while keeping the same type safety as a more strongly typed language.
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
fn «token» («parameter» «type», ...) «return_type» {
|
||||
«body»
|
||||
}
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
- Built in transtypes
|
||||
- sort
|
||||
|
@ -352,27 +354,27 @@ fn «token» («parameter» «type», ...) «return_type» {
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: loops
|
||||
:END:
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
for («token» in «collection») { «body» }
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
iterates through each object in the collection setting it to token
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
while («boolean expression») { «body» }
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
loops until the expression is false
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
loop { «body» }
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
loops infinitely until break or return
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
do («variable» = initial_value, end_value, increment) { «body» }
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
loops from initial value to end value by increment value
|
||||
|
||||
|
@ -380,7 +382,7 @@ loops from initial value to end value by increment value
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: branching
|
||||
:END:
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
if («boolean expression») {
|
||||
|
||||
} else if («boolean expression») {
|
||||
|
@ -388,7 +390,7 @@ if («boolean expression») {
|
|||
} else {
|
||||
|
||||
}
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
*** exceptions
|
||||
:PROPERTIES:
|
||||
|
@ -396,11 +398,11 @@ if («boolean expression») {
|
|||
:END:
|
||||
take a look at error's, but you can panic on an error like self:
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
panic(err("error message"));
|
||||
panic(err(3));
|
||||
panic(«some_error_token»);
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
** Localization
|
||||
:PROPERTIES:
|
||||
|
@ -408,9 +410,9 @@ panic(«some_error_token»);
|
|||
:END:
|
||||
will look up the text of «token» in the linked localization.json file
|
||||
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
#«token»
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
#+begin_src json
|
||||
{
|
||||
|
@ -436,13 +438,13 @@ can even use localization tokens to create config files. Since
|
|||
everything is lazily compiled jit anyways it (in theory) doesn't hurt
|
||||
pertypeance much
|
||||
|
||||
#+begin_src ztl
|
||||
use `https://example.com/some_library/some_file.ztl`
|
||||
#+end_src ztl
|
||||
#+begin_src zre
|
||||
use `https://example.com/some_library/some_file.zre`
|
||||
#+end_src zre
|
||||
|
||||
#+begin_src ztl
|
||||
use `./some_local_file.ztl`
|
||||
#+end_src ztl
|
||||
#+begin_src zre
|
||||
use `./some_local_file.zre`
|
||||
#+end_src zre
|
||||
|
||||
** Testing
|
||||
:PROPERTIES:
|
||||
|
@ -452,9 +454,9 @@ use `./some_local_file.ztl`
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: assertion
|
||||
:END:
|
||||
#+begin_src ztl
|
||||
#+begin_src zre
|
||||
assert(«expression», «expected output») /* returns «error or none» */
|
||||
#+end_src ztl
|
||||
#+end_src zre
|
||||
|
||||
** Measurements
|
||||
:PROPERTIES:
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MEMORY_SIZE 65536 // 64KB memory (adjustable)
|
||||
uint32_t memory[MEMORY_SIZE]; // Memory array
|
||||
|
||||
typedef enum {
|
||||
OP_ADD, // dest = src1 + src2
|
||||
OP_MOV, // dest = src1
|
||||
OP_JMP, // jump to address
|
||||
OP_HALT // terminate execution
|
||||
} Opcode;
|
||||
|
||||
void run_vm() {
|
||||
uint32_t pc = 0; // Program counter
|
||||
while (1) {
|
||||
// Fetch instruction
|
||||
Opcode opcode = memory[pc];
|
||||
uint32_t src1_addr = memory[pc + 1];
|
||||
uint32_t src2_addr = memory[pc + 2];
|
||||
uint32_t dest_addr = memory[pc + 3];
|
||||
pc += 4; // Advance to next instruction
|
||||
|
||||
// Validate addresses (safety check)
|
||||
if (src1_addr >= MEMORY_SIZE || src2_addr >= MEMORY_SIZE || dest_addr >= MEMORY_SIZE) {
|
||||
printf("Invalid memory address!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Execute instruction
|
||||
switch (opcode) {
|
||||
case OP_ADD:
|
||||
memory[dest_addr] = memory[src1_addr] + memory[src2_addr];
|
||||
break;
|
||||
case OP_MOV:
|
||||
memory[dest_addr] = memory[src1_addr];
|
||||
break;
|
||||
case OP_JMP:
|
||||
pc = src1_addr; // Jump to address
|
||||
break;
|
||||
case OP_HALT:
|
||||
return;
|
||||
default:
|
||||
printf("Unknown opcode: %d\n", opcode);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Initialize memory
|
||||
memory[0] = OP_ADD; // Opcode
|
||||
memory[1] = 100; // A (src1)
|
||||
memory[2] = 101; // B (src2)
|
||||
memory[3] = 102; // C (dest)
|
||||
memory[100] = 5; // Value of A
|
||||
memory[101] = 7; // Value of B
|
||||
memory[4] = OP_HALT; // Terminate after ADD
|
||||
|
||||
run_vm();
|
||||
|
||||
printf("Result at address 102: %u\n", memory[102]); // Output: 12
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Q16.16 fixed-point macros
|
||||
#define Q16_16_SCALE 16
|
||||
#define Q16_16_FRACTION_MASK 0x0000FFFF
|
||||
#define TO_Q16_16(x) \
|
||||
((int32_t)((x) << Q16_16_SCALE)) // Convert integer to Q16.16
|
||||
#define FROM_Q16_16(x) \
|
||||
((int32_t)((x) >> Q16_16_SCALE)) // Convert Q16.16 to integer
|
||||
|
||||
#define MEMORY_SIZE 65536
|
||||
int32_t memory[MEMORY_SIZE]; // Changed to signed for fixed-point support
|
||||
|
||||
typedef enum { OP_ADD, OP_MOV, OP_JMP, OP_HALT } Opcode;
|
||||
|
||||
|
||||
// Convert string to Q16.16 fixed-point
|
||||
int32_t string_to_q16_16(const char *str) {
|
||||
int32_t result = 0;
|
||||
int sign = 1;
|
||||
|
||||
// Handle sign
|
||||
if (*str == '-') {
|
||||
sign = -1;
|
||||
str++;
|
||||
}
|
||||
|
||||
// Parse integer part
|
||||
int32_t integer_part = 0;
|
||||
while (*str != '.' && *str != '\0') {
|
||||
if (*str < '0' || *str > '9') {
|
||||
return 0; // Invalid character
|
||||
}
|
||||
integer_part = integer_part * 10 + (*str - '0');
|
||||
str++;
|
||||
}
|
||||
|
||||
// Clamp integer part to 16-bit range
|
||||
if (integer_part > 32767) {
|
||||
integer_part = 32767; // Overflow clamp
|
||||
}
|
||||
|
||||
// Parse fractional part
|
||||
uint32_t frac_digits = 0;
|
||||
int frac_count = 0;
|
||||
|
||||
if (*str == '.') {
|
||||
str++; // Skip decimal point
|
||||
while (*str != '\0' && frac_count < 6) {
|
||||
if (*str < '0' || *str > '9') {
|
||||
break;
|
||||
}
|
||||
frac_digits = frac_digits * 10 + (*str - '0');
|
||||
frac_count++;
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale fractional part to Q16.16
|
||||
// frac_q16 = (frac_digits * 65536 + 500000) / 1000000
|
||||
// This avoids floating-point by using integer arithmetic
|
||||
uint32_t scaled_frac = 0;
|
||||
if (frac_count > 0) {
|
||||
// Pad with zeros if less than 6 digits
|
||||
while (frac_count < 6) {
|
||||
frac_digits *= 10;
|
||||
frac_count++;
|
||||
}
|
||||
// Compute scaled fractional part with rounding
|
||||
scaled_frac = (frac_digits * 65536 + 500000) / 1000000;
|
||||
if (scaled_frac > 0xFFFF) {
|
||||
scaled_frac = 0xFFFF; // Clamp to 16-bit range
|
||||
}
|
||||
}
|
||||
|
||||
// Combine integer and fractional parts
|
||||
result = (integer_part << Q16_16_SCALE) | (scaled_frac & Q16_16_FRACTION_MASK);
|
||||
return result * sign;
|
||||
}
|
||||
|
||||
// Convert Q16.16 to string using integer-only arithmetic
|
||||
int q16_16_to_string(int32_t value, char *buffer, size_t size) {
|
||||
if (size < 13) { // Minimum buffer size for "-32768.000000\0"
|
||||
if (size > 0)
|
||||
buffer[0] = '\0';
|
||||
return -1; // Buffer too small
|
||||
}
|
||||
|
||||
char *buf = buffer;
|
||||
size_t remaining = size;
|
||||
|
||||
// Handle sign
|
||||
if (value < 0) {
|
||||
*buf++ = '-';
|
||||
value = -value;
|
||||
remaining--;
|
||||
}
|
||||
|
||||
// Extract integer and fractional parts
|
||||
int32_t integer_part = value >> Q16_16_SCALE;
|
||||
uint32_t frac = value & Q16_16_FRACTION_MASK;
|
||||
|
||||
// Convert integer part to string
|
||||
char int_buf[11]; // Max 10 digits for 32-bit int
|
||||
char *int_end = int_buf + sizeof(int_buf);
|
||||
char *int_ptr = int_end;
|
||||
|
||||
// Special case for zero
|
||||
if (integer_part == 0) {
|
||||
*--int_ptr = '0';
|
||||
} else {
|
||||
while (integer_part > 0 && int_ptr > int_buf) {
|
||||
*--int_ptr = '0' + (integer_part % 10);
|
||||
integer_part /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy integer part to output buffer
|
||||
while (int_ptr < int_end && remaining > 1) {
|
||||
*buf++ = *int_ptr++;
|
||||
remaining--;
|
||||
}
|
||||
|
||||
// Add decimal point
|
||||
if (remaining > 7) { // Need space for .000000
|
||||
*buf++ = '.';
|
||||
remaining--;
|
||||
} else {
|
||||
if (remaining > 0)
|
||||
*buf = '\0';
|
||||
return size - remaining; // Truncate if insufficient space
|
||||
}
|
||||
|
||||
// Convert fractional part to 6 digits
|
||||
for (int i = 0; i < 6 && remaining > 1; i++) {
|
||||
frac *= 10;
|
||||
uint32_t digit = frac >> 16;
|
||||
frac &= 0xFFFF;
|
||||
*buf++ = '0' + digit;
|
||||
remaining--;
|
||||
}
|
||||
|
||||
// Null-terminate
|
||||
*buf = '\0';
|
||||
return buf - buffer; // Return length written
|
||||
}
|
||||
|
||||
void run_vm() {
|
||||
uint32_t pc = 0;
|
||||
while (1) {
|
||||
Opcode opcode = memory[pc];
|
||||
uint32_t src1_addr = memory[pc + 1];
|
||||
uint32_t src2_addr = memory[pc + 2];
|
||||
uint32_t dest_addr = memory[pc + 3];
|
||||
pc += 4;
|
||||
|
||||
if (src1_addr >= MEMORY_SIZE || src2_addr >= MEMORY_SIZE ||
|
||||
dest_addr >= MEMORY_SIZE) {
|
||||
printf("Invalid memory address!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case OP_ADD:
|
||||
// Q16.16 addition requires no scaling adjustment
|
||||
memory[dest_addr] = memory[src1_addr] + memory[src2_addr];
|
||||
break;
|
||||
case OP_MOV:
|
||||
// Direct copy preserves fixed-point representation
|
||||
memory[dest_addr] = memory[src1_addr];
|
||||
break;
|
||||
case OP_JMP:
|
||||
pc = src1_addr;
|
||||
break;
|
||||
case OP_HALT:
|
||||
return;
|
||||
default:
|
||||
printf("Unknown opcode: %d\n", opcode);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Initialize memory with Q16.16 values
|
||||
memory[0] = OP_ADD;
|
||||
memory[1] = 100;
|
||||
memory[2] = 101;
|
||||
memory[3] = 102;
|
||||
const char *input = "-5.0";
|
||||
memory[100] = string_to_q16_16(input); // Convert "-5.0" to Q16.16
|
||||
const char *input2 = "10.0";
|
||||
memory[101] = string_to_q16_16(input2); // Convert "10.0" to Q16.16
|
||||
memory[4] = OP_HALT;
|
||||
|
||||
run_vm();
|
||||
|
||||
char buffer[13]; // Sufficient for Q16.16 format
|
||||
|
||||
// Convert result back to integer
|
||||
q16_16_to_string(memory[102], buffer, sizeof(buffer));
|
||||
printf("Result at address 102: %s\n", buffer); // Output: 5.0
|
||||
return 0;
|
||||
}
|
|
@ -7,7 +7,7 @@ fn main(argc real, argv str[]) {
|
|||
let username = argv[0];
|
||||
let password = argv[1];
|
||||
|
||||
let me = Player(username, (0.0, 1.0, 2.0), PURPLE);
|
||||
let me = Player(username, Vec3(0.0, 1.0, 2.0), PURPLE);
|
||||
|
||||
let running = true;
|
||||
while (running) {
|
||||
|
@ -22,12 +22,12 @@ fn main(argc real, argv str[]) {
|
|||
}
|
||||
splitbox(parent.size * 0.75) {
|
||||
canvas("3D") {
|
||||
model(Floor((0, 0, 0), 30));
|
||||
model(Floor(Vec3(0, 0, 0), 30));
|
||||
me.update();
|
||||
model(Cube(me.pos, (0.5, 0.5, 0.5), me.appearance));
|
||||
model(Cube(me.pos, Vec3(0.5, 0.5, 0.5), me.appearance));
|
||||
if (let players = me.server.read("players")) {
|
||||
for (p in players) {
|
||||
model(Cube(p.pos, (0.5, 0.5, 0.5), p.apperance));
|
||||
model(Cube(p.pos, Vec3(0.5, 0.5, 0.5), p.apperance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ type Camera {
|
|||
init(pos Vec3, look Vec3) {
|
||||
this.setting = "CAMERA_PERSPECTIVE";
|
||||
this.pov = 45.0;
|
||||
this.up = (0.0, 1.0, 0.0);
|
||||
this.up = Vec3(0.0, 1.0, 0.0);
|
||||
this.pos = pos;
|
||||
this.look = look;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ type Player {
|
|||
Camera((this.pos.x + 10.0, this.pos.y + 10.0, this.pos.z), this.pos);
|
||||
}
|
||||
|
||||
login(password str) Player[] {
|
||||
login(str password) Player[] {
|
||||
this.server.attach(this.username, password);
|
||||
this.players = server.open("players");
|
||||
return players.read();
|
||||
|
@ -59,6 +59,6 @@ type Player {
|
|||
}
|
||||
}
|
||||
|
||||
const RED is[255, 0, 0];
|
||||
const WHITE is[0, 0, 0];
|
||||
const PURPLE is[255, 255, 0];
|
||||
const RED is [255, 0, 0];
|
||||
const WHITE is [0, 0, 0];
|
||||
const PURPLE is [255, 255, 0];
|
||||
|
|
|
@ -3,7 +3,7 @@ use "common.ztl";
|
|||
fn main(argc real, argv str[]) {
|
||||
let s = Server("tcp://0.0.0.0:25565");
|
||||
let running = true;
|
||||
let players = [Player("user", (0, 0, 0), RED)];
|
||||
let players = [Player("user", [0, 0, 0], RED)];
|
||||
while (running) {
|
||||
if (let client = s.accept("players")) {
|
||||
if (let message = client.get()) {
|
||||
|
|
Loading…
Reference in New Issue