use super::breakpoints::EvalContext;
pub trait DebuggableEmulator {
type InterruptKind: Copy + PartialEq + Eq + core::fmt::Debug;
fn pc(&self) -> u16;
fn cycles(&self) -> u64;
fn frame_count(&self) -> u64;
fn current_interrupt(&self) -> Option<Self::InterruptKind>;
fn read_for_debugger(&self, addr: u16) -> u8;
fn read_opcode(&self, addr: u16) -> u8 {
self.read_for_debugger(addr)
}
fn is_call_instruction(&self, addr: u16) -> bool;
fn instruction_length(&self, addr: u16) -> u16;
fn run_one_instruction(&mut self) -> u16;
fn set_cpu_trace_enabled(&mut self, enabled: bool);
fn is_cpu_trace_enabled(&self) -> bool;
fn build_eval_context(
&self,
prev_cycles: u64,
prev_frame: u64,
write_addr: Option<u16>,
) -> EvalContext;
}
pub trait CpuSnapshot: Clone + Default + PartialEq + Eq + core::fmt::Debug {
fn format_display(&self) -> String;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MemoryRegion {
pub name: &'static str,
pub min_addr: u16,
pub max_addr: u16,
pub default_base: u16,
}
impl MemoryRegion {
pub fn normalize_base(&self, base: u16) -> u16 {
let aligned = base & 0xFFF0;
let max_base = self.max_addr.saturating_sub(0xFF).max(self.min_addr);
aligned.clamp(self.min_addr, max_base)
}
pub fn base_from_pc(&self, pc: u16) -> u16 {
if (self.min_addr..=self.max_addr).contains(&pc) {
let centered = pc & 0xFFF0;
let base = centered.saturating_sub(0x80);
self.normalize_base(base)
} else {
self.default_base
}
}
}
pub trait MemoryRegions: Default + Clone + PartialEq + Eq + core::fmt::Debug {
fn regions() -> &'static [MemoryRegion];
fn primary_region() -> &'static MemoryRegion {
Self::regions()
.first()
.expect("at least one region required")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_region_normalize_base() {
let region = MemoryRegion {
name: "WRAM",
min_addr: 0xC000,
max_addr: 0xDFFF,
default_base: 0xC000,
};
assert_eq!(region.normalize_base(0xC000), 0xC000);
assert_eq!(region.normalize_base(0xC100), 0xC100);
assert_eq!(region.normalize_base(0xC005), 0xC000);
assert_eq!(region.normalize_base(0xC01F), 0xC010);
assert_eq!(region.normalize_base(0xB000), 0xC000);
assert_eq!(region.normalize_base(0xDF10), 0xDF00);
}
#[test]
fn test_memory_region_base_from_pc() {
let region = MemoryRegion {
name: "WRAM",
min_addr: 0xC000,
max_addr: 0xDFFF,
default_base: 0xC000,
};
assert_eq!(region.base_from_pc(0xC100), 0xC080);
assert_eq!(region.base_from_pc(0x8000), 0xC000);
}
}