clean up and organize
This commit is contained in:
parent
ba488a5fee
commit
de7c286f75
@ -1,6 +1,6 @@
|
||||
use crate::firmware::firmware_state::FirmwareState;
|
||||
use crate::hardware::{HardwareBridge, InputSignals};
|
||||
use crate::model::{AppMode, Cartridge, Color};
|
||||
use crate::peripherals::InputSignals;
|
||||
use crate::prometeu_hub::PrometeuHub;
|
||||
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 {
|
||||
FirmwareState::Reset => {
|
||||
self.os.reset();
|
||||
@ -34,33 +34,34 @@ impl Firmware {
|
||||
self.state = FirmwareState::HubHome;
|
||||
}
|
||||
FirmwareState::HubHome => {
|
||||
// UI do Hub
|
||||
self.hub.update(&mut self.os);
|
||||
hw.gfx_mut().clear(Color::INDIGO);
|
||||
self.hub.gui_update(&mut self.os);
|
||||
hw.gfx_mut().present();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
FirmwareState::PosRunSystem(_cart) => {
|
||||
// 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);
|
||||
}
|
||||
// TODO: Compor com a UI do Hub
|
||||
}
|
||||
FirmwareState::PosCrashScreen(_error) => {
|
||||
// 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
|
||||
self.os.hardware_mut().gfx_mut().clear(Color::RED);
|
||||
hw.gfx_mut().clear(Color::RED);
|
||||
// Por enquanto apenas logamos ou mostramos algo simples
|
||||
// No futuro, usar draw_text
|
||||
self.os.hardware_mut().gfx_mut().present();
|
||||
hw.gfx_mut().present();
|
||||
|
||||
// Se apertar START, volta pro Hub
|
||||
if self.os.hardware().pad().start.down {
|
||||
if hw.pad().start.down {
|
||||
self.state = FirmwareState::LaunchHubHome;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
36
crates/core/src/hardware/hardware.rs
Normal file
36
crates/core/src/hardware/hardware.rs
Normal 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,16 +3,15 @@ mod pad;
|
||||
mod touch;
|
||||
mod input_signal;
|
||||
mod audio;
|
||||
pub mod hardware;
|
||||
|
||||
pub use gfx::Gfx;
|
||||
pub use gfx::BlendMode;
|
||||
pub use input_signal::InputSignals;
|
||||
pub use pad::Pad;
|
||||
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 {
|
||||
fn gfx(&self) -> &Gfx;
|
||||
fn gfx_mut(&mut self) -> &mut Gfx;
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::model::Button;
|
||||
use crate::peripherals::input_signal::InputSignals;
|
||||
use crate::hardware::input_signal::InputSignals;
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct Pad {
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::model::Button;
|
||||
use crate::peripherals::input_signal::InputSignals;
|
||||
use crate::hardware::input_signal::InputSignals;
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct Touch {
|
||||
@ -1,9 +1,8 @@
|
||||
pub mod hardware;
|
||||
pub mod peripherals;
|
||||
pub mod vm;
|
||||
mod model;
|
||||
pub mod firmware;
|
||||
mod prometeu_os;
|
||||
mod prometeu_hub;
|
||||
|
||||
pub use hardware::Hardware;
|
||||
pub use hardware::hardware::Hardware;
|
||||
|
||||
@ -31,10 +31,7 @@ impl PrometeuHub {
|
||||
// Inicializa o Window System e lista apps
|
||||
}
|
||||
|
||||
pub fn update(&mut self, os: &mut PrometeuOS) {
|
||||
// Renderiza a UI básica do Hub
|
||||
os.hardware_mut().gfx_mut().clear(self.window_system.theme_color);
|
||||
// TODO: Desenhar lista de apps
|
||||
os.hardware_mut().gfx_mut().present();
|
||||
pub fn gui_update(&mut self, _os: &mut PrometeuOS) {
|
||||
// Atualiza a UI do Window System
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
mod prometeu_os;
|
||||
mod native_interface;
|
||||
|
||||
pub use prometeu_os::PrometeuOS;
|
||||
pub use native_interface::NativeInterface;
|
||||
pub use crate::vm::native_interface::NativeInterface;
|
||||
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
use crate::model::{Cartridge, Color, Sample};
|
||||
use crate::peripherals::{HardwareBridge, InputSignals};
|
||||
use crate::vm::VirtualMachine;
|
||||
use crate::hardware::{HardwareBridge, InputSignals};
|
||||
use crate::vm::{Value, VirtualMachine};
|
||||
use std::sync::Arc;
|
||||
use crate::prometeu_os::NativeInterface;
|
||||
|
||||
/// PrometeuOS (POS): O firmware/base do sistema.
|
||||
/// 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_open: bool,
|
||||
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)
|
||||
pub sample_square: Option<Arc<Sample>>,
|
||||
@ -26,12 +25,10 @@ impl PrometeuOS {
|
||||
pub fn new() -> Self {
|
||||
let mut os = Self {
|
||||
vm: VirtualMachine::default(),
|
||||
hw_ptr: None,
|
||||
tick_index: 0,
|
||||
logical_frame_index: 0,
|
||||
logical_frame_open: false,
|
||||
last_frame_cpu_time_us: 0,
|
||||
current_cartridge: None,
|
||||
sample_square: None,
|
||||
sample_kick: None,
|
||||
sample_snare: None,
|
||||
@ -43,31 +40,17 @@ impl PrometeuOS {
|
||||
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) {
|
||||
self.vm = VirtualMachine::default();
|
||||
self.tick_index = 0;
|
||||
self.logical_frame_index = 0;
|
||||
self.logical_frame_open = false;
|
||||
self.last_frame_cpu_time_us = 0;
|
||||
self.current_cartridge = None;
|
||||
}
|
||||
|
||||
/// Carrega um cartucho na PVM e reseta o estado de execução.
|
||||
pub fn load_cartridge(&mut self, cart: &Cartridge) {
|
||||
// Na spec: "resetar PC/stack/heap ao iniciar app"
|
||||
self.current_cartridge = Some(cart.clone());
|
||||
self.vm.program = cart.program.clone();
|
||||
self.vm.pc = 0;
|
||||
self.vm.operand_stack.clear();
|
||||
@ -78,25 +61,25 @@ impl PrometeuOS {
|
||||
}
|
||||
|
||||
/// 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();
|
||||
self.tick_index += 1;
|
||||
|
||||
if !self.logical_frame_open {
|
||||
self.logical_frame_open = true;
|
||||
self.begin_logical_frame(signals);
|
||||
self.begin_logical_frame(signals, hw);
|
||||
}
|
||||
|
||||
// Executa budget
|
||||
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;
|
||||
|
||||
match run_result {
|
||||
Ok(run) => {
|
||||
if run.reason == crate::vm::LogicalFrameEndingReason::FrameSync {
|
||||
self.hardware_mut().gfx_mut().render_all();
|
||||
self.end_logical_frame();
|
||||
hw.gfx_mut().render_all();
|
||||
self.end_logical_frame(hw);
|
||||
self.logical_frame_index += 1;
|
||||
self.logical_frame_open = false;
|
||||
}
|
||||
@ -110,20 +93,18 @@ impl PrometeuOS {
|
||||
None
|
||||
}
|
||||
|
||||
fn begin_logical_frame(&mut self, signals: &InputSignals) {
|
||||
let hw = self.hardware_mut();
|
||||
fn begin_logical_frame(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) {
|
||||
hw.audio_mut().clear_commands();
|
||||
hw.touch_mut().begin_frame(signals);
|
||||
hw.pad_mut().begin_frame(signals);
|
||||
}
|
||||
|
||||
fn end_logical_frame(&mut self) {
|
||||
self.hardware_mut().gfx_mut().present();
|
||||
fn end_logical_frame(&mut self, hw: &mut dyn HardwareBridge) {
|
||||
hw.gfx_mut().present();
|
||||
}
|
||||
|
||||
// Helper para syscalls
|
||||
pub fn get_color(&self, index: usize) -> Color {
|
||||
let hw = self.hardware();
|
||||
pub fn get_color(&self, index: usize, hw: &mut dyn HardwareBridge) -> Color {
|
||||
if let Some(bank) = hw.gfx().banks[0].as_ref() {
|
||||
bank.palettes[0][index % 16]
|
||||
} else {
|
||||
@ -143,8 +124,7 @@ impl PrometeuOS {
|
||||
}
|
||||
|
||||
// Helper para syscalls
|
||||
pub fn is_button_down(&self, id: u32) -> bool {
|
||||
let hw = self.hardware();
|
||||
pub fn is_button_down(&self, id: u32, hw: &mut dyn HardwareBridge) -> bool {
|
||||
match id {
|
||||
0 => hw.pad().up.down,
|
||||
1 => hw.pad().down.down,
|
||||
@ -163,7 +143,7 @@ impl PrometeuOS {
|
||||
}
|
||||
|
||||
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 mut data = Vec::with_capacity(num_samples);
|
||||
let period = sample_rate as f64 / freq;
|
||||
@ -179,4 +159,71 @@ impl PrometeuOS {
|
||||
|
||||
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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ mod value;
|
||||
mod opcode;
|
||||
mod call_frame;
|
||||
mod program;
|
||||
pub mod native_interface;
|
||||
|
||||
pub use opcode::OpCode;
|
||||
pub use program::Program;
|
||||
|
||||
6
crates/core/src/vm/native_interface.rs
Normal file
6
crates/core/src/vm/native_interface.rs
Normal 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>;
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
use crate::hardware::HardwareBridge;
|
||||
use crate::prometeu_os::NativeInterface;
|
||||
use crate::vm::call_frame::CallFrame;
|
||||
use crate::vm::opcode::OpCode;
|
||||
@ -54,7 +55,8 @@ impl VirtualMachine {
|
||||
pub fn run_budget(
|
||||
&mut self,
|
||||
budget: u64,
|
||||
native: &mut dyn NativeInterface
|
||||
native: &mut dyn NativeInterface,
|
||||
hw: &mut dyn HardwareBridge,
|
||||
) -> Result<BudgetReport, String> {
|
||||
let start_cycles = self.cycles;
|
||||
let mut ending_reason: Option<LogicalFrameEndingReason> = None;
|
||||
@ -76,7 +78,7 @@ impl VirtualMachine {
|
||||
break;
|
||||
}
|
||||
|
||||
self.step(native)?;
|
||||
self.step(native, hw)?;
|
||||
|
||||
// garante progresso real
|
||||
if self.pc == pc_before && self.cycles == cycles_before && !self.halted {
|
||||
@ -111,7 +113,7 @@ impl VirtualMachine {
|
||||
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() {
|
||||
return Ok(());
|
||||
}
|
||||
@ -325,7 +327,7 @@ impl VirtualMachine {
|
||||
}
|
||||
OpCode::Syscall => {
|
||||
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;
|
||||
}
|
||||
OpCode::FrameSync => {
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use crate::audio_mixer::AudioMixer;
|
||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
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 ringbuf::traits::{Consumer, Producer, Split};
|
||||
use ringbuf::HeapRb;
|
||||
@ -18,7 +19,8 @@ pub struct PrometeuRunner {
|
||||
window: Option<&'static Window>,
|
||||
pixels: Option<Pixels<'static>>,
|
||||
|
||||
hardware_bridge: Box<Hardware>,
|
||||
hardware: Hardware,
|
||||
firmware: Firmware,
|
||||
|
||||
input_signals: InputSignals,
|
||||
|
||||
@ -43,7 +45,8 @@ impl PrometeuRunner {
|
||||
Self {
|
||||
window: None,
|
||||
pixels: None,
|
||||
hardware_bridge: Hardware::new(),
|
||||
hardware: Hardware::new(),
|
||||
firmware: Firmware::new(),
|
||||
input_signals: InputSignals::default(),
|
||||
frame_target_dt: Duration::from_nanos(1_000_000_000 / target_fps),
|
||||
last_frame_time: Instant::now(),
|
||||
@ -180,7 +183,7 @@ impl ApplicationHandler for PrometeuRunner {
|
||||
let frame = pixels.frame_mut();
|
||||
|
||||
// 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);
|
||||
} // <- 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
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -279,11 +282,11 @@ impl ApplicationHandler for PrometeuRunner {
|
||||
if stats_elapsed >= Duration::from_secs(1) {
|
||||
if let Some(window) = self.window {
|
||||
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
|
||||
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 {
|
||||
// O load real é (tempo total processando) / (tempo total de parede).
|
||||
@ -294,7 +297,7 @@ impl ApplicationHandler for PrometeuRunner {
|
||||
|
||||
let title = format!(
|
||||
"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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user