204 lines
8.2 KiB
Rust
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();
|
|
}
|
|
}
|