Moved the debugger out of m68k

This commit is contained in:
transistor 2021-11-01 16:51:45 -07:00
parent a3a11f7459
commit 58fc9ac827
10 changed files with 194 additions and 133 deletions

View File

@ -74,7 +74,7 @@ impl MiniFrontend {
for key in keys {
match key {
Key::Enter => { modifiers |= 0xffff; },
Key::D => { system.get_interrupt_controller().target.as_ref().map(|target| target.borrow_mut().as_debuggable().unwrap().enable_debugging()); },
Key::D => { system.enable_debugging(); },
_ => { },
}
}

View File

@ -30,7 +30,6 @@ impl StackTracer {
pub struct M68kDebugger {
pub breakpoints: Vec<u32>,
pub use_tracing: bool,
pub use_debugger: bool,
pub step_until_return: Option<usize>,
pub stack_tracer: StackTracer,
}
@ -40,7 +39,6 @@ impl M68kDebugger {
M68kDebugger {
breakpoints: vec!(),
use_tracing: false,
use_debugger: false,
step_until_return: None,
stack_tracer: StackTracer::new(),
}
@ -48,14 +46,43 @@ impl M68kDebugger {
}
impl Debuggable for M68k {
fn enable_debugging(&mut self) {
self.debugger.use_tracing = true;
self.debugger.use_debugger = true;
}
fn add_breakpoint(&mut self, addr: Address) {
self.debugger.breakpoints.push(addr as u32);
}
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);
}
}
fn print_current_step(&mut self, system: &System) -> Result<(), Error> {
self.decoder.decode_at(&mut self.port, self.state.pc)?;
self.decoder.dump_decoded(&mut self.port);
self.dump_state(system);
Ok(())
}
fn print_disassembly(&mut self, addr: Address, count: usize) {
let mut decoder = M68kDecoder::new(self.cputype, 0);
decoder.dump_disassembly(&mut self.port, self.state.pc, 0x1000);
}
fn execute_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error> {
match args[0] {
"ds" | "stack" | "dumpstack" => {
println!("Stack:");
for addr in &self.debugger.stack_tracer.calls {
println!(" {:08x}", self.port.read_beu32(*addr as Address)?);
}
},
"so" | "stepout" => {
self.debugger.step_until_return = Some(self.debugger.stack_tracer.calls.len() - 1);
},
_ => { return Ok(true); },
}
Ok(false)
}
}
impl M68k {
@ -64,85 +91,14 @@ impl M68k {
self.debugger.use_tracing = true;
}
pub fn check_breakpoints(&mut self) {
pub fn check_breakpoints(&mut self, system: &System) {
for breakpoint in &self.debugger.breakpoints {
if *breakpoint == self.state.pc {
println!("Breakpoint reached: {:08x}", *breakpoint);
self.enable_debugging();
system.enable_debugging();
break;
}
}
}
pub fn run_debugger(&mut self, system: &System) {
self.dump_state(system);
match self.debugger.step_until_return {
Some(level) if level == self.debugger.stack_tracer.calls.len() => { self.debugger.step_until_return = None; },
Some(_) => { return; },
None => { },
}
loop {
let mut buffer = String::new();
std::io::stdin().read_line(&mut buffer).unwrap();
let args: Vec<&str> = buffer.split_whitespace().collect();
match self.run_debugger_command(system, args) {
Ok(true) => return,
Ok(false) => { },
Err(err) => {
println!("Error: {}", err.msg);
},
}
}
}
pub fn run_debugger_command(&mut self, system: &System, args: Vec<&str>) -> Result<bool, Error> {
if args.len() == 0 {
return Ok(true);
}
match args[0] {
"b" | "break" | "breakpoint" => {
if args.len() != 2 {
println!("Usage: breakpoint <addr>");
} else {
let addr = u32::from_str_radix(args[1], 16).map_err(|_| Error::new("Unable to parse breakpoint address"))?;
self.add_breakpoint(addr as Address);
println!("Breakpoint set for {:08x}", addr);
}
},
"d" | "dump" => {
if args.len() > 1 {
let addr = u32::from_str_radix(args[1], 16).map_err(|_| Error::new("Unable to parse address"))?;
let len = if args.len() > 2 { u32::from_str_radix(args[2], 16).map_err(|_| Error::new("Unable to parse length"))? } else { 0x20 };
self.port.dump_memory(addr as Address, len as Address);
} else {
self.port.dump_memory(self.state.msp as Address, 0x40 as Address);
}
},
"ds" | "stack" | "dumpstack" => {
println!("Stack:");
for addr in &self.debugger.stack_tracer.calls {
println!(" {:08x}", self.port.read_beu32(*addr as Address)?);
}
},
"dis" | "disassemble" => {
let mut decoder = M68kDecoder::new(self.cputype, 0);
decoder.dump_disassembly(&mut self.port, self.state.pc, 0x1000);
},
"so" | "stepout" => {
self.debugger.step_until_return = Some(self.debugger.stack_tracer.calls.len() - 1);
return Ok(true);
},
"c" | "continue" => {
self.debugger.use_tracing = false;
self.debugger.use_debugger = false;
return Ok(true);
},
_ => { return Ok(true); },
}
Ok(false)
}
}

View File

@ -35,10 +35,6 @@ impl Steppable for M68k {
fn on_error(&mut self, system: &System) {
self.dump_state(system);
}
fn on_debug(&mut self) {
self.enable_debugging();
}
}
impl Interruptable for M68k { }
@ -153,7 +149,7 @@ impl M68k {
}
pub fn decode_next(&mut self, system: &System) -> Result<(), Error> {
self.check_breakpoints();
self.check_breakpoints(system);
self.timer.decode.start();
self.decoder.decode_at(&mut self.port, self.state.pc)?;
@ -163,10 +159,6 @@ impl M68k {
self.decoder.dump_decoded(&mut self.port);
}
if self.debugger.use_debugger {
self.run_debugger(system);
}
self.state.pc = self.decoder.end;
Ok(())
}
@ -406,7 +398,11 @@ impl M68k {
self.require_supervisor()?;
self.state.sr = self.state.sr ^ value;
},
//Instruction::EXG(Target, Target) => {
//Instruction::EXG(target1, target2) => {
// let value1 = self.get_target_value(system, target1, Size::Long)?;
// let value2 = self.get_target_value(system, target2, Size::Long)?;
// self.set_target_value(system, target1, value2, Size::Long)?;
// self.set_target_value(system, target2, value1, Size::Long)?;
//},
Instruction::EXT(reg, from_size, to_size) => {
let input = self.state.d_reg[reg as usize];

102
src/debugger.rs Normal file
View File

@ -0,0 +1,102 @@
use crate::error::Error;
use crate::system::System;
use crate::devices::{Address, Debuggable, TransmutableBox};
/*
pub struct StackTracer {
pub calls: Vec<u32>,
}
impl StackTracer {
pub fn new() -> StackTracer {
StackTracer {
calls: vec![],
}
}
pub fn push_return(&mut self, addr: u32) {
self.calls.push(addr);
}
pub fn pop_return(&mut self) {
self.calls.pop();
}
}
*/
pub struct Debugger;
impl Debugger {
pub fn run_debugger(system: &System, target: TransmutableBox) -> Result<(), Error> {
let mut target = target.borrow_mut();
let debug_obj = target.as_debuggable().unwrap();
debug_obj.print_current_step(system)?;
loop {
let mut buffer = String::new();
std::io::stdin().read_line(&mut buffer).unwrap();
let args: Vec<&str> = buffer.split_whitespace().collect();
match Debugger::run_debugger_command(system, debug_obj, &args) {
Ok(true) => return Ok(()),
Ok(false) => { },
Err(err) => {
println!("Error: {}", err.msg);
},
}
}
}
pub fn run_debugger_command(system: &System, debug_obj: &mut dyn Debuggable, args: &[&str]) -> Result<bool, Error> {
if args.len() == 0 {
return Ok(true);
}
match args[0] {
"b" | "break" | "breakpoint" => {
if args.len() != 2 {
println!("Usage: breakpoint <addr>");
} else {
let addr = u32::from_str_radix(args[1], 16).map_err(|_| Error::new("Unable to parse breakpoint address"))?;
debug_obj.add_breakpoint(addr as Address);
println!("Breakpoint set for {:08x}", addr);
}
},
"c" | "continue" => {
system.disable_debugging();
return Ok(true);
},
"d" | "dump" => {
if args.len() > 1 {
let addr = u32::from_str_radix(args[1], 16).map_err(|_| Error::new("Unable to parse address"))?;
let len = if args.len() > 2 { u32::from_str_radix(args[2], 16).map_err(|_| Error::new("Unable to parse length"))? } else { 0x20 };
system.get_bus().dump_memory(addr as Address, len as Address);
} else {
//self.port.dump_memory(self.state.msp as Address, 0x40 as Address);
}
},
"dis" | "disassemble" => {
debug_obj.print_disassembly(0, 0);
},
//"ds" | "stack" | "dumpstack" => {
// println!("Stack:");
// for addr in &self.debugger.stack_tracer.calls {
// println!(" {:08x}", self.port.read_beu32(*addr as Address)?);
// }
//},
//"so" | "stepout" => {
// self.debugger.step_until_return = Some(self.debugger.stack_tracer.calls.len() - 1);
// return Ok(true);
//},
_ => {
if debug_obj.execute_command(system, args)? {
return Ok(true);
}
},
}
Ok(false)
}
}

View File

@ -25,16 +25,23 @@ pub type Address = u64;
pub trait Steppable {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error>;
fn on_error(&mut self, _system: &System) { }
fn on_debug(&mut self) { }
}
/// A device that can receive an interrupt. The `interrupt_state_change()` method
/// will be called whenever an interrupt signal changes goes high or low.
pub trait Interruptable {
//fn interrupt_state_change(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error>;
}
/// A device that can debugged using the built-in debugger
pub trait Debuggable {
fn add_breakpoint(&mut self, addr: Address);
fn remove_breakpoint(&mut self, addr: Address);
fn print_current_step(&mut self, system: &System) -> Result<(), Error>;
fn print_disassembly(&mut self, addr: Address, count: usize);
fn execute_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error>;
}
/// A device that can be addressed to read data from or write data to the device.
pub trait Addressable {
@ -108,12 +115,6 @@ pub fn write_beu32(value: u32) -> [u8; 4] {
]
}
/// A device that can debugged by putting it into debug mode, or setting breakpoints
pub trait Debuggable {
fn enable_debugging(&mut self);
fn add_breakpoint(&mut self, addr: Address);
}
pub trait Transmutable {
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {

View File

@ -5,6 +5,7 @@ pub mod memory;
pub mod timers;
pub mod signals;
pub mod devices;
pub mod debugger;
pub mod interrupts;
pub mod system;

View File

@ -35,7 +35,7 @@ pub fn build_computie<H: Host>(host: &H) -> Result<System, Error> {
let mut cpu = M68k::new(M68kType::MC68010, 10_000_000, BusPort::new(0, 24, 16, system.bus.clone()));
//cpu.enable_tracing();
//cpu.add_breakpoint(0x10781a);
cpu.add_breakpoint(0x10781a);
//cpu.add_breakpoint(0x10bc9c);
//cpu.add_breakpoint(0x106a94);
//cpu.add_breakpoint(0x1015b2);
@ -45,7 +45,7 @@ pub fn build_computie<H: Host>(host: &H) -> Result<System, Error> {
cpu.add_breakpoint(0);
system.add_interruptable_device(wrap_transmutable(cpu))?;
system.add_interruptable_device("cpu", wrap_transmutable(cpu))?;
Ok(system)
}
@ -81,7 +81,7 @@ pub fn build_computie_k30<H: Host>(host: &H) -> Result<System, Error> {
//cpu.decoder.dump_disassembly(&mut system, 0x100000, 0x2000);
//cpu.decoder.dump_disassembly(&mut system, 0x2ac, 0x200);
system.add_interruptable_device(wrap_transmutable(cpu))?;
system.add_interruptable_device("cpu", wrap_transmutable(cpu))?;
Ok(system)
}

View File

@ -51,7 +51,7 @@ pub fn build_genesis<H: Host>(host: &H) -> Result<System, Error> {
//cpu.add_breakpoint(0x16ee);
//cpu.decoder.dump_disassembly(&mut system, 0x206, 0x2000);
system.add_interruptable_device(wrap_transmutable(cpu)).unwrap();
system.add_interruptable_device("cpu", wrap_transmutable(cpu)).unwrap();
Ok(system)
}

View File

@ -1,18 +1,21 @@
use std::rc::Rc;
use std::cell::{RefCell, RefMut};
use std::cell::{Cell, RefCell, RefMut};
use std::collections::HashMap;
use crate::error::Error;
use crate::memory::Bus;
use crate::error::Error;
use crate::debugger::Debugger;
use crate::interrupts::InterruptController;
use crate::devices::{Clock, ClockElapsed, Address, TransmutableBox};
pub struct System {
pub clock: Clock,
pub devices: Vec<TransmutableBox>,
pub event_queue: Vec<SteppableDevice>,
pub devices: HashMap<String, TransmutableBox>,
pub event_queue: Vec<DeviceStep>,
pub debug_enabled: Cell<bool>,
pub bus: Rc<RefCell<Bus>>,
pub interrupt_controller: RefCell<InterruptController>,
}
@ -21,8 +24,10 @@ impl System {
pub fn new() -> System {
System {
clock: 0,
devices: vec![],
devices: HashMap::new(),
event_queue: vec![],
debug_enabled: Cell::new(false),
bus: Rc::new(RefCell::new(Bus::new())),
interrupt_controller: RefCell::new(InterruptController::new()),
}
@ -40,17 +45,25 @@ impl System {
let length = device.borrow_mut().as_addressable().unwrap().len();
self.bus.borrow_mut().insert(addr, length, device.clone());
self.try_queue_device(device.clone());
self.devices.push(device);
self.devices.insert(format!("ram{:x}", addr), device);
Ok(())
}
pub fn add_interruptable_device(&mut self, device: TransmutableBox) -> Result<(), Error> {
pub fn add_interruptable_device(&mut self, name: &str, device: TransmutableBox) -> Result<(), Error> {
self.interrupt_controller.borrow_mut().set_target(device.clone())?;
self.try_queue_device(device.clone());
self.devices.push(device);
self.devices.insert(name.to_string(), device);
Ok(())
}
pub fn enable_debugging(&self) {
self.debug_enabled.set(true);
}
pub fn disable_debugging(&self) {
self.debug_enabled.set(false);
}
pub fn step(&mut self) -> Result<(), Error> {
let mut event_device = self.event_queue.pop().unwrap();
self.clock = event_device.next_clock;
@ -63,6 +76,10 @@ impl System {
let target = self.clock + clocks;
while self.clock < target {
if self.debug_enabled.get() && self.event_queue[self.event_queue.len() - 1].device.borrow_mut().as_debuggable().is_some() {
Debugger::run_debugger(&self, self.event_queue[self.event_queue.len() - 1].device.clone()).unwrap();
}
match self.step() {
Ok(()) => { }
Err(err) => {
@ -80,7 +97,7 @@ impl System {
}
pub fn exit_error(&mut self) {
for dev in &self.devices {
for (_, dev) in self.devices.iter() {
match dev.borrow_mut().as_steppable() {
Some(dev) => dev.on_error(&self),
None => { },
@ -88,41 +105,31 @@ impl System {
}
}
pub fn debug(&mut self) -> Result<(), Error> {
for dev in &self.devices {
match dev.borrow_mut().as_steppable() {
Some(dev) => dev.on_debug(),
None => { },
}
}
Ok(())
}
fn try_queue_device(&mut self, device: TransmutableBox) {
if device.borrow_mut().as_steppable().is_some() {
self.queue_device(SteppableDevice::new(device));
self.queue_device(DeviceStep::new(device));
}
}
fn queue_device(&mut self, event_device: SteppableDevice) {
fn queue_device(&mut self, device_step: DeviceStep) {
for i in (0..self.event_queue.len()).rev() {
if self.event_queue[i].next_clock > event_device.next_clock {
self.event_queue.insert(i + 1, event_device);
if self.event_queue[i].next_clock > device_step.next_clock {
self.event_queue.insert(i + 1, device_step);
return;
}
}
self.event_queue.insert(0, event_device);
self.event_queue.insert(0, device_step);
}
}
pub struct SteppableDevice {
pub struct DeviceStep {
pub next_clock: Clock,
pub device: TransmutableBox,
}
impl SteppableDevice {
impl DeviceStep {
pub fn new(device: TransmutableBox) -> Self {
Self {
next_clock: 0,

View File

@ -1,10 +1,8 @@
* fix ym7101 to better handle V/H interrupts (right now it sets and then the next step will clear, but it'd be nice if it could 'edge trigger')
* how do you do the external interrupt, which is controlled by the YM7101 but the ... input comes from the controllers. I suppose it could actually come through the updater,
the video would have a reference to the controllers even... I don't really like config details in the sub devices, so is there a way to use JoystickUpdater or something like
it, such that the YM7101 would be given two generic joystick objects, which are also passed to or produced by the controller impl, so you could in theory swap the controllers out
and the YM7101 should use them just the same
* could have a remapper device, which takes a big swath of addresses in and maps them to another set of addresses (for Mac VIA generic to bus-hookup-in-mac adapter)
* sort out how inputing keys will work
* separate the debugger out of m68k, use on_debug or something to print out debug info
* make devices nameable, using a hashmap to store them