clean up and organize

This commit is contained in:
bQUARKz 2026-01-16 13:05:42 +00:00
parent 8fb0cb91e6
commit 38f07c0b26
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
19 changed files with 162 additions and 202 deletions

View File

@ -1,6 +1,6 @@
use crate::firmware::firmware_state::FirmwareState; use crate::firmware::firmware_state::FirmwareState;
use crate::hardware::{HardwareBridge, InputSignals};
use crate::model::{AppMode, Cartridge, Color}; use crate::model::{AppMode, Cartridge, Color};
use crate::peripherals::InputSignals;
use crate::prometeu_hub::PrometeuHub; use crate::prometeu_hub::PrometeuHub;
use crate::prometeu_os::PrometeuOS; use crate::prometeu_os::PrometeuOS;
@ -19,7 +19,7 @@ impl Firmware {
} }
} }
pub fn step_frame(&mut self, signals: &InputSignals) { pub fn step_frame(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) {
match &mut self.state { match &mut self.state {
FirmwareState::Reset => { FirmwareState::Reset => {
self.os.reset(); self.os.reset();
@ -34,33 +34,34 @@ impl Firmware {
self.state = FirmwareState::HubHome; self.state = FirmwareState::HubHome;
} }
FirmwareState::HubHome => { FirmwareState::HubHome => {
// UI do Hub hw.gfx_mut().clear(Color::INDIGO);
self.hub.update(&mut self.os); self.hub.gui_update(&mut self.os);
hw.gfx_mut().present();
} }
FirmwareState::PosRunGame(_cart) => { FirmwareState::PosRunGame(_cart) => {
if let Some(error) = self.os.step_frame(signals) { if let Some(error) = self.os.step_frame(signals, hw) {
self.state = FirmwareState::PosCrashScreen(error); self.state = FirmwareState::PosCrashScreen(error);
} }
} }
FirmwareState::PosRunSystem(_cart) => { FirmwareState::PosRunSystem(_cart) => {
// System Apps rodam "dentro" do Hub/Window System // System Apps rodam "dentro" do Hub/Window System
if let Some(error) = self.os.step_frame(signals) { if let Some(error) = self.os.step_frame(signals, hw) {
self.state = FirmwareState::PosCrashScreen(error); self.state = FirmwareState::PosCrashScreen(error);
} }
// TODO: Compor com a UI do Hub // TODO: Compor com a UI do Hub
} }
FirmwareState::PosCrashScreen(_error) => { FirmwareState::PosCrashScreen(_error) => {
// Atualiza periféricos para input na tela de crash // Atualiza periféricos para input na tela de crash
self.os.hardware_mut().pad_mut().begin_frame(signals); hw.pad_mut().begin_frame(signals);
// Tela de erro: fundo vermelho, texto branco // Tela de erro: fundo vermelho, texto branco
self.os.hardware_mut().gfx_mut().clear(Color::RED); hw.gfx_mut().clear(Color::RED);
// Por enquanto apenas logamos ou mostramos algo simples // Por enquanto apenas logamos ou mostramos algo simples
// No futuro, usar draw_text // No futuro, usar draw_text
self.os.hardware_mut().gfx_mut().present(); hw.gfx_mut().present();
// Se apertar START, volta pro Hub // Se apertar START, volta pro Hub
if self.os.hardware().pad().start.down { if hw.pad().start.down {
self.state = FirmwareState::LaunchHubHome; self.state = FirmwareState::LaunchHubHome;
} }
} }

View File

@ -1,57 +0,0 @@
use crate::peripherals::{Audio, Gfx, InputSignals, Pad, Touch, HardwareBridge};
use crate::firmware::{Firmware};
pub struct Hardware {
pub firmware: Firmware,
pub gfx: Gfx,
pub audio: Audio,
pub pad: Pad,
pub touch: Touch,
}
impl HardwareBridge for Hardware {
fn gfx(&self) -> &Gfx { &self.gfx }
fn gfx_mut(&mut self) -> &mut Gfx { &mut self.gfx }
fn audio(&self) -> &Audio { &self.audio }
fn audio_mut(&mut self) -> &mut Audio { &mut self.audio }
fn pad(&self) -> &Pad { &self.pad }
fn pad_mut(&mut self) -> &mut Pad { &mut self.pad }
fn touch(&self) -> &Touch { &self.touch }
fn touch_mut(&mut self) -> &mut Touch { &mut self.touch }
}
impl Hardware {
pub const W: usize = 320;
pub const H: usize = 180;
pub fn new() -> Box<Self> {
let mut hw = Box::new(Self {
firmware: Firmware::new(),
gfx: Gfx::new(Self::W, Self::H),
audio: Audio::new(),
pad: Pad::default(),
touch: Touch::default(),
});
// Configura a referência circular (através do trait HardwareBridge)
let hw_ptr = hw.as_mut() as *mut dyn HardwareBridge;
hw.firmware.os.hw_ptr = Some(hw_ptr);
hw
}
/// Atalhos para o Runner (desktop) não quebrar tanto,
/// delegando para o PrometeuOS onde os contadores agora vivem.
pub fn tick_index(&self) -> u64 { self.firmware.os.tick_index }
pub fn logical_frame_index(&self) -> u64 { self.firmware.os.logical_frame_index }
pub fn last_frame_cpu_time_us(&self) -> u64 { self.firmware.os.last_frame_cpu_time_us }
/// "Contrato de frame" do PROMETEU.
pub fn step_frame(&mut self, signals: &InputSignals) {
// O Host chama o hardware, que delega para o Firmware/OS.
self.firmware.step_frame(signals);
}
}

View File

@ -0,0 +1,36 @@
use crate::hardware::{Audio, Gfx, HardwareBridge, Pad, Touch};
pub struct Hardware {
pub gfx: Gfx,
pub audio: Audio,
pub pad: Pad,
pub touch: Touch,
}
impl HardwareBridge for Hardware {
fn gfx(&self) -> &Gfx { &self.gfx }
fn gfx_mut(&mut self) -> &mut Gfx { &mut self.gfx }
fn audio(&self) -> &Audio { &self.audio }
fn audio_mut(&mut self) -> &mut Audio { &mut self.audio }
fn pad(&self) -> &Pad { &self.pad }
fn pad_mut(&mut self) -> &mut Pad { &mut self.pad }
fn touch(&self) -> &Touch { &self.touch }
fn touch_mut(&mut self) -> &mut Touch { &mut self.touch }
}
impl Hardware {
pub const W: usize = 320;
pub const H: usize = 180;
pub fn new() -> Self {
Self {
gfx: Gfx::new(Self::W, Self::H),
audio: Audio::new(),
pad: Pad::default(),
touch: Touch::default(),
}
}
}

View File

@ -3,16 +3,15 @@ mod pad;
mod touch; mod touch;
mod input_signal; mod input_signal;
mod audio; mod audio;
pub mod hardware;
pub use gfx::Gfx; pub use gfx::Gfx;
pub use gfx::BlendMode; pub use gfx::BlendMode;
pub use input_signal::InputSignals; pub use input_signal::InputSignals;
pub use pad::Pad; pub use pad::Pad;
pub use touch::Touch; pub use touch::Touch;
pub use audio::{Audio, Channel, AudioCommand, LoopMode, MAX_CHANNELS, OUTPUT_SAMPLE_RATE}; pub use audio::{Audio, AudioCommand, Channel, LoopMode, MAX_CHANNELS, OUTPUT_SAMPLE_RATE};
/// Interface de acesso ao hardware para o Firmware/OS.
/// Centraliza as chamadas para periféricos sem expor a estrutura interna do HardwareBridge.
pub trait HardwareBridge { pub trait HardwareBridge {
fn gfx(&self) -> &Gfx; fn gfx(&self) -> &Gfx;
fn gfx_mut(&mut self) -> &mut Gfx; fn gfx_mut(&mut self) -> &mut Gfx;

View File

@ -1,5 +1,5 @@
use crate::model::Button; use crate::model::Button;
use crate::peripherals::input_signal::InputSignals; use crate::hardware::input_signal::InputSignals;
#[derive(Default, Clone, Copy, Debug)] #[derive(Default, Clone, Copy, Debug)]
pub struct Pad { pub struct Pad {

View File

@ -1,5 +1,5 @@
use crate::model::Button; use crate::model::Button;
use crate::peripherals::input_signal::InputSignals; use crate::hardware::input_signal::InputSignals;
#[derive(Default, Clone, Copy, Debug)] #[derive(Default, Clone, Copy, Debug)]
pub struct Touch { pub struct Touch {

View File

@ -1,9 +1,8 @@
pub mod hardware; pub mod hardware;
pub mod peripherals;
pub mod vm; pub mod vm;
mod model; mod model;
pub mod firmware; pub mod firmware;
mod prometeu_os; mod prometeu_os;
mod prometeu_hub; mod prometeu_hub;
pub use hardware::Hardware; pub use hardware::hardware::Hardware;

View File

@ -31,10 +31,7 @@ impl PrometeuHub {
// Inicializa o Window System e lista apps // Inicializa o Window System e lista apps
} }
pub fn update(&mut self, os: &mut PrometeuOS) { pub fn gui_update(&mut self, _os: &mut PrometeuOS) {
// Renderiza a UI básica do Hub // Atualiza a UI do Window System
os.hardware_mut().gfx_mut().clear(self.window_system.theme_color);
// TODO: Desenhar lista de apps
os.hardware_mut().gfx_mut().present();
} }
} }

View File

@ -1,5 +1,4 @@
mod prometeu_os; mod prometeu_os;
mod native_interface;
pub use prometeu_os::PrometeuOS; pub use prometeu_os::PrometeuOS;
pub use native_interface::NativeInterface; pub use crate::vm::native_interface::NativeInterface;

View File

@ -1,73 +0,0 @@
use crate::prometeu_os::prometeu_os::PrometeuOS;
use crate::vm::{Value, VirtualMachine};
pub trait NativeInterface {
fn syscall(&mut self, id: u32, vm: &mut VirtualMachine) -> Result<u64, String>;
}
impl NativeInterface for PrometeuOS {
fn syscall(&mut self, id: u32, vm: &mut VirtualMachine) -> Result<u64, String> {
match id {
// system.has_cart() -> bool
0x0001 => {
vm.push(Value::Boolean(self.current_cartridge.is_some()));
Ok(10)
}
// system.run_cart()
0x0002 => {
if let Some(cart) = self.current_cartridge.as_ref().cloned() {
self.load_cartridge(&cart);
} else {
return Err("No cartridge inserted".into());
}
Ok(100)
}
// gfx.clear(color_index)
0x1001 => {
let color_idx = vm.pop_integer()? as usize;
let color = self.get_color(color_idx);
self.hardware_mut().gfx_mut().clear(color);
Ok(100)
}
// gfx.draw_rect(x, y, w, h, color_index)
0x1002 => {
let color_idx = vm.pop_integer()? as usize;
let h = vm.pop_integer()? as i32;
let w = vm.pop_integer()? as i32;
let y = vm.pop_integer()? as i32;
let x = vm.pop_integer()? as i32;
let color = self.get_color(color_idx);
self.hardware_mut().gfx_mut().fill_rect(x, y, w, h, color);
Ok(200)
}
// input.get_pad(button_id) -> bool
0x2001 => {
let button_id = vm.pop_integer()? as u32;
let is_down = self.is_button_down(button_id);
vm.push(Value::Boolean(is_down));
Ok(50)
}
// audio.play_sample(sample_id, voice_id, volume, pan, pitch)
0x3001 => {
let pitch = vm.pop_number()?;
let pan = vm.pop_integer()? as u8;
let volume = vm.pop_integer()? as u8;
let voice_id = vm.pop_integer()? as usize;
let sample_id = vm.pop_integer()? as u32;
let sample = match sample_id {
0 => self.sample_square.clone(),
1 => self.sample_kick.clone(),
2 => self.sample_snare.clone(),
_ => None,
};
if let Some(s) = sample {
self.hardware_mut().audio_mut().play(s, voice_id, volume, pan, pitch, 0, crate::peripherals::LoopMode::Off);
}
Ok(300)
}
_ => Err(format!("Unknown syscall: 0x{:08X}", id)),
}
}
}

View File

@ -1,7 +1,8 @@
use crate::model::{Cartridge, Color, Sample}; use crate::model::{Cartridge, Color, Sample};
use crate::peripherals::{HardwareBridge, InputSignals}; use crate::hardware::{HardwareBridge, InputSignals};
use crate::vm::VirtualMachine; use crate::vm::{Value, VirtualMachine};
use std::sync::Arc; use std::sync::Arc;
use crate::prometeu_os::NativeInterface;
/// PrometeuOS (POS): O firmware/base do sistema. /// PrometeuOS (POS): O firmware/base do sistema.
/// Autoridade máxima do boot, periféricos, execução da PVM e tratamento de falhas. /// Autoridade máxima do boot, periféricos, execução da PVM e tratamento de falhas.
@ -11,8 +12,6 @@ pub struct PrometeuOS {
pub logical_frame_index: u64, pub logical_frame_index: u64,
pub logical_frame_open: bool, pub logical_frame_open: bool,
pub last_frame_cpu_time_us: u64, pub last_frame_cpu_time_us: u64,
pub hw_ptr: Option<*mut dyn HardwareBridge>,
pub current_cartridge: Option<Cartridge>,
// Assets de exemplo (mantidos para compatibilidade com syscalls de áudio v0) // Assets de exemplo (mantidos para compatibilidade com syscalls de áudio v0)
pub sample_square: Option<Arc<Sample>>, pub sample_square: Option<Arc<Sample>>,
@ -26,12 +25,10 @@ impl PrometeuOS {
pub fn new() -> Self { pub fn new() -> Self {
let mut os = Self { let mut os = Self {
vm: VirtualMachine::default(), vm: VirtualMachine::default(),
hw_ptr: None,
tick_index: 0, tick_index: 0,
logical_frame_index: 0, logical_frame_index: 0,
logical_frame_open: false, logical_frame_open: false,
last_frame_cpu_time_us: 0, last_frame_cpu_time_us: 0,
current_cartridge: None,
sample_square: None, sample_square: None,
sample_kick: None, sample_kick: None,
sample_snare: None, sample_snare: None,
@ -43,31 +40,17 @@ impl PrometeuOS {
os os
} }
/// Helper para acessar o hardware de forma segura (dentro do contexto do firmware).
#[inline]
pub fn hardware(&self) -> &dyn HardwareBridge {
unsafe { &*self.hw_ptr.expect("Hardware pointer not initialized") }
}
/// Helper para acessar o hardware de forma mutável.
#[inline]
pub fn hardware_mut(&mut self) -> &mut dyn HardwareBridge {
unsafe { &mut *self.hw_ptr.expect("Hardware pointer not initialized") }
}
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.vm = VirtualMachine::default(); self.vm = VirtualMachine::default();
self.tick_index = 0; self.tick_index = 0;
self.logical_frame_index = 0; self.logical_frame_index = 0;
self.logical_frame_open = false; self.logical_frame_open = false;
self.last_frame_cpu_time_us = 0; self.last_frame_cpu_time_us = 0;
self.current_cartridge = None;
} }
/// Carrega um cartucho na PVM e reseta o estado de execução. /// Carrega um cartucho na PVM e reseta o estado de execução.
pub fn load_cartridge(&mut self, cart: &Cartridge) { pub fn load_cartridge(&mut self, cart: &Cartridge) {
// Na spec: "resetar PC/stack/heap ao iniciar app" // Na spec: "resetar PC/stack/heap ao iniciar app"
self.current_cartridge = Some(cart.clone());
self.vm.program = cart.program.clone(); self.vm.program = cart.program.clone();
self.vm.pc = 0; self.vm.pc = 0;
self.vm.operand_stack.clear(); self.vm.operand_stack.clear();
@ -78,25 +61,25 @@ impl PrometeuOS {
} }
/// Executa um tick do host (60Hz). /// Executa um tick do host (60Hz).
pub fn step_frame(&mut self, signals: &InputSignals) -> Option<String> { pub fn step_frame(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) -> Option<String> {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
self.tick_index += 1; self.tick_index += 1;
if !self.logical_frame_open { if !self.logical_frame_open {
self.logical_frame_open = true; self.logical_frame_open = true;
self.begin_logical_frame(signals); self.begin_logical_frame(signals, hw);
} }
// Executa budget // Executa budget
let mut vm = std::mem::take(&mut self.vm); let mut vm = std::mem::take(&mut self.vm);
let run_result = vm.run_budget(Self::CYCLES_PER_FRAME, self); let run_result = vm.run_budget(Self::CYCLES_PER_FRAME, self, hw);
self.vm = vm; self.vm = vm;
match run_result { match run_result {
Ok(run) => { Ok(run) => {
if run.reason == crate::vm::LogicalFrameEndingReason::FrameSync { if run.reason == crate::vm::LogicalFrameEndingReason::FrameSync {
self.hardware_mut().gfx_mut().render_all(); hw.gfx_mut().render_all();
self.end_logical_frame(); self.end_logical_frame(hw);
self.logical_frame_index += 1; self.logical_frame_index += 1;
self.logical_frame_open = false; self.logical_frame_open = false;
} }
@ -110,20 +93,18 @@ impl PrometeuOS {
None None
} }
fn begin_logical_frame(&mut self, signals: &InputSignals) { fn begin_logical_frame(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) {
let hw = self.hardware_mut();
hw.audio_mut().clear_commands(); hw.audio_mut().clear_commands();
hw.touch_mut().begin_frame(signals); hw.touch_mut().begin_frame(signals);
hw.pad_mut().begin_frame(signals); hw.pad_mut().begin_frame(signals);
} }
fn end_logical_frame(&mut self) { fn end_logical_frame(&mut self, hw: &mut dyn HardwareBridge) {
self.hardware_mut().gfx_mut().present(); hw.gfx_mut().present();
} }
// Helper para syscalls // Helper para syscalls
pub fn get_color(&self, index: usize) -> Color { pub fn get_color(&self, index: usize, hw: &mut dyn HardwareBridge) -> Color {
let hw = self.hardware();
if let Some(bank) = hw.gfx().banks[0].as_ref() { if let Some(bank) = hw.gfx().banks[0].as_ref() {
bank.palettes[0][index % 16] bank.palettes[0][index % 16]
} else { } else {
@ -143,8 +124,7 @@ impl PrometeuOS {
} }
// Helper para syscalls // Helper para syscalls
pub fn is_button_down(&self, id: u32) -> bool { pub fn is_button_down(&self, id: u32, hw: &mut dyn HardwareBridge) -> bool {
let hw = self.hardware();
match id { match id {
0 => hw.pad().up.down, 0 => hw.pad().up.down,
1 => hw.pad().down.down, 1 => hw.pad().down.down,
@ -163,7 +143,7 @@ impl PrometeuOS {
} }
fn create_square_sample(freq: f64, duration: f64) -> Sample { fn create_square_sample(freq: f64, duration: f64) -> Sample {
let sample_rate = crate::peripherals::OUTPUT_SAMPLE_RATE; let sample_rate = crate::hardware::OUTPUT_SAMPLE_RATE;
let num_samples = (duration * sample_rate as f64) as usize; let num_samples = (duration * sample_rate as f64) as usize;
let mut data = Vec::with_capacity(num_samples); let mut data = Vec::with_capacity(num_samples);
let period = sample_rate as f64 / freq; let period = sample_rate as f64 / freq;
@ -180,3 +160,70 @@ impl PrometeuOS {
Sample::new(sample_rate, data) Sample::new(sample_rate, data)
} }
} }
impl NativeInterface for PrometeuOS {
fn syscall(&mut self, id: u32, vm: &mut VirtualMachine, hw: &mut dyn HardwareBridge) -> Result<u64, String> {
match id {
// system.has_cart() -> bool
0x0001 => {
// vm.push(Value::Boolean(self.current_cartridge.is_some()));
Ok(10)
}
// system.run_cart()
0x0002 => {
// if let Some(cart) = self.current_cartridge.as_ref().cloned() {
// self.load_cartridge(&cart);
// } else {
// return Err("No cartridge inserted".into());
// }
Ok(100)
}
// gfx.clear(color_index)
0x1001 => {
let color_idx = vm.pop_integer()? as usize;
let color = self.get_color(color_idx, hw);
hw.gfx_mut().clear(color);
Ok(100)
}
// gfx.draw_rect(x, y, w, h, color_index)
0x1002 => {
let color_idx = vm.pop_integer()? as usize;
let h = vm.pop_integer()? as i32;
let w = vm.pop_integer()? as i32;
let y = vm.pop_integer()? as i32;
let x = vm.pop_integer()? as i32;
let color = self.get_color(color_idx, hw);
hw.gfx_mut().fill_rect(x, y, w, h, color);
Ok(200)
}
// input.get_pad(button_id) -> bool
0x2001 => {
let button_id = vm.pop_integer()? as u32;
let is_down = self.is_button_down(button_id, hw);
vm.push(Value::Boolean(is_down));
Ok(50)
}
// audio.play_sample(sample_id, voice_id, volume, pan, pitch)
0x3001 => {
let pitch = vm.pop_number()?;
let pan = vm.pop_integer()? as u8;
let volume = vm.pop_integer()? as u8;
let voice_id = vm.pop_integer()? as usize;
let sample_id = vm.pop_integer()? as u32;
let sample = match sample_id {
0 => self.sample_square.clone(),
1 => self.sample_kick.clone(),
2 => self.sample_snare.clone(),
_ => None,
};
if let Some(s) = sample {
hw.audio_mut().play(s, voice_id, volume, pan, pitch, 0, crate::hardware::LoopMode::Off);
}
Ok(300)
}
_ => Err(format!("Unknown syscall: 0x{:08X}", id)),
}
}
}

View File

@ -3,6 +3,7 @@ mod value;
mod opcode; mod opcode;
mod call_frame; mod call_frame;
mod program; mod program;
pub mod native_interface;
pub use opcode::OpCode; pub use opcode::OpCode;
pub use program::Program; pub use program::Program;

View File

@ -0,0 +1,6 @@
use crate::hardware::HardwareBridge;
use crate::vm::VirtualMachine;
pub trait NativeInterface {
fn syscall(&mut self, id: u32, vm: &mut VirtualMachine, hw: &mut dyn HardwareBridge) -> Result<u64, String>;
}

View File

@ -1,3 +1,4 @@
use crate::hardware::HardwareBridge;
use crate::prometeu_os::NativeInterface; use crate::prometeu_os::NativeInterface;
use crate::vm::call_frame::CallFrame; use crate::vm::call_frame::CallFrame;
use crate::vm::opcode::OpCode; use crate::vm::opcode::OpCode;
@ -54,7 +55,8 @@ impl VirtualMachine {
pub fn run_budget( pub fn run_budget(
&mut self, &mut self,
budget: u64, budget: u64,
native: &mut dyn NativeInterface native: &mut dyn NativeInterface,
hw: &mut dyn HardwareBridge,
) -> Result<BudgetReport, String> { ) -> Result<BudgetReport, String> {
let start_cycles = self.cycles; let start_cycles = self.cycles;
let mut ending_reason: Option<LogicalFrameEndingReason> = None; let mut ending_reason: Option<LogicalFrameEndingReason> = None;
@ -76,7 +78,7 @@ impl VirtualMachine {
break; break;
} }
self.step(native)?; self.step(native, hw)?;
// garante progresso real // garante progresso real
if self.pc == pc_before && self.cycles == cycles_before && !self.halted { if self.pc == pc_before && self.cycles == cycles_before && !self.halted {
@ -111,7 +113,7 @@ impl VirtualMachine {
Ok(u16::from_le_bytes(bytes)) Ok(u16::from_le_bytes(bytes))
} }
pub fn step(&mut self, native: &mut dyn NativeInterface) -> Result<(), String> { pub fn step(&mut self, native: &mut dyn NativeInterface, hw: &mut dyn HardwareBridge) -> Result<(), String> {
if self.halted || self.pc >= self.program.rom.len() { if self.halted || self.pc >= self.program.rom.len() {
return Ok(()); return Ok(());
} }
@ -325,7 +327,7 @@ impl VirtualMachine {
} }
OpCode::Syscall => { OpCode::Syscall => {
let id = self.read_u32()?; let id = self.read_u32()?;
let native_cycles = native.syscall(id, self).map_err(|e| format!("syscall 0x{:08X} failed: {}", id, e))?; let native_cycles = native.syscall(id, self, hw).map_err(|e| format!("syscall 0x{:08X} failed: {}", id, e))?;
self.cycles += native_cycles; self.cycles += native_cycles;
} }
OpCode::FrameSync => { OpCode::FrameSync => {

View File

@ -1,4 +1,4 @@
use prometeu_core::peripherals::{AudioCommand, Channel, LoopMode, MAX_CHANNELS, OUTPUT_SAMPLE_RATE}; use prometeu_core::hardware::{AudioCommand, Channel, LoopMode, MAX_CHANNELS, OUTPUT_SAMPLE_RATE};
use std::time::Duration; use std::time::Duration;

View File

@ -1,7 +1,8 @@
use crate::audio_mixer::AudioMixer; use crate::audio_mixer::AudioMixer;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use pixels::{Pixels, SurfaceTexture}; use pixels::{Pixels, SurfaceTexture};
use prometeu_core::peripherals::{AudioCommand, InputSignals, OUTPUT_SAMPLE_RATE}; use prometeu_core::firmware::Firmware;
use prometeu_core::hardware::{AudioCommand, InputSignals, OUTPUT_SAMPLE_RATE};
use prometeu_core::Hardware; use prometeu_core::Hardware;
use ringbuf::traits::{Consumer, Producer, Split}; use ringbuf::traits::{Consumer, Producer, Split};
use ringbuf::HeapRb; use ringbuf::HeapRb;
@ -18,7 +19,8 @@ pub struct PrometeuRunner {
window: Option<&'static Window>, window: Option<&'static Window>,
pixels: Option<Pixels<'static>>, pixels: Option<Pixels<'static>>,
hardware_bridge: Box<Hardware>, hardware: Hardware,
firmware: Firmware,
input_signals: InputSignals, input_signals: InputSignals,
@ -43,7 +45,8 @@ impl PrometeuRunner {
Self { Self {
window: None, window: None,
pixels: None, pixels: None,
hardware_bridge: Hardware::new(), hardware: Hardware::new(),
firmware: Firmware::new(),
input_signals: InputSignals::default(), input_signals: InputSignals::default(),
frame_target_dt: Duration::from_nanos(1_000_000_000 / target_fps), frame_target_dt: Duration::from_nanos(1_000_000_000 / target_fps),
last_frame_time: Instant::now(), last_frame_time: Instant::now(),
@ -180,7 +183,7 @@ impl ApplicationHandler for PrometeuRunner {
let frame = pixels.frame_mut(); let frame = pixels.frame_mut();
// Borrow imutável do core (campo diferente, ok) // Borrow imutável do core (campo diferente, ok)
let src = self.hardware_bridge.gfx.front_buffer(); let src = self.hardware.gfx.front_buffer();
draw_rgb565_to_rgba8(src, frame); draw_rgb565_to_rgba8(src, frame);
} // <- frame borrow termina aqui } // <- frame borrow termina aqui
@ -253,11 +256,11 @@ impl ApplicationHandler for PrometeuRunner {
// 🔥 O coração do determinismo: consome o tempo em fatias exatas de 60Hz // 🔥 O coração do determinismo: consome o tempo em fatias exatas de 60Hz
while self.accumulator >= self.frame_target_dt { while self.accumulator >= self.frame_target_dt {
self.hardware_bridge.step_frame(&self.input_signals); self.firmware.step_frame(&self.input_signals, &mut self.hardware);
// Envia comandos de áudio gerados neste frame para a thread de áudio // Envia comandos de áudio gerados neste frame para a thread de áudio
if let Some(producer) = &mut self.audio_producer { if let Some(producer) = &mut self.audio_producer {
for cmd in self.hardware_bridge.audio.commands.drain(..) { for cmd in self.hardware.audio.commands.drain(..) {
let _ = producer.try_push(cmd); let _ = producer.try_push(cmd);
} }
} }
@ -279,11 +282,11 @@ impl ApplicationHandler for PrometeuRunner {
if stats_elapsed >= Duration::from_secs(1) { if stats_elapsed >= Duration::from_secs(1) {
if let Some(window) = self.window { if let Some(window) = self.window {
let fps = self.frames_since_last_update as f64 / stats_elapsed.as_secs_f64(); let fps = self.frames_since_last_update as f64 / stats_elapsed.as_secs_f64();
let kb = self.hardware_bridge.gfx.memory_usage_bytes() as f64 / 1024.0; let kb = self.hardware.gfx.memory_usage_bytes() as f64 / 1024.0;
// comparação fixa sempre contra 60Hz, manter mesmo quando fazer teste de stress na CPU // comparação fixa sempre contra 60Hz, manter mesmo quando fazer teste de stress na CPU
let frame_budget_us = 16666.0; let frame_budget_us = 16666.0;
let cpu_load_core = (self.hardware_bridge.last_frame_cpu_time_us() as f64 / frame_budget_us) * 100.0; let cpu_load_core = (self.firmware.os.last_frame_cpu_time_us as f64 / frame_budget_us) * 100.0;
let cpu_load_audio = if self.audio_load_samples > 0 { let cpu_load_audio = if self.audio_load_samples > 0 {
// O load real é (tempo total processando) / (tempo total de parede). // O load real é (tempo total processando) / (tempo total de parede).
@ -294,7 +297,7 @@ impl ApplicationHandler for PrometeuRunner {
let title = format!( let title = format!(
"PROMETEU | GFX: {:.1} KB | FPS: {:.1} | Load: {:.1}% (C) + {:.1}% (A) | Frame: tick {} logical {}", "PROMETEU | GFX: {:.1} KB | FPS: {:.1} | Load: {:.1}% (C) + {:.1}% (A) | Frame: tick {} logical {}",
kb, fps, cpu_load_core, cpu_load_audio, self.hardware_bridge.tick_index(), self.hardware_bridge.logical_frame_index() kb, fps, cpu_load_core, cpu_load_audio, self.firmware.os.tick_index, self.firmware.os.logical_frame_index
); );
window.set_title(&title); window.set_title(&title);
} }