clean up and organize
This commit is contained in:
parent
8fb0cb91e6
commit
38f07c0b26
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 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;
|
||||||
@ -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 {
|
||||||
@ -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 {
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
@ -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::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;
|
||||||
@ -179,4 +159,71 @@ 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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
|
|||||||
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::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 => {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user