implements PLN-0105

This commit is contained in:
bQUARKz 2026-06-15 05:59:06 +01:00
parent d0a2682366
commit 1d6b799960
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
12 changed files with 73 additions and 67 deletions

View File

@ -3,7 +3,7 @@ use crate::firmware::firmware_state::{FirmwareState, LoadCartridgeStep, ResetSte
use crate::firmware::prometeu_context::PrometeuContext;
use prometeu_hal::cartridge::Cartridge;
use prometeu_hal::telemetry::CertificationConfig;
use prometeu_hal::{HardwareBridge, InputSignals};
use prometeu_hal::{InputSignals, RuntimePlatform};
use prometeu_system::{PrometeuHub, SystemOS};
use prometeu_vm::VirtualMachine;
@ -54,24 +54,24 @@ impl Firmware {
///
/// 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) {
pub fn tick(&mut self, signals: &InputSignals, platform: &mut dyn RuntimePlatform) {
// 0. Process asset commits at the beginning of the frame boundary.
hw.assets_mut().apply_commits();
platform.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);
platform.input_mut().pad_mut().begin_frame(signals);
platform.input_mut().touch_mut().begin_frame(signals);
// 2. State machine lifecycle management.
if !self.state_initialized {
self.on_enter(signals, hw);
self.on_enter(signals, platform);
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);
if let Some(next_state) = self.on_update(signals, platform) {
self.change_state(next_state, signals, platform);
}
}
@ -80,26 +80,26 @@ impl Firmware {
&mut self,
new_state: FirmwareState,
signals: &InputSignals,
hw: &mut dyn HardwareBridge,
platform: &mut dyn RuntimePlatform,
) {
self.on_exit(signals, hw);
self.on_exit(signals, platform);
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.on_enter(signals, platform);
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) {
fn on_enter(&mut self, signals: &InputSignals, platform: &mut dyn RuntimePlatform) {
let mut req = PrometeuContext {
vm: &mut self.vm,
os: &mut self.os,
hub: &mut self.hub,
boot_target: &self.boot_target,
signals,
hw,
platform,
};
match &mut self.state {
FirmwareState::Reset(s) => s.on_enter(&mut req),
@ -118,7 +118,7 @@ impl Firmware {
fn on_update(
&mut self,
signals: &InputSignals,
hw: &mut dyn HardwareBridge,
platform: &mut dyn RuntimePlatform,
) -> Option<FirmwareState> {
let mut req = PrometeuContext {
vm: &mut self.vm,
@ -126,7 +126,7 @@ impl Firmware {
hub: &mut self.hub,
boot_target: &self.boot_target,
signals,
hw,
platform,
};
match &mut self.state {
FirmwareState::Reset(s) => s.on_update(&mut req),
@ -141,14 +141,14 @@ impl Firmware {
}
/// Dispatches the `on_exit` event to the current state implementation.
fn on_exit(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) {
fn on_exit(&mut self, signals: &InputSignals, platform: &mut dyn RuntimePlatform) {
let mut req = PrometeuContext {
vm: &mut self.vm,
os: &mut self.os,
hub: &mut self.hub,
boot_target: &self.boot_target,
signals,
hw,
platform,
};
match &mut self.state {
FirmwareState::Reset(s) => s.on_exit(&mut req),

View File

@ -2,10 +2,7 @@ use crate::firmware::firmware_state::{FirmwareState, LaunchHubStep};
use crate::firmware::prometeu_context::PrometeuContext;
use prometeu_hal::color::Color;
use prometeu_hal::log::{LogLevel, LogSource};
use prometeu_hal::{
FrameId, GfxUiCommand, LegacyHardwareRenderSubmissionSink, RenderSubmission,
RenderSubmissionSink, ShellUiFramePacket,
};
use prometeu_hal::{FrameId, GfxUiCommand, RenderSubmission, ShellUiFramePacket};
use prometeu_system::CrashReport;
#[derive(Debug, Clone)]
@ -24,15 +21,16 @@ impl AppCrashesStep {
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
// Update peripherals for input on the crash screen
ctx.hw.pad_mut().begin_frame(ctx.signals);
ctx.platform.input_mut().pad_mut().begin_frame(ctx.signals);
let packet = ShellUiFramePacket::new(vec![GfxUiCommand::Clear { color: Color::RED }]);
LegacyHardwareRenderSubmissionSink::new(ctx.hw)
ctx.platform
.render_submission_sink()
.submit_render_submission(RenderSubmission::shell_ui(FrameId::ZERO, packet))
.expect("firmware crash render submission should publish");
// If START is pressed, return to the Hub
if ctx.hw.pad().start().down {
if ctx.platform.input().pad().start().down {
return Some(FirmwareState::LaunchHub(LaunchHubStep));
}

View File

@ -36,7 +36,7 @@ impl GameRunningStep {
return Some(FirmwareState::AppCrashes(AppCrashesStep { report }));
}
let result = ctx.os.vm().tick(ctx.vm, ctx.signals, ctx.hw);
let result = ctx.os.vm().tick(ctx.vm, ctx.signals, ctx.platform);
if let Some(report) = result {
let _ = ctx.os.lifecycle().crash_task(self.task_id, Some(&report));

View File

@ -16,7 +16,7 @@ impl HubHomeStep {
}
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
let outcome = ctx.hub.update_shell_profile(ctx.os, ctx.vm, ctx.signals, ctx.hw);
let outcome = ctx.hub.update_shell_profile(ctx.os, ctx.vm, ctx.signals, ctx.platform);
if let Some(report) = outcome.crash {
return Some(FirmwareState::AppCrashes(AppCrashesStep { report }));
}

View File

@ -30,7 +30,7 @@ impl LoadCartridgeStep {
);
// Initialize Asset Manager
ctx.hw.assets_mut().initialize_for_cartridge(
ctx.platform.assets_mut().initialize_for_cartridge(
self.cartridge.asset_table.clone(),
self.cartridge.preload.clone(),
self.cartridge.assets.clone(),

View File

@ -59,7 +59,7 @@ impl ShellRunningStep {
}
}
let outcome = ctx.hub.update_shell_profile(ctx.os, ctx.vm, ctx.signals, ctx.hw);
let outcome = ctx.hub.update_shell_profile(ctx.os, ctx.vm, ctx.signals, ctx.platform);
if let Some(report) = outcome.crash {
let _ = ctx.os.lifecycle().crash_task(self.task_id, Some(&report));

View File

@ -3,10 +3,7 @@ use crate::firmware::prometeu_context::PrometeuContext;
use prometeu_hal::color::Color;
use prometeu_hal::log::{LogLevel, LogSource};
use prometeu_hal::primitives::Rect;
use prometeu_hal::{
FrameId, GfxUiCommand, LegacyHardwareRenderSubmissionSink, RenderSubmission,
RenderSubmissionSink, ShellUiFramePacket,
};
use prometeu_hal::{FrameId, GfxUiCommand, RenderSubmission, ShellUiFramePacket};
#[derive(Debug, Clone)]
pub struct SplashScreenStep {
@ -17,7 +14,7 @@ impl SplashScreenStep {
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
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);
// ctx.platform.audio_mut().play(0, 0, 0, 255, 127, 1.0, 0, LoopMode::Off);
}
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
@ -25,10 +22,10 @@ impl SplashScreenStep {
const TOTAL_DURATION: u32 = 240; // 4 seconds total (updated from 2s based on total_duration logic)
// Update peripherals for input
ctx.hw.pad_mut().begin_frame(ctx.signals);
ctx.platform.input_mut().pad_mut().begin_frame(ctx.signals);
// Draw expanding square
let (sw, sh) = ctx.hw.gfx().size();
let (sw, sh) = ctx.platform.display_size();
let max_size = (sw.min(sh) as i32 / 2).max(1);
let current_size = if self.frame < ANIMATION_DURATION {
@ -47,13 +44,14 @@ impl SplashScreenStep {
color: Color::WHITE,
},
]);
LegacyHardwareRenderSubmissionSink::new(ctx.hw)
ctx.platform
.render_submission_sink()
.submit_render_submission(RenderSubmission::shell_ui(FrameId::ZERO, packet))
.expect("firmware splash render submission should publish");
// Transition logic
// If any button is pressed at any time after the animation ends
if self.frame >= ANIMATION_DURATION && ctx.hw.pad().any() {
if self.frame >= ANIMATION_DURATION && ctx.platform.input().pad().any() {
return Some(FirmwareState::LaunchHub(LaunchHubStep));
}

View File

@ -1,5 +1,5 @@
use crate::firmware::boot_target::BootTarget;
use prometeu_hal::{HardwareBridge, InputSignals};
use prometeu_hal::{InputSignals, RuntimePlatform};
use prometeu_system::{PrometeuHub, SystemOS};
use prometeu_vm::VirtualMachine;
@ -9,5 +9,5 @@ pub struct PrometeuContext<'a> {
pub hub: &'a mut PrometeuHub,
pub boot_target: &'a BootTarget,
pub signals: &'a InputSignals,
pub hw: &'a mut dyn HardwareBridge,
pub platform: &'a mut dyn RuntimePlatform,
}

View File

@ -215,6 +215,10 @@ impl TelemetryPlatform for NoopTelemetryPlatform {}
static LEGACY_NOOP_TELEMETRY: NoopTelemetryPlatform = NoopTelemetryPlatform;
pub trait RuntimePlatform {
fn display_size(&self) -> (usize, usize) {
(480, 270)
}
fn render_submission_sink(&mut self) -> &mut dyn RenderSubmissionSink;
fn render_backend(&mut self) -> &mut dyn RenderBackend;
fn game2d_frame_composer(&mut self) -> &mut dyn Game2DFrameComposer;

View File

@ -3,7 +3,7 @@ use crate::os::SystemOS;
use prometeu_hal::app_mode::AppMode;
use prometeu_hal::cartridge::Cartridge;
use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame};
use prometeu_hal::{HardwareBridge, InputSignals};
use prometeu_hal::{HardwareBridge, InputSignals, RuntimePlatform};
use prometeu_vm::VirtualMachine;
use std::sync::atomic::Ordering;
@ -25,7 +25,7 @@ impl<'a> VmFacade<'a> {
&mut self,
vm: &mut VirtualMachine,
signals: &InputSignals,
hw: &mut dyn HardwareBridge,
platform: &mut dyn RuntimePlatform,
) -> Option<CrashReport> {
self.os.vm_runtime.tick(
&mut self.os.log_service,
@ -36,7 +36,7 @@ impl<'a> VmFacade<'a> {
&mut self.os.next_handle,
vm,
signals,
hw,
platform,
)
}

View File

@ -2,8 +2,8 @@ use crate::{CrashReport, SystemOS};
use prometeu_hal::color::Color;
use prometeu_hal::primitives::Rect;
use prometeu_hal::{
FrameId, GfxUiCommand, HardwareBridge, InputSignals, LegacyHardwareRenderSubmissionSink,
RenderSubmission, RenderSubmissionSink, ShellUiFramePacket,
FrameId, GfxUiCommand, InputPlatform, InputSignals, RenderSubmission, RuntimePlatform,
ShellUiFramePacket,
};
use prometeu_vm::VirtualMachine;
@ -101,31 +101,35 @@ impl PrometeuHub {
pub fn gui_update(
&mut self,
os: &mut SystemOS,
hw: &mut dyn HardwareBridge,
platform: &mut dyn RuntimePlatform,
) -> Option<SystemProfileAction> {
let in_shell = os.windows().focused_window().is_some();
if !in_shell {
if !self.home_input_armed {
self.home_input_armed = !activation_input_down(hw);
self.home_input_armed = !activation_input_down(platform.input());
return None;
}
} else {
self.home_input_armed = false;
}
if let Some(action) =
action_for_click(in_shell, hw.touch().x(), hw.touch().y(), hw.touch().f().pressed)
{
let input = platform.input();
if let Some(action) = action_for_click(
in_shell,
input.touch().x(),
input.touch().y(),
input.touch().f().pressed,
) {
return Some(action);
}
None
}
pub fn render(&mut self, os: &mut SystemOS, hw: &mut dyn HardwareBridge) {
let pointer_x = hw.touch().x();
let pointer_y = hw.touch().y();
pub fn render(&mut self, os: &mut SystemOS, platform: &mut dyn RuntimePlatform) {
let pointer_x = platform.input().touch().x();
let pointer_y = platform.input().touch().y();
let packet = if os.windows().window_count() == 0 {
render_home_packet(pointer_x, pointer_y)
@ -143,7 +147,8 @@ impl PrometeuHub {
}
ShellUiFramePacket::new(commands)
};
LegacyHardwareRenderSubmissionSink::new(hw)
platform
.render_submission_sink()
.submit_render_submission(RenderSubmission::shell_ui(FrameId::ZERO, packet))
.expect("hub render submission should publish");
}
@ -153,20 +158,20 @@ impl PrometeuHub {
os: &mut SystemOS,
vm: &mut VirtualMachine,
signals: &InputSignals,
hw: &mut dyn HardwareBridge,
platform: &mut dyn RuntimePlatform,
) -> SystemProfileUpdate {
let mut crash = None;
let mut action = self.gui_update(os, hw);
let mut action = self.gui_update(os, platform);
if os.windows().focused_window().is_some() {
if hw.pad().start().down {
if platform.input().pad().start().down {
action = Some(SystemProfileAction::CloseShell);
} else if action != Some(SystemProfileAction::CloseShell) {
crash = os.vm().tick(vm, signals, hw);
crash = os.vm().tick(vm, signals, platform);
}
}
self.render(os, hw);
self.render(os, platform);
SystemProfileUpdate { crash, action }
}
@ -191,8 +196,11 @@ fn rect_contains(rect: Rect, x: i32, y: i32) -> bool {
x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h
}
fn activation_input_down(hw: &dyn HardwareBridge) -> bool {
hw.touch().f().down || hw.pad().a().down || hw.pad().b().down || hw.pad().start().down
fn activation_input_down(input: &dyn InputPlatform) -> bool {
input.touch().f().down
|| input.pad().a().down
|| input.pad().b().down
|| input.pad().start().down
}
fn render_home_packet(pointer_x: i32, pointer_y: i32) -> ShellUiFramePacket {

View File

@ -135,7 +135,7 @@ impl VirtualMachineRuntime {
next_handle: &mut u32,
vm: &mut VirtualMachine,
signals: &InputSignals,
hw: &mut dyn HardwareBridge,
platform: &mut dyn RuntimePlatform,
) -> Option<CrashReport> {
let start = Instant::now();
self.tick_index += 1;
@ -145,8 +145,6 @@ impl VirtualMachineRuntime {
}
self.update_fs(log_service, fs, fs_state);
let mut platform = LegacyHardwareRuntimePlatform::new(hw);
if !self.logical_frame_active {
if RenderManager::policy_for_app_mode(self.current_cartridge_app_mode)
.uses_frame_scheduler()
@ -157,7 +155,7 @@ impl VirtualMachineRuntime {
}
self.logical_frame_active = true;
self.logical_frame_remaining_cycles = Self::CYCLES_PER_LOGICAL_FRAME;
self.begin_logical_frame(signals, &mut platform);
self.begin_logical_frame(signals, platform);
if self.needs_prepare_entry_call || vm.call_stack_is_empty() {
vm.prepare_boot_call();
@ -185,7 +183,7 @@ impl VirtualMachineRuntime {
if budget > 0 {
let run_result = {
let mut ctx = HostContext::new_platform(Some(&mut platform));
let mut ctx = HostContext::new_platform(Some(platform));
let mut host = VmRuntimeHost {
runtime: self,
log_service,
@ -311,7 +309,7 @@ impl VirtualMachineRuntime {
// 1. Snapshot full telemetry at logical frame end
let (glyph_bank, sound_bank, scene_bank) =
Self::bank_telemetry_summary(&platform);
Self::bank_telemetry_summary(platform);
self.atomic_telemetry
.glyph_slots_used
.store(glyph_bank.used_slots as u32, Ordering::Relaxed);
@ -402,7 +400,7 @@ impl VirtualMachineRuntime {
// 2. High-frequency telemetry update (only if inspection is active)
if self.inspection_active {
let (glyph_bank, sound_bank, scene_bank) = Self::bank_telemetry_summary(&platform);
let (glyph_bank, sound_bank, scene_bank) = Self::bank_telemetry_summary(platform);
self.atomic_telemetry
.glyph_slots_used
.store(glyph_bank.used_slots as u32, Ordering::Relaxed);