pr 00.1 decouple hardware bridge

This commit is contained in:
bQUARKz 2026-02-03 18:16:18 +00:00
parent c5c1f68f7c
commit aab53d30be
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
7 changed files with 79 additions and 32 deletions

View File

@ -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(())
}
}

View File

@ -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 ---

View File

@ -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),
}
}
}

View File

@ -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<u32, VmFault> {

View File

@ -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(())

View File

@ -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);