mirror of
https://github.com/transistorfet/moa.git
synced 2024-11-06 12:07:12 +00:00
Modified debugger so the input is in the frontend
The debug loop that reads a command and does something is part of the frontend's main loop, so that it can potentially update, even though it doesn't actually work for minifb because the command input is a blocking call. It's also not implemented in the pixels frontend. At some point I'll make a web frontend.
This commit is contained in:
parent
18d1912f9a
commit
3bd4c24ea8
107
Cargo.lock
generated
107
Cargo.lock
generated
@ -48,6 +48,55 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is-terminal",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@ -206,7 +255,7 @@ dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"clap_lex 0.2.4",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"strsim 0.10.0",
|
||||
@ -214,6 +263,28 @@ dependencies = [
|
||||
"textwrap 0.16.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"bitflags",
|
||||
"clap_lex 0.5.0",
|
||||
"strsim 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.2.25"
|
||||
@ -236,6 +307,18 @@ dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.0"
|
||||
@ -469,6 +552,18 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.6"
|
||||
@ -641,7 +736,7 @@ dependencies = [
|
||||
name = "moa_console"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap 3.2.25",
|
||||
"clap 4.3.3",
|
||||
"log",
|
||||
"moa_common",
|
||||
"moa_core",
|
||||
@ -672,7 +767,7 @@ dependencies = [
|
||||
name = "moa_minifb"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap 3.2.25",
|
||||
"clap 4.3.3",
|
||||
"log",
|
||||
"minifb",
|
||||
"moa_common",
|
||||
@ -1507,6 +1602,12 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
|
@ -15,4 +15,5 @@ default-members = ["emulator/frontends/minifb"]
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
#overflow-checks = true
|
||||
|
||||
|
@ -24,6 +24,7 @@ pub struct ClockDuration {
|
||||
|
||||
impl ClockDuration {
|
||||
pub const ZERO: Self = Self::from_femtos(0);
|
||||
pub const MAX: Self = Self::from_femtos(Femtos::MAX);
|
||||
|
||||
pub const FEMTOS_PER_SEC: Femtos = 1_000_000_000_000_000;
|
||||
pub const FEMTOS_PER_MILLISEC: Femtos = 1_000_000_000_000;
|
||||
@ -207,6 +208,7 @@ pub struct ClockTime(ClockDuration);
|
||||
|
||||
impl ClockTime {
|
||||
pub const START: Self = Self(ClockDuration::ZERO);
|
||||
pub const FOREVER: Self = Self(ClockDuration::MAX);
|
||||
|
||||
#[inline]
|
||||
pub const fn as_duration(self) -> ClockDuration {
|
||||
|
@ -1,15 +1,23 @@
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::system::System;
|
||||
use crate::devices::{Address, Addressable, Debuggable, Device};
|
||||
use crate::devices::{Address, Addressable};
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum DebugControl {
|
||||
/// Wait for the next user command
|
||||
Wait,
|
||||
/// Continue looping without accepting input
|
||||
Continue,
|
||||
/// Exit the debugger and return to normal operation
|
||||
Exit,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Debugger {
|
||||
last_command: Option<String>,
|
||||
repeat: u32,
|
||||
repeat_command: Option<(u32, String)>,
|
||||
trace_only: bool,
|
||||
}
|
||||
|
||||
@ -19,45 +27,43 @@ impl Debugger {
|
||||
self.trace_only = false;
|
||||
}
|
||||
|
||||
pub fn run_debugger(&mut self, system: &System, target: Device) -> Result<(), Error> {
|
||||
let mut target = target.borrow_mut();
|
||||
let debug_obj = target.as_debuggable().unwrap();
|
||||
pub fn print_step(&mut self, system: &mut System) -> Result<(), Error> {
|
||||
println!("@ {} ns", system.clock.as_duration().as_nanos());
|
||||
debug_obj.print_current_step(system)?;
|
||||
|
||||
if self.trace_only {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
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::stdout().write_all(b"> ").unwrap();
|
||||
std::io::stdin().read_line(&mut buffer).unwrap();
|
||||
let args: Vec<&str> = buffer.split_whitespace().collect();
|
||||
match self.run_debugger_command(system, debug_obj, &args) {
|
||||
Ok(true) => return Ok(()),
|
||||
Ok(false) => { },
|
||||
Err(err) => {
|
||||
println!("Error: {}", err.msg);
|
||||
},
|
||||
}
|
||||
if let Some(device) = system.get_next_debuggable_device() {
|
||||
device.borrow_mut().as_debuggable().unwrap().print_current_step(system)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_debugger_command(&mut self, system: &System, debug_obj: &mut dyn Debuggable, args: &[&str]) -> Result<bool, Error> {
|
||||
if args.is_empty() {
|
||||
// The Default Command
|
||||
return Ok(true);
|
||||
pub fn check_auto_command(&mut self, system: &mut System) -> Result<DebugControl, Error> {
|
||||
if self.trace_only {
|
||||
return Ok(DebugControl::Continue);
|
||||
}
|
||||
|
||||
if let Some((count, command)) = self.repeat_command.take() {
|
||||
self.run_command(system, &command)?;
|
||||
let next_count = count - 1;
|
||||
if next_count == 0 {
|
||||
self.repeat_command = None;
|
||||
} else {
|
||||
self.repeat_command = Some((next_count, command));
|
||||
}
|
||||
return Ok(DebugControl::Continue);
|
||||
}
|
||||
|
||||
Ok(DebugControl::Wait)
|
||||
}
|
||||
|
||||
pub fn run_command(&mut self, system: &mut System, command: &str) -> Result<DebugControl, Error> {
|
||||
let args: Vec<&str> = command.split_whitespace().collect();
|
||||
|
||||
// If no command given, then run the `step` command
|
||||
let args = if args.is_empty() {
|
||||
vec!["step"]
|
||||
} else {
|
||||
args
|
||||
};
|
||||
|
||||
match args[0] {
|
||||
"b" | "break" | "breakpoint" => {
|
||||
if args.len() != 2 {
|
||||
@ -71,8 +77,10 @@ impl Debugger {
|
||||
println!("Breakpoint set for devices {:?} at {:08x}", name, addr);
|
||||
},
|
||||
None => {
|
||||
debug_obj.add_breakpoint(addr);
|
||||
println!("Breakpoint set for {:08x}", addr);
|
||||
if let Some(device) = system.get_next_debuggable_device() {
|
||||
device.borrow_mut().as_debuggable().unwrap().add_breakpoint(addr);
|
||||
println!("Breakpoint set for {:08x}", addr);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -89,8 +97,10 @@ impl Debugger {
|
||||
println!("Breakpoint removed for devices {:?} at {:08x}", name, addr);
|
||||
},
|
||||
None => {
|
||||
debug_obj.remove_breakpoint(addr);
|
||||
println!("Breakpoint removed for {:08x}", addr);
|
||||
if let Some(device) = system.get_next_debuggable_device() {
|
||||
device.borrow_mut().as_debuggable().unwrap().remove_breakpoint(addr);
|
||||
println!("Breakpoint removed for {:08x}", addr);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -145,20 +155,23 @@ impl Debugger {
|
||||
0x1000
|
||||
};
|
||||
|
||||
debug_obj.print_disassembly(addr, count);
|
||||
if let Some(device) = system.get_next_debuggable_device() {
|
||||
device.borrow_mut().as_debuggable().unwrap().print_disassembly(addr, count);
|
||||
}
|
||||
},
|
||||
"c" | "continue" => {
|
||||
self.check_repeat_arg(args)?;
|
||||
system.disable_debugging();
|
||||
return Ok(true);
|
||||
self.check_repeat_arg(&args)?;
|
||||
return Ok(DebugControl::Exit);
|
||||
},
|
||||
"s" | "step" => {
|
||||
self.check_repeat_arg(args)?;
|
||||
return Ok(true);
|
||||
self.check_repeat_arg(&args)?;
|
||||
system.step_until_debuggable()?;
|
||||
return Ok(DebugControl::Wait);
|
||||
},
|
||||
"t" | "trace" => {
|
||||
self.trace_only = true;
|
||||
return Ok(true);
|
||||
system.step_until_debuggable()?;
|
||||
return Ok(DebugControl::Continue);
|
||||
}
|
||||
"setb" | "setw" | "setl" => {
|
||||
if args.len() != 3 {
|
||||
@ -185,18 +198,20 @@ impl Debugger {
|
||||
// return Ok(true);
|
||||
//},
|
||||
_ => {
|
||||
if debug_obj.execute_command(system, args)? {
|
||||
println!("Error: unknown command {}", args[0]);
|
||||
if let Some(device) = system.get_next_debuggable_device() {
|
||||
if device.borrow_mut().as_debuggable().unwrap().run_command(system, &args)? {
|
||||
println!("Error: unknown command {}", args[0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(false)
|
||||
Ok(DebugControl::Wait)
|
||||
}
|
||||
|
||||
fn check_repeat_arg(&mut self, args: &[&str]) -> Result<(), Error> {
|
||||
if args.len() > 1 {
|
||||
self.repeat = args[1].parse::<u32>().map_err(|_| Error::new("Unable to parse repeat number"))?;
|
||||
self.last_command = Some(args[0].to_string());
|
||||
let count = args[1].parse::<u32>().map_err(|_| Error::new("Unable to parse repeat number"))?;
|
||||
self.repeat_command = Some((count, args[0].to_string()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -168,14 +168,12 @@ 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);
|
||||
|
||||
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>;
|
||||
fn run_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error>;
|
||||
}
|
||||
|
||||
/// A device (peripheral) that can inspected using the built-in debugger
|
||||
@ -219,7 +217,7 @@ pub fn wrap_transmutable<T: Transmutable + 'static>(value: T) -> TransmutableBox
|
||||
|
||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct DeviceId(usize);
|
||||
|
||||
impl DeviceId {
|
||||
@ -247,6 +245,10 @@ impl Device {
|
||||
Self(DeviceId::new(), wrap_transmutable(value))
|
||||
}
|
||||
|
||||
pub fn id(&self) -> DeviceId {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn borrow_mut(&self) -> RefMut<'_, Box<dyn Transmutable>> {
|
||||
self.1.borrow_mut()
|
||||
}
|
||||
@ -254,33 +256,6 @@ impl Device {
|
||||
pub fn try_borrow_mut(&self) -> Result<RefMut<'_, Box<dyn Transmutable>>, BorrowMutError> {
|
||||
self.1.try_borrow_mut()
|
||||
}
|
||||
|
||||
/*
|
||||
#[inline]
|
||||
pub fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
|
||||
self.1.borrow_mut().as_steppable()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_interruptable(&mut self) -> Option<&mut dyn Interruptable> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> {
|
||||
None
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@ pub mod host;
|
||||
pub use log::{trace, debug, info, warn, error};
|
||||
|
||||
pub use crate::clock::{ClockTime, ClockDuration, Frequency};
|
||||
pub use crate::debugger::Debugger;
|
||||
pub use crate::debugger::{DebugControl, Debugger};
|
||||
pub use crate::devices::{Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Device};
|
||||
pub use crate::devices::{read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable};
|
||||
pub use crate::error::{Error, ErrorType};
|
||||
|
@ -1,10 +1,9 @@
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::{Cell, RefCell, RefMut};
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::memory::Bus;
|
||||
use crate::debugger::Debugger;
|
||||
use crate::signals::EdgeSignal;
|
||||
use crate::error::{Error, ErrorType};
|
||||
use crate::interrupts::InterruptController;
|
||||
@ -17,8 +16,6 @@ pub struct System {
|
||||
pub devices: HashMap<String, Device>,
|
||||
pub event_queue: Vec<NextStep>,
|
||||
|
||||
pub debug_enabled: Cell<bool>,
|
||||
pub debugger: RefCell<Debugger>,
|
||||
pub debuggables: Vec<Device>,
|
||||
|
||||
pub bus: Rc<RefCell<Bus>>,
|
||||
@ -35,8 +32,6 @@ impl Default for System {
|
||||
devices: HashMap::new(),
|
||||
event_queue: vec![],
|
||||
|
||||
debug_enabled: Cell::new(false),
|
||||
debugger: RefCell::new(Debugger::default()),
|
||||
debuggables: Vec::new(),
|
||||
|
||||
bus: Rc::new(RefCell::new(Bus::default())),
|
||||
@ -87,18 +82,6 @@ impl System {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enable_debugging(&self) {
|
||||
self.debug_enabled.set(true);
|
||||
for device in &self.debuggables {
|
||||
device.borrow_mut().as_debuggable().map(|device| device.set_debugging(true));
|
||||
}
|
||||
self.debugger.borrow_mut().breakpoint_occurred();
|
||||
}
|
||||
|
||||
pub fn disable_debugging(&self) {
|
||||
self.debug_enabled.set(false);
|
||||
}
|
||||
|
||||
fn process_one_event(&mut self) -> Result<(), Error> {
|
||||
let mut event_device = self.event_queue.pop().unwrap();
|
||||
self.clock = event_device.next_clock;
|
||||
@ -114,28 +97,50 @@ impl System {
|
||||
}
|
||||
|
||||
pub fn step(&mut self) -> Result<(), Error> {
|
||||
self.check_debugger();
|
||||
|
||||
match self.process_one_event() {
|
||||
Ok(()) => {
|
||||
if self.get_bus().check_and_reset_watcher_modified() {
|
||||
self.enable_debugging();
|
||||
}
|
||||
},
|
||||
Ok(()) => {},
|
||||
Err(err) if err.err == ErrorType::Breakpoint => {
|
||||
println!("Breakpoint reached: {}", err.msg);
|
||||
self.enable_debugging();
|
||||
return Err(err);
|
||||
},
|
||||
Err(err) => {
|
||||
self.exit_error();
|
||||
println!("{:?}", err);
|
||||
log::error!("{:?}", err);
|
||||
return Err(err);
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_for(&mut self, elapsed: ClockDuration) -> Result<(), Error> {
|
||||
pub fn step_until_device(&mut self, device: Device) -> Result<(), Error> {
|
||||
loop {
|
||||
self.step()?;
|
||||
|
||||
if self.get_next_event_device().id() == device.id() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn step_until_debuggable(&mut self) -> Result<(), Error> {
|
||||
loop {
|
||||
self.step()?;
|
||||
|
||||
if self.get_next_event_device().borrow_mut().as_debuggable().is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_until_clock(&mut self, clock: ClockTime) -> Result<(), Error> {
|
||||
while self.clock < clock {
|
||||
self.step()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_for_duration(&mut self, elapsed: ClockDuration) -> Result<(), Error> {
|
||||
let target = self.clock + elapsed;
|
||||
|
||||
while self.clock < target {
|
||||
@ -144,6 +149,11 @@ impl System {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_forever(&mut self) -> Result<(), Error> {
|
||||
self.run_until_clock(ClockTime::FOREVER)
|
||||
}
|
||||
|
||||
// TODO rename this run_until_signal, and make it take a signal as argument
|
||||
pub fn run_until_break(&mut self) -> Result<(), Error> {
|
||||
let mut signal = match &self.break_signal {
|
||||
Some(signal) => signal.clone(),
|
||||
@ -156,10 +166,6 @@ impl System {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_loop(&mut self) {
|
||||
self.run_for(ClockDuration::from_nanos(u64::MAX)).unwrap();
|
||||
}
|
||||
|
||||
pub fn exit_error(&mut self) {
|
||||
for (_, dev) in self.devices.iter() {
|
||||
if let Some(dev) = dev.borrow_mut().as_steppable() {
|
||||
@ -168,23 +174,25 @@ impl System {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_next_event_device(&self) -> Device {
|
||||
self.event_queue[self.event_queue.len() - 1].device.clone()
|
||||
}
|
||||
|
||||
pub fn get_next_debuggable_device(&self) -> Option<Device> {
|
||||
for event in self.event_queue.iter().rev() {
|
||||
if event.device.borrow_mut().as_debuggable().is_some() {
|
||||
return Some(event.device.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn try_add_debuggable(&mut self, device: Device) {
|
||||
if device.borrow_mut().as_debuggable().is_some() {
|
||||
self.debuggables.push(device);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if let Err(err) = self.debugger.borrow_mut().run_debugger(self, top.clone()) {
|
||||
println!("Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_queue_device(&mut self, device: Device) {
|
||||
if device.borrow_mut().as_steppable().is_some() {
|
||||
self.queue_device(NextStep::new(device));
|
||||
|
@ -22,36 +22,25 @@ impl StackTracer {
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct M68kDebugger {
|
||||
pub enabled: bool,
|
||||
pub breakpoints: Vec<u32>,
|
||||
pub use_tracing: bool,
|
||||
pub step_until_return: Option<usize>,
|
||||
pub stack_tracer: StackTracer,
|
||||
pub(crate) skip_breakpoint: usize,
|
||||
pub(crate) breakpoints: Vec<u32>,
|
||||
pub(crate) step_until_return: Option<usize>,
|
||||
pub(crate) stack_tracer: StackTracer,
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
fn print_current_step(&mut self, _system: &System) -> Result<(), Error> {
|
||||
self.decoder.decode_at(&mut self.port, true, self.state.pc)?;
|
||||
let _ = self.decoder.decode_at(&mut self.port, true, self.state.pc);
|
||||
self.decoder.dump_decoded(&mut self.port);
|
||||
self.dump_state();
|
||||
Ok(())
|
||||
@ -62,7 +51,7 @@ impl Debuggable for M68k {
|
||||
decoder.dump_disassembly(&mut self.port, addr as u32, count as u32);
|
||||
}
|
||||
|
||||
fn execute_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error> {
|
||||
fn run_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error> {
|
||||
match args[0] {
|
||||
"ds" | "stack" | "dumpstack" => {
|
||||
println!("Stack:");
|
||||
@ -80,19 +69,19 @@ impl Debuggable for M68k {
|
||||
}
|
||||
|
||||
impl M68k {
|
||||
#[allow(dead_code)]
|
||||
pub fn enable_tracing(&mut self) {
|
||||
self.debugger.use_tracing = true;
|
||||
}
|
||||
|
||||
pub fn check_breakpoints(&mut self, system: &System) {
|
||||
pub fn check_breakpoints(&mut self) -> Result<(), Error> {
|
||||
for breakpoint in &self.debugger.breakpoints {
|
||||
if *breakpoint == self.state.pc {
|
||||
println!("Breakpoint reached: {:08x}", *breakpoint);
|
||||
system.enable_debugging();
|
||||
break;
|
||||
if self.debugger.skip_breakpoint > 0 {
|
||||
self.debugger.skip_breakpoint -= 1;
|
||||
return Ok(());
|
||||
} else {
|
||||
self.debugger.skip_breakpoint = 1;
|
||||
return Err(Error::breakpoint(format!("breakpoint reached: {:08x}", *breakpoint)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,11 +94,12 @@ impl M68k {
|
||||
}
|
||||
|
||||
pub fn cycle_one(&mut self, system: &System) -> Result<ClockCycles, Error> {
|
||||
self.check_breakpoints()?;
|
||||
|
||||
self.decode_next()?;
|
||||
self.execute_current()?;
|
||||
|
||||
self.check_pending_interrupts(system)?;
|
||||
self.check_breakpoints(system);
|
||||
Ok(self.timing.calculate_clocks(false, 1))
|
||||
}
|
||||
|
||||
@ -206,10 +207,6 @@ impl M68k {
|
||||
|
||||
self.timing.add_instruction(&self.decoder.instruction);
|
||||
|
||||
if self.debugger.use_tracing {
|
||||
self.decoder.dump_decoded(&mut self.port);
|
||||
}
|
||||
|
||||
self.state.pc = self.decoder.end;
|
||||
|
||||
Ok(())
|
||||
|
@ -8,28 +8,18 @@ use crate::instructions::Register;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Z80Debugger {
|
||||
pub enabled: bool,
|
||||
pub breakpoints: Vec<u16>,
|
||||
pub(crate) skip_breakpoint: usize,
|
||||
pub(crate) breakpoints: Vec<u16>,
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +35,7 @@ impl Debuggable for Z80 {
|
||||
decoder.dump_disassembly(&mut self.port, addr as u16, count as u16);
|
||||
}
|
||||
|
||||
fn execute_command(&mut self, _system: &System, args: &[&str]) -> Result<bool, Error> {
|
||||
fn run_command(&mut self, _system: &System, args: &[&str]) -> Result<bool, Error> {
|
||||
match args[0] {
|
||||
"l" => {
|
||||
self.state.reg[Register::L as usize] = 0x05
|
||||
@ -57,14 +47,19 @@ impl Debuggable for Z80 {
|
||||
}
|
||||
|
||||
impl Z80 {
|
||||
pub fn check_breakpoints(&mut self, system: &System) {
|
||||
pub fn check_breakpoints(&mut self) -> Result<(), Error> {
|
||||
for breakpoint in &self.debugger.breakpoints {
|
||||
if *breakpoint == self.state.pc {
|
||||
println!("Breakpoint reached: {:08x}", *breakpoint);
|
||||
system.enable_debugging();
|
||||
break;
|
||||
if self.debugger.skip_breakpoint > 0 {
|
||||
self.debugger.skip_breakpoint -= 1;
|
||||
return Ok(());
|
||||
} else {
|
||||
self.debugger.skip_breakpoint = 1;
|
||||
return Err(Error::breakpoint(format!("breakpoint reached: {:08x}", *breakpoint)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ impl Z80 {
|
||||
Status::Init => self.init(),
|
||||
Status::Halted => Err(Error::new("CPU stopped")),
|
||||
Status::Running => {
|
||||
match self.cycle_one(system) {
|
||||
match self.cycle_one() {
|
||||
Ok(clocks) => Ok(clocks),
|
||||
Err(Error { err: ErrorType::Processor, .. }) => {
|
||||
Ok(4)
|
||||
@ -98,10 +98,11 @@ impl Z80 {
|
||||
Ok(16)
|
||||
}
|
||||
|
||||
pub fn cycle_one(&mut self, system: &System) -> Result<u16, Error> {
|
||||
pub fn cycle_one(&mut self) -> Result<u16, Error> {
|
||||
self.check_breakpoints()?;
|
||||
|
||||
self.decode_next()?;
|
||||
self.execute_current()?;
|
||||
self.check_breakpoints(system);
|
||||
Ok(Z80InstructionCycles::from_instruction(&self.decoder.instruction)?
|
||||
.calculate_cycles(self.executor.took_branch))
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ edition = "2021"
|
||||
default-run = "moa-computie"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
clap = "3.2.20"
|
||||
simple_logger = "2.3.0"
|
||||
log = "^0.4"
|
||||
clap = "^4"
|
||||
simple_logger = "^2"
|
||||
|
||||
moa_core = { path = "../../core" }
|
||||
moa_common = { path = "../common", features = ["tty"] }
|
||||
|
@ -3,14 +3,12 @@ use moa_console::ConsoleFrontend;
|
||||
use moa_systems_computie::build_computie;
|
||||
|
||||
fn main() {
|
||||
simple_logger::SimpleLogger::new()
|
||||
.with_level(log::Level::Debug.to_level_filter())
|
||||
.without_timestamps()
|
||||
.init().unwrap();
|
||||
let matches = ConsoleFrontend::args("Computie68k Emulator")
|
||||
.get_matches();
|
||||
|
||||
let mut frontend = ConsoleFrontend;
|
||||
let mut frontend = ConsoleFrontend::new();
|
||||
|
||||
let mut system = build_computie(&mut frontend).unwrap();
|
||||
system.run_loop();
|
||||
frontend.start(matches, system);
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,23 @@
|
||||
|
||||
use clap::{App, Arg};
|
||||
use clap::{Arg};
|
||||
|
||||
use moa_console::ConsoleFrontend;
|
||||
use moa_systems_genesis::{build_genesis, SegaGenesisOptions};
|
||||
|
||||
fn main() {
|
||||
let matches = App::new("Sega Genesis/Mega Drive Emulator")
|
||||
let matches = ConsoleFrontend::args("Sega Genesis/Mega Drive Emulator")
|
||||
.arg(Arg::new("ROM")
|
||||
.help("ROM file to load (must be flat binary)"))
|
||||
.get_matches();
|
||||
|
||||
let mut frontend = ConsoleFrontend;
|
||||
let mut frontend = ConsoleFrontend::new();
|
||||
|
||||
let mut options = SegaGenesisOptions::default();
|
||||
if let Some(filename) = matches.value_of("ROM") {
|
||||
if let Some(filename) = matches.get_one::<String>("ROM") {
|
||||
options.rom = filename.to_string();
|
||||
}
|
||||
|
||||
let mut system = build_genesis(&mut frontend, options).unwrap();
|
||||
system.run_loop();
|
||||
let system = build_genesis(&mut frontend, options).unwrap();
|
||||
frontend.start(matches, system);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
|
||||
use moa_core::Error;
|
||||
use clap::{Command, Arg, ArgAction, ArgMatches};
|
||||
use std::io::{self, Write};
|
||||
|
||||
use moa_core::{Error, ErrorType, System, ClockDuration, DebugControl, Debugger};
|
||||
use moa_core::host::{Host, Tty, ControllerEvent, Audio, DummyAudio, FrameReceiver, EventSender};
|
||||
|
||||
pub struct ConsoleFrontend;
|
||||
@ -26,3 +29,77 @@ impl Host for ConsoleFrontend {
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleFrontend {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
pub fn args(application_name: &'static str) -> Command {
|
||||
Command::new(application_name)
|
||||
.arg(Arg::new("log-level")
|
||||
.short('l')
|
||||
.long("log-level")
|
||||
.help("Set the type of log messages to print"))
|
||||
.arg(Arg::new("debugger")
|
||||
.short('d')
|
||||
.long("debugger")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Start the debugger before running machine"))
|
||||
}
|
||||
|
||||
pub fn start(self, matches: ArgMatches, mut system: System) {
|
||||
let log_level = match matches.get_one("log-level").map(|s: &String| s.as_str()) {
|
||||
Some("trace") => log::Level::Trace,
|
||||
Some("debug") => log::Level::Debug,
|
||||
Some("info") => log::Level::Info,
|
||||
Some("warn") => log::Level::Warn,
|
||||
Some("error") => log::Level::Error,
|
||||
_ => log::Level::Info,
|
||||
};
|
||||
|
||||
// Start the logger
|
||||
simple_logger::SimpleLogger::new()
|
||||
.with_level(log_level.to_level_filter())
|
||||
.without_timestamps()
|
||||
.init()
|
||||
.unwrap();
|
||||
|
||||
// Run the main loop
|
||||
let mut debugger = Debugger::default();
|
||||
let mut run_debugger = matches.get_flag("debugger");
|
||||
loop {
|
||||
if run_debugger {
|
||||
run_debugger = false;
|
||||
|
||||
loop {
|
||||
debugger.print_step(&mut system).unwrap();
|
||||
if debugger.check_auto_command(&mut system).unwrap() == DebugControl::Continue {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut buffer = String::new();
|
||||
io::stdout().write_all(b"> ").unwrap();
|
||||
io::stdin().read_line(&mut buffer).unwrap();
|
||||
match debugger.run_command(&mut system, &buffer) {
|
||||
Ok(DebugControl::Exit) => break,
|
||||
Ok(_) => {},
|
||||
Err(err) => {
|
||||
println!("Error: {}", err.msg);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match system.run_for_duration(ClockDuration::MAX - system.clock.as_duration()) {
|
||||
Ok(()) => {},
|
||||
Err(err) if err.err == ErrorType::Breakpoint => {
|
||||
run_debugger = true;
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("{:?}", err);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,9 +6,9 @@ default-run = "moa-genesis"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
minifb = "0.19"
|
||||
clap = "3.2.20"
|
||||
simple_logger = "2.3.0"
|
||||
minifb = "^0.19"
|
||||
clap = "^4"
|
||||
simple_logger = "^2"
|
||||
|
||||
moa_core = { path = "../../core" }
|
||||
moa_common = { path = "../common", features = ["audio"] }
|
||||
|
@ -10,7 +10,7 @@ fn main() {
|
||||
.get_matches();
|
||||
|
||||
let mut options = SegaGenesisOptions::default();
|
||||
if let Some(filename) = matches.value_of("ROM") {
|
||||
if let Some(filename) = matches.get_one::<String>("ROM") {
|
||||
options.rom = filename.to_string();
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
|
||||
use std::thread;
|
||||
use std::str::FromStr;
|
||||
use std::io::{self, Write};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use minifb::{self, Key, MouseMode, MouseButton};
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use clap::{Command, Arg, ArgAction, ArgMatches};
|
||||
|
||||
use moa_core::{System, Error, ClockDuration, Device};
|
||||
use moa_core::{System, Error, ErrorType, ClockDuration, Device, Debugger, DebugControl};
|
||||
use moa_core::host::{Host, Audio, KeyEvent, MouseEvent, MouseState, ControllerDevice, ControllerEvent, EventSender, PixelEncoding, Frame, FrameReceiver};
|
||||
|
||||
use moa_common::{AudioMixer, AudioSource};
|
||||
@ -24,34 +24,39 @@ const WIDTH: u32 = 320;
|
||||
const HEIGHT: u32 = 224;
|
||||
|
||||
|
||||
pub fn new(name: &str) -> App {
|
||||
App::new(name)
|
||||
pub fn new(name: &'static str) -> Command {
|
||||
Command::new(name)
|
||||
.arg(Arg::new("scale")
|
||||
.short('s')
|
||||
.long("scale")
|
||||
.takes_value(true)
|
||||
.help("Scale the screen"))
|
||||
.arg(Arg::new("threaded")
|
||||
.short('t')
|
||||
.long("threaded")
|
||||
.help("Run the simulation in a separate thread"))
|
||||
.arg(Arg::new("speed")
|
||||
.short('x')
|
||||
.long("speed")
|
||||
.takes_value(true)
|
||||
.help("Adjust the speed of the simulation"))
|
||||
.arg(Arg::new("threaded")
|
||||
.short('t')
|
||||
.long("threaded")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Run the simulation in a separate thread"))
|
||||
.arg(Arg::new("log-level")
|
||||
.short('l')
|
||||
.long("log-level")
|
||||
.help("Set the type of log messages to print"))
|
||||
.arg(Arg::new("debugger")
|
||||
.short('d')
|
||||
.long("debugger")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Start the debugger before running machine"))
|
||||
.arg(Arg::new("disable-audio")
|
||||
.short('a')
|
||||
.long("disable-audio")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Disable audio output"))
|
||||
}
|
||||
|
||||
pub fn run<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static {
|
||||
if matches.occurrences_of("threaded") > 0 {
|
||||
if matches.get_flag("threaded") {
|
||||
run_threaded(matches, init);
|
||||
} else {
|
||||
run_inline(matches, init);
|
||||
@ -75,7 +80,7 @@ pub fn run_threaded<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFr
|
||||
thread::spawn(move || {
|
||||
let mut system = init(&mut frontend.lock().unwrap()).unwrap();
|
||||
frontend.lock().unwrap().finalize();
|
||||
system.run_loop();
|
||||
system.run_forever().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
@ -203,18 +208,22 @@ impl MiniFrontend {
|
||||
}
|
||||
|
||||
pub fn start(&mut self, matches: ArgMatches, mut system: Option<System>) {
|
||||
let log_level = match matches.get_one("log-level").map(|s: &String| s.as_str()) {
|
||||
Some("trace") => log::Level::Trace,
|
||||
Some("debug") => log::Level::Debug,
|
||||
Some("info") => log::Level::Info,
|
||||
Some("warn") => log::Level::Warn,
|
||||
Some("error") => log::Level::Error,
|
||||
_ => log::Level::Warn,
|
||||
};
|
||||
|
||||
simple_logger::SimpleLogger::new()
|
||||
.with_level(log::Level::Warn.to_level_filter())
|
||||
.with_level(log_level.to_level_filter())
|
||||
.without_timestamps()
|
||||
.init().unwrap();
|
||||
.init()
|
||||
.unwrap();
|
||||
|
||||
if matches.occurrences_of("debugger") > 0 {
|
||||
if let Some(system) = system.as_mut() {
|
||||
system.enable_debugging();
|
||||
}
|
||||
}
|
||||
|
||||
if self.mixer.borrow_mut().num_sources() != 0 && matches.occurrences_of("disable-audio") == 0 {
|
||||
if self.mixer.borrow_mut().num_sources() != 0 && !matches.get_flag("disable-audio") {
|
||||
if let Some(system) = system.as_mut() {
|
||||
system.add_device("mixer", Device::new(self.mixer.clone())).unwrap();
|
||||
}
|
||||
@ -222,7 +231,7 @@ impl MiniFrontend {
|
||||
}
|
||||
|
||||
let options = minifb::WindowOptions {
|
||||
scale: match matches.value_of("scale").map(|s| s.parse::<u8>().unwrap()) {
|
||||
scale: match matches.get_one::<String>("scale").map(|s| s.parse::<u8>().unwrap()) {
|
||||
Some(1) => minifb::Scale::X1,
|
||||
Some(2) => minifb::Scale::X2,
|
||||
Some(4) => minifb::Scale::X4,
|
||||
@ -232,10 +241,7 @@ impl MiniFrontend {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let speed = match matches.value_of("speed") {
|
||||
Some(x) => f32::from_str(x).unwrap(),
|
||||
None => 1.0,
|
||||
};
|
||||
let speed = matches.get_one::<f32>("speed").cloned().unwrap_or(1.0);
|
||||
|
||||
let mut size = (WIDTH, HEIGHT);
|
||||
if let Some(queue) = self.video.as_mut() {
|
||||
@ -257,21 +263,49 @@ impl MiniFrontend {
|
||||
window.limit_update_rate(Some(Duration::from_micros(16600)));
|
||||
//let nanoseconds_per_frame = (16_600_000 as f32 * speed) as u64;
|
||||
|
||||
let mut debugger = Debugger::default();
|
||||
let mut run_debugger = matches.get_flag("debugger");
|
||||
let mut update_timer = Instant::now();
|
||||
let mut last_frame = Frame::new(size.0, size.1, PixelEncoding::ARGB);
|
||||
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||
let frame_time = update_timer.elapsed();
|
||||
update_timer = Instant::now();
|
||||
//println!("new frame after {:?}us", frame_time.as_micros());
|
||||
if run_debugger {
|
||||
if let Some(mut system) = system.as_mut() {
|
||||
debugger.print_step(&mut system).unwrap();
|
||||
if debugger.check_auto_command(&mut system).unwrap() != DebugControl::Continue {
|
||||
let mut buffer = String::new();
|
||||
io::stdout().write_all(b"> ").unwrap();
|
||||
io::stdin().read_line(&mut buffer).unwrap();
|
||||
match debugger.run_command(&mut system, &buffer) {
|
||||
Ok(DebugControl::Exit) => {
|
||||
run_debugger = false;
|
||||
},
|
||||
Ok(_) => {},
|
||||
Err(err) => {
|
||||
println!("Error: {}", err.msg);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let frame_time = update_timer.elapsed();
|
||||
update_timer = Instant::now();
|
||||
//println!("new frame after {:?}us", frame_time.as_micros());
|
||||
|
||||
//let run_timer = Instant::now();
|
||||
if let Some(system) = system.as_mut() {
|
||||
//system.run_for(nanoseconds_per_frame).unwrap();
|
||||
system.run_for(ClockDuration::from_nanos((frame_time.as_nanos() as f32 * speed) as u64)).unwrap();
|
||||
//system.run_until_break().unwrap();
|
||||
//let run_timer = Instant::now();
|
||||
if let Some(system) = system.as_mut() {
|
||||
//system.run_for(nanoseconds_per_frame).unwrap();
|
||||
match system.run_for_duration(ClockDuration::from_nanos((frame_time.as_nanos() as f32 * speed) as u64)) {
|
||||
Ok(()) => {},
|
||||
Err(err) if err.err == ErrorType::Breakpoint => {
|
||||
run_debugger = true;
|
||||
},
|
||||
Err(err) => panic!("{:?}", err),
|
||||
}
|
||||
//system.run_until_break().unwrap();
|
||||
}
|
||||
//let sim_time = run_timer.elapsed().as_micros();
|
||||
//println!("ran simulation for {:?}us in {:?}us (avg: {:?}us)", frame_time.as_micros(), sim_time, frame_time.as_micros() as f64 / sim_time as f64);
|
||||
}
|
||||
//let sim_time = run_timer.elapsed().as_micros();
|
||||
//println!("ran simulation for {:?}us in {:?}us (avg: {:?}us)", frame_time.as_micros(), sim_time, frame_time.as_micros() as f64 / sim_time as f64);
|
||||
|
||||
if let Some(keys) = window.get_keys_pressed(minifb::KeyRepeat::No) {
|
||||
for key in keys {
|
||||
@ -279,9 +313,7 @@ impl MiniFrontend {
|
||||
|
||||
// Process special keys
|
||||
if let Key::D = key {
|
||||
if let Some(system) = system.as_ref() {
|
||||
system.enable_debugging();
|
||||
}
|
||||
run_debugger = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -306,7 +338,7 @@ impl MiniFrontend {
|
||||
}
|
||||
|
||||
if let Some(queue) = self.video.as_mut() {
|
||||
if let Some((clock, frame)) = queue.latest() {
|
||||
if let Some((_clock, frame)) = queue.latest() {
|
||||
last_frame = frame
|
||||