159 lines
6.4 KiB
Rust
159 lines
6.4 KiB
Rust
use crate::firmware::boot_target::BootTarget;
|
|
use crate::firmware::firmware_state::{FirmwareState, LoadCartridgeStep, ResetStep};
|
|
use crate::firmware::prometeu_context::PrometeuContext;
|
|
use crate::hardware::{HardwareBridge, InputSignals};
|
|
use crate::model::Cartridge;
|
|
use crate::prometeu_hub::PrometeuHub;
|
|
use crate::prometeu_os::PrometeuOS;
|
|
use crate::virtual_machine::VirtualMachine;
|
|
|
|
use crate::telemetry::CertificationConfig;
|
|
|
|
/// PROMETEU Firmware.
|
|
///
|
|
/// The central orchestrator of the console. The firmware acts as the
|
|
/// "Control Unit", managing the high-level state machine of the system.
|
|
///
|
|
/// It is responsible for transitioning between different modes of operation,
|
|
/// such as showing the splash screen, running the Hub (launcher), or
|
|
/// executing a game/app.
|
|
///
|
|
/// ### Execution Loop:
|
|
/// The firmware is designed to be ticked once per frame (60Hz). During each
|
|
/// tick, it:
|
|
/// 1. Updates peripherals with the latest input signals.
|
|
/// 2. Delegates the logic update to the current active state.
|
|
/// 3. Handles state transitions (e.g., from Loading to Playing).
|
|
pub struct Firmware {
|
|
/// The execution engine (PVM) for user applications.
|
|
pub vm: VirtualMachine,
|
|
/// The underlying OS services (Syscalls, Filesystem, Telemetry).
|
|
pub os: PrometeuOS,
|
|
/// The internal state of the system launcher (Hub).
|
|
pub hub: PrometeuHub,
|
|
/// The current operational state (e.g., Reset, SplashScreen, GameRunning).
|
|
pub state: FirmwareState,
|
|
/// The desired application to run after boot (Hub or specific Cartridge).
|
|
pub boot_target: BootTarget,
|
|
/// State-machine lifecycle tracker.
|
|
state_initialized: bool,
|
|
}
|
|
|
|
impl Firmware {
|
|
/// Initializes the firmware in the `Reset` state.
|
|
pub fn new(cap_config: Option<CertificationConfig>) -> Self {
|
|
Self {
|
|
vm: VirtualMachine::default(),
|
|
os: PrometeuOS::new(cap_config),
|
|
hub: PrometeuHub::new(),
|
|
state: FirmwareState::Reset(ResetStep),
|
|
boot_target: BootTarget::Hub,
|
|
state_initialized: false,
|
|
}
|
|
}
|
|
|
|
/// The main entry point for the Host to advance the system logic.
|
|
///
|
|
/// This method is called exactly once per Host frame (60Hz).
|
|
/// It updates peripheral signals and delegates the logic to the current state.
|
|
pub fn tick(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) {
|
|
// 0. Process asset commits at the beginning of the frame boundary.
|
|
hw.assets_mut().apply_commits();
|
|
|
|
// 1. Update the peripheral state using the latest signals from the Host.
|
|
// This ensures input is consistent throughout the entire update.
|
|
hw.pad_mut().begin_frame(signals);
|
|
hw.touch_mut().begin_frame(signals);
|
|
|
|
// 2. State machine lifecycle management.
|
|
if !self.state_initialized {
|
|
self.on_enter(signals, hw);
|
|
self.state_initialized = true;
|
|
}
|
|
|
|
// 3. Update the current state and check for transitions.
|
|
if let Some(next_state) = self.on_update(signals, hw) {
|
|
self.change_state(next_state, signals, hw);
|
|
}
|
|
}
|
|
|
|
/// Transitions the system to a new state, handling lifecycle hooks.
|
|
pub fn change_state(&mut self, new_state: FirmwareState, signals: &InputSignals, hw: &mut dyn HardwareBridge) {
|
|
self.on_exit(signals, hw);
|
|
self.state = new_state;
|
|
self.state_initialized = false;
|
|
|
|
// Enter the new state immediately to avoid "empty" frames during transitions.
|
|
self.on_enter(signals, hw);
|
|
self.state_initialized = true;
|
|
}
|
|
|
|
/// Dispatches the `on_enter` event to the current state implementation.
|
|
fn on_enter(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) {
|
|
let mut req = PrometeuContext {
|
|
vm: &mut self.vm,
|
|
os: &mut self.os,
|
|
hub: &mut self.hub,
|
|
boot_target: &self.boot_target,
|
|
signals,
|
|
hw,
|
|
};
|
|
match &mut self.state {
|
|
FirmwareState::Reset(s) => s.on_enter(&mut req),
|
|
FirmwareState::SplashScreen(s) => s.on_enter(&mut req),
|
|
FirmwareState::LaunchHub(s) => s.on_enter(&mut req),
|
|
FirmwareState::HubHome(s) => s.on_enter(&mut req),
|
|
FirmwareState::LoadCartridge(s) => s.on_enter(&mut req),
|
|
FirmwareState::GameRunning(s) => s.on_enter(&mut req),
|
|
FirmwareState::AppCrashes(s) => s.on_enter(&mut req),
|
|
}
|
|
}
|
|
|
|
/// Dispatches the `on_update` event to the current state implementation.
|
|
/// Returns an optional `FirmwareState` if a transition is requested.
|
|
fn on_update(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) -> Option<FirmwareState> {
|
|
let mut req = PrometeuContext {
|
|
vm: &mut self.vm,
|
|
os: &mut self.os,
|
|
hub: &mut self.hub,
|
|
boot_target: &self.boot_target,
|
|
signals,
|
|
hw,
|
|
};
|
|
match &mut self.state {
|
|
FirmwareState::Reset(s) => s.on_update(&mut req),
|
|
FirmwareState::SplashScreen(s) => s.on_update(&mut req),
|
|
FirmwareState::LaunchHub(s) => s.on_update(&mut req),
|
|
FirmwareState::HubHome(s) => s.on_update(&mut req),
|
|
FirmwareState::LoadCartridge(s) => s.on_update(&mut req),
|
|
FirmwareState::GameRunning(s) => s.on_update(&mut req),
|
|
FirmwareState::AppCrashes(s) => s.on_update(&mut req),
|
|
}
|
|
}
|
|
|
|
/// Dispatches the `on_exit` event to the current state implementation.
|
|
fn on_exit(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) {
|
|
let mut req = PrometeuContext {
|
|
vm: &mut self.vm,
|
|
os: &mut self.os,
|
|
hub: &mut self.hub,
|
|
boot_target: &self.boot_target,
|
|
signals,
|
|
hw,
|
|
};
|
|
match &mut self.state {
|
|
FirmwareState::Reset(s) => s.on_exit(&mut req),
|
|
FirmwareState::SplashScreen(s) => s.on_exit(&mut req),
|
|
FirmwareState::LaunchHub(s) => s.on_exit(&mut req),
|
|
FirmwareState::HubHome(s) => s.on_exit(&mut req),
|
|
FirmwareState::LoadCartridge(s) => s.on_exit(&mut req),
|
|
FirmwareState::GameRunning(s) => s.on_exit(&mut req),
|
|
FirmwareState::AppCrashes(s) => s.on_exit(&mut req),
|
|
}
|
|
}
|
|
|
|
pub fn load_cartridge(&mut self, cartridge: Cartridge) {
|
|
self.state = FirmwareState::LoadCartridge(LoadCartridgeStep { cartridge });
|
|
self.state_initialized = false;
|
|
}
|
|
} |