use super::*; use crate::CrashReport; use prometeu_hal::asset::BankType; use prometeu_hal::log::{LogLevel, LogSource}; use prometeu_hal::{HardwareBridge, HostContext, InputSignals}; use prometeu_vm::LogicalFrameEndingReason; impl VirtualMachineRuntime { pub fn debug_step_instruction( &mut self, vm: &mut VirtualMachine, hw: &mut dyn HardwareBridge, ) -> Option { let mut ctx = HostContext::new(Some(hw)); match vm.step(self, &mut ctx) { Ok(_) => None, Err(e) => { let report = match e { LogicalFrameEndingReason::Trap(trap) => CrashReport::VmTrap { trap }, LogicalFrameEndingReason::Panic(message) => { CrashReport::VmPanic { message, pc: Some(vm.pc() as u32) } } other => CrashReport::VmPanic { message: format!("Unexpected fault during step: {:?}", other), pc: Some(vm.pc() as u32), }, }; self.log( LogLevel::Error, LogSource::Vm, report.log_tag(), format!("PVM Fault during Step: {}", report), ); self.last_crash_report = Some(report.clone()); Some(report) } } } pub fn tick( &mut self, vm: &mut VirtualMachine, signals: &InputSignals, hw: &mut dyn HardwareBridge, ) -> Option { let start = Instant::now(); self.tick_index += 1; if self.paused && !self.debug_step_request { return None; } self.update_fs(); if !self.logical_frame_active { self.logical_frame_active = true; self.logical_frame_remaining_cycles = Self::CYCLES_PER_LOGICAL_FRAME; self.begin_logical_frame(signals, hw); if self.needs_prepare_entry_call || vm.call_stack_is_empty() { vm.prepare_boot_call(); self.needs_prepare_entry_call = false; } self.telemetry_current = TelemetryFrame { frame_index: self.logical_frame_index, cycles_budget: self .certifier .config .cycles_budget_per_frame .unwrap_or(Self::CYCLES_PER_LOGICAL_FRAME), ..Default::default() }; } let budget = std::cmp::min(Self::SLICE_PER_TICK, self.logical_frame_remaining_cycles); if budget > 0 { let run_result = { let mut ctx = HostContext::new(Some(hw)); vm.run_budget(budget, self, &mut ctx) }; match run_result { Ok(run) => { self.logical_frame_remaining_cycles = self.logical_frame_remaining_cycles.saturating_sub(run.cycles_used); self.telemetry_current.cycles_used += run.cycles_used; self.telemetry_current.vm_steps += run.steps_executed; if run.reason == LogicalFrameEndingReason::Breakpoint { self.paused = true; self.debug_step_request = false; self.log( LogLevel::Info, LogSource::Vm, 0xDEB1, format!("Breakpoint hit at PC 0x{:X}", vm.pc()), ); } if let LogicalFrameEndingReason::Panic(err) = run.reason { let report = CrashReport::VmPanic { message: err, pc: Some(vm.pc() as u32) }; self.log( LogLevel::Error, LogSource::Vm, report.log_tag(), report.summary(), ); self.last_crash_report = Some(report.clone()); return Some(report); } if let LogicalFrameEndingReason::Trap(trap) = &run.reason { let report = CrashReport::VmTrap { trap: trap.clone() }; self.log( LogLevel::Error, LogSource::Vm, report.log_tag(), report.summary(), ); self.last_crash_report = Some(report.clone()); return Some(report); } if run.reason == LogicalFrameEndingReason::FrameSync || run.reason == LogicalFrameEndingReason::EndOfRom { hw.gfx_mut().render_all(); self.telemetry_current.host_cpu_time_us = start.elapsed().as_micros() as u64; let ts_ms = self.boot_time.elapsed().as_millis() as u64; self.telemetry_current.violations = self.certifier.evaluate( &self.telemetry_current, &mut self.log_service, ts_ms, ) as u32; self.telemetry_current.completed_logical_frames += 1; self.telemetry_last = self.telemetry_current; self.logical_frame_index += 1; self.logical_frame_active = false; self.logical_frame_remaining_cycles = 0; if run.reason == LogicalFrameEndingReason::FrameSync { self.needs_prepare_entry_call = true; } if self.debug_step_request { self.paused = true; self.debug_step_request = false; } } } Err(e) => { let report = CrashReport::VmPanic { message: e, pc: Some(vm.pc() as u32) }; self.log(LogLevel::Error, LogSource::Vm, report.log_tag(), report.summary()); self.last_crash_report = Some(report.clone()); return Some(report); } } } self.last_frame_cpu_time_us = start.elapsed().as_micros() as u64; let gfx_stats = hw.assets().bank_info(BankType::GLYPH); self.telemetry_current.gfx_used_bytes = gfx_stats.used_bytes; self.telemetry_current.gfx_inflight_bytes = gfx_stats.inflight_bytes; self.telemetry_current.gfx_slots_occupied = gfx_stats.slots_occupied as u32; let audio_stats = hw.assets().bank_info(BankType::SOUNDS); self.telemetry_current.audio_used_bytes = audio_stats.used_bytes; self.telemetry_current.audio_inflight_bytes = audio_stats.inflight_bytes; self.telemetry_current.audio_slots_occupied = audio_stats.slots_occupied as u32; if !self.logical_frame_active && self.telemetry_last.frame_index == self.logical_frame_index.wrapping_sub(1) { self.telemetry_last.host_cpu_time_us = self.last_frame_cpu_time_us; self.telemetry_last.cycles_budget = self.telemetry_current.cycles_budget; self.telemetry_last.gfx_used_bytes = self.telemetry_current.gfx_used_bytes; self.telemetry_last.gfx_inflight_bytes = self.telemetry_current.gfx_inflight_bytes; self.telemetry_last.gfx_slots_occupied = self.telemetry_current.gfx_slots_occupied; self.telemetry_last.audio_used_bytes = self.telemetry_current.audio_used_bytes; self.telemetry_last.audio_inflight_bytes = self.telemetry_current.audio_inflight_bytes; self.telemetry_last.audio_slots_occupied = self.telemetry_current.audio_slots_occupied; } None } pub(crate) fn begin_logical_frame( &mut self, _signals: &InputSignals, hw: &mut dyn HardwareBridge, ) { hw.audio_mut().clear_commands(); self.logs_written_this_frame.clear(); } }