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
This commit is contained in:
transistor fet 2024-06-23 19:42:36 -07:00 committed by GitHub
parent 6e7e315808
commit 342bb8aa3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 1017 additions and 556 deletions

4
Cargo.lock generated
View File

@ -994,6 +994,7 @@ name = "moa-z80"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"emulator-hal", "emulator-hal",
"emulator-hal-memory",
"femtos", "femtos",
"log", "log",
"moa-core", "moa-core",
@ -1247,9 +1248,10 @@ name = "rad-tests"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap 3.2.25", "clap 3.2.25",
"emulator-hal",
"emulator-hal-memory",
"femtos", "femtos",
"flate2", "flate2",
"moa-core",
"moa-z80", "moa-z80",
"serde", "serde",
"serde_derive", "serde_derive",

View File

@ -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 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 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: 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/) [moa_core](http://jabberwocky.ca/moa/doc/moa_core/)
[rustdocs for ym2612](http://jabberwocky.ca/moa/doc/moa_peripherals_yamaha/ym2612/index.html) [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 This repository uses submodules, so make sure to clone with
```sh ```sh

View File

@ -8,4 +8,4 @@ log = "0.4"
femtos = "0.1" femtos = "0.1"
thiserror = "1.0" thiserror = "1.0"
moa-host = { path = "../libraries/host" } moa-host = { path = "../libraries/host" }
emulator-hal = { path = "../libraries/emulator-hal/emulator-hal" } emulator-hal = { path = "../libraries/emulator-hal/emulator-hal", features = ["femtos"] }

View File

@ -171,6 +171,16 @@ pub trait Inspectable {
fn inspect(&mut self, system: &System, args: &[&str]) -> Result<(), Error>; 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<bool>;
}
pub trait Transmutable { pub trait Transmutable {
#[inline] #[inline]
@ -197,6 +207,11 @@ pub trait Transmutable {
fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> { fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> {
None None
} }
#[inline]
fn as_signalable(&mut self) -> Option<&mut dyn Signalable> {
None
}
} }
pub type TransmutableBox = Rc<RefCell<Box<dyn Transmutable>>>; pub type TransmutableBox = Rc<RefCell<Box<dyn Transmutable>>>;

View File

@ -71,3 +71,9 @@ impl<E> From<HostError<E>> for Error {
Self::Other("other".to_string()) Self::Other("other".to_string())
} }
} }
impl From<fmt::Error> for Error {
fn from(err: fmt::Error) -> Self {
Self::Other(format!("{:?}", err))
}
}

View File

