diff --git a/crates/console/prometeu-hal/src/syscalls.rs b/crates/console/prometeu-hal/src/syscalls.rs index 2290c651..7349a385 100644 --- a/crates/console/prometeu-hal/src/syscalls.rs +++ b/crates/console/prometeu-hal/src/syscalls.rs @@ -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, diff --git a/crates/console/prometeu-hal/src/syscalls/domains/input.rs b/crates/console/prometeu-hal/src/syscalls/domains/input.rs deleted file mode 100644 index acbb591a..00000000 --- a/crates/console/prometeu-hal/src/syscalls/domains/input.rs +++ /dev/null @@ -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, - ), -]; diff --git a/crates/console/prometeu-hal/src/syscalls/domains/mod.rs b/crates/console/prometeu-hal/src/syscalls/domains/mod.rs index 07f6abcf..975d5a03 100644 --- a/crates/console/prometeu-hal/src/syscalls/domains/mod.rs +++ b/crates/console/prometeu-hal/src/syscalls/domains/mod.rs @@ -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 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", diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs index f3a466e5..485ac069 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs @@ -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 { match args.get(index).ok_or_else(|| VmFault::Panic(format!("Missing {}", field)))? { Value::String(value) => Ok(value.clone()), diff --git a/crates/console/prometeu-vm/src/builtins.rs b/crates/console/prometeu-vm/src/builtins.rs index 845761b7..f4729e71 100644 --- a/crates/console/prometeu-vm/src/builtins.rs +++ b/crates/console/prometeu-vm/src/builtins.rs @@ -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, IntrinsicExecutionError>; +pub type IntrinsicImplementation = + fn(&[Value], &mut HostContext<'_>) -> Result, 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, 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, IntrinsicExecutionError> { if args.len() != 4 { return Err(IntrinsicExecutionError::ArityMismatch { expected: 4, got: args.len() }); } @@ -457,7 +743,10 @@ fn vec2_dot(args: &[Value]) -> Result, IntrinsicExecutionError> { Ok(vec![Value::Float((ax * bx) + (ay * by))]) } -fn vec2_length(args: &[Value]) -> Result, IntrinsicExecutionError> { +fn vec2_length( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { if args.len() != 2 { return Err(IntrinsicExecutionError::ArityMismatch { expected: 2, got: args.len() }); } @@ -466,6 +755,250 @@ fn vec2_length(args: &[Value]) -> Result, IntrinsicExecutionError> { Ok(vec![Value::Float((x * x + y * y).sqrt())]) } +fn input_pad( + _args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + Ok(vec![Value::Int64(PAD_HANDLE)]) +} + +fn input_touch( + _args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + Ok(vec![Value::Int64(TOUCH_HANDLE)]) +} + +fn input_pad_up( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "up")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE)]) +} + +fn input_pad_down( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "down")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 1)]) +} + +fn input_pad_left( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "left")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 2)]) +} + +fn input_pad_right( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "right")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 3)]) +} + +fn input_pad_a( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "a")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 4)]) +} + +fn input_pad_b( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "b")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 5)]) +} + +fn input_pad_x( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "x")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 6)]) +} + +fn input_pad_y( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "y")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 7)]) +} + +fn input_pad_l( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "l")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 8)]) +} + +fn input_pad_r( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "r")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 9)]) +} + +fn input_pad_start( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "start")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 10)]) +} + +fn input_pad_select( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_pad_handle(args, "select")?; + Ok(vec![Value::Int64(PAD_BUTTON_BASE + 11)]) +} + +fn input_touch_button( + args: &[Value], + _ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + expect_touch_handle(args, "button")?; + Ok(vec![Value::Int64(TOUCH_BUTTON_CARRIER)]) +} + +fn input_touch_x( + args: &[Value], + ctx: &mut HostContext<'_>, +) -> Result, 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, 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, IntrinsicExecutionError> { + let button = resolve_button(args, "pressed", ctx)?; + Ok(vec![Value::Boolean(button.pressed)]) +} + +fn input_button_released( + args: &[Value], + ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + let button = resolve_button(args, "released", ctx)?; + Ok(vec![Value::Boolean(button.released)]) +} + +fn input_button_down( + args: &[Value], + ctx: &mut HostContext<'_>, +) -> Result, IntrinsicExecutionError> { + let button = resolve_button(args, "down", ctx)?; + Ok(vec![Value::Boolean(button.down)]) +} + +fn input_button_hold( + args: &[Value], + ctx: &mut HostContext<'_>, +) -> Result, 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 { + 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 { + 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"); diff --git a/crates/console/prometeu-vm/src/virtual_machine.rs b/crates/console/prometeu-vm/src/virtual_machine.rs index 5c0402b8..cd6f88c9 100644 --- a/crates/console/prometeu-vm/src/virtual_machine.rs +++ b/crates/console/prometeu-vm/src/virtual_machine.rs @@ -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"); diff --git a/crates/console/prometeu-vm/tests/syscall_input_removed.rs b/crates/console/prometeu-vm/tests/syscall_input_removed.rs new file mode 100644 index 00000000..9b54dbd5 --- /dev/null +++ b/crates/console/prometeu-vm/tests/syscall_input_removed.rs @@ -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), + } +} diff --git a/crates/console/prometeu-vm/tests/syscall_multi_return.rs b/crates/console/prometeu-vm/tests/syscall_multi_return.rs deleted file mode 100644 index df34a05e..00000000 --- a/crates/console/prometeu-vm/tests/syscall_multi_return.rs +++ /dev/null @@ -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),] - ); -} diff --git a/docs/runtime/agendas/010-input-intrinsics-surface.md b/docs/runtime/agendas/010-input-intrinsics-surface.md index 9d74016d..a4b41a63 100644 --- a/docs/runtime/agendas/010-input-intrinsics-surface.md +++ b/docs/runtime/agendas/010-input-intrinsics-surface.md @@ -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 ` 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. diff --git a/docs/runtime/agendas/011-input-frame-semantics-and-portability.md b/docs/runtime/agendas/011-input-frame-semantics-and-portability.md index ab13d0ff..3df411ea 100644 --- a/docs/runtime/agendas/011-input-frame-semantics-and-portability.md +++ b/docs/runtime/agendas/011-input-frame-semantics-and-portability.md @@ -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. diff --git a/docs/runtime/agendas/017-vm-owned-builtins-protocol-and-system-services.md b/docs/runtime/agendas/017-vm-owned-builtins-protocol-and-system-services.md index ed47b8cb..2fbb4088 100644 --- a/docs/runtime/agendas/017-vm-owned-builtins-protocol-and-system-services.md +++ b/docs/runtime/agendas/017-vm-owned-builtins-protocol-and-system-services.md @@ -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 `; +- 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 ` 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 `; - - `kind=vm` -> `INTRINSIC `. -- 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`, 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`, 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` 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. diff --git a/docs/runtime/decisions/005-v1-vm-owned-input-intrinsics-and-language-agnostic-surface.md b/docs/runtime/decisions/005-v1-vm-owned-input-intrinsics-and-language-agnostic-surface.md new file mode 100644 index 00000000..3f2d9ece --- /dev/null +++ b/docs/runtime/decisions/005-v1-vm-owned-input-intrinsics-and-language-agnostic-surface.md @@ -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 ` 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` diff --git a/docs/runtime/decisions/README.md b/docs/runtime/decisions/README.md index e4d672f1..59976e06 100644 --- a/docs/runtime/decisions/README.md +++ b/docs/runtime/decisions/README.md @@ -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` diff --git a/docs/runtime/specs/06-input-peripheral.md b/docs/runtime/specs/06-input-peripheral.md index 30ed2c4c..02458c51 100644 --- a/docs/runtime/specs/06-input-peripheral.md +++ b/docs/runtime/specs/06-input-peripheral.md @@ -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 `; +- 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. diff --git a/docs/runtime/specs/16-host-abi-and-syscalls.md b/docs/runtime/specs/16-host-abi-and-syscalls.md index b5b192b7..f4b8bb20 100644 --- a/docs/runtime/specs/16-host-abi-and-syscalls.md +++ b/docs/runtime/specs/16-host-abi-and-syscalls.md @@ -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: diff --git a/docs/runtime/specs/16a-syscall-policies.md b/docs/runtime/specs/16a-syscall-policies.md index fa1e5a3e..e2501d0b 100644 --- a/docs/runtime/specs/16a-syscall-policies.md +++ b/docs/runtime/specs/16a-syscall-policies.md @@ -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.