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:
transistor 2021-11-03 15:33:22 -07:00
parent bd5a798fa1
commit b6cccea437
6 changed files with 99 additions and 21 deletions

View File

@ -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]);
}
},
}

View File

@ -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 }

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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);

View File

@ -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)