@ -7,7 +7,8 @@ mod memory;
mod system; mod system;
pub use crate::devices::{ 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::{ pub use crate::devices::{
read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable, 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::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice, dump_memory};
pub use crate::system::System; pub use crate::system::System;
pub use emulator_hal::bus::{BusAccess}; pub use emulator_hal::BusAccess;

View File

@ -4,6 +4,7 @@ use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::Write; use std::fmt::Write;
use femtos::Instant; use femtos::Instant;
use emulator_hal::{self, BusAccess, ErrorType};
use crate::error::Error; use crate::error::Error;
use crate::devices::{Address, Addressable, Transmutable, Device, read_beu16}; use crate::devices::{Address, Addressable, Transmutable, Device, read_beu16};
@ -236,7 +237,7 @@ impl Bus {
let to = if count < 16 { count / 2 } else { 8 }; let to = if count < 16 { count / 2 } else { 8 };
for _ in 0..to { for _ in 0..to {
let word = self.read_beu16(clock, addr); let word = Addressable::read_beu16(self, clock, addr);
if word.is_err() { if word.is_err() {
println!("{}", line); println!("{}", line);
return; return;
@ -353,7 +354,7 @@ impl Addressable for BusPort {
for i in (0..data.len()).step_by(self.data_width as usize) { for i in (0..data.len()).step_by(self.data_width as usize) {
let addr_index = (addr + i as Address) & self.address_mask; let addr_index = (addr + i as Address) & self.address_mask;
let end = cmp::min(i + self.data_width as usize, data.len()); 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(()) Ok(())
} }
@ -364,7 +365,7 @@ impl Addressable for BusPort {
for i in (0..data.len()).step_by(self.data_width as usize) { for i in (0..data.len()).step_by(self.data_width as usize) {
let addr_index = (addr + i as Address) & self.address_mask; let addr_index = (addr + i as Address) & self.address_mask;
let end = cmp::min(i + self.data_width as usize, data.len()); 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(()) Ok(())
} }
@ -412,9 +413,7 @@ where
} }
} }
use emulator_hal::bus::{self, BusAccess}; impl ErrorType for Error {}
impl bus::Error for Error {}
impl BusAccess<u64> for &mut dyn Addressable { impl BusAccess<u64> for &mut dyn Addressable {
type Instant = Instant; type Instant = Instant;
@ -430,3 +429,18 @@ impl BusAccess<u64> for &mut dyn Addressable {
Ok(data.len()) Ok(data.len())
} }
} }
impl BusAccess<u64> for Bus {
type Instant = Instant;
type Error = Error;
fn read(&mut self, now: Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
Addressable::read(self, now, addr, data)?;
Ok(data.len())
}
fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
Addressable::write(self, now, addr, data)?;
Ok(data.len())
}
}

View File

@ -2,9 +2,7 @@
use core::fmt; use core::fmt;
use emulator_hal::time; use emulator_hal::{Instant as BusInstant, ErrorType, BusAccess, Inspect, Debug};
use emulator_hal::bus::{self, BusAccess};
use emulator_hal::step::{Inspect, Debug};
use crate::{M68k, M68kError, M68kAddress, M68kCycleExecutor}; use crate::{M68k, M68kError, M68kAddress, M68kCycleExecutor};
@ -31,7 +29,7 @@ pub enum M68kInfo {
impl<Bus, BusError, Instant, Writer> Inspect<M68kAddress, Bus, Writer> for M68k<Instant> impl<Bus, BusError, Instant, Writer> Inspect<M68kAddress, Bus, Writer> for M68k<Instant>
where where
Bus: BusAccess<M68kAddress, Instant = Instant, Error = BusError>, Bus: BusAccess<M68kAddress, Instant = Instant, Error = BusError>,
BusError: bus::Error, BusError: ErrorType,
Writer: fmt::Write, Writer: fmt::Write,
{ {
type InfoType = M68kInfo; type InfoType = M68kInfo;
@ -60,8 +58,8 @@ where
impl<Bus, BusError, Instant, Writer> Debug<M68kAddress, Bus, Writer> for M68k<Instant> impl<Bus, BusError, Instant, Writer> Debug<M68kAddress, Bus, Writer> for M68k<Instant>
where where
Bus: BusAccess<M68kAddress, Instant = Instant, Error = BusError>, Bus: BusAccess<M68kAddress, Instant = Instant, Error = BusError>,
BusError: bus::Error, BusError: ErrorType,
Instant: time::Instant, Instant: BusInstant,
Writer: fmt::Write, Writer: fmt::Write,
{ {
// TODO this should be a new type // TODO this should be a new type

View File

@ -1,7 +1,7 @@
// Instruction Decoding // Instruction Decoding
use core::marker::PhantomData; use core::marker::PhantomData;
use emulator_hal::bus::BusAccess; use emulator_hal::BusAccess;
use crate::{M68kType, M68kError, M68kBusPort, M68kAddress, Exceptions}; use crate::{M68kType, M68kError, M68kBusPort, M68kAddress, Exceptions};
use crate::instructions::{ use crate::instructions::{

View File

@ -1,8 +1,6 @@
// Instruction Execution // Instruction Execution
use emulator_hal::time; use emulator_hal::{Instant as BusInstant, ErrorType, BusAccess, Step};
use emulator_hal::step::Step;
use emulator_hal::bus::{self, BusAccess};
use crate::{M68k, M68kType, M68kError, M68kState}; use crate::{M68k, M68kType, M68kError, M68kState};
use crate::state::{Status, Flags, Exceptions, InterruptPriority}; use crate::state::{Status, Flags, Exceptions, InterruptPriority};
@ -35,7 +33,7 @@ pub struct M68kCycle<Instant> {
impl<Instant> M68kCycle<Instant> impl<Instant> M68kCycle<Instant>
where where
Instant: time::Instant, Instant: BusInstant,
{ {
#[inline] #[inline]
pub fn default(cputype: M68kType, data_width: u8) -> Self { pub fn default(cputype: M68kType, data_width: u8) -> Self {
@ -77,8 +75,8 @@ where
impl<Bus, BusError, Instant> Step<M68kAddress, Bus> for M68k<Instant> impl<Bus, BusError, Instant> Step<M68kAddress, Bus> for M68k<Instant>
where where
Bus: BusAccess<M68kAddress, Instant = Instant, Error = BusError>, Bus: BusAccess<M68kAddress, Instant = Instant, Error = BusError>,
BusError: bus::Error, BusError: ErrorType,
Instant: time::Instant, Instant: BusInstant,
{ {
type Error = M68kError<BusError>; type Error = M68kError<BusError>;

View File

@ -18,4 +18,4 @@ pub use crate::memory::{M68kAddress, M68kAddressSpace, M68kBusPort};
pub use crate::decode::{M68kDecoder, InstructionDecoding}; pub use crate::decode::{M68kDecoder, InstructionDecoding};
pub use crate::execute::{M68kCycle, M68kCycleExecutor}; pub use crate::execute::{M68kCycle, M68kCycleExecutor};
pub use crate::timing::M68kInstructionTiming; pub use crate::timing::M68kInstructionTiming;
//pub use crate::instructions::{Instruction, Target, Size, Sign, XRegister, BaseRegister, IndexRegister, Direction}; pub use crate::instructions::*;

View File

@ -1,7 +1,6 @@
use core::cmp; use core::cmp;
use core::fmt::Write; use core::fmt::Write;
use emulator_hal::time; use emulator_hal::{Instant as BusInstant, BusAccess};
use emulator_hal::bus::BusAccess;
use crate::{M68kError, CpuInfo}; use crate::{M68kError, CpuInfo};
use crate::state::Exceptions; use crate::state::Exceptions;
@ -65,7 +64,7 @@ impl FunctionCode {
impl<Instant> Default for MemoryRequest<Instant> impl<Instant> Default for MemoryRequest<Instant>
where where
Instant: time::Instant, Instant: BusInstant,
{ {
fn default() -> Self { fn default() -> Self {
Self { Self {
@ -138,7 +137,7 @@ pub struct M68kBusPort<Instant> {
impl<Instant> Default for M68kBusPort<Instant> impl<Instant> Default for M68kBusPort<Instant>
where where
Instant: time::Instant, Instant: BusInstant,
{ {
fn default() -> Self { fn default() -> Self {
Self { Self {

View File

@ -1,5 +1,5 @@
use femtos::{Instant, Duration}; use femtos::{Instant, Duration};
use emulator_hal::bus; use emulator_hal::{ErrorType, BusAdapter};
use moa_core::{System, Error, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable}; use moa_core::{System, Error, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable};
@ -10,8 +10,7 @@ impl Steppable for M68k<Instant> {
let cycle = M68kCycle::new(self, system.clock); let cycle = M68kCycle::new(self, system.clock);
let mut bus = system.bus.borrow_mut(); let mut bus = system.bus.borrow_mut();
let mut adapter: bus::BusAdapter<u32, u64, &mut dyn Addressable, Error> = let mut adapter: BusAdapter<u32, u64, &mut dyn Addressable, Error> = BusAdapter::new(&mut *bus, |addr| addr as u64);
bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err);
let mut executor = cycle.begin(self, &mut adapter); let mut executor = cycle.begin(self, &mut adapter);
executor.check_breakpoints()?; executor.check_breakpoints()?;
@ -60,7 +59,7 @@ impl<BusError> From<Error> for M68kError<BusError> {
} }
} }
impl<BusError: bus::Error> From<M68kError<BusError>> for Error { impl<BusError: ErrorType> From<M68kError<BusError>> for Error {
fn from(err: M68kError<BusError>) -> Self { fn from(err: M68kError<BusError>) -> Self {
match err { match err {
M68kError::Halted => Self::Other("cpu halted".to_string()), M68kError::Halted => Self::Other("cpu halted".to_string()),
@ -86,11 +85,17 @@ impl Debuggable for M68k<Instant> {
} }
} }
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<u32, u64, &mut dyn Addressable, Error> = BusAdapter::new(&mut *bus, |addr| addr as u64);
// TODO this is called by the debugger, but should be called some other way // 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); let mut decoder = M68kDecoder::new(self.info.chip, true, self.state.pc);
//self.decoder.dump_decoded(&mut self.bus); decoder.decode_at(&mut adapter, &mut M68kBusPort::default(), true, self.state.pc)?;
//self.dump_state(); decoder.dump_decoded(system.clock, &mut adapter);
let mut writer = String::new();
self.dump_state(&mut writer)?;
println!("{}", writer);
Ok(()) Ok(())
} }
@ -99,8 +104,7 @@ impl Debuggable for M68k<Instant> {
let mut memory = M68kBusPort::from_info(&self.info, system.clock); let mut memory = M68kBusPort::from_info(&self.info, system.clock);
let mut bus = system.bus.borrow_mut(); let mut bus = system.bus.borrow_mut();
let mut adapter: bus::BusAdapter<u32, u64, &mut dyn Addressable, Error> = let mut adapter: BusAdapter<u32, u64, &mut dyn Addressable, Error> = BusAdapter::new(&mut *bus, |addr| addr as u64);
bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err);
decoder.dump_disassembly(&mut adapter, &mut memory, addr as u32, count as u32); decoder.dump_disassembly(&mut adapter, &mut memory, addr as u32, count as u32);
} }

View File

@ -2,7 +2,7 @@
use femtos::Frequency; use femtos::Frequency;
use core::fmt::{self, Write}; use core::fmt::{self, Write};
use emulator_hal::time; use emulator_hal::Instant as BusInstant;
use crate::{M68kDebugger, M68kCycle}; use crate::{M68kDebugger, M68kCycle};
use crate::instructions::Target; use crate::instructions::Target;
@ -243,7 +243,7 @@ impl M68kState {
impl<Instant> M68k<Instant> impl<Instant> M68k<Instant>
where where
Instant: time::Instant, Instant: BusInstant,
{ {
pub fn new(info: CpuInfo) -> Self { pub fn new(info: CpuInfo) -> Self {
M68k { M68k {

View File

@ -1,7 +1,7 @@
#[cfg(test)] #[cfg(test)]
mod decode_unit_tests { mod decode_unit_tests {
use femtos::Instant; use femtos::Instant;
use emulator_hal::bus::BusAccess; use emulator_hal::BusAccess;
use emulator_hal_memory::MemoryBlock; use emulator_hal_memory::MemoryBlock;
use crate::M68kType; use crate::M68kType;
@ -13,7 +13,7 @@ mod decode_unit_tests {
fn run_decode_test<F>(cputype: M68kType, mut test_func: F) fn run_decode_test<F>(cputype: M68kType, mut test_func: F)
where where
F: FnMut(&mut InstructionDecoding<'_, MemoryBlock<u32, Instant>, Instant>), F: FnMut(&mut InstructionDecoding<'_, MemoryBlock<Instant>, Instant>),
{ {
let mut memory = MemoryBlock::from(vec![0; 0x0000100]); let mut memory = MemoryBlock::from(vec![0; 0x0000100]);
let mut decoder = M68kDecoder::new(cputype, true, 0); let mut decoder = M68kDecoder::new(cputype, true, 0);
@ -316,8 +316,7 @@ mod decode_unit_tests {
#[cfg(test)] #[cfg(test)]
mod execute_unit_tests { mod execute_unit_tests {
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess; use emulator_hal::{Step, BusAccess};
use emulator_hal::step::Step;
use emulator_hal_memory::MemoryBlock; use emulator_hal_memory::MemoryBlock;
use crate::{M68k, M68kType}; use crate::{M68k, M68kType};
@ -330,7 +329,7 @@ mod execute_unit_tests {
#[allow(clippy::uninit_vec)] #[allow(clippy::uninit_vec)]
fn run_execute_test<F>(cputype: M68kType, mut test_func: F) fn run_execute_test<F>(cputype: M68kType, mut test_func: F)
where where
F: FnMut(M68kCycleExecutor<&mut MemoryBlock<u32, Instant>, Instant>), F: FnMut(M68kCycleExecutor<&mut MemoryBlock<Instant>, Instant>),
{ {
// Insert basic initialization // Insert basic initialization
let len = 0x10_0000; let len = 0x10_0000;

View File

@ -1,5 +1,5 @@
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess; use emulator_hal::BusAccess;
use emulator_hal_memory::MemoryBlock; use emulator_hal_memory::MemoryBlock;
use moa_m68k::{M68k, M68kType, M68kAddress}; use moa_m68k::{M68k, M68kType, M68kAddress};
@ -64,7 +64,7 @@ const DECODE_TESTS: &'static [TestCase] = &[
]; ];
fn init_decode_test(cputype: M68kType) -> (M68k<Instant>, M68kCycle<Instant>, MemoryBlock<u32, Instant>) { fn init_decode_test(cputype: M68kType) -> (M68k<Instant>, M68kCycle<Instant>, MemoryBlock<Instant>) {
// Insert basic initialization // Insert basic initialization
let len = 0x2000; let len = 0x2000;
let mut data = Vec::with_capacity(len); let mut data = Vec::with_capacity(len);

View File

@ -1,6 +1,5 @@
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess; use emulator_hal::{BusAccess, Step};
use emulator_hal::step::Step;
use emulator_hal_memory::MemoryBlock; use emulator_hal_memory::MemoryBlock;
use moa_m68k::{M68k, M68kType, M68kAddress}; use moa_m68k::{M68k, M68kType, M68kAddress};
@ -38,7 +37,7 @@ struct TestCase {
#[allow(clippy::uninit_vec)] #[allow(clippy::uninit_vec)]
fn run_execute_test<F>(cputype: M68kType, mut test_func: F) fn run_execute_test<F>(cputype: M68kType, mut test_func: F)
where where
F: FnMut(M68kCycleExecutor<&mut MemoryBlock<u32, Instant>, Instant>), F: FnMut(M68kCycleExecutor<&mut MemoryBlock<Instant>, Instant>),
{ {
// Insert basic initialization // Insert basic initialization
let len = 0x10_0000; let len = 0x10_0000;

View File

@ -1,5 +1,5 @@
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess; use emulator_hal::BusAccess;
use emulator_hal_memory::MemoryBlock; use emulator_hal_memory::MemoryBlock;
use moa_m68k::{M68k, M68kType, M68kAddress}; use moa_m68k::{M68k, M68kType, M68kAddress};
@ -12,7 +12,7 @@ const INIT_STACK: M68kAddress = 0x00002000;
const INIT_ADDR: M68kAddress = 0x00000010; const INIT_ADDR: M68kAddress = 0x00000010;
#[allow(clippy::uninit_vec)] #[allow(clippy::uninit_vec)]
fn init_decode_test(cputype: M68kType) -> (M68k<Instant>, M68kCycle<Instant>, MemoryBlock<u32, Instant>) { fn init_decode_test(cputype: M68kType) -> (M68k<Instant>, M68kCycle<Instant>, MemoryBlock<Instant>) {
// Insert basic initialization // Insert basic initialization
let len = 0x10_0000; let len = 0x10_0000;
let mut data = Vec::with_capacity(len); let mut data = Vec::with_capacity(len);

View File

@ -1,5 +1,5 @@
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess; use emulator_hal::BusAccess;
use emulator_hal_memory::MemoryBlock; use emulator_hal_memory::MemoryBlock;
use moa_m68k::{M68k, M68kType, M68kAddress}; use moa_m68k::{M68k, M68kType, M68kAddress};
@ -26,7 +26,7 @@ const TIMING_TESTS: &'static [TimingCase] = &[TimingCase {
}]; }];
fn init_decode_test(cputype: M68kType) -> (M68k<Instant>, M68kCycle<Instant>, MemoryBlock<u32, Instant>) { fn init_decode_test(cputype: M68kType) -> (M68k<Instant>, M68kCycle<Instant>, MemoryBlock<Instant>) {
// Insert basic initialization // Insert basic initialization
let len = 0x10_0000; let len = 0x10_0000;
let mut data = Vec::with_capacity(len); let mut data = Vec::with_capacity(len);

View File

@ -7,6 +7,14 @@ edition = "2021"
log = "0.4" log = "0.4"
thiserror = "1.0" thiserror = "1.0"
femtos = "0.1" 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" } 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"]

View File

@ -1,9 +1,4 @@
use moa_core::{System, Error, Address, Debuggable}; use crate::state::{Z80Error, Z80Address};
use crate::state::{Z80, Z80Error};
use crate::decode::Z80Decoder;
use crate::instructions::Register;
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Z80Debugger { pub struct Z80Debugger {
@ -11,49 +6,15 @@ pub struct Z80Debugger {
pub(crate) breakpoints: Vec<u16>, pub(crate) breakpoints: Vec<u16>,
} }
impl Debuggable for Z80 { impl Z80Debugger {
fn add_breakpoint(&mut self, addr: Address) { pub fn check_breakpoints(&mut self, pc: Z80Address) -> Result<(), Z80Error> {
self.debugger.breakpoints.push(addr as u16); for breakpoint in &self.breakpoints {
} if *breakpoint == pc {
if self.skip_breakpoint > 0 {
fn remove_breakpoint(&mut self, addr: Address) { self.skip_breakpoint -= 1;
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<bool, Error> {
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;
return Ok(()); return Ok(());
} else { } else {
self.debugger.skip_breakpoint = 1; self.skip_breakpoint = 1;
return Err(Z80Error::Breakpoint); return Err(Z80Error::Breakpoint);
} }
} }

View File

@ -1,9 +1,7 @@
use core::fmt::Write; use core::fmt::Write;
use femtos::Instant; use emulator_hal::{BusAccess, Instant as EmuInstant};
use moa_core::{Address, Addressable}; use crate::state::{Z80Error, Z80Address, Z80AddressSpace};
use crate::state::Z80Error;
use crate::instructions::{ use crate::instructions::{
Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target,
LoadTarget, UndocumentedCopy, Instruction, LoadTarget, UndocumentedCopy, Instruction,
@ -15,9 +13,8 @@ use crate::instructions::{
#[derive(Clone)] #[derive(Clone)]
pub struct Z80Decoder { pub struct Z80Decoder {
pub clock: Instant, pub start: Z80Address,
pub start: u16, pub end: Z80Address,
pub end: u16,
pub extra_instruction_bytes: u16, pub extra_instruction_bytes: u16,
pub instruction: Instruction, pub instruction: Instruction,
} }
@ -25,7 +22,6 @@ pub struct Z80Decoder {
impl Default for Z80Decoder { impl Default for Z80Decoder {
fn default() -> Self { fn default() -> Self {
Self { Self {
clock: Instant::START,
start: 0, start: 0,
end: 0, end: 0,
extra_instruction_bytes: 0, extra_instruction_bytes: 0,
@ -34,59 +30,119 @@ impl Default for Z80Decoder {
} }
} }
/* impl Z80Decoder {
fn read_test<B>(&mut self, device: &mut B) -> Result<u8, Z80Error> fn new(start: Z80Address) -> Self {
where Self {
B: BusAccess<Z80Address, Instant = Instant>, start,
{ end: start,
device.read_u8(self.clock, (false, self.end as u16)) extra_instruction_bytes: 0,
.map_err(|err| Z80Error::BusError(format!("butts"))) instruction: Instruction::NOP,
}
} }
*/ }
impl Z80Decoder { impl Z80Decoder {
pub fn decode_at(&mut self, memory: &mut dyn Addressable, clock: Instant, start: u16) -> Result<(), Z80Error> { pub fn decode_at<Bus>(bus: &mut Bus, clock: Bus::Instant, start: Z80Address) -> Result<Self, Z80Error>
self.clock = clock; where
self.start = start; Bus: BusAccess<Z80AddressSpace>,
self.end = start; {
self.extra_instruction_bytes = 0; let mut decoder: DecodeNext<'_, Bus, Bus::Instant> = DecodeNext {
self.instruction = self.decode_one(memory)?; clock,
bus,
decoder: Z80Decoder::new(start),
};
decoder.decode_one()?;
Ok(decoder.decoder)
}
pub fn dump_disassembly<Bus>(bus: &mut Bus, start: Z80Address, length: Z80Address)
where
Bus: BusAccess<Z80AddressSpace>,
{
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<Bus>(&mut self, bus: &mut Bus)
where
Bus: BusAccess<Z80AddressSpace>,
{
let ins_data = self.format_instruction_bytes(bus);
println!("{:#06x}: {}\n\t{:?}\n", self.start, ins_data, self.instruction);
}
pub fn format_instruction_bytes<Bus>(&mut self, bus: &mut Bus) -> String
where
Bus: BusAccess<Z80AddressSpace>,
{
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<Z80AddressSpace, Instant = Instant>,
{
clock: Instant,
bus: &'a mut Bus,
decoder: Z80Decoder,
}
impl<'a, Bus, Instant> DecodeNext<'a, Bus, Instant>
where
Bus: BusAccess<Z80AddressSpace, Instant = Instant>,
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(()) Ok(())
} }
pub fn decode_one(&mut self, memory: &mut dyn Addressable) -> Result<Instruction, Z80Error> { pub fn decode_bare(&mut self, ins: u8, extra_instruction_bytes: u16) -> Result<Instruction, Z80Error> {
let ins = self.read_instruction_byte(memory)?; self.decoder.extra_instruction_bytes = extra_instruction_bytes;
self.decode_bare(memory, ins, 0)
}
pub fn decode_bare(
&mut self,
memory: &mut dyn Addressable,
ins: u8,
extra_instruction_bytes: u16,
) -> Result<Instruction, Z80Error> {
self.extra_instruction_bytes = extra_instruction_bytes;
match get_ins_x(ins) { match get_ins_x(ins) {
0 => match get_ins_z(ins) { 0 => match get_ins_z(ins) {
0 => match get_ins_y(ins) { 0 => match get_ins_y(ins) {
0 => Ok(Instruction::NOP), 0 => Ok(Instruction::NOP),
1 => Ok(Instruction::EXafaf), 1 => Ok(Instruction::EXafaf),
2 => { 2 => {
let offset = self.read_instruction_byte(memory)? as i8; let offset = self.read_instruction_byte()? as i8;
Ok(Instruction::DJNZ(offset)) Ok(Instruction::DJNZ(offset))
}, },
3 => { 3 => {
let offset = self.read_instruction_byte(memory)? as i8; let offset = self.read_instruction_byte()? as i8;
Ok(Instruction::JR(offset)) Ok(Instruction::JR(offset))
}, },
y => { 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)) Ok(Instruction::JRcc(get_condition(y - 4), offset))
}, },
}, },
1 => { 1 => {
if get_ins_q(ins) == 0 { if get_ins_q(ins) == 0 {
let data = self.read_instruction_word(memory)?; let data = self.read_instruction_word()?;
Ok(Instruction::LD( Ok(Instruction::LD(
LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))), LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))),
LoadTarget::ImmediateWord(data), LoadTarget::ImmediateWord(data),
@ -107,7 +163,7 @@ impl Z80Decoder {
true => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), target)), true => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), target)),
} }
} else { } else {
let addr = self.read_instruction_word(memory)?; let addr = self.read_instruction_word()?;
match (ins >> 3) & 0x03 { match (ins >> 3) & 0x03 {
0 => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(RegisterPair::HL))), 0 => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(RegisterPair::HL))),
1 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(addr))), 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)))), 4 => Ok(Instruction::INC8(get_register(get_ins_y(ins)))),
5 => Ok(Instruction::DEC8(get_register(get_ins_y(ins)))), 5 => Ok(Instruction::DEC8(get_register(get_ins_y(ins)))),
6 => { 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))) Ok(Instruction::LD(to_load_target(get_register(get_ins_y(ins))), LoadTarget::ImmediateByte(data)))
}, },
7 => match get_ins_y(ins) { 7 => match get_ins_y(ins) {
@ -173,21 +229,21 @@ impl Z80Decoder {
} }
}, },
2 => { 2 => {
let addr = self.read_instruction_word(memory)?; let addr = self.read_instruction_word()?;
Ok(Instruction::JPcc(get_condition(get_ins_y(ins)), addr)) Ok(Instruction::JPcc(get_condition(get_ins_y(ins)), addr))
}, },
3 => match get_ins_y(ins) { 3 => match get_ins_y(ins) {
0 => { 0 => {
let addr = self.read_instruction_word(memory)?; let addr = self.read_instruction_word()?;
Ok(Instruction::JP(addr)) Ok(Instruction::JP(addr))
}, },
1 => self.decode_prefix_cb(memory), 1 => self.decode_prefix_cb(),
2 => { 2 => {
let port = self.read_instruction_byte(memory)?; let port = self.read_instruction_byte()?;
Ok(Instruction::OUTx(port)) Ok(Instruction::OUTx(port))
}, },
3 => { 3 => {
let port = self.read_instruction_byte(memory)?; let port = self.read_instruction_byte()?;
Ok(Instruction::INx(port)) Ok(Instruction::INx(port))
}, },
4 => Ok(Instruction::EXsp(RegisterPair::HL)), 4 => Ok(Instruction::EXsp(RegisterPair::HL)),
@ -197,7 +253,7 @@ impl Z80Decoder {
_ => panic!("InternalError: impossible value"), _ => panic!("InternalError: impossible value"),
}, },
4 => { 4 => {
let addr = self.read_instruction_word(memory)?; let addr = self.read_instruction_word()?;
Ok(Instruction::CALLcc(get_condition(get_ins_y(ins)), addr)) Ok(Instruction::CALLcc(get_condition(get_ins_y(ins)), addr))
}, },
5 => { 5 => {
@ -206,18 +262,18 @@ impl Z80Decoder {
} else { } else {
match get_ins_p(ins) { match get_ins_p(ins) {
0 => { 0 => {
let addr = self.read_instruction_word(memory)?; let addr = self.read_instruction_word()?;
Ok(Instruction::CALL(addr)) Ok(Instruction::CALL(addr))
}, },
1 => self.decode_prefix_dd_fd(memory, IndexRegister::IX), 1 => self.decode_prefix_dd_fd(IndexRegister::IX),
2 => self.decode_prefix_ed(memory), 2 => self.decode_prefix_ed(),
3 => self.decode_prefix_dd_fd(memory, IndexRegister::IY), 3 => self.decode_prefix_dd_fd(IndexRegister::IY),
_ => panic!("InternalError: impossible value"), _ => panic!("InternalError: impossible value"),
} }
} }
}, },
6 => { 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))) Ok(get_alu_instruction(get_ins_y(ins), Target::Immediate(data)))
}, },
7 => Ok(Instruction::RST(get_ins_y(ins) * 8)), 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<Instruction, Z80Error> { pub fn decode_prefix_cb(&mut self) -> Result<Instruction, Z80Error> {
let ins = self.read_instruction_byte(memory)?; let ins = self.read_instruction_byte()?;
match get_ins_x(ins) { match get_ins_x(ins) {
0 => Ok(get_rot_instruction(get_ins_y(ins), get_register(get_ins_z(ins)), None)), 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)))), 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<Instruction, Z80Error> { pub fn decode_sub_prefix_cb(&mut self, reg: IndexRegister) -> Result<Instruction, Z80Error> {
let offset = self.read_instruction_byte(memory)? as i8; let offset = self.read_instruction_byte()? as i8;
let ins = self.read_instruction_byte(memory)?; let ins = self.read_instruction_byte()?;
let opt_copy = match get_ins_z(ins) { let opt_copy = match get_ins_z(ins) {
6 => None, //Some(Target::DirectReg(Register::F)), 6 => None, //Some(Target::DirectReg(Register::F)),
z => Some(get_register(z)), z => Some(get_register(z)),
@ -255,8 +311,8 @@ impl Z80Decoder {
} }
} }
pub fn decode_prefix_ed(&mut self, memory: &mut dyn Addressable) -> Result<Instruction, Z80Error> { pub fn decode_prefix_ed(&mut self) -> Result<Instruction, Z80Error> {
let ins = self.read_instruction_byte(memory)?; let ins = self.read_instruction_byte()?;
match get_ins_x(ins) { match get_ins_x(ins) {
0 => Ok(Instruction::NOP), 0 => Ok(Instruction::NOP),
@ -285,7 +341,7 @@ impl Z80Decoder {
} }
}, },
3 => { 3 => {
let addr = self.read_instruction_word(memory)?; let addr = self.read_instruction_word()?;
if get_ins_q(ins) == 0 { if get_ins_q(ins) == 0 {
Ok(Instruction::LD( Ok(Instruction::LD(
LoadTarget::IndirectWord(addr), 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<Instruction, Z80Error> { pub fn decode_prefix_dd_fd(&mut self, index_reg: IndexRegister) -> Result<Instruction, Z80Error> {
let ins = self.read_instruction_byte(memory)?; let ins = self.read_instruction_byte()?;
if ins == 0xCB { 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) { match get_ins_x(ins) {
@ -364,11 +420,11 @@ impl Z80Decoder {
match get_ins_p(ins) { match get_ins_p(ins) {
2 => match get_ins_z(ins) { 2 => match get_ins_z(ins) {
1 => { 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))) Ok(Instruction::LD(LoadTarget::DirectRegWord(index_reg.into()), LoadTarget::ImmediateWord(data)))
}, },
2 => { 2 => {
let addr = self.read_instruction_word(memory)?; let addr = self.read_instruction_word()?;
let regpair = index_reg.into(); let regpair = index_reg.into();
match get_ins_q(ins) != 0 { match get_ins_q(ins) != 0 {
false => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(regpair))), false => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(regpair))),
@ -380,50 +436,50 @@ impl Z80Decoder {
true => Ok(Instruction::DEC16(index_reg.into())), true => Ok(Instruction::DEC16(index_reg.into())),
}, },
4 => { 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))); let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
Ok(Instruction::INC8(half_target)) Ok(Instruction::INC8(half_target))
}, },
5 => { 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))); let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
Ok(Instruction::DEC8(half_target)) Ok(Instruction::DEC8(half_target))
}, },
6 => { 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 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))) 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 { 3 => match ins {
0x34 => { 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))) Ok(Instruction::INC8(Target::IndirectOffset(index_reg, offset)))
}, },
0x35 => { 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))) Ok(Instruction::DEC8(Target::IndirectOffset(index_reg, offset)))
}, },
0x36 => { 0x36 => {
let offset = self.read_instruction_byte(memory)? as i8; let offset = self.read_instruction_byte()? as i8;
let immediate = self.read_instruction_byte(memory)?; let immediate = self.read_instruction_byte()?;
Ok(Instruction::LD( Ok(Instruction::LD(
LoadTarget::IndirectOffsetByte(index_reg, offset), LoadTarget::IndirectOffsetByte(index_reg, offset),
LoadTarget::ImmediateByte(immediate), 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) { 1 => match get_ins_p(ins) {
0 | 1 => { 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, Some(target) => target,
None => return self.decode_bare(memory, ins, 4), None => return self.decode_bare(ins, 4),
}; };
match (ins & 0x18) >> 3 { match (ins & 0x18) >> 3 {
@ -443,7 +499,7 @@ impl Z80Decoder {
4 => Target::DirectRegHalf(get_index_register_half(index_reg, 0)), 4 => Target::DirectRegHalf(get_index_register_half(index_reg, 0)),
5 => Target::DirectRegHalf(get_index_register_half(index_reg, 1)), 5 => Target::DirectRegHalf(get_index_register_half(index_reg, 1)),
6 => { 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)); let src = to_load_target(Target::IndirectOffset(index_reg, offset));
if get_ins_q(ins) == 0 { if get_ins_q(ins) == 0 {
return Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::H), src)); return Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::H), src));
@ -461,15 +517,15 @@ impl Z80Decoder {
3 => { 3 => {
if get_ins_q(ins) == 0 { if get_ins_q(ins) == 0 {
if get_ins_z(ins) == 6 { 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 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))) Ok(Instruction::LD(LoadTarget::IndirectOffsetByte(index_reg, offset), to_load_target(src)))
} else { } 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, 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))) Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), to_load_target(target)))
@ -478,11 +534,11 @@ impl Z80Decoder {
_ => panic!("InternalError: impossible value"), _ => panic!("InternalError: impossible value"),
}, },
2 => { 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, Some(target) => target,
None => return self.decode_bare(memory, ins, 4), None => return self.decode_bare(ins, 4),
}; };
match get_ins_y(ins) { match get_ins_y(ins) {
@ -506,23 +562,18 @@ impl Z80Decoder {
LoadTarget::DirectRegWord(RegisterPair::SP), LoadTarget::DirectRegWord(RegisterPair::SP),
LoadTarget::DirectRegWord(index_reg.into()), LoadTarget::DirectRegWord(index_reg.into()),
)), )),
_ => self.decode_bare(memory, ins, 4), _ => self.decode_bare(ins, 4),
}, },
_ => panic!("InternalError: impossible value"), _ => panic!("InternalError: impossible value"),
} }
} }
fn decode_index_target( fn decode_index_target(&mut self, index_reg: IndexRegister, z: u8) -> Result<Option<Target>, Z80Error> {
&mut self,
memory: &mut dyn Addressable,
index_reg: IndexRegister,
z: u8,
) -> Result<Option<Target>, Z80Error> {
let result = match z { let result = match z {
4 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 0))), 4 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 0))),
5 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 1))), 5 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 1))),
6 => { 6 => {
let offset = self.read_instruction_byte(memory)? as i8; let offset = self.read_instruction_byte()? as i8;
Some(Target::IndirectOffset(index_reg, offset)) Some(Target::IndirectOffset(index_reg, offset))
}, },
_ => None, _ => None,
@ -531,45 +582,25 @@ impl Z80Decoder {
} }
fn read_instruction_byte(&mut self, device: &mut dyn Addressable) -> Result<u8, Z80Error> { fn read_instruction_byte(&mut self) -> Result<u8, Z80Error> {
let byte = device.read_u8(self.clock, self.end as Address)?; let byte = self
self.end = self.end.wrapping_add(1); .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) Ok(byte)
} }
fn read_instruction_word(&mut self, device: &mut dyn Addressable) -> Result<u16, Z80Error> { fn read_instruction_word(&mut self) -> Result<u16, Z80Error> {
let word = device.read_leu16(self.clock, self.end as Address)?; let mut bytes = [0; 2];
self.end = self.end.wrapping_add(2); for byte in bytes.iter_mut() {
Ok(word) *byte = self
} .bus
.read_u8(self.clock, Z80AddressSpace::Memory(self.decoder.end))
pub fn format_instruction_bytes(&mut self, memory: &mut dyn Addressable) -> String { .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?;
let mut ins_data = String::new(); self.decoder.end = self.decoder.end.wrapping_add(1);
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;
},
}
} }
Ok(u16::from_le_bytes(bytes))
} }
} }

View File

@ -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<MemError, IOError>
where
MemError: ErrorType,
IOError: ErrorType,
{
Memory(MemError),
IO(IOError),
}
impl<MemError, IOError> ErrorType for Z80BusError<MemError, IOError>
where
MemError: ErrorType,
IOError: ErrorType,
{
}
pub struct Z80Port<MemBus, IOBus, Instant>
where
MemBus: BusAccess<Z80Address, Instant = Instant>,
IOBus: BusAccess<Z80IOAddress, Instant = Instant>,
{
mem_bus: MemBus,
io_bus: IOBus,
instant: PhantomData<Instant>,
}
impl<MemBus, IOBus, Instant> Z80Port<MemBus, IOBus, Instant>
where
MemBus: BusAccess<Z80Address, Instant = Instant>,
IOBus: BusAccess<Z80IOAddress, Instant = Instant>,
{
pub fn new(mem_bus: MemBus, io_bus: IOBus) -> Self {
Self {
mem_bus,
io_bus,
instant: PhantomData,
}
}
}
impl<MemBus, IOBus, Instant> BusAccess<Z80AddressSpace> for Z80Port<MemBus, IOBus, Instant>
where
Instant: EmuInstant,
MemBus: BusAccess<Z80Address, Instant = Instant>,
IOBus: BusAccess<Z80IOAddress, Instant = Instant>,
{
type Instant = Instant;
type Error = Z80BusError<MemBus::Error, IOBus::Error>;
#[inline]
fn read(&mut self, now: Self::Instant, addr: Z80AddressSpace, data: &mut [u8]) -> Result<usize, Self::Error> {
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<usize, Self::Error> {
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<Instant, Bus> Step<Z80AddressSpace, Bus> for Z80<Instant>
where
Instant: EmuInstant,
Bus: BusAccess<Z80AddressSpace, Instant = Instant>,
{
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<Bus::Instant, Self::Error> {
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<Bus, BusError, Instant, Writer> Inspect<Z80AddressSpace, Bus, Writer> for Z80<Instant>
where
Bus: BusAccess<Z80AddressSpace, Instant = Instant, Error = BusError>,
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<Bus, BusError, Instant, Writer> Debug<Z80AddressSpace, Bus, Writer> for Z80<Instant>
where
Bus: BusAccess<Z80AddressSpace, Instant = Instant, Error = BusError>,
BusError: ErrorType,
Instant: EmuInstant,
Writer: fmt::Write,
{
// TODO this should be a new type
type DebugError = Z80Error;
fn get_execution_address(&mut self) -> Result<Z80AddressSpace, Self::DebugError> {
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();
}
}

View File

@ -1,13 +1,13 @@
use femtos::{Instant, Duration}; use emulator_hal::{BusAccess, Instant as EmuInstant};
use moa_core::{System, Error, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16};
use crate::decode::Z80Decoder;
use crate::instructions::{ use crate::instructions::{
Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister, Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister,
IndexRegisterHalf, Size, Direction, UndocumentedCopy, 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::timing::Z80InstructionCycles;
use crate::debugger::Z80Debugger;
const FLAGS_NUMERIC: u8 = 0xC0; const FLAGS_NUMERIC: u8 = 0xC0;
@ -20,79 +20,79 @@ enum RotateType {
Bit9, Bit9,
} }
impl Steppable for Z80 {
fn step(&mut self, system: &System) -> Result<Duration, Error> {
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<Z80Error> 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<Error> 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)] #[derive(Clone)]
pub struct Z80Executor { pub struct Z80Cycle<Instant> {
pub current_clock: Instant, pub current_clock: Instant,
pub decoder: Z80Decoder,
pub took_branch: bool, pub took_branch: bool,
} }
impl Z80Executor { impl<Instant> Z80Cycle<Instant> {
pub fn at_time(current_clock: Instant) -> Self { pub fn at_time(current_clock: Instant) -> Self {
Self { Self {
current_clock, current_clock,
decoder: Default::default(),
took_branch: false, took_branch: false,
} }
} }
} }
impl Z80 { impl<Instant> Z80<Instant>
pub fn step_internal(&mut self, system: &System) -> Result<u16, Z80Error> { where
self.executor = Z80Executor::at_time(system.clock); Instant: EmuInstant,
{
pub(crate) fn begin<'a, Bus>(
&'a mut self,
clock: Instant,
bus: &'a mut Bus,
) -> Result<ExecuteNext<'a, &'a mut Bus, Instant>, Z80Error>
where
Bus: BusAccess<Z80AddressSpace, Instant = Instant>,
{
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<Z80AddressSpace, Instant = Instant>,
{
state: &'a mut Z80State,
signals: &'a mut Z80Signals,
debugger: &'a mut Z80Debugger,
cycle: Z80Cycle<Instant>,
bus: Bus,
}
impl<'a, Bus, Instant> ExecuteNext<'a, Bus, Instant>
where
Bus: BusAccess<Z80AddressSpace, Instant = Instant>,
Instant: EmuInstant,
{
pub(crate) fn end(self) -> Z80Cycle<Instant> {
self.cycle
}
pub(crate) fn step_one(&mut self) -> Result<u16, Z80Error> {
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<u16, Z80Error> {
match self.state.status { match self.state.status {
Status::Init => self.init(), Status::Init => self.init(),
Status::Halted => Err(Z80Error::Halted), Status::Halted => Err(Z80Error::Halted),
@ -103,38 +103,37 @@ impl Z80 {
} }
} }
pub fn init(&mut self) -> Result<u16, Z80Error> { fn init(&mut self) -> Result<u16, Z80Error> {
self.state.pc = 0; self.state.pc = 0;
self.state.status = Status::Running; self.state.status = Status::Running;
Ok(16) Ok(16)
} }
pub fn reset(&mut self) -> Result<u16, Z80Error> { fn reset(&mut self) -> Result<u16, Z80Error> {
self.clear_state(); *self.state = Default::default();
Ok(16) Ok(16)
} }
pub fn cycle_one(&mut self) -> Result<u16, Z80Error> { fn cycle_one(&mut self) -> Result<u16, Z80Error> {
self.check_breakpoints()?; self.debugger.check_breakpoints(self.state.pc)?;
self.decode_next()?; self.decode_next()?;
self.execute_current()?; self.execute_current()?;
Ok( Ok(
Z80InstructionCycles::from_instruction(&self.decoder.instruction, self.decoder.extra_instruction_bytes)? Z80InstructionCycles::from_instruction(&self.cycle.decoder.instruction, self.cycle.decoder.extra_instruction_bytes)?
.calculate_cycles(self.executor.took_branch), .calculate_cycles(self.cycle.took_branch),
) )
} }
pub fn decode_next(&mut self) -> Result<(), Z80Error> { fn decode_next(&mut self) -> Result<(), Z80Error> {
self.decoder self.cycle.decoder = Z80Decoder::decode_at(&mut self.bus, self.cycle.current_clock, self.state.pc)?;
.decode_at(&mut self.port, self.executor.current_clock, self.state.pc)?; self.increment_refresh(self.cycle.decoder.end.saturating_sub(self.cycle.decoder.start) as u8);
self.increment_refresh(self.decoder.end.saturating_sub(self.decoder.start) as u8); self.state.pc = self.cycle.decoder.end;
self.state.pc = self.decoder.end;
Ok(()) Ok(())
} }
pub fn execute_current(&mut self) -> Result<(), Z80Error> { fn execute_current(&mut self) -> Result<(), Z80Error> {
match self.decoder.instruction { match self.cycle.decoder.instruction {
Instruction::ADCa(target) => self.execute_adca(target), Instruction::ADCa(target) => self.execute_adca(target),
Instruction::ADC16(dest_pair, src_pair) => self.execute_adc16(dest_pair, src_pair), Instruction::ADC16(dest_pair, src_pair) => self.execute_adc16(dest_pair, src_pair),
Instruction::ADDa(target) => self.execute_adda(target), 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::SRL(target, opt_copy) => self.execute_srl(target, opt_copy),
Instruction::SUB(target) => self.execute_sub(target), Instruction::SUB(target) => self.execute_sub(target),
Instruction::XOR(target) => self.execute_xor(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> { 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; self.state.pc = addr;
Ok(()) Ok(())
} }
fn execute_callcc(&mut self, cond: Condition, addr: u16) -> Result<(), Z80Error> { fn execute_callcc(&mut self, cond: Condition, addr: u16) -> Result<(), Z80Error> {
if self.get_current_condition(cond) { if self.get_current_condition(cond) {
self.executor.took_branch = true; self.cycle.took_branch = true;
self.push_word(self.decoder.end)?; self.push_word(self.cycle.decoder.end)?;
self.state.pc = addr; self.state.pc = addr;
} }
Ok(()) Ok(())
@ -434,7 +433,7 @@ impl Z80 {
self.set_register_value(Register::B, result); self.set_register_value(Register::B, result);
if result != 0 { 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); self.state.pc = self.state.pc.wrapping_add_signed(offset as i16);
} }
Ok(()) Ok(())
@ -567,7 +566,7 @@ impl Z80 {
fn execute_jpcc(&mut self, cond: Condition, addr: u16) -> Result<(), Z80Error> { fn execute_jpcc(&mut self, cond: Condition, addr: u16) -> Result<(), Z80Error> {
if self.get_current_condition(cond) { if self.get_current_condition(cond) {
self.executor.took_branch = true; self.cycle.took_branch = true;
self.state.pc = addr; self.state.pc = addr;
} }
Ok(()) Ok(())
@ -580,7 +579,7 @@ impl Z80 {
fn execute_jrcc(&mut self, cond: Condition, offset: i8) -> Result<(), Z80Error> { fn execute_jrcc(&mut self, cond: Condition, offset: i8) -> Result<(), Z80Error> {
if self.get_current_condition(cond) { 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); self.state.pc = self.state.pc.wrapping_add_signed(offset as i16);
} }
Ok(()) Ok(())
@ -616,7 +615,7 @@ impl Z80 {
} }
fn execute_ldx(&mut self) -> Result<(), Z80Error> { 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 1
} else { } else {
-1 -1
@ -631,8 +630,10 @@ impl Z80 {
let parity = if count != 0 { Flags::Parity as u8 } else { 0 }; let parity = if count != 0 { Flags::Parity as u8 } else { 0 };
self.set_flags(mask, parity); self.set_flags(mask, parity);
if (self.decoder.instruction == Instruction::LDIR || self.decoder.instruction == Instruction::LDDR) && count != 0 { if (self.cycle.decoder.instruction == Instruction::LDIR || self.cycle.decoder.instruction == Instruction::LDDR)
self.executor.took_branch = true; && count != 0
{
self.cycle.took_branch = true;
self.state.pc -= 2; self.state.pc -= 2;
} }
Ok(()) Ok(())
@ -725,7 +726,7 @@ impl Z80 {
fn execute_retcc(&mut self, cond: Condition) -> Result<(), Z80Error> { fn execute_retcc(&mut self, cond: Condition) -> Result<(), Z80Error> {
if self.get_current_condition(cond) { if self.get_current_condition(cond) {
self.executor.took_branch = true; self.cycle.took_branch = true;
self.state.pc = self.pop_word()?; self.state.pc = self.pop_word()?;
} }
Ok(()) Ok(())
@ -852,7 +853,7 @@ impl Z80 {
} }
fn execute_rst(&mut self, addr: u8) -> Result<(), Z80Error> { 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; self.state.pc = addr as u16;
Ok(()) Ok(())
} }
@ -1010,13 +1011,11 @@ impl Z80 {
_ => panic!("RegPair is not supported by inc/dec"), _ => panic!("RegPair is not supported by inc/dec"),
}; };
let result = (read_beu16(addr) as i16).wrapping_add(value) as u16; let result = (u16::from_be_bytes(addr.try_into().unwrap()) as i16).wrapping_add(value) as u16;
write_beu16(addr, result); addr.copy_from_slice(&result.to_be_bytes()[..]);
result result
} }
fn push_word(&mut self, value: u16) -> Result<(), Z80Error> { fn push_word(&mut self, value: u16) -> Result<(), Z80Error> {
self.state.sp = self.state.sp.wrapping_sub(1); self.state.sp = self.state.sp.wrapping_sub(1);
self.write_port_u8(self.state.sp, (value >> 8) as u8)?; 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<u8, Z80Error> { fn read_port_u8(&mut self, addr: u16) -> Result<u8, Z80Error> {
self.increment_refresh(1); 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> { fn write_port_u8(&mut self, addr: u16, value: u8) -> Result<(), Z80Error> {
self.increment_refresh(1); 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<u16, Z80Error> { /// Read a u16 value through this CPU's memory port
self.increment_refresh(2); ///
Ok(self.port.read_leu16(self.executor.current_clock, addr as Address)?) /// 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<u16, Z80Error> {
fn write_port_u16(&mut self, addr: u16, value: u16) -> Result<(), Z80Error> { let mut bytes = [0; 2];
self.increment_refresh(2); for byte in bytes.iter_mut() {
Ok(self.port.write_leu16(self.executor.current_clock, addr as Address, value)?) self.increment_refresh(1);
} *byte = self
.bus
fn read_ioport_value(&mut self, upper: u8, lower: u8) -> Result<u8, Z80Error> { .read_u8(self.cycle.current_clock, Z80AddressSpace::Memory(addr))
let addr = ((upper as Address) << 8) | (lower as Address); .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?;
if let Some(io) = self.ioport.as_mut() { addr = addr.wrapping_add(1);
Ok(io.read_u8(self.executor.current_clock, addr)?)
} else {
Ok(0)
} }
Ok(u16::from_le_bytes(bytes))
} }
fn write_ioport_value(&mut self, upper: u8, lower: u8, value: u8) -> Result<(), Z80Error> { /// Write a u16 value through this CPU's memory port
let addr = ((upper as Address) << 8) | (lower as Address); ///
if let Some(io) = self.ioport.as_mut() { /// Since the memory port is only able to read 8 bits at a time, this does two writes
io.write_u8(self.executor.current_clock, addr, value)? /// 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(()) Ok(())
} }
fn read_ioport_value(&mut self, upper: u8, lower: u8) -> Result<u8, Z80Error> {
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 { fn get_register_value(&mut self, reg: Register) -> u8 {
self.state.reg[reg as usize] self.state.reg[reg as usize]
@ -1199,10 +1224,10 @@ impl Z80 {
fn get_register_pair_value(&mut self, regpair: RegisterPair) -> u16 { fn get_register_pair_value(&mut self, regpair: RegisterPair) -> u16 {
match regpair { match regpair {
RegisterPair::BC => read_beu16(&self.state.reg[0..2]), RegisterPair::BC => u16::from_be_bytes(self.state.reg[0..2].try_into().unwrap()),
RegisterPair::DE => read_beu16(&self.state.reg[2..4]), RegisterPair::DE => u16::from_be_bytes(self.state.reg[2..4].try_into().unwrap()),
RegisterPair::HL => read_beu16(&self.state.reg[4..6]), RegisterPair::HL => u16::from_be_bytes(self.state.reg[4..6].try_into().unwrap()),
RegisterPair::AF => read_beu16(&self.state.reg[6..8]), RegisterPair::AF => u16::from_be_bytes(self.state.reg[6..8].try_into().unwrap()),
RegisterPair::SP => self.state.sp, RegisterPair::SP => self.state.sp,
RegisterPair::IX => self.state.ix, RegisterPair::IX => self.state.ix,
RegisterPair::IY => self.state.iy, RegisterPair::IY => self.state.iy,
@ -1212,16 +1237,16 @@ impl Z80 {
fn set_register_pair_value(&mut self, regpair: RegisterPair, value: u16) { fn set_register_pair_value(&mut self, regpair: RegisterPair, value: u16) {
match regpair { match regpair {
RegisterPair::BC => { 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 => { 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 => { 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 => { 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 => { RegisterPair::SP => {
self.state.sp = value; self.state.sp = value;

View File

@ -1,8 +1,21 @@
pub mod debugger; mod debugger;
pub mod decode; mod decode;
pub mod execute; mod emuhal;
pub mod instructions; mod execute;
pub mod state; mod instructions;
pub mod timing; 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;

View File

@ -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<Instant>
where
Instant: EmuInstant,
{
pub bus: Rc<RefCell<Bus>>,
pub cpu: Z80<Instant>,
}
impl Steppable for MoaZ80<Instant>
where
Instant: EmuInstant,
{
fn step(&mut self, system: &System) -> Result<Duration, Error> {
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<Instant> {}
/*
impl Signalable for Z80<Instant> {
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<bool> {
match signal {
Signal::Reset => Some(self.signals.reset),
Signal::BusRequest => Some(self.signals.bus_request),
}
}
}
*/
impl Transmutable for MoaZ80<Instant> {
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<Z80Error> 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<Error> 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<Instant> {
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<bool, Error> {
match args[0] {
"l" => self.cpu.state.reg[Register::L as usize] = 0x05,
_ => {
return Ok(true);
},
}
Ok(false)
}
}

View File

@ -1,13 +1,11 @@
use std::rc::Rc; use core::fmt::{self, Write};
use std::cell::RefCell; use femtos::Frequency;
use femtos::{Instant, Frequency}; use emulator_hal::{Instant as EmuInstant, BusAccess};
use moa_core::{Address, Bus, BusPort};
use moa_signals::Signal; use moa_signals::Signal;
use crate::decode::Z80Decoder;
use crate::debugger::Z80Debugger; use crate::debugger::Z80Debugger;
use crate::execute::Z80Executor; use crate::execute::Z80Cycle;
use crate::instructions::{Instruction, Register, InterruptMode}; 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<bool>,
pub bus_request: Signal<bool>,
}
#[derive(Clone, Debug, thiserror::Error)] #[derive(Clone, Debug, thiserror::Error)]
pub enum Z80Error /* <B: fmt::Display> */ { pub enum Z80Error /* <B: fmt::Display> */ {
#[error("cpu halted")] #[error("cpu halted")]
@ -100,111 +106,119 @@ pub enum Z80Error /* <B: fmt::Display> */ {
Breakpoint, Breakpoint,
#[error("unimplemented instruction {0:?}")] #[error("unimplemented instruction {0:?}")]
Unimplemented(Instruction), Unimplemented(Instruction),
#[error("unexpected instruction {0:?}")]
UnexpectedInstruction(Instruction),
#[error("bus error: {0}")] #[error("bus error: {0}")]
BusError(String /* B */), 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)] #[derive(Clone)]
pub struct Z80 { pub struct Z80<Instant> {
pub cputype: Z80Type, pub cputype: Z80Type,
pub frequency: Frequency, pub frequency: Frequency,
pub state: Z80State, pub state: Z80State,
pub decoder: Z80Decoder,
pub debugger: Z80Debugger, pub debugger: Z80Debugger,
pub executor: Z80Executor, pub previous_cycle: Z80Cycle<Instant>,
pub port: BusPort, pub signals: Z80Signals,
pub ioport: Option<BusPort>,
pub reset: Signal<bool>,
pub bus_request: Signal<bool>,
} }
impl Z80 { impl<Instant> Z80<Instant>
pub fn new(cputype: Z80Type, frequency: Frequency, port: BusPort, ioport: Option<BusPort>) -> Self { where
Instant: EmuInstant,
{
pub fn new(cputype: Z80Type, frequency: Frequency) -> Self {
Self { Self {
cputype, cputype,
frequency, frequency,
state: Z80State::default(), state: Z80State::default(),
decoder: Z80Decoder::default(),
debugger: Z80Debugger::default(), debugger: Z80Debugger::default(),
executor: Z80Executor::at_time(Instant::START), previous_cycle: Z80Cycle::at_time(Instant::START),
port, signals: Z80Signals::default(),
ioport,
reset: Signal::new(false),
bus_request: Signal::new(false),
} }
} }
pub fn from_type( pub fn from_type(cputype: Z80Type, frequency: Frequency) -> Self {
cputype: Z80Type,
frequency: Frequency,
bus: Rc<RefCell<Bus>>,
addr_offset: Address,
io_bus: Option<(Rc<RefCell<Bus>>, Address)>,
) -> Self {
match cputype { match cputype {
Z80Type::Z80 => Self::new( Z80Type::Z80 => Self::new(cputype, frequency),
cputype,
frequency,
BusPort::new(addr_offset, 16, 8, bus),
io_bus.map(|(io_bus, io_offset)| BusPort::new(io_offset, 16, 8, io_bus)),
),
} }
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn clear_state(&mut self) { pub fn clear_state(&mut self) {
self.state = Z80State::default(); self.state = Z80State::default();
self.decoder = Z80Decoder::default();
self.debugger = Z80Debugger::default(); self.debugger = Z80Debugger::default();
self.executor = Z80Executor::at_time(Instant::START);
} }
pub fn dump_state(&mut self, clock: Instant) { pub fn dump_state<W, Bus>(&mut self, writer: &mut W, _clock: Instant, bus: &mut Bus) -> Result<(), fmt::Error>
println!("Status: {:?}", self.state.status); where
println!("PC: {:#06x}", self.state.pc); W: Write,
println!("SP: {:#06x}", self.state.sp); Bus: BusAccess<Z80AddressSpace, Instant = Instant>,
println!("IX: {:#06x}", self.state.ix); {
println!("IY: {:#06x}", self.state.iy); 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}", "A: {:#04x} F: {:#04x} A': {:#04x} F': {:#04x}",
self.state.reg[Register::A as usize], self.state.reg[Register::A as usize],
self.state.reg[Register::F as usize], self.state.reg[Register::F as usize],
self.state.shadow_reg[Register::A as usize], self.state.shadow_reg[Register::A as usize],
self.state.shadow_reg[Register::F as usize] self.state.shadow_reg[Register::F as usize]
); )?;
println!( writeln!(
writer,
"B: {:#04x} C: {:#04x} B': {:#04x} C': {:#04x}", "B: {:#04x} C: {:#04x} B': {:#04x} C': {:#04x}",
self.state.reg[Register::B as usize], self.state.reg[Register::B as usize],
self.state.reg[Register::C as usize], self.state.reg[Register::C as usize],
self.state.shadow_reg[Register::B as usize], self.state.shadow_reg[Register::B as usize],
self.state.shadow_reg[Register::C as usize] self.state.shadow_reg[Register::C as usize]
); )?;
println!( writeln!(
writer,
"D: {:#04x} E: {:#04x} D': {:#04x} E': {:#04x}", "D: {:#04x} E: {:#04x} D': {:#04x} E': {:#04x}",
self.state.reg[Register::D as usize], self.state.reg[Register::D as usize],
self.state.reg[Register::E as usize], self.state.reg[Register::E as usize],
self.state.shadow_reg[Register::D as usize], self.state.shadow_reg[Register::D as usize],
self.state.shadow_reg[Register::E as usize] self.state.shadow_reg[Register::E as usize]
); )?;
println!( writeln!(
writer,
"H: {:#04x} L: {:#04x} H': {:#04x} L': {:#04x}", "H: {:#04x} L: {:#04x} H': {:#04x} L': {:#04x}",
self.state.reg[Register::H as usize], self.state.reg[Register::H as usize],
self.state.reg[Register::L as usize], self.state.reg[Register::L as usize],
self.state.shadow_reg[Register::H as usize], self.state.shadow_reg[Register::H as usize],
self.state.shadow_reg[Register::L as usize] self.state.shadow_reg[Register::L as usize]
); )?;
println!("I: {:#04x} R: {:#04x}", self.state.i, self.state.r); writeln!(writer, "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, "IM: {:?} IFF1: {:?} IFF2: {:?}", self.state.im, self.state.iff1, self.state.iff2)?;
println!( writeln!(
writer,
"Current Instruction: {} {:?}", "Current Instruction: {} {:?}",
self.decoder.format_instruction_bytes(&mut self.port), self.previous_cycle.decoder.format_instruction_bytes(bus),
self.decoder.instruction self.previous_cycle.decoder.instruction
); )?;
println!(); writeln!(writer, "Previous Instruction: {:?}", self.previous_cycle.decoder.instruction)?;
self.port.dump_memory(clock, self.state.sp as Address, 0x40); writeln!(writer)?;
println!(); // TODO disabled until function is reimplemented
//self.port.dump_memory(clock, self.state.sp as Address, 0x40);
writeln!(writer)?;
Ok(())
} }
} }

View File

@ -1,6 +1,4 @@
use moa_core::Error; use crate::{Z80Error, Instruction, Target, LoadTarget, RegisterPair};
use crate::instructions::{Instruction, Target, LoadTarget, RegisterPair};
pub enum Z80InstructionCycles { pub enum Z80InstructionCycles {
Single(u16), Single(u16),
@ -37,7 +35,7 @@ impl Z80InstructionCycles {
} }
} }
pub fn from_instruction(instruction: &Instruction, extra: u16) -> Result<Z80InstructionCycles, Error> { pub fn from_instruction(instruction: &Instruction, extra: u16) -> Result<Z80InstructionCycles, Z80Error> {
let cycles = match instruction { let cycles = match instruction {
Instruction::ADCa(target) Instruction::ADCa(target)
| Instruction::ADDa(target) | Instruction::ADDa(target)
@ -67,7 +65,7 @@ impl Z80InstructionCycles {
Target::DirectReg(_) => 8, Target::DirectReg(_) => 8,
Target::IndirectReg(_) => 12, Target::IndirectReg(_) => 12,
Target::IndirectOffset(_, _) => 20, Target::IndirectOffset(_, _) => 20,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))), _ => return Err(Z80Error::UnexpectedInstruction(instruction.clone())),
}, },
Instruction::CALL(_) => 17, Instruction::CALL(_) => 17,
@ -111,7 +109,7 @@ impl Z80InstructionCycles {
Target::DirectReg(_) | Target::DirectRegHalf(_) => 4, Target::DirectReg(_) | Target::DirectRegHalf(_) => 4,
Target::IndirectReg(_) => 11, Target::IndirectReg(_) => 11,
Target::IndirectOffset(_, _) => 23, Target::IndirectOffset(_, _) => 23,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))), _ => return Err(Z80Error::UnexpectedInstruction(instruction.clone())),
}, },
Instruction::DEC16(regpair) | Instruction::INC16(regpair) => { Instruction::DEC16(regpair) | Instruction::INC16(regpair) => {
@ -210,7 +208,7 @@ impl Z80InstructionCycles {
(LoadTarget::IndirectWord(_), _) | (_, LoadTarget::IndirectWord(_)) => 20, (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::DirectReg(_) => 8,
Target::IndirectReg(_) => 15, Target::IndirectReg(_) => 15,
Target::IndirectOffset(_, _) => 23, Target::IndirectOffset(_, _) => 23,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))), _ => return Err(Z80Error::UnexpectedInstruction(instruction.clone())),
}, },
Instruction::RET => 10, Instruction::RET => 10,
@ -263,7 +261,7 @@ impl Z80InstructionCycles {
Target::DirectReg(_) => 8, Target::DirectReg(_) => 8,
Target::IndirectReg(_) => 15, Target::IndirectReg(_) => 15,
Target::IndirectOffset(_, _) => 23, 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, Instruction::RLA | Instruction::RLCA | Instruction::RRA | Instruction::RRCA => 4,

View File

@ -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::{Z80, Z80Type, Z80Port, Instruction, LoadTarget, Target, Register, RegisterPair, IndexRegister, IndexRegisterHalf};
use moa_z80::instructions::{Instruction, LoadTarget, Target, Register, RegisterPair, IndexRegister, IndexRegisterHalf};
fn init_decode_test() -> (Z80, System) {
let mut system = System::default();
fn init_decode_test() -> (Z80<Instant>, MemoryBlock<Instant>) {
// Insert basic initialization // Insert basic initialization
let data = vec![0; 0x10000]; let len = 0x10_0000;
let mem = MemoryBlock::new(data); let mut data = Vec::with_capacity(len);
system.add_addressable_device(0x0000, Device::new(mem)).unwrap(); 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 // 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); let mut cpu = Z80::new(Z80Type::Z80, Frequency::from_mhz(4));
cpu.reset().unwrap(); 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<Instant>, data: &[u8]) {
for i in 0..data.len() { 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 { fn run_decode_test(data: &[u8]) -> Instruction {
let (mut cpu, system) = init_decode_test(); let (mut cpu, mut memory) = init_decode_test();
load_memory(&system, data); load_memory(&mut memory, data);
cpu.decode_next().unwrap(); let mut io = NoBus::new();
cpu.decoder.instruction let mut bus = Z80Port::new(&mut memory, &mut io);
cpu.step(Instant::START, &mut bus).unwrap();
cpu.previous_cycle.decoder.instruction
} }
#[test] #[test]

View File

@ -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::{Z80, Z80Type, Z80Port, Z80State, Status, Instruction, LoadTarget, Target, Register, RegisterPair, Condition};
use moa_z80::state::Z80State;
use moa_z80::instructions::{Instruction, LoadTarget, Target, Register, RegisterPair, Condition};
struct TestState { struct TestState {
pc: u16, pc: u16,
@ -482,23 +481,28 @@ const TEST_CASES: &'static [TestCase] = &[
]; ];
fn init_execute_test() -> (Z80, System) { fn init_execute_test() -> (Z80<Instant>, MemoryBlock<Instant>) {
let mut system = System::default();
// Insert basic initialization // Insert basic initialization
let data = vec![0; 0x10000]; let len = 0x10_0000;
let mem = MemoryBlock::new(data); let mut data = Vec::with_capacity(len);
system.add_addressable_device(0x0000, Device::new(mem)).unwrap(); 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 // 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); let mut cpu = Z80::new(Z80Type::Z80, Frequency::from_mhz(4));
cpu.init().unwrap(); 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 { fn build_state(state: &TestState) -> Z80State {
let mut new_state = Z80State::default(); let mut new_state = Z80State::default();
new_state.status = Status::Running;
new_state.pc = state.pc; new_state.pc = state.pc;
new_state.sp = state.sp; new_state.sp = state.sp;
new_state.ix = state.ix; new_state.ix = state.ix;
@ -514,25 +518,26 @@ fn build_state(state: &TestState) -> Z80State {
new_state new_state
} }
fn load_memory(system: &System, data: &[u8]) { fn load_memory(memory: &mut MemoryBlock<Instant>, data: &[u8]) {
for i in 0..data.len() { 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) { 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 init_state = build_state(&case.init);
let mut expected_state = build_state(&case.fini); let mut expected_state = build_state(&case.fini);
load_memory(&system, case.data); load_memory(&mut memory, case.data);
cpu.state = init_state; cpu.state = init_state;
cpu.decode_next().unwrap(); let mut io = NoBus::new();
assert_eq!(cpu.decoder.instruction, case.ins); 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 // TODO this is a hack to ignore the functioning of the F5, F3 flags for now
cpu.state.reg[Register::F as usize] &= 0xD7; cpu.state.reg[Register::F as usize] &= 0xD7;

View File

@ -8,7 +8,7 @@ fn main() {
Arg::new("ROM") Arg::new("ROM")
.short('r') .short('r')
.long("rom") .long("rom")
.action(ArgAction::SetTrue) .action(ArgAction::Set)
.value_name("FILE") .value_name("FILE")
.help("ROM file to load at the start of memory"), .help("ROM file to load at the start of memory"),
) )

@ -1 +1 @@
Subproject commit 2391a324376bdd9fa1ae9801bbe3d12f2e69fa62 Subproject commit e4c6a04f59dc06798e67c86cdfb8299ddada6696

View File

@ -2,7 +2,7 @@
name = "moa-host" name = "moa-host"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
rust-version = "1.60" rust-version = "1.70"
categories = ["emulators"] categories = ["emulators"]
keywords = ["emulators"] keywords = ["emulators"]
description = "traits for abstracting the I/O of an emulated system to the host" description = "traits for abstracting the I/O of an emulated system to the host"

View File

@ -19,7 +19,7 @@ type Input<T> = Signal<T>;
#[allow(dead_code)] #[allow(dead_code)]
type TriState<T> = Signal<T>; type TriState<T> = Signal<T>;
#[derive(Clone, Debug)] #[derive(Clone, Debug, Default)]
pub struct Signal<T: Copy>(Rc<Cell<T>>); pub struct Signal<T: Copy>(Rc<Cell<T>>);
impl<T: Copy> Signal<T> { impl<T: Copy> Signal<T> {

View File

@ -11,5 +11,5 @@ moa-signals = { path = "../../libraries/signals" }
moa-host = { path = "../../libraries/host" } moa-host = { path = "../../libraries/host" }
moa-peripherals-yamaha = { path = "../../peripherals/yamaha" } moa-peripherals-yamaha = { path = "../../peripherals/yamaha" }
moa-m68k = { path = "../../cpus/m68k", features = ["moa"] } moa-m68k = { path = "../../cpus/m68k", features = ["moa"] }
moa-z80 = { path = "../../cpus/z80" } moa-z80 = { path = "../../cpus/z80", features = ["moa"] }

View File

@ -8,7 +8,7 @@ use moa_core::{System, Error, MemoryBlock, Bus, Address, Addressable, Device};
use moa_host::Host; use moa_host::Host;
use moa_m68k::{M68k, M68kType}; 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::Ym2612;
use moa_peripherals_yamaha::Sn76489; use moa_peripherals_yamaha::Sn76489;
@ -68,11 +68,16 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
coproc_bus.borrow_mut().insert(0x6000, coproc_register.clone()); coproc_bus.borrow_mut().insert(0x6000, coproc_register.clone());
coproc_bus.borrow_mut().insert(0x7f11, coproc_sn_sound.clone()); coproc_bus.borrow_mut().insert(0x7f11, coproc_sn_sound.clone());
coproc_bus.borrow_mut().insert(0x8000, coproc_area); 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 coproc = Z80::from_type(Z80Type::Z80, Frequency::from_hz(3_579_545));
let mut reset = coproc.reset.clone(); let coproc = MoaZ80 {
let mut bus_request = coproc.bus_request.clone(); 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); reset.set(true);
bus_request.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 // Add coprocessor devices to the system bus so the 68000 can access them too
system.add_addressable_device(0x00a00000, coproc_ram)?; system.add_addressable_device(0x00a00000, coproc_ram)?;
@ -80,7 +85,7 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
system.add_addressable_device(0x00a06000, coproc_register)?; system.add_addressable_device(0x00a06000, coproc_register)?;
//system.add_addressable_device(0x00c00010, coproc_sn_sound)?; //system.add_addressable_device(0x00c00010, coproc_sn_sound)?;
system.add_device("sn_sound", coproc_sn_sound.clone())?; 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)?; let controllers = GenesisControllers::new(host)?;

View File

@ -3,7 +3,7 @@ use femtos::Frequency;
use moa_core::{System, Error, MemoryBlock, Device}; use moa_core::{System, Error, MemoryBlock, Device};
use moa_host::Host; use moa_host::Host;
use moa_z80::{Z80, Z80Type}; use moa_z80::{MoaZ80, Z80, Z80Type};
use crate::peripherals::model1::{Model1Keyboard, Model1Video}; use crate::peripherals::model1::{Model1Keyboard, Model1Video};
@ -44,7 +44,11 @@ pub fn build_trs80<H: Host>(host: &mut H, options: Trs80Options) -> Result<Syste
system.add_addressable_device(0x37E0 + 0x420, Device::new(video)).unwrap(); system.add_addressable_device(0x37E0 + 0x420, Device::new(video)).unwrap();
// TODO the ioport needs to be hooked up // TODO the ioport needs to be hooked up
let cpu = Z80::from_type(Z80Type::Z80, options.frequency, system.bus.clone(), 0, None); let cpu = Z80::from_type(Z80Type::Z80, options.frequency);
let cpu = MoaZ80 {
bus: system.bus.clone(),
cpu,
};
system.add_interruptable_device("cpu", Device::new(cpu))?; system.add_interruptable_device("cpu", Device::new(cpu))?;

View File

@ -11,8 +11,7 @@ use flate2::read::GzDecoder;
use serde_derive::Deserialize; use serde_derive::Deserialize;
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess; use emulator_hal::{BusAccess, Step};
use emulator_hal::step::Step;
use emulator_hal_memory::MemoryBlock; use emulator_hal_memory::MemoryBlock;
use moa_m68k::{M68k, M68kType}; use moa_m68k::{M68k, M68kType};
@ -146,14 +145,14 @@ impl TestCase {
#[allow(clippy::uninit_vec)] #[allow(clippy::uninit_vec)]
fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k<Instant>, MemoryBlock<u32, Instant>), Error> { fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k<Instant>, MemoryBlock<Instant>), Error> {
// Insert basic initialization // Insert basic initialization
let len = 0x100_0000; let len = 0x100_0000;
let mut data = Vec::with_capacity(len); let mut data = Vec::with_capacity(len);
unsafe { unsafe {
data.set_len(len); data.set_len(len);
} }
let mut memory = MemoryBlock::<u32, Instant>::from(data); let mut memory = MemoryBlock::from(data);
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10)); let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
cpu.state.status = Status::Running; cpu.state.status = Status::Running;
@ -174,7 +173,7 @@ where
} }
} }
fn load_state(cpu: &mut M68k<Instant>, memory: &mut MemoryBlock<u32, Instant>, initial: &TestState) -> Result<(), Error> { fn load_state(cpu: &mut M68k<Instant>, memory: &mut MemoryBlock<Instant>, initial: &TestState) -> Result<(), Error> {
cpu.state.d_reg[0] = initial.d0; cpu.state.d_reg[0] = initial.d0;
cpu.state.d_reg[1] = initial.d1; cpu.state.d_reg[1] = initial.d1;
cpu.state.d_reg[2] = initial.d2; cpu.state.d_reg[2] = initial.d2;
@ -213,7 +212,7 @@ fn load_state(cpu: &mut M68k<Instant>, memory: &mut MemoryBlock<u32, Instant>, i
Ok(()) Ok(())
} }
fn assert_state(cpu: &M68k<Instant>, memory: &mut MemoryBlock<u32, Instant>, expected: &TestState) -> Result<(), Error> { fn assert_state(cpu: &M68k<Instant>, memory: &mut MemoryBlock<Instant>, expected: &TestState) -> Result<(), Error> {
assert_value(cpu.state.d_reg[0], expected.d0, "d0")?; 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[1], expected.d1, "d1")?;
assert_value(cpu.state.d_reg[2], expected.d2, "d2")?; assert_value(cpu.state.d_reg[2], expected.d2, "d2")?;
@ -259,7 +258,7 @@ fn assert_state(cpu: &M68k<Instant>, memory: &mut MemoryBlock<u32, Instant>, exp
fn step_cpu_and_assert( fn step_cpu_and_assert(
cpu: &mut M68k<Instant>, cpu: &mut M68k<Instant>,
memory: &mut MemoryBlock<u32, Instant>, memory: &mut MemoryBlock<Instant>,
case: &TestCase, case: &TestCase,
test_timing: bool, test_timing: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {

View File

@ -5,7 +5,8 @@ edition = "2021"
[dependencies] [dependencies]
femtos = "0.1" 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" } moa-z80 = { path = "../../emulator/cpus/z80" }
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"

View File

@ -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! 00.json completed, all passed!
01.json completed, all passed! 01.json completed, all passed!
@ -1612,4 +1612,4 @@ fe.json completed, all passed!
ff.json completed, all passed! ff.json completed, all passed!
passed: 1574638, failed: 35362, total 98% passed: 1574638, failed: 35362, total 98%
completed in 1m 19s completed in 0m 9s

View File

@ -2,10 +2,11 @@
COMMIT=$(git rev-parse HEAD) COMMIT=$(git rev-parse HEAD)
DATE=$(date --iso) DATE=$(date --iso)
LOCATION=$(dirname ${BASH_SOURCE[0]}) LOCATION=$(dirname ${BASH_SOURCE[0]})
FLAGS=("--check-undocumented" "--check-timings")
RESULTS=latest.txt RESULTS=latest.txt
{ {
cd $LOCATION 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 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
} }

View File

@ -1,7 +1,5 @@
const DEFAULT_RAD_TESTS: &str = "tests/jsmoo/misc/tests/GeneratedTests/z80/v1/"; 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::io::prelude::*;
use std::fmt::{Debug, UpperHex}; use std::fmt::{Debug, UpperHex};
use std::path::PathBuf; use std::path::PathBuf;
@ -11,15 +9,19 @@ use std::fs::{self, File};
use clap::Parser; use clap::Parser;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use serde_derive::Deserialize; 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::{Z80, Z80Type, Z80Port, InterruptMode, Flags, Status};
use moa_z80::instructions::InterruptMode;
use moa_z80::state::Flags;
use moa_z80::state::Status;
#[derive(Clone, Debug)]
enum Error {
Assertion(String),
Bus(String),
Step(String),
}
#[derive(Parser)] #[derive(Parser)]
struct Args { struct Args {
@ -43,6 +45,9 @@ struct Args {
/// Check instruction timings /// Check instruction timings
#[clap(short = 't', long)] #[clap(short = 't', long)]
check_timings: bool, check_timings: bool,
/// Don't check I/O instructions
#[clap(short = 'i', long)]
no_check_io: bool,
/// Directory to the test suite to run /// Directory to the test suite to run
#[clap(long, default_value = DEFAULT_RAD_TESTS)] #[clap(long, default_value = DEFAULT_RAD_TESTS)]
testsuite: String, testsuite: String,
@ -53,7 +58,6 @@ fn main() {
run_all_tests(&args); run_all_tests(&args);
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct TestState { struct TestState {
pc: u16, pc: u16,
@ -107,6 +111,8 @@ struct TestCase {
ports: Vec<TestPort>, ports: Vec<TestPort>,
} }
type Machine = (Z80<Instant>, MemoryBlock<Instant>, MemoryBlock<Instant>);
impl TestState { impl TestState {
pub fn dump(&self) { pub fn dump(&self) {
println!(" a: {:02x} a': {:02x}", self.a, self.af_ >> 8); println!(" a: {:02x} a': {:02x}", self.a, self.af_ >> 8);
@ -144,28 +150,30 @@ impl TestCase {
} }
} }
#[allow(clippy::uninit_vec)]
fn init_execute_test(cputype: Z80Type, state: &TestState, ports: &[TestPort]) -> Result<(Z80, System, Rc<RefCell<Bus>>), Error> { fn init_execute_test(cputype: Z80Type, state: &TestState, ports: &[TestPort]) -> Result<Machine, Error> {
let mut system = System::default();
// Insert basic initialization // Insert basic initialization
let mem = MemoryBlock::new(vec![0; 0x1_0000]); let len = 0x1_0000;
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); let mut data = Vec::with_capacity(len);
unsafe {
data.set_len(len);
}
let mut memory = MemoryBlock::<Instant>::from(data);
// Set up IOREQ as memory space // Set up IOREQ as memory space
let io_ram = Device::new(MemoryBlock::new(vec![0; 0x10000])); let len = 0x1_0000;
let io_bus = Rc::new(RefCell::new(Bus::default())); let mut data = Vec::with_capacity(len);
io_bus.borrow_mut().set_ignore_unmapped(true); unsafe {
io_bus.borrow_mut().insert(0x0000, io_ram); data.set_len(len);
}
let mut io = MemoryBlock::<Instant>::from(data);
let port = BusPort::new(0, 16, 8, system.bus.clone()); let mut cpu = Z80::new(cputype, Frequency::from_mhz(10));
let ioport = BusPort::new(0, 16, 8, io_bus.clone());
let mut cpu = Z80::new(cputype, Frequency::from_mhz(10), port, Some(ioport));
cpu.state.status = Status::Running; 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<T>(actual: T, expected: T, message: &str) -> Result<(), Error> fn assert_value<T>(actual: T, expected: T, message: &str) -> Result<(), Error>
@ -175,14 +183,14 @@ where
if actual == expected { if actual == expected {
Ok(()) Ok(())
} else { } else {
Err(Error::assertion(format!("{:#X} != {:#X}, {}", actual, expected, message))) Err(Error::Assertion(format!("{:#X} != {:#X}, {}", actual, expected, message)))
} }
} }
fn load_state( fn load_state(
cpu: &mut Z80, cpu: &mut Z80<Instant>,
system: &mut System, memory: &mut MemoryBlock<Instant>,
io_bus: Rc<RefCell<Bus>>, io: &mut MemoryBlock<Instant>,
initial: &TestState, initial: &TestState,
ports: &[TestPort], ports: &[TestPort],
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -215,12 +223,15 @@ fn load_state(
// Load data bytes into memory // Load data bytes into memory
for (addr, byte) in initial.ram.iter() { 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 // Load data bytes into io space
for port in ports.iter() { 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(()) Ok(())
@ -229,9 +240,9 @@ fn load_state(
const IGNORE_FLAG_MASK: u8 = Flags::F3 as u8 | Flags::F5 as u8; const IGNORE_FLAG_MASK: u8 = Flags::F3 as u8 | Flags::F5 as u8;
fn assert_state( fn assert_state(
cpu: &Z80, cpu: &Z80<Instant>,
system: &System, memory: &mut MemoryBlock<Instant>,
io_bus: Rc<RefCell<Bus>>, io: &mut MemoryBlock<Instant>,
expected: &TestState, expected: &TestState,
check_extra_flags: bool, check_extra_flags: bool,
ports: &[TestPort], ports: &[TestPort],
@ -267,23 +278,25 @@ fn assert_state(
let expected_im: InterruptMode = expected.im.into(); let expected_im: InterruptMode = expected.im.into();
if cpu.state.im != expected_im { 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.iff1 as u8, expected.iff1, "iff1")?;
assert_value(cpu.state.iff2 as u8, expected.iff2, "iff2")?; assert_value(cpu.state.iff2 as u8, expected.iff2, "iff2")?;
let addr_mask = cpu.port.address_mask(); // Compare data bytes in memory
// Load data bytes into memory
for (addr, byte) in expected.ram.iter() { 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))?; 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() { for port in ports.iter() {
if port.atype == "w" { 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))?; assert_value(actual, port.value, &format!("port value at {:x}", port.addr))?;
} }
} }
@ -292,19 +305,22 @@ fn assert_state(
} }
fn step_cpu_and_assert( fn step_cpu_and_assert(
cpu: &mut Z80, cpu: &mut Z80<Instant>,
system: &System, memory: &mut MemoryBlock<Instant>,
io_bus: Rc<RefCell<Bus>>, io: &mut MemoryBlock<Instant>,
case: &TestCase, case: &TestCase,
args: &Args, args: &Args,
) -> Result<(), Error> { ) -> 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 { if args.check_timings {
let cycles = clock_elapsed / cpu.frequency.period_duration(); let cycles = clock_elapsed.as_duration() / cpu.frequency.period_duration();
if cycles != case.cycles.len() as Address { if cycles != case.cycles.len() as u64 {
return Err(Error::assertion(format!( return Err(Error::Assertion(format!(
"expected instruction to take {} cycles, but took {}", "expected instruction to take {} cycles, but took {}",
case.cycles.len(), case.cycles.len(),
cycles cycles
@ -316,10 +332,10 @@ fn step_cpu_and_assert(
} }
fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> { 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 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 { match result {
Ok(()) => Ok(()), Ok(()) => Ok(()),
@ -328,8 +344,11 @@ fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> {
if args.debug { if args.debug {
case.dump(); case.dump();
println!(); println!();
initial_cpu.dump_state(system.clock); let mut bus = Z80Port::new(&mut memory, &mut io);
cpu.dump_state(system.clock); 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); 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 // Sort the ram memory for debugging help
if args.debug { if args.debug {
case.initial_state.ram.sort_by_key(|(addr, _)| *addr); 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) (passed, failed, message)
} }
fn run_all_tests(args: &Args) { fn run_all_tests(args: &Args) {
let mut passed = 0; let mut passed = 0;
let mut failed = 0; let mut failed = 0;
let mut messages = vec![]; let mut messages = vec![];
let mut tests: Vec<PathBuf> = fs::read_dir(&args.testsuite) let mut tests: Vec<PathBuf> = fs::read_dir(&args.testsuite)
.unwrap() .unwrap()
.map(|dirent| dirent.unwrap().path()) .map(|dirent| dirent.unwrap().path())

View File

@ -1,14 +1,20 @@
* decide if you should continue expecting Instant to usable through the trait alone, despite issues * fix the Z80 reset and bus_request signals
* 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 * the emulator_hal_memory should throw an error when an access will straddle the end of memory? Or should it autowrap?
* 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 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 * 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 the Z80
* convert peripherals to use BusAccess and Step * 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 * replace Addressable/Steppable and modify Transmutable to use the emulator-hal traits
* remove the custom moa impls from m68k if possible at this point * remove the custom moa impls from m68k if possible at this point
* publish the emulator-hal crate * publish the emulator-hal crate
@ -79,7 +85,7 @@
* add doc strings everywhere * add doc strings everywhere
* get rustfmt, rustdoc, and clippy working in some kind of semi-automatic fashion * 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?