implements PLN-0053
This commit is contained in:
parent
ab825d3274
commit
d1f4547e44
@ -15,12 +15,7 @@ impl AppCrashesStep {
|
||||
}
|
||||
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.vm_runtime.log(
|
||||
LogLevel::Error,
|
||||
LogSource::Pos,
|
||||
self.report.log_tag(),
|
||||
self.log_message(),
|
||||
);
|
||||
ctx.os.log(LogLevel::Error, LogSource::Pos, self.report.log_tag(), self.log_message());
|
||||
}
|
||||
|
||||
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
||||
|
||||
@ -15,12 +15,7 @@ impl GameRunningStep {
|
||||
}
|
||||
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.vm_runtime.log(
|
||||
LogLevel::Info,
|
||||
LogSource::Pos,
|
||||
0,
|
||||
"Entering GameRunning".to_string(),
|
||||
);
|
||||
ctx.os.log(LogLevel::Info, LogSource::Pos, 0, "Entering GameRunning".to_string());
|
||||
}
|
||||
|
||||
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
||||
@ -40,7 +35,7 @@ impl GameRunningStep {
|
||||
return Some(FirmwareState::AppCrashes(AppCrashesStep { report }));
|
||||
}
|
||||
|
||||
let result = ctx.os.vm_runtime.tick(ctx.vm, ctx.signals, ctx.hw);
|
||||
let result = ctx.os.tick_vm(ctx.vm, ctx.signals, ctx.hw);
|
||||
|
||||
if !ctx.os.vm_runtime.logical_frame_active {
|
||||
ctx.hw.gfx_mut().present();
|
||||
|
||||
@ -7,7 +7,7 @@ pub struct HubHomeStep;
|
||||
|
||||
impl HubHomeStep {
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.vm_runtime.log(LogLevel::Info, LogSource::Hub, 0, "Entering HubHome".to_string());
|
||||
ctx.os.log(LogLevel::Info, LogSource::Hub, 0, "Entering HubHome".to_string());
|
||||
}
|
||||
|
||||
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
||||
|
||||
@ -9,7 +9,7 @@ pub struct LaunchHubStep;
|
||||
|
||||
impl LaunchHubStep {
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.vm_runtime.log(LogLevel::Info, LogSource::Pos, 0, "Launching Hub".to_string());
|
||||
ctx.os.log(LogLevel::Info, LogSource::Pos, 0, "Launching Hub".to_string());
|
||||
ctx.hub.init();
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ impl LaunchHubStep {
|
||||
return Some(FirmwareState::LoadCartridge(LoadCartridgeStep::new(cartridge)));
|
||||
}
|
||||
Err(e) => {
|
||||
ctx.os.vm_runtime.log(
|
||||
ctx.os.log(
|
||||
LogLevel::Error,
|
||||
LogSource::Pos,
|
||||
0,
|
||||
|
||||
@ -20,7 +20,7 @@ impl LoadCartridgeStep {
|
||||
}
|
||||
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.vm_runtime.log(
|
||||
ctx.os.log(
|
||||
LogLevel::Info,
|
||||
LogSource::Pos,
|
||||
0,
|
||||
@ -34,7 +34,7 @@ impl LoadCartridgeStep {
|
||||
self.cartridge.assets.clone(),
|
||||
);
|
||||
|
||||
self.init_error = ctx.os.vm_runtime.initialize_vm(ctx.vm, &self.cartridge).err();
|
||||
self.init_error = ctx.os.initialize_vm(ctx.vm, &self.cartridge).err();
|
||||
}
|
||||
|
||||
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
||||
|
||||
@ -8,7 +8,7 @@ pub struct ResetStep;
|
||||
|
||||
impl ResetStep {
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.vm_runtime.log(LogLevel::Info, LogSource::Pos, 0, "Firmware Reset".to_string());
|
||||
ctx.os.log(LogLevel::Info, LogSource::Pos, 0, "Firmware Reset".to_string());
|
||||
ctx.os.vm_runtime.reset(ctx.vm);
|
||||
}
|
||||
|
||||
|
||||
@ -10,12 +10,7 @@ pub struct SplashScreenStep {
|
||||
|
||||
impl SplashScreenStep {
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.vm_runtime.log(
|
||||
LogLevel::Info,
|
||||
LogSource::Pos,
|
||||
0,
|
||||
"Showing SplashScreen".to_string(),
|
||||
);
|
||||
ctx.os.log(LogLevel::Info, LogSource::Pos, 0, "Showing SplashScreen".to_string());
|
||||
// Play sound on enter
|
||||
// ctx.hw.audio_mut().play(0, 0, 0, 255, 127, 1.0, 0, LoopMode::Off);
|
||||
}
|
||||
|
||||
@ -14,12 +14,7 @@ impl SystemRunningStep {
|
||||
}
|
||||
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.vm_runtime.log(
|
||||
LogLevel::Info,
|
||||
LogSource::Hub,
|
||||
0,
|
||||
"Entering SystemRunning".to_string(),
|
||||
);
|
||||
ctx.os.log(LogLevel::Info, LogSource::Hub, 0, "Entering SystemRunning".to_string());
|
||||
}
|
||||
|
||||
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
||||
|
||||
@ -1,25 +1,62 @@
|
||||
use crate::CrashReport;
|
||||
use crate::VirtualMachineRuntime;
|
||||
use crate::process::ProcessManager;
|
||||
use crate::services::window_manager::WindowManager;
|
||||
use crate::task::TaskId;
|
||||
use crate::task::TaskManager;
|
||||
use prometeu_hal::cartridge::Cartridge;
|
||||
use prometeu_hal::log::{LogLevel, LogService, LogSource};
|
||||
use prometeu_hal::telemetry::CertificationConfig;
|
||||
use prometeu_hal::{HardwareBridge, InputSignals};
|
||||
use prometeu_vm::VirtualMachine;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct SystemOS {
|
||||
pub vm_runtime: VirtualMachineRuntime,
|
||||
pub process_manager: ProcessManager,
|
||||
pub task_manager: TaskManager,
|
||||
pub window_manager: WindowManager,
|
||||
pub log_service: LogService,
|
||||
}
|
||||
|
||||
impl SystemOS {
|
||||
pub fn new(cap_config: Option<CertificationConfig>) -> Self {
|
||||
Self {
|
||||
vm_runtime: VirtualMachineRuntime::new(cap_config),
|
||||
let log_service = LogService::new(4096);
|
||||
let mut os = Self {
|
||||
vm_runtime: VirtualMachineRuntime::new_with_log_counter(
|
||||
cap_config,
|
||||
Arc::clone(&log_service.logs_count),
|
||||
),
|
||||
process_manager: ProcessManager::new(),
|
||||
task_manager: TaskManager::new(),
|
||||
window_manager: WindowManager::new(),
|
||||
}
|
||||
log_service,
|
||||
};
|
||||
|
||||
os.log(LogLevel::Info, LogSource::Pos, 0, "PrometeuOS starting...".to_string());
|
||||
|
||||
os
|
||||
}
|
||||
|
||||
pub fn log(&mut self, level: LogLevel, source: LogSource, tag: u16, msg: String) {
|
||||
self.vm_runtime.log(&mut self.log_service, level, source, tag, msg);
|
||||
}
|
||||
|
||||
pub fn initialize_vm(
|
||||
&mut self,
|
||||
vm: &mut VirtualMachine,
|
||||
cartridge: &Cartridge,
|
||||
) -> Result<(), CrashReport> {
|
||||
self.vm_runtime.initialize_vm(&mut self.log_service, vm, cartridge)
|
||||
}
|
||||
|
||||
pub fn tick_vm(
|
||||
&mut self,
|
||||
vm: &mut VirtualMachine,
|
||||
signals: &InputSignals,
|
||||
hw: &mut dyn HardwareBridge,
|
||||
) -> Option<CrashReport> {
|
||||
self.vm_runtime.tick(&mut self.log_service, vm, signals, hw)
|
||||
}
|
||||
|
||||
pub fn create_vm_game_task(&mut self, app_id: u32, title: impl Into<String>) -> TaskId {
|
||||
|
||||
@ -34,28 +34,28 @@ impl PrometeuHub {
|
||||
let mut next_window = None;
|
||||
|
||||
if hw.pad().a().pressed {
|
||||
os.vm_runtime.log(LogLevel::Debug, LogSource::Hub, 0, "window A opened".to_string());
|
||||
os.log(LogLevel::Debug, LogSource::Hub, 0, "window A opened".to_string());
|
||||
next_window = Some((
|
||||
"Green Window".to_string(),
|
||||
Rect { x: 0, y: 0, w: 160, h: 90 },
|
||||
Color::GREEN,
|
||||
));
|
||||
} else if hw.pad().b().pressed {
|
||||
os.vm_runtime.log(LogLevel::Debug, LogSource::Hub, 0, "window B opened".to_string());
|
||||
os.log(LogLevel::Debug, LogSource::Hub, 0, "window B opened".to_string());
|
||||
next_window = Some((
|
||||
"Indigo Window".to_string(),
|
||||
Rect { x: 160, y: 0, w: 160, h: 90 },
|
||||
Color::INDIGO,
|
||||
));
|
||||
} else if hw.pad().x().pressed {
|
||||
os.vm_runtime.log(LogLevel::Debug, LogSource::Hub, 0, "window X opened".to_string());
|
||||
os.log(LogLevel::Debug, LogSource::Hub, 0, "window X opened".to_string());
|
||||
next_window = Some((
|
||||
"Yellow Window".to_string(),
|
||||
Rect { x: 0, y: 90, w: 160, h: 90 },
|
||||
Color::YELLOW,
|
||||
));
|
||||
} else if hw.pad().y().pressed {
|
||||
os.vm_runtime.log(LogLevel::Debug, LogSource::Hub, 0, "window Y opened".to_string());
|
||||
os.log(LogLevel::Debug, LogSource::Hub, 0, "window Y opened".to_string());
|
||||
next_window =
|
||||
Some(("Red Window".to_string(), Rect { x: 160, y: 90, w: 160, h: 90 }, Color::RED));
|
||||
}
|
||||
@ -94,7 +94,7 @@ impl PrometeuHub {
|
||||
if hw.pad().start().down {
|
||||
os.window_manager.remove_window(focused_id);
|
||||
} else {
|
||||
crash = os.vm_runtime.tick(vm, signals, hw);
|
||||
crash = os.tick_vm(vm, signals, hw);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,10 +13,34 @@ use prometeu_hal::{
|
||||
AudioOpStatus, ComposerOpStatus, HostContext, HostReturn, NativeInterface, SyscallId,
|
||||
expect_bool, expect_int,
|
||||
};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
impl VirtualMachineRuntime {
|
||||
pub(crate) struct VmRuntimeHost<'a> {
|
||||
pub runtime: &'a mut VirtualMachineRuntime,
|
||||
pub log_service: &'a mut LogService,
|
||||
}
|
||||
|
||||
impl Deref for VmRuntimeHost<'_> {
|
||||
type Target = VirtualMachineRuntime;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.runtime
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for VmRuntimeHost<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.runtime
|
||||
}
|
||||
}
|
||||
|
||||
impl VmRuntimeHost<'_> {
|
||||
fn log(&mut self, level: LogLevel, source: LogSource, tag: u16, msg: String) {
|
||||
self.runtime.log(self.log_service, level, source, tag, msg);
|
||||
}
|
||||
|
||||
fn panic_payload_to_string(payload: Box<dyn std::any::Any + Send>) -> String {
|
||||
if let Some(message) = payload.downcast_ref::<String>() {
|
||||
return message.clone();
|
||||
@ -40,8 +64,8 @@ impl VirtualMachineRuntime {
|
||||
let app_id = self.current_app_id;
|
||||
let count = *self.logs_written_this_frame.get(&app_id).unwrap_or(&0);
|
||||
|
||||
if count >= Self::MAX_LOGS_PER_FRAME {
|
||||
if count == Self::MAX_LOGS_PER_FRAME {
|
||||
if count >= VirtualMachineRuntime::MAX_LOGS_PER_FRAME {
|
||||
if count == VirtualMachineRuntime::MAX_LOGS_PER_FRAME {
|
||||
self.logs_written_this_frame.insert(app_id, count + 1);
|
||||
self.log(
|
||||
LogLevel::Warn,
|
||||
@ -56,8 +80,8 @@ impl VirtualMachineRuntime {
|
||||
self.logs_written_this_frame.insert(app_id, count + 1);
|
||||
|
||||
let mut final_msg = msg;
|
||||
if final_msg.len() > Self::MAX_LOG_LEN {
|
||||
final_msg.truncate(Self::MAX_LOG_LEN);
|
||||
if final_msg.len() > VirtualMachineRuntime::MAX_LOG_LEN {
|
||||
final_msg.truncate(VirtualMachineRuntime::MAX_LOG_LEN);
|
||||
}
|
||||
|
||||
self.log(level, LogSource::App { app_id }, tag, final_msg);
|
||||
@ -122,7 +146,7 @@ impl VirtualMachineRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeInterface for VirtualMachineRuntime {
|
||||
impl NativeInterface for VmRuntimeHost<'_> {
|
||||
fn syscall(
|
||||
&mut self,
|
||||
id: SyscallId,
|
||||
@ -410,8 +434,9 @@ impl NativeInterface for VirtualMachineRuntime {
|
||||
let path = self
|
||||
.open_files
|
||||
.get(&handle)
|
||||
.ok_or_else(|| VmFault::Panic("Invalid handle".into()))?;
|
||||
match self.fs.read_file(path) {
|
||||
.ok_or_else(|| VmFault::Panic("Invalid handle".into()))?
|
||||
.clone();
|
||||
match self.fs.read_file(&path) {
|
||||
Ok(data) => ret.push_string(String::from_utf8_lossy(&data).into_owned()),
|
||||
Err(_) => ret.push_null(),
|
||||
}
|
||||
@ -423,8 +448,9 @@ impl NativeInterface for VirtualMachineRuntime {
|
||||
let path = self
|
||||
.open_files
|
||||
.get(&handle)
|
||||
.ok_or_else(|| VmFault::Panic("Invalid handle".into()))?;
|
||||
match self.fs.write_file(path, &content) {
|
||||
.ok_or_else(|| VmFault::Panic("Invalid handle".into()))?
|
||||
.clone();
|
||||
match self.fs.write_file(&path, &content) {
|
||||
Ok(_) => ret.push_bool(true),
|
||||
Err(_) => ret.push_bool(false),
|
||||
}
|
||||
@ -516,8 +542,14 @@ impl NativeInterface for VirtualMachineRuntime {
|
||||
return Ok(());
|
||||
}
|
||||
let payload = hex_decode(&payload_hex)?;
|
||||
let write =
|
||||
self.memcard.slot_write(&self.fs, self.current_app_id, slot, offset, &payload);
|
||||
let runtime = &mut self.runtime;
|
||||
let write = runtime.memcard.slot_write(
|
||||
&runtime.fs,
|
||||
runtime.current_app_id,
|
||||
slot,
|
||||
offset,
|
||||
&payload,
|
||||
);
|
||||
ret.push_int(write.status as i64);
|
||||
ret.push_int(write.bytes_written as i64);
|
||||
Ok(())
|
||||
@ -529,9 +561,10 @@ impl NativeInterface for VirtualMachineRuntime {
|
||||
return Ok(());
|
||||
}
|
||||
let status = {
|
||||
let memcard = &mut self.memcard;
|
||||
let fs = &mut self.fs;
|
||||
memcard.slot_commit(fs, self.current_app_id, slot)
|
||||
let runtime = &mut self.runtime;
|
||||
let memcard = &mut runtime.memcard;
|
||||
let fs = &mut runtime.fs;
|
||||
memcard.slot_commit(fs, runtime.current_app_id, slot)
|
||||
};
|
||||
ret.push_int(status as i64);
|
||||
Ok(())
|
||||
@ -543,9 +576,10 @@ impl NativeInterface for VirtualMachineRuntime {
|
||||
return Ok(());
|
||||
}
|
||||
let status = {
|
||||
let memcard = &mut self.memcard;
|
||||
let fs = &mut self.fs;
|
||||
memcard.slot_clear(fs, self.current_app_id, slot)
|
||||
let runtime = &mut self.runtime;
|
||||
let memcard = &mut runtime.memcard;
|
||||
let fs = &mut runtime.fs;
|
||||
memcard.slot_clear(fs, runtime.current_app_id, slot)
|
||||
};
|
||||
ret.push_int(status as i64);
|
||||
Ok(())
|
||||
|
||||
@ -5,11 +5,13 @@ use prometeu_hal::cartridge::Cartridge;
|
||||
use prometeu_hal::log::{LogLevel, LogSource};
|
||||
|
||||
impl VirtualMachineRuntime {
|
||||
pub fn new(cap_config: Option<CertificationConfig>) -> Self {
|
||||
pub fn new_with_log_counter(
|
||||
cap_config: Option<CertificationConfig>,
|
||||
logs_count: Arc<AtomicU32>,
|
||||
) -> Self {
|
||||
let boot_time = Instant::now();
|
||||
let log_service = LogService::new(4096);
|
||||
let atomic_telemetry = Arc::new(AtomicTelemetry::new(Arc::clone(&log_service.logs_count)));
|
||||
let mut os = Self {
|
||||
let atomic_telemetry = Arc::new(AtomicTelemetry::new(logs_count));
|
||||
Self {
|
||||
tick_index: 0,
|
||||
logical_frame_index: 0,
|
||||
logical_frame_active: false,
|
||||
@ -20,7 +22,6 @@ impl VirtualMachineRuntime {
|
||||
memcard: MemcardService::new(),
|
||||
open_files: HashMap::new(),
|
||||
next_handle: 1,
|
||||
log_service,
|
||||
current_app_id: 0,
|
||||
current_cartridge_title: String::new(),
|
||||
current_cartridge_app_version: String::new(),
|
||||
@ -36,25 +37,35 @@ impl VirtualMachineRuntime {
|
||||
frame_start_string_materializations: 0,
|
||||
needs_prepare_entry_call: false,
|
||||
boot_time,
|
||||
};
|
||||
|
||||
os.log(LogLevel::Info, LogSource::Pos, 0, "PrometeuOS starting...".to_string());
|
||||
|
||||
os
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log(&mut self, level: LogLevel, source: LogSource, tag: u16, msg: String) {
|
||||
pub fn log(
|
||||
&self,
|
||||
log_service: &mut LogService,
|
||||
level: LogLevel,
|
||||
source: LogSource,
|
||||
tag: u16,
|
||||
msg: String,
|
||||
) {
|
||||
let ts_ms = self.boot_time.elapsed().as_millis() as u64;
|
||||
let frame = self.logical_frame_index;
|
||||
self.log_service.log(ts_ms, frame, level, source, tag, msg);
|
||||
log_service.log(ts_ms, frame, level, source, tag, msg);
|
||||
}
|
||||
|
||||
pub fn mount_fs(&mut self, backend: Box<dyn FsBackend>) {
|
||||
self.log(LogLevel::Info, LogSource::Fs, 0, "Attempting to mount filesystem".to_string());
|
||||
pub fn mount_fs(&mut self, log_service: &mut LogService, backend: Box<dyn FsBackend>) {
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Info,
|
||||
LogSource::Fs,
|
||||
0,
|
||||
"Attempting to mount filesystem".to_string(),
|
||||
);
|
||||
match self.fs.mount(backend) {
|
||||
Ok(_) => {
|
||||
self.fs_state = FsState::Mounted;
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Info,
|
||||
LogSource::Fs,
|
||||
0,
|
||||
@ -63,7 +74,7 @@ impl VirtualMachineRuntime {
|
||||
}
|
||||
Err(e) => {
|
||||
let err_msg = format!("Failed to mount filesystem: {:?}", e);
|
||||
self.log(LogLevel::Error, LogSource::Fs, 0, err_msg);
|
||||
self.log(log_service, LogLevel::Error, LogSource::Fs, 0, err_msg);
|
||||
self.fs_state = FsState::Error(e);
|
||||
}
|
||||
}
|
||||
@ -74,9 +85,10 @@ impl VirtualMachineRuntime {
|
||||
self.fs_state = FsState::Unmounted;
|
||||
}
|
||||
|
||||
pub(crate) fn update_fs(&mut self) {
|
||||
pub(crate) fn update_fs(&mut self, log_service: &mut LogService) {
|
||||
if self.fs_state == FsState::Mounted && !self.fs.is_healthy() {
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Error,
|
||||
LogSource::Fs,
|
||||
0,
|
||||
@ -120,6 +132,7 @@ impl VirtualMachineRuntime {
|
||||
|
||||
pub fn initialize_vm(
|
||||
&mut self,
|
||||
log_service: &mut LogService,
|
||||
vm: &mut VirtualMachine,
|
||||
cartridge: &Cartridge,
|
||||
) -> Result<(), CrashReport> {
|
||||
@ -138,6 +151,7 @@ impl VirtualMachineRuntime {
|
||||
let report = CrashReport::VmInit { error: e };
|
||||
self.last_crash_report = Some(report.clone());
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Error,
|
||||
LogSource::Vm,
|
||||
report.log_tag(),
|
||||
|
||||
@ -14,6 +14,7 @@ use prometeu_hal::telemetry::{AtomicTelemetry, CertificationConfig, Certifier};
|
||||
use prometeu_vm::VirtualMachine;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::time::Instant;
|
||||
|
||||
pub struct VirtualMachineRuntime {
|
||||
@ -27,7 +28,6 @@ pub struct VirtualMachineRuntime {
|
||||
pub memcard: MemcardService,
|
||||
pub open_files: HashMap<u32, String>,
|
||||
pub next_handle: u32,
|
||||
pub log_service: LogService,
|
||||
pub current_app_id: u32,
|
||||
pub current_cartridge_title: String,
|
||||
pub current_cartridge_app_version: String,
|
||||
@ -50,4 +50,8 @@ impl VirtualMachineRuntime {
|
||||
pub const SLICE_PER_TICK: u64 = 1_500_000;
|
||||
pub const MAX_LOG_LEN: usize = 256;
|
||||
pub const MAX_LOGS_PER_FRAME: u32 = 10;
|
||||
|
||||
pub fn new(cap_config: Option<CertificationConfig>) -> Self {
|
||||
Self::new_with_log_counter(cap_config, Arc::new(AtomicU32::new(0)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,6 +188,7 @@ mod fs_memcard;
|
||||
#[test]
|
||||
fn initialize_vm_applies_cartridge_capabilities_before_loader_resolution() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let code = assemble("PUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||
let program = serialized_single_function_module(
|
||||
@ -202,7 +203,7 @@ fn initialize_vm_applies_cartridge_capabilities_before_loader_resolution() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::NONE);
|
||||
|
||||
let res = runtime.initialize_vm(&mut vm, &cartridge);
|
||||
let res = runtime.initialize_vm(&mut log_service, &mut vm, &cartridge);
|
||||
|
||||
assert!(matches!(res, Err(CrashReport::VmInit { error: VmInitError::LoaderPatchFailed(_) })));
|
||||
assert_eq!(runtime.current_app_id, 0);
|
||||
@ -213,6 +214,7 @@ fn initialize_vm_applies_cartridge_capabilities_before_loader_resolution() {
|
||||
#[test]
|
||||
fn initialize_vm_succeeds_when_cartridge_capabilities_cover_hostcalls() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let code = assemble("PUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||
let program = serialized_single_function_module(
|
||||
@ -227,7 +229,7 @@ fn initialize_vm_succeeds_when_cartridge_capabilities_cover_hostcalls() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
let res = runtime.initialize_vm(&mut vm, &cartridge);
|
||||
let res = runtime.initialize_vm(&mut log_service, &mut vm, &cartridge);
|
||||
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(runtime.current_app_id, 42);
|
||||
@ -237,6 +239,7 @@ fn initialize_vm_succeeds_when_cartridge_capabilities_cover_hostcalls() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -253,10 +256,11 @@ fn tick_returns_error_when_vm_ends_slice_with_trap() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
|
||||
let report =
|
||||
runtime.tick(&mut vm, &signals, &mut hardware).expect("trap must surface as runtime error");
|
||||
let report = runtime
|
||||
.tick(&mut log_service, &mut vm, &signals, &mut hardware)
|
||||
.expect("trap must surface as runtime error");
|
||||
|
||||
match report {
|
||||
CrashReport::VmTrap { trap } => {
|
||||
@ -271,6 +275,7 @@ fn tick_returns_error_when_vm_ends_slice_with_trap() {
|
||||
#[test]
|
||||
fn tick_system_profile_rejects_gfx_game_surface() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -287,8 +292,10 @@ fn tick_system_profile_rejects_gfx_game_surface() {
|
||||
);
|
||||
let cartridge = cartridge_with_program_and_mode(program, caps::GFX, AppMode::Shell);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware).expect("system gfx call must 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)
|
||||
.expect("system gfx call must trap");
|
||||
|
||||
match report {
|
||||
CrashReport::VmTrap { trap } => {
|
||||
@ -302,6 +309,7 @@ fn tick_system_profile_rejects_gfx_game_surface() {
|
||||
#[test]
|
||||
fn tick_system_profile_rejects_composer_game_surface() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -318,9 +326,10 @@ fn tick_system_profile_rejects_composer_game_surface() {
|
||||
);
|
||||
let cartridge = cartridge_with_program_and_mode(program, caps::GFX, AppMode::Shell);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report =
|
||||
runtime.tick(&mut vm, &signals, &mut hardware).expect("system composer call must 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)
|
||||
.expect("system composer call must trap");
|
||||
|
||||
match report {
|
||||
CrashReport::VmTrap { trap } => {
|
||||
@ -336,6 +345,7 @@ fn tick_system_profile_rejects_composer_game_surface() {
|
||||
#[test]
|
||||
fn tick_system_profile_rejects_bank_game_surface() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -352,9 +362,10 @@ fn tick_system_profile_rejects_bank_game_surface() {
|
||||
);
|
||||
let cartridge = cartridge_with_program_and_mode(program, caps::BANK, AppMode::Shell);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report =
|
||||
runtime.tick(&mut vm, &signals, &mut hardware).expect("system bank call must 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)
|
||||
.expect("system bank call must trap");
|
||||
|
||||
match report {
|
||||
CrashReport::VmTrap { trap } => {
|
||||
@ -368,6 +379,7 @@ fn tick_system_profile_rejects_bank_game_surface() {
|
||||
#[test]
|
||||
fn tick_system_profile_can_use_shared_log_transport() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -385,8 +397,8 @@ 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 vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
|
||||
assert!(report.is_none(), "system log transport must remain internally shared");
|
||||
assert!(vm.is_halted());
|
||||
@ -395,12 +407,13 @@ fn tick_system_profile_can_use_shared_log_transport() {
|
||||
#[test]
|
||||
fn tick_returns_panic_report_distinct_from_trap() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
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 vm, &signals, &mut hardware)
|
||||
.tick(&mut log_service, &mut vm, &signals, &mut hardware)
|
||||
.expect("panic must surface as runtime error");
|
||||
|
||||
match report {
|
||||
@ -416,6 +429,7 @@ fn tick_returns_panic_report_distinct_from_trap() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let signals = InputSignals::default();
|
||||
let program =
|
||||
@ -432,8 +446,8 @@ fn tick_renders_bound_eight_pixel_scene_through_frame_composer_path() {
|
||||
glyph_slot_index.rebuild_from_slots(&slots);
|
||||
assert!(hardware.frame_composer.bind_scene(0));
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
|
||||
assert!(report.is_none(), "frame render path must not crash");
|
||||
hardware.gfx.present();
|
||||
@ -443,6 +457,7 @@ fn tick_renders_bound_eight_pixel_scene_through_frame_composer_path() {
|
||||
#[test]
|
||||
fn tick_renders_scene_through_public_composer_syscalls() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let signals = InputSignals::default();
|
||||
let code = assemble(
|
||||
@ -491,8 +506,8 @@ fn tick_renders_scene_through_public_composer_syscalls() {
|
||||
hardware.gfx.scene_fade_level = 31;
|
||||
hardware.gfx.hud_fade_level = 31;
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
|
||||
assert!(report.is_none(), "public composer path must not crash");
|
||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(ComposerOpStatus::Ok as i64)]);
|
||||
@ -503,6 +518,7 @@ fn tick_renders_scene_through_public_composer_syscalls() {
|
||||
#[test]
|
||||
fn tick_draw_text_survives_scene_backed_frame_composition() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let signals = InputSignals::default();
|
||||
let code = assemble(
|
||||
@ -552,8 +568,8 @@ fn tick_draw_text_survives_scene_backed_frame_composition() {
|
||||
hardware.gfx.scene_fade_level = 31;
|
||||
hardware.gfx.hud_fade_level = 31;
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
|
||||
assert!(report.is_none(), "scene-backed overlay text must not crash");
|
||||
hardware.gfx.present();
|
||||
@ -563,6 +579,7 @@ fn tick_draw_text_survives_scene_backed_frame_composition() {
|
||||
#[test]
|
||||
fn tick_draw_text_survives_no_scene_frame_path() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let signals = InputSignals::default();
|
||||
let code = assemble(
|
||||
@ -585,8 +602,8 @@ fn tick_draw_text_survives_no_scene_frame_path() {
|
||||
hardware.gfx.scene_fade_level = 31;
|
||||
hardware.gfx.hud_fade_level = 31;
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
|
||||
assert!(report.is_none(), "no-scene overlay text must not crash");
|
||||
hardware.gfx.present();
|
||||
@ -596,6 +613,7 @@ fn tick_draw_text_survives_no_scene_frame_path() {
|
||||
#[test]
|
||||
fn initialize_vm_success_clears_previous_crash_report() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
runtime.last_crash_report = Some(CrashReport::VmPanic { message: "stale".into(), pc: Some(1) });
|
||||
|
||||
let mut vm = VirtualMachine::default();
|
||||
@ -612,7 +630,7 @@ fn initialize_vm_success_clears_previous_crash_report() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
assert!(runtime.last_crash_report.is_none());
|
||||
}
|
||||
|
||||
@ -679,6 +697,7 @@ fn reset_clears_cartridge_scoped_runtime_state() {
|
||||
#[test]
|
||||
fn tick_numeric_happy_path_records_zero_internal_allocations() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -687,8 +706,8 @@ fn tick_numeric_happy_path_records_zero_internal_allocations() {
|
||||
let program = serialized_single_function_module(code, vec![]);
|
||||
let cartridge = cartridge_with_program(program, caps::NONE);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
|
||||
assert!(report.is_none());
|
||||
let snapshot = runtime.atomic_telemetry.snapshot();
|
||||
@ -699,6 +718,7 @@ fn tick_numeric_happy_path_records_zero_internal_allocations() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -711,8 +731,8 @@ fn tick_already_materialized_string_path_records_zero_internal_allocations() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::NONE);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
|
||||
assert!(report.is_none());
|
||||
let snapshot = runtime.atomic_telemetry.snapshot();
|
||||
@ -723,6 +743,7 @@ fn tick_already_materialized_string_path_records_zero_internal_allocations() {
|
||||
#[test]
|
||||
fn initialize_vm_failure_clears_previous_identity_and_handles() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
|
||||
let good_program = serialized_single_function_module(
|
||||
@ -736,7 +757,9 @@ fn initialize_vm_failure_clears_previous_identity_and_handles() {
|
||||
}],
|
||||
);
|
||||
let good_cartridge = cartridge_with_program(good_program, caps::GFX);
|
||||
runtime.initialize_vm(&mut vm, &good_cartridge).expect("runtime must initialize");
|
||||
runtime
|
||||
.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;
|
||||
@ -756,7 +779,7 @@ fn initialize_vm_failure_clears_previous_identity_and_handles() {
|
||||
);
|
||||
let bad_cartridge = cartridge_with_program(bad_program, caps::NONE);
|
||||
|
||||
let res = runtime.initialize_vm(&mut vm, &bad_cartridge);
|
||||
let res = runtime.initialize_vm(&mut log_service, &mut vm, &bad_cartridge);
|
||||
|
||||
assert!(matches!(res, Err(CrashReport::VmInit { error: VmInitError::LoaderPatchFailed(_) })));
|
||||
assert_eq!(runtime.current_app_id, 0);
|
||||
@ -774,6 +797,7 @@ fn initialize_vm_failure_clears_previous_identity_and_handles() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -790,8 +814,8 @@ fn tick_composer_bind_scene_operational_error_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
assert!(report.is_none(), "operational error must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
@ -803,6 +827,7 @@ fn tick_composer_bind_scene_operational_error_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -822,8 +847,8 @@ fn tick_composer_emit_sprite_operational_error_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
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)]);
|
||||
@ -832,6 +857,7 @@ fn tick_composer_emit_sprite_operational_error_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -851,8 +877,8 @@ fn tick_composer_emit_sprite_invalid_layer_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
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)]);
|
||||
@ -861,6 +887,7 @@ fn tick_composer_emit_sprite_invalid_layer_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -887,8 +914,8 @@ fn tick_composer_emit_sprite_invalid_range_returns_status_not_crash() {
|
||||
AssetsPayloadSource::from_bytes(asset_data),
|
||||
);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
assert!(report.is_none(), "invalid composer parameter range must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
@ -900,6 +927,7 @@ fn tick_composer_emit_sprite_invalid_range_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -919,8 +947,8 @@ fn tick_audio_play_sample_operational_error_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::AUDIO);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
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)]);
|
||||
@ -929,6 +957,7 @@ fn tick_audio_play_sample_operational_error_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -948,8 +977,8 @@ fn tick_audio_play_voice_invalid_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::AUDIO);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
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)]);
|
||||
@ -958,6 +987,7 @@ fn tick_audio_play_voice_invalid_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -977,8 +1007,8 @@ fn tick_audio_play_missing_asset_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::AUDIO);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
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)]);
|
||||
@ -987,6 +1017,7 @@ fn tick_audio_play_missing_asset_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -1006,9 +1037,10 @@ fn tick_audio_play_type_mismatch_surfaces_trap_not_panic() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::AUDIO);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report =
|
||||
runtime.tick(&mut vm, &signals, &mut hardware).expect("type mismatch must surface as 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)
|
||||
.expect("type mismatch must surface as trap");
|
||||
match report {
|
||||
CrashReport::VmTrap { trap } => {
|
||||
assert_eq!(trap.code, TRAP_TYPE);
|
||||
@ -1022,6 +1054,7 @@ fn tick_audio_play_type_mismatch_surfaces_trap_not_panic() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -1041,9 +1074,10 @@ fn tick_composer_emit_sprite_type_mismatch_surfaces_trap_not_panic() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report =
|
||||
runtime.tick(&mut vm, &signals, &mut hardware).expect("type mismatch must surface as 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)
|
||||
.expect("type mismatch must surface as trap");
|
||||
match report {
|
||||
CrashReport::VmTrap { trap } => {
|
||||
assert_eq!(trap.code, TRAP_TYPE);
|
||||
|
||||
@ -3,6 +3,7 @@ use super::*;
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -19,8 +20,8 @@ fn tick_asset_commit_operational_error_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
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)]);
|
||||
@ -29,6 +30,7 @@ fn tick_asset_commit_operational_error_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -45,8 +47,8 @@ fn tick_asset_load_missing_asset_returns_status_and_zero_handle() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
assert!(report.is_none(), "missing asset must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
@ -58,6 +60,7 @@ fn tick_asset_load_missing_asset_returns_status_and_zero_handle() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -80,8 +83,8 @@ fn tick_asset_load_invalid_slot_returns_status_and_zero_handle() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
assert!(report.is_none(), "invalid slot must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
@ -93,6 +96,7 @@ fn tick_asset_load_invalid_slot_returns_status_and_zero_handle() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -109,8 +113,8 @@ fn tick_asset_status_unknown_handle_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
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)]);
|
||||
@ -119,6 +123,7 @@ fn tick_asset_status_unknown_handle_returns_status_not_crash() {
|
||||
#[test]
|
||||
fn tick_bank_info_returns_slot_summary_not_json() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -141,8 +146,8 @@ fn tick_bank_info_returns_slot_summary_not_json() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::BANK);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
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)]);
|
||||
@ -151,6 +156,7 @@ fn tick_bank_info_returns_slot_summary_not_json() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let code = assemble("PUSH_I32 0\nPUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||
let program = serialized_single_function_module(
|
||||
@ -165,7 +171,7 @@ fn initialize_vm_rejects_removed_bank_slot_info_syscall_identity() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::BANK);
|
||||
|
||||
let res = runtime.initialize_vm(&mut vm, &cartridge);
|
||||
let res = runtime.initialize_vm(&mut log_service, &mut vm, &cartridge);
|
||||
|
||||
assert!(matches!(res, Err(CrashReport::VmInit { error: VmInitError::LoaderPatchFailed(_) })));
|
||||
}
|
||||
@ -173,6 +179,7 @@ fn initialize_vm_rejects_removed_bank_slot_info_syscall_identity() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -207,8 +214,8 @@ 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 vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
assert!(report.is_none(), "invalid transition must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(handle, 1);
|
||||
@ -218,6 +225,7 @@ fn tick_asset_commit_invalid_transition_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -234,8 +242,8 @@ fn tick_asset_cancel_unknown_handle_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
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)]);
|
||||
@ -244,6 +252,7 @@ fn tick_asset_cancel_unknown_handle_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -268,7 +277,7 @@ fn tick_asset_cancel_invalid_transition_returns_status_not_crash() {
|
||||
);
|
||||
let handle = hardware.assets.load(7, 0).expect("asset handle must be allocated");
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
|
||||
loop {
|
||||
match hardware.assets.status(handle) {
|
||||
@ -284,7 +293,7 @@ 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 vm, &signals, &mut hardware);
|
||||
let report = runtime.tick(&mut log_service, &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)]);
|
||||
@ -293,6 +302,7 @@ fn tick_asset_cancel_invalid_transition_returns_status_not_crash() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -331,8 +341,8 @@ 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 vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
assert!(report.is_none(), "mixed status-first surface must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
|
||||
@ -3,7 +3,8 @@ use super::*;
|
||||
#[test]
|
||||
fn tick_memcard_slot_roundtrip_for_game_profile() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
runtime.mount_fs(Box::new(MemFsBackend::default()));
|
||||
let mut log_service = LogService::new(4096);
|
||||
runtime.mount_fs(&mut log_service, Box::new(MemFsBackend::default()));
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -50,8 +51,8 @@ fn tick_memcard_slot_roundtrip_for_game_profile() {
|
||||
preload: vec![],
|
||||
};
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
assert!(report.is_none(), "memcard roundtrip must not crash");
|
||||
assert!(vm.is_halted());
|
||||
assert_eq!(
|
||||
@ -63,6 +64,7 @@ fn tick_memcard_slot_roundtrip_for_game_profile() {
|
||||
#[test]
|
||||
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 vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
@ -89,8 +91,8 @@ fn tick_memcard_access_is_denied_for_non_game_profile() {
|
||||
preload: vec![],
|
||||
};
|
||||
|
||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||
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);
|
||||
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,7 +1,8 @@
|
||||
use super::dispatch::VmRuntimeHost;
|
||||
use super::*;
|
||||
use crate::CrashReport;
|
||||
use prometeu_hal::asset::{BankTelemetry, BankType};
|
||||
use prometeu_hal::log::{LogLevel, LogSource};
|
||||
use prometeu_hal::log::{LogLevel, LogService, LogSource};
|
||||
use prometeu_hal::{HardwareBridge, HostContext, InputSignals};
|
||||
use prometeu_vm::LogicalFrameEndingReason;
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
@ -52,11 +53,17 @@ impl VirtualMachineRuntime {
|
||||
|
||||
pub fn debug_step_instruction(
|
||||
&mut self,
|
||||
log_service: &mut LogService,
|
||||
vm: &mut VirtualMachine,
|
||||
hw: &mut dyn HardwareBridge,
|
||||
) -> Option<CrashReport> {
|
||||
let mut ctx = HostContext::new(Some(hw));
|
||||
match vm.step(self, &mut ctx) {
|
||||
let step_result = {
|
||||
let mut ctx = HostContext::new(Some(hw));
|
||||
let mut host = VmRuntimeHost { runtime: self, log_service };
|
||||
vm.step(&mut host, &mut ctx)
|
||||
};
|
||||
|
||||
match step_result {
|
||||
Ok(_) => None,
|
||||
Err(e) => {
|
||||
let report = match e {
|
||||
@ -70,6 +77,7 @@ impl VirtualMachineRuntime {
|
||||
},
|
||||
};
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Error,
|
||||
LogSource::Vm,
|
||||
report.log_tag(),
|
||||
@ -83,6 +91,7 @@ impl VirtualMachineRuntime {
|
||||
|
||||
pub fn tick(
|
||||
&mut self,
|
||||
log_service: &mut LogService,
|
||||
vm: &mut VirtualMachine,
|
||||
signals: &InputSignals,
|
||||
hw: &mut dyn HardwareBridge,
|
||||
@ -94,7 +103,7 @@ impl VirtualMachineRuntime {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.update_fs();
|
||||
self.update_fs(log_service);
|
||||
|
||||
if !self.logical_frame_active {
|
||||
self.logical_frame_active = true;
|
||||
@ -128,7 +137,8 @@ impl VirtualMachineRuntime {
|
||||
if budget > 0 {
|
||||
let run_result = {
|
||||
let mut ctx = HostContext::new(Some(hw));
|
||||
vm.run_budget(budget, self, &mut ctx)
|
||||
let mut host = VmRuntimeHost { runtime: self, log_service };
|
||||
vm.run_budget(budget, &mut host, &mut ctx)
|
||||
};
|
||||
|
||||
match run_result {
|
||||
@ -143,6 +153,7 @@ impl VirtualMachineRuntime {
|
||||
self.paused = true;
|
||||
self.debug_step_request = false;
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Info,
|
||||
LogSource::Vm,
|
||||
0xDEB1,
|
||||
@ -154,6 +165,7 @@ impl VirtualMachineRuntime {
|
||||
let report =
|
||||
CrashReport::VmPanic { message: err, pc: Some(vm.pc() as u32) };
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Error,
|
||||
LogSource::Vm,
|
||||
report.log_tag(),
|
||||
@ -166,6 +178,7 @@ impl VirtualMachineRuntime {
|
||||
if let LogicalFrameEndingReason::Trap(trap) = &run.reason {
|
||||
let report = CrashReport::VmTrap { trap: trap.clone() };
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Error,
|
||||
LogSource::Vm,
|
||||
report.log_tag(),
|
||||
@ -182,6 +195,7 @@ impl VirtualMachineRuntime {
|
||||
let message = Self::host_panic_payload_to_string(payload);
|
||||
let report = CrashReport::VmPanic { message, pc: Some(vm.pc() as u32) };
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Error,
|
||||
LogSource::Vm,
|
||||
report.log_tag(),
|
||||
@ -229,18 +243,15 @@ impl VirtualMachineRuntime {
|
||||
let ts_ms = self.boot_time.elapsed().as_millis() as u64;
|
||||
let telemetry_snapshot = self.atomic_telemetry.snapshot();
|
||||
|
||||
let violations = self.certifier.evaluate(
|
||||
&telemetry_snapshot,
|
||||
&mut self.log_service,
|
||||
ts_ms,
|
||||
) as u32;
|
||||
let violations =
|
||||
self.certifier.evaluate(&telemetry_snapshot, log_service, ts_ms) as u32;
|
||||
|
||||
self.atomic_telemetry.violations.store(violations, Ordering::Relaxed);
|
||||
self.atomic_telemetry
|
||||
.completed_logical_frames
|
||||
.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
self.log_service.reset_count();
|
||||
log_service.reset_count();
|
||||
|
||||
self.logical_frame_index += 1;
|
||||
self.logical_frame_active = false;
|
||||
@ -258,7 +269,13 @@ impl VirtualMachineRuntime {
|
||||
}
|
||||
Err(e) => {
|
||||
let report = CrashReport::VmPanic { message: e, pc: Some(vm.pc() as u32) };
|
||||
self.log(LogLevel::Error, LogSource::Vm, report.log_tag(), report.summary());
|
||||
self.log(
|
||||
log_service,
|
||||
LogLevel::Error,
|
||||
LogSource::Vm,
|
||||
report.log_tag(),
|
||||
report.summary(),
|
||||
);
|
||||
self.last_crash_report = Some(report.clone());
|
||||
return Some(report);
|
||||
}
|
||||
|
||||
@ -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":"open","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":"open","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0024"]}],"lessons":[]}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: PLN-0053
|
||||
ticket: system-os-service-ownership-and-module-layout
|
||||
title: Move Logging 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