pr 00.1 decouple hardware bridge
This commit is contained in:
parent
c5c1f68f7c
commit
aab53d30be
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 ---
|
||||
|
||||
|
||||
20
crates/prometeu-core/src/virtual_machine/host_context.rs
Normal file
20
crates/prometeu-core/src/virtual_machine/host_context.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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> {
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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);
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user