logical frame computed by cost
This commit is contained in:
parent
85a5f2d63f
commit
43e366bcd5
@ -15,14 +15,15 @@ pub struct LogicalHardware {
|
||||
pub vm: VirtualMachine,
|
||||
pub cartridge: Option<Cartridge>,
|
||||
|
||||
// Estado Interno
|
||||
logical_frame_open: bool,
|
||||
|
||||
// Assets de exemplo
|
||||
pub sample_square: Option<Arc<Sample>>,
|
||||
pub sample_kick: Option<Arc<Sample>>,
|
||||
pub sample_snare: Option<Arc<Sample>>,
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl LogicalHardware {
|
||||
pub const W: usize = 320;
|
||||
pub const H: usize = 180;
|
||||
@ -38,6 +39,7 @@ impl LogicalHardware {
|
||||
last_frame_cpu_time_us: 0,
|
||||
vm: VirtualMachine::default(),
|
||||
cartridge: None,
|
||||
logical_frame_open: false,
|
||||
sample_square: None,
|
||||
sample_kick: None,
|
||||
sample_snare: None,
|
||||
@ -45,7 +47,7 @@ impl LogicalHardware {
|
||||
|
||||
// Inicializa samples básicos
|
||||
logical_hardware.sample_square = Some(Arc::new(Self::create_square_sample(440.0, 0.1)));
|
||||
|
||||
|
||||
logical_hardware
|
||||
}
|
||||
|
||||
@ -114,24 +116,34 @@ impl LogicalHardware {
|
||||
/// O Host controla tempo/event loop; o Core define a sequência do frame.
|
||||
pub fn step_frame(&mut self, signals: &InputSignals) {
|
||||
let start = std::time::Instant::now();
|
||||
self.begin_frame(signals);
|
||||
|
||||
// Retira a VM temporariamente para executar run_budget passando self (Logical Hardware) como NativeInterface.
|
||||
// Como VirtualMachine implementa Default, usamos std::mem::take.
|
||||
|
||||
// Se um frame logica estiver aberto evita limpar o input
|
||||
if !self.logical_frame_open {
|
||||
self.logical_frame_open = true;
|
||||
self.begin_frame(signals);
|
||||
}
|
||||
|
||||
// Executa uma fatia fixa de ciclos (budget do tick real do host)
|
||||
let mut vm = std::mem::take(&mut self.vm);
|
||||
let _result = vm.run_budget(Self::CYCLES_PER_FRAME, self);
|
||||
let run = vm
|
||||
.run_budget(Self::CYCLES_PER_FRAME, self)
|
||||
.expect("vm error");
|
||||
self.vm = vm;
|
||||
|
||||
self.gfx.render_all(); // A máquina executa o pipeline gráfico (Ação física)
|
||||
self.end_frame(); // present/housekeeping
|
||||
self.last_frame_cpu_time_us = start.elapsed().as_micros() as u64; // Calcula quanto tempo a "CPU simulada" trabalhou de verdade
|
||||
// Só “materializa” e apresenta quando o frame lógico fecha
|
||||
if run.reason == crate::vm::LogicalFrameEndingReason::FrameSync {
|
||||
self.gfx.render_all();
|
||||
self.end_frame();
|
||||
self.frame_index += 1; // conta frames lógicos apresentados
|
||||
self.logical_frame_open = false;
|
||||
}
|
||||
|
||||
self.last_frame_cpu_time_us = start.elapsed().as_micros() as u64;
|
||||
}
|
||||
|
||||
/// Início do frame: zera flags transitórias.
|
||||
pub fn begin_frame(&mut self, signals: &InputSignals) {
|
||||
self.frame_index += 1;
|
||||
|
||||
// Limpa comandos de áudio do frame anterior.
|
||||
// Limpa comandos de áudio do frame anterior.
|
||||
// Nota: O Host deve consumir esses comandos ANTES ou DURANTE o step_frame se quiser processá-los.
|
||||
// Como o Host atual consome logo após o step_frame, limpar aqui no início do PRÓXIMO frame está correto.
|
||||
self.audio.clear_commands();
|
||||
|
||||
@ -4,7 +4,8 @@ mod opcode;
|
||||
mod call_frame;
|
||||
mod program;
|
||||
|
||||
pub use value::Value;
|
||||
pub use virtual_machine::VirtualMachine;
|
||||
pub use opcode::OpCode;
|
||||
pub use program::Program;
|
||||
pub use value::Value;
|
||||
pub use virtual_machine::{BudgetReport, LogicalFrameEndingReason, VirtualMachine};
|
||||
|
||||
|
||||
@ -4,9 +4,22 @@ use crate::vm::call_frame::CallFrame;
|
||||
use crate::vm::opcode::OpCode;
|
||||
use crate::vm::value::Value;
|
||||
|
||||
|
||||
use crate::vm::Program;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum LogicalFrameEndingReason {
|
||||
FrameSync,
|
||||
BudgetExhausted,
|
||||
Halted,
|
||||
EndOfRom,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct BudgetReport {
|
||||
pub cycles_used: u64,
|
||||
pub reason: LogicalFrameEndingReason,
|
||||
}
|
||||
|
||||
pub struct VirtualMachine {
|
||||
pub pc: usize,
|
||||
pub operand_stack: Vec<Value>,
|
||||
@ -275,33 +288,53 @@ impl Default for VirtualMachine {
|
||||
}
|
||||
|
||||
impl VirtualMachine {
|
||||
pub fn run_budget(&mut self, budget: u64, native: &mut dyn NativeInterface) -> Result<u64, String> {
|
||||
pub fn run_budget(
|
||||
&mut self,
|
||||
budget: u64,
|
||||
native: &mut dyn NativeInterface
|
||||
) -> Result<BudgetReport, String> {
|
||||
let start_cycles = self.cycles;
|
||||
let mut budget_used = 0;
|
||||
let mut ending_reason: Option<LogicalFrameEndingReason> = None;
|
||||
|
||||
while budget_used < budget && !self.halted && self.pc < self.program.rom.len() {
|
||||
while (self.cycles - start_cycles) < budget
|
||||
&& !self.halted
|
||||
&& self.pc < self.program.rom.len()
|
||||
{
|
||||
let pc_before = self.pc;
|
||||
let cycles_before = self.cycles;
|
||||
|
||||
// Fast-path: FRAME_SYNC encerra o frame lógico
|
||||
let opcode_val = self.peek_u16()?;
|
||||
let opcode = OpCode::try_from(opcode_val)?;
|
||||
|
||||
if opcode == OpCode::FrameSync {
|
||||
self.pc += 2;
|
||||
self.cycles += OpCode::FrameSync.cycles();
|
||||
ending_reason = Some(LogicalFrameEndingReason::FrameSync);
|
||||
break;
|
||||
}
|
||||
|
||||
self.step(native)?;
|
||||
|
||||
// Garantir progresso para evitar loop infinito real (onde nem PC nem ciclos avançam)
|
||||
|
||||
// garante progresso real
|
||||
if self.pc == pc_before && self.cycles == cycles_before && !self.halted {
|
||||
return Err(format!("VM stuck at PC 0x{:08X}", self.pc));
|
||||
}
|
||||
|
||||
budget_used = self.cycles - start_cycles;
|
||||
}
|
||||
|
||||
Ok(budget_used)
|
||||
if ending_reason.is_none() {
|
||||
if self.halted {
|
||||
ending_reason = Some(LogicalFrameEndingReason::Halted);
|
||||
} else if self.pc >= self.program.rom.len() {
|
||||
ending_reason = Some(LogicalFrameEndingReason::EndOfRom);
|
||||
} else {
|
||||
ending_reason = Some(LogicalFrameEndingReason::BudgetExhausted);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(BudgetReport {
|
||||
cycles_used: self.cycles - start_cycles,
|
||||
reason: ending_reason.unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
fn peek_u16(&self) -> Result<u16, String> {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user