Track HALT

This commit is contained in:
Simon Gellis 2025-08-11 21:00:45 -04:00
parent aa3cc3df9b
commit 7f9791c4c5
2 changed files with 80 additions and 34 deletions

View File

@ -210,7 +210,7 @@ extern "C" fn on_execute(sim: *mut VB, address: u32, code: *const u16, length: c
}
}
let mut stopped = data.stop_reason.is_some();
let mut stopped = data.stop_reason.is_some() || data.monitor.event.is_some();
if data.step_from.is_some_and(|s| s != address) {
data.step_from = None;
data.stop_reason = Some(StopReason::Stepped);
@ -247,7 +247,13 @@ extern "C" fn on_exception(sim: *mut VB, cause: *mut u16) -> c_int {
// There is no way for the userdata to be null or otherwise invalid.
let data: &mut VBState = unsafe { &mut *vb_get_user_data(sim).cast() };
data.monitor.event = data.monitor.queued_event.take();
data.monitor.queued_event = Some(SimEvent::Interrupt(unsafe { *cause }));
let cause = unsafe { *cause };
let pc = if cause == 0xff70 {
0xffffff60
} else {
(cause & 0xfff0) as u32 | 0xffff0000
};
data.monitor.queued_event = Some(SimEvent::Interrupt(cause, pc));
unsafe { vb_set_exception_callback(sim, None) };
unsafe { vb_set_fetch_callback(sim, Some(on_fetch)) };
if data.monitor.event.is_some() { 1 } else { 0 }
@ -319,7 +325,8 @@ extern "C" fn on_write(
pub enum SimEvent {
Call(u32),
Return,
Interrupt(u16),
Halt,
Interrupt(u16, u32),
Reti,
}
@ -327,6 +334,7 @@ struct EventMonitor {
enabled: bool,
event: Option<SimEvent>,
queued_event: Option<SimEvent>,
just_halted: bool,
}
impl EventMonitor {
@ -335,6 +343,7 @@ impl EventMonitor {
enabled: false,
event: None,
queued_event: None,
just_halted: false,
}
}
@ -343,7 +352,8 @@ impl EventMonitor {
self.queued_event.is_some()
}
fn do_detect_event(&self, sim: *mut VB, address: u32, code: &[u16]) -> Option<SimEvent> {
fn do_detect_event(&mut self, sim: *mut VB, address: u32, code: &[u16]) -> Option<SimEvent> {
const HALT_OPCODE: u16 = 0b011010;
const JAL_OPCODE: u16 = 0b101011;
const JMP_OPCODE: u16 = 0b000110;
const RETI_OPCODE: u16 = 0b011001;
@ -359,6 +369,18 @@ impl EventMonitor {
let opcode = code[0] >> 10;
if opcode == HALT_OPCODE {
if !self.just_halted {
self.just_halted = true;
self.event = Some(SimEvent::Halt);
} else {
self.just_halted = false;
}
// Don't _return_ an event, we want to emit this right away.
// If the CPU is halting, no other callbacks will run for a long time.
return None;
}
if opcode == JAL_OPCODE {
let disp = format_iv_disp(code);
if disp != 4 {

View File

@ -96,10 +96,11 @@ struct ProfileSession {
struct StackFrame {
#[expect(dead_code)]
address: Option<u32>,
address: u32,
cycles: u64,
}
const RESET_CODE: u16 = 0xfff0;
impl ProfileSession {
async fn new(file_path: PathBuf) -> Self {
let symbol_manager = SymbolManager::with_config(Default::default());
@ -109,22 +110,24 @@ impl ProfileSession {
.expect("cannae load symbols");
let mut call_stacks = HashMap::new();
call_stacks.insert(
0,
RESET_CODE,
vec![StackFrame {
address: None,
address: 0xfffffff0,
cycles: 0,
}],
);
Self {
symbol_map,
call_stacks,
context_stack: vec![],
context_stack: vec![RESET_CODE],
}
}
fn track_elapsed_cycles(&mut self, cycles: u32) {
let code = self.context_stack.last().copied().unwrap_or(0);
let Some(stack) = self.call_stacks.get_mut(&code) else {
let Some(code) = self.context_stack.last() else {
return; // program is halted, CPU is idle
};
let Some(stack) = self.call_stacks.get_mut(code) else {
panic!("missing stack {code:04x}");
};
for frame in stack {
@ -134,42 +137,63 @@ impl ProfileSession {
fn track_event(&mut self, event: SimEvent) {
match event {
SimEvent::Interrupt(code) => {
self.context_stack.push(code);
if self.call_stacks.insert(code, vec![]).is_some() {
panic!("{code:04x} fired twice");
}
}
SimEvent::Reti => {
let Some(code) = self.context_stack.pop() else {
panic!("reti when not in interrupt");
SimEvent::Call(address) => {
let Some(code) = self.context_stack.last() else {
panic!("How did we call anything when we're halted?");
};
if self.call_stacks.remove(&code).is_none() {
panic!("{code:04x} popped but never called")
}
}
SimEvent::Call(addr) => {
let code = self.context_stack.last().copied().unwrap_or(0);
let Some(stack) = self.call_stacks.get_mut(&code) else {
let Some(stack) = self.call_stacks.get_mut(code) else {
panic!("missing stack {code:04x}");
};
let name = self
.symbol_map
.lookup_sync(wholesym::LookupAddress::Svma(addr as u64));
println!("depth {}: {:?}", stack.len(), name);
stack.push(StackFrame {
address: Some(addr),
cycles: 0,
});
.lookup_sync(wholesym::LookupAddress::Svma(address as u64));
println!("depth {}: {:x?}", stack.len(), name);
stack.push(StackFrame { address, cycles: 0 });
}
SimEvent::Return => {
let code = self.context_stack.last().copied().unwrap_or(0);
let Some(stack) = self.call_stacks.get_mut(&code) else {
let Some(code) = self.context_stack.last() else {
panic!("how did we return when we're halted?");
};
let Some(stack) = self.call_stacks.get_mut(code) else {
panic!("missing stack {code:04x}");
};
if stack.pop().is_none() {
panic!("returned from {code:04x} but stack was empty");
}
if stack.is_empty() {
panic!("returned to oblivion");
}
}
SimEvent::Halt => {
let Some(RESET_CODE) = self.context_stack.pop() else {
panic!("halted when not in an interrupt");
};
}
SimEvent::Interrupt(code, address) => {
// if the CPU was halted before, wake it up now
if self.context_stack.is_empty() {
self.context_stack.push(RESET_CODE);
}
self.context_stack.push(code);
if self
.call_stacks
.insert(code, vec![StackFrame { address, cycles: 0 }])
.is_some()
{
panic!("{code:04x} fired twice");
}
}
SimEvent::Reti => {
let Some(code) = self.context_stack.pop() else {
panic!("RETI when halted");
};
if code == RESET_CODE {
panic!("RETI when not in interrupt");
}
if self.call_stacks.remove(&code).is_none() {
panic!("{code:04x} popped but never called");
}
}
}
}