mirror of
https://github.com/transistorfet/moa.git
synced 2025-04-06 11:38:21 +00:00
Added read only memory and debugger numbered continuations
There is also a breakpoint error, so that if a read-only memory location is written to, it will escape to the debugger rather than exiting the program.
This commit is contained in:
parent
bd5a798fa1
commit
b6cccea437
@ -26,20 +26,38 @@ impl StackTracer {
|
||||
}
|
||||
*/
|
||||
|
||||
pub struct Debugger;
|
||||
pub struct Debugger {
|
||||
pub last_command: Option<String>,
|
||||
pub repeat: u32,
|
||||
}
|
||||
|
||||
|
||||
impl Debugger {
|
||||
pub fn run_debugger(system: &System, target: TransmutableBox) -> Result<(), Error> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
last_command: None,
|
||||
repeat: 0,
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
debug_obj.print_current_step(system)?;
|
||||
|
||||
if self.repeat > 0 {
|
||||
self.repeat -= 1;
|
||||
let last_command = self.last_command.clone().unwrap();
|
||||
let args: Vec<&str> = vec![&last_command];
|
||||
self.run_debugger_command(system, debug_obj, &args)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
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) {
|
||||
match self.run_debugger_command(system, debug_obj, &args) {
|
||||
Ok(true) => return Ok(()),
|
||||
Ok(false) => { },
|
||||
Err(err) => {
|
||||
@ -49,8 +67,9 @@ impl Debugger {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_debugger_command(system: &System, debug_obj: &mut dyn Debuggable, args: &[&str]) -> Result<bool, Error> {
|
||||
pub fn run_debugger_command(&mut self, system: &System, debug_obj: &mut dyn Debuggable, args: &[&str]) -> Result<bool, Error> {
|
||||
if args.len() == 0 {
|
||||
// The Default Command
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
@ -65,6 +84,11 @@ impl Debugger {
|
||||
}
|
||||
},
|
||||
"c" | "continue" => {
|
||||
if args.len() > 1 {
|
||||
self.repeat = u32::from_str_radix(args[1], 10).map_err(|_| Error::new("Unable to parse repeat number"))?;
|
||||
self.last_command = Some("c".to_string());
|
||||
}
|
||||
|
||||
system.disable_debugging();
|
||||
return Ok(true);
|
||||
},
|
||||
@ -80,6 +104,9 @@ impl Debugger {
|
||||
"dis" | "disassemble" => {
|
||||
debug_obj.print_disassembly(0, 0);
|
||||
},
|
||||
"s" | "step" => {
|
||||
return Ok(true);
|
||||
},
|
||||
//"ds" | "stack" | "dumpstack" => {
|
||||
// println!("Stack:");
|
||||
// for addr in &self.debugger.stack_tracer.calls {
|
||||
@ -92,7 +119,7 @@ impl Debugger {
|
||||
//},
|
||||
_ => {
|
||||
if debug_obj.execute_command(system, args)? {
|
||||
return Ok(true);
|
||||
println!("Error: unknown command {}", args[0]);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
13
src/error.rs
13
src/error.rs
@ -1,8 +1,9 @@
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ErrorType {
|
||||
Emulator,
|
||||
Processor,
|
||||
Breakpoint,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -28,6 +29,14 @@ impl Error {
|
||||
msg: "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn breakpoint(msg: &str) -> Error {
|
||||
Error {
|
||||
err: ErrorType::Breakpoint,
|
||||
native: 0,
|
||||
msg: msg.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +48,7 @@ pub enum LogLevel {
|
||||
Debug,
|
||||
}
|
||||
|
||||
static mut LOG_LEVEL: LogLevel = LogLevel::Debug;
|
||||
static mut LOG_LEVEL: LogLevel = LogLevel::Info;
|
||||
|
||||
pub fn log_level() -> LogLevel {
|
||||
unsafe { LOG_LEVEL }
|
||||
|
@ -13,15 +13,18 @@ use crate::host::traits::{Host, WindowUpdater};
|
||||
pub fn build_genesis<H: Host>(host: &H) -> Result<System, Error> {
|
||||
let mut system = System::new();
|
||||
|
||||
//let rom = MemoryBlock::load("binaries/genesis/Sonic The Hedgehog (W) (REV 00) [!].bin").unwrap();
|
||||
//let rom = MemoryBlock::load("binaries/genesis/Sonic The Hedgehog (W) (REV 01) [!].bin").unwrap();
|
||||
let rom = MemoryBlock::load("binaries/genesis/Earthworm Jim (U) [h1].bin").unwrap();
|
||||
//let rom = MemoryBlock::load("binaries/genesis/Home Alone (beta).bin").unwrap();
|
||||
//let rom = MemoryBlock::load("binaries/genesis/F1 World Championship (JUE) [!].bin").unwrap();
|
||||
//let rom = MemoryBlock::load("binaries/genesis/Ren and Stimpy's Invention (U) [!].bin").unwrap();
|
||||
//let rom = MemoryBlock::load("binaries/genesis/Out of this World (U) [!].bin").unwrap();
|
||||
//let rom = MemoryBlock::load("binaries/genesis/Ghostbusters (REV 00) (JUE).bin").unwrap();
|
||||
//let rom = MemoryBlock::load("binaries/genesis/Teenage Mutant Ninja Turtles - The Hyperstone Heist (U) [!].bin").unwrap();
|
||||
//let mut rom = MemoryBlock::load("binaries/genesis/Sonic The Hedgehog (W) (REV 00) [!].bin").unwrap();
|
||||
//let mut rom = MemoryBlock::load("binaries/genesis/Sonic The Hedgehog (W) (REV 01) [!].bin").unwrap();
|
||||
let mut rom = MemoryBlock::load("binaries/genesis/Sonic the Hedgehog 2 (JUE) [!].bin").unwrap();
|
||||
//let mut rom = MemoryBlock::load("binaries/genesis/Sonic the Hedgehog 3 (U) [!].bin").unwrap();
|
||||
//let mut rom = MemoryBlock::load("binaries/genesis/Earthworm Jim (U) [h1].bin").unwrap();
|
||||
//let mut rom = MemoryBlock::load("binaries/genesis/Home Alone (beta).bin").unwrap();
|
||||
//let mut mut rom = MemoryBlock::load("binaries/genesis/F1 World Championship (JUE) [!].bin").unwrap();
|
||||
//let mut rom = MemoryBlock::load("binaries/genesis/Ren and Stimpy's Invention (U) [!].bin").unwrap();
|
||||
//let mut rom = MemoryBlock::load("binaries/genesis/Out of this World (U) [!].bin").unwrap();
|
||||
//let mut rom = MemoryBlock::load("binaries/genesis/Ghostbusters (REV 00) (JUE).bin").unwrap();
|
||||
//let mut rom = MemoryBlock::load("binaries/genesis/Teenage Mutant Ninja Turtles - The Hyperstone Heist (U) [!].bin").unwrap();
|
||||
rom.read_only();
|
||||
system.add_addressable_device(0x00000000, wrap_transmutable(rom)).unwrap();
|
||||
|
||||
let ram = MemoryBlock::new(vec![0; 0x00010000]);
|
||||
@ -47,10 +50,17 @@ pub fn build_genesis<H: Host>(host: &H) -> Result<System, Error> {
|
||||
let mut cpu = M68k::new(M68kType::MC68000, 7_670_454, BusPort::new(0, 24, 16, system.bus.clone()));
|
||||
|
||||
//cpu.enable_tracing();
|
||||
//cpu.add_breakpoint(0x206);
|
||||
//cpu.add_breakpoint(0x1dd0); // Sonic: some kind of palette fading function
|
||||
//cpu.add_breakpoint(0x16ee);
|
||||
//cpu.decoder.dump_disassembly(&mut system, 0x206, 0x2000);
|
||||
|
||||
//cpu.add_breakpoint(0x16a0e);
|
||||
//cpu.add_breakpoint(0x16812);
|
||||
//cpu.add_breakpoint(0x166ec);
|
||||
cpu.add_breakpoint(0x13e18);
|
||||
cpu.add_breakpoint(0x16570);
|
||||
|
||||
system.add_interruptable_device("cpu", wrap_transmutable(cpu)).unwrap();
|
||||
|
||||
Ok(system)
|
||||
|
@ -8,12 +8,14 @@ use crate::devices::{Address, Addressable, Transmutable, TransmutableBox, MAX_RE
|
||||
|
||||
|
||||
pub struct MemoryBlock {
|
||||
pub read_only: bool,
|
||||
pub contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl MemoryBlock {
|
||||
pub fn new(contents: Vec<u8>) -> MemoryBlock {
|
||||
MemoryBlock {
|
||||
read_only: false,
|
||||
contents
|
||||
}
|
||||
}
|
||||
@ -37,6 +39,10 @@ impl MemoryBlock {
|
||||
Err(_) => Err(Error::new(&format!("Error reading contents of {}", filename))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_only(&mut self) {
|
||||
self.read_only = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl Addressable for MemoryBlock {
|
||||
@ -52,6 +58,10 @@ impl Addressable for MemoryBlock {
|
||||
}
|
||||
|
||||
fn write(&mut self, mut addr: Address, data: &[u8]) -> Result<(), Error> {
|
||||
if self.read_only {
|
||||
return Err(Error::breakpoint(&format!("Attempt to write to read-only memory at {:x} with data {:?}", addr, data)));
|
||||
}
|
||||
|
||||
for byte in data {
|
||||
self.contents[addr as usize] = *byte;
|
||||
addr += 1;
|
||||
@ -172,7 +182,7 @@ impl BusPort {
|
||||
}
|
||||
|
||||
pub fn dump_memory(&mut self, mut addr: Address, mut count: Address) {
|
||||
self.subdevice.borrow_mut().dump_memory(addr, count)
|
||||
self.subdevice.borrow_mut().dump_memory(self.offset + (addr & self.address_mask), count)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,8 @@ use std::cell::{Cell, RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::memory::Bus;
|
||||
use crate::error::Error;
|
||||
use crate::debugger::Debugger;
|
||||
use crate::error::{Error, ErrorType};
|
||||
use crate::interrupts::InterruptController;
|
||||
use crate::devices::{Clock, ClockElapsed, Address, TransmutableBox};
|
||||
|
||||
@ -16,6 +16,8 @@ pub struct System {
|
||||
pub event_queue: Vec<DeviceStep>,
|
||||
|
||||
pub debug_enabled: Cell<bool>,
|
||||
pub debugger: RefCell<Debugger>,
|
||||
|
||||
pub bus: Rc<RefCell<Bus>>,
|
||||
pub interrupt_controller: RefCell<InterruptController>,
|
||||
}
|
||||
@ -28,6 +30,8 @@ impl System {
|
||||
event_queue: vec![],
|
||||
|
||||
debug_enabled: Cell::new(false),
|
||||
debugger: RefCell::new(Debugger::new()),
|
||||
|
||||
bus: Rc::new(RefCell::new(Bus::new())),
|
||||
interrupt_controller: RefCell::new(InterruptController::new()),
|
||||
}
|
||||
@ -67,9 +71,15 @@ impl System {
|
||||
pub fn step(&mut self) -> Result<(), Error> {
|
||||
let mut event_device = self.event_queue.pop().unwrap();
|
||||
self.clock = event_device.next_clock;
|
||||
event_device.next_clock = self.clock + event_device.device.borrow_mut().as_steppable().unwrap().step(&self)?;
|
||||
let result = match event_device.device.borrow_mut().as_steppable().unwrap().step(&self) {
|
||||
Ok(diff) => {
|
||||
event_device.next_clock = self.clock + diff;
|
||||
Ok(())
|
||||
},
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
self.queue_device(event_device);
|
||||
Ok(())
|
||||
result
|
||||
}
|
||||
|
||||
pub fn run_for(&mut self, clocks: Clock) -> Result<(), Error> {
|
||||
@ -77,11 +87,15 @@ impl System {
|
||||
|
||||
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();
|
||||
self.debugger.borrow_mut().run_debugger(&self, self.event_queue[self.event_queue.len() - 1].device.clone()).unwrap();
|
||||
}
|
||||
|
||||
match self.step() {
|
||||
Ok(()) => { }
|
||||
Err(err) if err.err == ErrorType::Breakpoint => {
|
||||
println!("Breakpoint reached: {}", err.msg);
|
||||
self.enable_debugging();
|
||||
},
|
||||
Err(err) => {
|
||||
self.exit_error();
|
||||
println!("{:?}", err);
|
||||
|
8
todo.txt
8
todo.txt
@ -1,4 +1,12 @@
|
||||
|
||||
At 0x16cde, a move writes an invalid value to 0 via an indirect %a2 reg. The value of the reg might have changed during an interrupt, but it definitely breaks when the next interrupt occurs
|
||||
Before the loop is 0x16a0e which then calculates the count and such
|
||||
0x16584 is where the memory address 0xffd11a is updated, which is then used for the bad 0x0000 address which causes the improper write. 0x16570 is a better start
|
||||
On broken cycle: %a1 = 1df40, moves that location + 1 to %d0
|
||||
|
||||
|
||||
* there is a problem where something writes to the rom area which causes a crash
|
||||
|
||||
* 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')
|
||||
|
||||
* 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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user