Implemented Inspect and Debug traits from emulator-hal

This commit is contained in:
transistor 2024-03-18 22:40:12 -07:00
parent 5cd1111fc7
commit c74ea8f462
12 changed files with 205 additions and 92 deletions

View File

@ -1,9 +1,12 @@
use femtos::Instant;
use emulator_hal::bus::BusAccess;
// m68k Debugger
use super::state::M68kError;
use super::execute::M68kCycleExecutor;
use super::memory::M68kAddress;
use core::fmt;
use emulator_hal::time;
use emulator_hal::bus::{self, BusAccess};
use emulator_hal::step::{Inspect, Debug};
use crate::{M68k, M68kError, M68kAddress, M68kCycleExecutor};
#[derive(Clone, Default)]
pub struct StackTracer {
@ -20,6 +23,74 @@ impl StackTracer {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum M68kInfo {
State,
}
impl<Bus, BusError, Instant, Writer> Inspect<M68kAddress, Instant, Bus, Writer> for M68k<Instant>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
BusError: bus::Error,
Writer: fmt::Write,
{
type InfoType = M68kInfo;
type Error = M68kError<BusError>;
fn inspect(&mut self, info: Self::InfoType, bus: &mut Bus, writer: &mut Writer) -> Result<(), Self::Error> {
match info {
M68kInfo::State => self
.state
.dump_state(writer)
.map_err(|_| M68kError::Other("error while formatting state".to_string())),
}
}
fn brief_summary(&mut self, bus: &mut Bus, writer: &mut Writer) -> Result<(), Self::Error> {
self.inspect(M68kInfo::State, bus, writer)
}
fn detailed_summary(&mut self, bus: &mut Bus, writer: &mut Writer) -> Result<(), Self::Error> {
self.inspect(M68kInfo::State, bus, writer)
}
}
/// Control the execution of a CPU device for debugging purposes
impl<Bus, BusError, Instant, Writer> Debug<M68kAddress, Instant, Bus, Writer> for M68k<Instant>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
BusError: bus::Error,
Instant: time::Instant,
Writer: fmt::Write,
{
// TODO this should be a new type
type DebugError = M68kError<BusError>;
fn get_execution_address(&mut self) -> Result<M68kAddress, Self::DebugError> {
Ok(self.state.pc)
}
fn set_execution_address(&mut self, address: M68kAddress) -> Result<(), Self::DebugError> {
self.state.pc = address;
Ok(())
}
fn add_breakpoint(&mut self, address: M68kAddress) {
self.debugger.breakpoints.push(address as u32);
}
fn remove_breakpoint(&mut self, address: M68kAddress) {
if let Some(index) = self.debugger.breakpoints.iter().position(|a| *a == address as u32) {
self.debugger.breakpoints.remove(index);
}
}
fn clear_breakpoints(&mut self) {
self.debugger.breakpoints.clear();
}
}
#[derive(Clone, Default)]
pub struct M68kDebugger {
@ -30,9 +101,10 @@ pub struct M68kDebugger {
pub(crate) stack_tracer: StackTracer,
}
impl<'a, Bus, BusError> M68kCycleExecutor<'a, Bus>
impl<'a, Bus, BusError, Instant> M68kCycleExecutor<'a, Bus, Instant>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
Instant: Copy,
{
pub fn check_breakpoints(&mut self) -> Result<(), M68kError<BusError>> {
for breakpoint in &self.debugger.breakpoints {

View File

@ -1,8 +1,9 @@
use femtos::Instant;
// Instruction Decoding
use core::marker::PhantomData;
use emulator_hal::bus::BusAccess;
use crate::state::{M68kType, M68kError, Exceptions};
use crate::memory::{M68kBusPort, M68kAddress};
use crate::{M68kType, M68kError, M68kBusPort, M68kAddress, Exceptions};
use crate::instructions::{
Size, Sign, Direction, XRegister, BaseRegister, IndexRegister, RegOrImmediate, ControlRegister, Condition, Target, Instruction,
sign_extend_to_long,
@ -28,27 +29,31 @@ const OPCG_FLINE: u8 = 0xF;
#[derive(Clone, Debug)]
pub struct M68kDecoder {
pub struct M68kDecoder<Instant> {
pub cputype: M68kType,
pub is_supervisor: bool,
pub start: u32,
pub end: u32,
pub instruction_word: u16,
pub instruction: Instruction,
pub instant: PhantomData<Instant>,
}
pub struct InstructionDecoding<'a, Bus>
pub struct InstructionDecoding<'a, Bus, Instant>
where
Bus: BusAccess<M68kAddress, Instant>,
{
pub(crate) bus: &'a mut Bus,
pub(crate) memory: &'a mut M68kBusPort,
pub(crate) decoder: &'a mut M68kDecoder,
pub(crate) memory: &'a mut M68kBusPort<Instant>,
pub(crate) decoder: &'a mut M68kDecoder<Instant>,
}
impl M68kDecoder {
impl<Instant> M68kDecoder<Instant>
where
Instant: Copy,
{
#[inline]
pub fn new(cputype: M68kType, is_supervisor: bool, start: u32) -> M68kDecoder {
pub fn new(cputype: M68kType, is_supervisor: bool, start: u32) -> M68kDecoder<Instant> {
M68kDecoder {
cputype,
is_supervisor,
@ -56,6 +61,7 @@ impl M68kDecoder {
end: start,
instruction_word: 0,
instruction: Instruction::NOP,
instant: PhantomData,
}
}
@ -70,7 +76,7 @@ impl M68kDecoder {
pub fn decode_at<Bus>(
&mut self,
bus: &mut Bus,
memory: &mut M68kBusPort,
memory: &mut M68kBusPort<Instant>,
is_supervisor: bool,
start: u32,
) -> Result<(), M68kError<Bus::Error>>
@ -87,14 +93,13 @@ impl M68kDecoder {
Ok(())
}
pub fn dump_disassembly<Bus>(&mut self, bus: &mut Bus, start: u32, length: u32)
pub fn dump_disassembly<Bus>(&mut self, bus: &mut Bus, memory: &mut M68kBusPort<Instant>, start: u32, length: u32)
where
Bus: BusAccess<M68kAddress, Instant>,
{
let mut memory = M68kBusPort::default();
let mut next = start;
while next < (start + length) {
match self.decode_at(bus, &mut memory, self.is_supervisor, next) {
match self.decode_at(bus, memory, self.is_supervisor, next) {
Ok(()) => {
self.dump_decoded(memory.current_clock, bus);
next = self.end;
@ -121,9 +126,10 @@ impl M68kDecoder {
}
}
impl<'a, Bus> InstructionDecoding<'a, Bus>
impl<'a, Bus, Instant> InstructionDecoding<'a, Bus, Instant>
where
Bus: BusAccess<M68kAddress, Instant>,
Instant: Copy,
{
#[inline]
pub fn decode_next(&mut self) -> Result<Instruction, M68kError<Bus::Error>> {

View File

@ -1,8 +1,11 @@
use femtos::Instant;
use emulator_hal::bus::{self, BusAccess};
use emulator_hal::step::Step;
// Instruction Execution
use crate::state::{M68k, M68kType, M68kError, M68kState, Status, Flags, Exceptions, InterruptPriority};
use emulator_hal::time;
use emulator_hal::step::Step;
use emulator_hal::bus::{self, BusAccess};
use crate::{M68k, M68kType, M68kError, M68kState};
use crate::state::{Status, Flags, Exceptions, InterruptPriority};
use crate::memory::{MemType, MemAccess, M68kBusPort, M68kAddress};
use crate::decode::M68kDecoder;
use crate::debugger::M68kDebugger;
@ -23,14 +26,17 @@ pub enum Used {
#[derive(Clone, Debug)]
pub struct M68kCycle {
pub decoder: M68kDecoder,
pub struct M68kCycle<Instant> {
pub decoder: M68kDecoder<Instant>,
pub timing: M68kInstructionTiming,
pub memory: M68kBusPort,
pub memory: M68kBusPort<Instant>,
pub current_clock: Instant,
}
impl M68kCycle {
impl<Instant> M68kCycle<Instant>
where
Instant: time::Instant,
{
#[inline]
pub fn default(cputype: M68kType, data_width: u8) -> Self {
Self {
@ -42,7 +48,7 @@ impl M68kCycle {
}
#[inline]
pub fn new(cpu: &M68k, clock: Instant) -> Self {
pub fn new(cpu: &M68k<Instant>, clock: Instant) -> Self {
let is_supervisor = cpu.state.sr & (Flags::Supervisor as u16) != 0;
Self {
decoder: M68kDecoder::new(cpu.info.chip, is_supervisor, cpu.state.pc),
@ -53,17 +59,11 @@ impl M68kCycle {
}
#[inline]
pub fn begin<Bus>(self, cpu: &mut M68k, bus: Bus) -> M68kCycleExecutor<'_, Bus>
pub fn begin<Bus>(self, cpu: &mut M68k<Instant>, bus: Bus) -> M68kCycleExecutor<'_, Bus, Instant>
where
Bus: BusAccess<M68kAddress, Instant>,
{
cpu.stats.cycle_number += 1;
if cpu.stats.cycle_number > cpu.stats.last_update {
cpu.stats.last_update += 1_000_000;
let now = std::time::SystemTime::now();
log::warn!("{} per million", now.duration_since(cpu.stats.last_time).unwrap().as_micros());
cpu.stats.last_time = now;
}
cpu.stats.cycle_number = cpu.stats.cycle_number.wrapping_add(1);
M68kCycleExecutor {
state: &mut cpu.state,
@ -74,10 +74,11 @@ impl M68kCycle {
}
}
impl<Bus, BusError> Step<M68kAddress, Instant, Bus> for M68k
impl<Bus, BusError, Instant> Step<M68kAddress, Instant, Bus> for M68k<Instant>
where
BusError: bus::Error,
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
BusError: bus::Error,
Instant: time::Instant,
{
type Error = M68kError<BusError>;
@ -107,28 +108,30 @@ where
}
}
pub struct M68kCycleExecutor<'a, Bus>
pub struct M68kCycleExecutor<'a, Bus, Instant>
where
Bus: BusAccess<M68kAddress, Instant>,
{
pub state: &'a mut M68kState,
pub bus: Bus,
pub debugger: &'a mut M68kDebugger,
pub cycle: M68kCycle,
pub cycle: M68kCycle<Instant>,
}
impl<'a, Bus> M68kCycleExecutor<'a, Bus>
impl<'a, Bus, Instant> M68kCycleExecutor<'a, Bus, Instant>
where
Bus: BusAccess<M68kAddress, Instant>,
Instant: Copy,
{
pub fn end(self) -> M68kCycle {
pub fn end(self) -> M68kCycle<Instant> {
self.cycle
}
}
impl<'a, Bus> M68kCycleExecutor<'a, Bus>
impl<'a, Bus, Instant> M68kCycleExecutor<'a, Bus, Instant>
where
Bus: BusAccess<M68kAddress, Instant>,
Instant: Copy,
{
#[inline]
pub fn step(&mut self) -> Result<(), M68kError<Bus::Error>> {

View File

@ -11,5 +11,11 @@ pub mod timing;
#[cfg(feature = "moa")]
pub mod moa;
pub use crate::state::{M68k, M68kType, M68kError};
pub use crate::memory::{M68kAddress, M68kAddressSpace};
pub use crate::assembler::M68kAssembler;
pub use crate::debugger::M68kDebugger;
pub use crate::state::{M68k, M68kType, M68kState, M68kError, CpuInfo, Exceptions};
pub use crate::memory::{M68kAddress, M68kAddressSpace, M68kBusPort};
pub use crate::decode::{M68kDecoder, InstructionDecoding};
pub use crate::execute::{M68kCycle, M68kCycleExecutor};
pub use crate::timing::M68kInstructionTiming;
//pub use crate::instructions::{Instruction, Target, Size, Sign, XRegister, BaseRegister, IndexRegister, Direction};

View File

@ -1,9 +1,10 @@
use core::cmp;
use core::fmt::Write;
use femtos::Instant;
use emulator_hal::time;
use emulator_hal::bus::BusAccess;
use crate::state::{M68k, M68kError, CpuInfo, Exceptions};
use crate::{M68kError, CpuInfo};
use crate::state::Exceptions;
use crate::instructions::Size;
#[repr(u8)]
@ -35,7 +36,7 @@ pub enum MemAccess {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
// TODO change to MemoryState or RequestState or AccessState or maybe even BusState
pub struct MemoryRequest {
pub struct MemoryRequest<Instant> {
pub i_n_bit: bool,
pub access: MemAccess,
pub code: FunctionCode,
@ -62,7 +63,10 @@ impl FunctionCode {
}
}
impl Default for MemoryRequest {
impl<Instant> Default for MemoryRequest<Instant>
where
Instant: time::Instant,
{
fn default() -> Self {
Self {
i_n_bit: false,
@ -75,7 +79,18 @@ impl Default for MemoryRequest {
}
}
impl MemoryRequest {
impl<Instant> MemoryRequest<Instant> {
fn new(clock: Instant) -> Self {
Self {
i_n_bit: false,
access: MemAccess::Read,
code: FunctionCode::Reserved0,
size: Size::Word,
address: 0,
clock,
}
}
pub(crate) fn instruction<BusError>(&mut self, is_supervisor: bool, addr: u32) -> Result<u32, M68kError<BusError>> {
self.i_n_bit = false;
self.code = FunctionCode::program(is_supervisor);
@ -106,14 +121,14 @@ pub type M68kAddress = u32;
pub type M68kAddressSpace = (FunctionCode, u32);
#[derive(Clone, Debug)]
pub struct InstructionRequest {
pub request: MemoryRequest,
pub struct InstructionRequest<Instant> {
pub request: MemoryRequest<Instant>,
pub current_clock: Instant,
}
#[derive(Clone, Debug)]
pub struct M68kBusPort {
pub request: MemoryRequest,
pub struct M68kBusPort<Instant> {
pub request: MemoryRequest<Instant>,
pub data_bytewidth: usize,
pub address_mask: u32,
pub cycle_start_clock: Instant,
@ -121,11 +136,10 @@ pub struct M68kBusPort {
}
impl M68k {
// TODO should some of the ones from execute.rs move here
}
impl Default for M68kBusPort {
impl<Instant> Default for M68kBusPort<Instant>
where
Instant: time::Instant,
{
fn default() -> Self {
Self {
request: Default::default(),
@ -137,10 +151,13 @@ impl Default for M68kBusPort {
}
}
impl M68kBusPort {
impl<Instant> M68kBusPort<Instant>
where
Instant: Copy,
{
pub fn from_info(info: &CpuInfo, clock: Instant) -> Self {
Self {
request: Default::default(),
request: MemoryRequest::new(clock),
data_bytewidth: info.data_width as usize / 8,
address_mask: 1_u32.checked_shl(info.address_width as u32).unwrap_or(0).wrapping_sub(1),
cycle_start_clock: clock,

View File

@ -3,11 +3,9 @@ use emulator_hal::bus;
use moa_core::{System, Error, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable};
use crate::state::{M68k, M68kError};
use crate::decode::M68kDecoder;
use crate::execute::M68kCycle;
use crate::{M68k, M68kError, M68kDecoder, M68kCycle, M68kBusPort};
impl Steppable for M68k {
impl Steppable for M68k<Instant> {
fn step(&mut self, system: &System) -> Result<Duration, Error> {
let cycle = M68kCycle::new(self, system.clock);
@ -36,9 +34,9 @@ impl Steppable for M68k {
}
}
impl Interruptable for M68k {}
impl Interruptable for M68k<Instant> {}
impl Transmutable for M68k {
impl Transmutable for M68k<Instant> {
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
Some(self)
}
@ -77,7 +75,7 @@ impl<BusError: bus::Error> From<M68kError<BusError>> for Error {
}
impl Debuggable for M68k {
impl Debuggable for M68k<Instant> {
fn add_breakpoint(&mut self, addr: Address) {
self.debugger.breakpoints.push(addr as u32);
}
@ -98,12 +96,13 @@ impl Debuggable for M68k {
fn print_disassembly(&mut self, system: &System, addr: Address, count: usize) {
let mut decoder = M68kDecoder::new(self.info.chip, true, 0);
let mut memory = M68kBusPort::from_info(&self.info, system.clock);
let mut bus = system.bus.borrow_mut();
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> =
bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err);
decoder.dump_disassembly(&mut adapter, addr as u32, count as u32);
decoder.dump_disassembly(&mut adapter, &mut memory, addr as u32, count as u32);
}
fn run_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error> {

View File

@ -1,9 +1,11 @@
use core::fmt::{self, Write};
use femtos::{Duration, Frequency};
// m68k CPU State
use crate::debugger::M68kDebugger;
use femtos::Frequency;
use core::fmt::{self, Write};
use emulator_hal::time;
use crate::{M68kDebugger, M68kCycle};
use crate::instructions::Target;
use crate::execute::M68kCycle;
pub type ClockCycles = u16;
@ -195,27 +197,23 @@ pub enum M68kError<BusError> {
#[derive(Clone)]
pub struct M68kStatistics {
pub cycle_number: usize,
pub last_update: usize,
pub last_time: std::time::SystemTime,
}
impl Default for M68kStatistics {
fn default() -> Self {
Self {
cycle_number: 0,
last_update: 0,
last_time: std::time::SystemTime::now(),
}
}
}
#[derive(Clone)]
pub struct M68k {
pub struct M68k<Instant> {
pub info: CpuInfo,
pub state: M68kState,
pub debugger: M68kDebugger,
pub stats: M68kStatistics,
pub cycle: Option<M68kCycle>,
pub cycle: Option<M68kCycle<Instant>>,
}
impl Default for M68kState {
@ -251,7 +249,10 @@ impl M68kState {
}
}
impl M68k {
impl<Instant> M68k<Instant>
where
Instant: time::Instant,
{
pub fn new(info: CpuInfo) -> Self {
M68k {
info,
@ -279,9 +280,10 @@ impl M68k {
}
#[inline]
pub fn last_cycle_duration(&self) -> Duration {
pub fn last_cycle_duration(&self) -> Instant::Duration {
let clocks = self.cycle.as_ref().map(|cycle| cycle.timing.calculate_clocks()).unwrap_or(4);
self.info.frequency.period_duration() * clocks as u64
//self.info.frequency.period_duration() * clocks as u64
Instant::hertz_to_duration(self.info.frequency.as_hz() as u64) * clocks as u32
}
}

View File

@ -1,3 +1,5 @@
// m68k Instruction Timing Calclator
use crate::M68kType;
use crate::state::ClockCycles;
use crate::instructions::{Size, Sign, Direction, Target, Instruction};

View File

@ -145,7 +145,7 @@ pub fn run_system_for(handle: &mut SystemHandle, nanos: u32) -> usize {
log::error!("{:?}", err);
}
let run_time = run_timer.elapsed().as_millis();
log::debug!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000, run_time);
log::debug!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000_u32, run_time);
run_time as usize
}
@ -272,7 +272,7 @@ fn update(emulator: Rc<RefCell<Emulator>>) {
log::error!("{:?}", err);
}
let run_time = run_timer.elapsed().as_millis();
log::debug!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000, run_time);
log::debug!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000_u32, run_time);
let running = emulator.borrow().running;
if running {

View File

@ -768,7 +768,7 @@ impl Steppable for Ym7101 {
});
}
Ok(Frequency::from_hz(13_423_294).period_duration() * 4)
Ok(Frequency::from_hz(13_423_294).period_duration() * 4_u32)
}
}

View File

@ -146,7 +146,7 @@ impl TestCase {
#[allow(clippy::uninit_vec)]
fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k, MemoryBlock<u32, Instant>), Error> {
fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k<Instant>, MemoryBlock<u32, Instant>), Error> {
// Insert basic initialization
let len = 0x100_0000;
let mut data = Vec::with_capacity(len);
@ -174,7 +174,7 @@ where
}
}
fn load_state(cpu: &mut M68k, memory: &mut MemoryBlock<u32, Instant>, initial: &TestState) -> Result<(), Error> {
fn load_state(cpu: &mut M68k<Instant>, memory: &mut MemoryBlock<u32, Instant>, initial: &TestState) -> Result<(), Error> {
cpu.state.d_reg[0] = initial.d0;
cpu.state.d_reg[1] = initial.d1;
cpu.state.d_reg[2] = initial.d2;
@ -213,7 +213,7 @@ fn load_state(cpu: &mut M68k, memory: &mut MemoryBlock<u32, Instant>, initial: &
Ok(())
}
fn assert_state(cpu: &M68k, memory: &mut MemoryBlock<u32, Instant>, expected: &TestState) -> Result<(), Error> {
fn assert_state(cpu: &M68k<Instant>, memory: &mut MemoryBlock<u32, Instant>, expected: &TestState) -> Result<(), Error> {
assert_value(cpu.state.d_reg[0], expected.d0, "d0")?;
assert_value(cpu.state.d_reg[1], expected.d1, "d1")?;
assert_value(cpu.state.d_reg[2], expected.d2, "d2")?;
@ -258,7 +258,7 @@ fn assert_state(cpu: &M68k, memory: &mut MemoryBlock<u32, Instant>, expected: &T
}
fn step_cpu_and_assert(
cpu: &mut M68k,
cpu: &mut M68k<Instant>,
memory: &mut MemoryBlock<u32, Instant>,
case: &TestCase,
test_timing: bool,

View File

@ -1,7 +1,11 @@
* implement Inspect/Debug traits
* fix dump_state everywhere, which now requires a writer. Is there an easier way? Is there a way that doesn't require std
* decide if you should continue expecting Instant to usable through the trait alone, despite issues
* fix it to use the full 68k address space, and maybe see if it's possible to make the address translation cleaner/nicer/simpler/faster
* now that you have Instant as generic in m68k, try making it an associated type to see if it's possible to hide it away
* try using the debug and inspect traits elsewhere in moa
* convert all code to use fmt::Writer instead of println
* figure out how to do interrupts, and add them to emulator-hal, implement them in m68k
* convert the Z80
* convert peripherals to use BusAccess and Step
@ -15,6 +19,8 @@
-----
* add pub use for all the pub types to the lib.rs of the m68k crate
* there are many issues with the coprocessor address space, and the VDP
* I mapped the sn sound chip into 0xC00010, in the middle of the VDP's address space, and didn't get a runtime error!!! needs fixing
* there should be a better way of aliasing addresses. Can you make the actual Z80 bus get mapped into 0xA00000?