implemented intrinsecs de input and specs adjustment

This commit is contained in:
bQUARKz 2026-03-07 14:15:37 +00:00
parent 1bd921979f
commit 10dfc7bf15
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
17 changed files with 957 additions and 1089 deletions

View File

@ -19,7 +19,7 @@ pub use resolver::{
/// Each Syscall has a unique 32-bit ID. The IDs are grouped by category:
/// - **0x0xxx**: System & OS Control
/// - **0x1xxx**: Graphics (GFX)
/// - **0x2xxx**: Input (Gamepad & Touch)
/// - **0x2xxx**: Reserved for legacy input syscalls (disabled for v1 VM-owned input)
/// - **0x3xxx**: Audio (PCM & Mixing)
/// - **0x4xxx**: Filesystem (Sandboxed I/O)
/// - **0x5xxx**: Logging & Debugging
@ -38,31 +38,6 @@ pub enum Syscall {
GfxSetSprite = 0x1007,
GfxDrawText = 0x1008,
GfxClear565 = 0x1010,
InputGetPad = 0x2001,
InputGetPadPressed = 0x2002,
InputGetPadReleased = 0x2003,
InputGetPadHold = 0x2004,
InputPadSnapshot = 0x2010,
InputTouchSnapshot = 0x2011,
TouchGetX = 0x2101,
TouchGetY = 0x2102,
TouchIsDown = 0x2103,
TouchIsPressed = 0x2104,
TouchIsReleased = 0x2105,
TouchGetHold = 0x2106,
TouchGetFinger = 0x2107,
PadGetUp = 0x2200,
PadGetDown = 0x2201,
PadGetLeft = 0x2202,
PadGetRight = 0x2203,
PadGetA = 0x2204,
PadGetB = 0x2205,
PadGetX = 0x2206,
PadGetY = 0x2207,
PadGetL = 0x2208,
PadGetR = 0x2209,
PadGetStart = 0x220A,
PadGetSelect = 0x220B,
AudioPlaySample = 0x3001,
AudioPlay = 0x3002,
FsOpen = 0x4001,

View File

@ -1,305 +0,0 @@
use super::entry;
use crate::syscalls::{Determinism, Syscall, SyscallRegistryEntry, caps};
pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[
entry(
Syscall::InputGetPad,
"input",
"get_pad",
1,
1,
1,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::InputGetPadPressed,
"input",
"get_pad_pressed",
1,
1,
1,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::InputGetPadReleased,
"input",
"get_pad_released",
1,
1,
1,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::InputGetPadHold,
"input",
"get_pad_hold",
1,
1,
1,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::InputPadSnapshot,
"input",
"pad_snapshot",
1,
0,
48,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::InputTouchSnapshot,
"input",
"touch_snapshot",
1,
0,
6,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::TouchGetX,
"input",
"touch_get_x",
1,
0,
1,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::TouchGetY,
"input",
"touch_get_y",
1,
0,
1,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::TouchIsDown,
"input",
"touch_is_down",
1,
0,
1,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::TouchIsPressed,
"input",
"touch_is_pressed",
1,
0,
1,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::TouchIsReleased,
"input",
"touch_is_released",
1,
0,
1,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::TouchGetHold,
"input",
"touch_get_hold",
1,
0,
1,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::TouchGetFinger,
"input",
"touch_get_finger",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetUp,
"input",
"pad_get_up",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetDown,
"input",
"pad_get_down",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetLeft,
"input",
"pad_get_left",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetRight,
"input",
"pad_get_right",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetA,
"input",
"pad_get_a",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetB,
"input",
"pad_get_b",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetX,
"input",
"pad_get_x",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetY,
"input",
"pad_get_y",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetL,
"input",
"pad_get_l",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetR,
"input",
"pad_get_r",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetStart,
"input",
"pad_get_start",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
entry(
Syscall::PadGetSelect,
"input",
"pad_get_select",
1,
0,
4,
caps::INPUT,
Determinism::Deterministic,
false,
1,
),
];

View File

@ -3,7 +3,6 @@ mod audio;
mod bank;
mod fs;
mod gfx;
mod input;
mod log;
mod system;
@ -42,7 +41,6 @@ pub(crate) fn all_entries() -> impl Iterator<Item = &'static SyscallRegistryEntr
system::ENTRIES
.iter()
.chain(gfx::ENTRIES.iter())
.chain(input::ENTRIES.iter())
.chain(audio::ENTRIES.iter())
.chain(fs::ENTRIES.iter())
.chain(log::ENTRIES.iter())

View File

@ -23,31 +23,6 @@ impl Syscall {
0x1007 => Some(Self::GfxSetSprite),
0x1008 => Some(Self::GfxDrawText),
0x1010 => Some(Self::GfxClear565),
0x2001 => Some(Self::InputGetPad),
0x2002 => Some(Self::InputGetPadPressed),
0x2003 => Some(Self::InputGetPadReleased),
0x2004 => Some(Self::InputGetPadHold),
0x2010 => Some(Self::InputPadSnapshot),
0x2011 => Some(Self::InputTouchSnapshot),
0x2101 => Some(Self::TouchGetX),
0x2102 => Some(Self::TouchGetY),
0x2103 => Some(Self::TouchIsDown),
0x2104 => Some(Self::TouchIsPressed),
0x2105 => Some(Self::TouchIsReleased),
0x2106 => Some(Self::TouchGetHold),
0x2107 => Some(Self::TouchGetFinger),
0x2200 => Some(Self::PadGetUp),
0x2201 => Some(Self::PadGetDown),
0x2202 => Some(Self::PadGetLeft),
0x2203 => Some(Self::PadGetRight),
0x2204 => Some(Self::PadGetA),
0x2205 => Some(Self::PadGetB),
0x2206 => Some(Self::PadGetX),
0x2207 => Some(Self::PadGetY),
0x2208 => Some(Self::PadGetL),
0x2209 => Some(Self::PadGetR),
0x220A => Some(Self::PadGetStart),
0x220B => Some(Self::PadGetSelect),
0x3001 => Some(Self::AudioPlaySample),
0x3002 => Some(Self::AudioPlay),
0x4001 => Some(Self::FsOpen),
@ -90,31 +65,6 @@ impl Syscall {
Self::GfxSetSprite => "GfxSetSprite",
Self::GfxDrawText => "GfxDrawText",
Self::GfxClear565 => "GfxClear565",
Self::InputGetPad => "InputGetPad",
Self::InputGetPadPressed => "InputGetPadPressed",
Self::InputGetPadReleased => "InputGetPadReleased",
Self::InputGetPadHold => "InputGetPadHold",
Self::InputPadSnapshot => "InputPadSnapshot",
Self::InputTouchSnapshot => "InputTouchSnapshot",
Self::TouchGetX => "TouchGetX",
Self::TouchGetY => "TouchGetY",
Self::TouchIsDown => "TouchIsDown",
Self::TouchIsPressed => "TouchIsPressed",
Self::TouchIsReleased => "TouchIsReleased",
Self::TouchGetHold => "TouchGetHold",
Self::TouchGetFinger => "TouchGetFinger",
Self::PadGetUp => "PadGetUp",
Self::PadGetDown => "PadGetDown",
Self::PadGetLeft => "PadGetLeft",
Self::PadGetRight => "PadGetRight",
Self::PadGetA => "PadGetA",
Self::PadGetB => "PadGetB",
Self::PadGetX => "PadGetX",
Self::PadGetY => "PadGetY",
Self::PadGetL => "PadGetL",
Self::PadGetR => "PadGetR",
Self::PadGetStart => "PadGetStart",
Self::PadGetSelect => "PadGetSelect",
Self::AudioPlaySample => "AudioPlaySample",
Self::AudioPlay => "AudioPlay",
Self::FsOpen => "FsOpen",

View File

@ -1,7 +1,6 @@
use super::*;
use prometeu_bytecode::{TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_TYPE, Value};
use prometeu_hal::asset::{BankType, LoadStatus, SlotRef};
use prometeu_hal::button::Button;
use prometeu_hal::color::Color;
use prometeu_hal::log::{LogLevel, LogSource};
use prometeu_hal::sprite::Sprite;
@ -51,51 +50,6 @@ impl VirtualMachineRuntime {
pub(crate) fn get_color(&self, value: i64) -> Color {
Color::from_raw(value as u16)
}
pub(crate) fn get_button<'a>(
&self,
id: u32,
hw: &'a dyn prometeu_hal::HardwareBridge,
) -> Option<&'a Button> {
let pad = hw.pad();
match id {
0 => Some(pad.up()),
1 => Some(pad.down()),
2 => Some(pad.left()),
3 => Some(pad.right()),
4 => Some(pad.a()),
5 => Some(pad.b()),
6 => Some(pad.x()),
7 => Some(pad.y()),
8 => Some(pad.l()),
9 => Some(pad.r()),
10 => Some(pad.start()),
11 => Some(pad.select()),
_ => None,
}
}
pub(crate) fn is_button_down(
&self,
id: u32,
hw: &mut dyn prometeu_hal::HardwareBridge,
) -> bool {
match id {
0 => hw.pad().up().down,
1 => hw.pad().down().down,
2 => hw.pad().left().down,
3 => hw.pad().right().down,
4 => hw.pad().a().down,
5 => hw.pad().b().down,
6 => hw.pad().x().down,
7 => hw.pad().y().down,
8 => hw.pad().l().down,
9 => hw.pad().r().down,
10 => hw.pad().start().down,
11 => hw.pad().select().down,
_ => false,
}
}
}
impl NativeInterface for VirtualMachineRuntime {
@ -232,111 +186,6 @@ impl NativeInterface for VirtualMachineRuntime {
hw.gfx_mut().clear(Color::from_raw(color_val as u16));
Ok(())
}
Syscall::InputGetPad => {
ret.push_bool(self.is_button_down(expect_int(args, 0)? as u32, hw));
Ok(())
}
Syscall::InputGetPadPressed => {
let val = self
.get_button(expect_int(args, 0)? as u32, hw)
.map(|b| b.pressed)
.unwrap_or(false);
ret.push_bool(val);
Ok(())
}
Syscall::InputGetPadReleased => {
let val = self
.get_button(expect_int(args, 0)? as u32, hw)
.map(|b| b.released)
.unwrap_or(false);
ret.push_bool(val);
Ok(())
}
Syscall::InputGetPadHold => {
let val = self
.get_button(expect_int(args, 0)? as u32, hw)
.map(|b| b.hold_frames)
.unwrap_or(0);
ret.push_int(val as i64);
Ok(())
}
Syscall::TouchGetX => {
ret.push_int(hw.touch().x() as i64);
Ok(())
}
Syscall::TouchGetY => {
ret.push_int(hw.touch().y() as i64);
Ok(())
}
Syscall::TouchIsDown => {
ret.push_bool(hw.touch().f().down);
Ok(())
}
Syscall::TouchIsPressed => {
ret.push_bool(hw.touch().f().pressed);
Ok(())
}
Syscall::TouchIsReleased => {
ret.push_bool(hw.touch().f().released);
Ok(())
}
Syscall::TouchGetHold => {
ret.push_int(hw.touch().f().hold_frames as i64);
Ok(())
}
Syscall::TouchGetFinger => {
let btn = hw.touch().f();
ret.push_bool(btn.pressed);
ret.push_bool(btn.released);
ret.push_bool(btn.down);
ret.push_int(btn.hold_frames as i64);
Ok(())
}
Syscall::InputPadSnapshot => {
let pad = hw.pad();
for btn in [
pad.up(),
pad.down(),
pad.left(),
pad.right(),
pad.a(),
pad.b(),
pad.x(),
pad.y(),
pad.l(),
pad.r(),
pad.start(),
pad.select(),
] {
ret.push_bool(btn.pressed);
ret.push_bool(btn.released);
ret.push_bool(btn.down);
ret.push_int(btn.hold_frames as i64);
}
Ok(())
}
Syscall::InputTouchSnapshot => {
let touch = hw.touch();
ret.push_bool(touch.f().pressed);
ret.push_bool(touch.f().released);
ret.push_bool(touch.f().down);
ret.push_int(touch.f().hold_frames as i64);
ret.push_int(touch.x() as i64);
ret.push_int(touch.y() as i64);
Ok(())
}
Syscall::PadGetUp => push_button(ret, hw.pad().up()),
Syscall::PadGetDown => push_button(ret, hw.pad().down()),
Syscall::PadGetLeft => push_button(ret, hw.pad().left()),
Syscall::PadGetRight => push_button(ret, hw.pad().right()),
Syscall::PadGetA => push_button(ret, hw.pad().a()),
Syscall::PadGetB => push_button(ret, hw.pad().b()),
Syscall::PadGetX => push_button(ret, hw.pad().x()),
Syscall::PadGetY => push_button(ret, hw.pad().y()),
Syscall::PadGetL => push_button(ret, hw.pad().l()),
Syscall::PadGetR => push_button(ret, hw.pad().r()),
Syscall::PadGetStart => push_button(ret, hw.pad().start()),
Syscall::PadGetSelect => push_button(ret, hw.pad().select()),
Syscall::AudioPlaySample => {
let sample_id = expect_int(args, 0)? as u32;
let voice_id = expect_int(args, 1)? as usize;
@ -521,14 +370,6 @@ impl NativeInterface for VirtualMachineRuntime {
}
}
fn push_button(ret: &mut HostReturn, button: &Button) -> Result<(), VmFault> {
ret.push_bool(button.pressed);
ret.push_bool(button.released);
ret.push_bool(button.down);
ret.push_int(button.hold_frames as i64);
Ok(())
}
fn expect_string(args: &[Value], index: usize, field: &str) -> Result<String, VmFault> {
match args.get(index).ok_or_else(|| VmFault::Panic(format!("Missing {}", field)))? {
Value::String(value) => Ok(value.clone()),

View File

@ -1,4 +1,5 @@
use prometeu_bytecode::Value;
use prometeu_hal::HostContext;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BuiltinTypeKey {
@ -176,6 +177,8 @@ impl BuiltinConstMeta {
pub enum IntrinsicExecutionError {
ArityMismatch { expected: usize, got: usize },
TypeMismatch { index: usize, expected: AbiType },
InvalidBuiltinCarrier { owner: &'static str, name: &'static str, carrier: i64 },
HardwareUnavailable,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -184,7 +187,8 @@ pub enum BuiltinValueError {
TypeMismatch { index: usize, expected: AbiType },
}
pub type IntrinsicImplementation = fn(&[Value]) -> Result<Vec<Value>, IntrinsicExecutionError>;
pub type IntrinsicImplementation =
fn(&[Value], &mut HostContext<'_>) -> Result<Vec<Value>, IntrinsicExecutionError>;
#[derive(Debug, Clone, Copy)]
pub struct IntrinsicMeta {
@ -245,6 +249,9 @@ impl BuiltinConstSlotValue {
const COLOR: BuiltinTypeKey = BuiltinTypeKey::new("color", 1);
const VEC2: BuiltinTypeKey = BuiltinTypeKey::new("vec2", 1);
const PIXEL: BuiltinTypeKey = BuiltinTypeKey::new("pixel", 1);
const INPUT_PAD: BuiltinTypeKey = BuiltinTypeKey::new("input.pad", 1);
const INPUT_TOUCH: BuiltinTypeKey = BuiltinTypeKey::new("input.touch", 1);
const INPUT_BUTTON: BuiltinTypeKey = BuiltinTypeKey::new("input.button", 1);
const COLOR_LAYOUT: [AbiType; 1] = [AbiType::Builtin(COLOR)];
const VEC2_LAYOUT: [AbiType; 2] =
@ -254,6 +261,9 @@ const PIXEL_LAYOUT: [AbiType; 3] = [
AbiType::Scalar(BuiltinScalarType::Int),
AbiType::Builtin(COLOR),
];
const INPUT_PAD_LAYOUT: [AbiType; 1] = [AbiType::Builtin(INPUT_PAD)];
const INPUT_TOUCH_LAYOUT: [AbiType; 1] = [AbiType::Builtin(INPUT_TOUCH)];
const INPUT_BUTTON_LAYOUT: [AbiType; 1] = [AbiType::Builtin(INPUT_BUTTON)];
const VEC2_FIELDS: [BuiltinFieldMeta; 2] = [
BuiltinFieldMeta {
@ -291,7 +301,7 @@ const PIXEL_FIELDS: [BuiltinFieldMeta; 3] = [
},
];
const BUILTIN_TYPES: [BuiltinTypeMeta; 3] = [
const BUILTIN_TYPES: [BuiltinTypeMeta; 6] = [
BuiltinTypeMeta {
id: 0x0001,
name: COLOR.name,
@ -319,6 +329,33 @@ const BUILTIN_TYPES: [BuiltinTypeMeta; 3] = [
flat_slot_layout: &PIXEL_LAYOUT,
flat_slot_width: 3,
},
BuiltinTypeMeta {
id: 0x0100,
name: INPUT_PAD.name,
version: INPUT_PAD.version,
shape: BuiltinTypeShape::Scalar,
fields: &[],
flat_slot_layout: &INPUT_PAD_LAYOUT,
flat_slot_width: 1,
},
BuiltinTypeMeta {
id: 0x0101,
name: INPUT_TOUCH.name,
version: INPUT_TOUCH.version,
shape: BuiltinTypeShape::Scalar,
fields: &[],
flat_slot_layout: &INPUT_TOUCH_LAYOUT,
flat_slot_width: 1,
},
BuiltinTypeMeta {
id: 0x0102,
name: INPUT_BUTTON.name,
version: INPUT_BUTTON.version,
shape: BuiltinTypeShape::Scalar,
fields: &[],
flat_slot_layout: &INPUT_BUTTON_LAYOUT,
flat_slot_width: 1,
},
];
const VEC2_ZERO_LAYOUT: [AbiType; 2] =
@ -341,7 +378,17 @@ const VEC2_DOT_ARGS: [AbiType; 4] = [
const SINGLE_FLOAT_RET: [AbiType; 1] = [AbiType::Scalar(BuiltinScalarType::Float)];
const VEC2_LENGTH_ARGS: [AbiType; 2] =
[AbiType::Scalar(BuiltinScalarType::Float), AbiType::Scalar(BuiltinScalarType::Float)];
const INTRINSICS: [IntrinsicMeta; 2] = [
const NO_ARGS: [AbiType; 0] = [];
const PAD_RET: [AbiType; 1] = [AbiType::Builtin(INPUT_PAD)];
const TOUCH_RET: [AbiType; 1] = [AbiType::Builtin(INPUT_TOUCH)];
const BUTTON_RET: [AbiType; 1] = [AbiType::Builtin(INPUT_BUTTON)];
const PAD_ARGS: [AbiType; 1] = [AbiType::Builtin(INPUT_PAD)];
const TOUCH_ARGS: [AbiType; 1] = [AbiType::Builtin(INPUT_TOUCH)];
const BUTTON_ARGS: [AbiType; 1] = [AbiType::Builtin(INPUT_BUTTON)];
const BOOL_RET: [AbiType; 1] = [AbiType::Scalar(BuiltinScalarType::Bool)];
const INT_RET: [AbiType; 1] = [AbiType::Scalar(BuiltinScalarType::Int)];
const INTRINSICS: [IntrinsicMeta; 23] = [
IntrinsicMeta {
id: 0x1000,
owner: "vec2",
@ -364,6 +411,237 @@ const INTRINSICS: [IntrinsicMeta; 2] = [
may_allocate: false,
implementation: vec2_length,
},
IntrinsicMeta {
id: 0x2000,
owner: "input",
name: "pad",
version: 1,
arg_layout: &NO_ARGS,
ret_layout: &PAD_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad,
},
IntrinsicMeta {
id: 0x2001,
owner: "input",
name: "touch",
version: 1,
arg_layout: &NO_ARGS,
ret_layout: &TOUCH_RET,
deterministic: true,
may_allocate: false,
implementation: input_touch,
},
IntrinsicMeta {
id: 0x2010,
owner: "input.pad",
name: "up",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_up,
},
IntrinsicMeta {
id: 0x2011,
owner: "input.pad",
name: "down",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_down,
},
IntrinsicMeta {
id: 0x2012,
owner: "input.pad",
name: "left",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_left,
},
IntrinsicMeta {
id: 0x2013,
owner: "input.pad",
name: "right",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_right,
},
IntrinsicMeta {
id: 0x2014,
owner: "input.pad",
name: "a",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_a,
},
IntrinsicMeta {
id: 0x2015,
owner: "input.pad",
name: "b",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_b,
},
IntrinsicMeta {
id: 0x2016,
owner: "input.pad",
name: "x",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_x,
},
IntrinsicMeta {
id: 0x2017,
owner: "input.pad",
name: "y",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_y,
},
IntrinsicMeta {
id: 0x2018,
owner: "input.pad",
name: "l",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_l,
},
IntrinsicMeta {
id: 0x2019,
owner: "input.pad",
name: "r",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_r,
},
IntrinsicMeta {
id: 0x201A,
owner: "input.pad",
name: "start",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_start,
},
IntrinsicMeta {
id: 0x201B,
owner: "input.pad",
name: "select",
version: 1,
arg_layout: &PAD_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_pad_select,
},
IntrinsicMeta {
id: 0x2020,
owner: "input.touch",
name: "button",
version: 1,
arg_layout: &TOUCH_ARGS,
ret_layout: &BUTTON_RET,
deterministic: true,
may_allocate: false,
implementation: input_touch_button,
},
IntrinsicMeta {
id: 0x2021,
owner: "input.touch",
name: "x",
version: 1,
arg_layout: &TOUCH_ARGS,
ret_layout: &INT_RET,
deterministic: true,
may_allocate: false,
implementation: input_touch_x,
},
IntrinsicMeta {
id: 0x2022,
owner: "input.touch",
name: "y",
version: 1,
arg_layout: &TOUCH_ARGS,
ret_layout: &INT_RET,
deterministic: true,
may_allocate: false,
implementation: input_touch_y,
},
IntrinsicMeta {
id: 0x2030,
owner: "input.button",
name: "pressed",
version: 1,
arg_layout: &BUTTON_ARGS,
ret_layout: &BOOL_RET,
deterministic: true,
may_allocate: false,
implementation: input_button_pressed,
},
IntrinsicMeta {
id: 0x2031,
owner: "input.button",
name: "released",
version: 1,
arg_layout: &BUTTON_ARGS,
ret_layout: &BOOL_RET,
deterministic: true,
may_allocate: false,
implementation: input_button_released,
},
IntrinsicMeta {
id: 0x2032,
owner: "input.button",
name: "down",
version: 1,
arg_layout: &BUTTON_ARGS,
ret_layout: &BOOL_RET,
deterministic: true,
may_allocate: false,
implementation: input_button_down,
},
IntrinsicMeta {
id: 0x2033,
owner: "input.button",
name: "hold",
version: 1,
arg_layout: &BUTTON_ARGS,
ret_layout: &INT_RET,
deterministic: true,
may_allocate: false,
implementation: input_button_hold,
},
];
pub fn lookup_builtin_type(name: &str, version: u16) -> Option<&'static BuiltinTypeMeta> {
@ -446,7 +724,15 @@ fn validate_values_against_layout(
Ok(())
}
fn vec2_dot(args: &[Value]) -> Result<Vec<Value>, IntrinsicExecutionError> {
const PAD_HANDLE: i64 = 1;
const TOUCH_HANDLE: i64 = 1;
const PAD_BUTTON_BASE: i64 = 0x100;
const TOUCH_BUTTON_CARRIER: i64 = 0x200;
fn vec2_dot(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
if args.len() != 4 {
return Err(IntrinsicExecutionError::ArityMismatch { expected: 4, got: args.len() });
}
@ -457,7 +743,10 @@ fn vec2_dot(args: &[Value]) -> Result<Vec<Value>, IntrinsicExecutionError> {
Ok(vec![Value::Float((ax * bx) + (ay * by))])
}
fn vec2_length(args: &[Value]) -> Result<Vec<Value>, IntrinsicExecutionError> {
fn vec2_length(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
if args.len() != 2 {
return Err(IntrinsicExecutionError::ArityMismatch { expected: 2, got: args.len() });
}
@ -466,6 +755,250 @@ fn vec2_length(args: &[Value]) -> Result<Vec<Value>, IntrinsicExecutionError> {
Ok(vec![Value::Float((x * x + y * y).sqrt())])
}
fn input_pad(
_args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
Ok(vec![Value::Int64(PAD_HANDLE)])
}
fn input_touch(
_args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
Ok(vec![Value::Int64(TOUCH_HANDLE)])
}
fn input_pad_up(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "up")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE)])
}
fn input_pad_down(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "down")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 1)])
}
fn input_pad_left(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "left")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 2)])
}
fn input_pad_right(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "right")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 3)])
}
fn input_pad_a(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "a")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 4)])
}
fn input_pad_b(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "b")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 5)])
}
fn input_pad_x(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "x")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 6)])
}
fn input_pad_y(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "y")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 7)])
}
fn input_pad_l(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "l")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 8)])
}
fn input_pad_r(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "r")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 9)])
}
fn input_pad_start(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "start")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 10)])
}
fn input_pad_select(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_pad_handle(args, "select")?;
Ok(vec![Value::Int64(PAD_BUTTON_BASE + 11)])
}
fn input_touch_button(
args: &[Value],
_ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_touch_handle(args, "button")?;
Ok(vec![Value::Int64(TOUCH_BUTTON_CARRIER)])
}
fn input_touch_x(
args: &[Value],
ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_touch_handle(args, "x")?;
let hw = ctx.require_hw().map_err(|_| IntrinsicExecutionError::HardwareUnavailable)?;
Ok(vec![Value::Int64(hw.touch().x() as i64)])
}
fn input_touch_y(
args: &[Value],
ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
expect_touch_handle(args, "y")?;
let hw = ctx.require_hw().map_err(|_| IntrinsicExecutionError::HardwareUnavailable)?;
Ok(vec![Value::Int64(hw.touch().y() as i64)])
}
fn input_button_pressed(
args: &[Value],
ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
let button = resolve_button(args, "pressed", ctx)?;
Ok(vec![Value::Boolean(button.pressed)])
}
fn input_button_released(
args: &[Value],
ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
let button = resolve_button(args, "released", ctx)?;
Ok(vec![Value::Boolean(button.released)])
}
fn input_button_down(
args: &[Value],
ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
let button = resolve_button(args, "down", ctx)?;
Ok(vec![Value::Boolean(button.down)])
}
fn input_button_hold(
args: &[Value],
ctx: &mut HostContext<'_>,
) -> Result<Vec<Value>, IntrinsicExecutionError> {
let button = resolve_button(args, "hold", ctx)?;
Ok(vec![Value::Int64(button.hold_frames as i64)])
}
fn expect_pad_handle(args: &[Value], name: &'static str) -> Result<(), IntrinsicExecutionError> {
let carrier = expect_builtin_carrier(args, 0)?;
if carrier != PAD_HANDLE {
return Err(IntrinsicExecutionError::InvalidBuiltinCarrier {
owner: "input.pad",
name,
carrier,
});
}
Ok(())
}
fn expect_touch_handle(args: &[Value], name: &'static str) -> Result<(), IntrinsicExecutionError> {
let carrier = expect_builtin_carrier(args, 0)?;
if carrier != TOUCH_HANDLE {
return Err(IntrinsicExecutionError::InvalidBuiltinCarrier {
owner: "input.touch",
name,
carrier,
});
}
Ok(())
}
fn expect_builtin_carrier(args: &[Value], index: usize) -> Result<i64, IntrinsicExecutionError> {
let value = args
.get(index)
.ok_or(IntrinsicExecutionError::ArityMismatch { expected: index + 1, got: args.len() })?;
value.as_integer().ok_or(IntrinsicExecutionError::TypeMismatch {
index,
expected: AbiType::Scalar(BuiltinScalarType::Int),
})
}
fn resolve_button(
args: &[Value],
name: &'static str,
ctx: &mut HostContext<'_>,
) -> Result<prometeu_hal::button::Button, IntrinsicExecutionError> {
let carrier = expect_builtin_carrier(args, 0)?;
let hw = ctx.require_hw().map_err(|_| IntrinsicExecutionError::HardwareUnavailable)?;
if carrier == TOUCH_BUTTON_CARRIER {
return Ok(*hw.touch().f());
}
let Some(index) = carrier.checked_sub(PAD_BUTTON_BASE) else {
return Err(IntrinsicExecutionError::InvalidBuiltinCarrier {
owner: "input.button",
name,
carrier,
});
};
let button = match index {
0 => *hw.pad().up(),
1 => *hw.pad().down(),
2 => *hw.pad().left(),
3 => *hw.pad().right(),
4 => *hw.pad().a(),
5 => *hw.pad().b(),
6 => *hw.pad().x(),
7 => *hw.pad().y(),
8 => *hw.pad().l(),
9 => *hw.pad().r(),
10 => *hw.pad().start(),
11 => *hw.pad().select(),
_ => {
return Err(IntrinsicExecutionError::InvalidBuiltinCarrier {
owner: "input.button",
name,
carrier,
});
}
};
Ok(button)
}
#[cfg(test)]
mod tests {
use super::*;
@ -546,26 +1079,41 @@ mod tests {
assert_eq!(length.arg_layout.len(), 2);
assert_eq!(length.ret_layout.len(), 1);
assert_eq!(lookup_intrinsic_by_id(length.id).map(|meta| meta.key()), Some(length.key()));
let hold =
lookup_intrinsic("input.button", "hold", 1).expect("input.button.hold must exist");
assert_eq!(hold.arg_layout.len(), 1);
assert_eq!(hold.ret_layout.len(), 1);
assert!(hold.deterministic);
assert!(!hold.may_allocate);
}
#[test]
fn intrinsic_implementations_are_registered_without_syscalls() {
let dot = lookup_intrinsic("vec2", "dot", 1).expect("vec2.dot must exist");
let result = (dot.implementation)(&[
Value::Float(1.0),
Value::Float(2.0),
Value::Float(3.0),
Value::Float(4.0),
])
let mut ctx = HostContext::new(None);
let result = (dot.implementation)(
&[Value::Float(1.0), Value::Float(2.0), Value::Float(3.0), Value::Float(4.0)],
&mut ctx,
)
.expect("dot implementation must execute");
assert_eq!(result, vec![Value::Float(11.0)]);
let length = lookup_intrinsic("vec2", "length", 1).expect("vec2.length must exist");
let result = (length.implementation)(&[Value::Float(3.0), Value::Float(4.0)])
let result = (length.implementation)(&[Value::Float(3.0), Value::Float(4.0)], &mut ctx)
.expect("length implementation must execute");
assert_eq!(result, vec![Value::Float(5.0)]);
}
#[test]
fn input_intrinsic_requires_hardware_context() {
let mut ctx = HostContext::new(None);
let touch_x = lookup_intrinsic("input.touch", "x", 1).expect("input.touch.x must exist");
let err = (touch_x.implementation)(&[Value::Int64(TOUCH_HANDLE)], &mut ctx)
.expect_err("touch.x must fail without hardware context");
assert_eq!(err, IntrinsicExecutionError::HardwareUnavailable);
}
#[test]
fn pixel_flat_values_validate_with_color_carrier_slot() {
let pixel = lookup_builtin_type("pixel", 1).expect("pixel builtin must exist");

View File

@ -1111,7 +1111,7 @@ impl VirtualMachine {
}
args.reverse();
let results = (intrinsic.implementation)(&args).map_err(|err| match err {
let results = (intrinsic.implementation)(&args, ctx).map_err(|err| match err {
crate::IntrinsicExecutionError::ArityMismatch { expected, got } => self.trap(
TRAP_INVALID_INTRINSIC,
OpCode::Intrinsic as u16,
@ -1130,6 +1130,22 @@ impl VirtualMachine {
),
pc_at_intrinsic,
),
crate::IntrinsicExecutionError::InvalidBuiltinCarrier {
owner,
name,
carrier,
} => self.trap(
TRAP_INVALID_INTRINSIC,
OpCode::Intrinsic as u16,
format!(
"Intrinsic {}.{} received invalid builtin carrier {} for {}.{}",
intrinsic.owner, intrinsic.name, carrier, owner, name
),
pc_at_intrinsic,
),
crate::IntrinsicExecutionError::HardwareUnavailable => {
LogicalFrameEndingReason::Panic("Host feature unavailable".into())
}
})?;
intrinsic.validate_result_values(&results).map_err(|err| match err {
@ -1151,6 +1167,24 @@ impl VirtualMachine {
),
pc_at_intrinsic,
),
crate::IntrinsicExecutionError::InvalidBuiltinCarrier {
owner,
name,
carrier,
} => self.trap(
TRAP_INVALID_INTRINSIC,
OpCode::Intrinsic as u16,
format!(
"Intrinsic {}.{} produced invalid builtin carrier {} for {}.{}",
intrinsic.owner, intrinsic.name, carrier, owner, name
),
pc_at_intrinsic,
),
crate::IntrinsicExecutionError::HardwareUnavailable => {
LogicalFrameEndingReason::Panic(
"Host feature unavailable while validating intrinsic results".into(),
)
}
})?;
for value in results {
@ -2331,6 +2365,34 @@ mod tests {
assert_eq!(vm.operand_stack, vec![Value::Float(5.0)]);
}
#[test]
fn test_intrinsic_input_pad_returns_builtin_carrier() {
let rom = assemble("INTRINSIC 0x2000\nHALT").expect("assemble");
let mut vm = new_test_vm(rom, vec![]);
let mut native = MockNative;
let mut ctx = HostContext::new(None);
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
assert!(matches!(report.reason, LogicalFrameEndingReason::Halted));
assert_eq!(vm.operand_stack, vec![Value::Int64(1)]);
}
#[test]
fn test_intrinsic_input_touch_x_requires_hardware_context() {
let rom = assemble("INTRINSIC 0x2001\nINTRINSIC 0x2021\nHALT").expect("assemble");
let mut vm = new_test_vm(rom, vec![]);
let mut native = MockNative;
let mut ctx = HostContext::new(None);
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
match report.reason {
LogicalFrameEndingReason::Panic(msg) => {
assert!(msg.contains("Host feature unavailable"));
}
other => panic!("Expected Panic due to missing hardware context, got {:?}", other),
}
}
#[test]
fn test_invalid_intrinsic_trap_is_distinct_from_syscall() {
let rom = assemble("INTRINSIC 0xDEADBEEF\nHALT").expect("assemble");

View File

@ -0,0 +1,45 @@
//! Regression test: input is no longer exposed through the syscall registry.
use prometeu_bytecode::isa::core::CoreOpCode as OpCode;
use prometeu_hal::vm_fault::VmFault;
use prometeu_vm::{HostContext, HostReturn, NativeInterface, VirtualMachine};
fn enc_op(op: OpCode) -> [u8; 2] {
(op as u16).to_le_bytes()
}
#[test]
fn vm_rejects_legacy_input_syscall_id() {
// Legacy input syscall id (PadGetUp) must now be rejected as unknown syscall.
let mut rom = Vec::new();
rom.extend_from_slice(&enc_op(OpCode::Syscall));
rom.extend_from_slice(&0x2200u32.to_le_bytes());
rom.extend_from_slice(&enc_op(OpCode::Halt));
struct NoopNative;
impl NativeInterface for NoopNative {
fn syscall(
&mut self,
_id: u32,
_args: &[prometeu_bytecode::Value],
_ret: &mut HostReturn,
_ctx: &mut HostContext,
) -> Result<(), VmFault> {
Ok(())
}
}
let mut vm = VirtualMachine::new(rom, vec![]);
let mut native = NoopNative;
let mut ctx = HostContext::new(None);
vm.prepare_call("0");
vm.set_capabilities(prometeu_hal::syscalls::caps::ALL);
let report = vm.run_budget(100, &mut native, &mut ctx).expect("VM run failed");
match report.reason {
prometeu_vm::LogicalFrameEndingReason::Trap(trap) => {
assert_eq!(trap.code, prometeu_bytecode::TRAP_INVALID_SYSCALL);
assert!(trap.message.contains("Unknown syscall"));
}
other => panic!("Expected invalid syscall trap, got {:?}", other),
}
}

View File

@ -1,54 +0,0 @@
//! Deterministic tests for multi-return syscalls with the slot-based ABI.
use prometeu_bytecode::Value;
use prometeu_bytecode::isa::core::CoreOpCode as OpCode;
use prometeu_hal::vm_fault::VmFault;
use prometeu_vm::{HostContext, HostReturn, NativeInterface, VirtualMachine};
fn enc_op(op: OpCode) -> [u8; 2] {
(op as u16).to_le_bytes()
}
#[test]
fn vm_syscall_multi_return_stack_contents() {
// Use a real multi-return syscall id with ret_slots = 4: PadGetUp (0x2200)
// ROM: SYSCALL 0x2200; HALT
let mut rom = Vec::new();
rom.extend_from_slice(&enc_op(OpCode::Syscall));
rom.extend_from_slice(&0x2200u32.to_le_bytes());
rom.extend_from_slice(&enc_op(OpCode::Halt));
struct TestNative;
impl NativeInterface for TestNative {
fn syscall(
&mut self,
id: u32,
_args: &[Value],
ret: &mut HostReturn,
_ctx: &mut HostContext,
) -> Result<(), VmFault> {
assert_eq!(id, 0x2200);
// Push exactly 4 results in a known order.
ret.push_int(11);
ret.push_int(22);
ret.push_bool(true);
ret.push_int(7);
Ok(())
}
}
let mut vm = VirtualMachine::new(rom.clone(), vec![]);
let mut native = TestNative;
let mut ctx = HostContext::new(None);
vm.prepare_call("0");
// Grant INPUT capability to pass capability gate for PadGetUp
vm.set_capabilities(prometeu_hal::syscalls::caps::INPUT);
let _ = vm.run_budget(100, &mut native, &mut ctx).expect("VM run failed");
// Verify top-of-stack order: last pushed is on top
let top = vm.operand_stack_top(4);
assert_eq!(
top,
vec![Value::Int64(7), Value::Boolean(true), Value::Int64(22), Value::Int64(11),]
);
}

View File

@ -1,135 +1,31 @@
# Agenda - Input Intrinsics Surface
# Agenda 010 - Input Intrinsics Surface (Fechada no v1)
## Problema
## Status
`input` ja esta conceitualmente do lado de intrinsics, nao de syscalls, mas a superficie concreta desse dominio ainda nao esta congelada.
Fechada para v1 pela decisao:
Nesta agenda, `input` significa o dominio formado por:
- `../decisions/005-v1-vm-owned-input-intrinsics-and-language-agnostic-surface.md`
- `pad`;
- `touch`.
## O Que Foi Fechado
Sem uma agenda propria, o dominio de input corre o risco de ficar:
1. Input sai da fronteira de syscall e entra em VM-owned intrinsics.
2. Superficie obrigatoria do dominio: `button`, `pad`, `touch`.
3. Sem `axis` no v1.
4. Snapshot de input congelado no inicio do frame logico.
5. API de linguagem pode ser encadeada/ergonomica, mas o contrato normativo e language-agnostic.
6. Sem capability gate e sem custo de certificacao por syscall para leitura de input.
- espalhado pela spec de input;
- implícito no ISA;
- indefinido no verifier;
- lembrado tarde demais na fase de implementacao.
## Efeito Pratico
Hoje existe uma ambiguidade concreta no runtime:
- Toolchain/backend devem emitir `INTRINSIC <id_final>` para input.
- Syscalls de input legadas devem ser removidas/desativadas da superficie oficial.
- as specs tratam input como estado amostrado por frame;
- a linguagem/runtime quer expor `pad` e `touch` como surface built-in do guest;
- mas a implementacao ainda coleta esse estado por syscalls.
## Risco Residual
Exemplos atuais no runtime:
- Nenhum bloqueador do bytecode v1 permanece nesta agenda.
- Novas discussoes de input so devem reabrir em caso de mudanca de versao de contrato.
- `InputGetPad`
- `InputGetPadPressed`
- `InputGetPadReleased`
- `InputGetPadHold`
- `InputPadSnapshot`
- `InputTouchSnapshot`
- `TouchGetX`
- `TouchGetY`
- `TouchIsDown`
- `TouchIsPressed`
- `TouchIsReleased`
- `TouchGetHold`
- `TouchGetFinger`
- `PadGetUp` ate `PadGetSelect`
## Follow-up
## Dor
- o runtime pode implementar intrinsics de input com shape arbitrario;
- custo, assinatura e IDs logicos podem divergir entre docs;
- o verifier pode aceitar chamadas sem contrato fechado;
- `button` pode ganhar peso desproporcional e esconder que ele eh apenas uma parte da surface de `pad`;
- o dominio de input fica misturado com decisoes de syscall, onde ele nao pertence.
- o runtime mantem duas historias ao mesmo tempo: input como estado latched e input como host service;
- a superficie explode em muitas syscalls pequenas para ler algo que ja esta disponivel como estado do hardware.
## Alvo da Discussao
Definir a superficie canonica dos intrinsics de input observavel pela VM.
`pad` e `touch` sao os dois blocos do dominio.
`button` entra aqui apenas como parte da surface de `pad`, nao como centro exclusivo da agenda.
Esta agenda tambem precisa decidir a migracao explicita:
- quais syscalls de input serao removidas;
- quais intrinsics as substituem;
- qual surface final o guest enxerga como built-in.
## O Que Precisa Ser Definido
1. Conjunto de intrinsics.
Quais operacoes existem no MVP:
- operacoes de `pad`;
- operacoes de `touch`, se entrarem no MVP.
No caso de `pad`, os candidatos iniciais sao:
- `btn`
- `btnp`
- `btnr`
- `axis`, se entrar ou nao.
2. Assinatura exata.
Definir:
- argumentos;
- tipo de retorno;
- custo/cycles;
- comportamento para ID invalido;
- comportamento para ausencia de suporte do dispositivo.
3. Superfícies do dominio.
Fechar quais familias de input a VM pode consultar no contrato inicial:
- pad;
- axis;
- touch.
4. Identificadores logicos.
Fechar o conjunto de IDs logicos que a VM pode consultar.
Isso inclui:
- botoes do pad;
- eixos, se existirem;
- pontos/canais de touch, se existirem.
5. Integracao com ISA e verifier.
Como os intrinsics sao referenciados, validados e emitidos no bytecode.
6. Migração da surface atual.
Definir:
- quais syscalls de input deixam de existir;
- se toda leitura de `pad` e `touch` sai da ABI de syscall;
- como a toolchain e a surface do guest passam a apontar apenas para intrinsics.
7. Observabilidade.
O que entra em trace, certificacao e relatorios de custo.
## Dependencias
- ISA/intrinsic model da VM;
- spec de input para o dominio logico;
- definicao de fault ou resposta para IDs invalidos, se houver;
- alinhamento com a spec de touch, se o dominio entrar no MVP.
## Fora de Escopo
- mapeamento fisico de teclado/gamepad;
- pipeline de coleta de input no host;
- gestos de alto nivel;
- novos syscalls de input;
- transporte de bytes.
## Critério de Saida Desta Agenda
Pode virar PR quando houver decisao escrita sobre:
- lista final de intrinsics de input;
- assinatura e retorno de cada intrinsic;
- superfícies de input suportadas no MVP;
- IDs logicos suportados;
- lista das syscalls de input a serem removidas;
- integracao com ISA, verifier e tracing.
- Validar alinhamento de specs (`06`, `16`, `16a`) com a decisao `005`.
- Validar runtime/verifier contra IDs finais de input intrinsics.

View File

@ -1,95 +1,35 @@
# Agenda - Input Frame Semantics and Portability
# Agenda 011 - Input Frame Semantics and Portability (Fechada no v1)
## Problema
## Status
O dominio de input ja tem uma direcao documental, mas ainda precisa de fechamento operacional na borda runtime <-> plataforma.
Fechada para v1 pela decisao:
Nesta agenda, `input` significa o dominio formado por:
- `../decisions/005-v1-vm-owned-input-intrinsics-and-language-agnostic-surface.md`
- `pad`;
- `touch`.
## O Que Foi Fechado
Sem isso, intrinsics de input podem existir com nome certo e semantica errada.
1. A amostragem de input ocorre antes do frame logico.
2. O estado observado pelo guest fica congelado durante todo o frame.
3. Semantica de `button`:
- `pressed` no frame de transicao up -> down
- `released` no frame de transicao down -> up
- `down` enquanto pressionado
- `hold` contando frames pressionado
4. `touch` e single-point no perfil handheld v1, com coordenadas `(x, y)` e persistencia do ultimo estado no snapshot vigente.
5. `pad` e `touch` sao obrigatorios como elementos do contrato de plataforma.
6. Leitura de input nao participa de capability gate de syscall nem de scoring de certificacao por syscall.
Hoje a dor nao eh apenas teorica:
## Efeito Pratico
- `pad` e `touch` ja sao latched no host no inicio do frame;
- `Button` ja eh a unidade de estado usada por `pad` e pelo toque ativo;
- mas o runtime ainda expoe a leitura desse estado por syscalls em vez de intrinsics.
- O runtime deve expor input como leitura deterministica de snapshot VM-owned.
- O host deve fornecer os sinais necessarios para pad/touch a cada frame.
## Dor
## Risco Residual
- ambiguidades sobre quando o input e amostrado;
- risco de divergencia entre plataformas sobre pressed/held/released do `pad`;
- risco de divergencia sobre touch ativo, posicao, multitouch e ausencia de touch;
- replay, certificacao e reproducibilidade ficam dependentes de interpretacao;
- edge cases de foco, multiplos dispositivos e conflitos de direcao ficam sem dono.
- a borda guest pode acabar observando input como consulta host-driven, quando o modelo correto eh leitura de estado ja congelado no frame.
- Nenhum bloqueador de bytecode v1 permanece nesta agenda.
- Edge cases de app model/window system ficam para agendas futuras (fora de input v1).
## Alvo da Discussao
## Follow-up
Fixar a semantica temporal e de portabilidade do dominio de input.
`pad` e `touch` sao os dois subconjuntos principais do contrato.
`button` entra apenas como parte da semantica de `pad`.
## O Que Precisa Ser Definido
1. Momento de amostragem.
Em que ponto do frame o estado e capturado e congelado.
2. Semantica de estado.
Definir formalmente:
- pressed no `pad`;
- released no `pad`;
- held no `pad`;
- transicao entre frames.
3. Semantica de touch.
Definir formalmente, se touch fizer parte do contrato:
- ponto ativo ou inativo;
- coordenadas;
- multitouch ou touch unico;
- persistencia entre frames.
4. Determinismo e replay.
Como input gravado, injetado ou reproduzido conversa com o runtime.
5. Regras de portabilidade.
O que deve ser igual em todas as plataformas e o que pode variar.
6. Edge cases.
Exemplos:
- perda de foco;
- multiplos devices;
- direcoes opostas simultaneas no `pad`;
- ausencia de determinado dispositivo fisico;
- plataforma sem touch;
- diferenca de resolucao ou area de toque.
7. Relacao com CAP e certificacao.
Como consultas de input aparecem em custo e relatorios.
Esta agenda tambem precisa decidir se input continua ou nao exigindo capability de syscall depois da migracao para intrinsic.
## Dependencias
- `010-input-intrinsics-surface.md`;
- spec de input;
- modelo de frame do runtime.
## Fora de Escopo
- opcode ou encoding do intrinsic;
- mapeamento detalhado por plataforma;
- bytes e heap.
## Critério de Saida Desta Agenda
Pode virar PR quando houver decisao escrita sobre:
- momento de amostragem;
- semantica formal dos intrinsics de input do MVP;
- regras de replay e reproducibilidade;
- edge cases minimos que o runtime precisa tratar;
- posicao explicita sobre a saida de `input` da fronteira de syscall;
- impactos em CAP e certificacao.
- Manter `06-input-peripheral.md` e `07-touch-peripheral.md` alinhadas com esta semantica.
- Cobrir no runtime testes de regressao para determinismo por frame.

View File

@ -1,180 +1,88 @@
# Agenda - VM-Owned Builtins Protocol and System Services
# Agenda - VM-Owned Stateful Protocol (Pos-v1)
## Problema
## Base Ja Fechada (Nao Reabrir Nesta Agenda)
O modelo de `syscall` atual atende bem chamadas host-backed e nao deve ser quebrado.
Esta agenda passa a considerar fechado no v1:
Ao mesmo tempo, falta um protocolo formal para chamadas e dados VM-owned entre guest e VM para casos como:
- input VM-owned por `INTRINSIC <id_final>`;
- sem tabela dedicada de preload VM-owned no v1;
- `HOSTCALL`/`SYSCALL` host-backed preservados sem redesign;
- surface de linguagem livre (exemplo PBS apenas ilustrativo, nao normativo);
- snapshot de input congelado por frame;
- input sem capability/telemetria de certificacao.
- input;
- random;
- window system integrado ao SO;
- builtins com leitura/escrita e lifecycle.
Fonte normativa do recorte v1:
Hoje ja existe base para bytes VM-owned (decisao `003`), mas ainda nao existe contrato equivalente para valores builtin, recursos tipados e handles VM-owned.
- `../decisions/005-v1-vm-owned-input-intrinsics-and-language-agnostic-surface.md`
## Dor
## Problema Desta Agenda
- sem protocolo, cada dominio tende a criar shape ad-hoc de stack e retorno;
- surfaces de linguagem (FE) e backend podem divergir do runtime real;
- faltam regras canonicas para criar/ler/escrever/destruir recursos VM-owned;
- verifier e toolchain ficam sem alvo estavel para validacao de chamadas VM-owned;
- discussoes de input/random/window tendem a virar agendas grandes e misturadas.
Falta fechar o protocolo VM-owned para casos stateful que exigem recurso vivo, ownership e lifecycle, por exemplo:
- random com instancia/estado (`RngRef`);
- estruturas VM-owned (ex.: map/set/colecoes runtime-owned);
- window system para app model futuro;
- filas/eventos VM-owned.
Ja existe uma base de data plane low-level de bytes (decisao `003`), mas ainda falta o contrato stateful tipado para builtins/servicos com `HeapRef` ou handle equivalente VM-owned.
## Alvo da Discussao
Definir um protocolo canonico de comunicacao guest <-> VM para operacoes VM-owned, preservando:
Definir um protocolo canonico de recursos VM-owned stateful, preservando:
- `syscall` como fronteira host-backed existente;
- compatibilidade com o ABI atual de slots;
- determinismo por frame.
- fronteira host-backed atual intacta;
- ABI estavel por slots;
- versionamento e verificacao 1:1 por operacao;
- determinismo quando requerido pelo dominio.
## Achados Consolidados Desta Rodada
## O Que Ainda Precisa Ser Definido
- manter separacao por camadas:
- decisao `003` como `Byte Transfer Core` (data plane low-level);
- protocolo VM-owned desta agenda como `Service Control Plane` para builtins e servicos.
- o guest nao recebe handle host-owned e nao acessa memoria host-owned diretamente.
- servicos VM-owned podem internamente delegar para host/sistema operacional, sem expor essa borda para userland.
- `syscall` host-backed atual permanece valido; esta agenda nao redesenha essa fronteira.
- input e random passam a ser tratados como servicos VM-owned, com retorno de builtins/handles VM-owned.
- para input, a direcao preferida e leitura de snapshot congelado por frame com semantica de registrador (sem copia grande de dados por chamada).
## Consolidado Parcial - `HOSTCALL` com `kind` (`host` | `vm`)
- proposta candidata:
- manter `HOSTCALL <index>` no artefato pre-load;
- substituir `SYSC` por uma tabela unificada de bindings (nome provisorio: `CALLS`/`BIND`);
- cada entrada declara `kind`, `module`, `name`, `version`, `arg_slots`, `ret_slots`.
- regra de resolucao:
- a decisao `host` vs `vm` ocorre somente no loader;
- loader resolve e patcha deterministamente:
- `kind=host` -> `SYSCALL <id>`;
- `kind=vm` -> `INTRINSIC <id>`.
- o runtime nunca executa branch dinamico `host|vm` em tempo de execucao.
- `kind` separa semanticas sem colapsar contratos:
- `host` continua host-backed (capability/politicas do dominio host);
- `vm` segue contrato VM-owned (builtins/recursos/estado interno da VM).
- ponto tecnico critico identificado:
- o executor atual de intrinsic usa `fn(&[Value])` e nao recebe contexto da VM/runtime;
- servicos VM-owned stateful (input/map/window/random com recurso) exigem evolucao dessa interface.
- regra de ABI para VM-owned:
- operacoes devem mapear 1:1 para diretivas versionadas;
- `arg_slots` e `ret_slots` fixos por operacao/versao (sem shape dinamico).
- extensoes previstas (nao bloqueadoras do recorte inicial):
- lifecycle de handles VM-owned com anti-stale/generation;
- politica explicita de roots/GC para recursos vivos.
- ponto ainda pendente de fechamento nesta agenda:
- semantica de determinismo para random e, futuramente, fila/eventos de window system.
## O Que Precisa Ser Definido
1. Plano de chamada VM-owned.
1. Modelo canonico de recurso VM-owned stateful.
Definir:
- ponto de entrada no bytecode para VM-owned:
- `INTRINSIC` com registry formal; ou
- `HOSTCALL + kind=vm` com patch de loader para `INTRINSIC`;
- namespace de IDs;
- versionamento por operacao;
- metadados canonicos (`arg_slots`, `ret_slots`, layout, custo, efeito, determinismo).
- representacao de handle/referencia (`HeapRef<TBuiltin>`, handle tipado, ou hibrido);
- ownership e regra anti-stale (generation/version);
- lifecycle minimo (`create`, `read`, `update`, `destroy`).
2. Plano de dados builtin.
2. Evolucao da execucao de intrinsic para servicos stateful.
Definir:
- como builtins sao representados em slots;
- quando um builtin e inline e quando vira handle;
- regras de leitura/escrita/atualizacao em stack e heap;
- regras de compatibilidade de layout entre versoes.
- como a operacao intrinsic recebe contexto VM/runtime de forma segura;
- como manter compatibilidade com intrinsics atuais read-only.
3. Modelo de recursos VM-owned.
3. Contrato de ABI/meta para operacoes VM-owned stateful.
Definir:
- formato de handle (incluindo estrategia anti-stale);
- ownership;
- lifecycle (`create`, `read`, `update`, `destroy`);
- invariantes de validade.
- politica de singleton vs instancia alocada para builtins de snapshot (ex.: `Pad`, `Touch`).
- metadados canonicos por operacao (`arg_slots`, `ret_slots`, efeito, custo, determinismo);
- namespace/versionamento de IDs;
- criterio de compatibilidade binaria entre versoes.
4. Perfil de input no protocolo.
4. Perfil de random stateful.
Fechar:
- como `Input.getPad()` e `Input.getTouch()` mapeiam para o protocolo VM-owned;
- se a API retorna ref singleton (`Input.getPad() -> PadRef`) ou recebe destino (`Input.getPad(dst_ref)`), e por que;
- relacao com as syscalls atuais de input no periodo de transicao (se houver);
- garantia de snapshot congelado por frame para leitura deterministica.
- superficie minima (`seed` explicita, instancia, leitura de proximo valor);
- regra de determinismo/replay;
- politica de status/fault.
5. Perfil de random no protocolo.
Fechar:
- API minima (`RndG.pcg32(seed) -> RngRef` e/ou `getRandom`), incluindo superficie inicial oficial;
- PRNG deterministico e regras de seed;
- politicas de reproducibilidade.
6. Perfil de window system no protocolo.
5. Perfil de window system/app model.
Fechar contrato minimo para:
- criacao e destruicao de janelas;
- leitura/escrita de estado de janela;
- criacao/destruicao de janelas;
- leitura/escrita de estado;
- fila de eventos;
- relacao com foco e input.
- relacao com foco/input sem quebrar determinismo do runtime.
7. Modelo de fault/status.
6. Verifier/toolchain/disasm.
Definir:
- fronteira entre erro estrutural (`Trap`) e resultado operacional (`status`);
- como erros de handle/recurso/estado invalido sao expostos.
8. Integracao com verifier e toolchain.
Definir:
- validacoes estaticas minimas;
- regras de emissao no backend;
- requisitos para decode/disasm/assembler.
9. Politica de CAP, certificacao e telemetria para VM-owned.
Definir:
- se VM-owned usa ou nao capability;
- como custo entra em certificacao;
- o que e observavel em telemetria.
10. Migração.
Definir:
- como coexistem superficies antigas e novas;
- quando e como descontinuar chamadas legadas.
## Shape Inicial de Referencia (Nao Decisao)
Este shape e apenas base de discussao para fechar bytecode/ABI, sem efeito normativo ate decisao formal:
```text
vm_service_call(service_id, op_id, args...) -> (status, ret...)
```
Exemplos candidatos:
- `Input.getPad() -> (status, pad_ref)`
- `Input.getTouch() -> (status, touch_ref)`
- `RndG.pcg32(seed) -> (status, rng_ref)`
Onde `pad_ref`, `touch_ref` e `rng_ref` sao builtins/handles VM-owned.
## Sistemas Candidatos Para Cobertura
Esta agenda deve discutir explicitamente, alem de input/random/window:
- frame clock/tick index para consultas deterministicas de tempo de jogo;
- event queue VM-owned para UI/app;
- text input para apps com janelas;
- notificacoes de sistema para app.
- validacoes estaticas obrigatorias;
- requisitos de assembler/disassembler para intrinsics stateful;
- estrategia de diagnostico para mismatch de assinatura/layout.
## Open Questions de Arquitetura
1. Qual e o entrypoint de pre-load para VM-owned: `HOSTCALL + kind=vm` em tabela unificada, ou tabela dedicada?
2. Como representar retorno de builtin em slots: `HeapRef<TBuiltin>`, handle numerico tipado, ou forma hibrida?
3. Como evoluir a interface de intrinsic para receber contexto VM/runtime (alem de `args`) sem quebrar o que ja existe?
4. Input usa singleton por dispositivo/frame ou instancia nova por chamada?
5. Quais campos de `Pad` e `Touch` sao estritamente read-only para guest, e como bloquear escrita indevida?
6. Qual politica de lifetime dos snapshots de input entre frames (substituicao, aliasing, invalidacao)?
7. Random sera estritamente deterministico por seed explicitamente passado, ou existe modo sem seed (host entropy)?
8. Qual taxonomia canonica de `status` compartilhada entre servicos VM-owned (input/random/window)?
9. Como o verifier valida compatibilidade entre assinatura esperada pelo FE e metadados reais do service registry?
10. Como garantir compatibilidade binaria quando um builtin evoluir layout entre versoes?
11. Como tratar custo/certificacao para VM-owned sem reintroduzir modelo de capability desnecessario?
12. Window system exigira fila/event loop VM-owned; como evitar acoplamento indevido com scheduler do runtime?
13. Qual estrategia de migracao para remover chamadas legadas de input sem quebrar toolchain antiga?
1. `HeapRef<TBuiltin>` sera o padrao unico para recurso stateful ou teremos handles numericos tipados em alguns dominios?
2. Qual shape final da interface de intrinsic com contexto VM/runtime?
3. Qual politica de roots/GC/lifetime para recursos VM-owned vivos entre frames?
4. Como impedir aliasing perigoso quando multiplas referencias apontarem para o mesmo recurso stateful?
5. Random stateful deve permitir modo nao deterministico (entropy host) ou apenas deterministico por seed?
6. Qual taxonomia minima de `status` para servicos VM-owned stateful sem criar enum global monolitico?
7. Quais gatilhos tecnicos justificariam introduzir no futuro um mecanismo simbolico adicional (ex.: tabela dedicada), em vez de manter IDs finais?
8. Como garantir que extensoes para window/app model nao enrijeçam a base e continuem language-agnostic?
## Dependencias
@ -183,20 +91,21 @@ Esta agenda deve discutir explicitamente, alem de input/random/window:
- `../specs/16a-syscall-policies.md`
- `../decisions/003-vm-owned-byte-transfer-protocol.md`
- `../decisions/004-host-fault-taxonomy.md`
- `../decisions/005-v1-vm-owned-input-intrinsics-and-language-agnostic-surface.md`
## Fora de Escopo
- redesenhar o modelo de syscall host-backed;
- UX final do window manager;
- detalhes de compositor/renderizacao de UI;
- reabrir a decisao de input v1;
- redesenhar o caminho host-backed (`HOSTCALL`/`SYSCALL`);
- UX final de compositor/tema/desktop do window manager;
- protocolo de rede/distribuicao remota.
## Critério de Saida Desta Agenda
## Criterio de Saida Desta Agenda
Pode virar PR quando houver decisao escrita sobre:
- contrato canonico do protocolo VM-owned (call plane + data plane);
- shape e lifecycle de builtins/handles;
- perfil minimo fechado para input, random e window system;
- regras de fault/status e verificacao;
- plano de migracao sem quebra da fronteira de syscall existente.
- contrato stateful VM-owned (resource model + lifecycle);
- forma definitiva de referencia/handle com validade;
- evolucao do executor de intrinsic com contexto VM/runtime;
- perfil minimo fechado para random stateful e base de window resources;
- regras de verificacao e compatibilidade binaria por versao.

View File

@ -0,0 +1,115 @@
# Decision Record - V1 VM-Owned Input Intrinsics and Language-Agnostic Surface
## Status
Accepted
## Contexto
Para destravar bytecode/backend no v1, precisamos fechar o caminho de input sem:
- quebrar a fronteira host-backed atual (`HOSTCALL`/`SYSCALL`);
- copiar estruturas grandes de input na stack a cada consulta;
- acoplar a decisao a uma unica linguagem de frontend.
As discussoes das agendas `010`, `011` e `017` convergiram para:
- input deve sair do caminho de syscall legado;
- input deve ser VM-owned, leitura-only e deterministico por frame;
- a linguagem pode expor API encadeada ergonomica (ex.: PBS), mas isso nao define o contrato normativo do runtime.
## Decisao
### 1. Fronteira host-backed permanece inalterada
- `HOSTCALL` + tabela `SYSC` + `SYSCALL` continuam como caminho host-backed oficial.
- Esta decisao nao unifica nem altera o contrato de syscall atual.
### 2. Input v1 e VM-owned por intrinsics finais
- Input v1 e exposto por intrinsics VM-owned.
- O backend emite `INTRINSIC <id_final>` diretamente.
- Nao sera introduzida tabela de pre-load adicional para VM-owned no v1.
### 3. Surface de linguagem e livre (nao hardcoded)
- O contrato normativo e de VM/bytecode, nao de sintaxe de uma linguagem.
- Frontends podem expor APIs diferentes, desde que mapeiem 1:1 para intrinsics versionados.
- Exemplo PBS (nao normativo): `Input.pad().up().hold()`.
### 4. Semantica de snapshot por frame
- O host captura estado de input no inicio do frame logico.
- A VM usa esse snapshot congelado durante todo o frame.
- Leitura repetida no mesmo frame retorna o mesmo valor observavel.
### 5. Modelo de dispositivos v1
- Elementos obrigatorios de input em todas as plataformas: `button`, `pad`, `touch`.
- Perfil Prometeu handheld v1: um `pad` e um `touch` single-point.
- `touch` preserva ultimo valor conhecido (incluindo arraste) conforme snapshot vigente.
### 6. Politica de acesso e fault
- Input e leitura-only para userland.
- Nao ha capability gate nem impacto de certificacao para leitura de input.
- Falhas operacionais devem seguir a taxonomia de fault vigente (`status` quando aplicavel; `Trap` para erro estrutural).
### 7. Consequencia de arquitetura imediata
- Builtins de input devem ser tratados como referencias/superficies VM-owned, sem copia "mastodonte" por chamada.
- O executor de intrinsic atual pode atender casos read-only de snapshot no v1.
- Casos stateful com alocacao/lifecycle (ex.: RNG com instancia, mapas VM-owned, window resources) ficam para agenda posterior.
## Exemplo de Mapeamento (Ilustrativo, nao normativo)
Objetivo da linguagem:
```text
Input.pad().up().hold()
```
Possivel lowering:
```text
INTRINSIC input.pad@1
INTRINSIC input.pad.up@1
INTRINSIC input.button.hold@1
```
Os nomes acima sao apenas ilustrativos; o contrato real e o ID/versionamento canonico do intrinsic definido pelo toolchain/runtime.
## Consequencias
### Positivas
- destrava o fechamento do bytecode para backend no v1;
- elimina dependencia de syscall para consulta de input;
- preserva compatibilidade da infraestrutura host-backed existente;
- evita acoplamento prematuro a uma unica linguagem.
### Custos
- exige registry de intrinsics VM-owned consistente entre FE/backend/runtime;
- exige disciplina de versionamento 1:1 por operacao;
- adia para etapa posterior o protocolo stateful completo com `HeapRef`.
## Fora de Escopo
- protocolo generico VM-owned stateful com lifecycle de recursos;
- random com instancia alocada e ownership explicito;
- window system/app model completo;
- redesign de `HOSTCALL`/`SYSCALL`.
## Follow-up Obrigatorio
- agenda `010-input-intrinsics-surface.md` deve considerar input v1 fechado;
- agenda `011-input-frame-semantics-and-portability.md` deve considerar semantica base fechada;
- agenda `017-vm-owned-builtins-protocol-and-system-services.md` fica restrita a pos-v1 (stateful/`HeapRef`).
Specs a alinhar/confirmar:
- `../virtual-machine/ISA_CORE.md`
- `../specs/16-host-abi-and-syscalls.md`
- `../specs/06-input-peripheral.md`
- `../specs/07-touch-peripheral.md`

View File

@ -18,3 +18,4 @@ Decisoes atuais:
- `003-vm-owned-byte-transfer-protocol.md`
- `004-host-fault-taxonomy.md`
- `005-v1-vm-owned-input-intrinsics-and-language-agnostic-surface.md`

View File

@ -1,6 +1,6 @@
# Input Peripheral (Input System)
# Input Peripheral (VM-Owned Snapshot Input)
Domain: virtual hardware: input
Domain: virtual hardware: input
Function: normative
Didactic companion: [`../learn/input-mental-model.md`](../learn/input-mental-model.md)
@ -11,154 +11,99 @@ This chapter defines the runtime-facing input contract of PROMETEU.
Core contract:
- input is exposed as state, not asynchronous callback flow;
- sampling occurs at a defined moment in the logical frame;
- queries are deterministic during the frame;
- logical identifiers are stable across host devices.
- input is exposed as VM-owned snapshot state;
- sampling happens at a deterministic point of each logical frame;
- queries are deterministic inside the same frame;
- input access uses VM-owned intrinsics (not host syscalls in v1).
## 2 Input Devices
## 2 Devices and Surface
PROMETEU abstracts different devices into a common interface.
For v1, the input domain includes:
Typical devices:
- `pad`
- `touch`
- `button` (as a nested state surface used by `pad` and `touch`)
- digital control (D-Pad + botões)
- keyboard (mapped to buttons)
- gamepad
- touch (mobile, mapped)
No analog axis is part of the v1 input contract.
Regardless of source, PROMETEU exposes the same logical model.
## 3 Frame Sampling Model
## 3 State Model
### 3.1 Per-Frame State
For each frame, the system maintains:
- current state of buttons
- state from the previous frame
This allows querying:
- button pressed
- button released
- button held down
### 3.2 Query Types
Typical operations:
```
input.btn(id)// button is pressed
input.btnp(id)// button was pressed this frame
input.btnr(id)// button was released this frame
```
These functions:
- are purely advisory
- do not change state
- have an explicit cost
## 4 Sampling Moment
The input state is captured **at the beginning of each frame**, before the `UPDATE` phase.
Input state is captured at the beginning of each logical frame, before update logic runs.
Conceptual flow:
```
```text
FRAME N:
SAMPLEINPUT
SAMPLE_INPUT
UPDATE
DRAW
AUDIO
SYNC
```
Throughout the entire frame:
Within the same frame:
- the input state is immutable
- repeated calls return the same value
- input state is immutable;
- repeated reads return the same values.
## 5 Determinism and Reproducibility
## 4 State Semantics
The PROMETEU input model guarantees:
`button` exposes:
- same sequence of inputs
- same frames
- same results
- `pressed: bool` (true only on transition up -> down in this frame)
- `released: bool` (true only on transition down -> up in this frame)
- `down: bool` (true while physically pressed in this frame snapshot)
- `hold: int` (count of consecutive pressed frames)
This allows:
`pad` is a fixed set of buttons:
- execution playback
- deterministic replays
- reliable certification
- `up`, `down`, `left`, `right`
- `a`, `b`, `x`, `y`
- `l`, `r`, `start`, `select`
Input can be:
`touch` exposes:
- recorded
- played back
- artificially injected
- `x: int`
- `y: int`
- `button` with the same `pressed/released/down/hold` semantics
## 6 Input and CAP
PROMETEU handheld v1 uses single-touch active pointer semantics.
Input operations:
## 5 Access Model
- consume few cycles
- participate in the per-frame budget
- appear in certification reports
Input is VM-owned in v1:
Example:
- frontend surfaces may be ergonomic and language-specific;
- lowering maps to VM-owned `INTRINSIC <id_final>`;
- no input syscall is required in the host ABI path.
```
Frame 18231:
input.btn():12cycles
Illustrative (language-level) shape:
```text
Input.pad().up().hold()
Input.touch().x()
```
Although cheap, input **is not free**.
The normative contract is intrinsic identity/version and semantics, not source syntax.
## 7 Button Mapping
## 6 Determinism and Replay
### 7.1 Logical Identifiers
Given the same per-frame input snapshots, execution must produce the same observable results.
PROMETEU defines logical button identifiers:
This enables:
- `UP`, `DOWN`, `LEFT`, `RIGHT`
- `A`, `B`, `X`, `Y`
- `START`, `SELECT`
- deterministic replay;
- deterministic certification analysis;
- controlled input injection in tests/tooling.
The physical mapping:
## 7 Capability and Certification
- depends on the platform
- is resolved outside the VM
- is transparent to the program
Input reads are not capability-gated by syscall capability policy in v1.
### 7.2 Portability
Input access is VM-owned and should not be reported as host syscall consumption.
The same PROMETEU cartridge:
## 8 Portability
- runs on a keyboard
- runs on a gamepad
- runs on touch
All platforms must provide the mandatory input elements (`pad`, `touch`, `button`) to the runtime.
Without any code changes.
## 8 Analog Input (Optional)
PROMETEU can expose analog axes explicitly:
```
input.axis(id)
```
Characteristics:
- normalized value (e.g.: -1.0 to 1.0)
- explicit cost
- per-frame update
Analog input:
- is not mandatory
- does not replace digital input
- must be used consciously
Platform differences in physical device mapping are resolved outside VM semantics.

View File

@ -37,7 +37,6 @@ Example:
```
("gfx", "present", 1)
("input", "state", 1)
("audio", "play", 2)
```
@ -47,6 +46,8 @@ This identity is:
- 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:

View File

@ -41,12 +41,13 @@ Example groups:
- `gfx`
- `audio`
- `input`
- `asset`
- `memcard`
Capability checks exist to constrain which host-managed surfaces a cartridge may use.
Input in v1 is VM-owned intrinsic surface and is not capability-gated through syscall policy.
## 3 Interaction with the Garbage Collector
The VM heap and host-managed memory are separate.