implemented intrinsecs de input and specs adjustment
This commit is contained in:
parent
1bd921979f
commit
10dfc7bf15
@ -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,
|
||||
|
||||
@ -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,
|
||||
),
|
||||
];
|
||||
@ -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())
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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()),
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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");
|
||||
|
||||
45
crates/console/prometeu-vm/tests/syscall_input_removed.rs
Normal file
45
crates/console/prometeu-vm/tests/syscall_input_removed.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
@ -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),]
|
||||
);
|
||||
}
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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`
|
||||
@ -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`
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user