2026-03-24 13:40:39 +00:00

8.3 KiB

< Back | Summary >

Host ABI and Syscalls

This chapter defines the Application Binary Interface (ABI) between the Prometeu Virtual Machine (PVM) and the host environment. It specifies how syscalls are identified, resolved, invoked, verified, and accounted for.

Syscalls provide controlled access to host-managed subsystems such as graphics, audio, input, asset banks, and persistent storage.

This chapter defines the contract. Individual subsystems (GFX, AUDIO, MEMCARD, ASSETS, etc.) define their own syscall tables that conform to this ABI.


1 Design Principles

The syscall system follows these rules:

  1. Deterministic: Syscalls must behave deterministically for the same inputs and frame state.
  2. Synchronous: Syscalls execute to completion within the current VM slice.
  3. Non-blocking: Long operations must be modeled as request + status polling.
  4. Capability-gated: Each syscall requires a declared capability.
  5. Stack-based ABI: Arguments and return values are passed via VM slots.
  6. Not first-class: Syscalls are callable but cannot be stored as values.
  7. Language-agnostic identity: Syscalls are identified by canonical names, not language syntax.

2 Canonical Syscall Identity

Syscalls are identified by a canonical triple:

(module, name, version)

Example:

("gfx", "present", 1)
("input", "state", 1)
("audio", "play", 2)

This identity is:

  • Independent of language syntax.
  • Stable across languages and toolchains.
  • Used for capability checks and linking.

Language independence

Different languages may reference the same syscall using different syntax:

Language style Reference form
Dot-based gfx.present
Namespace gfx::present
Object-style Gfx.present()
Functional present(gfx)

All of these map to the same canonical identity:

("gfx", "present", 1)

The compiler is responsible for this mapping.


3 Syscall Resolution

The host maintains a Syscall Registry:

(module, name, version) -> syscall_id

Example:

("gfx", "present", 1) -> 0x0101
("gfx", "submit", 1)  -> 0x0102
("audio", "play", 1)  -> 0x0201

Resolution process

At cartridge load time:

  1. The cartridge declares required syscalls using canonical identities.
  2. The host verifies capabilities.
  3. The host resolves each canonical identity to a numeric syscall_id.
  4. The VM stores the resolved table for fast execution.

At runtime, the VM executes:

SYSCALL <id>

Only numeric IDs are used during execution.


4 Syscall Instruction Semantics

The VM provides a single instruction:

SYSCALL <id>

Where:

  • <id> is a 32-bit integer identifying the syscall.

Execution steps:

  1. The VM looks up syscall metadata using <id>.
  2. The VM verifies that enough arguments exist on the stack.
  3. The VM checks capability requirements.
  4. The syscall executes in the host environment.
  5. The syscall leaves exactly ret_slots values on the stack.

If any contract rule is violated, the VM traps.


5 Syscall Metadata Table

Each syscall is defined by a metadata entry.

SyscallMeta structure

SyscallMeta {
    id: u32
    module: string
    name: string
    version: u16
    arg_slots: u8
    ret_slots: u8
    capability: CapabilityId
    may_allocate: bool
    cost_hint: u32
}

Fields:

Field Description
id Unique numeric syscall identifier
module Canonical module name
name Canonical syscall name
version ABI version of the syscall
arg_slots Number of input stack slots
ret_slots Number of return stack slots
capability Required capability
may_allocate Whether the syscall may allocate VM heap objects
cost_hint Expected cycle cost

The verifier uses this table to validate stack effects.


6 Arguments and Return Values

Syscalls use the same slot-based ABI as functions.

Argument passing

Arguments are pushed onto the stack before the syscall.

Example:

push a
push b
SYSCALL X   // expects 2 arguments

Return values

After execution, the syscall leaves exactly ret_slots values on the stack.

Example:

// before: []
SYSCALL input_state
// after: [held, pressed, released]

Composite return values are represented as multiple slots (stack tuples).


7 Syscalls as Callable Entities (Not First-Class)

Syscalls behave like functions in terms of arguments and return values, but they are not first-class values.

This means:

  • Syscalls can be invoked.
  • Syscalls cannot be stored in variables.
  • Syscalls cannot be passed as arguments.
  • Syscalls cannot be returned from functions.

Only user-defined functions and closures are first-class.

Conceptual declaration

host fn input_state() -> (int, int, int)

This represents a syscall with three return values, but it cannot be treated as a function value.


8 Error Model: Traps vs Status Codes

Syscalls use a hybrid error model.

Trap conditions (contract violations)

The VM traps when:

  • The syscall id is invalid.
  • The canonical identity cannot be resolved.
  • The required capability is missing.
  • The stack does not contain enough arguments.
  • A handle is invalid or dead.

These are fatal contract violations.

Status returns (domain conditions)

Normal operational states are returned as values.

Examples:

  • asset not yet loaded
  • audio voice unavailable
  • memcard full

These are represented by status codes in return slots.


9 Capability System

Each syscall requires a capability.

Capabilities are declared by the cartridge manifest.

Example capability groups:

  • gfx
  • audio
  • input
  • asset
  • memcard

If a syscall is invoked without the required capability:

  • The VM traps.

10 Interaction with the Garbage Collector

The VM heap is managed by the GC. Host-managed memory is separate.

Heap vs host memory

Memory Managed by GC scanned
VM heap objects VM GC Yes
Asset banks Host No
Audio buffers Host No
Framebuffers Host No

Assets are addressed by identifiers, not VM heap handles.

Host root rule

If a syscall stores a handle to a VM heap object beyond the duration of the call, it must register that handle as a host root.

This prevents the GC from collecting objects still in use by the host.

This rule applies only to VM heap objects (such as closures or user objects), not to asset identifiers or primitive values.


11 Determinism Rules

Syscalls must obey deterministic execution rules.

Forbidden behaviors:

  • reading real-time clocks
  • accessing non-deterministic OS APIs
  • performing blocking I/O

Allowed patterns:

  • frame-based timers
  • request + poll status models
  • event delivery at frame boundaries

12 Cost Model and Budgeting

Each syscall contributes to frame cost.

The VM tracks:

  • cycles spent in syscalls
  • syscall counts
  • allocation cost (if any)

Example telemetry:

Frame 10231:
Syscalls: 12
Cycles (syscalls): 380
Allocations via syscalls: 2

Nothing is free.


13 Blocking and Long Operations

Syscalls must not block.

Long operations must use a two-phase model:

  1. Request
  2. Status polling or event notification

Example pattern:

asset.load(id)
...
status, progress = asset.status(id)

14 Summary

  • Syscalls are deterministic, synchronous, and non-blocking.
  • They use the same slot-based ABI as functions.
  • They are callable but not first-class.
  • They are identified canonically by (module, name, version).
  • Language syntax does not affect the ABI.
  • The host resolves canonical identities to numeric syscall IDs.
  • Capabilities control access to host subsystems.
  • GC only manages VM heap objects.
  • Host-held heap objects must be registered as roots.
  • All syscall costs are tracked per frame.

< Back | Summary >