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:
|
/// Each Syscall has a unique 32-bit ID. The IDs are grouped by category:
|
||||||
/// - **0x0xxx**: System & OS Control
|
/// - **0x0xxx**: System & OS Control
|
||||||
/// - **0x1xxx**: Graphics (GFX)
|
/// - **0x1xxx**: Graphics (GFX)
|
||||||
/// - **0x2xxx**: Input (Gamepad & Touch)
|
/// - **0x2xxx**: Reserved for legacy input syscalls (disabled for v1 VM-owned input)
|
||||||
/// - **0x3xxx**: Audio (PCM & Mixing)
|
/// - **0x3xxx**: Audio (PCM & Mixing)
|
||||||
/// - **0x4xxx**: Filesystem (Sandboxed I/O)
|
/// - **0x4xxx**: Filesystem (Sandboxed I/O)
|
||||||
/// - **0x5xxx**: Logging & Debugging
|
/// - **0x5xxx**: Logging & Debugging
|
||||||
@ -38,31 +38,6 @@ pub enum Syscall {
|
|||||||
GfxSetSprite = 0x1007,
|
GfxSetSprite = 0x1007,
|
||||||
GfxDrawText = 0x1008,
|
GfxDrawText = 0x1008,
|
||||||
GfxClear565 = 0x1010,
|
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,
|
AudioPlaySample = 0x3001,
|
||||||
AudioPlay = 0x3002,
|
AudioPlay = 0x3002,
|
||||||
FsOpen = 0x4001,
|
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 bank;
|
||||||
mod fs;
|
mod fs;
|
||||||
mod gfx;
|
mod gfx;
|
||||||
mod input;
|
|
||||||
mod log;
|
mod log;
|
||||||
mod system;
|
mod system;
|
||||||
|
|
||||||
@ -42,7 +41,6 @@ pub(crate) fn all_entries() -> impl Iterator<Item = &'static SyscallRegistryEntr
|
|||||||
system::ENTRIES
|
system::ENTRIES
|
||||||
.iter()
|
.iter()
|
||||||
.chain(gfx::ENTRIES.iter())
|
.chain(gfx::ENTRIES.iter())
|
||||||
.chain(input::ENTRIES.iter())
|
|
||||||
.chain(audio::ENTRIES.iter())
|
.chain(audio::ENTRIES.iter())
|
||||||
.chain(fs::ENTRIES.iter())
|
.chain(fs::ENTRIES.iter())
|
||||||
.chain(log::ENTRIES.iter())
|
.chain(log::ENTRIES.iter())
|
||||||
|
|||||||
@ -23,31 +23,6 @@ impl Syscall {
|
|||||||
0x1007 => Some(Self::GfxSetSprite),
|
0x1007 => Some(Self::GfxSetSprite),
|
||||||
0x1008 => Some(Self::GfxDrawText),
|
0x1008 => Some(Self::GfxDrawText),
|
||||||
0x1010 => Some(Self::GfxClear565),
|
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),
|
0x3001 => Some(Self::AudioPlaySample),
|
||||||
0x3002 => Some(Self::AudioPlay),
|
0x3002 => Some(Self::AudioPlay),
|
||||||
0x4001 => Some(Self::FsOpen),
|
0x4001 => Some(Self::FsOpen),
|
||||||
@ -90,31 +65,6 @@ impl Syscall {
|
|||||||
Self::GfxSetSprite => "GfxSetSprite",
|
Self::GfxSetSprite => "GfxSetSprite",
|
||||||
Self::GfxDrawText => "GfxDrawText",
|
Self::GfxDrawText => "GfxDrawText",
|
||||||
Self::GfxClear565 => "GfxClear565",
|
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::AudioPlaySample => "AudioPlaySample",
|
||||||
Self::AudioPlay => "AudioPlay",
|
Self::AudioPlay => "AudioPlay",
|
||||||
Self::FsOpen => "FsOpen",
|
Self::FsOpen => "FsOpen",
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use prometeu_bytecode::{TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_TYPE, Value};
|
use prometeu_bytecode::{TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_TYPE, Value};
|
||||||
use prometeu_hal::asset::{BankType, LoadStatus, SlotRef};
|
use prometeu_hal::asset::{BankType, LoadStatus, SlotRef};
|
||||||
use prometeu_hal::button::Button;
|
|
||||||
use prometeu_hal::color::Color;
|
use prometeu_hal::color::Color;
|
||||||
use prometeu_hal::log::{LogLevel, LogSource};
|
use prometeu_hal::log::{LogLevel, LogSource};
|
||||||
use prometeu_hal::sprite::Sprite;
|
use prometeu_hal::sprite::Sprite;
|
||||||
@ -51,51 +50,6 @@ impl VirtualMachineRuntime {
|
|||||||
pub(crate) fn get_color(&self, value: i64) -> Color {
|
pub(crate) fn get_color(&self, value: i64) -> Color {
|
||||||
Color::from_raw(value as u16)
|
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 {
|
impl NativeInterface for VirtualMachineRuntime {
|
||||||
@ -232,111 +186,6 @@ impl NativeInterface for VirtualMachineRuntime {
|
|||||||
hw.gfx_mut().clear(Color::from_raw(color_val as u16));
|
hw.gfx_mut().clear(Color::from_raw(color_val as u16));
|
||||||
Ok(())
|
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 => {
|
Syscall::AudioPlaySample => {
|
||||||
let sample_id = expect_int(args, 0)? as u32;
|
let sample_id = expect_int(args, 0)? as u32;
|
||||||
let voice_id = expect_int(args, 1)? as usize;
|
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> {
|
fn expect_string(args: &[Value], index: usize, field: &str) -> Result<String, VmFault> {
|
||||||
match args.get(index).ok_or_else(|| VmFault::Panic(format!("Missing {}", field)))? {
|
match args.get(index).ok_or_else(|| VmFault::Panic(format!("Missing {}", field)))? {
|
||||||
Value::String(value) => Ok(value.clone()),
|
Value::String(value) => Ok(value.clone()),
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use prometeu_bytecode::Value;
|
use prometeu_bytecode::Value;
|
||||||
|
use prometeu_hal::HostContext;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct BuiltinTypeKey {
|
pub struct BuiltinTypeKey {
|
||||||
@ -176,6 +177,8 @@ impl BuiltinConstMeta {
|
|||||||
pub enum IntrinsicExecutionError {
|
pub enum IntrinsicExecutionError {
|
||||||
ArityMismatch { expected: usize, got: usize },
|
ArityMismatch { expected: usize, got: usize },
|
||||||
TypeMismatch { index: usize, expected: AbiType },
|
TypeMismatch { index: usize, expected: AbiType },
|
||||||
|
InvalidBuiltinCarrier { owner: &'static str, name: &'static str, carrier: i64 },
|
||||||
|
HardwareUnavailable,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@ -184,7 +187,8 @@ pub enum BuiltinValueError {
|
|||||||
TypeMismatch { index: usize, expected: AbiType },
|
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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct IntrinsicMeta {
|
pub struct IntrinsicMeta {
|
||||||
@ -245,6 +249,9 @@ impl BuiltinConstSlotValue {
|
|||||||
const COLOR: BuiltinTypeKey = BuiltinTypeKey::new("color", 1);
|
const COLOR: BuiltinTypeKey = BuiltinTypeKey::new("color", 1);
|
||||||
const VEC2: BuiltinTypeKey = BuiltinTypeKey::new("vec2", 1);
|
const VEC2: BuiltinTypeKey = BuiltinTypeKey::new("vec2", 1);
|
||||||
const PIXEL: BuiltinTypeKey = BuiltinTypeKey::new("pixel", 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 COLOR_LAYOUT: [AbiType; 1] = [AbiType::Builtin(COLOR)];
|
||||||
const VEC2_LAYOUT: [AbiType; 2] =
|
const VEC2_LAYOUT: [AbiType; 2] =
|
||||||
@ -254,6 +261,9 @@ const PIXEL_LAYOUT: [AbiType; 3] = [
|
|||||||
AbiType::Scalar(BuiltinScalarType::Int),
|
AbiType::Scalar(BuiltinScalarType::Int),
|
||||||
AbiType::Builtin(COLOR),
|
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] = [
|
const VEC2_FIELDS: [BuiltinFieldMeta; 2] = [
|
||||||
BuiltinFieldMeta {
|
BuiltinFieldMeta {
|
||||||
@ -291,7 +301,7 @@ const PIXEL_FIELDS: [BuiltinFieldMeta; 3] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const BUILTIN_TYPES: [BuiltinTypeMeta; 3] = [
|
const BUILTIN_TYPES: [BuiltinTypeMeta; 6] = [
|
||||||
BuiltinTypeMeta {
|
BuiltinTypeMeta {
|
||||||
id: 0x0001,
|
id: 0x0001,
|
||||||
name: COLOR.name,
|
name: COLOR.name,
|
||||||
@ -319,6 +329,33 @@ const BUILTIN_TYPES: [BuiltinTypeMeta; 3] = [
|
|||||||
flat_slot_layout: &PIXEL_LAYOUT,
|
flat_slot_layout: &PIXEL_LAYOUT,
|
||||||
flat_slot_width: 3,
|
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] =
|
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 SINGLE_FLOAT_RET: [AbiType; 1] = [AbiType::Scalar(BuiltinScalarType::Float)];
|
||||||
const VEC2_LENGTH_ARGS: [AbiType; 2] =
|
const VEC2_LENGTH_ARGS: [AbiType; 2] =
|
||||||
[AbiType::Scalar(BuiltinScalarType::Float), AbiType::Scalar(BuiltinScalarType::Float)];
|
[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 {
|
IntrinsicMeta {
|
||||||
id: 0x1000,
|
id: 0x1000,
|
||||||
owner: "vec2",
|
owner: "vec2",
|
||||||
@ -364,6 +411,237 @@ const INTRINSICS: [IntrinsicMeta; 2] = [
|
|||||||
may_allocate: false,
|
may_allocate: false,
|
||||||
implementation: vec2_length,
|
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> {
|
pub fn lookup_builtin_type(name: &str, version: u16) -> Option<&'static BuiltinTypeMeta> {
|
||||||
@ -446,7 +724,15 @@ fn validate_values_against_layout(
|
|||||||
Ok(())
|
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 {
|
if args.len() != 4 {
|
||||||
return Err(IntrinsicExecutionError::ArityMismatch { expected: 4, got: args.len() });
|
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))])
|
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 {
|
if args.len() != 2 {
|
||||||
return Err(IntrinsicExecutionError::ArityMismatch { expected: 2, got: args.len() });
|
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())])
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -546,26 +1079,41 @@ mod tests {
|
|||||||
assert_eq!(length.arg_layout.len(), 2);
|
assert_eq!(length.arg_layout.len(), 2);
|
||||||
assert_eq!(length.ret_layout.len(), 1);
|
assert_eq!(length.ret_layout.len(), 1);
|
||||||
assert_eq!(lookup_intrinsic_by_id(length.id).map(|meta| meta.key()), Some(length.key()));
|
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]
|
#[test]
|
||||||
fn intrinsic_implementations_are_registered_without_syscalls() {
|
fn intrinsic_implementations_are_registered_without_syscalls() {
|
||||||
let dot = lookup_intrinsic("vec2", "dot", 1).expect("vec2.dot must exist");
|
let dot = lookup_intrinsic("vec2", "dot", 1).expect("vec2.dot must exist");
|
||||||
let result = (dot.implementation)(&[
|
let mut ctx = HostContext::new(None);
|
||||||
Value::Float(1.0),
|
let result = (dot.implementation)(
|
||||||
Value::Float(2.0),
|
&[Value::Float(1.0), Value::Float(2.0), Value::Float(3.0), Value::Float(4.0)],
|
||||||
Value::Float(3.0),
|
&mut ctx,
|
||||||
Value::Float(4.0),
|
)
|
||||||
])
|
|
||||||
.expect("dot implementation must execute");
|
.expect("dot implementation must execute");
|
||||||
assert_eq!(result, vec![Value::Float(11.0)]);
|
assert_eq!(result, vec![Value::Float(11.0)]);
|
||||||
|
|
||||||
let length = lookup_intrinsic("vec2", "length", 1).expect("vec2.length must exist");
|
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");
|
.expect("length implementation must execute");
|
||||||
assert_eq!(result, vec![Value::Float(5.0)]);
|
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]
|
#[test]
|
||||||
fn pixel_flat_values_validate_with_color_carrier_slot() {
|
fn pixel_flat_values_validate_with_color_carrier_slot() {
|
||||||
let pixel = lookup_builtin_type("pixel", 1).expect("pixel builtin must exist");
|
let pixel = lookup_builtin_type("pixel", 1).expect("pixel builtin must exist");
|
||||||
|
|||||||
@ -1111,7 +1111,7 @@ impl VirtualMachine {
|
|||||||
}
|
}
|
||||||
args.reverse();
|
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(
|
crate::IntrinsicExecutionError::ArityMismatch { expected, got } => self.trap(
|
||||||
TRAP_INVALID_INTRINSIC,
|
TRAP_INVALID_INTRINSIC,
|
||||||
OpCode::Intrinsic as u16,
|
OpCode::Intrinsic as u16,
|
||||||
@ -1130,6 +1130,22 @@ impl VirtualMachine {
|
|||||||
),
|
),
|
||||||
pc_at_intrinsic,
|
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 {
|
intrinsic.validate_result_values(&results).map_err(|err| match err {
|
||||||
@ -1151,6 +1167,24 @@ impl VirtualMachine {
|
|||||||
),
|
),
|
||||||
pc_at_intrinsic,
|
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 {
|
for value in results {
|
||||||
@ -2331,6 +2365,34 @@ mod tests {
|
|||||||
assert_eq!(vm.operand_stack, vec![Value::Float(5.0)]);
|
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]
|
#[test]
|
||||||
fn test_invalid_intrinsic_trap_is_distinct_from_syscall() {
|
fn test_invalid_intrinsic_trap_is_distinct_from_syscall() {
|
||||||
let rom = assemble("INTRINSIC 0xDEADBEEF\nHALT").expect("assemble");
|
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`;
|
## O Que Foi Fechado
|
||||||
- `touch`.
|
|
||||||
|
|
||||||
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;
|
## Efeito Pratico
|
||||||
- implícito no ISA;
|
|
||||||
- indefinido no verifier;
|
|
||||||
- lembrado tarde demais na fase de implementacao.
|
|
||||||
|
|
||||||
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;
|
## Risco Residual
|
||||||
- a linguagem/runtime quer expor `pad` e `touch` como surface built-in do guest;
|
|
||||||
- mas a implementacao ainda coleta esse estado por syscalls.
|
|
||||||
|
|
||||||
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`
|
## Follow-up
|
||||||
- `InputGetPadPressed`
|
|
||||||
- `InputGetPadReleased`
|
|
||||||
- `InputGetPadHold`
|
|
||||||
- `InputPadSnapshot`
|
|
||||||
- `InputTouchSnapshot`
|
|
||||||
- `TouchGetX`
|
|
||||||
- `TouchGetY`
|
|
||||||
- `TouchIsDown`
|
|
||||||
- `TouchIsPressed`
|
|
||||||
- `TouchIsReleased`
|
|
||||||
- `TouchGetHold`
|
|
||||||
- `TouchGetFinger`
|
|
||||||
- `PadGetUp` ate `PadGetSelect`
|
|
||||||
|
|
||||||
## Dor
|
- Validar alinhamento de specs (`06`, `16`, `16a`) com a decisao `005`.
|
||||||
|
- Validar runtime/verifier contra IDs finais de input intrinsics.
|
||||||
- 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.
|
|
||||||
|
|||||||
@ -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`;
|
## O Que Foi Fechado
|
||||||
- `touch`.
|
|
||||||
|
|
||||||
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;
|
- O runtime deve expor input como leitura deterministica de snapshot VM-owned.
|
||||||
- `Button` ja eh a unidade de estado usada por `pad` e pelo toque ativo;
|
- O host deve fornecer os sinais necessarios para pad/touch a cada frame.
|
||||||
- mas o runtime ainda expoe a leitura desse estado por syscalls em vez de intrinsics.
|
|
||||||
|
|
||||||
## Dor
|
## Risco Residual
|
||||||
|
|
||||||
- ambiguidades sobre quando o input e amostrado;
|
- Nenhum bloqueador de bytecode v1 permanece nesta agenda.
|
||||||
- risco de divergencia entre plataformas sobre pressed/held/released do `pad`;
|
- Edge cases de app model/window system ficam para agendas futuras (fora de input v1).
|
||||||
- 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.
|
|
||||||
|
|
||||||
## Alvo da Discussao
|
## Follow-up
|
||||||
|
|
||||||
Fixar a semantica temporal e de portabilidade do dominio de input.
|
- Manter `06-input-peripheral.md` e `07-touch-peripheral.md` alinhadas com esta semantica.
|
||||||
|
- Cobrir no runtime testes de regressao para determinismo por frame.
|
||||||
`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.
|
|
||||||
|
|||||||
@ -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;
|
Fonte normativa do recorte v1:
|
||||||
- random;
|
|
||||||
- window system integrado ao SO;
|
|
||||||
- builtins com leitura/escrita e lifecycle.
|
|
||||||
|
|
||||||
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;
|
Falta fechar o protocolo VM-owned para casos stateful que exigem recurso vivo, ownership e lifecycle, por exemplo:
|
||||||
- surfaces de linguagem (FE) e backend podem divergir do runtime real;
|
|
||||||
- faltam regras canonicas para criar/ler/escrever/destruir recursos VM-owned;
|
- random com instancia/estado (`RngRef`);
|
||||||
- verifier e toolchain ficam sem alvo estavel para validacao de chamadas VM-owned;
|
- estruturas VM-owned (ex.: map/set/colecoes runtime-owned);
|
||||||
- discussoes de input/random/window tendem a virar agendas grandes e misturadas.
|
- 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
|
## 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;
|
- fronteira host-backed atual intacta;
|
||||||
- compatibilidade com o ABI atual de slots;
|
- ABI estavel por slots;
|
||||||
- determinismo por frame.
|
- 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:
|
1. Modelo canonico de recurso VM-owned stateful.
|
||||||
- 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.
|
|
||||||
Definir:
|
Definir:
|
||||||
- ponto de entrada no bytecode para VM-owned:
|
- representacao de handle/referencia (`HeapRef<TBuiltin>`, handle tipado, ou hibrido);
|
||||||
- `INTRINSIC` com registry formal; ou
|
- ownership e regra anti-stale (generation/version);
|
||||||
- `HOSTCALL + kind=vm` com patch de loader para `INTRINSIC`;
|
- lifecycle minimo (`create`, `read`, `update`, `destroy`).
|
||||||
- namespace de IDs;
|
|
||||||
- versionamento por operacao;
|
|
||||||
- metadados canonicos (`arg_slots`, `ret_slots`, layout, custo, efeito, determinismo).
|
|
||||||
|
|
||||||
2. Plano de dados builtin.
|
2. Evolucao da execucao de intrinsic para servicos stateful.
|
||||||
Definir:
|
Definir:
|
||||||
- como builtins sao representados em slots;
|
- como a operacao intrinsic recebe contexto VM/runtime de forma segura;
|
||||||
- quando um builtin e inline e quando vira handle;
|
- como manter compatibilidade com intrinsics atuais read-only.
|
||||||
- regras de leitura/escrita/atualizacao em stack e heap;
|
|
||||||
- regras de compatibilidade de layout entre versoes.
|
|
||||||
|
|
||||||
3. Modelo de recursos VM-owned.
|
3. Contrato de ABI/meta para operacoes VM-owned stateful.
|
||||||
Definir:
|
Definir:
|
||||||
- formato de handle (incluindo estrategia anti-stale);
|
- metadados canonicos por operacao (`arg_slots`, `ret_slots`, efeito, custo, determinismo);
|
||||||
- ownership;
|
- namespace/versionamento de IDs;
|
||||||
- lifecycle (`create`, `read`, `update`, `destroy`);
|
- criterio de compatibilidade binaria entre versoes.
|
||||||
- invariantes de validade.
|
|
||||||
- politica de singleton vs instancia alocada para builtins de snapshot (ex.: `Pad`, `Touch`).
|
|
||||||
|
|
||||||
4. Perfil de input no protocolo.
|
4. Perfil de random stateful.
|
||||||
Fechar:
|
Fechar:
|
||||||
- como `Input.getPad()` e `Input.getTouch()` mapeiam para o protocolo VM-owned;
|
- superficie minima (`seed` explicita, instancia, leitura de proximo valor);
|
||||||
- se a API retorna ref singleton (`Input.getPad() -> PadRef`) ou recebe destino (`Input.getPad(dst_ref)`), e por que;
|
- regra de determinismo/replay;
|
||||||
- relacao com as syscalls atuais de input no periodo de transicao (se houver);
|
- politica de status/fault.
|
||||||
- garantia de snapshot congelado por frame para leitura deterministica.
|
|
||||||
|
|
||||||
5. Perfil de random no protocolo.
|
5. Perfil de window system/app model.
|
||||||
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.
|
|
||||||
Fechar contrato minimo para:
|
Fechar contrato minimo para:
|
||||||
- criacao e destruicao de janelas;
|
- criacao/destruicao de janelas;
|
||||||
- leitura/escrita de estado de janela;
|
- leitura/escrita de estado;
|
||||||
- fila de eventos;
|
- 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:
|
Definir:
|
||||||
- fronteira entre erro estrutural (`Trap`) e resultado operacional (`status`);
|
- validacoes estaticas obrigatorias;
|
||||||
- como erros de handle/recurso/estado invalido sao expostos.
|
- requisitos de assembler/disassembler para intrinsics stateful;
|
||||||
|
- estrategia de diagnostico para mismatch de assinatura/layout.
|
||||||
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.
|
|
||||||
|
|
||||||
## Open Questions de Arquitetura
|
## Open Questions de Arquitetura
|
||||||
|
|
||||||
1. Qual e o entrypoint de pre-load para VM-owned: `HOSTCALL + kind=vm` em tabela unificada, ou tabela dedicada?
|
1. `HeapRef<TBuiltin>` sera o padrao unico para recurso stateful ou teremos handles numericos tipados em alguns dominios?
|
||||||
2. Como representar retorno de builtin em slots: `HeapRef<TBuiltin>`, handle numerico tipado, ou forma hibrida?
|
2. Qual shape final da interface de intrinsic com contexto VM/runtime?
|
||||||
3. Como evoluir a interface de intrinsic para receber contexto VM/runtime (alem de `args`) sem quebrar o que ja existe?
|
3. Qual politica de roots/GC/lifetime para recursos VM-owned vivos entre frames?
|
||||||
4. Input usa singleton por dispositivo/frame ou instancia nova por chamada?
|
4. Como impedir aliasing perigoso quando multiplas referencias apontarem para o mesmo recurso stateful?
|
||||||
5. Quais campos de `Pad` e `Touch` sao estritamente read-only para guest, e como bloquear escrita indevida?
|
5. Random stateful deve permitir modo nao deterministico (entropy host) ou apenas deterministico por seed?
|
||||||
6. Qual politica de lifetime dos snapshots de input entre frames (substituicao, aliasing, invalidacao)?
|
6. Qual taxonomia minima de `status` para servicos VM-owned stateful sem criar enum global monolitico?
|
||||||
7. Random sera estritamente deterministico por seed explicitamente passado, ou existe modo sem seed (host entropy)?
|
7. Quais gatilhos tecnicos justificariam introduzir no futuro um mecanismo simbolico adicional (ex.: tabela dedicada), em vez de manter IDs finais?
|
||||||
8. Qual taxonomia canonica de `status` compartilhada entre servicos VM-owned (input/random/window)?
|
8. Como garantir que extensoes para window/app model nao enrijeçam a base e continuem language-agnostic?
|
||||||
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?
|
|
||||||
|
|
||||||
## Dependencias
|
## Dependencias
|
||||||
|
|
||||||
@ -183,20 +91,21 @@ Esta agenda deve discutir explicitamente, alem de input/random/window:
|
|||||||
- `../specs/16a-syscall-policies.md`
|
- `../specs/16a-syscall-policies.md`
|
||||||
- `../decisions/003-vm-owned-byte-transfer-protocol.md`
|
- `../decisions/003-vm-owned-byte-transfer-protocol.md`
|
||||||
- `../decisions/004-host-fault-taxonomy.md`
|
- `../decisions/004-host-fault-taxonomy.md`
|
||||||
|
- `../decisions/005-v1-vm-owned-input-intrinsics-and-language-agnostic-surface.md`
|
||||||
|
|
||||||
## Fora de Escopo
|
## Fora de Escopo
|
||||||
|
|
||||||
- redesenhar o modelo de syscall host-backed;
|
- reabrir a decisao de input v1;
|
||||||
- UX final do window manager;
|
- redesenhar o caminho host-backed (`HOSTCALL`/`SYSCALL`);
|
||||||
- detalhes de compositor/renderizacao de UI;
|
- UX final de compositor/tema/desktop do window manager;
|
||||||
- protocolo de rede/distribuicao remota.
|
- protocolo de rede/distribuicao remota.
|
||||||
|
|
||||||
## Critério de Saida Desta Agenda
|
## Criterio de Saida Desta Agenda
|
||||||
|
|
||||||
Pode virar PR quando houver decisao escrita sobre:
|
Pode virar PR quando houver decisao escrita sobre:
|
||||||
|
|
||||||
- contrato canonico do protocolo VM-owned (call plane + data plane);
|
- contrato stateful VM-owned (resource model + lifecycle);
|
||||||
- shape e lifecycle de builtins/handles;
|
- forma definitiva de referencia/handle com validade;
|
||||||
- perfil minimo fechado para input, random e window system;
|
- evolucao do executor de intrinsic com contexto VM/runtime;
|
||||||
- regras de fault/status e verificacao;
|
- perfil minimo fechado para random stateful e base de window resources;
|
||||||
- plano de migracao sem quebra da fronteira de syscall existente.
|
- 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`
|
- `003-vm-owned-byte-transfer-protocol.md`
|
||||||
- `004-host-fault-taxonomy.md`
|
- `004-host-fault-taxonomy.md`
|
||||||
|
- `005-v1-vm-owned-input-intrinsics-and-language-agnostic-surface.md`
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# Input Peripheral (Input System)
|
# Input Peripheral (VM-Owned Snapshot Input)
|
||||||
|
|
||||||
Domain: virtual hardware: input
|
Domain: virtual hardware: input
|
||||||
Function: normative
|
Function: normative
|
||||||
@ -11,154 +11,99 @@ This chapter defines the runtime-facing input contract of PROMETEU.
|
|||||||
|
|
||||||
Core contract:
|
Core contract:
|
||||||
|
|
||||||
- input is exposed as state, not asynchronous callback flow;
|
- input is exposed as VM-owned snapshot state;
|
||||||
- sampling occurs at a defined moment in the logical frame;
|
- sampling happens at a deterministic point of each logical frame;
|
||||||
- queries are deterministic during the frame;
|
- queries are deterministic inside the same frame;
|
||||||
- logical identifiers are stable across host devices.
|
- 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)
|
No analog axis is part of the v1 input contract.
|
||||||
- keyboard (mapped to buttons)
|
|
||||||
- gamepad
|
|
||||||
- touch (mobile, mapped)
|
|
||||||
|
|
||||||
Regardless of source, PROMETEU exposes the same logical model.
|
## 3 Frame Sampling Model
|
||||||
|
|
||||||
## 3 State Model
|
Input state is captured at the beginning of each logical frame, before update logic runs.
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
Conceptual flow:
|
Conceptual flow:
|
||||||
|
|
||||||
```
|
```text
|
||||||
FRAME N:
|
FRAME N:
|
||||||
SAMPLEINPUT
|
SAMPLE_INPUT
|
||||||
UPDATE
|
UPDATE
|
||||||
DRAW
|
DRAW
|
||||||
AUDIO
|
AUDIO
|
||||||
SYNC
|
SYNC
|
||||||
```
|
```
|
||||||
|
|
||||||
Throughout the entire frame:
|
Within the same frame:
|
||||||
|
|
||||||
- the input state is immutable
|
- input state is immutable;
|
||||||
- repeated calls return the same value
|
- repeated reads return the same values.
|
||||||
|
|
||||||
## 5 Determinism and Reproducibility
|
## 4 State Semantics
|
||||||
|
|
||||||
The PROMETEU input model guarantees:
|
`button` exposes:
|
||||||
|
|
||||||
- same sequence of inputs
|
- `pressed: bool` (true only on transition up -> down in this frame)
|
||||||
- same frames
|
- `released: bool` (true only on transition down -> up in this frame)
|
||||||
- same results
|
- `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
|
- `up`, `down`, `left`, `right`
|
||||||
- deterministic replays
|
- `a`, `b`, `x`, `y`
|
||||||
- reliable certification
|
- `l`, `r`, `start`, `select`
|
||||||
|
|
||||||
Input can be:
|
`touch` exposes:
|
||||||
|
|
||||||
- recorded
|
- `x: int`
|
||||||
- played back
|
- `y: int`
|
||||||
- artificially injected
|
- `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
|
Input is VM-owned in v1:
|
||||||
- participate in the per-frame budget
|
|
||||||
- appear in certification reports
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
```
|
Illustrative (language-level) shape:
|
||||||
Frame 18231:
|
|
||||||
input.btn():12cycles
|
```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`
|
- deterministic replay;
|
||||||
- `A`, `B`, `X`, `Y`
|
- deterministic certification analysis;
|
||||||
- `START`, `SELECT`
|
- controlled input injection in tests/tooling.
|
||||||
|
|
||||||
The physical mapping:
|
## 7 Capability and Certification
|
||||||
|
|
||||||
- depends on the platform
|
Input reads are not capability-gated by syscall capability policy in v1.
|
||||||
- is resolved outside the VM
|
|
||||||
- is transparent to the program
|
|
||||||
|
|
||||||
### 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
|
All platforms must provide the mandatory input elements (`pad`, `touch`, `button`) to the runtime.
|
||||||
- runs on a gamepad
|
|
||||||
- runs on touch
|
|
||||||
|
|
||||||
Without any code changes.
|
Platform differences in physical device mapping are resolved outside VM semantics.
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|||||||
@ -37,7 +37,6 @@ Example:
|
|||||||
|
|
||||||
```
|
```
|
||||||
("gfx", "present", 1)
|
("gfx", "present", 1)
|
||||||
("input", "state", 1)
|
|
||||||
("audio", "play", 2)
|
("audio", "play", 2)
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -47,6 +46,8 @@ This identity is:
|
|||||||
- toolchain-stable;
|
- toolchain-stable;
|
||||||
- used for linking and capability gating.
|
- 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
|
## 3 Syscall Resolution
|
||||||
|
|
||||||
The host maintains a registry:
|
The host maintains a registry:
|
||||||
|
|||||||
@ -41,12 +41,13 @@ Example groups:
|
|||||||
|
|
||||||
- `gfx`
|
- `gfx`
|
||||||
- `audio`
|
- `audio`
|
||||||
- `input`
|
|
||||||
- `asset`
|
- `asset`
|
||||||
- `memcard`
|
- `memcard`
|
||||||
|
|
||||||
Capability checks exist to constrain which host-managed surfaces a cartridge may use.
|
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
|
## 3 Interaction with the Garbage Collector
|
||||||
|
|
||||||
The VM heap and host-managed memory are separate.
|
The VM heap and host-managed memory are separate.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user