fixed issues with Input API
This commit is contained in:
parent
0a087d51fb
commit
2592b1a1d1
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -1940,13 +1940,9 @@ name = "prometeu-hardware"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"prometeu-abi",
|
||||
"prometeu-bytecode",
|
||||
"prometeu-hardware-contract",
|
||||
"prometeu-kernel",
|
||||
"prometeu-vm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -15,12 +15,13 @@ pub enum ButtonId {
|
||||
Select = 11,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct Button {
|
||||
pub pressed: bool,
|
||||
pub released: bool,
|
||||
pub down: bool,
|
||||
pub hold_frames: u32,
|
||||
pub hold_frames: u16,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
|
||||
@ -66,6 +66,23 @@ pub enum Syscall {
|
||||
TouchIsReleased = 0x2105,
|
||||
/// Returns how many frames the pointer has been held down.
|
||||
TouchGetHold = 0x2106,
|
||||
/// Returns the full Button struct (6 bytes) for the touch finger state.
|
||||
TouchGetFinger = 0x2107,
|
||||
|
||||
// --- Input (Pad service-based) ---
|
||||
/// Returns the full Button struct (6 bytes) for each gamepad button
|
||||
PadGetUp = 0x2200,
|
||||
PadGetDown = 0x2201,
|
||||
PadGetLeft = 0x2202,
|
||||
PadGetRight = 0x2203,
|
||||
PadGetA = 0x2204,
|
||||
PadGetB = 0x2205,
|
||||
PadGetX = 0x2206,
|
||||
PadGetY = 0x2207,
|
||||
PadGetL = 0x2208,
|
||||
PadGetR = 0x2209,
|
||||
PadGetStart = 0x220A,
|
||||
PadGetSelect = 0x220B,
|
||||
|
||||
// --- Audio ---
|
||||
/// Starts playback of a sound sample by its Bank and ID.
|
||||
@ -138,6 +155,19 @@ impl Syscall {
|
||||
0x2104 => Some(Self::TouchIsPressed),
|
||||
0x2105 => Some(Self::TouchIsReleased),
|
||||
0x2106 => Some(Self::TouchGetHold),
|
||||
0x2107 => Some(Self::TouchGetFinger),
|
||||
0x2200 => Some(Self::PadGetUp),
|
||||
0x2201 => Some(Self::PadGetDown),
|
||||
0x2202 => Some(Self::PadGetLeft),
|
||||
0x2203 => Some(Self::PadGetRight),
|
||||
0x2204 => Some(Self::PadGetA),
|
||||
0x2205 => Some(Self::PadGetB),
|
||||
0x2206 => Some(Self::PadGetX),
|
||||
0x2207 => Some(Self::PadGetY),
|
||||
0x2208 => Some(Self::PadGetL),
|
||||
0x2209 => Some(Self::PadGetR),
|
||||
0x220A => Some(Self::PadGetStart),
|
||||
0x220B => Some(Self::PadGetSelect),
|
||||
0x3001 => Some(Self::AudioPlaySample),
|
||||
0x3002 => Some(Self::AudioPlay),
|
||||
0x4001 => Some(Self::FsOpen),
|
||||
@ -184,6 +214,19 @@ impl Syscall {
|
||||
Self::TouchIsPressed => 0,
|
||||
Self::TouchIsReleased => 0,
|
||||
Self::TouchGetHold => 0,
|
||||
Self::TouchGetFinger => 0,
|
||||
Self::PadGetUp => 0,
|
||||
Self::PadGetDown => 0,
|
||||
Self::PadGetLeft => 0,
|
||||
Self::PadGetRight => 0,
|
||||
Self::PadGetA => 0,
|
||||
Self::PadGetB => 0,
|
||||
Self::PadGetX => 0,
|
||||
Self::PadGetY => 0,
|
||||
Self::PadGetL => 0,
|
||||
Self::PadGetR => 0,
|
||||
Self::PadGetStart => 0,
|
||||
Self::PadGetSelect => 0,
|
||||
Self::AudioPlaySample => 5,
|
||||
Self::AudioPlay => 7,
|
||||
Self::FsOpen => 1,
|
||||
@ -209,6 +252,20 @@ impl Syscall {
|
||||
Self::GfxClear565 => 0,
|
||||
Self::InputPadSnapshot => 48,
|
||||
Self::InputTouchSnapshot => 6,
|
||||
// Touch finger and Pad per-button services return a Button (4 slots)
|
||||
Self::TouchGetFinger => 4,
|
||||
Self::PadGetUp
|
||||
| Self::PadGetDown
|
||||
| Self::PadGetLeft
|
||||
| Self::PadGetRight
|
||||
| Self::PadGetA
|
||||
| Self::PadGetB
|
||||
| Self::PadGetX
|
||||
| Self::PadGetY
|
||||
| Self::PadGetL
|
||||
| Self::PadGetR
|
||||
| Self::PadGetStart
|
||||
| Self::PadGetSelect => 4,
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
@ -238,6 +295,19 @@ impl Syscall {
|
||||
Self::TouchIsPressed => "TouchIsPressed",
|
||||
Self::TouchIsReleased => "TouchIsReleased",
|
||||
Self::TouchGetHold => "TouchGetHold",
|
||||
Self::TouchGetFinger => "TouchGetFinger",
|
||||
Self::PadGetUp => "PadGetUp",
|
||||
Self::PadGetDown => "PadGetDown",
|
||||
Self::PadGetLeft => "PadGetLeft",
|
||||
Self::PadGetRight => "PadGetRight",
|
||||
Self::PadGetA => "PadGetA",
|
||||
Self::PadGetB => "PadGetB",
|
||||
Self::PadGetX => "PadGetX",
|
||||
Self::PadGetY => "PadGetY",
|
||||
Self::PadGetL => "PadGetL",
|
||||
Self::PadGetR => "PadGetR",
|
||||
Self::PadGetStart => "PadGetStart",
|
||||
Self::PadGetSelect => "PadGetSelect",
|
||||
Self::AudioPlaySample => "AudioPlaySample",
|
||||
Self::AudioPlay => "AudioPlay",
|
||||
Self::FsOpen => "FsOpen",
|
||||
|
||||
@ -59,7 +59,7 @@ impl ContractRegistry {
|
||||
});
|
||||
mappings.insert("Gfx".to_string(), gfx);
|
||||
|
||||
// Input mappings
|
||||
// Input legacy mappings (kept for backward compatibility)
|
||||
let mut input = HashMap::new();
|
||||
input.insert("pad".to_string(), ContractMethod {
|
||||
id: 0x2010,
|
||||
@ -73,37 +73,90 @@ impl ContractRegistry {
|
||||
});
|
||||
mappings.insert("Input".to_string(), input);
|
||||
|
||||
// Touch mappings
|
||||
// New Pad service-based mappings (each button as a method returning Button)
|
||||
let mut pad = HashMap::new();
|
||||
// NOTE: The syscalls for per-button access must be handled by the runtime.
|
||||
// We reserve the 0x2200..0x220B range for Pad buttons returning a Button struct (6 bytes).
|
||||
pad.insert("up".to_string(), ContractMethod {
|
||||
id: 0x2200,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("down".to_string(), ContractMethod {
|
||||
id: 0x2201,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("left".to_string(), ContractMethod {
|
||||
id: 0x2202,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("right".to_string(), ContractMethod {
|
||||
id: 0x2203,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("a".to_string(), ContractMethod {
|
||||
id: 0x2204,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("b".to_string(), ContractMethod {
|
||||
id: 0x2205,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("x".to_string(), ContractMethod {
|
||||
id: 0x2206,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("y".to_string(), ContractMethod {
|
||||
id: 0x2207,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("l".to_string(), ContractMethod {
|
||||
id: 0x2208,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("r".to_string(), ContractMethod {
|
||||
id: 0x2209,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("start".to_string(), ContractMethod {
|
||||
id: 0x220A,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
pad.insert("select".to_string(), ContractMethod {
|
||||
id: 0x220B,
|
||||
params: vec![],
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
mappings.insert("Pad".to_string(), pad);
|
||||
|
||||
// Touch mappings (service-based)
|
||||
let mut touch = HashMap::new();
|
||||
touch.insert("getX".to_string(), ContractMethod {
|
||||
// SDK agora expõe screen_x/screen_y/finger()
|
||||
touch.insert("screen_x".to_string(), ContractMethod {
|
||||
id: 0x2101,
|
||||
params: vec![],
|
||||
return_type: PbsType::Int,
|
||||
});
|
||||
touch.insert("getY".to_string(), ContractMethod {
|
||||
touch.insert("screen_y".to_string(), ContractMethod {
|
||||
id: 0x2102,
|
||||
params: vec![],
|
||||
return_type: PbsType::Int,
|
||||
});
|
||||
touch.insert("isDown".to_string(), ContractMethod {
|
||||
id: 0x2103,
|
||||
// Novo syscall dedicado para retornar Button completo do touch (dedo)
|
||||
touch.insert("finger".to_string(), ContractMethod {
|
||||
id: 0x2107,
|
||||
params: vec![],
|
||||
return_type: PbsType::Bool,
|
||||
});
|
||||
touch.insert("isPressed".to_string(), ContractMethod {
|
||||
id: 0x2104,
|
||||
params: vec![],
|
||||
return_type: PbsType::Bool,
|
||||
});
|
||||
touch.insert("isReleased".to_string(), ContractMethod {
|
||||
id: 0x2105,
|
||||
params: vec![],
|
||||
return_type: PbsType::Bool,
|
||||
});
|
||||
touch.insert("getHold".to_string(), ContractMethod {
|
||||
id: 0x2106,
|
||||
params: vec![],
|
||||
return_type: PbsType::Int,
|
||||
return_type: PbsType::Struct("Button".to_string()),
|
||||
});
|
||||
mappings.insert("Touch".to_string(), touch);
|
||||
|
||||
|
||||
@ -57,6 +57,8 @@ impl<'a> Lowerer<'a> {
|
||||
let mut struct_slots = HashMap::new();
|
||||
struct_slots.insert("Color".to_string(), 1);
|
||||
struct_slots.insert("ButtonState".to_string(), 4);
|
||||
// New service-based input returns `Button` (same layout as legacy ButtonState: 4 slots)
|
||||
struct_slots.insert("Button".to_string(), 4);
|
||||
struct_slots.insert("Pad".to_string(), 48);
|
||||
struct_slots.insert("Touch".to_string(), 6);
|
||||
|
||||
@ -564,7 +566,46 @@ impl<'a> Lowerer<'a> {
|
||||
fn lower_block(&mut self, _node: NodeId, n: &BlockNodeArena) -> Result<(), ()> {
|
||||
self.local_vars.push(HashMap::new());
|
||||
for stmt in &n.stmts {
|
||||
// Lower the statement normally
|
||||
self.lower_node(*stmt)?;
|
||||
|
||||
// Guardrail: if the statement is a standalone Call that returns values,
|
||||
// discard them to keep the stack balanced. This is essential for host
|
||||
// contract calls that return multi-slot structs (e.g., Button=4 slots).
|
||||
if let NodeKind::Call(call) = self.arena.kind(*stmt) {
|
||||
// Try to compute return slots conservatively
|
||||
let mut return_slots: u32 = 0;
|
||||
|
||||
match self.arena.kind(call.callee) {
|
||||
NodeKind::MemberAccess(ma) => {
|
||||
// Static contract call: Contract.method(...)
|
||||
if let NodeKind::Ident(obj_id) = self.arena.kind(ma.object) {
|
||||
let contract_name = self.interner.resolve(obj_id.name);
|
||||
let method_name = self.interner.resolve(ma.member);
|
||||
if let Some(method) = self.contract_registry.get_method(contract_name, method_name) {
|
||||
let ty = self.convert_pbs_type(&method.return_type);
|
||||
return_slots = self.get_type_slots(&ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
NodeKind::Ident(id_node) => {
|
||||
// Calling a local function or imported symbol — attempt best effort via symbol table
|
||||
let callee_name = self.interner.resolve(id_node.name).to_string();
|
||||
if let Some(func_id) = self.function_ids.get(&callee_name) {
|
||||
let _ = func_id; // unresolved return info in v0 → assume 0 (no discard)
|
||||
} else {
|
||||
// ImportCall or unknown — assume 0 (safe)
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if return_slots > 0 {
|
||||
for _ in 0..return_slots {
|
||||
self.emit(InstrKind::Pop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(tail) = n.tail {
|
||||
self.lower_node(tail)?;
|
||||
@ -585,11 +626,17 @@ impl<'a> Lowerer<'a> {
|
||||
if let NodeKind::Ident(obj) = self.arena.kind(ma.object) {
|
||||
let obj_name = self.interner.resolve(obj.name);
|
||||
let member_name = self.interner.resolve(ma.member);
|
||||
// 1) Prefer exact contract registry mapping (Pad/Touch services, etc.)
|
||||
if let Some(method) = self.contract_registry.get_method(obj_name, member_name) {
|
||||
self.convert_pbs_type(&method.return_type)
|
||||
} else {
|
||||
// 2) Legacy snapshot helpers
|
||||
match (obj_name, member_name) {
|
||||
("Input", "pad") => Type::Struct("Pad".to_string()),
|
||||
("Input", "touch") => Type::Struct("Touch".to_string()),
|
||||
_ => Type::Int,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Type::Int
|
||||
}
|
||||
@ -747,6 +794,39 @@ impl<'a> Lowerer<'a> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Fallback: Handle member access where the object is a Call to a host contract method
|
||||
// Example: Pad.a().down — object is a Call(MemberAccess(Pad, a))
|
||||
if let NodeKind::Call(call_node) = self.arena.kind(n.object) {
|
||||
if let NodeKind::MemberAccess(inner_ma) = self.arena.kind(call_node.callee) {
|
||||
if let NodeKind::Ident(obj_id) = self.arena.kind(inner_ma.object) {
|
||||
let contract_name = self.interner.resolve(obj_id.name);
|
||||
let method_name = self.interner.resolve(inner_ma.member);
|
||||
if let Some(method) = self.contract_registry.get_method(contract_name, method_name) {
|
||||
// Determine return type and slots
|
||||
let ret_ty = self.convert_pbs_type(&method.return_type);
|
||||
let slots = self.get_type_slots(&ret_ty);
|
||||
if let Type::Struct(struct_name) = &ret_ty {
|
||||
// Lower the call to push all slots
|
||||
self.lower_call(n.object, &call_node)?;
|
||||
// Store into a temp local to avoid leaving extra slots on the operand stack
|
||||
let tmp_slot = self.add_local_to_scope(
|
||||
format!("$tmp_{}", self.get_next_local_slot()),
|
||||
ret_ty.clone(),
|
||||
);
|
||||
for i in (0..slots).rev() {
|
||||
self.emit(InstrKind::SetLocal(tmp_slot + i));
|
||||
}
|
||||
// Load only the requested field
|
||||
let field_name = self.interner.resolve(n.member);
|
||||
let field_off = self.get_field_offset(struct_name, field_name);
|
||||
self.emit(InstrKind::GetLocal(tmp_slot + field_off));
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -789,6 +869,14 @@ impl<'a> Lowerer<'a> {
|
||||
}
|
||||
}
|
||||
match struct_name {
|
||||
// New `Button` mirrors legacy `ButtonState` offsets
|
||||
"Button" => match field_name {
|
||||
"pressed" => 0,
|
||||
"released" => 1,
|
||||
"down" => 2,
|
||||
"hold_frames" => 3,
|
||||
_ => 0,
|
||||
},
|
||||
"ButtonState" => match field_name {
|
||||
"pressed" => 0,
|
||||
"released" => 1,
|
||||
@ -828,13 +916,20 @@ impl<'a> Lowerer<'a> {
|
||||
}
|
||||
}
|
||||
match struct_name {
|
||||
"Pad" => Type::Struct("ButtonState".to_string()),
|
||||
// Pad's per-button service returns a `Button` in the new API
|
||||
"Pad" => Type::Struct("Button".to_string()),
|
||||
// Field types for `Button` mirror legacy `ButtonState`
|
||||
"Button" => match field_name {
|
||||
"hold_frames" => Type::Bounded,
|
||||
_ => Type::Bool,
|
||||
},
|
||||
"ButtonState" => match field_name {
|
||||
"hold_frames" => Type::Bounded,
|
||||
_ => Type::Bool,
|
||||
},
|
||||
"Touch" => match field_name {
|
||||
"f" => Type::Struct("ButtonState".to_string()),
|
||||
// Touch.f() now returns a `Button`
|
||||
"f" => Type::Struct("Button".to_string()),
|
||||
_ => Type::Int,
|
||||
},
|
||||
_ => Type::Int,
|
||||
@ -943,6 +1038,46 @@ impl<'a> Lowerer<'a> {
|
||||
}
|
||||
}
|
||||
NodeKind::MemberAccess(ma) => {
|
||||
// Special-case: Member access over a call expression that returns a struct from a host contract,
|
||||
// e.g., Pad.a().down — we must:
|
||||
// 1) lower the call (pushing all struct slots),
|
||||
// 2) store it into a temporary local (all slots),
|
||||
// 3) load only the requested field slot back to the stack.
|
||||
if let NodeKind::Call(call_node) = self.arena.kind(ma.object) {
|
||||
// Try to resolve if callee is a host contract method to infer return struct and slots
|
||||
if let NodeKind::MemberAccess(inner_ma) = self.arena.kind(call_node.callee) {
|
||||
if let NodeKind::Ident(obj_id) = self.arena.kind(inner_ma.object) {
|
||||
let contract_name = self.interner.resolve(obj_id.name);
|
||||
let method_name = self.interner.resolve(inner_ma.member);
|
||||
if let Some(method) = self.contract_registry.get_method(contract_name, method_name) {
|
||||
// Determine slots from the declared return type BEFORE lowering the call
|
||||
let ret_ty = self.convert_pbs_type(&method.return_type);
|
||||
let slots = self.get_type_slots(&ret_ty);
|
||||
let struct_name = match &ret_ty { Type::Struct(s) => s.clone(), _ => String::new() };
|
||||
|
||||
// Lower the call first (this will push all return slots from HostCall)
|
||||
self.lower_call(n.callee, &call_node)?;
|
||||
|
||||
// Allocate a temp local to capture the struct
|
||||
let tmp_slot = self.add_local_to_scope(
|
||||
format!("$tmp_{}", self.get_next_local_slot()),
|
||||
ret_ty.clone(),
|
||||
);
|
||||
// Store all slots (top of stack has the last slot). We must store in reverse order.
|
||||
for i in (0..slots).rev() {
|
||||
self.emit(InstrKind::SetLocal(tmp_slot + i));
|
||||
}
|
||||
|
||||
// Compute field offset/type and load only that field
|
||||
let field_name = self.interner.resolve(ma.member);
|
||||
let field_off = self.get_field_offset(&struct_name, field_name);
|
||||
let _field_ty = self.get_field_type(&struct_name, field_name);
|
||||
self.emit(InstrKind::GetLocal(tmp_slot + field_off));
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if it's a constructor alias: TypeName.Alias(...)
|
||||
let ctor = if let NodeKind::Ident(obj_id) = self.arena.kind(ma.object) {
|
||||
let obj_name = self.interner.resolve(obj_id.name);
|
||||
@ -995,10 +1130,17 @@ impl<'a> Lowerer<'a> {
|
||||
}
|
||||
if let Some(method) = self.contract_registry.get_method(obj_name, member_name) {
|
||||
let id = method.id;
|
||||
let return_slots = if matches!(method.return_type, PbsType::Void) {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
// Compute return slots from declared return type. Structs may span >1 slot (e.g., Button=4)
|
||||
let return_slots = match &method.return_type {
|
||||
PbsType::Void | PbsType::None => 0,
|
||||
PbsType::Struct(name) => {
|
||||
// Prefer builtin struct slots, then fallback to struct_slots map, default 1
|
||||
if let Some(bi) = self.get_builtin_struct_slots(name) { bi } else { *self.struct_slots.get(name).unwrap_or(&1) }
|
||||
}
|
||||
other => {
|
||||
let ty = self.convert_pbs_type(other);
|
||||
self.get_type_slots(&ty)
|
||||
}
|
||||
};
|
||||
self.emit(InstrKind::HostCall(id, return_slots));
|
||||
return Ok(());
|
||||
|
||||
@ -362,7 +362,8 @@ impl<'a> TypeChecker<'a> {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
"ButtonState" => {
|
||||
// New `Button` mirrors legacy `ButtonState` for field typing
|
||||
"Button" | "ButtonState" => {
|
||||
match member_str {
|
||||
"pressed" | "released" | "down" => return PbsType::Bool,
|
||||
"hold_frames" => return PbsType::Bounded,
|
||||
@ -372,7 +373,7 @@ impl<'a> TypeChecker<'a> {
|
||||
"Pad" => {
|
||||
match member_str {
|
||||
"up" | "down" | "left" | "right" | "a" | "b" | "x" | "y" | "l" | "r" | "start" | "select" => {
|
||||
return PbsType::Struct("ButtonState".to_string());
|
||||
return PbsType::Struct("Button".to_string());
|
||||
}
|
||||
"any" => {
|
||||
return PbsType::Function {
|
||||
@ -385,7 +386,7 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
"Touch" => {
|
||||
match member_str {
|
||||
"f" => return PbsType::Struct("ButtonState".to_string()),
|
||||
"f" => return PbsType::Struct("Button".to_string()),
|
||||
"x" | "y" => return PbsType::Int,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@ -4,11 +4,11 @@ pub mod gfx_bridge;
|
||||
pub mod hardware_bridge;
|
||||
pub mod host_context;
|
||||
pub mod host_return;
|
||||
pub mod input_signals;
|
||||
pub mod native_interface;
|
||||
pub mod pad_bridge;
|
||||
pub mod touch_bridge;
|
||||
pub mod native_helpers;
|
||||
pub mod input_signals;
|
||||
|
||||
pub use asset_bridge::AssetBridge;
|
||||
pub use audio_bridge::{AudioBridge, LoopMode};
|
||||
|
||||
@ -5,11 +5,7 @@ edition = "2024"
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.149"
|
||||
prometeu-vm = { path = "../prometeu-vm" }
|
||||
prometeu-kernel = { path = "../prometeu-kernel" }
|
||||
prometeu-bytecode = { path = "../prometeu-bytecode" }
|
||||
prometeu-abi = { path = "../prometeu-abi" }
|
||||
prometeu-hardware-contract = { path = "../prometeu-hardware-contract" }
|
||||
url = "2.5.8"
|
||||
@ -11,4 +11,3 @@ pub use crate::audio::{Audio, AudioCommand, Channel, MAX_CHANNELS, OUTPUT_SAMPLE
|
||||
pub use crate::gfx::Gfx;
|
||||
pub use crate::memory_banks::MemoryBanks;
|
||||
pub use crate::pad::Pad;
|
||||
pub use crate::touch::Touch;
|
||||
@ -1,4 +1,4 @@
|
||||
use prometeu_abi::model::Button;
|
||||
use prometeu_abi::model::{Button};
|
||||
use prometeu_hardware_contract::{InputSignals, TouchBridge};
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
@ -8,13 +8,6 @@ pub struct Touch {
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
impl TouchBridge for Touch {
|
||||
fn begin_frame(&mut self, signals: &InputSignals) { self.begin_frame(signals) }
|
||||
fn f(&self) -> &Button { &self.f }
|
||||
fn x(&self) -> i32 { self.x }
|
||||
fn y(&self) -> i32 { self.y }
|
||||
}
|
||||
|
||||
impl Touch {
|
||||
/// Transient flags should last only 1 frame.
|
||||
pub fn begin_frame(&mut self, signals: &InputSignals) {
|
||||
@ -23,3 +16,10 @@ impl Touch {
|
||||
self.y = signals.y_pos.clone();
|
||||
}
|
||||
}
|
||||
|
||||
impl TouchBridge for Touch {
|
||||
fn begin_frame(&mut self, signals: &InputSignals) { self.begin_frame(signals) }
|
||||
fn f(&self) -> &Button { &self.f }
|
||||
fn x(&self) -> i32 { self.x }
|
||||
fn y(&self) -> i32 { self.y }
|
||||
}
|
||||
@ -1066,6 +1066,15 @@ impl NativeInterface for PrometeuOS {
|
||||
ret.push_int(hw.touch().f().hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::TouchGetFinger => {
|
||||
let btn = hw.touch().f();
|
||||
// Return as 4 slots to mirror snapshot order
|
||||
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 [
|
||||
@ -1091,6 +1100,104 @@ impl NativeInterface for PrometeuOS {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- Pad per-button service-based syscalls (return Button as 4 slots) ---
|
||||
Syscall::PadGetUp => {
|
||||
let b = hw.pad().up();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetDown => {
|
||||
let b = hw.pad().down();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetLeft => {
|
||||
let b = hw.pad().left();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetRight => {
|
||||
let b = hw.pad().right();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetA => {
|
||||
let b = hw.pad().a();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetB => {
|
||||
let b = hw.pad().b();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetX => {
|
||||
let b = hw.pad().x();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetY => {
|
||||
let b = hw.pad().y();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetL => {
|
||||
let b = hw.pad().l();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetR => {
|
||||
let b = hw.pad().r();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetStart => {
|
||||
let b = hw.pad().start();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::PadGetSelect => {
|
||||
let b = hw.pad().select();
|
||||
ret.push_bool(b.pressed);
|
||||
ret.push_bool(b.released);
|
||||
ret.push_bool(b.down);
|
||||
ret.push_int(b.hold_frames as i64);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- Audio Syscalls ---
|
||||
|
||||
// audio.play_sample(sample_id, voice_id, volume, pan, pitch)
|
||||
|
||||
8
test-cartridges/sdk/src/main/modules/gfx/color.pbs
Normal file
8
test-cartridges/sdk/src/main/modules/gfx/color.pbs
Normal file
@ -0,0 +1,8 @@
|
||||
pub declare struct Color(raw: bounded)
|
||||
[[
|
||||
BLACK: Color(0b),
|
||||
WHITE: Color(65535b),
|
||||
RED: Color(63488b),
|
||||
GREEN: Color(2016b),
|
||||
BLUE: Color(31b)
|
||||
]]
|
||||
@ -1,12 +1,3 @@
|
||||
pub declare struct Color(raw: bounded)
|
||||
[[
|
||||
BLACK: Color(0b),
|
||||
WHITE: Color(65535b),
|
||||
RED: Color(63488b),
|
||||
GREEN: Color(2016b),
|
||||
BLUE: Color(31b)
|
||||
]]
|
||||
|
||||
pub declare contract Gfx host {
|
||||
fn clear(color: Color): void;
|
||||
}
|
||||
6
test-cartridges/sdk/src/main/modules/input/button.pbs
Normal file
6
test-cartridges/sdk/src/main/modules/input/button.pbs
Normal file
@ -0,0 +1,6 @@
|
||||
pub declare struct Button(
|
||||
pressed: bool,
|
||||
released: bool,
|
||||
down: bool,
|
||||
hold_frames: bounded
|
||||
)
|
||||
@ -1,25 +1,20 @@
|
||||
pub declare struct ButtonState(
|
||||
pressed: bool,
|
||||
released: bool,
|
||||
down: bool,
|
||||
hold_frames: bounded
|
||||
)
|
||||
|
||||
pub declare struct Pad(
|
||||
up: ButtonState,
|
||||
down: ButtonState,
|
||||
left: ButtonState,
|
||||
right: ButtonState,
|
||||
a: ButtonState,
|
||||
b: ButtonState,
|
||||
x: ButtonState,
|
||||
y: ButtonState,
|
||||
l: ButtonState,
|
||||
r: ButtonState,
|
||||
start: ButtonState,
|
||||
select: ButtonState
|
||||
)
|
||||
|
||||
pub declare contract Input host {
|
||||
fn pad(): Pad;
|
||||
pub declare contract Pad host {
|
||||
fn up(): Button;
|
||||
fn down(): Button;
|
||||
fn left(): Button;
|
||||
fn right(): Button;
|
||||
fn a(): Button;
|
||||
fn b(): Button;
|
||||
fn x(): Button;
|
||||
fn y(): Button;
|
||||
fn l(): Button;
|
||||
fn r(): Button;
|
||||
fn start(): Button;
|
||||
fn select(): Button;
|
||||
}
|
||||
|
||||
pub declare contract Touch host {
|
||||
fn screen_x(): int;
|
||||
fn screen_y(): int;
|
||||
fn finger(): Button;
|
||||
}
|
||||
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
import { Color, Gfx } from "@sdk:gfx";
|
||||
import { Input } from "@sdk:input";
|
||||
import { Pad, Touch } from "@sdk:input";
|
||||
|
||||
declare struct Vec2(x: int, y: int)
|
||||
[
|
||||
@ -51,8 +51,10 @@ fn frame(): void {
|
||||
}
|
||||
|
||||
// 4. Input Snapshot & Nested Member Access
|
||||
let p = Input.pad();
|
||||
if p.a.down {
|
||||
let pad = Pad.a();
|
||||
let touch = Touch.finger();
|
||||
let is_pressed = pad.down || touch.down;
|
||||
if is_pressed {
|
||||
Gfx.clear(Color.BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user