8.8 KiB
Prometeu Virtual Machine (PVM)
This chapter defines the execution model, value system, calling convention, memory model, and host interface of the Prometeu Virtual Machine.
The PVM is a deterministic, stack-based VM designed for a 2D fantasy console environment. Its primary goal is to provide predictable performance, safe memory access, and a stable execution contract suitable for real-time games running at a fixed frame rate. fileciteturn2file0
1 Core Design Principles
The PVM is designed around the following constraints:
- Deterministic execution: no hidden threads or asynchronous callbacks.
- Frame-based timing: execution is bounded by frame time.
- Safe memory model: all heap objects are accessed through handles.
- Simple compilation target: stack-based bytecode with verified control flow.
- Stable ABI: multi-value returns with fixed slot semantics.
- First-class functions: functions can be passed, stored, and returned.
2 Execution Model
The PVM executes bytecode in a frame loop. Each frame:
-
The firmware enters the VM.
-
The VM runs until:
- the frame budget is consumed, or
- a
FRAME_SYNCinstruction is reached.
-
At
FRAME_SYNC:- events are delivered
- input is sampled
- optional GC may run
-
Control returns to the firmware.
FRAME_SYNC is the primary safepoint in the system.
3 Value Types
All runtime values are stored in VM slots as a Value.
Primitive value types (stack values)
| Type | Description |
|---|---|
int |
64-bit signed integer |
bool |
Boolean value |
float |
64-bit floating point |
Built-in vector and graphics types (stack values)
These are treated as VM primitives with dedicated opcodes:
| Type | Description |
|---|---|
vec2 |
2D vector (x, y) |
color |
Packed color value |
pixel |
Combination of position and color |
These types:
- live entirely on the stack
- are copied by value
- never allocate on the heap
Heap values
All user-defined objects live on the heap and are accessed via handles.
| Type | Description |
|---|---|
handle |
Reference to a heap object |
null |
Null handle |
Handles may refer to:
- user objects
- arrays
- strings
- closures
4 Handles and Gate Table
Heap objects are accessed through handles. A handle is a pair:
handle = { index, generation }
The VM maintains a gate table:
GateEntry {
alive: bool
generation: u32
base: usize
slots: u32
type_id: u32
}
When an object is freed:
- its gate entry is marked dead
- its generation is incremented
If a handle’s generation does not match the gate entry, the VM traps.
This prevents use-after-free bugs.
5 Heap Model
- All user objects live in the heap.
- Objects are fixed-layout blocks of slots.
- No inheritance at the memory level.
- Traits/interfaces are resolved by the compiler or via vtables.
Built-in types remain stack-only.
Heap objects include:
- user structs/classes
- strings
- arrays
- closures
6 Tuples and Multi-Return ABI
The PVM supports multi-value returns.
Tuple rules
- Tuples are stack-only.
- Maximum tuple arity is N = 6.
- Tuples are not heap objects by default.
- To persist a tuple, it must be explicitly boxed.
Call convention
Each function declares a fixed ret_slots value.
At call time:
- Caller prepares arguments.
CALLtransfers control.- Callee executes.
RETleaves exactlyret_slotsvalues on the stack.
The verifier ensures that:
- all control paths produce the same
ret_slots - stack depth is consistent.
7 Call Stack and Frames
The VM uses a call stack.
Each frame contains:
Frame {
return_pc
base_pointer
ret_slots
}
Execution uses only the following call instructions:
| Opcode | Description |
|---|---|
CALL |
Calls a function by id |
RET |
Returns from function |
There is no separate PUSH_FRAME or POP_FRAME instruction in the public ISA.
8 Closures and First-Class Functions
Closures are heap objects and represent function values.
The PVM treats functions as first-class values. This means:
- Functions can be stored in variables.
- Functions can be passed as arguments.
- Functions can be returned from other functions.
- All function values are represented as closures.
Even functions without captures are represented as closures with an empty capture set.
Closure layout
Closure {
func_id
captures[]
}
Captures are stored as handles or value copies.
All closure environments are part of the GC root set.
Direct and indirect calls
The PVM supports two forms of function invocation:
| Opcode | Description |
|---|---|
CALL |
Direct call by function id |
CALL_CLOSURE |
Indirect call through a closure handle |
For CALL_CLOSURE:
- The closure handle is read from the stack.
- The VM extracts the
func_idfrom the closure. - The function is invoked using the closure’s captures as its environment.
The verifier ensures that:
- The closure handle is valid.
- The target function’s arity matches the call site.
- The
ret_slotscontract is respected.
9 Coroutines (Deterministic)
The PVM supports cooperative coroutines.
Characteristics:
- Coroutines are scheduled deterministically.
- No preemption.
- No parallel execution.
- All scheduling happens at safepoints.
Each coroutine contains:
Coroutine {
call_stack
operand_stack
state
}
Coroutine instructions
| Opcode | Description |
|---|---|
SPAWN |
Creates a coroutine |
YIELD |
Suspends current coroutine |
SLEEP |
Suspends for N frames |
Scheduling is:
- round-robin
- deterministic
- bounded by frame budget
Coroutine stacks are part of the GC root set.
10 Garbage Collection
The PVM uses a mark-sweep collector.
GC rules
-
GC runs only at safepoints.
-
The primary safepoint is
FRAME_SYNC. -
GC is triggered by:
- heap thresholds, or
- allocation pressure.
Root set
The GC marks from:
- operand stack
- call stack frames
- global variables
- coroutine stacks
- closure environments
- host-held handles
The collector:
- does not compact memory (v1)
- uses free lists for reuse
11 Event and Interrupt Model
The PVM does not allow asynchronous callbacks.
All events are:
- queued by the firmware
- delivered at
FRAME_SYNC
This ensures:
- deterministic execution
- predictable frame timing
Coroutines are the only supported concurrency mechanism.
12 Host Interface (Syscalls)
All hardware access occurs through syscalls.
Syscalls are:
- synchronous
- deterministic
- capability-checked
They operate on the following subsystems:
Graphics
- tilebanks
- layers
- sprites
- palette control
- fade registers
- frame present
Audio
- voice allocation
- play/stop
- volume/pan/pitch
- steal policy
Input
- sampled once per frame
- exposed as frame state
Assets
Asset banks are host-owned memory.
The VM interacts through handles:
| Syscall | Description |
|---|---|
asset.load |
Request asset load into slot |
asset.status |
Query load state |
asset.commit |
Activate loaded asset |
Asset memory:
- is not part of the VM heap
- is not scanned by GC
Save Memory (MEMCARD)
| Syscall | Description |
|---|---|
mem.read_all |
Read save data |
mem.write_all |
Write save data |
mem.commit |
Persist save |
mem.size |
Query capacity |
13 Verifier Requirements
Before execution, bytecode must pass the verifier.
The verifier ensures:
- Valid jump targets
- Stack depth consistency
- Correct
ret_slotsacross all paths - Handle safety rules
- Closure call safety
- No invalid opcode sequences
Invalid bytecode is rejected.
14 Summary
The Prometeu VM is:
- stack-based
- deterministic
- frame-synchronized
- handle-based for heap access
- multi-return capable
- first-class function capable
- coroutine-driven for concurrency
This design balances:
- ease of compilation
- predictable performance
- safety and debuggability
- suitability for real-time 2D games.