use crate::CrashReport; use crate::os::{GameLifecycleEvent, SystemOS}; use crate::task::TaskId; use crate::vm_session::VmSession; use crate::{RenderWorkerConfig, RenderWorkerController}; use prometeu_bytecode::Value; use prometeu_hal::app_mode::AppMode; use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame}; use prometeu_hal::{ FrameId, InputSignals, RenderOwnership, RenderWorkerBackend, RenderWorkerError, RenderWorkerFrameSink, RuntimePlatform, }; use std::sync::atomic::Ordering; pub struct VmFacade<'a> { pub(in crate::os) os: &'a mut SystemOS, } impl<'a> VmFacade<'a> { pub fn tick_session( &mut self, task_id: TaskId, signals: &InputSignals, platform: &mut dyn RuntimePlatform, ) -> Option { self.deliver_pending_game_lifecycle_events_to_session(task_id); let Some(session) = self.os.vm_sessions.for_task_mut(task_id) else { return Some(CrashReport::VmPanic { message: format!("task {:?} has no VM session", task_id), pc: None, }); }; session.runtime.tick( &mut self.os.log_service, &mut self.os.fs, &mut session.fs_state, &mut self.os.memcard, &mut session.open_files, &mut session.next_handle, &mut session.vm, signals, platform, ) } fn deliver_pending_game_lifecycle_events_to_session(&mut self, task_id: TaskId) { let Some(session) = self.os.vm_sessions.for_task(task_id) else { return; }; if session.runtime.current_cartridge_app_mode != AppMode::Game { return; } let mut retained = Vec::new(); let mut delivered = Vec::new(); for event in self.os.game_lifecycle_events.drain(..) { if event.task_id == task_id { delivered.push(event); } else { retained.push(event); } } self.os.game_lifecycle_events = retained; if let Some(session) = self.os.vm_sessions.for_task_mut(task_id) { session.runtime.deliver_game_lifecycle_events(delivered); } } pub fn reset_global_runtime_state(&mut self) { self.os.clear_cartridge_service_state(); self.os.vm_runtime.clear_cartridge_state(); } pub fn debug_step_active_session( &mut self, platform: &mut dyn RuntimePlatform, ) -> Option { let Some(task_id) = self.active_session_task_id() else { return Some(CrashReport::VmPanic { message: "debug step requested without an active VM session".to_string(), pc: None, }); }; let Some(session) = self.os.vm_sessions.for_task_mut(task_id) else { return Some(CrashReport::VmPanic { message: format!("active debug task {:?} has no VM session", task_id), pc: None, }); }; session.runtime.debug_step_instruction(&mut self.os.log_service, &mut session.vm, platform) } pub fn active_pc(&self) -> Option { self.active_session().map(|session| session.vm.pc()) } pub fn active_operand_stack_top(&self, n: usize) -> Vec { self.active_session().map(|session| session.vm.operand_stack_top(n)).unwrap_or_default() } pub fn insert_active_breakpoint(&mut self, pc: usize) -> bool { if let Some(session) = self.active_session_mut() { session.vm.insert_breakpoint(pc); return true; } false } pub fn remove_active_breakpoint(&mut self, pc: usize) -> bool { if let Some(session) = self.active_session_mut() { session.vm.remove_breakpoint(pc); return true; } false } pub fn active_breakpoints_list(&self) -> Vec { self.active_session().map(|session| session.vm.breakpoints_list()).unwrap_or_default() } pub fn paused(&self) -> bool { self.active_session() .map(|session| session.runtime.paused) .unwrap_or(self.os.vm_runtime.paused) } pub fn set_paused(&mut self, paused: bool) { if let Some(session) = self.active_session_mut() { session.runtime.paused = paused; return; } self.os.vm_runtime.paused = paused; } pub fn set_inspection_active(&mut self, active: bool) { if let Some(session) = self.active_session_mut() { session.runtime.inspection_active = active; return; } self.os.vm_runtime.inspection_active = active; } pub fn request_debug_step(&mut self) { if let Some(session) = self.active_session_mut() { session.runtime.debug_step_request = true; return; } self.os.vm_runtime.debug_step_request = true; } pub fn debug_step_requested(&self) -> bool { self.active_session() .map(|session| session.runtime.debug_step_request) .unwrap_or(self.os.vm_runtime.debug_step_request) } pub fn logical_frame_active(&self) -> bool { self.active_session() .map(|session| session.runtime.logical_frame_active) .unwrap_or(self.os.vm_runtime.logical_frame_active) } pub fn delivered_game_lifecycle_events(&self) -> &[GameLifecycleEvent] { self.active_session() .map(|session| session.runtime.delivered_game_lifecycle_events()) .unwrap_or_else(|| self.os.vm_runtime.delivered_game_lifecycle_events()) } pub fn take_delivered_game_lifecycle_events(&mut self) -> Vec { if let Some(session) = self.active_session_mut() { return session.runtime.take_delivered_game_lifecycle_events(); } self.os.vm_runtime.take_delivered_game_lifecycle_events() } pub fn tick_index(&self) -> u64 { self.active_session() .map(|session| session.runtime.tick_index) .unwrap_or(self.os.vm_runtime.tick_index) } pub fn logical_frame_index(&self) -> u64 { self.active_session() .map(|session| session.runtime.logical_frame_index) .unwrap_or(self.os.vm_runtime.logical_frame_index) } pub fn last_frame_cpu_time_us(&self) -> u64 { self.active_session() .map(|session| session.runtime.last_frame_cpu_time_us) .unwrap_or(self.os.vm_runtime.last_frame_cpu_time_us) } pub fn completed_logical_frames(&self) -> u64 { self.active_session() .map(|session| { session .runtime .atomic_telemetry .completed_logical_frames .load(Ordering::Relaxed) .into() }) .unwrap_or_else(|| { self.os .vm_runtime .atomic_telemetry .completed_logical_frames .load(Ordering::Relaxed) .into() }) } pub fn telemetry_snapshot(&self) -> TelemetryFrame { self.active_session() .map(|session| session.runtime.atomic_telemetry.snapshot()) .unwrap_or_else(|| self.os.vm_runtime.atomic_telemetry.snapshot()) } pub fn start_render_worker(&mut self, config: RenderWorkerConfig, backend: B, sink: S) where B: RenderWorkerBackend, S: RenderWorkerFrameSink, { self.os.vm_runtime.start_render_worker(config, backend, sink); } pub fn stop_render_worker(&mut self) -> Result<(), RenderWorkerError> { self.os.vm_runtime.stop_render_worker() } pub fn render_worker_controller(&self) -> Option<&RenderWorkerController> { self.os.vm_runtime.render_worker_controller.as_ref() } pub fn record_repeated_render_worker_frame(&self, frame_id: FrameId) { self.os.vm_runtime.record_repeated_render_worker_frame(frame_id); } pub fn active_render_ownership(&self) -> RenderOwnership { self.os.vm_runtime.render_manager.active_ownership() } pub fn transition_render_owner(&mut self, app_mode: AppMode, app_id: u32) { self.os.vm_runtime.render_manager.transition_render_owner(app_mode, app_id); self.os.vm_runtime.sync_render_worker_ownership(); self.os.vm_runtime.render_manager.sync_telemetry(&self.os.vm_runtime.atomic_telemetry); } pub fn cert_config(&self) -> &CertificationConfig { &self.os.vm_runtime.certifier.config } pub fn last_crash_report(&self) -> Option<&CrashReport> { self.active_session() .and_then(|session| session.runtime.last_crash_report.as_ref()) .or(self.os.vm_runtime.last_crash_report.as_ref()) } pub fn current_app_id(&self) -> u32 { self.active_session() .map(|session| session.runtime.current_app_id) .unwrap_or(self.os.vm_runtime.current_app_id) } pub fn current_cartridge_title(&self) -> String { self.active_session() .map(|session| session.runtime.current_cartridge_title.clone()) .unwrap_or_else(|| self.os.vm_runtime.current_cartridge_title.clone()) } pub fn current_cartridge_app_version(&self) -> String { self.active_session() .map(|session| session.runtime.current_cartridge_app_version.clone()) .unwrap_or_else(|| self.os.vm_runtime.current_cartridge_app_version.clone()) } pub fn current_cartridge_app_mode(&self) -> AppMode { self.active_session() .map(|session| session.runtime.current_cartridge_app_mode) .unwrap_or(self.os.vm_runtime.current_cartridge_app_mode) } fn active_session(&self) -> Option<&VmSession> { self.os .task_manager .foreground_task() .and_then(|task_id| self.os.vm_sessions.for_task(task_id)) .or_else(|| { self.os .foreground_stack .resident_game_task() .and_then(|task_id| self.os.vm_sessions.for_task(task_id)) }) } fn active_session_mut(&mut self) -> Option<&mut VmSession> { let task_id = self.active_session_task_id()?; self.os.vm_sessions.for_task_mut(task_id) } fn active_session_task_id(&self) -> Option { self.os .task_manager .foreground_task() .or_else(|| self.os.foreground_stack.resident_game_task()) } }