mirror of
https://github.com/transistorfet/moa.git
synced 2024-06-04 14:29:41 +00:00
Fixed debugger to easier debug one cpu at a time
Previously it would show a dump of whatever device was next scheduled to step, if it was Debuggable, but now each debuggable device has a flag for whether that device is being debugging, and a system flag to disable it entirely. When the system flag is set, it will try to enable debugging on the device labelled "cpu". I need to also add a way of setting a breakpoint on a named device, which will enable debugging of that device
This commit is contained in:
parent
5095aee531
commit
cbf91309d9
|
@ -28,6 +28,7 @@ impl StackTracer {
|
|||
|
||||
|
||||
pub struct M68kDebugger {
|
||||
pub enabled: bool,
|
||||
pub breakpoints: Vec<u32>,
|
||||
pub use_tracing: bool,
|
||||
pub step_until_return: Option<usize>,
|
||||
|
@ -37,6 +38,7 @@ pub struct M68kDebugger {
|
|||
impl M68kDebugger {
|
||||
pub fn new() -> M68kDebugger {
|
||||
M68kDebugger {
|
||||
enabled: false,
|
||||
breakpoints: vec!(),
|
||||
use_tracing: false,
|
||||
step_until_return: None,
|
||||
|
@ -46,13 +48,23 @@ impl M68kDebugger {
|
|||
}
|
||||
|
||||
impl Debuggable for M68k {
|
||||
fn debugging_enabled(&mut self) -> bool {
|
||||
self.debugger.enabled
|
||||
}
|
||||
|
||||
fn set_debugging(&mut self, enable: bool) {
|
||||
self.debugger.enabled = enable;
|
||||
}
|
||||
|
||||
fn add_breakpoint(&mut self, addr: Address) {
|
||||
self.debugger.breakpoints.push(addr as u32);
|
||||
self.debugger.enabled = true;
|
||||
}
|
||||
|
||||
fn remove_breakpoint(&mut self, addr: Address) {
|
||||
if let Some(index) = self.debugger.breakpoints.iter().position(|a| *a == addr as u32) {
|
||||
self.debugger.breakpoints.remove(index);
|
||||
self.debugger.enabled = !self.debugger.breakpoints.is_empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,7 @@ use super::state::{M68k, M68kType, Status, Flags, Exceptions, InterruptPriority}
|
|||
|
||||
impl Steppable for M68k {
|
||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
||||
self.step_internal(system)?;
|
||||
Ok((1_000_000_000 / self.frequency as u64) * self.timing.calculate_clocks(false, 1) as ClockElapsed)
|
||||
self.step_internal(system)
|
||||
}
|
||||
|
||||
fn on_error(&mut self, system: &System) {
|
||||
|
@ -60,18 +59,18 @@ impl M68k {
|
|||
self.state.status != Status::Stopped
|
||||
}
|
||||
|
||||
pub fn step_internal(&mut self, system: &System) -> Result<(), Error> {
|
||||
pub fn step_internal(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
||||
match self.state.status {
|
||||
Status::Init => self.init(),
|
||||
Status::Stopped => Err(Error::new("CPU stopped")),
|
||||
Status::Running => {
|
||||
match self.cycle_one(system) {
|
||||
Ok(()) => Ok(()),
|
||||
Ok(diff) => Ok(diff),
|
||||
//Err(Error { err: ErrorType::Processor, native, .. }) => {
|
||||
// TODO match arm conditional is temporary: illegal instructions generate a top level error in order to debug and fix issues with decode
|
||||
Err(Error { err: ErrorType::Processor, native, .. }) if native != Exceptions::IllegalInstruction as u32 => {
|
||||
self.exception(native as u8, false)?;
|
||||
Ok(())
|
||||
Ok(4)
|
||||
},
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
|
@ -79,14 +78,14 @@ impl M68k {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self) -> Result<(), Error> {
|
||||
pub fn init(&mut self) -> Result<ClockElapsed, Error> {
|
||||
self.state.ssp = self.port.read_beu32(0)?;
|
||||
self.state.pc = self.port.read_beu32(4)?;
|
||||
self.state.status = Status::Running;
|
||||
Ok(())
|
||||
Ok(16)
|
||||
}
|
||||
|
||||
pub fn cycle_one(&mut self, system: &System) -> Result<(), Error> {
|
||||
pub fn cycle_one(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
||||
self.timer.cycle.start();
|
||||
self.decode_next()?;
|
||||
self.execute_current()?;
|
||||
|
@ -98,7 +97,7 @@ impl M68k {
|
|||
|
||||
self.check_pending_interrupts(system)?;
|
||||
self.check_breakpoints(system);
|
||||
Ok(())
|
||||
Ok((1_000_000_000 / self.frequency as u64) * self.timing.calculate_clocks(false, 1) as ClockElapsed)
|
||||
}
|
||||
|
||||
pub fn check_pending_interrupts(&mut self, system: &System) -> Result<(), Error> {
|
||||
|
@ -114,7 +113,7 @@ impl M68k {
|
|||
let priority_mask = ((self.state.sr & Flags::IntMask as u16) >> 8) as u8;
|
||||
|
||||
if (pending_ipl > priority_mask || pending_ipl == 7) && pending_ipl >= current_ipl {
|
||||
debug!("{} interrupt: {} {}", DEV_NAME, pending_ipl, priority_mask);
|
||||
debug!("{} interrupt: {} @ {} ns", DEV_NAME, pending_ipl, system.clock);
|
||||
self.state.current_ipl = self.state.pending_ipl;
|
||||
let ack_num = system.get_interrupt_controller().acknowledge(self.state.current_ipl as u8)?;
|
||||
self.exception(ack_num, true)?;
|
||||
|
|
|
@ -8,25 +8,37 @@ use super::decode::Z80Decoder;
|
|||
|
||||
|
||||
pub struct Z80Debugger {
|
||||
pub enabled: bool,
|
||||
pub breakpoints: Vec<u16>,
|
||||
}
|
||||
|
||||
impl Z80Debugger {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
breakpoints: vec!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debuggable for Z80 {
|
||||
fn debugging_enabled(&mut self) -> bool {
|
||||
self.debugger.enabled
|
||||
}
|
||||
|
||||
fn set_debugging(&mut self, enable: bool) {
|
||||
self.debugger.enabled = enable;
|
||||
}
|
||||
|
||||
fn add_breakpoint(&mut self, addr: Address) {
|
||||
self.debugger.breakpoints.push(addr as u16);
|
||||
self.debugger.enabled = true;
|
||||
}
|
||||
|
||||
fn remove_breakpoint(&mut self, addr: Address) {
|
||||
if let Some(index) = self.debugger.breakpoints.iter().position(|a| *a == addr as u16) {
|
||||
self.debugger.breakpoints.remove(index);
|
||||
self.debugger.enabled = !self.debugger.breakpoints.is_empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -177,6 +177,7 @@ pub struct Z80Decoder {
|
|||
pub start: u16,
|
||||
pub end: u16,
|
||||
pub instruction: Instruction,
|
||||
pub execution_time: u16,
|
||||
}
|
||||
|
||||
impl Z80Decoder {
|
||||
|
@ -185,6 +186,7 @@ impl Z80Decoder {
|
|||
start: 0,
|
||||
end: 0,
|
||||
instruction: Instruction::NOP,
|
||||
execution_time: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -193,6 +195,7 @@ impl Z80Decoder {
|
|||
pub fn decode_at(&mut self, memory: &mut dyn Addressable, start: u16) -> Result<(), Error> {
|
||||
self.start = start;
|
||||
self.end = start;
|
||||
self.execution_time = 0;
|
||||
self.instruction = self.decode_one(memory)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -674,12 +677,14 @@ impl Z80Decoder {
|
|||
fn read_instruction_byte(&mut self, device: &mut dyn Addressable) -> Result<u8, Error> {
|
||||
let byte = device.read_u8(self.end as Address)?;
|
||||
self.end += 1;
|
||||
self.execution_time += 4;
|
||||
Ok(byte)
|
||||
}
|
||||
|
||||
fn read_instruction_word(&mut self, device: &mut dyn Addressable) -> Result<u16, Error> {
|
||||
let word = device.read_leu16(self.end as Address)?;
|
||||
self.end += 2;
|
||||
self.execution_time += 8;
|
||||
Ok(word)
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,13 @@ enum RotateType {
|
|||
impl Steppable for Z80 {
|
||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
||||
let clocks = if self.reset.get() {
|
||||
//println!("RESET");
|
||||
self.reset()?
|
||||
} else if self.bus_request.get() {
|
||||
1
|
||||
//println!("BUS REQ");
|
||||
4
|
||||
} else {
|
||||
//println!("RUNNING {:?}", self.decoder.instruction);
|
||||
self.step_internal(system)?
|
||||
};
|
||||
|
||||
|
@ -77,7 +80,7 @@ impl Z80 {
|
|||
pub fn init(&mut self) -> Result<u16, Error> {
|
||||
self.state.pc = 0;
|
||||
self.state.status = Status::Running;
|
||||
Ok(4)
|
||||
Ok(16)
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Result<u16, Error> {
|
||||
|
@ -90,7 +93,7 @@ impl Z80 {
|
|||
self.execute_current()?;
|
||||
//self.check_pending_interrupts(system)?;
|
||||
self.check_breakpoints(system);
|
||||
Ok(4)
|
||||
Ok(self.decoder.execution_time)
|
||||
}
|
||||
|
||||
pub fn decode_next(&mut self) -> Result<(), Error> {
|
||||
|
@ -878,17 +881,6 @@ fn sub_words(operand1: u16, operand2: u16) -> (u16, bool, bool) {
|
|||
(result, carry, overflow)
|
||||
}
|
||||
|
||||
|
||||
#[inline(always)]
|
||||
fn get_msb_byte(value: u8) -> bool {
|
||||
(value & 0x80) != 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_msb_word(value: u16) -> bool {
|
||||
(value & 0x8000) != 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_msb(value: u16, size: Size) -> bool {
|
||||
match size {
|
||||
|
|
|
@ -29,6 +29,7 @@ impl Debugger {
|
|||
pub fn run_debugger(&mut self, system: &System, target: TransmutableBox) -> Result<(), Error> {
|
||||
let mut target = target.borrow_mut();
|
||||
let debug_obj = target.as_debuggable().unwrap();
|
||||
println!("@ {} ns", system.clock);
|
||||
debug_obj.print_current_step(system)?;
|
||||
|
||||
if self.trace_only {
|
||||
|
|
|
@ -162,6 +162,8 @@ pub fn write_leu32(data: &mut [u8], value: u32) -> &mut [u8] {
|
|||
|
||||
/// A device (cpu) that can debugged using the built-in debugger
|
||||
pub trait Debuggable {
|
||||
fn debugging_enabled(&mut self) -> bool;
|
||||
fn set_debugging(&mut self, enable: bool);
|
||||
fn add_breakpoint(&mut self, addr: Address);
|
||||
fn remove_breakpoint(&mut self, addr: Address);
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ impl System {
|
|||
|
||||
pub fn enable_debugging(&self) {
|
||||
self.debug_enabled.set(true);
|
||||
self.devices.get("cpu").map(|result| result.try_borrow_mut().map(|mut borrow| borrow.as_debuggable().map(|debug| debug.set_debugging(true))));
|
||||
self.debugger.borrow_mut().breakpoint_occurred();
|
||||
}
|
||||
|
||||
|
@ -98,9 +99,7 @@ impl System {
|
|||
}
|
||||
|
||||
pub fn step(&mut self) -> Result<(), Error> {
|
||||
if self.debug_enabled.get() && self.event_queue[self.event_queue.len() - 1].device.borrow_mut().as_debuggable().is_some() {
|
||||
self.debugger.borrow_mut().run_debugger(&self, self.event_queue[self.event_queue.len() - 1].device.clone()).unwrap();
|
||||
}
|
||||
self.check_debugger();
|
||||
|
||||
match self.process_one_event() {
|
||||
Ok(()) => { }
|
||||
|
@ -151,6 +150,14 @@ impl System {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_debugger(&mut self) {
|
||||
if self.debug_enabled.get() {
|
||||
let top = self.event_queue[self.event_queue.len() - 1].device.clone();
|
||||
if top.borrow_mut().as_debuggable().map(|debug| debug.debugging_enabled()).unwrap_or(false) {
|
||||
self.debugger.borrow_mut().run_debugger(&self, top.clone()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_queue_device(&mut self, device: TransmutableBox) {
|
||||
if device.borrow_mut().as_steppable().is_some() {
|
||||
|
|
Loading…
Reference in New Issue
Block a user