From aab53d30becd1fcf3a6f4964f8383c4827b303ce Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Tue, 3 Feb 2026 18:16:18 +0000 Subject: [PATCH] pr 00.1 decouple hardware bridge --- .../tests/link_integration.rs | 4 +- .../src/prometeu_os/prometeu_os.rs | 59 ++++++++++++------ .../src/virtual_machine/host_context.rs | 20 ++++++ .../prometeu-core/src/virtual_machine/mod.rs | 6 +- .../src/virtual_machine/virtual_machine.rs | 18 +++--- crates/prometeu-core/tests/heartbeat.rs | 4 +- test-cartridges/canonical/golden/program.pbc | Bin 3727 -> 3727 bytes 7 files changed, 79 insertions(+), 32 deletions(-) create mode 100644 crates/prometeu-core/src/virtual_machine/host_context.rs diff --git a/crates/prometeu-compiler/tests/link_integration.rs b/crates/prometeu-compiler/tests/link_integration.rs index dab6ca8d..01569268 100644 --- a/crates/prometeu-compiler/tests/link_integration.rs +++ b/crates/prometeu-compiler/tests/link_integration.rs @@ -1,12 +1,12 @@ use prometeu_compiler::compiler::compile; use prometeu_core::hardware::{AssetManager, Audio, Gfx, HardwareBridge, MemoryBanks, Pad, Touch}; -use prometeu_core::virtual_machine::{HostReturn, LogicalFrameEndingReason, NativeInterface, Value, VirtualMachine, VmFault}; +use prometeu_core::virtual_machine::{HostReturn, LogicalFrameEndingReason, NativeInterface, Value, VirtualMachine, VmFault, HostContext}; use std::path::PathBuf; use std::sync::Arc; struct SimpleNative; impl NativeInterface for SimpleNative { - fn syscall(&mut self, _id: u32, _args: &[Value], _ret: &mut HostReturn, _hw: &mut dyn HardwareBridge) -> Result<(), VmFault> { + fn syscall(&mut self, _id: u32, _args: &[Value], _ret: &mut HostReturn, _ctx: &mut HostContext) -> Result<(), VmFault> { Ok(()) } } diff --git a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs index e2e1f068..d25af7a8 100644 --- a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs +++ b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs @@ -5,7 +5,7 @@ use crate::log::{LogLevel, LogService, LogSource}; use crate::model::{BankType, Cartridge, Color}; use crate::prometeu_os::NativeInterface; use crate::telemetry::{CertificationConfig, Certifier, TelemetryFrame}; -use crate::virtual_machine::{expect_bool, expect_int, HostReturn, SyscallId, Value, VirtualMachine, VmFault}; +use crate::virtual_machine::{expect_bool, expect_int, HostReturn, SyscallId, Value, VirtualMachine, VmFault, HostContext}; use std::collections::HashMap; use std::time::Instant; @@ -424,7 +424,7 @@ mod tests { } args.reverse(); let mut ret = HostReturn::new(&mut vm.operand_stack); - os.syscall(id, &args, &mut ret, hw) + os.syscall(id, &args, &mut ret, &mut HostContext::new(Some(hw))) } #[test] @@ -753,7 +753,7 @@ mod tests { let mut hw = crate::Hardware::new(); let mut ret = HostReturn::new(&mut vm.operand_stack); - let res = os.syscall(0xDEADBEEF, &[], &mut ret, &mut hw); + let res = os.syscall(0xDEADBEEF, &[], &mut ret, &mut HostContext::new(Some(&mut hw))); assert!(res.is_err()); match res.err().unwrap() { VmFault::Trap(code, _) => assert_eq!(code, prometeu_bytecode::abi::TRAP_INVALID_SYSCALL), @@ -771,7 +771,7 @@ mod tests { let args = vec![Value::Bounded(0xF800)]; // Red { let mut ret = HostReturn::new(&mut stack); - os.syscall(Syscall::GfxClear565 as u32, &args, &mut ret, &mut hw).unwrap(); + os.syscall(Syscall::GfxClear565 as u32, &args, &mut ret, &mut HostContext::new(Some(&mut hw))).unwrap(); } assert_eq!(stack.len(), 0); // void return @@ -779,7 +779,7 @@ mod tests { let args = vec![Value::Bounded(0x10000)]; { let mut ret = HostReturn::new(&mut stack); - let res = os.syscall(Syscall::GfxClear565 as u32, &args, &mut ret, &mut hw); + let res = os.syscall(Syscall::GfxClear565 as u32, &args, &mut ret, &mut HostContext::new(Some(&mut hw))); assert!(res.is_err()); match res.err().unwrap() { VmFault::Trap(trap, _) => assert_eq!(trap, prometeu_bytecode::abi::TRAP_OOB), @@ -797,7 +797,7 @@ mod tests { let mut stack = Vec::new(); { let mut ret = HostReturn::new(&mut stack); - os.syscall(Syscall::InputPadSnapshot as u32, &[], &mut ret, &mut hw).unwrap(); + os.syscall(Syscall::InputPadSnapshot as u32, &[], &mut ret, &mut HostContext::new(Some(&mut hw))).unwrap(); } assert_eq!(stack.len(), 48); @@ -805,10 +805,25 @@ mod tests { let mut stack = Vec::new(); { let mut ret = HostReturn::new(&mut stack); - os.syscall(Syscall::InputTouchSnapshot as u32, &[], &mut ret, &mut hw).unwrap(); + os.syscall(Syscall::InputTouchSnapshot as u32, &[], &mut ret, &mut HostContext::new(Some(&mut hw))).unwrap(); } assert_eq!(stack.len(), 6); } + + #[test] + fn test_os_syscall_without_hardware_fails_graciously() { + let mut os = PrometeuOS::new(None); + let mut stack = Vec::new(); + let mut ret = HostReturn::new(&mut stack); + + // GfxClear needs hardware + let res = os.syscall(Syscall::GfxClear as u32, &[Value::Int64(0)], &mut ret, &mut HostContext::new(None)); + assert_eq!(res, Err(VmFault::Unavailable)); + + // SystemHasCart DOES NOT need hardware + let res = os.syscall(Syscall::SystemHasCart as u32, &[], &mut ret, &mut HostContext::new(None)); + assert!(res.is_ok()); + } } impl NativeInterface for PrometeuOS { @@ -823,24 +838,32 @@ impl NativeInterface for PrometeuOS { /// - 0x5000: Logging /// /// Each syscall returns the number of virtual cycles it consumed. - fn syscall(&mut self, id: SyscallId, args: &[Value], ret: &mut HostReturn, hw: &mut dyn HardwareBridge) -> Result<(), VmFault> { + fn syscall(&mut self, id: SyscallId, args: &[Value], ret: &mut HostReturn, ctx: &mut HostContext) -> Result<(), VmFault> { self.telemetry_current.syscalls += 1; let syscall = Syscall::from_u32(id).ok_or_else(|| VmFault::Trap(prometeu_bytecode::abi::TRAP_INVALID_SYSCALL, format!( "Unknown syscall: 0x{:08X}", id )))?; + + // Handle hardware-less syscalls first + match syscall { + Syscall::SystemHasCart => { + ret.push_bool(true); + return Ok(()); + } + Syscall::SystemRunCart => { + ret.push_null(); + return Ok(()); + } + _ => {} + } + + let hw = ctx.require_hw()?; + match syscall { // --- System Syscalls --- - // system.has_cart() -> bool - Syscall::SystemHasCart => { - ret.push_bool(true); - Ok(()) - } - // system.run_cart() -> null - Syscall::SystemRunCart => { - ret.push_null(); - Ok(()) - } + Syscall::SystemHasCart => unreachable!(), + Syscall::SystemRunCart => unreachable!(), // --- GFX Syscalls --- diff --git a/crates/prometeu-core/src/virtual_machine/host_context.rs b/crates/prometeu-core/src/virtual_machine/host_context.rs new file mode 100644 index 00000000..3841715c --- /dev/null +++ b/crates/prometeu-core/src/virtual_machine/host_context.rs @@ -0,0 +1,20 @@ +use crate::hardware::HardwareBridge; +use crate::virtual_machine::VmFault; + +pub struct HostContext<'a> { + pub hw: Option<&'a mut dyn HardwareBridge>, +} + +impl<'a> HostContext<'a> { + pub fn new(hw: Option<&'a mut dyn HardwareBridge>) -> Self { + Self { hw } + } + + #[inline] + pub fn require_hw(&mut self) -> Result<&mut dyn HardwareBridge, VmFault> { + match &mut self.hw { + Some(hw) => Ok(*hw), + None => Err(VmFault::Unavailable), + } + } +} diff --git a/crates/prometeu-core/src/virtual_machine/mod.rs b/crates/prometeu-core/src/virtual_machine/mod.rs index 593def8b..140ec66a 100644 --- a/crates/prometeu-core/src/virtual_machine/mod.rs +++ b/crates/prometeu-core/src/virtual_machine/mod.rs @@ -2,12 +2,13 @@ mod virtual_machine; mod call_frame; mod scope_frame; mod program; +pub mod host_context; pub mod local_addressing; pub mod opcode_spec; pub mod bytecode; pub mod verifier; -use crate::hardware::HardwareBridge; +pub use host_context::HostContext; pub use program::ProgramImage; pub use prometeu_bytecode::abi::TrapInfo; pub use prometeu_bytecode::opcode::OpCode; @@ -21,6 +22,7 @@ pub type SyscallId = u32; pub enum VmFault { Trap(u32, String), Panic(String), + Unavailable, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -72,7 +74,7 @@ pub trait NativeInterface { /// ABI Rule: Arguments for the syscall are passed in `args`. /// /// Returns are written via `ret`. - fn syscall(&mut self, id: SyscallId, args: &[Value], ret: &mut HostReturn, hw: &mut dyn HardwareBridge) -> Result<(), VmFault>; + fn syscall(&mut self, id: SyscallId, args: &[Value], ret: &mut HostReturn, ctx: &mut HostContext) -> Result<(), VmFault>; } pub fn expect_bounded(args: &[Value], idx: usize) -> Result { diff --git a/crates/prometeu-core/src/virtual_machine/virtual_machine.rs b/crates/prometeu-core/src/virtual_machine/virtual_machine.rs index 8ac5a763..dcafa3d8 100644 --- a/crates/prometeu-core/src/virtual_machine/virtual_machine.rs +++ b/crates/prometeu-core/src/virtual_machine/virtual_machine.rs @@ -2,7 +2,7 @@ use crate::hardware::HardwareBridge; use crate::virtual_machine::call_frame::CallFrame; use crate::virtual_machine::scope_frame::ScopeFrame; use crate::virtual_machine::Value; -use crate::virtual_machine::{NativeInterface, ProgramImage, VmInitError}; +use crate::virtual_machine::{NativeInterface, ProgramImage, VmInitError, HostContext}; use prometeu_bytecode::abi::{TrapInfo, TRAP_BAD_RET_SLOTS, TRAP_DIV_ZERO, TRAP_INVALID_FUNC, TRAP_OOB, TRAP_TYPE}; use prometeu_bytecode::opcode::OpCode; @@ -843,9 +843,11 @@ impl VirtualMachine { let stack_height_before = self.operand_stack.len(); let mut ret = crate::virtual_machine::HostReturn::new(&mut self.operand_stack); - native.syscall(id, &args, &mut ret, hw).map_err(|fault| match fault { + let mut ctx = HostContext::new(Some(hw)); + native.syscall(id, &args, &mut ret, &mut ctx).map_err(|fault| match fault { crate::virtual_machine::VmFault::Trap(code, msg) => self.trap(code, OpCode::Syscall as u16, msg, pc_at_syscall), crate::virtual_machine::VmFault::Panic(msg) => LogicalFrameEndingReason::Panic(msg), + crate::virtual_machine::VmFault::Unavailable => LogicalFrameEndingReason::Panic("Host feature unavailable".into()), })?; let stack_height_after = self.operand_stack.len(); @@ -934,7 +936,7 @@ mod tests { struct MockNative; impl NativeInterface for MockNative { - fn syscall(&mut self, _id: u32, _args: &[Value], _ret: &mut HostReturn, _hw: &mut dyn HardwareBridge) -> Result<(), VmFault> { + fn syscall(&mut self, _id: u32, _args: &[Value], _ret: &mut HostReturn, _ctx: &mut HostContext) -> Result<(), VmFault> { Ok(()) } } @@ -1655,7 +1657,7 @@ mod tests { let mut hw = crate::Hardware::new(); struct TestNative; impl NativeInterface for TestNative { - fn syscall(&mut self, _id: u32, _args: &[Value], _ret: &mut HostReturn, _hw: &mut dyn HardwareBridge) -> Result<(), VmFault> { Ok(()) } + fn syscall(&mut self, _id: u32, _args: &[Value], _ret: &mut HostReturn, _ctx: &mut HostContext) -> Result<(), VmFault> { Ok(()) } } let mut native = TestNative; @@ -1673,7 +1675,7 @@ mod tests { struct MultiReturnNative; impl NativeInterface for MultiReturnNative { - fn syscall(&mut self, _id: u32, _args: &[Value], ret: &mut HostReturn, _hw: &mut dyn HardwareBridge) -> Result<(), VmFault> { + fn syscall(&mut self, _id: u32, _args: &[Value], ret: &mut HostReturn, _ctx: &mut HostContext) -> Result<(), VmFault> { ret.push_bool(true); ret.push_int(42); ret.push_bounded(255)?; @@ -1707,7 +1709,7 @@ mod tests { struct VoidReturnNative; impl NativeInterface for VoidReturnNative { - fn syscall(&mut self, _id: u32, _args: &[Value], _ret: &mut HostReturn, _hw: &mut dyn HardwareBridge) -> Result<(), VmFault> { + fn syscall(&mut self, _id: u32, _args: &[Value], _ret: &mut HostReturn, _ctx: &mut HostContext) -> Result<(), VmFault> { Ok(()) } } @@ -1741,7 +1743,7 @@ mod tests { struct ArgCheckNative; impl NativeInterface for ArgCheckNative { - fn syscall(&mut self, _id: u32, args: &[Value], _ret: &mut HostReturn, _hw: &mut dyn HardwareBridge) -> Result<(), VmFault> { + fn syscall(&mut self, _id: u32, args: &[Value], _ret: &mut HostReturn, _ctx: &mut HostContext) -> Result<(), VmFault> { expect_int(args, 0)?; Ok(()) } @@ -1829,7 +1831,7 @@ mod tests { struct BadNative; impl NativeInterface for BadNative { - fn syscall(&mut self, _id: u32, _args: &[Value], ret: &mut HostReturn, _hw: &mut dyn HardwareBridge) -> Result<(), VmFault> { + fn syscall(&mut self, _id: u32, _args: &[Value], ret: &mut HostReturn, _ctx: &mut HostContext) -> Result<(), VmFault> { // Wrong: GfxClear565 is void but we push something ret.push_int(42); Ok(()) diff --git a/crates/prometeu-core/tests/heartbeat.rs b/crates/prometeu-core/tests/heartbeat.rs index dcccb73f..362f727a 100644 --- a/crates/prometeu-core/tests/heartbeat.rs +++ b/crates/prometeu-core/tests/heartbeat.rs @@ -2,14 +2,14 @@ use prometeu_core::hardware::HardwareBridge; use prometeu_core::virtual_machine::HostReturn; use prometeu_core::virtual_machine::NativeInterface; use prometeu_core::virtual_machine::Value; -use prometeu_core::virtual_machine::{LogicalFrameEndingReason, VirtualMachine}; +use prometeu_core::virtual_machine::{LogicalFrameEndingReason, VirtualMachine, HostContext}; use prometeu_core::Hardware; use std::fs; use std::path::Path; struct MockNative; impl NativeInterface for MockNative { - fn syscall(&mut self, id: u32, _args: &[Value], ret: &mut HostReturn, _hw: &mut dyn HardwareBridge) -> Result<(), prometeu_core::virtual_machine::VmFault> { + fn syscall(&mut self, id: u32, _args: &[Value], ret: &mut HostReturn, _ctx: &mut HostContext) -> Result<(), prometeu_core::virtual_machine::VmFault> { if id == 0x2010 { // InputPadSnapshot for _ in 0..48 { ret.push_bool(false); diff --git a/test-cartridges/canonical/golden/program.pbc b/test-cartridges/canonical/golden/program.pbc index 666448e9c82fd9759bc1514ce6c916eaa9193fac..b06b91caaacaf4416129e782584fa51dc847ec21 100644 GIT binary patch delta 47 wcmeB|?U&tadA