From be684ab0f950685c40bf488d7a3fa39010f20582 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Thu, 14 May 2026 17:39:55 +0100 Subject: [PATCH] add process and task managers --- .../src/firmware/firmware.rs | 14 +- .../firmware/firmware_step_crash_screen.rs | 7 +- .../firmware/firmware_step_game_running.rs | 37 ++- .../src/firmware/firmware_step_hub_home.rs | 2 +- .../src/firmware/firmware_step_launch_hub.rs | 4 +- .../firmware/firmware_step_load_cartridge.rs | 14 +- .../src/firmware/firmware_step_reset.rs | 4 +- .../firmware/firmware_step_splash_screen.rs | 7 +- .../firmware/firmware_step_system_running.rs | 16 +- .../src/firmware/prometeu_context.rs | 4 +- crates/console/prometeu-hal/src/cartridge.rs | 2 +- crates/console/prometeu-system/src/lib.rs | 4 + crates/console/prometeu-system/src/os/mod.rs | 3 + .../prometeu-system/src/os/system_os.rs | 48 +++ .../src/programs/prometeu_hub/prometeu_hub.rs | 18 +- .../prometeu-system/src/services/mod.rs | 2 + .../src/services/process/mod.rs | 9 + .../src/services/process/model.rs | 42 +++ .../src/services/process/process_kind.rs | 11 + .../src/services/process/process_manager.rs | 180 +++++++++++ .../src/services/process/process_state.rs | 8 + .../prometeu-system/src/services/task/mod.rs | 10 + .../src/services/task/model.rs | 51 ++++ .../src/services/task/task_kind.rs | 5 + .../src/services/task/task_manager.rs | 282 ++++++++++++++++++ .../src/services/task/task_state.rs | 8 + .../src/virtual_machine_runtime/tests.rs | 10 +- .../tests_fs_memcard.rs | 2 +- .../src/debugger.rs | 60 ++-- .../src/overlay.rs | 7 +- .../prometeu-host-desktop-winit/src/runner.rs | 33 +- .../prometeu-host-desktop-winit/src/stats.rs | 5 +- 32 files changed, 816 insertions(+), 93 deletions(-) create mode 100644 crates/console/prometeu-system/src/os/mod.rs create mode 100644 crates/console/prometeu-system/src/os/system_os.rs create mode 100644 crates/console/prometeu-system/src/services/process/mod.rs create mode 100644 crates/console/prometeu-system/src/services/process/model.rs create mode 100644 crates/console/prometeu-system/src/services/process/process_kind.rs create mode 100644 crates/console/prometeu-system/src/services/process/process_manager.rs create mode 100644 crates/console/prometeu-system/src/services/process/process_state.rs create mode 100644 crates/console/prometeu-system/src/services/task/mod.rs create mode 100644 crates/console/prometeu-system/src/services/task/model.rs create mode 100644 crates/console/prometeu-system/src/services/task/task_kind.rs create mode 100644 crates/console/prometeu-system/src/services/task/task_manager.rs create mode 100644 crates/console/prometeu-system/src/services/task/task_state.rs diff --git a/crates/console/prometeu-firmware/src/firmware/firmware.rs b/crates/console/prometeu-firmware/src/firmware/firmware.rs index 83470ec7..93787873 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware.rs @@ -4,7 +4,7 @@ use crate::firmware::prometeu_context::PrometeuContext; use prometeu_hal::cartridge::Cartridge; use prometeu_hal::telemetry::CertificationConfig; use prometeu_hal::{HardwareBridge, InputSignals}; -use prometeu_system::{PrometeuHub, VirtualMachineRuntime}; +use prometeu_system::{PrometeuHub, SystemOS}; use prometeu_vm::VirtualMachine; /// PROMETEU Firmware. @@ -26,7 +26,7 @@ pub struct Firmware { /// The execution engine (PVM) for user applications. pub vm: VirtualMachine, /// The underlying OS services (Syscalls, Filesystem, Telemetry). - pub os: VirtualMachineRuntime, + pub os: SystemOS, /// The internal state of the system launcher (Hub). pub hub: PrometeuHub, /// The current operational state (e.g., Reset, SplashScreen, GameRunning). @@ -42,7 +42,7 @@ impl Firmware { pub fn new(cap_config: Option) -> Self { Self { vm: VirtualMachine::default(), - os: VirtualMachineRuntime::new(cap_config), + os: SystemOS::new(cap_config), hub: PrometeuHub::new(), state: FirmwareState::Reset(ResetStep), boot_target: BootTarget::Hub, @@ -335,7 +335,7 @@ mod tests { let mut hardware = Hardware::new(); let signals = InputSignals::default(); - firmware.load_cartridge(valid_cartridge(AppMode::System)); + firmware.load_cartridge(valid_cartridge(AppMode::Shell)); firmware.tick(&signals, &mut hardware); assert!(matches!(firmware.state, FirmwareState::SystemRunning(_))); @@ -349,14 +349,14 @@ mod tests { let mut hardware = Hardware::new(); let signals = InputSignals::default(); - firmware.load_cartridge(valid_cartridge(AppMode::System)); + firmware.load_cartridge(valid_cartridge(AppMode::Shell)); firmware.tick(&signals, &mut hardware); - let tick_index_before_system_update = firmware.os.tick_index; + let tick_index_before_system_update = firmware.os.vm_runtime.tick_index; firmware.tick(&signals, &mut hardware); assert!(matches!(firmware.state, FirmwareState::SystemRunning(_))); - assert_eq!(firmware.os.tick_index, tick_index_before_system_update + 1); + assert_eq!(firmware.os.vm_runtime.tick_index, tick_index_before_system_update + 1); } #[test] diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_step_crash_screen.rs b/crates/console/prometeu-firmware/src/firmware/firmware_step_crash_screen.rs index 8133a3cc..050ffe0c 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_step_crash_screen.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_step_crash_screen.rs @@ -15,7 +15,12 @@ impl AppCrashesStep { } pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { - ctx.os.log(LogLevel::Error, LogSource::Pos, self.report.log_tag(), self.log_message()); + ctx.os.vm_runtime.log( + LogLevel::Error, + LogSource::Pos, + self.report.log_tag(), + self.log_message(), + ); } pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_step_game_running.rs b/crates/console/prometeu-firmware/src/firmware/firmware_step_game_running.rs index 9ed3376f..508cfd16 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_step_game_running.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_step_game_running.rs @@ -1,19 +1,48 @@ use crate::firmware::firmware_state::{AppCrashesStep, FirmwareState}; use crate::firmware::prometeu_context::PrometeuContext; use prometeu_hal::log::{LogLevel, LogSource}; +use prometeu_system::CrashReport; +use prometeu_system::task::{TaskId, TaskState}; #[derive(Debug, Clone)] -pub struct GameRunningStep; +pub struct GameRunningStep { + pub task_id: TaskId, +} impl GameRunningStep { + pub fn new(task_id: TaskId) -> Self { + Self { task_id } + } + pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { - ctx.os.log(LogLevel::Info, LogSource::Pos, 0, "Entering GameRunning".to_string()); + ctx.os.vm_runtime.log( + LogLevel::Info, + LogSource::Pos, + 0, + "Entering GameRunning".to_string(), + ); } pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { - let result = ctx.os.tick(ctx.vm, ctx.signals, ctx.hw); + let Some(task) = ctx.os.task_manager.get(self.task_id) else { + // TODO: is it panic? + let report = + CrashReport::VmPanic { message: "task is gone, panic!".to_string(), pc: None }; + return Some(FirmwareState::AppCrashes(AppCrashesStep { report })); + }; - if !ctx.os.logical_frame_active { + if task.state != TaskState::Foreground { + // TODO: is it panic? + let report = CrashReport::VmPanic { + message: "should be running as foreground, panic!".to_string(), + pc: None, + }; + return Some(FirmwareState::AppCrashes(AppCrashesStep { report })); + } + + let result = ctx.os.vm_runtime.tick(ctx.vm, ctx.signals, ctx.hw); + + if !ctx.os.vm_runtime.logical_frame_active { ctx.hw.gfx_mut().present(); } diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs b/crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs index 8ad987b5..bd558cae 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs @@ -7,7 +7,7 @@ pub struct HubHomeStep; impl HubHomeStep { pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { - ctx.os.log(LogLevel::Info, LogSource::Hub, 0, "Entering HubHome".to_string()); + ctx.os.vm_runtime.log(LogLevel::Info, LogSource::Hub, 0, "Entering HubHome".to_string()); } pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_step_launch_hub.rs b/crates/console/prometeu-firmware/src/firmware/firmware_step_launch_hub.rs index eb20a8c4..c3f73043 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_step_launch_hub.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_step_launch_hub.rs @@ -9,7 +9,7 @@ pub struct LaunchHubStep; impl LaunchHubStep { pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { - ctx.os.log(LogLevel::Info, LogSource::Pos, 0, "Launching Hub".to_string()); + ctx.os.vm_runtime.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.log( + ctx.os.vm_runtime.log( LogLevel::Error, LogSource::Pos, 0, diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_step_load_cartridge.rs b/crates/console/prometeu-firmware/src/firmware/firmware_step_load_cartridge.rs index 3fdb9956..4b2951c5 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_step_load_cartridge.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_step_load_cartridge.rs @@ -20,7 +20,7 @@ impl LoadCartridgeStep { } pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { - ctx.os.log( + ctx.os.vm_runtime.log( LogLevel::Info, LogSource::Pos, 0, @@ -34,7 +34,7 @@ impl LoadCartridgeStep { self.cartridge.assets.clone(), ); - self.init_error = ctx.os.initialize_vm(ctx.vm, &self.cartridge).err(); + self.init_error = ctx.os.vm_runtime.initialize_vm(ctx.vm, &self.cartridge).err(); } pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { @@ -42,7 +42,7 @@ impl LoadCartridgeStep { return Some(FirmwareState::AppCrashes(AppCrashesStep { report })); } - if self.cartridge.app_mode == AppMode::System { + if self.cartridge.app_mode == AppMode::Shell { let id = ctx.hub.window_manager.add_window( self.cartridge.title.clone(), Rect { x: 40, y: 20, w: 240, h: 140 }, @@ -50,10 +50,14 @@ impl LoadCartridgeStep { ); ctx.hub.window_manager.set_focus(id); - return Some(FirmwareState::SystemRunning(SystemRunningStep)); + let task_id = + ctx.os.create_vm_shell_task(self.cartridge.app_id, self.cartridge.title.clone()); + return Some(FirmwareState::SystemRunning(SystemRunningStep::new(task_id))); } - Some(FirmwareState::GameRunning(GameRunningStep)) + let task_id = + ctx.os.create_vm_game_task(self.cartridge.app_id, self.cartridge.title.clone()); + Some(FirmwareState::GameRunning(GameRunningStep::new(task_id))) } pub fn on_exit(&mut self, _ctx: &mut PrometeuContext) {} diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_step_reset.rs b/crates/console/prometeu-firmware/src/firmware/firmware_step_reset.rs index 4c14c749..8718369e 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_step_reset.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_step_reset.rs @@ -8,8 +8,8 @@ pub struct ResetStep; impl ResetStep { pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { - ctx.os.log(LogLevel::Info, LogSource::Pos, 0, "Firmware Reset".to_string()); - ctx.os.reset(ctx.vm); + ctx.os.vm_runtime.log(LogLevel::Info, LogSource::Pos, 0, "Firmware Reset".to_string()); + ctx.os.vm_runtime.reset(ctx.vm); } pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_step_splash_screen.rs b/crates/console/prometeu-firmware/src/firmware/firmware_step_splash_screen.rs index 6695a7dd..da1e14d1 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_step_splash_screen.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_step_splash_screen.rs @@ -10,7 +10,12 @@ pub struct SplashScreenStep { impl SplashScreenStep { pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { - ctx.os.log(LogLevel::Info, LogSource::Pos, 0, "Showing SplashScreen".to_string()); + ctx.os.vm_runtime.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); } diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_step_system_running.rs b/crates/console/prometeu-firmware/src/firmware/firmware_step_system_running.rs index bfc18cd9..71c33289 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_step_system_running.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_step_system_running.rs @@ -1,13 +1,25 @@ use crate::firmware::firmware_state::{AppCrashesStep, FirmwareState, HubHomeStep}; use crate::firmware::prometeu_context::PrometeuContext; use prometeu_hal::log::{LogLevel, LogSource}; +use prometeu_system::task::TaskId; #[derive(Debug, Clone)] -pub struct SystemRunningStep; +pub struct SystemRunningStep { + pub task_id: TaskId, +} impl SystemRunningStep { + pub fn new(task_id: TaskId) -> Self { + Self { task_id } + } + pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { - ctx.os.log(LogLevel::Info, LogSource::Hub, 0, "Entering SystemRunning".to_string()); + ctx.os.vm_runtime.log( + LogLevel::Info, + LogSource::Hub, + 0, + "Entering SystemRunning".to_string(), + ); } pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { diff --git a/crates/console/prometeu-firmware/src/firmware/prometeu_context.rs b/crates/console/prometeu-firmware/src/firmware/prometeu_context.rs index 2e06bcde..b3412092 100644 --- a/crates/console/prometeu-firmware/src/firmware/prometeu_context.rs +++ b/crates/console/prometeu-firmware/src/firmware/prometeu_context.rs @@ -1,11 +1,11 @@ use crate::firmware::boot_target::BootTarget; use prometeu_hal::{HardwareBridge, InputSignals}; -use prometeu_system::{PrometeuHub, VirtualMachineRuntime}; +use prometeu_system::{PrometeuHub, SystemOS}; use prometeu_vm::VirtualMachine; pub struct PrometeuContext<'a> { pub vm: &'a mut VirtualMachine, - pub os: &'a mut VirtualMachineRuntime, + pub os: &'a mut SystemOS, pub hub: &'a mut PrometeuHub, pub boot_target: &'a BootTarget, pub signals: &'a InputSignals, diff --git a/crates/console/prometeu-hal/src/cartridge.rs b/crates/console/prometeu-hal/src/cartridge.rs index eb49f03c..8fa72386 100644 --- a/crates/console/prometeu-hal/src/cartridge.rs +++ b/crates/console/prometeu-hal/src/cartridge.rs @@ -13,7 +13,7 @@ pub const ASSETS_PA_PRELUDE_SIZE: usize = 32; #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] pub enum AppMode { Game, - System, + Shell, } #[derive(Debug, Clone)] diff --git a/crates/console/prometeu-system/src/lib.rs b/crates/console/prometeu-system/src/lib.rs index 53dc009c..4cb3affd 100644 --- a/crates/console/prometeu-system/src/lib.rs +++ b/crates/console/prometeu-system/src/lib.rs @@ -1,9 +1,13 @@ mod crash_report; +mod os; mod programs; mod services; mod virtual_machine_runtime; pub use crash_report::CrashReport; +pub use os::SystemOS; pub use programs::PrometeuHub; pub use services::fs; +pub use services::process; +pub use services::task; pub use virtual_machine_runtime::VirtualMachineRuntime; diff --git a/crates/console/prometeu-system/src/os/mod.rs b/crates/console/prometeu-system/src/os/mod.rs new file mode 100644 index 00000000..61995fd9 --- /dev/null +++ b/crates/console/prometeu-system/src/os/mod.rs @@ -0,0 +1,3 @@ +mod system_os; + +pub use system_os::SystemOS; diff --git a/crates/console/prometeu-system/src/os/system_os.rs b/crates/console/prometeu-system/src/os/system_os.rs new file mode 100644 index 00000000..78500121 --- /dev/null +++ b/crates/console/prometeu-system/src/os/system_os.rs @@ -0,0 +1,48 @@ +use crate::VirtualMachineRuntime; +use crate::process::ProcessManager; +use crate::task::TaskId; +use crate::task::TaskManager; +use prometeu_hal::telemetry::CertificationConfig; + +pub struct SystemOS { + pub vm_runtime: VirtualMachineRuntime, + pub process_manager: ProcessManager, + pub task_manager: TaskManager, +} + +impl SystemOS { + pub fn new(cap_config: Option) -> Self { + Self { + vm_runtime: VirtualMachineRuntime::new(cap_config), + process_manager: ProcessManager::new(), + task_manager: TaskManager::new(), + } + } + + pub fn create_vm_game_task(&mut self, app_id: u32, title: impl Into) -> TaskId { + let title = title.into(); + let process_id = self.process_manager.spawn_vm_game(app_id, title.clone()); + let task_id = self.task_manager.create_game_task(process_id, app_id, title); + self.task_manager.set_foreground(task_id); + + task_id + } + + pub fn create_vm_shell_task(&mut self, app_id: u32, title: impl Into) -> TaskId { + let title = title.into(); + let process_id = self.process_manager.spawn_vm_shell(app_id, title.clone()); + let task_id = self.task_manager.create_shell_task(process_id, app_id, title); + self.task_manager.set_foreground(task_id); + + task_id + } + + pub fn create_native_shell_task(&mut self, app_id: u32, title: impl Into) -> TaskId { + let title = title.into(); + let process_id = self.process_manager.spawn_native_shell(app_id, title.clone()); + let task_id = self.task_manager.create_shell_task(process_id, app_id, title); + self.task_manager.set_foreground(task_id); + + task_id + } +} diff --git a/crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs b/crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs index 5519af6e..6d013dd9 100644 --- a/crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs +++ b/crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs @@ -1,5 +1,5 @@ use crate::programs::prometeu_hub::window_manager::WindowManager; -use crate::{CrashReport, VirtualMachineRuntime}; +use crate::{CrashReport, SystemOS}; use prometeu_hal::color::Color; use prometeu_hal::log::{LogLevel, LogSource}; use prometeu_hal::window::Rect; @@ -31,34 +31,34 @@ impl PrometeuHub { // Initializes the Window System and lists apps } - pub fn gui_update(&mut self, os: &mut VirtualMachineRuntime, hw: &mut dyn HardwareBridge) { + pub fn gui_update(&mut self, os: &mut SystemOS, hw: &mut dyn HardwareBridge) { hw.gfx_mut().clear(Color::BLACK); let mut next_window = None; if hw.pad().a().pressed { - os.log(LogLevel::Debug, LogSource::Hub, 0, "window A opened".to_string()); + os.vm_runtime.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.log(LogLevel::Debug, LogSource::Hub, 0, "window B opened".to_string()); + os.vm_runtime.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.log(LogLevel::Debug, LogSource::Hub, 0, "window X opened".to_string()); + os.vm_runtime.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.log(LogLevel::Debug, LogSource::Hub, 0, "window Y opened".to_string()); + os.vm_runtime.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)); } @@ -70,7 +70,7 @@ impl PrometeuHub { } } - pub fn render(&mut self, _os: &mut VirtualMachineRuntime, hw: &mut dyn HardwareBridge) { + pub fn render(&mut self, _os: &mut SystemOS, hw: &mut dyn HardwareBridge) { for window in &self.window_manager.windows { hw.gfx_mut().fill_rect( window.viewport.x, @@ -84,7 +84,7 @@ impl PrometeuHub { pub fn update_system_profile( &mut self, - os: &mut VirtualMachineRuntime, + os: &mut SystemOS, vm: &mut VirtualMachine, signals: &InputSignals, hw: &mut dyn HardwareBridge, @@ -97,7 +97,7 @@ impl PrometeuHub { if hw.pad().start().down { self.window_manager.remove_window(focused_id); } else { - crash = os.tick(vm, signals, hw); + crash = os.vm_runtime.tick(vm, signals, hw); } } diff --git a/crates/console/prometeu-system/src/services/mod.rs b/crates/console/prometeu-system/src/services/mod.rs index 0eb81e85..afdf1642 100644 --- a/crates/console/prometeu-system/src/services/mod.rs +++ b/crates/console/prometeu-system/src/services/mod.rs @@ -1,2 +1,4 @@ pub mod fs; pub mod memcard; +pub mod process; +pub mod task; diff --git a/crates/console/prometeu-system/src/services/process/mod.rs b/crates/console/prometeu-system/src/services/process/mod.rs new file mode 100644 index 00000000..3aed9d4d --- /dev/null +++ b/crates/console/prometeu-system/src/services/process/mod.rs @@ -0,0 +1,9 @@ +mod model; +mod process_kind; +pub mod process_manager; +mod process_state; + +pub use model::{Process, ProcessId}; +pub use process_kind::ProcessKind; +pub use process_manager::ProcessManager; +pub use process_state::ProcessState; diff --git a/crates/console/prometeu-system/src/services/process/model.rs b/crates/console/prometeu-system/src/services/process/model.rs new file mode 100644 index 00000000..43bf3c1b --- /dev/null +++ b/crates/console/prometeu-system/src/services/process/model.rs @@ -0,0 +1,42 @@ +use crate::process::{ProcessKind, ProcessState}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ProcessId(pub u32); + +#[derive(Debug, Clone)] +pub struct Process { + pub id: ProcessId, + pub app_id: u32, + pub title: String, + pub kind: ProcessKind, + pub state: ProcessState, +} + +impl Process { + pub fn new(id: ProcessId, app_id: u32, title: impl Into, kind: ProcessKind) -> Self { + Self { id, app_id, title: title.into(), kind, state: ProcessState::Created } + } + + pub fn mark_running(&mut self) { + self.state = ProcessState::Running; + } + + pub fn mark_suspended(&mut self) { + self.state = ProcessState::Suspended; + } + + pub fn mark_stopped(&mut self) { + self.state = ProcessState::Stopped; + } + + pub fn mark_crashed(&mut self) { + self.state = ProcessState::Crashed; + } + + pub fn is_alive(&self) -> bool { + matches!( + self.state, + ProcessState::Created | ProcessState::Running | ProcessState::Suspended + ) + } +} diff --git a/crates/console/prometeu-system/src/services/process/process_kind.rs b/crates/console/prometeu-system/src/services/process/process_kind.rs new file mode 100644 index 00000000..c4368206 --- /dev/null +++ b/crates/console/prometeu-system/src/services/process/process_kind.rs @@ -0,0 +1,11 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProcessKind { + /// VM-backed fullscreen game cartridge. + VmGame, + + /// VM-backed app hosted by the Prometeu Shell. + VmShell, + + /// Native Rust app/component hosted by the Prometeu Shell. + NativeShell, +} diff --git a/crates/console/prometeu-system/src/services/process/process_manager.rs b/crates/console/prometeu-system/src/services/process/process_manager.rs new file mode 100644 index 00000000..e3a348c1 --- /dev/null +++ b/crates/console/prometeu-system/src/services/process/process_manager.rs @@ -0,0 +1,180 @@ +use crate::process::{Process, ProcessId, ProcessKind, ProcessState}; +use std::collections::HashMap; + +#[derive(Debug, Default)] +pub struct ProcessManager { + next_id: u32, + processes: HashMap, +} + +impl ProcessManager { + pub fn new() -> Self { + Self { next_id: 1, processes: HashMap::new() } + } + + pub fn spawn_vm_game(&mut self, app_id: u32, title: impl Into) -> ProcessId { + self.spawn(app_id, title, ProcessKind::VmGame) + } + + pub fn spawn_vm_shell(&mut self, app_id: u32, title: impl Into) -> ProcessId { + self.spawn(app_id, title, ProcessKind::VmShell) + } + + pub fn spawn_native_shell(&mut self, app_id: u32, title: impl Into) -> ProcessId { + self.spawn(app_id, title, ProcessKind::NativeShell) + } + + fn spawn(&mut self, app_id: u32, title: impl Into, kind: ProcessKind) -> ProcessId { + let id = ProcessId(self.next_id); + self.next_id += 1; + + let mut process = Process::new(id, app_id, title, kind); + process.mark_running(); + + self.processes.insert(id, process); + + id + } + + pub fn get(&self, id: ProcessId) -> Option<&Process> { + self.processes.get(&id) + } + + pub fn get_mut(&mut self, id: ProcessId) -> Option<&mut Process> { + self.processes.get_mut(&id) + } + + pub fn contains(&self, id: ProcessId) -> bool { + self.processes.contains_key(&id) + } + + pub fn all(&self) -> impl Iterator { + self.processes.values() + } + + pub fn mark_running(&mut self, id: ProcessId) -> bool { + self.transition(id, ProcessState::Running) + } + + pub fn mark_suspended(&mut self, id: ProcessId) -> bool { + self.transition(id, ProcessState::Suspended) + } + + pub fn mark_stopped(&mut self, id: ProcessId) -> bool { + self.transition(id, ProcessState::Stopped) + } + + pub fn mark_crashed(&mut self, id: ProcessId) -> bool { + self.transition(id, ProcessState::Crashed) + } + + pub fn remove_stopped(&mut self) { + self.processes.retain(|_, process| process.state != ProcessState::Stopped); + } + + fn transition(&mut self, id: ProcessId, state: ProcessState) -> bool { + let Some(process) = self.get_mut(id) else { + return false; + }; + + process.state = state; + true + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn spawn_vm_game_creates_running_process() { + let mut manager = ProcessManager::new(); + + let id = manager.spawn_vm_game(42, "Amazing Game"); + + let process = manager.get(id).expect("process should exist"); + + assert_eq!(process.id, id); + assert_eq!(process.app_id, 42); + assert_eq!(process.title, "Amazing Game"); + assert_eq!(process.kind, ProcessKind::VmGame); + assert_eq!(process.state, ProcessState::Running); + } + + #[test] + fn spawn_vm_shell_creates_running_process() { + let mut manager = ProcessManager::new(); + + let id = manager.spawn_vm_shell(42, "Amazing Shell"); + + let process = manager.get(id).expect("process should exist"); + + assert_eq!(process.id, id); + assert_eq!(process.app_id, 42); + assert_eq!(process.title, "Amazing Shell"); + assert_eq!(process.kind, ProcessKind::VmShell); + assert_eq!(process.state, ProcessState::Running); + } + + #[test] + fn spawn_native_shell_creates_running_process() { + let mut manager = ProcessManager::new(); + + let id = manager.spawn_native_shell(42, "Amazing Shell"); + + let process = manager.get(id).expect("process should exist"); + + assert_eq!(process.id, id); + assert_eq!(process.app_id, 42); + assert_eq!(process.title, "Amazing Shell"); + assert_eq!(process.kind, ProcessKind::NativeShell); + assert_eq!(process.state, ProcessState::Running); + } + + #[test] + fn mark_suspended_updates_process_state() { + let mut manager = ProcessManager::new(); + + let id = manager.spawn_vm_game(42, "Amazing Game"); + + assert!(manager.mark_suspended(id)); + + let process = manager.get(id).expect("process should exist"); + + assert_eq!(process.state, ProcessState::Suspended); + } + + #[test] + fn mark_stopped_updates_process_state() { + let mut manager = ProcessManager::new(); + + let id = manager.spawn_vm_game(42, "Amazing Game"); + + assert!(manager.mark_stopped(id)); + + let process = manager.get(id).expect("process should exist"); + + assert_eq!(process.state, ProcessState::Stopped); + } + + #[test] + fn unknown_process_transition_returns_false() { + let mut manager = ProcessManager::new(); + + assert!(!manager.mark_stopped(ProcessId(999))); + } + + #[test] + fn remove_stopped_keeps_running_processes() { + let mut manager = ProcessManager::new(); + + let running = manager.spawn_vm_game(1, "Running"); + let stopped = manager.spawn_vm_game(2, "Stopped"); + + manager.mark_stopped(stopped); + manager.remove_stopped(); + + assert!(manager.get(running).is_some()); + assert!(manager.get(stopped).is_none()); + } +} diff --git a/crates/console/prometeu-system/src/services/process/process_state.rs b/crates/console/prometeu-system/src/services/process/process_state.rs new file mode 100644 index 00000000..44b4f386 --- /dev/null +++ b/crates/console/prometeu-system/src/services/process/process_state.rs @@ -0,0 +1,8 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProcessState { + Created, + Running, + Stopped, + Suspended, + Crashed, +} diff --git a/crates/console/prometeu-system/src/services/task/mod.rs b/crates/console/prometeu-system/src/services/task/mod.rs new file mode 100644 index 00000000..9c08990a --- /dev/null +++ b/crates/console/prometeu-system/src/services/task/mod.rs @@ -0,0 +1,10 @@ +mod model; +mod task_kind; +mod task_manager; +mod task_state; + +pub use model::Task; +pub use model::TaskId; +pub use task_kind::TaskKind; +pub use task_manager::TaskManager; +pub use task_state::TaskState; diff --git a/crates/console/prometeu-system/src/services/task/model.rs b/crates/console/prometeu-system/src/services/task/model.rs new file mode 100644 index 00000000..524a4ee6 --- /dev/null +++ b/crates/console/prometeu-system/src/services/task/model.rs @@ -0,0 +1,51 @@ +use crate::process::ProcessId; +use crate::task::{TaskKind, TaskState}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct TaskId(pub u32); + +#[derive(Debug, Clone)] +pub struct Task { + pub id: TaskId, + pub process_id: ProcessId, + pub app_id: u32, + pub title: String, + pub kind: TaskKind, + pub state: TaskState, +} + +impl Task { + pub fn new( + id: TaskId, + process_id: ProcessId, + app_id: u32, + title: impl Into, + kind: TaskKind, + ) -> Self { + Self { id, process_id, app_id, title: title.into(), kind, state: TaskState::Background } + } + + pub fn mark_foreground(&mut self) { + self.state = TaskState::Foreground; + } + + pub fn mark_background(&mut self) { + self.state = TaskState::Background; + } + + pub fn mark_suspended(&mut self) { + self.state = TaskState::Suspended; + } + + pub fn mark_closed(&mut self) { + self.state = TaskState::Closed; + } + + pub fn mark_crashed(&mut self) { + self.state = TaskState::Crashed; + } + + pub fn is_active(&self) -> bool { + matches!(self.state, TaskState::Foreground | TaskState::Background | TaskState::Suspended) + } +} diff --git a/crates/console/prometeu-system/src/services/task/task_kind.rs b/crates/console/prometeu-system/src/services/task/task_kind.rs new file mode 100644 index 00000000..f831d05a --- /dev/null +++ b/crates/console/prometeu-system/src/services/task/task_kind.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TaskKind { + Game, + Shell, +} diff --git a/crates/console/prometeu-system/src/services/task/task_manager.rs b/crates/console/prometeu-system/src/services/task/task_manager.rs new file mode 100644 index 00000000..ace3fc56 --- /dev/null +++ b/crates/console/prometeu-system/src/services/task/task_manager.rs @@ -0,0 +1,282 @@ +use super::{Task, TaskId, TaskKind, TaskState}; +use crate::services::process::ProcessId; +use std::collections::HashMap; + +#[derive(Debug, Default)] +pub struct TaskManager { + next_id: u32, + tasks: HashMap, + foreground_task: Option, +} + +impl TaskManager { + pub fn new() -> Self { + Self { next_id: 1, tasks: HashMap::new(), foreground_task: None } + } + + pub fn create_game_task( + &mut self, + process_id: ProcessId, + app_id: u32, + title: impl Into, + ) -> TaskId { + self.create(process_id, app_id, title, TaskKind::Game) + } + + pub fn create_shell_task( + &mut self, + process_id: ProcessId, + app_id: u32, + title: impl Into, + ) -> TaskId { + self.create(process_id, app_id, title, TaskKind::Shell) + } + + fn create( + &mut self, + process_id: ProcessId, + app_id: u32, + title: impl Into, + kind: TaskKind, + ) -> TaskId { + let id = TaskId(self.next_id); + self.next_id += 1; + + let task = Task::new(id, process_id, app_id, title, kind); + self.tasks.insert(id, task); + + id + } + + pub fn get(&self, id: TaskId) -> Option<&Task> { + self.tasks.get(&id) + } + + pub fn get_mut(&mut self, id: TaskId) -> Option<&mut Task> { + self.tasks.get_mut(&id) + } + + pub fn contains(&self, id: TaskId) -> bool { + self.tasks.contains_key(&id) + } + + pub fn all(&self) -> impl Iterator { + self.tasks.values() + } + + pub fn foreground_task(&self) -> Option { + self.foreground_task + } + + pub fn foreground(&self) -> Option<&Task> { + self.foreground_task.and_then(|id| self.get(id)) + } + + pub fn set_foreground(&mut self, id: TaskId) -> bool { + if !self.tasks.contains_key(&id) { + return false; + } + + if let Some(current_id) = self.foreground_task + && current_id != id + && let Some(current) = self.tasks.get_mut(¤t_id) + && current.state == TaskState::Foreground + { + current.mark_background(); + } + + let Some(task) = self.tasks.get_mut(&id) else { + return false; + }; + + task.mark_foreground(); + self.foreground_task = Some(id); + + true + } + + pub fn clear_foreground(&mut self) { + if let Some(id) = self.foreground_task.take() + && let Some(task) = self.tasks.get_mut(&id) + && task.state == TaskState::Foreground + { + task.mark_background(); + } + } + + pub fn mark_background(&mut self, id: TaskId) -> bool { + let Some(task) = self.tasks.get_mut(&id) else { + return false; + }; + + task.mark_background(); + + if self.foreground_task == Some(id) { + self.foreground_task = None; + } + + true + } + + pub fn mark_suspended(&mut self, id: TaskId) -> bool { + let Some(task) = self.tasks.get_mut(&id) else { + return false; + }; + + task.mark_suspended(); + + if self.foreground_task == Some(id) { + self.foreground_task = None; + } + + true + } + + pub fn close_task(&mut self, id: TaskId) -> bool { + let Some(task) = self.tasks.get_mut(&id) else { + return false; + }; + + task.mark_closed(); + + if self.foreground_task == Some(id) { + self.foreground_task = None; + } + + true + } + + pub fn mark_crashed(&mut self, id: TaskId) -> bool { + let Some(task) = self.tasks.get_mut(&id) else { + return false; + }; + + task.mark_crashed(); + + if self.foreground_task == Some(id) { + self.foreground_task = None; + } + + true + } + + pub fn remove_closed(&mut self) { + self.tasks.retain(|id, task| { + let should_keep = task.state != TaskState::Closed; + if !should_keep && self.foreground_task == Some(*id) { + self.foreground_task = None; + } + should_keep + }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::services::process::ProcessId; + + #[test] + fn create_game_task_registers_task() { + let mut manager = TaskManager::new(); + let process_id = ProcessId(7); + + let task_id = manager.create_game_task(process_id, 42, "Sector Crawl"); + + let task = manager.get(task_id).expect("task should exist"); + + assert_eq!(task.id, task_id); + assert_eq!(task.process_id, process_id); + assert_eq!(task.app_id, 42); + assert_eq!(task.title, "Sector Crawl"); + assert_eq!(task.kind, TaskKind::Game); + assert_eq!(task.state, TaskState::Background); + } + + #[test] + fn set_foreground_marks_task_as_foreground() { + let mut manager = TaskManager::new(); + let task_id = manager.create_game_task(ProcessId(1), 42, "Sector Crawl"); + + assert!(manager.set_foreground(task_id)); + + let task = manager.get(task_id).expect("task should exist"); + + assert_eq!(manager.foreground_task(), Some(task_id)); + assert_eq!(task.state, TaskState::Foreground); + } + + #[test] + fn set_foreground_moves_previous_task_to_background() { + let mut manager = TaskManager::new(); + + let first = manager.create_game_task(ProcessId(1), 1, "First"); + let second = manager.create_game_task(ProcessId(2), 2, "Second"); + + assert!(manager.set_foreground(first)); + assert!(manager.set_foreground(second)); + + assert_eq!(manager.foreground_task(), Some(second)); + assert_eq!( + manager.get(first).expect("first task should exist").state, + TaskState::Background + ); + assert_eq!( + manager.get(second).expect("second task should exist").state, + TaskState::Foreground + ); + } + + #[test] + fn set_foreground_unknown_task_returns_false() { + let mut manager = TaskManager::new(); + + assert!(!manager.set_foreground(TaskId(999))); + assert_eq!(manager.foreground_task(), None); + } + + #[test] + fn close_foreground_task_clears_foreground() { + let mut manager = TaskManager::new(); + + let task_id = manager.create_game_task(ProcessId(1), 42, "Sector Crawl"); + + assert!(manager.set_foreground(task_id)); + assert!(manager.close_task(task_id)); + + let task = manager.get(task_id).expect("task should exist"); + + assert_eq!(task.state, TaskState::Closed); + assert_eq!(manager.foreground_task(), None); + } + + #[test] + fn suspend_foreground_task_clears_foreground() { + let mut manager = TaskManager::new(); + + let task_id = manager.create_game_task(ProcessId(1), 42, "Sector Crawl"); + + assert!(manager.set_foreground(task_id)); + assert!(manager.mark_suspended(task_id)); + + let task = manager.get(task_id).expect("task should exist"); + + assert_eq!(task.state, TaskState::Suspended); + assert_eq!(manager.foreground_task(), None); + } + + #[test] + fn crashed_foreground_task_clears_foreground() { + let mut manager = TaskManager::new(); + + let task_id = manager.create_game_task(ProcessId(1), 42, "Sector Crawl"); + + assert!(manager.set_foreground(task_id)); + assert!(manager.mark_crashed(task_id)); + + let task = manager.get(task_id).expect("task should exist"); + + assert_eq!(task.state, TaskState::Crashed); + assert_eq!(manager.foreground_task(), None); + } +} diff --git a/crates/console/prometeu-system/src/services/task/task_state.rs b/crates/console/prometeu-system/src/services/task/task_state.rs new file mode 100644 index 00000000..d7df7029 --- /dev/null +++ b/crates/console/prometeu-system/src/services/task/task_state.rs @@ -0,0 +1,8 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TaskState { + Foreground, + Background, + Suspended, + Closed, + Crashed, +} diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs index 30c5eff1..1867bcb9 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs @@ -285,7 +285,7 @@ fn tick_system_profile_rejects_gfx_game_surface() { ret_slots: 0, }], ); - let cartridge = cartridge_with_program_and_mode(program, caps::GFX, AppMode::System); + 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"); @@ -316,7 +316,7 @@ fn tick_system_profile_rejects_composer_game_surface() { ret_slots: 1, }], ); - let cartridge = cartridge_with_program_and_mode(program, caps::GFX, AppMode::System); + let cartridge = cartridge_with_program_and_mode(program, caps::GFX, AppMode::Shell); runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize"); let report = @@ -350,7 +350,7 @@ fn tick_system_profile_rejects_bank_game_surface() { ret_slots: 2, }], ); - let cartridge = cartridge_with_program_and_mode(program, caps::BANK, AppMode::System); + let cartridge = cartridge_with_program_and_mode(program, caps::BANK, AppMode::Shell); runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize"); let report = @@ -383,7 +383,7 @@ fn tick_system_profile_can_use_shared_log_transport() { ret_slots: 0, }], ); - let cartridge = cartridge_with_program_and_mode(program, caps::LOG, AppMode::System); + 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); @@ -632,7 +632,7 @@ fn reset_clears_cartridge_scoped_runtime_state() { runtime.current_app_id = 42; runtime.current_cartridge_title = "Cart".into(); runtime.current_cartridge_app_version = "1.2.3".into(); - runtime.current_cartridge_app_mode = AppMode::System; + runtime.current_cartridge_app_mode = AppMode::Shell; runtime.logs_written_this_frame.insert(42, 3); runtime.atomic_telemetry.logs_count.store(5, Ordering::Relaxed); runtime.atomic_telemetry.current_logs_count.store(8, Ordering::Relaxed); diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/tests_fs_memcard.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/tests_fs_memcard.rs index a9878795..bfd30f5a 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/tests_fs_memcard.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/tests_fs_memcard.rs @@ -81,7 +81,7 @@ fn tick_memcard_access_is_denied_for_non_game_profile() { app_id: 101, title: "System App".into(), app_version: "1.0.0".into(), - app_mode: AppMode::System, + app_mode: AppMode::Shell, capabilities: caps::FS, program, assets: AssetsPayloadSource::empty(), diff --git a/crates/host/prometeu-host-desktop-winit/src/debugger.rs b/crates/host/prometeu-host-desktop-winit/src/debugger.rs index 6941fa46..5430cd15 100644 --- a/crates/host/prometeu-host-desktop-winit/src/debugger.rs +++ b/crates/host/prometeu-host-desktop-winit/src/debugger.rs @@ -58,7 +58,7 @@ impl HostDebugger { // Pre-load cartridge metadata so the Handshake message can contain // valid information about the App being debugged. if let Ok(cartridge) = CartridgeLoader::load(path) { - let _ = firmware.os.initialize_vm(&mut firmware.vm, &cartridge); + let _ = firmware.os.vm_runtime.initialize_vm(&mut firmware.vm, &cartridge); } match TcpListener::bind(format!("127.0.0.1:{}", debug_port)) { @@ -117,10 +117,10 @@ impl HostDebugger { protocol_version: DEVTOOLS_PROTOCOL_VERSION, runtime_version: "0.1".to_string(), cartridge: HandshakeCartridge { - app_id: firmware.os.current_app_id, - title: firmware.os.current_cartridge_title.clone(), - app_version: firmware.os.current_cartridge_app_version.clone(), - app_mode: firmware.os.current_cartridge_app_mode, + app_id: firmware.os.vm_runtime.current_app_id, + title: firmware.os.vm_runtime.current_cartridge_title.clone(), + app_version: firmware.os.vm_runtime.current_cartridge_app_version.clone(), + app_mode: firmware.os.vm_runtime.current_cartridge_app_mode, }, }; self.send_response(handshake); @@ -138,7 +138,7 @@ impl HostDebugger { println!("[Debugger] Connection closed by remote."); self.stream = None; // Resume VM execution if it was paused waiting for the debugger. - firmware.os.paused = false; + firmware.os.vm_runtime.paused = false; self.waiting_for_start = false; } Ok(n) => { @@ -165,7 +165,7 @@ impl HostDebugger { Err(e) => { eprintln!("[Debugger] Connection error: {}", e); self.stream = None; - firmware.os.paused = false; + firmware.os.vm_runtime.paused = false; self.waiting_for_start = false; } } @@ -190,23 +190,23 @@ impl HostDebugger { println!("[Debugger] Starting execution..."); self.waiting_for_start = false; } - firmware.os.paused = false; + firmware.os.vm_runtime.paused = false; } DebugCommand::Pause => { - firmware.os.paused = true; + firmware.os.vm_runtime.paused = true; } DebugCommand::Resume => { - firmware.os.paused = false; + firmware.os.vm_runtime.paused = false; } DebugCommand::Step => { // Execute exactly one instruction and keep paused. - firmware.os.paused = true; - let _ = firmware.os.debug_step_instruction(&mut firmware.vm, hardware); + firmware.os.vm_runtime.paused = true; + let _ = firmware.os.vm_runtime.debug_step_instruction(&mut firmware.vm, hardware); } DebugCommand::StepFrame => { // Execute until the end of the current logical frame. - firmware.os.paused = false; - firmware.os.debug_step_request = true; + firmware.os.vm_runtime.paused = false; + firmware.os.vm_runtime.debug_step_request = true; } DebugCommand::GetState => { // Return detailed VM register and stack state. @@ -215,8 +215,8 @@ impl HostDebugger { let resp = DebugResponse::GetState { pc: firmware.vm.pc(), stack_top, - frame_index: firmware.os.logical_frame_index, - app_id: firmware.os.current_app_id, + frame_index: firmware.os.vm_runtime.logical_frame_index, + app_id: firmware.os.vm_runtime.current_app_id, }; self.send_response(resp); } @@ -301,14 +301,14 @@ impl HostDebugger { /// Scans the system for new information to push to the debugger client. fn stream_events(&mut self, firmware: &mut Firmware) { - if let Some(report) = firmware.os.last_crash_report.as_ref() { + if let Some(report) = firmware.os.vm_runtime.last_crash_report.as_ref() { self.stream_fault(report); } else { self.last_fault_summary = None; } // 1. Process and send new log entries. - let new_events = firmware.os.log_service.get_after(self.last_log_seq); + let new_events = firmware.os.vm_runtime.log_service.get_after(self.last_log_seq); for event in new_events { self.last_log_seq = event.seq; @@ -316,16 +316,16 @@ impl HostDebugger { if event.tag == 0xDEB1 { self.send_event(DebugEvent::BreakpointHit { pc: firmware.vm.pc(), - frame_index: firmware.os.logical_frame_index, + frame_index: firmware.os.vm_runtime.logical_frame_index, }); } if (0xCA01..=0xCA07).contains(&event.tag) && let Some(cert_event) = Self::cert_event_from_snapshot( event.tag, - firmware.os.atomic_telemetry.snapshot(), - &firmware.os.certifier.config, - firmware.os.logical_frame_index, + firmware.os.vm_runtime.atomic_telemetry.snapshot(), + &firmware.os.vm_runtime.certifier.config, + firmware.os.vm_runtime.logical_frame_index, ) { self.send_event(cert_event); @@ -339,9 +339,9 @@ impl HostDebugger { } // 2. Send telemetry snapshots at the completion of every frame. - let current_frame = firmware.os.logical_frame_index; + let current_frame = firmware.os.vm_runtime.logical_frame_index; if current_frame > self.last_telemetry_frame { - let tel = firmware.os.atomic_telemetry.snapshot(); + let tel = firmware.os.vm_runtime.atomic_telemetry.snapshot(); self.send_event(Self::telemetry_event_from_snapshot(tel)); self.last_telemetry_frame = current_frame; } @@ -399,14 +399,14 @@ mod tests { let mut hardware = Hardware::new(); debugger.handle_command(DebugCommand::Pause, &mut firmware, &mut hardware); - assert!(firmware.os.paused); + assert!(firmware.os.vm_runtime.paused); debugger.handle_command(DebugCommand::Resume, &mut firmware, &mut hardware); - assert!(!firmware.os.paused); + assert!(!firmware.os.vm_runtime.paused); debugger.handle_command(DebugCommand::StepFrame, &mut firmware, &mut hardware); - assert!(!firmware.os.paused); - assert!(firmware.os.debug_step_request); + assert!(!firmware.os.vm_runtime.paused); + assert!(firmware.os.vm_runtime.debug_step_request); } #[test] @@ -416,11 +416,11 @@ mod tests { let mut hardware = Hardware::new(); debugger.waiting_for_start = true; - firmware.os.paused = true; + firmware.os.vm_runtime.paused = true; debugger.handle_command(DebugCommand::Start, &mut firmware, &mut hardware); assert!(!debugger.waiting_for_start); - assert!(!firmware.os.paused); + assert!(!firmware.os.vm_runtime.paused); } } diff --git a/crates/host/prometeu-host-desktop-winit/src/overlay.rs b/crates/host/prometeu-host-desktop-winit/src/overlay.rs index 5028eb86..b54707bb 100644 --- a/crates/host/prometeu-host-desktop-winit/src/overlay.rs +++ b/crates/host/prometeu-host-desktop-winit/src/overlay.rs @@ -58,8 +58,8 @@ struct FrameCanvas<'a> { } pub(crate) fn capture_snapshot(stats: &HostStats, firmware: &Firmware) -> OverlaySnapshot { - let tel = firmware.os.atomic_telemetry.snapshot(); - let recent_logs = firmware.os.log_service.get_recent(10); + let tel = firmware.os.vm_runtime.atomic_telemetry.snapshot(); + let recent_logs = firmware.os.vm_runtime.log_service.get_recent(10); let violations_count = recent_logs.iter().filter(|e| e.tag >= 0xCA01 && e.tag <= 0xCA07).count(); @@ -75,7 +75,7 @@ pub(crate) fn capture_snapshot(stats: &HostStats, firmware: &Firmware) -> Overla }); } - if let Some(report) = firmware.os.last_crash_report.as_ref() { + if let Some(report) = firmware.os.vm_runtime.last_crash_report.as_ref() { footer.push(OverlayMetric { label: "CRASH", value: truncate_value(&report.summary(), 28), @@ -86,6 +86,7 @@ pub(crate) fn capture_snapshot(stats: &HostStats, firmware: &Firmware) -> Overla let cycles_ratio = ratio(tel.cycles_used, tel.cycles_budget); let heap_total_bytes = firmware .os + .vm_runtime .certifier .config .max_heap_bytes diff --git a/crates/host/prometeu-host-desktop-winit/src/runner.rs b/crates/host/prometeu-host-desktop-winit/src/runner.rs index 239b0c59..af4dd44e 100644 --- a/crates/host/prometeu-host-desktop-winit/src/runner.rs +++ b/crates/host/prometeu-host-desktop-winit/src/runner.rs @@ -153,7 +153,7 @@ impl HostRunner { let mut firmware = Firmware::new(cap_config); if let Some(root) = &fs_root { let backend = HostDirBackend::new(root); - firmware.os.mount_fs(Box::new(backend)); + firmware.os.vm_runtime.mount_fs(Box::new(backend)); } Self { @@ -204,7 +204,7 @@ impl HostRunner { } fn machine_running(&self) -> bool { - !self.firmware.os.paused && !self.debugger.waiting_for_start + !self.firmware.os.vm_runtime.paused && !self.debugger.waiting_for_start } } @@ -320,23 +320,26 @@ impl ApplicationHandler for HostRunner { fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { let was_debugger_connected = self.debugger.stream.is_some(); let was_waiting_for_start = self.debugger.waiting_for_start; - let was_paused = self.firmware.os.paused; + let was_paused = self.firmware.os.vm_runtime.paused; // 1. Process pending debug commands from the network. self.debugger.check_commands(&mut self.firmware, &mut self.hardware); // Sync inspection mode state. This is host-owned overlay/debugger control, // not a guest-visible debug ABI switch. - self.firmware.os.inspection_active = self.overlay_enabled || self.debugger.stream.is_some(); + self.firmware.os.vm_runtime.inspection_active = + self.overlay_enabled || self.debugger.stream.is_some(); // 2. Maintain filesystem connection if it was lost (e.g., directory removed). if let Some(root) = &self.fs_root { use prometeu_system::fs::FsState; - if matches!(self.firmware.os.fs_state, FsState::Unmounted | FsState::Error(_)) - && std::path::Path::new(root).exists() + if matches!( + self.firmware.os.vm_runtime.fs_state, + FsState::Unmounted | FsState::Error(_) + ) && std::path::Path::new(root).exists() { let backend = HostDirBackend::new(root); - self.firmware.os.mount_fs(Box::new(backend)); + self.firmware.os.vm_runtime.mount_fs(Box::new(backend)); } } @@ -361,13 +364,13 @@ impl ApplicationHandler for HostRunner { // Unless the debugger is waiting for a 'start' command, advance the system. if !self.debugger.waiting_for_start { self.firmware.tick(&self.input.signals, &mut self.hardware); - self.stats.record_host_cpu_time(self.firmware.os.last_frame_cpu_time_us); + self.stats.record_host_cpu_time(self.firmware.os.vm_runtime.last_frame_cpu_time_us); } // Sync pause state with audio. // We do this AFTER firmware.tick to avoid MasterPause/Resume commands // being cleared by the OS if a new logical frame starts in this tick. - let is_paused = self.firmware.os.paused || self.debugger.waiting_for_start; + let is_paused = self.firmware.os.vm_runtime.paused || self.debugger.waiting_for_start; if is_paused != self.last_paused_state { self.last_paused_state = is_paused; let cmd = @@ -382,11 +385,11 @@ impl ApplicationHandler for HostRunner { self.stats.record_frame(); } - self.presentation.note_published_frame(self.firmware.os.logical_frame_index); + self.presentation.note_published_frame(self.firmware.os.vm_runtime.logical_frame_index); if was_debugger_connected != self.debugger.stream.is_some() || was_waiting_for_start != self.debugger.waiting_for_start - || was_paused != self.firmware.os.paused + || was_paused != self.firmware.os.vm_runtime.paused { self.invalidate_host_surface(); } @@ -400,9 +403,9 @@ impl ApplicationHandler for HostRunner { // Synchronize system logs to the host console. let last_seq = self.log_sink.last_seq().unwrap_or(u64::MAX); let new_events = if last_seq == u64::MAX { - self.firmware.os.log_service.get_recent(4096) + self.firmware.os.vm_runtime.log_service.get_recent(4096) } else { - self.firmware.os.log_service.get_after(last_seq) + self.firmware.os.vm_runtime.log_service.get_after(last_seq) }; self.log_sink.process_events(new_events); @@ -812,7 +815,7 @@ mod tests { stream.write_all(b"{\"type\":\"pause\"}\n").expect("Should write pause"); std::thread::sleep(std::time::Duration::from_millis(50)); runner.debugger.check_commands(&mut runner.firmware, &mut runner.hardware); - assert!(runner.firmware.os.paused, "VM should be paused"); + assert!(runner.firmware.os.vm_runtime.paused, "VM should be paused"); } // 2. Disconnect (stream goes out of scope) @@ -820,7 +823,7 @@ mod tests { runner.debugger.check_commands(&mut runner.firmware, &mut runner.hardware); // 3. Check if unpaused - assert!(!runner.firmware.os.paused, "VM should have unpaused after disconnect"); + assert!(!runner.firmware.os.vm_runtime.paused, "VM should have unpaused after disconnect"); assert!(!runner.debugger.waiting_for_start, "VM should have left waiting_for_start state"); } } diff --git a/crates/host/prometeu-host-desktop-winit/src/stats.rs b/crates/host/prometeu-host-desktop-winit/src/stats.rs index 3cbda095..a14b4994 100644 --- a/crates/host/prometeu-host-desktop-winit/src/stats.rs +++ b/crates/host/prometeu-host-desktop-winit/src/stats.rs @@ -88,10 +88,11 @@ impl HostStats { self.current_fps, cpu_load_core, cpu_load_audio, - firmware.os.tick_index, - firmware.os.logical_frame_index, + firmware.os.vm_runtime.tick_index, + firmware.os.vm_runtime.logical_frame_index, firmware .os + .vm_runtime .atomic_telemetry .completed_logical_frames .load(std::sync::atomic::Ordering::Relaxed),