Profiling #7
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -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