From 342bb8aa3d166aa4dd47b3d93e353f80b6e4487a Mon Sep 17 00:00:00 2001 From: transistor fet Date: Sun, 23 Jun 2024 19:42:36 -0700 Subject: [PATCH] Converted Z80 to use emulator-hal traits (#7) * Converted Z80 to use emulator-hal traits * Updated emulator-hal * Added a hacky Signalable trait to replace the Z80 signals * Minor fixes * Fixed timing tests and added no io tests option * Fixed genesis Z80 bus issue * Fixed addressing for BusAccess impl of Z80 * Fixed tests and clippy lints --- Cargo.lock | 4 +- README.md | 6 +- emulator/core/Cargo.toml | 2 +- emulator/core/src/devices.rs | 15 + emulator/core/src/error.rs | 6 + emulator/core/src/lib.rs | 5 +- emulator/core/src/memory.rs | 26 +- emulator/cpus/m68k/src/debugger.rs | 10 +- emulator/cpus/m68k/src/decode.rs | 2 +- emulator/cpus/m68k/src/execute.rs | 10 +- emulator/cpus/m68k/src/lib.rs | 2 +- emulator/cpus/m68k/src/memory.rs | 7 +- emulator/cpus/m68k/src/moa.rs | 24 +- emulator/cpus/m68k/src/state.rs | 4 +- emulator/cpus/m68k/src/tests.rs | 9 +- emulator/cpus/m68k/tests/decode_tests.rs | 4 +- emulator/cpus/m68k/tests/execute_tests.rs | 5 +- .../cpus/m68k/tests/musashi_timing_tests.rs | 4 +- emulator/cpus/m68k/tests/timing_tests.rs | 4 +- emulator/cpus/z80/Cargo.toml | 12 +- emulator/cpus/z80/src/debugger.rs | 55 +--- emulator/cpus/z80/src/decode.rs | 287 ++++++++++-------- emulator/cpus/z80/src/emuhal.rs | 174 +++++++++++ emulator/cpus/z80/src/execute.rs | 273 +++++++++-------- emulator/cpus/z80/src/lib.rs | 27 +- emulator/cpus/z80/src/moa.rs | 152 ++++++++++ emulator/cpus/z80/src/state.rs | 132 ++++---- emulator/cpus/z80/src/timing.rs | 16 +- emulator/cpus/z80/tests/decode_tests.rs | 44 +-- emulator/cpus/z80/tests/execute_tests.rs | 47 +-- .../frontends/minifb/src/bin/moa-trs80.rs | 2 +- emulator/libraries/emulator-hal | 2 +- emulator/libraries/host/Cargo.toml | 2 +- emulator/libraries/signals/src/lib.rs | 2 +- emulator/systems/genesis/Cargo.toml | 2 +- emulator/systems/genesis/src/system.rs | 15 +- emulator/systems/trs80/src/system.rs | 8 +- tests/harte_tests/src/main.rs | 13 +- tests/rad_tests/Cargo.toml | 3 +- tests/rad_tests/latest.txt | 4 +- tests/rad_tests/run_all.sh | 5 +- tests/rad_tests/src/main.rs | 129 ++++---- todo.txt | 18 +- 43 files changed, 1017 insertions(+), 556 deletions(-) create mode 100644 emulator/cpus/z80/src/emuhal.rs create mode 100644 emulator/cpus/z80/src/moa.rs diff --git a/Cargo.lock b/Cargo.lock index c8201a5..daff098 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -994,6 +994,7 @@ name = "moa-z80" version = "0.1.0" dependencies = [ "emulator-hal", + "emulator-hal-memory", "femtos", "log", "moa-core", @@ -1247,9 +1248,10 @@ name = "rad-tests" version = "0.1.0" dependencies = [ "clap 3.2.25", + "emulator-hal", + "emulator-hal-memory", "femtos", "flate2", - "moa-core", "moa-z80", "serde", "serde_derive", diff --git a/README.md b/README.md index 3e2bfbe..8fb516c 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,10 @@ For more about the Sega Genesis support, check out this series I wrote about imp I've also generated rustdocs of the workspace. All the various crates within moa are listed in the crates section in the sidebar on the left. There's not a lot of doc comments in the code yet but I plan to eventually write more: -[rustdocs for moa_core](http://jabberwocky.ca/moa/doc/moa_core/) -[rustdocs for ym2612](http://jabberwocky.ca/moa/doc/moa_peripherals_yamaha/ym2612/index.html) +[moa_core](http://jabberwocky.ca/moa/doc/moa_core/) +[moa_m68k](http://jabberwocky.ca/moa/doc/moa_m68k/) +[moa_z80](http://jabberwocky.ca/moa/doc/moa_z80/) +[ym2612](http://jabberwocky.ca/moa/doc/moa_peripherals_yamaha/ym2612/index.html) This repository uses submodules, so make sure to clone with ```sh diff --git a/emulator/core/Cargo.toml b/emulator/core/Cargo.toml index ec434d7..fa3cda6 100644 --- a/emulator/core/Cargo.toml +++ b/emulator/core/Cargo.toml @@ -8,4 +8,4 @@ log = "0.4" femtos = "0.1" thiserror = "1.0" moa-host = { path = "../libraries/host" } -emulator-hal = { path = "../libraries/emulator-hal/emulator-hal" } +emulator-hal = { path = "../libraries/emulator-hal/emulator-hal", features = ["femtos"] } diff --git a/emulator/core/src/devices.rs b/emulator/core/src/devices.rs index 6ffae86..3f46c4c 100644 --- a/emulator/core/src/devices.rs +++ b/emulator/core/src/devices.rs @@ -171,6 +171,16 @@ pub trait Inspectable { fn inspect(&mut self, system: &System, args: &[&str]) -> Result<(), Error>; } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Signal { + Reset, + BusRequest, +} + +pub trait Signalable { + fn set_signal(&mut self, signal: Signal, flag: bool) -> Result<(), Error>; + fn signal(&mut self, signal: Signal) -> Option; +} pub trait Transmutable { #[inline] @@ -197,6 +207,11 @@ pub trait Transmutable { fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> { None } + + #[inline] + fn as_signalable(&mut self) -> Option<&mut dyn Signalable> { + None + } } pub type TransmutableBox = Rc>>; diff --git a/emulator/core/src/error.rs b/emulator/core/src/error.rs index ff1ea22..6cdc985 100644 --- a/emulator/core/src/error.rs +++ b/emulator/core/src/error.rs @@ -71,3 +71,9 @@ impl From> for Error { Self::Other("other".to_string()) } } + +impl From for Error { + fn from(err: fmt::Error) -> Self { + Self::Other(format!("{:?}", err)) + } +} diff --git a/emulator/core/src/lib.rs b/emulator/core/src/lib.rs index c0c332f..a13dc08 100644 --- a/emulator/core/src/lib.rs +++ b/emulator/core/src/lib.rs @@ -7,7 +7,8 @@ mod memory; mod system; pub use crate::devices::{ - Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Device, + Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Signalable, Signal, 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, @@ -17,4 +18,4 @@ pub use crate::interrupts::InterruptController; pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice, dump_memory}; pub use crate::system::System; -pub use emulator_hal::bus::{BusAccess}; +pub use emulator_hal::BusAccess; diff --git a/emulator/core/src/memory.rs b/emulator/core/src/memory.rs index b5e545b..5c9ae57 100644 --- a/emulator/core/src/memory.rs +++ b/emulator/core/src/memory.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use std::cell::RefCell; use std::fmt::Write; use femtos::Instant; +use emulator_hal::{self, BusAccess, ErrorType}; use crate::error::Error; use crate::devices::{Address, Addressable, Transmutable, Device, read_beu16}; @@ -236,7 +237,7 @@ impl Bus { let to = if count < 16 { count / 2 } else { 8 }; for _ in 0..to { - let word = self.read_beu16(clock, addr); + let word = Addressable::read_beu16(self, clock, addr); if word.is_err() { println!("{}", line); return; @@ -353,7 +354,7 @@ impl Addressable for BusPort { for i in (0..data.len()).step_by(self.data_width as usize) { let addr_index = (addr + i as Address) & self.address_mask; let end = cmp::min(i + self.data_width as usize, data.len()); - subdevice.read(clock, addr_index, &mut data[i..end])?; + Addressable::read(&mut *subdevice, clock, addr_index, &mut data[i..end])?; } Ok(()) } @@ -364,7 +365,7 @@ impl Addressable for BusPort { for i in (0..data.len()).step_by(self.data_width as usize) { let addr_index = (addr + i as Address) & self.address_mask; let end = cmp::min(i + self.data_width as usize, data.len()); - subdevice.write(clock, addr_index, &data[i..end])?; + Addressable::write(&mut *subdevice, clock, addr_index, &data[i..end])?; } Ok(()) } @@ -412,9 +413,7 @@ where } } -use emulator_hal::bus::{self, BusAccess}; - -impl bus::Error for Error {} +impl ErrorType for Error {} impl BusAccess for &mut dyn Addressable { type Instant = Instant; @@ -430,3 +429,18 @@ impl BusAccess for &mut dyn Addressable { Ok(data.len()) } } + +impl BusAccess for Bus { + type Instant = Instant; + type Error = Error; + + fn read(&mut self, now: Instant, addr: Address, data: &mut [u8]) -> Result { + Addressable::read(self, now, addr, data)?; + Ok(data.len()) + } + + fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result { + Addressable::write(self, now, addr, data)?; + Ok(data.len()) + } +} diff --git a/emulator/cpus/m68k/src/debugger.rs b/emulator/cpus/m68k/src/debugger.rs index d438944..755b6d4 100644 --- a/emulator/cpus/m68k/src/debugger.rs +++ b/emulator/cpus/m68k/src/debugger.rs @@ -2,9 +2,7 @@ use core::fmt; -use emulator_hal::time; -use emulator_hal::bus::{self, BusAccess}; -use emulator_hal::step::{Inspect, Debug}; +use emulator_hal::{Instant as BusInstant, ErrorType, BusAccess, Inspect, Debug}; use crate::{M68k, M68kError, M68kAddress, M68kCycleExecutor}; @@ -31,7 +29,7 @@ pub enum M68kInfo { impl Inspect for M68k where Bus: BusAccess, - BusError: bus::Error, + BusError: ErrorType, Writer: fmt::Write, { type InfoType = M68kInfo; @@ -60,8 +58,8 @@ where impl Debug for M68k where Bus: BusAccess, - BusError: bus::Error, - Instant: time::Instant, + BusError: ErrorType, + Instant: BusInstant, Writer: fmt::Write, { // TODO this should be a new type diff --git a/emulator/cpus/m68k/src/decode.rs b/emulator/cpus/m68k/src/decode.rs index fd2c891..62704be 100644 --- a/emulator/cpus/m68k/src/decode.rs +++ b/emulator/cpus/m68k/src/decode.rs @@ -1,7 +1,7 @@ // Instruction Decoding use core::marker::PhantomData; -use emulator_hal::bus::BusAccess; +use emulator_hal::BusAccess; use crate::{M68kType, M68kError, M68kBusPort, M68kAddress, Exceptions}; use crate::instructions::{ diff --git a/emulator/cpus/m68k/src/execute.rs b/emulator/cpus/m68k/src/execute.rs index e2fadb7..4f8fd13 100644 --- a/emulator/cpus/m68k/src/execute.rs +++ b/emulator/cpus/m68k/src/execute.rs @@ -1,8 +1,6 @@ // Instruction Execution -use emulator_hal::time; -use emulator_hal::step::Step; -use emulator_hal::bus::{self, BusAccess}; +use emulator_hal::{Instant as BusInstant, ErrorType, BusAccess, Step}; use crate::{M68k, M68kType, M68kError, M68kState}; use crate::state::{Status, Flags, Exceptions, InterruptPriority}; @@ -35,7 +33,7 @@ pub struct M68kCycle { impl M68kCycle where - Instant: time::Instant, + Instant: BusInstant, { #[inline] pub fn default(cputype: M68kType, data_width: u8) -> Self { @@ -77,8 +75,8 @@ where impl Step for M68k where Bus: BusAccess, - BusError: bus::Error, - Instant: time::Instant, + BusError: ErrorType, + Instant: BusInstant, { type Error = M68kError; diff --git a/emulator/cpus/m68k/src/lib.rs b/emulator/cpus/m68k/src/lib.rs index 6e8a942..166fa1a 100644 --- a/emulator/cpus/m68k/src/lib.rs +++ b/emulator/cpus/m68k/src/lib.rs @@ -18,4 +18,4 @@ 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}; +pub use crate::instructions::*; diff --git a/emulator/cpus/m68k/src/memory.rs b/emulator/cpus/m68k/src/memory.rs index f95f194..429ba2f 100644 --- a/emulator/cpus/m68k/src/memory.rs +++ b/emulator/cpus/m68k/src/memory.rs @@ -1,7 +1,6 @@ use core::cmp; use core::fmt::Write; -use emulator_hal::time; -use emulator_hal::bus::BusAccess; +use emulator_hal::{Instant as BusInstant, BusAccess}; use crate::{M68kError, CpuInfo}; use crate::state::Exceptions; @@ -65,7 +64,7 @@ impl FunctionCode { impl Default for MemoryRequest where - Instant: time::Instant, + Instant: BusInstant, { fn default() -> Self { Self { @@ -138,7 +137,7 @@ pub struct M68kBusPort { impl Default for M68kBusPort where - Instant: time::Instant, + Instant: BusInstant, { fn default() -> Self { Self { diff --git a/emulator/cpus/m68k/src/moa.rs b/emulator/cpus/m68k/src/moa.rs index 7ab9703..d2d84eb 100644 --- a/emulator/cpus/m68k/src/moa.rs +++ b/emulator/cpus/m68k/src/moa.rs @@ -1,5 +1,5 @@ use femtos::{Instant, Duration}; -use emulator_hal::bus; +use emulator_hal::{ErrorType, BusAdapter}; use moa_core::{System, Error, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable}; @@ -10,8 +10,7 @@ impl Steppable for M68k { let cycle = M68kCycle::new(self, system.clock); let mut bus = system.bus.borrow_mut(); - let mut adapter: bus::BusAdapter = - bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err); + let mut adapter: BusAdapter = BusAdapter::new(&mut *bus, |addr| addr as u64); let mut executor = cycle.begin(self, &mut adapter); executor.check_breakpoints()?; @@ -60,7 +59,7 @@ impl From for M68kError { } } -impl From> for Error { +impl From> for Error { fn from(err: M68kError) -> Self { match err { M68kError::Halted => Self::Other("cpu halted".to_string()), @@ -86,11 +85,17 @@ impl Debuggable for M68k { } } - fn print_current_step(&mut self, _system: &System) -> Result<(), Error> { + fn print_current_step(&mut self, system: &System) -> Result<(), Error> { + let mut bus = system.bus.borrow_mut(); + let mut adapter: BusAdapter = BusAdapter::new(&mut *bus, |addr| addr as u64); + // TODO this is called by the debugger, but should be called some other way - //let _ = self.decoder.decode_at(&mut self.bus, true, self.state.pc); - //self.decoder.dump_decoded(&mut self.bus); - //self.dump_state(); + let mut decoder = M68kDecoder::new(self.info.chip, true, self.state.pc); + decoder.decode_at(&mut adapter, &mut M68kBusPort::default(), true, self.state.pc)?; + decoder.dump_decoded(system.clock, &mut adapter); + let mut writer = String::new(); + self.dump_state(&mut writer)?; + println!("{}", writer); Ok(()) } @@ -99,8 +104,7 @@ impl Debuggable for M68k { let mut memory = M68kBusPort::from_info(&self.info, system.clock); let mut bus = system.bus.borrow_mut(); - let mut adapter: bus::BusAdapter = - bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err); + let mut adapter: BusAdapter = BusAdapter::new(&mut *bus, |addr| addr as u64); decoder.dump_disassembly(&mut adapter, &mut memory, addr as u32, count as u32); } diff --git a/emulator/cpus/m68k/src/state.rs b/emulator/cpus/m68k/src/state.rs index 4dbe34a..1dfdac5 100644 --- a/emulator/cpus/m68k/src/state.rs +++ b/emulator/cpus/m68k/src/state.rs @@ -2,7 +2,7 @@ use femtos::Frequency; use core::fmt::{self, Write}; -use emulator_hal::time; +use emulator_hal::Instant as BusInstant; use crate::{M68kDebugger, M68kCycle}; use crate::instructions::Target; @@ -243,7 +243,7 @@ impl M68kState { impl M68k where - Instant: time::Instant, + Instant: BusInstant, { pub fn new(info: CpuInfo) -> Self { M68k { diff --git a/emulator/cpus/m68k/src/tests.rs b/emulator/cpus/m68k/src/tests.rs index facb94d..a14511a 100644 --- a/emulator/cpus/m68k/src/tests.rs +++ b/emulator/cpus/m68k/src/tests.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod decode_unit_tests { use femtos::Instant; - use emulator_hal::bus::BusAccess; + use emulator_hal::BusAccess; use emulator_hal_memory::MemoryBlock; use crate::M68kType; @@ -13,7 +13,7 @@ mod decode_unit_tests { fn run_decode_test(cputype: M68kType, mut test_func: F) where - F: FnMut(&mut InstructionDecoding<'_, MemoryBlock, Instant>), + F: FnMut(&mut InstructionDecoding<'_, MemoryBlock, Instant>), { let mut memory = MemoryBlock::from(vec![0; 0x0000100]); let mut decoder = M68kDecoder::new(cputype, true, 0); @@ -316,8 +316,7 @@ mod decode_unit_tests { #[cfg(test)] mod execute_unit_tests { use femtos::{Instant, Frequency}; - use emulator_hal::bus::BusAccess; - use emulator_hal::step::Step; + use emulator_hal::{Step, BusAccess}; use emulator_hal_memory::MemoryBlock; use crate::{M68k, M68kType}; @@ -330,7 +329,7 @@ mod execute_unit_tests { #[allow(clippy::uninit_vec)] fn run_execute_test(cputype: M68kType, mut test_func: F) where - F: FnMut(M68kCycleExecutor<&mut MemoryBlock, Instant>), + F: FnMut(M68kCycleExecutor<&mut MemoryBlock, Instant>), { // Insert basic initialization let len = 0x10_0000; diff --git a/emulator/cpus/m68k/tests/decode_tests.rs b/emulator/cpus/m68k/tests/decode_tests.rs index b0843b9..0c657c0 100644 --- a/emulator/cpus/m68k/tests/decode_tests.rs +++ b/emulator/cpus/m68k/tests/decode_tests.rs @@ -1,5 +1,5 @@ use femtos::{Instant, Frequency}; -use emulator_hal::bus::BusAccess; +use emulator_hal::BusAccess; use emulator_hal_memory::MemoryBlock; use moa_m68k::{M68k, M68kType, M68kAddress}; @@ -64,7 +64,7 @@ const DECODE_TESTS: &'static [TestCase] = &[ ]; -fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock) { +fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock) { // Insert basic initialization let len = 0x2000; let mut data = Vec::with_capacity(len); diff --git a/emulator/cpus/m68k/tests/execute_tests.rs b/emulator/cpus/m68k/tests/execute_tests.rs index a26e221..8fb8308 100644 --- a/emulator/cpus/m68k/tests/execute_tests.rs +++ b/emulator/cpus/m68k/tests/execute_tests.rs @@ -1,6 +1,5 @@ use femtos::{Instant, Frequency}; -use emulator_hal::bus::BusAccess; -use emulator_hal::step::Step; +use emulator_hal::{BusAccess, Step}; use emulator_hal_memory::MemoryBlock; use moa_m68k::{M68k, M68kType, M68kAddress}; @@ -38,7 +37,7 @@ struct TestCase { #[allow(clippy::uninit_vec)] fn run_execute_test(cputype: M68kType, mut test_func: F) where - F: FnMut(M68kCycleExecutor<&mut MemoryBlock, Instant>), + F: FnMut(M68kCycleExecutor<&mut MemoryBlock, Instant>), { // Insert basic initialization let len = 0x10_0000; diff --git a/emulator/cpus/m68k/tests/musashi_timing_tests.rs b/emulator/cpus/m68k/tests/musashi_timing_tests.rs index 696915f..323646c 100644 --- a/emulator/cpus/m68k/tests/musashi_timing_tests.rs +++ b/emulator/cpus/m68k/tests/musashi_timing_tests.rs @@ -1,5 +1,5 @@ use femtos::{Instant, Frequency}; -use emulator_hal::bus::BusAccess; +use emulator_hal::BusAccess; use emulator_hal_memory::MemoryBlock; use moa_m68k::{M68k, M68kType, M68kAddress}; @@ -12,7 +12,7 @@ const INIT_STACK: M68kAddress = 0x00002000; const INIT_ADDR: M68kAddress = 0x00000010; #[allow(clippy::uninit_vec)] -fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock) { +fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock) { // Insert basic initialization let len = 0x10_0000; let mut data = Vec::with_capacity(len); diff --git a/emulator/cpus/m68k/tests/timing_tests.rs b/emulator/cpus/m68k/tests/timing_tests.rs index 8e00d8a..2f2f987 100644 --- a/emulator/cpus/m68k/tests/timing_tests.rs +++ b/emulator/cpus/m68k/tests/timing_tests.rs @@ -1,5 +1,5 @@ use femtos::{Instant, Frequency}; -use emulator_hal::bus::BusAccess; +use emulator_hal::BusAccess; use emulator_hal_memory::MemoryBlock; use moa_m68k::{M68k, M68kType, M68kAddress}; @@ -26,7 +26,7 @@ const TIMING_TESTS: &'static [TimingCase] = &[TimingCase { }]; -fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock) { +fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock) { // Insert basic initialization let len = 0x10_0000; let mut data = Vec::with_capacity(len); diff --git a/emulator/cpus/z80/Cargo.toml b/emulator/cpus/z80/Cargo.toml index 29a194d..9cbc39e 100644 --- a/emulator/cpus/z80/Cargo.toml +++ b/emulator/cpus/z80/Cargo.toml @@ -7,6 +7,14 @@ edition = "2021" log = "0.4" thiserror = "1.0" femtos = "0.1" -moa-core = { path = "../../core" } +emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal", features = ["femtos"] } + +# TODO the goal is to make these optional, or remove them entirely +moa-core = { path = "../../core", optional = true } moa-signals = { path = "../../libraries/signals" } -emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal" } + +[dev-dependencies] +emulator-hal-memory = { path = "../../libraries/emulator-hal/emulator-hal-memory" } + +[features] +moa = ["moa-core"] diff --git a/emulator/cpus/z80/src/debugger.rs b/emulator/cpus/z80/src/debugger.rs index c05e70a..f21f245 100644 --- a/emulator/cpus/z80/src/debugger.rs +++ b/emulator/cpus/z80/src/debugger.rs @@ -1,9 +1,4 @@ -use moa_core::{System, Error, Address, Debuggable}; - -use crate::state::{Z80, Z80Error}; -use crate::decode::Z80Decoder; -use crate::instructions::Register; - +use crate::state::{Z80Error, Z80Address}; #[derive(Clone, Default)] pub struct Z80Debugger { @@ -11,49 +6,15 @@ pub struct Z80Debugger { pub(crate) breakpoints: Vec, } -impl Debuggable for Z80 { - fn add_breakpoint(&mut self, addr: Address) { - self.debugger.breakpoints.push(addr as u16); - } - - 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); - } - } - - fn print_current_step(&mut self, system: &System) -> Result<(), Error> { - self.decoder.decode_at(&mut self.port, system.clock, self.state.pc)?; - self.decoder.dump_decoded(&mut self.port); - self.dump_state(system.clock); - Ok(()) - } - - fn print_disassembly(&mut self, _system: &System, addr: Address, count: usize) { - let mut decoder = Z80Decoder::default(); - decoder.dump_disassembly(&mut self.port, addr as u16, count as u16); - } - - fn run_command(&mut self, _system: &System, args: &[&str]) -> Result { - match args[0] { - "l" => self.state.reg[Register::L as usize] = 0x05, - _ => { - return Ok(true); - }, - } - Ok(false) - } -} - -impl Z80 { - pub fn check_breakpoints(&mut self) -> Result<(), Z80Error> { - for breakpoint in &self.debugger.breakpoints { - if *breakpoint == self.state.pc { - if self.debugger.skip_breakpoint > 0 { - self.debugger.skip_breakpoint -= 1; +impl Z80Debugger { + pub fn check_breakpoints(&mut self, pc: Z80Address) -> Result<(), Z80Error> { + for breakpoint in &self.breakpoints { + if *breakpoint == pc { + if self.skip_breakpoint > 0 { + self.skip_breakpoint -= 1; return Ok(()); } else { - self.debugger.skip_breakpoint = 1; + self.skip_breakpoint = 1; return Err(Z80Error::Breakpoint); } } diff --git a/emulator/cpus/z80/src/decode.rs b/emulator/cpus/z80/src/decode.rs index 2e47774..1c6b7b9 100644 --- a/emulator/cpus/z80/src/decode.rs +++ b/emulator/cpus/z80/src/decode.rs @@ -1,9 +1,7 @@ use core::fmt::Write; -use femtos::Instant; +use emulator_hal::{BusAccess, Instant as EmuInstant}; -use moa_core::{Address, Addressable}; - -use crate::state::Z80Error; +use crate::state::{Z80Error, Z80Address, Z80AddressSpace}; use crate::instructions::{ Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, LoadTarget, UndocumentedCopy, Instruction, @@ -15,9 +13,8 @@ use crate::instructions::{ #[derive(Clone)] pub struct Z80Decoder { - pub clock: Instant, - pub start: u16, - pub end: u16, + pub start: Z80Address, + pub end: Z80Address, pub extra_instruction_bytes: u16, pub instruction: Instruction, } @@ -25,7 +22,6 @@ pub struct Z80Decoder { impl Default for Z80Decoder { fn default() -> Self { Self { - clock: Instant::START, start: 0, end: 0, extra_instruction_bytes: 0, @@ -34,59 +30,119 @@ impl Default for Z80Decoder { } } -/* - fn read_test(&mut self, device: &mut B) -> Result - where - B: BusAccess, - { - device.read_u8(self.clock, (false, self.end as u16)) - .map_err(|err| Z80Error::BusError(format!("butts"))) +impl Z80Decoder { + fn new(start: Z80Address) -> Self { + Self { + start, + end: start, + extra_instruction_bytes: 0, + instruction: Instruction::NOP, + } } -*/ +} impl Z80Decoder { - pub fn decode_at(&mut self, memory: &mut dyn Addressable, clock: Instant, start: u16) -> Result<(), Z80Error> { - self.clock = clock; - self.start = start; - self.end = start; - self.extra_instruction_bytes = 0; - self.instruction = self.decode_one(memory)?; + pub fn decode_at(bus: &mut Bus, clock: Bus::Instant, start: Z80Address) -> Result + where + Bus: BusAccess, + { + let mut decoder: DecodeNext<'_, Bus, Bus::Instant> = DecodeNext { + clock, + bus, + decoder: Z80Decoder::new(start), + }; + decoder.decode_one()?; + Ok(decoder.decoder) + } + + pub fn dump_disassembly(bus: &mut Bus, start: Z80Address, length: Z80Address) + where + Bus: BusAccess, + { + let mut next = start; + while next < (start + length) { + match Z80Decoder::decode_at(bus, Bus::Instant::START, next) { + Ok(mut decoder) => { + decoder.dump_decoded(bus); + next = decoder.end; + }, + Err(err) => { + println!("{:?}", err); + return; + }, + } + } + } + + pub fn dump_decoded(&mut self, bus: &mut Bus) + where + Bus: BusAccess, + { + let ins_data = self.format_instruction_bytes(bus); + println!("{:#06x}: {}\n\t{:?}\n", self.start, ins_data, self.instruction); + } + + pub fn format_instruction_bytes(&mut self, bus: &mut Bus) -> String + where + Bus: BusAccess, + { + let mut ins_data = String::new(); + for offset in 0..self.end.saturating_sub(self.start) { + write!( + ins_data, + "{:02x} ", + bus.read_u8(Bus::Instant::START, Z80AddressSpace::Memory(self.start + offset)) + .unwrap() + ) + .unwrap() + } + ins_data + } +} + +pub struct DecodeNext<'a, Bus, Instant> +where + Bus: BusAccess, +{ + clock: Instant, + bus: &'a mut Bus, + decoder: Z80Decoder, +} + +impl<'a, Bus, Instant> DecodeNext<'a, Bus, Instant> +where + Bus: BusAccess, + Instant: EmuInstant, +{ + pub fn decode_one(&mut self) -> Result<(), Z80Error> { + let ins = self.read_instruction_byte()?; + self.decoder.instruction = self.decode_bare(ins, 0)?; Ok(()) } - pub fn decode_one(&mut self, memory: &mut dyn Addressable) -> Result { - let ins = self.read_instruction_byte(memory)?; - self.decode_bare(memory, ins, 0) - } - - pub fn decode_bare( - &mut self, - memory: &mut dyn Addressable, - ins: u8, - extra_instruction_bytes: u16, - ) -> Result { - self.extra_instruction_bytes = extra_instruction_bytes; + pub fn decode_bare(&mut self, ins: u8, extra_instruction_bytes: u16) -> Result { + self.decoder.extra_instruction_bytes = extra_instruction_bytes; match get_ins_x(ins) { 0 => match get_ins_z(ins) { 0 => match get_ins_y(ins) { 0 => Ok(Instruction::NOP), 1 => Ok(Instruction::EXafaf), 2 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::DJNZ(offset)) }, 3 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::JR(offset)) }, y => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::JRcc(get_condition(y - 4), offset)) }, }, 1 => { if get_ins_q(ins) == 0 { - let data = self.read_instruction_word(memory)?; + let data = self.read_instruction_word()?; Ok(Instruction::LD( LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))), LoadTarget::ImmediateWord(data), @@ -107,7 +163,7 @@ impl Z80Decoder { true => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), target)), } } else { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; match (ins >> 3) & 0x03 { 0 => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(RegisterPair::HL))), 1 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(addr))), @@ -127,7 +183,7 @@ impl Z80Decoder { 4 => Ok(Instruction::INC8(get_register(get_ins_y(ins)))), 5 => Ok(Instruction::DEC8(get_register(get_ins_y(ins)))), 6 => { - let data = self.read_instruction_byte(memory)?; + let data = self.read_instruction_byte()?; Ok(Instruction::LD(to_load_target(get_register(get_ins_y(ins))), LoadTarget::ImmediateByte(data))) }, 7 => match get_ins_y(ins) { @@ -173,21 +229,21 @@ impl Z80Decoder { } }, 2 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; Ok(Instruction::JPcc(get_condition(get_ins_y(ins)), addr)) }, 3 => match get_ins_y(ins) { 0 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; Ok(Instruction::JP(addr)) }, - 1 => self.decode_prefix_cb(memory), + 1 => self.decode_prefix_cb(), 2 => { - let port = self.read_instruction_byte(memory)?; + let port = self.read_instruction_byte()?; Ok(Instruction::OUTx(port)) }, 3 => { - let port = self.read_instruction_byte(memory)?; + let port = self.read_instruction_byte()?; Ok(Instruction::INx(port)) }, 4 => Ok(Instruction::EXsp(RegisterPair::HL)), @@ -197,7 +253,7 @@ impl Z80Decoder { _ => panic!("InternalError: impossible value"), }, 4 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; Ok(Instruction::CALLcc(get_condition(get_ins_y(ins)), addr)) }, 5 => { @@ -206,18 +262,18 @@ impl Z80Decoder { } else { match get_ins_p(ins) { 0 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; Ok(Instruction::CALL(addr)) }, - 1 => self.decode_prefix_dd_fd(memory, IndexRegister::IX), - 2 => self.decode_prefix_ed(memory), - 3 => self.decode_prefix_dd_fd(memory, IndexRegister::IY), + 1 => self.decode_prefix_dd_fd(IndexRegister::IX), + 2 => self.decode_prefix_ed(), + 3 => self.decode_prefix_dd_fd(IndexRegister::IY), _ => panic!("InternalError: impossible value"), } } }, 6 => { - let data = self.read_instruction_byte(memory)?; + let data = self.read_instruction_byte()?; Ok(get_alu_instruction(get_ins_y(ins), Target::Immediate(data))) }, 7 => Ok(Instruction::RST(get_ins_y(ins) * 8)), @@ -227,8 +283,8 @@ impl Z80Decoder { } } - pub fn decode_prefix_cb(&mut self, memory: &mut dyn Addressable) -> Result { - let ins = self.read_instruction_byte(memory)?; + pub fn decode_prefix_cb(&mut self) -> Result { + let ins = self.read_instruction_byte()?; match get_ins_x(ins) { 0 => Ok(get_rot_instruction(get_ins_y(ins), get_register(get_ins_z(ins)), None)), 1 => Ok(Instruction::BIT(get_ins_y(ins), get_register(get_ins_z(ins)))), @@ -238,9 +294,9 @@ impl Z80Decoder { } } - pub fn decode_sub_prefix_cb(&mut self, memory: &mut dyn Addressable, reg: IndexRegister) -> Result { - let offset = self.read_instruction_byte(memory)? as i8; - let ins = self.read_instruction_byte(memory)?; + pub fn decode_sub_prefix_cb(&mut self, reg: IndexRegister) -> Result { + let offset = self.read_instruction_byte()? as i8; + let ins = self.read_instruction_byte()?; let opt_copy = match get_ins_z(ins) { 6 => None, //Some(Target::DirectReg(Register::F)), z => Some(get_register(z)), @@ -255,8 +311,8 @@ impl Z80Decoder { } } - pub fn decode_prefix_ed(&mut self, memory: &mut dyn Addressable) -> Result { - let ins = self.read_instruction_byte(memory)?; + pub fn decode_prefix_ed(&mut self) -> Result { + let ins = self.read_instruction_byte()?; match get_ins_x(ins) { 0 => Ok(Instruction::NOP), @@ -285,7 +341,7 @@ impl Z80Decoder { } }, 3 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; if get_ins_q(ins) == 0 { Ok(Instruction::LD( LoadTarget::IndirectWord(addr), @@ -348,11 +404,11 @@ impl Z80Decoder { } } - pub fn decode_prefix_dd_fd(&mut self, memory: &mut dyn Addressable, index_reg: IndexRegister) -> Result { - let ins = self.read_instruction_byte(memory)?; + pub fn decode_prefix_dd_fd(&mut self, index_reg: IndexRegister) -> Result { + let ins = self.read_instruction_byte()?; if ins == 0xCB { - return self.decode_sub_prefix_cb(memory, index_reg); + return self.decode_sub_prefix_cb(index_reg); } match get_ins_x(ins) { @@ -364,11 +420,11 @@ impl Z80Decoder { match get_ins_p(ins) { 2 => match get_ins_z(ins) { 1 => { - let data = self.read_instruction_word(memory)?; + let data = self.read_instruction_word()?; Ok(Instruction::LD(LoadTarget::DirectRegWord(index_reg.into()), LoadTarget::ImmediateWord(data))) }, 2 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; let regpair = index_reg.into(); match get_ins_q(ins) != 0 { false => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(regpair))), @@ -380,50 +436,50 @@ impl Z80Decoder { true => Ok(Instruction::DEC16(index_reg.into())), }, 4 => { - self.extra_instruction_bytes = 4; + self.decoder.extra_instruction_bytes = 4; let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins))); Ok(Instruction::INC8(half_target)) }, 5 => { - self.extra_instruction_bytes = 4; + self.decoder.extra_instruction_bytes = 4; let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins))); Ok(Instruction::DEC8(half_target)) }, 6 => { - self.extra_instruction_bytes = 4; + self.decoder.extra_instruction_bytes = 4; let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins))); - let data = self.read_instruction_byte(memory)?; + let data = self.read_instruction_byte()?; Ok(Instruction::LD(to_load_target(half_target), LoadTarget::ImmediateByte(data))) }, - _ => self.decode_bare(memory, ins, 4), + _ => self.decode_bare(ins, 4), }, 3 => match ins { 0x34 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::INC8(Target::IndirectOffset(index_reg, offset))) }, 0x35 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::DEC8(Target::IndirectOffset(index_reg, offset))) }, 0x36 => { - let offset = self.read_instruction_byte(memory)? as i8; - let immediate = self.read_instruction_byte(memory)?; + let offset = self.read_instruction_byte()? as i8; + let immediate = self.read_instruction_byte()?; Ok(Instruction::LD( LoadTarget::IndirectOffsetByte(index_reg, offset), LoadTarget::ImmediateByte(immediate), )) }, - _ => self.decode_bare(memory, ins, 4), + _ => self.decode_bare(ins, 4), }, - _ => self.decode_bare(memory, ins, 4), + _ => self.decode_bare(ins, 4), } }, 1 => match get_ins_p(ins) { 0 | 1 => { - let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? { + let target = match self.decode_index_target(index_reg, get_ins_z(ins))? { Some(target) => target, - None => return self.decode_bare(memory, ins, 4), + None => return self.decode_bare(ins, 4), }; match (ins & 0x18) >> 3 { @@ -443,7 +499,7 @@ impl Z80Decoder { 4 => Target::DirectRegHalf(get_index_register_half(index_reg, 0)), 5 => Target::DirectRegHalf(get_index_register_half(index_reg, 1)), 6 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; let src = to_load_target(Target::IndirectOffset(index_reg, offset)); if get_ins_q(ins) == 0 { return Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::H), src)); @@ -461,15 +517,15 @@ impl Z80Decoder { 3 => { if get_ins_q(ins) == 0 { if get_ins_z(ins) == 6 { - return self.decode_bare(memory, ins, 4); + return self.decode_bare(ins, 4); } let src = get_register(get_ins_z(ins)); - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::LD(LoadTarget::IndirectOffsetByte(index_reg, offset), to_load_target(src))) } else { - let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? { + let target = match self.decode_index_target(index_reg, get_ins_z(ins))? { Some(target) => target, - None => return self.decode_bare(memory, ins, 4), + None => return self.decode_bare(ins, 4), }; Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), to_load_target(target))) @@ -478,11 +534,11 @@ impl Z80Decoder { _ => panic!("InternalError: impossible value"), }, 2 => { - self.extra_instruction_bytes = 4; + self.decoder.extra_instruction_bytes = 4; - let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? { + let target = match self.decode_index_target(index_reg, get_ins_z(ins))? { Some(target) => target, - None => return self.decode_bare(memory, ins, 4), + None => return self.decode_bare(ins, 4), }; match get_ins_y(ins) { @@ -506,23 +562,18 @@ impl Z80Decoder { LoadTarget::DirectRegWord(RegisterPair::SP), LoadTarget::DirectRegWord(index_reg.into()), )), - _ => self.decode_bare(memory, ins, 4), + _ => self.decode_bare(ins, 4), }, _ => panic!("InternalError: impossible value"), } } - fn decode_index_target( - &mut self, - memory: &mut dyn Addressable, - index_reg: IndexRegister, - z: u8, - ) -> Result, Z80Error> { + fn decode_index_target(&mut self, index_reg: IndexRegister, z: u8) -> Result, Z80Error> { let result = match z { 4 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 0))), 5 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 1))), 6 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Some(Target::IndirectOffset(index_reg, offset)) }, _ => None, @@ -531,45 +582,25 @@ impl Z80Decoder { } - fn read_instruction_byte(&mut self, device: &mut dyn Addressable) -> Result { - let byte = device.read_u8(self.clock, self.end as Address)?; - self.end = self.end.wrapping_add(1); + fn read_instruction_byte(&mut self) -> Result { + let byte = self + .bus + .read_u8(self.clock, Z80AddressSpace::Memory(self.decoder.end)) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; + self.decoder.end = self.decoder.end.wrapping_add(1); Ok(byte) } - fn read_instruction_word(&mut self, device: &mut dyn Addressable) -> Result { - let word = device.read_leu16(self.clock, self.end as Address)?; - self.end = self.end.wrapping_add(2); - Ok(word) - } - - pub fn format_instruction_bytes(&mut self, memory: &mut dyn Addressable) -> String { - let mut ins_data = String::new(); - for offset in 0..self.end.saturating_sub(self.start) { - write!(ins_data, "{:02x} ", memory.read_u8(self.clock, (self.start + offset) as Address).unwrap()).unwrap() - } - ins_data - } - - pub fn dump_decoded(&mut self, memory: &mut dyn Addressable) { - let ins_data = self.format_instruction_bytes(memory); - println!("{:#06x}: {}\n\t{:?}\n", self.start, ins_data, self.instruction); - } - - pub fn dump_disassembly(&mut self, memory: &mut dyn Addressable, start: u16, length: u16) { - let mut next = start; - while next < (start + length) { - match self.decode_at(memory, self.clock, next) { - Ok(()) => { - self.dump_decoded(memory); - next = self.end; - }, - Err(err) => { - println!("{:?}", err); - return; - }, - } + fn read_instruction_word(&mut self) -> Result { + let mut bytes = [0; 2]; + for byte in bytes.iter_mut() { + *byte = self + .bus + .read_u8(self.clock, Z80AddressSpace::Memory(self.decoder.end)) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; + self.decoder.end = self.decoder.end.wrapping_add(1); } + Ok(u16::from_le_bytes(bytes)) } } diff --git a/emulator/cpus/z80/src/emuhal.rs b/emulator/cpus/z80/src/emuhal.rs new file mode 100644 index 0000000..db7e3d2 --- /dev/null +++ b/emulator/cpus/z80/src/emuhal.rs @@ -0,0 +1,174 @@ +use core::fmt; +use core::marker::PhantomData; +use emulator_hal::{BusAccess, Instant as EmuInstant, ErrorType, Step, Inspect, Debug}; +use crate::state::{Z80, Z80Error, Z80Address, Z80IOAddress, Z80AddressSpace, Status}; + +#[derive(Clone, Debug)] +pub enum Z80BusError +where + MemError: ErrorType, + IOError: ErrorType, +{ + Memory(MemError), + IO(IOError), +} + +impl ErrorType for Z80BusError +where + MemError: ErrorType, + IOError: ErrorType, +{ +} + +pub struct Z80Port +where + MemBus: BusAccess, + IOBus: BusAccess, +{ + mem_bus: MemBus, + io_bus: IOBus, + instant: PhantomData, +} + +impl Z80Port +where + MemBus: BusAccess, + IOBus: BusAccess, +{ + pub fn new(mem_bus: MemBus, io_bus: IOBus) -> Self { + Self { + mem_bus, + io_bus, + instant: PhantomData, + } + } +} + +impl BusAccess for Z80Port +where + Instant: EmuInstant, + MemBus: BusAccess, + IOBus: BusAccess, +{ + type Instant = Instant; + type Error = Z80BusError; + + #[inline] + fn read(&mut self, now: Self::Instant, addr: Z80AddressSpace, data: &mut [u8]) -> Result { + match addr { + Z80AddressSpace::Memory(addr) => self.mem_bus.read(now, addr, data).map_err(Z80BusError::Memory), + Z80AddressSpace::IO(addr) => self.io_bus.read(now, addr, data).map_err(Z80BusError::IO), + } + } + + #[inline] + fn write(&mut self, now: Self::Instant, addr: Z80AddressSpace, data: &[u8]) -> Result { + match addr { + Z80AddressSpace::Memory(addr) => self.mem_bus.write(now, addr, data).map_err(Z80BusError::Memory), + Z80AddressSpace::IO(addr) => self.io_bus.write(now, addr, data).map_err(Z80BusError::IO), + } + } +} + +impl ErrorType for Z80Error {} + +impl Step for Z80 +where + Instant: EmuInstant, + Bus: BusAccess, +{ + type Error = Z80Error; + + fn is_running(&mut self) -> bool { + self.state.status == Status::Running + } + + fn reset(&mut self, _now: Bus::Instant, _bus: &mut Bus) -> Result<(), Self::Error> { + self.clear_state(); + Ok(()) + } + + fn step(&mut self, now: Bus::Instant, bus: &mut Bus) -> Result { + let mut executor = self.begin(now, bus)?; + let clocks = executor.step_one()?; + self.previous_cycle = executor.end(); + Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32) + } +} + + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Z80Info { + State, +} + +impl Inspect for Z80 +where + Bus: BusAccess, + BusError: ErrorType, + Instant: EmuInstant, + Writer: fmt::Write, +{ + type InfoType = Z80Info; + + type Error = Z80Error; + + fn inspect(&mut self, info: Self::InfoType, bus: &mut Bus, writer: &mut Writer) -> Result<(), Self::Error> { + match info { + Z80Info::State => self + .dump_state(writer, Instant::START, bus) + .map_err(|_| Z80Error::Other("error while formatting state".to_string())), + } + } + + fn brief_summary(&mut self, bus: &mut Bus, writer: &mut Writer) -> Result<(), Self::Error> { + self.inspect(Z80Info::State, bus, writer) + } + + fn detailed_summary(&mut self, bus: &mut Bus, writer: &mut Writer) -> Result<(), Self::Error> { + self.inspect(Z80Info::State, bus, writer) + } +} + +/// Control the execution of a CPU device for debugging purposes +impl Debug for Z80 +where + Bus: BusAccess, + BusError: ErrorType, + Instant: EmuInstant, + Writer: fmt::Write, +{ + // TODO this should be a new type + type DebugError = Z80Error; + + fn get_execution_address(&mut self) -> Result { + Ok(Z80AddressSpace::Memory(self.state.pc)) + } + + fn set_execution_address(&mut self, address: Z80AddressSpace) -> Result<(), Self::DebugError> { + if let Z80AddressSpace::Memory(address) = address { + self.state.pc = address; + Ok(()) + } else { + Err(Z80Error::Other("PC can only be set to a memory address, given an IO address".to_string())) + } + } + + fn add_breakpoint(&mut self, address: Z80AddressSpace) { + if let Z80AddressSpace::Memory(address) = address { + self.debugger.breakpoints.push(address); + } + } + + fn remove_breakpoint(&mut self, address: Z80AddressSpace) { + if let Z80AddressSpace::Memory(address) = address { + if let Some(index) = self.debugger.breakpoints.iter().position(|a| *a == address) { + self.debugger.breakpoints.remove(index); + } + } + } + + fn clear_breakpoints(&mut self) { + self.debugger.breakpoints.clear(); + } +} diff --git a/emulator/cpus/z80/src/execute.rs b/emulator/cpus/z80/src/execute.rs index 581b8f4..2d742bb 100644 --- a/emulator/cpus/z80/src/execute.rs +++ b/emulator/cpus/z80/src/execute.rs @@ -1,13 +1,13 @@ -use femtos::{Instant, Duration}; - -use moa_core::{System, Error, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16}; +use emulator_hal::{BusAccess, Instant as EmuInstant}; +use crate::decode::Z80Decoder; use crate::instructions::{ Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister, IndexRegisterHalf, Size, Direction, UndocumentedCopy, }; -use crate::state::{Z80, Z80Error, Status, Flags}; +use crate::state::{Z80, Z80Error, Z80State, Z80Signals, Z80Address, Z80AddressSpace, Status, Flags}; use crate::timing::Z80InstructionCycles; +use crate::debugger::Z80Debugger; const FLAGS_NUMERIC: u8 = 0xC0; @@ -20,79 +20,79 @@ enum RotateType { Bit9, } -impl Steppable for Z80 { - fn step(&mut self, system: &System) -> Result { - let clocks = if self.reset.get() { - self.reset()? - } else if self.bus_request.get() { - 4 - } else { - self.step_internal(system)? - }; - - Ok(self.frequency.period_duration() * clocks as u64) - } - - fn on_error(&mut self, system: &System) { - self.dump_state(system.clock); - } -} - -impl Interruptable for Z80 {} - - -impl Transmutable for Z80 { - fn as_steppable(&mut self) -> Option<&mut dyn Steppable> { - Some(self) - } - - fn as_interruptable(&mut self) -> Option<&mut dyn Interruptable> { - Some(self) - } - - fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> { - Some(self) - } -} - -impl From for Error { - fn from(err: Z80Error) -> Self { - match err { - Z80Error::Halted => Self::Other("cpu halted".to_string()), - Z80Error::Breakpoint => Self::Breakpoint("breakpoint".to_string()), - Z80Error::Unimplemented(instruction) => Self::new(format!("unimplemented instruction {:?}", instruction)), - Z80Error::BusError(msg) => Self::Other(msg), - } - } -} - -impl From for Z80Error { - fn from(err: Error) -> Self { - match err { - Error::Processor(ex) => Z80Error::BusError(format!("processor error {}", ex)), - Error::Breakpoint(_) => Z80Error::Breakpoint, - Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => Z80Error::BusError(msg), - } - } -} #[derive(Clone)] -pub struct Z80Executor { +pub struct Z80Cycle { pub current_clock: Instant, + pub decoder: Z80Decoder, pub took_branch: bool, } -impl Z80Executor { +impl Z80Cycle { pub fn at_time(current_clock: Instant) -> Self { Self { current_clock, + decoder: Default::default(), took_branch: false, } } } -impl Z80 { - pub fn step_internal(&mut self, system: &System) -> Result { - self.executor = Z80Executor::at_time(system.clock); +impl Z80 +where + Instant: EmuInstant, +{ + pub(crate) fn begin<'a, Bus>( + &'a mut self, + clock: Instant, + bus: &'a mut Bus, + ) -> Result, Z80Error> + where + Bus: BusAccess, + { + let executor = ExecuteNext { + state: &mut self.state, + signals: &mut self.signals, + debugger: &mut self.debugger, + cycle: Z80Cycle::at_time(clock), + bus, + }; + + Ok(executor) + } +} + +pub(crate) struct ExecuteNext<'a, Bus, Instant> +where + Bus: BusAccess, +{ + state: &'a mut Z80State, + signals: &'a mut Z80Signals, + debugger: &'a mut Z80Debugger, + cycle: Z80Cycle, + bus: Bus, +} + +impl<'a, Bus, Instant> ExecuteNext<'a, Bus, Instant> +where + Bus: BusAccess, + Instant: EmuInstant, +{ + pub(crate) fn end(self) -> Z80Cycle { + self.cycle + } + + pub(crate) fn step_one(&mut self) -> Result { + let clocks = if self.signals.reset.get() { + self.reset()? + } else if self.signals.bus_request.get() { + 4 + } else { + self.step_internal()? + }; + Ok(clocks) + } + + fn step_internal(&mut self) -> Result { match self.state.status { Status::Init => self.init(), Status::Halted => Err(Z80Error::Halted), @@ -103,38 +103,37 @@ impl Z80 { } } - pub fn init(&mut self) -> Result { + fn init(&mut self) -> Result { self.state.pc = 0; self.state.status = Status::Running; Ok(16) } - pub fn reset(&mut self) -> Result { - self.clear_state(); + fn reset(&mut self) -> Result { + *self.state = Default::default(); Ok(16) } - pub fn cycle_one(&mut self) -> Result { - self.check_breakpoints()?; + fn cycle_one(&mut self) -> Result { + self.debugger.check_breakpoints(self.state.pc)?; self.decode_next()?; self.execute_current()?; Ok( - Z80InstructionCycles::from_instruction(&self.decoder.instruction, self.decoder.extra_instruction_bytes)? - .calculate_cycles(self.executor.took_branch), + Z80InstructionCycles::from_instruction(&self.cycle.decoder.instruction, self.cycle.decoder.extra_instruction_bytes)? + .calculate_cycles(self.cycle.took_branch), ) } - pub fn decode_next(&mut self) -> Result<(), Z80Error> { - self.decoder - .decode_at(&mut self.port, self.executor.current_clock, self.state.pc)?; - self.increment_refresh(self.decoder.end.saturating_sub(self.decoder.start) as u8); - self.state.pc = self.decoder.end; + fn decode_next(&mut self) -> Result<(), Z80Error> { + self.cycle.decoder = Z80Decoder::decode_at(&mut self.bus, self.cycle.current_clock, self.state.pc)?; + self.increment_refresh(self.cycle.decoder.end.saturating_sub(self.cycle.decoder.start) as u8); + self.state.pc = self.cycle.decoder.end; Ok(()) } - pub fn execute_current(&mut self) -> Result<(), Z80Error> { - match self.decoder.instruction { + fn execute_current(&mut self) -> Result<(), Z80Error> { + match self.cycle.decoder.instruction { Instruction::ADCa(target) => self.execute_adca(target), Instruction::ADC16(dest_pair, src_pair) => self.execute_adc16(dest_pair, src_pair), Instruction::ADDa(target) => self.execute_adda(target), @@ -230,7 +229,7 @@ impl Z80 { Instruction::SRL(target, opt_copy) => self.execute_srl(target, opt_copy), Instruction::SUB(target) => self.execute_sub(target), Instruction::XOR(target) => self.execute_xor(target), - _ => Err(Z80Error::Unimplemented(self.decoder.instruction.clone())), + _ => Err(Z80Error::Unimplemented(self.cycle.decoder.instruction.clone())), } } @@ -310,15 +309,15 @@ impl Z80 { } fn execute_call(&mut self, addr: u16) -> Result<(), Z80Error> { - self.push_word(self.decoder.end)?; + self.push_word(self.cycle.decoder.end)?; self.state.pc = addr; Ok(()) } fn execute_callcc(&mut self, cond: Condition, addr: u16) -> Result<(), Z80Error> { if self.get_current_condition(cond) { - self.executor.took_branch = true; - self.push_word(self.decoder.end)?; + self.cycle.took_branch = true; + self.push_word(self.cycle.decoder.end)?; self.state.pc = addr; } Ok(()) @@ -434,7 +433,7 @@ impl Z80 { self.set_register_value(Register::B, result); if result != 0 { - self.executor.took_branch = true; + self.cycle.took_branch = true; self.state.pc = self.state.pc.wrapping_add_signed(offset as i16); } Ok(()) @@ -567,7 +566,7 @@ impl Z80 { fn execute_jpcc(&mut self, cond: Condition, addr: u16) -> Result<(), Z80Error> { if self.get_current_condition(cond) { - self.executor.took_branch = true; + self.cycle.took_branch = true; self.state.pc = addr; } Ok(()) @@ -580,7 +579,7 @@ impl Z80 { fn execute_jrcc(&mut self, cond: Condition, offset: i8) -> Result<(), Z80Error> { if self.get_current_condition(cond) { - self.executor.took_branch = true; + self.cycle.took_branch = true; self.state.pc = self.state.pc.wrapping_add_signed(offset as i16); } Ok(()) @@ -616,7 +615,7 @@ impl Z80 { } fn execute_ldx(&mut self) -> Result<(), Z80Error> { - let diff = if self.decoder.instruction == Instruction::LDI || self.decoder.instruction == Instruction::LDIR { + let diff = if self.cycle.decoder.instruction == Instruction::LDI || self.cycle.decoder.instruction == Instruction::LDIR { 1 } else { -1 @@ -631,8 +630,10 @@ impl Z80 { let parity = if count != 0 { Flags::Parity as u8 } else { 0 }; self.set_flags(mask, parity); - if (self.decoder.instruction == Instruction::LDIR || self.decoder.instruction == Instruction::LDDR) && count != 0 { - self.executor.took_branch = true; + if (self.cycle.decoder.instruction == Instruction::LDIR || self.cycle.decoder.instruction == Instruction::LDDR) + && count != 0 + { + self.cycle.took_branch = true; self.state.pc -= 2; } Ok(()) @@ -725,7 +726,7 @@ impl Z80 { fn execute_retcc(&mut self, cond: Condition) -> Result<(), Z80Error> { if self.get_current_condition(cond) { - self.executor.took_branch = true; + self.cycle.took_branch = true; self.state.pc = self.pop_word()?; } Ok(()) @@ -852,7 +853,7 @@ impl Z80 { } fn execute_rst(&mut self, addr: u8) -> Result<(), Z80Error> { - self.push_word(self.decoder.end)?; + self.push_word(self.cycle.decoder.end)?; self.state.pc = addr as u16; Ok(()) } @@ -1010,13 +1011,11 @@ impl Z80 { _ => panic!("RegPair is not supported by inc/dec"), }; - let result = (read_beu16(addr) as i16).wrapping_add(value) as u16; - write_beu16(addr, result); + let result = (u16::from_be_bytes(addr.try_into().unwrap()) as i16).wrapping_add(value) as u16; + addr.copy_from_slice(&result.to_be_bytes()[..]); result } - - fn push_word(&mut self, value: u16) -> Result<(), Z80Error> { self.state.sp = self.state.sp.wrapping_sub(1); self.write_port_u8(self.state.sp, (value >> 8) as u8)?; @@ -1127,41 +1126,67 @@ impl Z80 { fn read_port_u8(&mut self, addr: u16) -> Result { self.increment_refresh(1); - Ok(self.port.read_u8(self.executor.current_clock, addr as Address)?) + self.bus + .read_u8(self.cycle.current_clock, Z80AddressSpace::Memory(addr as Z80Address)) + .map_err(|err| Z80Error::BusError(format!("{:?}", err))) } fn write_port_u8(&mut self, addr: u16, value: u8) -> Result<(), Z80Error> { self.increment_refresh(1); - Ok(self.port.write_u8(self.executor.current_clock, addr as Address, value)?) + self.bus + .write_u8(self.cycle.current_clock, Z80AddressSpace::Memory(addr as Z80Address), value) + .map_err(|err| Z80Error::BusError(format!("{:?}", err))) } - fn read_port_u16(&mut self, addr: u16) -> Result { - self.increment_refresh(2); - Ok(self.port.read_leu16(self.executor.current_clock, addr as Address)?) - } - - fn write_port_u16(&mut self, addr: u16, value: u16) -> Result<(), Z80Error> { - self.increment_refresh(2); - Ok(self.port.write_leu16(self.executor.current_clock, addr as Address, value)?) - } - - fn read_ioport_value(&mut self, upper: u8, lower: u8) -> Result { - let addr = ((upper as Address) << 8) | (lower as Address); - if let Some(io) = self.ioport.as_mut() { - Ok(io.read_u8(self.executor.current_clock, addr)?) - } else { - Ok(0) + /// Read a u16 value through this CPU's memory port + /// + /// Since the memory port is only able to read 8 bits at a time, this does two reads + /// in little endian byte order + fn read_port_u16(&mut self, mut addr: u16) -> Result { + let mut bytes = [0; 2]; + for byte in bytes.iter_mut() { + self.increment_refresh(1); + *byte = self + .bus + .read_u8(self.cycle.current_clock, Z80AddressSpace::Memory(addr)) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; + addr = addr.wrapping_add(1); } + Ok(u16::from_le_bytes(bytes)) } - fn write_ioport_value(&mut self, upper: u8, lower: u8, value: u8) -> Result<(), Z80Error> { - let addr = ((upper as Address) << 8) | (lower as Address); - if let Some(io) = self.ioport.as_mut() { - io.write_u8(self.executor.current_clock, addr, value)? + /// Write a u16 value through this CPU's memory port + /// + /// Since the memory port is only able to read 8 bits at a time, this does two writes + /// in little endian byte order + fn write_port_u16(&mut self, mut addr: u16, value: u16) -> Result<(), Z80Error> { + let mut bytes = value.to_le_bytes(); + for byte in bytes.iter_mut() { + self.increment_refresh(1); + self.bus + .write_u8(self.cycle.current_clock, Z80AddressSpace::Memory(addr), *byte) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; + addr = addr.wrapping_add(1); } Ok(()) } + fn read_ioport_value(&mut self, upper: u8, lower: u8) -> Result { + let addr = ((upper as Z80Address) << 8) | (lower as Z80Address); + let bytes_read = self + .bus + .read_u8(self.cycle.current_clock, Z80AddressSpace::IO(addr)) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; + Ok(bytes_read) + } + + fn write_ioport_value(&mut self, upper: u8, lower: u8, value: u8) -> Result<(), Z80Error> { + let addr = ((upper as Z80Address) << 8) | (lower as Z80Address); + self.bus + .write_u8(self.cycle.current_clock, Z80AddressSpace::IO(addr), value) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; + Ok(()) + } fn get_register_value(&mut self, reg: Register) -> u8 { self.state.reg[reg as usize] @@ -1199,10 +1224,10 @@ impl Z80 { fn get_register_pair_value(&mut self, regpair: RegisterPair) -> u16 { match regpair { - RegisterPair::BC => read_beu16(&self.state.reg[0..2]), - RegisterPair::DE => read_beu16(&self.state.reg[2..4]), - RegisterPair::HL => read_beu16(&self.state.reg[4..6]), - RegisterPair::AF => read_beu16(&self.state.reg[6..8]), + RegisterPair::BC => u16::from_be_bytes(self.state.reg[0..2].try_into().unwrap()), + RegisterPair::DE => u16::from_be_bytes(self.state.reg[2..4].try_into().unwrap()), + RegisterPair::HL => u16::from_be_bytes(self.state.reg[4..6].try_into().unwrap()), + RegisterPair::AF => u16::from_be_bytes(self.state.reg[6..8].try_into().unwrap()), RegisterPair::SP => self.state.sp, RegisterPair::IX => self.state.ix, RegisterPair::IY => self.state.iy, @@ -1212,16 +1237,16 @@ impl Z80 { fn set_register_pair_value(&mut self, regpair: RegisterPair, value: u16) { match regpair { RegisterPair::BC => { - write_beu16(&mut self.state.reg[0..2], value); + self.state.reg[0..2].copy_from_slice(&value.to_be_bytes()[..]); }, RegisterPair::DE => { - write_beu16(&mut self.state.reg[2..4], value); + self.state.reg[2..4].copy_from_slice(&value.to_be_bytes()[..]); }, RegisterPair::HL => { - write_beu16(&mut self.state.reg[4..6], value); + self.state.reg[4..6].copy_from_slice(&value.to_be_bytes()[..]); }, RegisterPair::AF => { - write_beu16(&mut self.state.reg[6..8], value); + self.state.reg[6..8].copy_from_slice(&value.to_be_bytes()[..]); }, RegisterPair::SP => { self.state.sp = value; diff --git a/emulator/cpus/z80/src/lib.rs b/emulator/cpus/z80/src/lib.rs index e95fcbe..3f3d8fd 100644 --- a/emulator/cpus/z80/src/lib.rs +++ b/emulator/cpus/z80/src/lib.rs @@ -1,8 +1,21 @@ -pub mod debugger; -pub mod decode; -pub mod execute; -pub mod instructions; -pub mod state; -pub mod timing; +mod debugger; +mod decode; +mod emuhal; +mod execute; +mod instructions; +mod state; +mod timing; -pub use self::state::{Z80, Z80Type, Z80Error}; +#[cfg(feature = "moa")] +pub mod moa; +#[cfg(feature = "moa")] +pub use crate::moa::MoaZ80; + +pub use crate::state::{Z80, Z80Type, Z80Address, Z80IOAddress, Z80Error, Z80State, Status, Flags}; +pub use crate::decode::Z80Decoder; +pub use crate::execute::Z80Cycle; +pub use crate::instructions::{ + Size, Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, + LoadTarget, UndocumentedCopy, Instruction, +}; +pub use crate::emuhal::Z80Port; diff --git a/emulator/cpus/z80/src/moa.rs b/emulator/cpus/z80/src/moa.rs new file mode 100644 index 0000000..c76c623 --- /dev/null +++ b/emulator/cpus/z80/src/moa.rs @@ -0,0 +1,152 @@ +use std::rc::Rc; +use std::cell::RefCell; +use femtos::{Instant, Duration}; +use emulator_hal::{BusAdapter, NoBus, Instant as EmuInstant}; + +use moa_core::{System, Error, Bus, Address, Steppable, Interruptable, /* Signalable, Signal,*/ Debuggable, Transmutable}; + +use crate::{Z80, Z80Error, Z80Decoder}; +use crate::instructions::Register; +use crate::emuhal::Z80Port; + +pub struct MoaZ80 +where + Instant: EmuInstant, +{ + pub bus: Rc>, + pub cpu: Z80, +} + +impl Steppable for MoaZ80 +where + Instant: EmuInstant, +{ + fn step(&mut self, system: &System) -> Result { + let bus = &mut *self.bus.borrow_mut(); + let mut adapter = BusAdapter::<_, _, _, Z80Error>::new(bus, |addr| addr as u64); + let mut io_bus = NoBus::new(); + let mut bus = Z80Port::new(&mut adapter, &mut io_bus); + + let mut executor = self.cpu.begin(system.clock, &mut bus)?; + let clocks = executor.step_one()?; + self.cpu.previous_cycle = executor.end(); + Ok(Instant::hertz_to_duration(self.cpu.frequency.as_hz() as u64) * clocks as u32) + } + + fn on_error(&mut self, system: &System) { + let bus = &mut *system.bus.borrow_mut(); + let mut adapter = BusAdapter::<_, _, _, Z80Error>::new(bus, |addr| addr as u64); + let mut io_bus = NoBus::new(); + let mut bus = Z80Port::new(&mut adapter, &mut io_bus); + let mut output = String::with_capacity(256); + let _ = self.cpu.dump_state(&mut output, system.clock, &mut bus); + println!("{}", output); + } +} + +impl Interruptable for MoaZ80 {} + +/* +impl Signalable for Z80 { + fn set_signal(&mut self, signal: Signal, flag: bool) -> Result<(), Error> { + match signal { + Signal::Reset => self.signals.reset = flag, + Signal::BusRequest => self.signals.bus_request = flag, + } + Ok(()) + } + + fn signal(&mut self, signal: Signal) -> Option { + match signal { + Signal::Reset => Some(self.signals.reset), + Signal::BusRequest => Some(self.signals.bus_request), + } + } +} +*/ + +impl Transmutable for MoaZ80 { + fn as_steppable(&mut self) -> Option<&mut dyn Steppable> { + Some(self) + } + + fn as_interruptable(&mut self) -> Option<&mut dyn Interruptable> { + Some(self) + } + + fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> { + Some(self) + } + + //#[inline] + //fn as_signalable(&mut self) -> Option<&mut dyn Signalable> { + // Some(self) + //} +} + +impl From for Error { + fn from(err: Z80Error) -> Self { + match err { + Z80Error::Halted => Self::Other("cpu halted".to_string()), + Z80Error::Breakpoint => Self::Breakpoint("breakpoint".to_string()), + Z80Error::Unimplemented(instruction) => Self::new(format!("unimplemented instruction {:?}", instruction)), + Z80Error::UnexpectedInstruction(instruction) => Self::new(format!("unexpected instruction {:?}", instruction)), + Z80Error::Other(msg) => Self::Other(msg), + Z80Error::BusError(msg) => Self::Other(msg), + } + } +} + +impl From for Z80Error { + fn from(err: Error) -> Self { + match err { + Error::Processor(ex) => Z80Error::BusError(format!("processor error {}", ex)), + Error::Breakpoint(_) => Z80Error::Breakpoint, + Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => Z80Error::BusError(msg), + } + } +} + +impl Debuggable for MoaZ80 { + fn add_breakpoint(&mut self, addr: Address) { + self.cpu.debugger.breakpoints.push(addr as u16); + } + + fn remove_breakpoint(&mut self, addr: Address) { + if let Some(index) = self.cpu.debugger.breakpoints.iter().position(|a| *a == addr as u16) { + self.cpu.debugger.breakpoints.remove(index); + } + } + + fn print_current_step(&mut self, system: &System) -> Result<(), Error> { + let bus = &mut *system.bus.borrow_mut(); + let mut adapter = BusAdapter::<_, _, _, Z80Error>::new(bus, |addr| addr as u64); + let mut io_bus = NoBus::new(); + let mut bus = Z80Port::new(&mut adapter, &mut io_bus); + + self.cpu.previous_cycle.decoder.dump_decoded(&mut bus); + let mut output = String::with_capacity(256); + let _ = self.cpu.dump_state(&mut output, system.clock, &mut bus); + println!("{}", output); + Ok(()) + } + + fn print_disassembly(&mut self, system: &System, addr: Address, count: usize) { + let bus = &mut *system.bus.borrow_mut(); + let mut adapter = BusAdapter::<_, _, _, Z80Error>::new(bus, |addr| addr as u64); + let mut io_bus = NoBus::new(); + let mut bus = Z80Port::new(&mut adapter, &mut io_bus); + + Z80Decoder::dump_disassembly(&mut bus, addr as u16, count as u16); + } + + fn run_command(&mut self, _system: &System, args: &[&str]) -> Result { + match args[0] { + "l" => self.cpu.state.reg[Register::L as usize] = 0x05, + _ => { + return Ok(true); + }, + } + Ok(false) + } +} diff --git a/emulator/cpus/z80/src/state.rs b/emulator/cpus/z80/src/state.rs index 2b40704..0166da9 100644 --- a/emulator/cpus/z80/src/state.rs +++ b/emulator/cpus/z80/src/state.rs @@ -1,13 +1,11 @@ -use std::rc::Rc; -use std::cell::RefCell; -use femtos::{Instant, Frequency}; +use core::fmt::{self, Write}; +use femtos::Frequency; +use emulator_hal::{Instant as EmuInstant, BusAccess}; -use moa_core::{Address, Bus, BusPort}; use moa_signals::Signal; -use crate::decode::Z80Decoder; use crate::debugger::Z80Debugger; -use crate::execute::Z80Executor; +use crate::execute::Z80Cycle; use crate::instructions::{Instruction, Register, InterruptMode}; @@ -92,6 +90,14 @@ impl Z80State { } } +#[derive(Clone, Debug, Default)] +pub struct Z80Signals { + //pub reset: bool, + //pub bus_request: bool, + pub reset: Signal, + pub bus_request: Signal, +} + #[derive(Clone, Debug, thiserror::Error)] pub enum Z80Error /* */ { #[error("cpu halted")] @@ -100,111 +106,119 @@ pub enum Z80Error /* */ { Breakpoint, #[error("unimplemented instruction {0:?}")] Unimplemented(Instruction), + #[error("unexpected instruction {0:?}")] + UnexpectedInstruction(Instruction), #[error("bus error: {0}")] BusError(String /* B */), + #[error("{0}")] + Other(String), +} + + +pub type Z80Address = u16; +pub type Z80IOAddress = u16; + +#[derive(Copy, Clone, Debug)] +pub enum Z80AddressSpace { + Memory(Z80Address), + IO(Z80IOAddress), } #[derive(Clone)] -pub struct Z80 { +pub struct Z80 { pub cputype: Z80Type, pub frequency: Frequency, pub state: Z80State, - pub decoder: Z80Decoder, pub debugger: Z80Debugger, - pub executor: Z80Executor, - pub port: BusPort, - pub ioport: Option, - pub reset: Signal, - pub bus_request: Signal, + pub previous_cycle: Z80Cycle, + pub signals: Z80Signals, } -impl Z80 { - pub fn new(cputype: Z80Type, frequency: Frequency, port: BusPort, ioport: Option) -> Self { +impl Z80 +where + Instant: EmuInstant, +{ + pub fn new(cputype: Z80Type, frequency: Frequency) -> Self { Self { cputype, frequency, state: Z80State::default(), - decoder: Z80Decoder::default(), debugger: Z80Debugger::default(), - executor: Z80Executor::at_time(Instant::START), - port, - ioport, - reset: Signal::new(false), - bus_request: Signal::new(false), + previous_cycle: Z80Cycle::at_time(Instant::START), + signals: Z80Signals::default(), } } - pub fn from_type( - cputype: Z80Type, - frequency: Frequency, - bus: Rc>, - addr_offset: Address, - io_bus: Option<(Rc>, Address)>, - ) -> Self { + pub fn from_type(cputype: Z80Type, frequency: Frequency) -> Self { match cputype { - Z80Type::Z80 => Self::new( - cputype, - frequency, - BusPort::new(addr_offset, 16, 8, bus), - io_bus.map(|(io_bus, io_offset)| BusPort::new(io_offset, 16, 8, io_bus)), - ), + Z80Type::Z80 => Self::new(cputype, frequency), } } #[allow(dead_code)] pub fn clear_state(&mut self) { self.state = Z80State::default(); - self.decoder = Z80Decoder::default(); self.debugger = Z80Debugger::default(); - self.executor = Z80Executor::at_time(Instant::START); } - pub fn dump_state(&mut self, clock: Instant) { - println!("Status: {:?}", self.state.status); - println!("PC: {:#06x}", self.state.pc); - println!("SP: {:#06x}", self.state.sp); - println!("IX: {:#06x}", self.state.ix); - println!("IY: {:#06x}", self.state.iy); + pub fn dump_state(&mut self, writer: &mut W, _clock: Instant, bus: &mut Bus) -> Result<(), fmt::Error> + where + W: Write, + Bus: BusAccess, + { + writeln!(writer, "Status: {:?}", self.state.status)?; + writeln!(writer, "PC: {:#06x}", self.state.pc)?; + writeln!(writer, "SP: {:#06x}", self.state.sp)?; + writeln!(writer, "IX: {:#06x}", self.state.ix)?; + writeln!(writer, "IY: {:#06x}", self.state.iy)?; - println!( + writeln!( + writer, "A: {:#04x} F: {:#04x} A': {:#04x} F': {:#04x}", self.state.reg[Register::A as usize], self.state.reg[Register::F as usize], self.state.shadow_reg[Register::A as usize], self.state.shadow_reg[Register::F as usize] - ); - println!( + )?; + writeln!( + writer, "B: {:#04x} C: {:#04x} B': {:#04x} C': {:#04x}", self.state.reg[Register::B as usize], self.state.reg[Register::C as usize], self.state.shadow_reg[Register::B as usize], self.state.shadow_reg[Register::C as usize] - ); - println!( + )?; + writeln!( + writer, "D: {:#04x} E: {:#04x} D': {:#04x} E': {:#04x}", self.state.reg[Register::D as usize], self.state.reg[Register::E as usize], self.state.shadow_reg[Register::D as usize], self.state.shadow_reg[Register::E as usize] - ); - println!( + )?; + writeln!( + writer, "H: {:#04x} L: {:#04x} H': {:#04x} L': {:#04x}", self.state.reg[Register::H as usize], self.state.reg[Register::L as usize], self.state.shadow_reg[Register::H as usize], self.state.shadow_reg[Register::L as usize] - ); + )?; - println!("I: {:#04x} R: {:#04x}", self.state.i, self.state.r); - println!("IM: {:?} IFF1: {:?} IFF2: {:?}", self.state.im, self.state.iff1, self.state.iff2); + writeln!(writer, "I: {:#04x} R: {:#04x}", self.state.i, self.state.r)?; + writeln!(writer, "IM: {:?} IFF1: {:?} IFF2: {:?}", self.state.im, self.state.iff1, self.state.iff2)?; - println!( + writeln!( + writer, "Current Instruction: {} {:?}", - self.decoder.format_instruction_bytes(&mut self.port), - self.decoder.instruction - ); - println!(); - self.port.dump_memory(clock, self.state.sp as Address, 0x40); - println!(); + self.previous_cycle.decoder.format_instruction_bytes(bus), + self.previous_cycle.decoder.instruction + )?; + writeln!(writer, "Previous Instruction: {:?}", self.previous_cycle.decoder.instruction)?; + writeln!(writer)?; + // TODO disabled until function is reimplemented + //self.port.dump_memory(clock, self.state.sp as Address, 0x40); + writeln!(writer)?; + Ok(()) } } diff --git a/emulator/cpus/z80/src/timing.rs b/emulator/cpus/z80/src/timing.rs index b9d3dc0..7244d14 100644 --- a/emulator/cpus/z80/src/timing.rs +++ b/emulator/cpus/z80/src/timing.rs @@ -1,6 +1,4 @@ -use moa_core::Error; - -use crate::instructions::{Instruction, Target, LoadTarget, RegisterPair}; +use crate::{Z80Error, Instruction, Target, LoadTarget, RegisterPair}; pub enum Z80InstructionCycles { Single(u16), @@ -37,7 +35,7 @@ impl Z80InstructionCycles { } } - pub fn from_instruction(instruction: &Instruction, extra: u16) -> Result { + pub fn from_instruction(instruction: &Instruction, extra: u16) -> Result { let cycles = match instruction { Instruction::ADCa(target) | Instruction::ADDa(target) @@ -67,7 +65,7 @@ impl Z80InstructionCycles { Target::DirectReg(_) => 8, Target::IndirectReg(_) => 12, Target::IndirectOffset(_, _) => 20, - _ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))), + _ => return Err(Z80Error::UnexpectedInstruction(instruction.clone())), }, Instruction::CALL(_) => 17, @@ -111,7 +109,7 @@ impl Z80InstructionCycles { Target::DirectReg(_) | Target::DirectRegHalf(_) => 4, Target::IndirectReg(_) => 11, Target::IndirectOffset(_, _) => 23, - _ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))), + _ => return Err(Z80Error::UnexpectedInstruction(instruction.clone())), }, Instruction::DEC16(regpair) | Instruction::INC16(regpair) => { @@ -210,7 +208,7 @@ impl Z80InstructionCycles { (LoadTarget::IndirectWord(_), _) | (_, LoadTarget::IndirectWord(_)) => 20, - _ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))), + _ => return Err(Z80Error::UnexpectedInstruction(instruction.clone())), } }, @@ -238,7 +236,7 @@ impl Z80InstructionCycles { Target::DirectReg(_) => 8, Target::IndirectReg(_) => 15, Target::IndirectOffset(_, _) => 23, - _ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))), + _ => return Err(Z80Error::UnexpectedInstruction(instruction.clone())), }, Instruction::RET => 10, @@ -263,7 +261,7 @@ impl Z80InstructionCycles { Target::DirectReg(_) => 8, Target::IndirectReg(_) => 15, Target::IndirectOffset(_, _) => 23, - _ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))), + _ => return Err(Z80Error::UnexpectedInstruction(instruction.clone())), }, Instruction::RLA | Instruction::RLCA | Instruction::RRA | Instruction::RRCA => 4, diff --git a/emulator/cpus/z80/tests/decode_tests.rs b/emulator/cpus/z80/tests/decode_tests.rs index d55e10e..bd8a3eb 100644 --- a/emulator/cpus/z80/tests/decode_tests.rs +++ b/emulator/cpus/z80/tests/decode_tests.rs @@ -1,36 +1,42 @@ -use femtos::Frequency; +use femtos::{Instant, Frequency}; -use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device}; +use emulator_hal::{BusAccess, Step, NoBus}; +use emulator_hal_memory::MemoryBlock; -use moa_z80::{Z80, Z80Type}; -use moa_z80::instructions::{Instruction, LoadTarget, Target, Register, RegisterPair, IndexRegister, IndexRegisterHalf}; - -fn init_decode_test() -> (Z80, System) { - let mut system = System::default(); +use moa_z80::{Z80, Z80Type, Z80Port, Instruction, LoadTarget, Target, Register, RegisterPair, IndexRegister, IndexRegisterHalf}; +fn init_decode_test() -> (Z80, MemoryBlock) { // Insert basic initialization - let data = vec![0; 0x10000]; - let mem = MemoryBlock::new(data); - system.add_addressable_device(0x0000, Device::new(mem)).unwrap(); + let len = 0x10_0000; + let mut data = Vec::with_capacity(len); + unsafe { + data.set_len(len); + } + let mut memory = MemoryBlock::from(data); + let mut io = NoBus::new(); // Initialize the CPU and make sure it's in the expected state - let mut cpu = Z80::new(Z80Type::Z80, Frequency::from_mhz(4), BusPort::new(0, 16, 8, system.bus.clone()), None); - cpu.reset().unwrap(); + let mut cpu = Z80::new(Z80Type::Z80, Frequency::from_mhz(4)); + let mut bus = Z80Port::new(&mut memory, &mut io); + cpu.reset(Instant::START, &mut bus).unwrap(); + cpu.step(Instant::START, &mut bus).unwrap(); - (cpu, system) + (cpu, memory) } -fn load_memory(system: &System, data: &[u8]) { +fn load_memory(memory: &mut MemoryBlock, data: &[u8]) { for i in 0..data.len() { - system.get_bus().write_u8(system.clock, i as Address, data[i]).unwrap(); + memory.write_u8(Instant::START, i, data[i]).unwrap(); } } fn run_decode_test(data: &[u8]) -> Instruction { - let (mut cpu, system) = init_decode_test(); - load_memory(&system, data); - cpu.decode_next().unwrap(); - cpu.decoder.instruction + let (mut cpu, mut memory) = init_decode_test(); + load_memory(&mut memory, data); + let mut io = NoBus::new(); + let mut bus = Z80Port::new(&mut memory, &mut io); + cpu.step(Instant::START, &mut bus).unwrap(); + cpu.previous_cycle.decoder.instruction } #[test] diff --git a/emulator/cpus/z80/tests/execute_tests.rs b/emulator/cpus/z80/tests/execute_tests.rs index 3c2d716..1d8b437 100644 --- a/emulator/cpus/z80/tests/execute_tests.rs +++ b/emulator/cpus/z80/tests/execute_tests.rs @@ -1,10 +1,9 @@ -use femtos::Frequency; +use femtos::{Instant, Frequency}; -use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device}; +use emulator_hal::{BusAccess, Step, NoBus}; +use emulator_hal_memory::MemoryBlock; -use moa_z80::{Z80, Z80Type}; -use moa_z80::state::Z80State; -use moa_z80::instructions::{Instruction, LoadTarget, Target, Register, RegisterPair, Condition}; +use moa_z80::{Z80, Z80Type, Z80Port, Z80State, Status, Instruction, LoadTarget, Target, Register, RegisterPair, Condition}; struct TestState { pc: u16, @@ -482,23 +481,28 @@ const TEST_CASES: &'static [TestCase] = &[ ]; -fn init_execute_test() -> (Z80, System) { - let mut system = System::default(); - +fn init_execute_test() -> (Z80, MemoryBlock) { // Insert basic initialization - let data = vec![0; 0x10000]; - let mem = MemoryBlock::new(data); - system.add_addressable_device(0x0000, Device::new(mem)).unwrap(); + let len = 0x10_0000; + let mut data = Vec::with_capacity(len); + unsafe { + data.set_len(len); + } + let mut memory = MemoryBlock::from(data); + let mut io = NoBus::new(); // Initialize the CPU and make sure it's in the expected state - let mut cpu = Z80::new(Z80Type::Z80, Frequency::from_mhz(4), BusPort::new(0, 16, 8, system.bus.clone()), None); - cpu.init().unwrap(); + let mut cpu = Z80::new(Z80Type::Z80, Frequency::from_mhz(4)); + let mut bus = Z80Port::new(&mut memory, &mut io); + cpu.reset(Instant::START, &mut bus).unwrap(); + cpu.step(Instant::START, &mut bus).unwrap(); - (cpu, system) + (cpu, memory) } fn build_state(state: &TestState) -> Z80State { let mut new_state = Z80State::default(); + new_state.status = Status::Running; new_state.pc = state.pc; new_state.sp = state.sp; new_state.ix = state.ix; @@ -514,25 +518,26 @@ fn build_state(state: &TestState) -> Z80State { new_state } -fn load_memory(system: &System, data: &[u8]) { +fn load_memory(memory: &mut MemoryBlock, data: &[u8]) { for i in 0..data.len() { - system.get_bus().write_u8(system.clock, i as Address, data[i]).unwrap(); + memory.write_u8(Instant::START, i, data[i]).unwrap(); } } fn run_test(case: &TestCase) { - let (mut cpu, system) = init_execute_test(); + let (mut cpu, mut memory) = init_execute_test(); let init_state = build_state(&case.init); let mut expected_state = build_state(&case.fini); - load_memory(&system, case.data); + load_memory(&mut memory, case.data); cpu.state = init_state; - cpu.decode_next().unwrap(); - assert_eq!(cpu.decoder.instruction, case.ins); + let mut io = NoBus::new(); + let mut bus = Z80Port::new(&mut memory, &mut io); + cpu.step(Instant::START, &mut bus).unwrap(); + assert_eq!(cpu.previous_cycle.decoder.instruction, case.ins); - cpu.execute_current().unwrap(); // TODO this is a hack to ignore the functioning of the F5, F3 flags for now cpu.state.reg[Register::F as usize] &= 0xD7; diff --git a/emulator/frontends/minifb/src/bin/moa-trs80.rs b/emulator/frontends/minifb/src/bin/moa-trs80.rs index f5cc7a1..0b1ef3a 100644 --- a/emulator/frontends/minifb/src/bin/moa-trs80.rs +++ b/emulator/frontends/minifb/src/bin/moa-trs80.rs @@ -8,7 +8,7 @@ fn main() { Arg::new("ROM") .short('r') .long("rom") - .action(ArgAction::SetTrue) + .action(ArgAction::Set) .value_name("FILE") .help("ROM file to load at the start of memory"), ) diff --git a/emulator/libraries/emulator-hal b/emulator/libraries/emulator-hal index 2391a32..e4c6a04 160000 --- a/emulator/libraries/emulator-hal +++ b/emulator/libraries/emulator-hal @@ -1 +1 @@ -Subproject commit 2391a324376bdd9fa1ae9801bbe3d12f2e69fa62 +Subproject commit e4c6a04f59dc06798e67c86cdfb8299ddada6696 diff --git a/emulator/libraries/host/Cargo.toml b/emulator/libraries/host/Cargo.toml index 3557e5b..ada299c 100644 --- a/emulator/libraries/host/Cargo.toml +++ b/emulator/libraries/host/Cargo.toml @@ -2,7 +2,7 @@ name = "moa-host" version = "0.1.0" edition = "2021" -rust-version = "1.60" +rust-version = "1.70" categories = ["emulators"] keywords = ["emulators"] description = "traits for abstracting the I/O of an emulated system to the host" diff --git a/emulator/libraries/signals/src/lib.rs b/emulator/libraries/signals/src/lib.rs index 7c9f331..a79e495 100644 --- a/emulator/libraries/signals/src/lib.rs +++ b/emulator/libraries/signals/src/lib.rs @@ -19,7 +19,7 @@ type Input = Signal; #[allow(dead_code)] type TriState = Signal; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Signal(Rc>); impl Signal { diff --git a/emulator/systems/genesis/Cargo.toml b/emulator/systems/genesis/Cargo.toml index 774fe24..0bf4ca5 100644 --- a/emulator/systems/genesis/Cargo.toml +++ b/emulator/systems/genesis/Cargo.toml @@ -11,5 +11,5 @@ moa-signals = { path = "../../libraries/signals" } moa-host = { path = "../../libraries/host" } moa-peripherals-yamaha = { path = "../../peripherals/yamaha" } moa-m68k = { path = "../../cpus/m68k", features = ["moa"] } -moa-z80 = { path = "../../cpus/z80" } +moa-z80 = { path = "../../cpus/z80", features = ["moa"] } diff --git a/emulator/systems/genesis/src/system.rs b/emulator/systems/genesis/src/system.rs index d57e2e5..4b8104d 100644 --- a/emulator/systems/genesis/src/system.rs +++ b/emulator/systems/genesis/src/system.rs @@ -8,7 +8,7 @@ use moa_core::{System, Error, MemoryBlock, Bus, Address, Addressable, Device}; use moa_host::Host; use moa_m68k::{M68k, M68kType}; -use moa_z80::{Z80, Z80Type}; +use moa_z80::{MoaZ80, Z80, Z80Type}; use moa_peripherals_yamaha::Ym2612; use moa_peripherals_yamaha::Sn76489; @@ -68,11 +68,16 @@ pub fn build_genesis(host: &mut H, mut options: SegaGenesisOptions) -> coproc_bus.borrow_mut().insert(0x6000, coproc_register.clone()); coproc_bus.borrow_mut().insert(0x7f11, coproc_sn_sound.clone()); coproc_bus.borrow_mut().insert(0x8000, coproc_area); - let coproc = Z80::from_type(Z80Type::Z80, Frequency::from_hz(3_579_545), coproc_bus, 0, None); - let mut reset = coproc.reset.clone(); - let mut bus_request = coproc.bus_request.clone(); + let coproc = Z80::from_type(Z80Type::Z80, Frequency::from_hz(3_579_545)); + let coproc = MoaZ80 { + bus: coproc_bus, + cpu: coproc, + }; + let mut reset = coproc.cpu.signals.reset.clone(); + let mut bus_request = coproc.cpu.signals.bus_request.clone(); reset.set(true); bus_request.set(true); + let coproc = Device::new(coproc); // Add coprocessor devices to the system bus so the 68000 can access them too system.add_addressable_device(0x00a00000, coproc_ram)?; @@ -80,7 +85,7 @@ pub fn build_genesis(host: &mut H, mut options: SegaGenesisOptions) -> system.add_addressable_device(0x00a06000, coproc_register)?; //system.add_addressable_device(0x00c00010, coproc_sn_sound)?; system.add_device("sn_sound", coproc_sn_sound.clone())?; - system.add_device("coproc", Device::new(coproc))?; + system.add_device("coproc", coproc.clone())?; let controllers = GenesisControllers::new(host)?; diff --git a/emulator/systems/trs80/src/system.rs b/emulator/systems/trs80/src/system.rs index d692853..8e4cdee 100644 --- a/emulator/systems/trs80/src/system.rs +++ b/emulator/systems/trs80/src/system.rs @@ -3,7 +3,7 @@ use femtos::Frequency; use moa_core::{System, Error, MemoryBlock, Device}; use moa_host::Host; -use moa_z80::{Z80, Z80Type}; +use moa_z80::{MoaZ80, Z80, Z80Type}; use crate::peripherals::model1::{Model1Keyboard, Model1Video}; @@ -44,7 +44,11 @@ pub fn build_trs80(host: &mut H, options: Trs80Options) -> Result Result<(M68k, MemoryBlock), Error> { +fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k, MemoryBlock), Error> { // Insert basic initialization let len = 0x100_0000; let mut data = Vec::with_capacity(len); unsafe { data.set_len(len); } - let mut memory = MemoryBlock::::from(data); + let mut memory = MemoryBlock::from(data); let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10)); cpu.state.status = Status::Running; @@ -174,7 +173,7 @@ where } } -fn load_state(cpu: &mut M68k, memory: &mut MemoryBlock, initial: &TestState) -> Result<(), Error> { +fn load_state(cpu: &mut M68k, memory: &mut MemoryBlock, 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 +212,7 @@ fn load_state(cpu: &mut M68k, memory: &mut MemoryBlock, i Ok(()) } -fn assert_state(cpu: &M68k, memory: &mut MemoryBlock, expected: &TestState) -> Result<(), Error> { +fn assert_state(cpu: &M68k, memory: &mut MemoryBlock, 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")?; @@ -259,7 +258,7 @@ fn assert_state(cpu: &M68k, memory: &mut MemoryBlock, exp fn step_cpu_and_assert( cpu: &mut M68k, - memory: &mut MemoryBlock, + memory: &mut MemoryBlock, case: &TestCase, test_timing: bool, ) -> Result<(), Error> { diff --git a/tests/rad_tests/Cargo.toml b/tests/rad_tests/Cargo.toml index 5f507ed..be74c88 100644 --- a/tests/rad_tests/Cargo.toml +++ b/tests/rad_tests/Cargo.toml @@ -5,7 +5,8 @@ edition = "2021" [dependencies] femtos = "0.1" -moa-core = { path = "../../emulator/core" } +emulator-hal = { path = "../../emulator/libraries/emulator-hal/emulator-hal" } +emulator-hal-memory = { path = "../../emulator/libraries/emulator-hal/emulator-hal-memory" } moa-z80 = { path = "../../emulator/cpus/z80" } serde = "1.0" serde_json = "1.0" diff --git a/tests/rad_tests/latest.txt b/tests/rad_tests/latest.txt index 4f91649..ce34e44 100644 --- a/tests/rad_tests/latest.txt +++ b/tests/rad_tests/latest.txt @@ -1,4 +1,4 @@ -Last run on 2023-06-10 at commit cbcfb26f49c23414fe00317fddc65ffcbb087b18 +Last run on 2024-06-23 at commit 82fb5822ee2ded38b3760f7a56a7892019dcc720 with flags --check-undocumented --check-timings 00.json completed, all passed! 01.json completed, all passed! @@ -1612,4 +1612,4 @@ fe.json completed, all passed! ff.json completed, all passed! passed: 1574638, failed: 35362, total 98% -completed in 1m 19s +completed in 0m 9s diff --git a/tests/rad_tests/run_all.sh b/tests/rad_tests/run_all.sh index 8152dc8..7bca526 100755 --- a/tests/rad_tests/run_all.sh +++ b/tests/rad_tests/run_all.sh @@ -2,10 +2,11 @@ COMMIT=$(git rev-parse HEAD) DATE=$(date --iso) LOCATION=$(dirname ${BASH_SOURCE[0]}) +FLAGS=("--check-undocumented" "--check-timings") RESULTS=latest.txt { cd $LOCATION - echo "Last run on $DATE at commit $COMMIT" | tee $RESULTS + echo "Last run on $DATE at commit $COMMIT" with flags ${FLAGS[@]} | tee $RESULTS echo "" | tee -a $RESULTS - cargo run -- -q --testsuite "../jsmoo/misc/tests/GeneratedTests/z80/v1/" --check-undocumented --check-timings | tee -a $RESULTS + cargo run -- -q --testsuite "../jsmoo/misc/tests/GeneratedTests/z80/v1/" ${FLAGS[@]} | tee -a $RESULTS } diff --git a/tests/rad_tests/src/main.rs b/tests/rad_tests/src/main.rs index f4df1d8..9aab466 100644 --- a/tests/rad_tests/src/main.rs +++ b/tests/rad_tests/src/main.rs @@ -1,7 +1,5 @@ const DEFAULT_RAD_TESTS: &str = "tests/jsmoo/misc/tests/GeneratedTests/z80/v1/"; -use std::rc::Rc; -use std::cell::RefCell; use std::io::prelude::*; use std::fmt::{Debug, UpperHex}; use std::path::PathBuf; @@ -11,15 +9,19 @@ use std::fs::{self, File}; use clap::Parser; use flate2::read::GzDecoder; use serde_derive::Deserialize; -use femtos::Frequency; +use femtos::{Instant, Frequency}; -use moa_core::{System, Error, MemoryBlock, Bus, BusPort, Address, Addressable, Steppable, Device}; +use emulator_hal::{Step, BusAccess}; +use emulator_hal_memory::MemoryBlock; -use moa_z80::{Z80, Z80Type}; -use moa_z80::instructions::InterruptMode; -use moa_z80::state::Flags; -use moa_z80::state::Status; +use moa_z80::{Z80, Z80Type, Z80Port, InterruptMode, Flags, Status}; +#[derive(Clone, Debug)] +enum Error { + Assertion(String), + Bus(String), + Step(String), +} #[derive(Parser)] struct Args { @@ -43,6 +45,9 @@ struct Args { /// Check instruction timings #[clap(short = 't', long)] check_timings: bool, + /// Don't check I/O instructions + #[clap(short = 'i', long)] + no_check_io: bool, /// Directory to the test suite to run #[clap(long, default_value = DEFAULT_RAD_TESTS)] testsuite: String, @@ -53,7 +58,6 @@ fn main() { run_all_tests(&args); } - #[derive(Debug, Deserialize)] struct TestState { pc: u16, @@ -107,6 +111,8 @@ struct TestCase { ports: Vec, } +type Machine = (Z80, MemoryBlock, MemoryBlock); + impl TestState { pub fn dump(&self) { println!(" a: {:02x} a': {:02x}", self.a, self.af_ >> 8); @@ -144,28 +150,30 @@ impl TestCase { } } - -fn init_execute_test(cputype: Z80Type, state: &TestState, ports: &[TestPort]) -> Result<(Z80, System, Rc>), Error> { - let mut system = System::default(); - +#[allow(clippy::uninit_vec)] +fn init_execute_test(cputype: Z80Type, state: &TestState, ports: &[TestPort]) -> Result { // Insert basic initialization - let mem = MemoryBlock::new(vec![0; 0x1_0000]); - system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); + let len = 0x1_0000; + let mut data = Vec::with_capacity(len); + unsafe { + data.set_len(len); + } + let mut memory = MemoryBlock::::from(data); // Set up IOREQ as memory space - let io_ram = Device::new(MemoryBlock::new(vec![0; 0x10000])); - let io_bus = Rc::new(RefCell::new(Bus::default())); - io_bus.borrow_mut().set_ignore_unmapped(true); - io_bus.borrow_mut().insert(0x0000, io_ram); + let len = 0x1_0000; + let mut data = Vec::with_capacity(len); + unsafe { + data.set_len(len); + } + let mut io = MemoryBlock::::from(data); - let port = BusPort::new(0, 16, 8, system.bus.clone()); - let ioport = BusPort::new(0, 16, 8, io_bus.clone()); - let mut cpu = Z80::new(cputype, Frequency::from_mhz(10), port, Some(ioport)); + let mut cpu = Z80::new(cputype, Frequency::from_mhz(10)); cpu.state.status = Status::Running; - load_state(&mut cpu, &mut system, io_bus.clone(), state, ports)?; + load_state(&mut cpu, &mut memory, &mut io, state, ports)?; - Ok((cpu, system, io_bus)) + Ok((cpu, memory, io)) } fn assert_value(actual: T, expected: T, message: &str) -> Result<(), Error> @@ -175,14 +183,14 @@ where if actual == expected { Ok(()) } else { - Err(Error::assertion(format!("{:#X} != {:#X}, {}", actual, expected, message))) + Err(Error::Assertion(format!("{:#X} != {:#X}, {}", actual, expected, message))) } } fn load_state( - cpu: &mut Z80, - system: &mut System, - io_bus: Rc>, + cpu: &mut Z80, + memory: &mut MemoryBlock, + io: &mut MemoryBlock, initial: &TestState, ports: &[TestPort], ) -> Result<(), Error> { @@ -215,12 +223,15 @@ fn load_state( // Load data bytes into memory for (addr, byte) in initial.ram.iter() { - system.get_bus().write_u8(system.clock, *addr as u64, *byte)?; + memory + .write_u8(Instant::START, *addr, *byte) + .map_err(|err| Error::Bus(format!("{:?}", err)))?; } // Load data bytes into io space for port in ports.iter() { - io_bus.borrow_mut().write_u8(system.clock, port.addr as u64, port.value)?; + io.write_u8(Instant::START, port.addr, port.value) + .map_err(|err| Error::Bus(format!("{:?}", err)))?; } Ok(()) @@ -229,9 +240,9 @@ fn load_state( const IGNORE_FLAG_MASK: u8 = Flags::F3 as u8 | Flags::F5 as u8; fn assert_state( - cpu: &Z80, - system: &System, - io_bus: Rc>, + cpu: &Z80, + memory: &mut MemoryBlock, + io: &mut MemoryBlock, expected: &TestState, check_extra_flags: bool, ports: &[TestPort], @@ -267,23 +278,25 @@ fn assert_state( let expected_im: InterruptMode = expected.im.into(); if cpu.state.im != expected_im { - return Err(Error::assertion(format!("{:?} != {:?}, im", cpu.state.im, expected_im))); + return Err(Error::Assertion(format!("{:?} != {:?}, im", cpu.state.im, expected_im))); } assert_value(cpu.state.iff1 as u8, expected.iff1, "iff1")?; assert_value(cpu.state.iff2 as u8, expected.iff2, "iff2")?; - let addr_mask = cpu.port.address_mask(); - - // Load data bytes into memory + // Compare data bytes in memory for (addr, byte) in expected.ram.iter() { - let actual = system.get_bus().read_u8(system.clock, *addr as Address & addr_mask)?; + let actual = memory + .read_u8(Instant::START, *addr) + .map_err(|err| Error::Bus(format!("{:?}", err)))?; assert_value(actual, *byte, &format!("ram at {:x}", addr))?; } - // Load data bytes into io space + // Compare data bytes in io space for port in ports.iter() { if port.atype == "w" { - let actual = io_bus.borrow_mut().read_u8(system.clock, port.addr as u64)?; + let actual = io + .read_u8(Instant::START, port.addr) + .map_err(|err| Error::Bus(format!("{:?}", err)))?; assert_value(actual, port.value, &format!("port value at {:x}", port.addr))?; } } @@ -292,19 +305,22 @@ fn assert_state( } fn step_cpu_and_assert( - cpu: &mut Z80, - system: &System, - io_bus: Rc>, + cpu: &mut Z80, + memory: &mut MemoryBlock, + io: &mut MemoryBlock, case: &TestCase, args: &Args, ) -> Result<(), Error> { - let clock_elapsed = cpu.step(system)?; + let mut bus = Z80Port::new(&mut *memory, &mut *io); + let clock_elapsed = cpu + .step(Instant::START, &mut bus) + .map_err(|err| Error::Step(format!("{:?}", err)))?; - assert_state(cpu, system, io_bus, &case.final_state, args.check_extra_flags, &case.ports)?; + assert_state(cpu, memory, io, &case.final_state, args.check_extra_flags, &case.ports)?; if args.check_timings { - let cycles = clock_elapsed / cpu.frequency.period_duration(); - if cycles != case.cycles.len() as Address { - return Err(Error::assertion(format!( + let cycles = clock_elapsed.as_duration() / cpu.frequency.period_duration(); + if cycles != case.cycles.len() as u64 { + return Err(Error::Assertion(format!( "expected instruction to take {} cycles, but took {}", case.cycles.len(), cycles @@ -316,10 +332,10 @@ fn step_cpu_and_assert( } fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> { - let (mut cpu, system, io_bus) = init_execute_test(Z80Type::Z80, &case.initial_state, &case.ports).unwrap(); + let (mut cpu, mut memory, mut io) = init_execute_test(Z80Type::Z80, &case.initial_state, &case.ports).unwrap(); let mut initial_cpu = cpu.clone(); - let result = step_cpu_and_assert(&mut cpu, &system, io_bus, case, args); + let result = step_cpu_and_assert(&mut cpu, &mut memory, &mut io, case, args); match result { Ok(()) => Ok(()), @@ -328,8 +344,11 @@ fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> { if args.debug { case.dump(); println!(); - initial_cpu.dump_state(system.clock); - cpu.dump_state(system.clock); + let mut bus = Z80Port::new(&mut memory, &mut io); + let mut writer = String::new(); + initial_cpu.dump_state(&mut writer, Instant::START, &mut bus).unwrap(); + cpu.dump_state(&mut writer, Instant::START, &mut bus).unwrap(); + println!("{}", writer); } println!("FAILED: {:?}", err); } @@ -361,6 +380,10 @@ fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) { } } + if args.no_check_io && !case.ports.is_empty() { + continue; + } + // Sort the ram memory for debugging help if args.debug { case.initial_state.ram.sort_by_key(|(addr, _)| *addr); @@ -392,13 +415,11 @@ fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) { (passed, failed, message) } - fn run_all_tests(args: &Args) { let mut passed = 0; let mut failed = 0; let mut messages = vec![]; - let mut tests: Vec = fs::read_dir(&args.testsuite) .unwrap() .map(|dirent| dirent.unwrap().path()) diff --git a/todo.txt b/todo.txt index 5609095..fdaee8f 100644 --- a/todo.txt +++ b/todo.txt @@ -1,14 +1,20 @@ -* 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 +* fix the Z80 reset and bus_request signals +* the emulator_hal_memory should throw an error when an access will straddle the end of memory? Or should it autowrap? +* fix the m68k dumping functions + +* convert computie system to use the new moa-system library crate to replace the old core crate +* change package names to drop the 's', so moa-systems-computie becomes moa-system-computie -* try using the debug and inspect traits elsewhere in moa * convert all code to use fmt::Writer instead of println +* 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 -* 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 +* try using the debug and inspect traits elsewhere in moa + +* decide if you should continue expecting Instant to usable through the trait alone, despite issues +* figure out how to do interrupts, and add them to emulator-hal, implement them in m68k * replace Addressable/Steppable and modify Transmutable to use the emulator-hal traits * remove the custom moa impls from m68k if possible at this point * publish the emulator-hal crate @@ -79,7 +85,7 @@ * add doc strings everywhere * get rustfmt, rustdoc, and clippy working in some kind of semi-automatic fashion -* you really need a full web-based debugger +* you really need a full web-based debugger; look into egui as the basis for it?