204 lines
8.2 KiB
Rust

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<CrashReport> {
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<CrashReport> {
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();
}
}