427 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

< [Back](chapter-1.md) | [Summary](table-of-contents.md) | [Next](chapter-3.md) >
# **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. fileciteturn2file0
---
## 1 Core Design Principles
The PVM is designed around the following constraints:
1. **Deterministic execution**: no hidden threads or asynchronous callbacks.
2. **Frame-based timing**: execution is bounded by frame time.
3. **Safe memory model**: all heap objects are accessed through handles.
4. **Simple compilation target**: stack-based bytecode with verified control flow.
5. **Stable ABI**: multi-value returns with fixed slot semantics.
6. **First-class functions**: functions can be passed, stored, and returned.
---
## 2 Execution Model
The PVM executes bytecode in a **frame loop**. Each frame:
1. The firmware enters the VM.
2. The VM runs until:
* the frame budget is consumed, or
* a `FRAME_SYNC` instruction is reached.
3. At `FRAME_SYNC`:
* events are delivered
* input is sampled
* optional GC may run
4. 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 handles 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:
1. Caller prepares arguments.
2. `CALL` transfers control.
3. Callee executes.
4. `RET` leaves exactly `ret_slots` values 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`:
1. The closure handle is read from the stack.
2. The VM extracts the `func_id` from the closure.
3. The function is invoked using the closures captures as its environment.
The verifier ensures that:
* The closure handle is valid.
* The target functions arity matches the call site.
* The `ret_slots` contract 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:
1. Valid jump targets
2. Stack depth consistency
3. Correct `ret_slots` across all paths
4. Handle safety rules
5. Closure call safety
6. 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.
< [Back](chapter-1.md) | [Summary](table-of-contents.md) | [Next](chapter-3.md) >