implements PLN-0054
This commit is contained in:
parent
d1f4547e44
commit
446044c4e6
@ -1,6 +1,8 @@
|
||||
use crate::CrashReport;
|
||||
use crate::VirtualMachineRuntime;
|
||||
use crate::fs::{FsBackend, FsState, VirtualFS};
|
||||
use crate::process::ProcessManager;
|
||||
use crate::services::memcard::MemcardService;
|
||||
use crate::services::window_manager::WindowManager;
|
||||
use crate::task::TaskId;
|
||||
use crate::task::TaskManager;
|
||||
@ -9,6 +11,7 @@ use prometeu_hal::log::{LogLevel, LogService, LogSource};
|
||||
use prometeu_hal::telemetry::CertificationConfig;
|
||||
use prometeu_hal::{HardwareBridge, InputSignals};
|
||||
use prometeu_vm::VirtualMachine;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct SystemOS {
|
||||
@ -17,6 +20,11 @@ pub struct SystemOS {
|
||||
pub task_manager: TaskManager,
|
||||
pub window_manager: WindowManager,
|
||||
pub log_service: LogService,
|
||||
pub fs: VirtualFS,
|
||||
pub fs_state: FsState,
|
||||
pub memcard: MemcardService,
|
||||
pub open_files: HashMap<u32, String>,
|
||||
pub next_handle: u32,
|
||||
}
|
||||
|
||||
impl SystemOS {
|
||||
@ -31,6 +39,11 @@ impl SystemOS {
|
||||
task_manager: TaskManager::new(),
|
||||
window_manager: WindowManager::new(),
|
||||
log_service,
|
||||
fs: VirtualFS::new(),
|
||||
fs_state: FsState::Unmounted,
|
||||
memcard: MemcardService::new(),
|
||||
open_files: HashMap::new(),
|
||||
next_handle: 1,
|
||||
};
|
||||
|
||||
os.log(LogLevel::Info, LogSource::Pos, 0, "PrometeuOS starting...".to_string());
|
||||
@ -47,6 +60,7 @@ impl SystemOS {
|
||||
vm: &mut VirtualMachine,
|
||||
cartridge: &Cartridge,
|
||||
) -> Result<(), CrashReport> {
|
||||
self.clear_cartridge_service_state();
|
||||
self.vm_runtime.initialize_vm(&mut self.log_service, vm, cartridge)
|
||||
}
|
||||
|
||||
@ -56,7 +70,32 @@ impl SystemOS {
|
||||
signals: &InputSignals,
|
||||
hw: &mut dyn HardwareBridge,
|
||||
) -> Option<CrashReport> {
|
||||
self.vm_runtime.tick(&mut self.log_service, vm, signals, hw)
|
||||
self.vm_runtime.tick(
|
||||
&mut self.log_service,
|
||||
&mut self.fs,
|
||||
&mut self.fs_state,
|
||||
&mut self.memcard,
|
||||
&mut self.open_files,
|
||||
&mut self.next_handle,
|
||||
vm,
|
||||
signals,
|
||||
hw,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn mount_fs(&mut self, backend: Box<dyn FsBackend>) {
|
||||
self.vm_runtime.mount_fs(&mut self.log_service, &mut self.fs, &mut self.fs_state, backend);
|
||||
}
|
||||
|
||||
pub fn unmount_fs(&mut self) {
|
||||
self.fs.unmount();
|
||||
self.fs_state = FsState::Unmounted;
|
||||
}
|
||||
|
||||
fn clear_cartridge_service_state(&mut self) {
|
||||
self.open_files.clear();
|
||||
self.next_handle = 1;
|
||||
self.memcard.clear_all_staging();
|
||||
}
|
||||
|
||||
pub fn create_vm_game_task(&mut self, app_id: u32, title: impl Into<String>) -> TaskId {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use super::*;
|
||||
use crate::services::memcard::{MemcardSlotState, MemcardStatus};
|
||||
use crate::fs::{FsState, VirtualFS};
|
||||
use crate::services::memcard::{MemcardService, MemcardSlotState, MemcardStatus};
|
||||
use prometeu_bytecode::{TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_TYPE, Value};
|
||||
use prometeu_hal::asset::{AssetId, AssetOpStatus, BankType, SlotRef};
|
||||
use prometeu_hal::cartridge::AppMode;
|
||||
@ -13,6 +14,7 @@ use prometeu_hal::{
|
||||
AudioOpStatus, ComposerOpStatus, HostContext, HostReturn, NativeInterface, SyscallId,
|
||||
expect_bool, expect_int,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
use std::sync::atomic::Ordering;
|
||||
@ -20,6 +22,11 @@ use std::sync::atomic::Ordering;
|
||||
pub(crate) struct VmRuntimeHost<'a> {
|
||||
pub runtime: &'a mut VirtualMachineRuntime,
|
||||
pub log_service: &'a mut LogService,
|
||||
pub fs: &'a mut VirtualFS,
|
||||
pub fs_state: &'a mut FsState,
|
||||
pub memcard: &'a mut MemcardService,
|
||||
pub open_files: &'a mut HashMap<u32, String>,
|
||||
pub next_handle: &'a mut u32,
|
||||
}
|
||||
|
||||
impl Deref for VmRuntimeHost<'_> {
|
||||
@ -419,13 +426,13 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
}
|
||||
Syscall::FsOpen => {
|
||||
let path = expect_string(args, 0, "path")?;
|
||||
if self.fs_state != FsState::Mounted {
|
||||
if *self.fs_state != FsState::Mounted {
|
||||
ret.push_int(-1);
|
||||
return Ok(());
|
||||
}
|
||||
let handle = self.next_handle;
|
||||
let handle = *self.next_handle;
|
||||
self.open_files.insert(handle, path);
|
||||
self.next_handle += 1;
|
||||
*self.next_handle += 1;
|
||||
ret.push_int(handle as i64);
|
||||
Ok(())
|
||||
}
|
||||
@ -542,14 +549,8 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
return Ok(());
|
||||
}
|
||||
let payload = hex_decode(&payload_hex)?;
|
||||
let runtime = &mut self.runtime;
|
||||
let write = runtime.memcard.slot_write(
|
||||
&runtime.fs,
|
||||
runtime.current_app_id,
|
||||
slot,
|
||||
offset,
|
||||
&payload,
|
||||
);
|
||||
let app_id = self.current_app_id;
|
||||
let write = self.memcard.slot_write(&self.fs, app_id, slot, offset, &payload);
|
||||
ret.push_int(write.status as i64);
|
||||
ret.push_int(write.bytes_written as i64);
|
||||
Ok(())
|
||||
@ -561,10 +562,10 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
return Ok(());
|
||||
}
|
||||
let status = {
|
||||
let runtime = &mut self.runtime;
|
||||
let memcard = &mut runtime.memcard;
|
||||
let fs = &mut runtime.fs;
|
||||
memcard.slot_commit(fs, runtime.current_app_id, slot)
|
||||
let app_id = self.current_app_id;
|
||||
let memcard = &mut self.memcard;
|
||||
let fs = &mut self.fs;
|
||||
memcard.slot_commit(fs, app_id, slot)
|
||||
};
|
||||
ret.push_int(status as i64);
|
||||
Ok(())
|
||||
@ -576,10 +577,10 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
return Ok(());
|
||||
}
|
||||
let status = {
|
||||
let runtime = &mut self.runtime;
|
||||
let memcard = &mut runtime.memcard;
|
||||
let fs = &mut runtime.fs;
|
||||
memcard.slot_clear(fs, runtime.current_app_id, slot)
|
||||
let app_id = self.current_app_id;
|
||||
let memcard = &mut self.memcard;
|
||||
let fs = &mut self.fs;
|
||||
memcard.slot_clear(fs, app_id, slot)
|
||||
};
|
||||
ret.push_int(status as i64);
|
||||
Ok(())
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
use super::*;
|
||||
use crate::CrashReport;
|
||||
use crate::fs::FsBackend;
|
||||
use crate::fs::{FsBackend, FsState, VirtualFS};
|
||||
use prometeu_hal::cartridge::Cartridge;
|
||||
use prometeu_hal::log::{LogLevel, LogSource};
|
||||
use std::collections::HashMap;
|
||||
|
||||
impl VirtualMachineRuntime {
|
||||
pub fn new_with_log_counter(
|
||||
@ -17,11 +18,6 @@ impl VirtualMachineRuntime {
|
||||
logical_frame_active: false,
|
||||
logical_frame_remaining_cycles: 0,
|
||||
last_frame_cpu_time_us: 0,
|
||||
fs: VirtualFS::new(),
|
||||
fs_state: FsState::Unmounted,
|
||||
memcard: MemcardService::new(),
|
||||
open_files: HashMap::new(),
|
||||
next_handle: 1,
|
||||
current_app_id: 0,
|
||||
current_cartridge_title: String::new(),
|
||||
current_cartridge_app_version: String::new(),
|
||||
@ -53,7 +49,13 @@ impl VirtualMachineRuntime {
|
||||
log_service.log(ts_ms, frame, level, source, tag, msg);
|
||||
}
|
||||
|
||||
pub fn mount_fs(&mut self, log_service: &mut LogService, backend: Box<dyn FsBackend>) {
|
||||
pub fn mount_fs(
|
||||
&mut self,
|
||||
log_service: &mut LogService,
|
||||
fs: &mut VirtualFS,
|
||||
fs_state: &mut FsState,
|
||||
backend: Box<dyn FsBackend>,
|
||||
) {
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Info,
|
||||
@ -61,9 +63,9 @@ impl VirtualMachineRuntime {
|
||||
0,
|
||||
"Attempting to mount filesystem".to_string(),
|
||||
);
|
||||
match self.fs.mount(backend) {
|
||||
match fs.mount(backend) {
|
||||
Ok(_) => {
|
||||
self.fs_state = FsState::Mounted;
|
||||
*fs_state = FsState::Mounted;
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Info,
|
||||
@ -75,18 +77,23 @@ impl VirtualMachineRuntime {
|
||||
Err(e) => {
|
||||
let err_msg = format!("Failed to mount filesystem: {:?}", e);
|
||||
self.log(log_service, LogLevel::Error, LogSource::Fs, 0, err_msg);
|
||||
self.fs_state = FsState::Error(e);
|
||||
*fs_state = FsState::Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmount_fs(&mut self) {
|
||||
self.fs.unmount();
|
||||
self.fs_state = FsState::Unmounted;
|
||||
pub fn unmount_fs(&mut self, fs: &mut VirtualFS, fs_state: &mut FsState) {
|
||||
fs.unmount();
|
||||
*fs_state = FsState::Unmounted;
|
||||
}
|
||||
|
||||
pub(crate) fn update_fs(&mut self, log_service: &mut LogService) {
|
||||
if self.fs_state == FsState::Mounted && !self.fs.is_healthy() {
|
||||
pub(crate) fn update_fs(
|
||||
&mut self,
|
||||
log_service: &mut LogService,
|
||||
fs: &mut VirtualFS,
|
||||
fs_state: &mut FsState,
|
||||
) {
|
||||
if *fs_state == FsState::Mounted && !fs.is_healthy() {
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Error,
|
||||
@ -94,7 +101,7 @@ impl VirtualMachineRuntime {
|
||||
0,
|
||||
"Filesystem became unhealthy, unmounting".to_string(),
|
||||
);
|
||||
self.unmount_fs();
|
||||
self.unmount_fs(fs, fs_state);
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,10 +112,6 @@ impl VirtualMachineRuntime {
|
||||
self.last_frame_cpu_time_us = 0;
|
||||
self.atomic_telemetry.reset();
|
||||
|
||||
self.open_files.clear();
|
||||
self.next_handle = 1;
|
||||
self.memcard.clear_all_staging();
|
||||
|
||||
self.current_app_id = 0;
|
||||
self.current_cartridge_title.clear();
|
||||
self.current_cartridge_app_version.clear();
|
||||
|
||||
@ -5,8 +5,6 @@ mod tests;
|
||||
mod tick;
|
||||
|
||||
use crate::CrashReport;
|
||||
use crate::fs::{FsState, VirtualFS};
|
||||
use crate::services::memcard::MemcardService;
|
||||
use prometeu_bytecode::string_materialization_count;
|
||||
use prometeu_hal::cartridge::AppMode;
|
||||
use prometeu_hal::log::LogService;
|
||||
@ -23,11 +21,6 @@ pub struct VirtualMachineRuntime {
|
||||
pub logical_frame_active: bool,
|
||||
pub logical_frame_remaining_cycles: u64,
|
||||
pub last_frame_cpu_time_us: u64,
|
||||
pub fs: VirtualFS,
|
||||
pub fs_state: FsState,
|
||||
pub memcard: MemcardService,
|
||||
pub open_files: HashMap<u32, String>,
|
||||
pub next_handle: u32,
|
||||
pub current_app_id: u32,
|
||||
pub current_cartridge_title: String,
|
||||
pub current_cartridge_app_version: String,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use super::*;
|
||||
use crate::fs::{FsBackend, FsEntry, FsError};
|
||||
use crate::fs::{FsBackend, FsEntry, FsError, FsState, VirtualFS};
|
||||
use crate::services::memcard::MemcardService;
|
||||
use prometeu_bytecode::TRAP_TYPE;
|
||||
use prometeu_bytecode::Value;
|
||||
use prometeu_bytecode::assembler::assemble;
|
||||
@ -189,6 +190,11 @@ mod fs_memcard;
|
||||
fn initialize_vm_applies_cartridge_capabilities_before_loader_resolution() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let code = assemble("PUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||
let program = serialized_single_function_module(
|
||||
@ -215,6 +221,11 @@ fn initialize_vm_applies_cartridge_capabilities_before_loader_resolution() {
|
||||
fn initialize_vm_succeeds_when_cartridge_capabilities_cover_hostcalls() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let code = assemble("PUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||
let program = serialized_single_function_module(
|
||||
@ -240,6 +251,11 @@ fn initialize_vm_succeeds_when_cartridge_capabilities_cover_hostcalls() {
|
||||
fn tick_returns_error_when_vm_ends_slice_with_trap() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -259,7 +275,17 @@ fn tick_returns_error_when_vm_ends_slice_with_trap() {
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
|
||||
let report = runtime
|
||||
.tick(&mut log_service, &mut vm, &signals, &mut hardware)
|
||||
.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
)
|
||||
.expect("trap must surface as runtime error");
|
||||
|
||||
match report {
|
||||
@ -276,6 +302,11 @@ fn tick_returns_error_when_vm_ends_slice_with_trap() {
|
||||
fn tick_system_profile_rejects_gfx_game_surface() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -294,7 +325,17 @@ fn tick_system_profile_rejects_gfx_game_surface() {
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime
|
||||
.tick(&mut log_service, &mut vm, &signals, &mut hardware)
|
||||
.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
)
|
||||
.expect("system gfx call must trap");
|
||||
|
||||
match report {
|
||||
@ -310,6 +351,11 @@ fn tick_system_profile_rejects_gfx_game_surface() {
|
||||
fn tick_system_profile_rejects_composer_game_surface() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -328,7 +374,17 @@ fn tick_system_profile_rejects_composer_game_surface() {
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime
|
||||
.tick(&mut log_service, &mut vm, &signals, &mut hardware)
|
||||
.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
)
|
||||
.expect("system composer call must trap");
|
||||
|
||||
match report {
|
||||
@ -346,6 +402,11 @@ fn tick_system_profile_rejects_composer_game_surface() {
|
||||
fn tick_system_profile_rejects_bank_game_surface() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -364,7 +425,17 @@ fn tick_system_profile_rejects_bank_game_surface() {
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime
|
||||
.tick(&mut log_service, &mut vm, &signals, &mut hardware)
|
||||
.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
)
|
||||
.expect("system bank call must trap");
|
||||
|
||||
match report {
|
||||
@ -380,6 +451,11 @@ fn tick_system_profile_rejects_bank_game_surface() {
|
||||
fn tick_system_profile_can_use_shared_log_transport() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -398,7 +474,17 @@ fn tick_system_profile_can_use_shared_log_transport() {
|
||||
let cartridge = cartridge_with_program_and_mode(program, caps::LOG, AppMode::Shell);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
|
||||
assert!(report.is_none(), "system log transport must remain internally shared");
|
||||
assert!(vm.is_halted());
|
||||
@ -408,12 +494,27 @@ fn tick_system_profile_can_use_shared_log_transport() {
|
||||
fn tick_returns_panic_report_distinct_from_trap() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::new(assemble("HOSTCALL 0\nHALT").expect("assemble"), vec![]);
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
|
||||
let report = runtime
|
||||
.tick(&mut log_service, &mut vm, &signals, &mut hardware)
|
||||
.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
)
|
||||
.expect("panic must surface as runtime error");
|
||||
|
||||
match report {
|
||||
@ -430,6 +531,11 @@ fn tick_returns_panic_report_distinct_from_trap() {
|
||||
fn tick_renders_bound_eight_pixel_scene_through_frame_composer_path() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let signals = InputSignals::default();
|
||||
let program =
|
||||
@ -447,7 +553,17 @@ fn tick_renders_bound_eight_pixel_scene_through_frame_composer_path() {
|
||||
assert!(hardware.frame_composer.bind_scene(0));
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
|
||||
assert!(report.is_none(), "frame render path must not crash");
|
||||
hardware.gfx.present();
|
||||
@ -458,6 +574,11 @@ fn tick_renders_bound_eight_pixel_scene_through_frame_composer_path() {
|
||||
fn tick_renders_scene_through_public_composer_syscalls() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let signals = InputSignals::default();
|
||||
let code = assemble(
|
||||
@ -507,7 +628,17 @@ fn tick_renders_scene_through_public_composer_syscalls() {
|
||||
hardware.gfx.hud_fade_level = 31;
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
|
||||
assert!(report.is_none(), "public composer path must not crash");
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(ComposerOpStatus::Ok as i64)]);
|
||||
@ -519,6 +650,11 @@ fn tick_renders_scene_through_public_composer_syscalls() {
|
||||
fn tick_draw_text_survives_scene_backed_frame_composition() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let signals = InputSignals::default();
|
||||
let code = assemble(
|
||||
@ -569,7 +705,17 @@ fn tick_draw_text_survives_scene_backed_frame_composition() {
|
||||
hardware.gfx.hud_fade_level = 31;
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
|
||||
assert!(report.is_none(), "scene-backed overlay text must not crash");
|
||||
hardware.gfx.present();
|
||||
@ -580,6 +726,11 @@ fn tick_draw_text_survives_scene_backed_frame_composition() {
|
||||
fn tick_draw_text_survives_no_scene_frame_path() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let signals = InputSignals::default();
|
||||
let code = assemble(
|
||||
@ -603,7 +754,17 @@ fn tick_draw_text_survives_no_scene_frame_path() {
|
||||
hardware.gfx.hud_fade_level = 31;
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
|
||||
assert!(report.is_none(), "no-scene overlay text must not crash");
|
||||
hardware.gfx.present();
|
||||
@ -614,6 +775,11 @@ fn tick_draw_text_survives_no_scene_frame_path() {
|
||||
fn initialize_vm_success_clears_previous_crash_report() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
runtime.last_crash_report = Some(CrashReport::VmPanic { message: "stale".into(), pc: Some(1) });
|
||||
|
||||
let mut vm = VirtualMachine::default();
|
||||
@ -638,15 +804,16 @@ fn initialize_vm_success_clears_previous_crash_report() {
|
||||
fn reset_clears_cartridge_scoped_runtime_state() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut fs_state = FsState::Mounted;
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 9;
|
||||
|
||||
runtime.tick_index = 77;
|
||||
runtime.logical_frame_index = 12;
|
||||
runtime.logical_frame_active = true;
|
||||
runtime.logical_frame_remaining_cycles = 345;
|
||||
runtime.last_frame_cpu_time_us = 678;
|
||||
runtime.fs_state = FsState::Mounted;
|
||||
runtime.open_files.insert(7, "/save.dat".into());
|
||||
runtime.next_handle = 9;
|
||||
open_files.insert(7, "/save.dat".into());
|
||||
runtime.current_app_id = 42;
|
||||
runtime.current_cartridge_title = "Cart".into();
|
||||
runtime.current_cartridge_app_version = "1.2.3".into();
|
||||
@ -668,13 +835,13 @@ fn reset_clears_cartridge_scoped_runtime_state() {
|
||||
runtime.reset(&mut vm);
|
||||
|
||||
assert_eq!(runtime.tick_index, 77);
|
||||
assert_eq!(runtime.fs_state, FsState::Mounted);
|
||||
assert_eq!(fs_state, FsState::Mounted);
|
||||
assert_eq!(runtime.logical_frame_index, 0);
|
||||
assert!(!runtime.logical_frame_active);
|
||||
assert_eq!(runtime.logical_frame_remaining_cycles, 0);
|
||||
assert_eq!(runtime.last_frame_cpu_time_us, 0);
|
||||
assert!(runtime.open_files.is_empty());
|
||||
assert_eq!(runtime.next_handle, 1);
|
||||
assert_eq!(open_files.len(), 1);
|
||||
assert_eq!(next_handle, 9);
|
||||
assert_eq!(runtime.current_app_id, 0);
|
||||
assert!(runtime.current_cartridge_title.is_empty());
|
||||
assert!(runtime.current_cartridge_app_version.is_empty());
|
||||
@ -698,6 +865,11 @@ fn reset_clears_cartridge_scoped_runtime_state() {
|
||||
fn tick_numeric_happy_path_records_zero_internal_allocations() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -707,7 +879,17 @@ fn tick_numeric_happy_path_records_zero_internal_allocations() {
|
||||
let cartridge = cartridge_with_program(program, caps::NONE);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
|
||||
assert!(report.is_none());
|
||||
let snapshot = runtime.atomic_telemetry.snapshot();
|
||||
@ -719,6 +901,11 @@ fn tick_numeric_happy_path_records_zero_internal_allocations() {
|
||||
fn tick_already_materialized_string_path_records_zero_internal_allocations() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -732,7 +919,17 @@ fn tick_already_materialized_string_path_records_zero_internal_allocations() {
|
||||
let cartridge = cartridge_with_program(program, caps::NONE);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
|
||||
assert!(report.is_none());
|
||||
let snapshot = runtime.atomic_telemetry.snapshot();
|
||||
@ -744,6 +941,11 @@ fn tick_already_materialized_string_path_records_zero_internal_allocations() {
|
||||
fn initialize_vm_failure_clears_previous_identity_and_handles() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
|
||||
let good_program = serialized_single_function_module(
|
||||
@ -761,8 +963,8 @@ fn initialize_vm_failure_clears_previous_identity_and_handles() {
|
||||
.initialize_vm(&mut log_service, &mut vm, &good_cartridge)
|
||||
.expect("runtime must initialize");
|
||||
|
||||
runtime.open_files.insert(5, "/save.dat".into());
|
||||
runtime.next_handle = 6;
|
||||
open_files.insert(5, "/save.dat".into());
|
||||
next_handle = 6;
|
||||
runtime.paused = true;
|
||||
runtime.debug_step_request = true;
|
||||
runtime.atomic_telemetry.cycles_used.store(123, Ordering::Relaxed);
|
||||
@ -786,8 +988,8 @@ fn initialize_vm_failure_clears_previous_identity_and_handles() {
|
||||
assert!(runtime.current_cartridge_title.is_empty());
|
||||
assert!(runtime.current_cartridge_app_version.is_empty());
|
||||
assert_eq!(runtime.current_cartridge_app_mode, AppMode::Game);
|
||||
assert!(runtime.open_files.is_empty());
|
||||
assert_eq!(runtime.next_handle, 1);
|
||||
assert_eq!(open_files.len(), 1);
|
||||
assert_eq!(next_handle, 6);
|
||||
assert!(!runtime.paused);
|
||||
assert!(!runtime.debug_step_request);
|
||||
assert_eq!(runtime.atomic_telemetry.cycles_used.load(Ordering::Relaxed), 0);
|
||||
@ -798,6 +1000,11 @@ fn initialize_vm_failure_clears_previous_identity_and_handles() {
|
||||
fn tick_composer_bind_scene_operational_error_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -815,7 +1022,17 @@ fn tick_composer_bind_scene_operational_error_returns_status_not_crash() {
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "operational error must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
@ -828,6 +1045,11 @@ fn tick_composer_bind_scene_operational_error_returns_status_not_crash() {
|
||||
fn tick_composer_emit_sprite_operational_error_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -848,7 +1070,17 @@ fn tick_composer_emit_sprite_operational_error_returns_status_not_crash() {
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "operational error must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(ComposerOpStatus::BankInvalid as i64)]);
|
||||
@ -858,6 +1090,11 @@ fn tick_composer_emit_sprite_operational_error_returns_status_not_crash() {
|
||||
fn tick_composer_emit_sprite_invalid_layer_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -878,7 +1115,17 @@ fn tick_composer_emit_sprite_invalid_layer_returns_status_not_crash() {
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "invalid layer must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(ComposerOpStatus::LayerInvalid as i64)]);
|
||||
@ -888,6 +1135,11 @@ fn tick_composer_emit_sprite_invalid_layer_returns_status_not_crash() {
|
||||
fn tick_composer_emit_sprite_invalid_range_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -915,7 +1167,17 @@ fn tick_composer_emit_sprite_invalid_range_returns_status_not_crash() {
|
||||
);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "invalid composer parameter range must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
@ -928,6 +1190,11 @@ fn tick_composer_emit_sprite_invalid_range_returns_status_not_crash() {
|
||||
fn tick_audio_play_sample_operational_error_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -948,7 +1215,17 @@ fn tick_audio_play_sample_operational_error_returns_status_not_crash() {
|
||||
let cartridge = cartridge_with_program(program, caps::AUDIO);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "operational error must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(AudioOpStatus::ArgRangeInvalid as i64)]);
|
||||
@ -958,6 +1235,11 @@ fn tick_audio_play_sample_operational_error_returns_status_not_crash() {
|
||||
fn tick_audio_play_voice_invalid_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -978,7 +1260,17 @@ fn tick_audio_play_voice_invalid_returns_status_not_crash() {
|
||||
let cartridge = cartridge_with_program(program, caps::AUDIO);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "invalid voice must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(AudioOpStatus::VoiceInvalid as i64)]);
|
||||
@ -988,6 +1280,11 @@ fn tick_audio_play_voice_invalid_returns_status_not_crash() {
|
||||
fn tick_audio_play_missing_asset_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -1008,7 +1305,17 @@ fn tick_audio_play_missing_asset_returns_status_not_crash() {
|
||||
let cartridge = cartridge_with_program(program, caps::AUDIO);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "missing audio asset must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(AudioOpStatus::BankInvalid as i64)]);
|
||||
@ -1018,6 +1325,11 @@ fn tick_audio_play_missing_asset_returns_status_not_crash() {
|
||||
fn tick_audio_play_type_mismatch_surfaces_trap_not_panic() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -1039,7 +1351,17 @@ fn tick_audio_play_type_mismatch_surfaces_trap_not_panic() {
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime
|
||||
.tick(&mut log_service, &mut vm, &signals, &mut hardware)
|
||||
.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
)
|
||||
.expect("type mismatch must surface as trap");
|
||||
match report {
|
||||
CrashReport::VmTrap { trap } => {
|
||||
@ -1055,6 +1377,11 @@ fn tick_audio_play_type_mismatch_surfaces_trap_not_panic() {
|
||||
fn tick_composer_emit_sprite_type_mismatch_surfaces_trap_not_panic() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -1076,7 +1403,17 @@ fn tick_composer_emit_sprite_type_mismatch_surfaces_trap_not_panic() {
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime
|
||||
.tick(&mut log_service, &mut vm, &signals, &mut hardware)
|
||||
.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
)
|
||||
.expect("type mismatch must surface as trap");
|
||||
match report {
|
||||
CrashReport::VmTrap { trap } => {
|
||||
|
||||
@ -4,6 +4,11 @@ use super::*;
|
||||
fn tick_asset_commit_operational_error_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -21,7 +26,17 @@ fn tick_asset_commit_operational_error_returns_status_not_crash() {
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "operational error must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(AssetOpStatus::UnknownHandle as i64)]);
|
||||
@ -31,6 +46,11 @@ fn tick_asset_commit_operational_error_returns_status_not_crash() {
|
||||
fn tick_asset_load_missing_asset_returns_status_and_zero_handle() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -48,7 +68,17 @@ fn tick_asset_load_missing_asset_returns_status_and_zero_handle() {
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "missing asset must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
@ -61,6 +91,11 @@ fn tick_asset_load_missing_asset_returns_status_and_zero_handle() {
|
||||
fn tick_asset_load_invalid_slot_returns_status_and_zero_handle() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -84,7 +119,17 @@ fn tick_asset_load_invalid_slot_returns_status_and_zero_handle() {
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "invalid slot must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
@ -97,6 +142,11 @@ fn tick_asset_load_invalid_slot_returns_status_and_zero_handle() {
|
||||
fn tick_asset_status_unknown_handle_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -114,7 +164,17 @@ fn tick_asset_status_unknown_handle_returns_status_not_crash() {
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "unknown asset handle must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(LoadStatus::UnknownHandle as i64)]);
|
||||
@ -124,6 +184,11 @@ fn tick_asset_status_unknown_handle_returns_status_not_crash() {
|
||||
fn tick_bank_info_returns_slot_summary_not_json() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -147,7 +212,17 @@ fn tick_bank_info_returns_slot_summary_not_json() {
|
||||
let cartridge = cartridge_with_program(program, caps::BANK);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "bank summary must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(2), vec![Value::Int64(16), Value::Int64(1)]);
|
||||
@ -157,6 +232,11 @@ fn tick_bank_info_returns_slot_summary_not_json() {
|
||||
fn initialize_vm_rejects_removed_bank_slot_info_syscall_identity() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let code = assemble("PUSH_I32 0\nPUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||
let program = serialized_single_function_module(
|
||||
@ -180,6 +260,11 @@ fn initialize_vm_rejects_removed_bank_slot_info_syscall_identity() {
|
||||
fn tick_asset_commit_invalid_transition_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -215,7 +300,17 @@ fn tick_asset_commit_invalid_transition_returns_status_not_crash() {
|
||||
let handle = hardware.assets.load(7, 0).expect("asset handle must be allocated");
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "invalid transition must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(handle, 1);
|
||||
@ -226,6 +321,11 @@ fn tick_asset_commit_invalid_transition_returns_status_not_crash() {
|
||||
fn tick_asset_cancel_unknown_handle_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -243,7 +343,17 @@ fn tick_asset_cancel_unknown_handle_returns_status_not_crash() {
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "unknown handle cancel must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(AssetOpStatus::UnknownHandle as i64)]);
|
||||
@ -253,6 +363,11 @@ fn tick_asset_cancel_unknown_handle_returns_status_not_crash() {
|
||||
fn tick_asset_cancel_invalid_transition_returns_status_not_crash() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -293,7 +408,17 @@ fn tick_asset_cancel_invalid_transition_returns_status_not_crash() {
|
||||
hardware.assets.apply_commits();
|
||||
assert_eq!(hardware.assets.status(handle), LoadStatus::COMMITTED);
|
||||
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "cancel after commit must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(AssetOpStatus::InvalidState as i64)]);
|
||||
@ -303,6 +428,11 @@ fn tick_asset_cancel_invalid_transition_returns_status_not_crash() {
|
||||
fn tick_status_first_surface_smoke_across_composer_audio_and_asset() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -342,7 +472,17 @@ fn tick_status_first_surface_smoke_across_composer_audio_and_asset() {
|
||||
let cartridge = cartridge_with_program(program, caps::GFX | caps::AUDIO | caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "mixed status-first surface must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
|
||||
@ -4,7 +4,12 @@ use super::*;
|
||||
fn tick_memcard_slot_roundtrip_for_game_profile() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
runtime.mount_fs(&mut log_service, Box::new(MemFsBackend::default()));
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
runtime.mount_fs(&mut log_service, &mut fs, &mut fs_state, Box::new(MemFsBackend::default()));
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -52,7 +57,17 @@ fn tick_memcard_slot_roundtrip_for_game_profile() {
|
||||
};
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "memcard roundtrip must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
@ -65,6 +80,11 @@ fn tick_memcard_slot_roundtrip_for_game_profile() {
|
||||
fn tick_memcard_access_is_denied_for_non_game_profile() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -92,7 +112,17 @@ fn tick_memcard_access_is_denied_for_non_game_profile() {
|
||||
};
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut log_service, &mut vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
assert!(report.is_none(), "non-game memcard call must return status");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(vm.operand_stack_top(2), vec![Value::Int64(0), Value::Int64(4)]);
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
use super::dispatch::VmRuntimeHost;
|
||||
use super::*;
|
||||
use crate::CrashReport;
|
||||
use crate::fs::{FsState, VirtualFS};
|
||||
use crate::services::memcard::MemcardService;
|
||||
use prometeu_hal::asset::{BankTelemetry, BankType};
|
||||
use prometeu_hal::log::{LogLevel, LogService, LogSource};
|
||||
use prometeu_hal::{HardwareBridge, HostContext, InputSignals};
|
||||
use prometeu_vm::LogicalFrameEndingReason;
|
||||
use std::collections::HashMap;
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
@ -59,7 +62,20 @@ impl VirtualMachineRuntime {
|
||||
) -> Option<CrashReport> {
|
||||
let step_result = {
|
||||
let mut ctx = HostContext::new(Some(hw));
|
||||
let mut host = VmRuntimeHost { runtime: self, log_service };
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut host = VmRuntimeHost {
|
||||
runtime: self,
|
||||
log_service,
|
||||
fs: &mut fs,
|
||||
fs_state: &mut fs_state,
|
||||
memcard: &mut memcard,
|
||||
open_files: &mut open_files,
|
||||
next_handle: &mut next_handle,
|
||||
};
|
||||
vm.step(&mut host, &mut ctx)
|
||||
};
|
||||
|
||||
@ -92,6 +108,11 @@ impl VirtualMachineRuntime {
|
||||
pub fn tick(
|
||||
&mut self,
|
||||
log_service: &mut LogService,
|
||||
fs: &mut VirtualFS,
|
||||
fs_state: &mut FsState,
|
||||
memcard: &mut MemcardService,
|
||||
open_files: &mut HashMap<u32, String>,
|
||||
next_handle: &mut u32,
|
||||
vm: &mut VirtualMachine,
|
||||
signals: &InputSignals,
|
||||
hw: &mut dyn HardwareBridge,
|
||||
@ -103,7 +124,7 @@ impl VirtualMachineRuntime {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.update_fs(log_service);
|
||||
self.update_fs(log_service, fs, fs_state);
|
||||
|
||||
if !self.logical_frame_active {
|
||||
self.logical_frame_active = true;
|
||||
@ -137,7 +158,15 @@ impl VirtualMachineRuntime {
|
||||
if budget > 0 {
|
||||
let run_result = {
|
||||
let mut ctx = HostContext::new(Some(hw));
|
||||
let mut host = VmRuntimeHost { runtime: self, log_service };
|
||||
let mut host = VmRuntimeHost {
|
||||
runtime: self,
|
||||
log_service,
|
||||
fs,
|
||||
fs_state,
|
||||
memcard,
|
||||
open_files,
|
||||
next_handle,
|
||||
};
|
||||
vm.run_budget(budget, &mut host, &mut ctx)
|
||||
};
|
||||
|
||||
|
||||
@ -31,4 +31,4 @@
|
||||
{"type":"discussion","id":"DSC-0030","status":"done","ticket":"internal-viewport-270p","title":"Agenda - Internal Viewport 270p (480x270)","created_at":"2026-04-27","updated_at":"2026-04-28","tags":["gfx","runtime","viewport","resolution","frame-composer","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"discussion/lessons/DSC-0030-internal-viewport-270p/LSN-0039-resolution-baseline-changes-are-runtime-wide-contracts.md","status":"done","created_at":"2026-04-28","updated_at":"2026-04-28"}]}
|
||||
{"type":"discussion","id":"DSC-0031","status":"done","ticket":"runtime-mode-separation-game-system","title":"Agenda - Runtime Mode Separation: Game and System","created_at":"2026-05-11","updated_at":"2026-05-14","tags":["runtime","firmware","hub","system-apps","game-mode","scheduler"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0040","file":"discussion/lessons/DSC-0031-runtime-mode-separation-game-system/LSN-0040-system-pipeline-separation.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14"}]}
|
||||
{"type":"discussion","id":"DSC-0032","status":"open","ticket":"system-os-lifecycle-process-task-contract","title":"Agenda - SystemOS Lifecycle, Process and Task Contract","created_at":"2026-05-14","updated_at":"2026-05-14","tags":["runtime","os","lifecycle","process","task","shell","firmware"],"agendas":[{"id":"AGD-0032","file":"AGD-0032-system-os-lifecycle-process-task-contract.md","status":"open","created_at":"2026-05-14","updated_at":"2026-05-14"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0033","status":"in_progress","ticket":"system-os-service-ownership-and-module-layout","title":"Agenda - SystemOS Service Ownership and Module Layout","created_at":"2026-05-14","updated_at":"2026-05-14","tags":["runtime","os","services","module-layout","vm","window-manager","logging"],"agendas":[{"id":"AGD-0033","file":"AGD-0033-system-os-service-ownership-and-module-layout.md","status":"accepted","created_at":"2026-05-14","updated_at":"2026-05-14"}],"decisions":[{"id":"DEC-0024","file":"DEC-0024-systemos-service-ownership-and-module-layout.md","status":"accepted","created_at":"2026-05-14","updated_at":"2026-05-14","ref_agenda":"AGD-0033"}],"plans":[{"id":"PLN-0051","file":"PLN-0051-move-vm-runtime-into-services.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0024"]},{"id":"PLN-0052","file":"PLN-0052-promote-window-manager-to-systemos-service.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0024"]},{"id":"PLN-0053","file":"PLN-0053-move-logging-ownership-to-systemos.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0024"]},{"id":"PLN-0054","file":"PLN-0054-move-fs-and-memcard-ownership-to-systemos.md","status":"open","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0024"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0033","status":"in_progress","ticket":"system-os-service-ownership-and-module-layout","title":"Agenda - SystemOS Service Ownership and Module Layout","created_at":"2026-05-14","updated_at":"2026-05-14","tags":["runtime","os","services","module-layout","vm","window-manager","logging"],"agendas":[{"id":"AGD-0033","file":"AGD-0033-system-os-service-ownership-and-module-layout.md","status":"accepted","created_at":"2026-05-14","updated_at":"2026-05-14"}],"decisions":[{"id":"DEC-0024","file":"DEC-0024-systemos-service-ownership-and-module-layout.md","status":"accepted","created_at":"2026-05-14","updated_at":"2026-05-14","ref_agenda":"AGD-0033"}],"plans":[{"id":"PLN-0051","file":"PLN-0051-move-vm-runtime-into-services.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0024"]},{"id":"PLN-0052","file":"PLN-0052-promote-window-manager-to-systemos-service.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0024"]},{"id":"PLN-0053","file":"PLN-0053-move-logging-ownership-to-systemos.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0024"]},{"id":"PLN-0054","file":"PLN-0054-move-fs-and-memcard-ownership-to-systemos.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0024"]}],"lessons":[]}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: PLN-0054
|
||||
ticket: system-os-service-ownership-and-module-layout
|
||||
title: Move FS And Memcard Ownership To SystemOS
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-05-14
|
||||
ref_decisions: [DEC-0024]
|
||||
tags: [runtime, os, services, module-layout, vm, window-manager, logging]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user