8.8 KiB
Raw Blame History

< Back | Summary | Next >

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 | Summary | Next >