Track HALT
This commit is contained in:
parent
aa3cc3df9b
commit
7f9791c4c5
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue