7.0 KiB
Host ABI and Syscalls
Domain: host ABI structure Function: normative
This chapter defines the structural ABI between the PVM and the host environment.
It focuses on:
- syscall identity;
- load-time resolution;
- instruction-level call semantics;
- metadata shape;
- argument and return-slot contract.
Operational policies such as capabilities, fault classes, determinism, GC interaction, budgeting, and blocking are split into a companion chapter.
1 Design Principles
The syscall ABI follows these rules:
- Stack-based ABI: arguments and return values are passed through VM slots.
- Canonical identity: host services are named by stable
(module, name, version). - Load-time resolution: symbolic host bindings are resolved before execution.
- Metadata-driven execution: arity and result shape come from syscall metadata.
- Not first-class: syscalls are callable but not ordinary function values.
- Status-first operational policy: across syscall domains, operationally observable failure must surface through explicit
statusvalues, while only structural violations fault asTrapand only internal invariant breaks escalate asPanic.
2 Canonical Syscall Identity
Syscalls are identified by a canonical triple:
(module, name, version)
Example:
("gfx", "present", 1)
("audio", "play", 2)
This identity is:
- language-independent;
- toolchain-stable;
- used for linking and capability gating.
Input queries are VM-owned intrinsic calls in v1 and are outside the syscall identity space.
3 Syscall Resolution
The host maintains a registry:
(module, name, version) -> syscall_id
At load time:
- the cartridge declares required syscalls by canonical identity;
- bytecode encodes host-backed call sites as
HOSTCALL <sysc_index>; - the loader validates and resolves those identities;
- the loader rewrites
HOSTCALL <sysc_index>intoSYSCALL <id>; - the executable image uses only numeric syscall ids at runtime.
Raw SYSCALL <id> is not valid in a PBX pre-load artifact and must be rejected there.
4 Syscall Instruction Semantics
Pre-load artifact form:
HOSTCALL <sysc_index>
Final executable form:
SYSCALL <id>
Where:
<sysc_index>indexes the program-declared syscall table;<id>is the final numeric host syscall id.
Execution steps:
- the VM looks up syscall metadata by
<id>; - the VM ensures the argument contract is satisfiable;
- the syscall executes in the host environment;
- the syscall produces exactly the declared
ret_slots.
The execution result must respect the canonical runtime boundary defined with 16a-syscall-policies.md:
- successful or operationally rejected execution returns values in the declared stack shape;
Trapis reserved for structural ABI misuse or invalid call shape;Panicis reserved for runtime/host invariant failure.
5 Syscall Metadata Table
Each syscall is defined by metadata.
Conceptual 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 loader uses this table to resolve identities and validate declared ABI shape.
6 Arguments and Return Values
Syscalls use the same slot-oriented argument/return philosophy as ordinary calls.
Argument passing
Arguments are pushed before the syscall.
Example:
push a
push b
SYSCALL X
Return values
After execution, the syscall leaves exactly ret_slots values on the stack.
Composite results use multiple stack slots rather than implicit hidden structures.
Return shape must also follow the transversal status-first policy in 16a-syscall-policies.md:
- when a syscall exposes operational failure,
status:intis the canonical first return slot; - operations with observable operational failure paths must expose that explicit
status:intreturn; - operations with no real operational error path may remain
void(ret_slots = 0); - if extra payload is returned together with
status, the payload must follow the leading status slot in the declared stack shape; - stack shape remains strict in both cases and must match syscall metadata exactly.
MEMCARD game surface (mem, v1)
The game memcard profile uses module mem with status-first return shapes.
mem is a domain layer backed by runtime fs (it is not a separate storage backend).
Canonical operations in v1 are:
mem.slot_count() -> (status, count)mem.slot_stat(slot) -> (status, state, used_bytes, generation, checksum)mem.slot_read(slot, offset, max_bytes) -> (status, payload_hex, bytes_read)mem.slot_write(slot, offset, payload_hex) -> (status, bytes_written)mem.slot_commit(slot) -> statusmem.slot_clear(slot) -> status
Semantics and domain status catalog are defined by 08-save-memory-and-memcard.md.
Asset surface (asset, v1)
The asset runtime surface also follows the status-first ABI shape.
Canonical operations in v1 are:
asset.load(asset_id, slot) -> (status, handle)asset.status(handle) -> statusasset.commit(handle) -> statusasset.cancel(handle) -> status
For asset.load:
asset_idis a signed 32-bit runtime identity;slotis the target slot index;- bank kind is resolved from
asset_tablebyasset_id, not supplied by the caller.
7 Syscalls as Callable Entities (Not First-Class)
Syscalls behave like call sites, not like first-class guest values.
This means:
- syscalls can be invoked;
- syscalls cannot be stored in variables;
- syscalls cannot be passed as function values;
- syscalls cannot be returned as closures.
Only user-defined functions and closures are first-class.
8 Relationship to Other Specs
16a-syscall-policies.mddefines operational policies layered on top of this ABI.15-asset-management.mddefines asset-domain semantics behind some syscall families.08-save-memory-and-memcard.mddefines persistent save-domain semantics.02a-vm-values-and-calling-convention.mddefines the slot/call philosophy reused by the syscall ABI.