This commit is contained in:
transistor fet 2024-04-13 18:36:08 +00:00 committed by GitHub
commit b34588d73a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 786 additions and 469 deletions

3
Cargo.lock generated
View File

@ -1247,9 +1247,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, Error as EmuError};
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 EmuError 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, Error as 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};
@ -28,10 +26,10 @@ pub enum M68kInfo {
State, State,
} }
impl<Bus, BusError, Instant, Writer> Inspect<M68kAddress, Bus, Writer> for M68k<Instant> impl<Bus, BusError, Instant, Writer> Inspect<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::{Instant as BusInstant, Error as BusError, BusAccess, Step};
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, Error, 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 {
@ -74,12 +72,13 @@ where
} }
} }
impl<Bus, BusError, Instant> Step<M68kAddress, Bus> for M68k<Instant> impl<Bus, BusError, Instant> Step<Bus> for M68k<Instant>
where where
Bus: BusAccess<M68kAddress, Instant = Instant, Error = BusError>, Bus: BusAccess<M68kAddress, Instant = Instant, Error = BusError>,
BusError: bus::Error, BusError: Error,
Instant: time::Instant, Instant: BusInstant,
{ {
type Instant = Instant;
type Error = M68kError<BusError>; type Error = M68kError<BusError>;
fn is_running(&mut self) -> bool { fn is_running(&mut self) -> bool {

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::{Error as 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,8 @@ 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> =
bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err); 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 +60,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 +86,18 @@ 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, |err| err);
// 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 +106,8 @@ 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> =
bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err); 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,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};

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

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

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

View File

@ -7,6 +7,8 @@ edition = "2021"
log = "0.4" log = "0.4"
thiserror = "1.0" thiserror = "1.0"
femtos = "0.1" femtos = "0.1"
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" } moa-core = { path = "../../core" }
moa-signals = { path = "../../libraries/signals" } moa-signals = { path = "../../libraries/signals" }
emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal" }

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};
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,113 @@ 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<Z80Address>,
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<Z80Address>,
{
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<Z80Address>,
{
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<Z80Address>,
{
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, self.start + offset).unwrap()).unwrap()
}
ins_data
}
}
pub struct DecodeNext<'a, Bus, Instant>
where
Bus: BusAccess<Z80Address, Instant = Instant>,
{
clock: Instant,
bus: &'a mut Bus,
decoder: Z80Decoder,
}
impl<'a, Bus, Instant> DecodeNext<'a, Bus, Instant>
where
Bus: BusAccess<Z80Address, 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 +157,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 +177,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 +223,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 +247,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 +256,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 +277,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 +288,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 +305,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 +335,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 +398,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 +414,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 +430,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 +493,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 +511,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 +528,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 +556,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 +576,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, 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, self.decoder.end & 0xFFFF)
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,57 @@
use emulator_hal::{BusAccess, Instant as EmuInstant, Error as EmuError, Step, Inspect, Debug, IntoAddress};
use crate::state::{Z80, Z80Error, Z80Address, Status};
impl EmuError for Z80Error {}
impl<Instant, Bus> Step<Bus> for Z80<Instant>
where
Instant: EmuInstant,
Bus: BusAccess<Z80Address, Instant = Instant>,
{
type Instant = Instant;
type Error = Z80Error;
fn is_running(&mut self) -> bool {
self.state.status == Status::Running
}
fn reset(&mut self, _now: Self::Instant, _bus: &mut Bus) -> Result<(), Self::Error> {
self.clear_state();
Ok(())
}
fn step(&mut self, now: Self::Instant, bus: &mut Bus) -> Result<Self::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)
}
}
/*
impl<Instant, MemBus, IoBus> Step<(&mut MemBus, &mut IoBus)> for Z80<Instant>
where
Instant: EmuInstant,
MemBus: BusAccess<Z80Address, Instant = Instant>,
IoBus: BusAccess<Z80Address, Instant = Instant>,
{
type Instant = Instant;
type Error = Z80Error;
fn is_running(&mut self) -> bool {
self.state.status == Status::Running
}
fn reset(&mut self, _now: Self::Instant, _bus: (&mut MemBus, &mut IoBus)) -> Result<(), Self::Error> {
self.clear_state();
Ok(())
}
fn step(&mut self, now: Self::Instant, bus: (&mut MemBus, &mut IoBus)) -> Result<Self::Instant, Self::Error> {
let 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)
}
}
*/

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, 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<Z80Address, 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<Z80Address, 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<Z80Address, 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,8 +1011,8 @@ 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
} }
@ -1127,38 +1128,69 @@ 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)?) Ok(self
.bus
.read_u8(self.cycle.current_clock, 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)?) Ok(self
.bus
.write_u8(self.cycle.current_clock, 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> {
let mut bytes = [0; 2];
for byte in bytes.iter_mut() {
self.increment_refresh(1);
*byte = self
.bus
.read_u8(self.cycle.current_clock, addr & 0xFFFF)
.map_err(|err| Z80Error::BusError(format!("{:?}", err)))?;
addr = addr.wrapping_add(1);
}
Ok(u16::from_le_bytes(bytes))
} }
fn write_port_u16(&mut self, addr: u16, value: u16) -> Result<(), Z80Error> { /// Write a u16 value through this CPU's memory port
self.increment_refresh(2); ///
Ok(self.port.write_leu16(self.executor.current_clock, addr as Address, value)?) /// Since the memory port is only able to read 8 bits at a time, this does two writes
/// in little endian byte order
fn write_port_u16(&mut self, mut addr: u16, value: u16) -> Result<(), Z80Error> {
let mut bytes = value.to_le_bytes();
for byte in bytes.iter_mut() {
self.increment_refresh(1);
self.bus
.write_u8(self.cycle.current_clock, addr & 0xFFFF, *byte)
.map_err(|err| Z80Error::BusError(format!("{:?}", err)))?;
addr = addr.wrapping_add(1);
}
Ok(())
} }
fn read_ioport_value(&mut self, upper: u8, lower: u8) -> Result<u8, Z80Error> { fn read_ioport_value(&mut self, upper: u8, lower: u8) -> Result<u8, Z80Error> {
let addr = ((upper as Address) << 8) | (lower as Address); let addr = ((upper as Z80Address) << 8) | (lower as Z80Address);
if let Some(io) = self.ioport.as_mut() { // TODO restore this eventually
Ok(io.read_u8(self.executor.current_clock, addr)?) //if let Some(io) = self.ioport.as_mut() {
} else { // Ok(io.read_u8(self.cycle.current_clock, addr)?)
Ok(0) //} else {
} Ok(0)
//}
} }
fn write_ioport_value(&mut self, upper: u8, lower: u8, value: u8) -> Result<(), Z80Error> { fn write_ioport_value(&mut self, upper: u8, lower: u8, value: u8) -> Result<(), Z80Error> {
let addr = ((upper as Address) << 8) | (lower as Address); let addr = ((upper as Z80Address) << 8) | (lower as Z80Address);
if let Some(io) = self.ioport.as_mut() { // TODO restore this eventually
io.write_u8(self.executor.current_clock, addr, value)? //if let Some(io) = self.ioport.as_mut() {
} // io.write_u8(self.cycle.current_clock, addr, value)?
//}
Ok(()) Ok(())
} }
@ -1199,10 +1231,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 +1244,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); (&mut 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); (&mut 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); (&mut 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); (&mut 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,19 @@
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;
pub use crate::moa::MoaZ80;
pub use crate::state::{Z80, Z80Type, 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,
};

View File

@ -0,0 +1,138 @@
use std::rc::Rc;
use std::cell::RefCell;
use femtos::{Instant, Duration};
use emulator_hal::{BusAdapter, Instant as EmuInstant};
use moa_core::{System, Error, Bus, Address, Steppable, Addressable, Interruptable, Signalable, Signal, Debuggable, Transmutable};
use crate::{Z80, Z80Error, Z80Decoder};
use crate::instructions::Register;
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 mut bus = &mut *self.bus.borrow_mut();
let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err)));
let mut executor = self.cpu.begin(system.clock, &mut adapter)?;
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::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err)));
self.cpu.dump_state(system.clock, &mut adapter);
}
}
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::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::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err)));
let decoder = Z80Decoder::decode_at(&mut adapter, system.clock, self.cpu.state.pc)?;
self.cpu.previous_cycle.decoder.dump_decoded(&mut adapter);
self.cpu.dump_state(system.clock, &mut adapter);
Ok(())
}
fn print_disassembly(&mut self, system: &System, addr: Address, count: usize) {
let bus = &mut *system.bus.borrow_mut();
let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err)));
Z80Decoder::dump_disassembly(&mut adapter, 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,13 @@
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::{Instant as EmuInstant, BusAccess};
use moa_core::{Address, Bus, BusPort}; 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 +92,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")]
@ -104,62 +112,61 @@ pub enum Z80Error /* <B: fmt::Display> */ {
BusError(String /* B */), BusError(String /* B */),
} }
pub type Z80Address = u16;
pub type Z80IOAddress = u16;
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>, // TODO activate later
pub reset: Signal<bool>, //pub reset: Signal<bool>,
pub bus_request: 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),
reset: Signal::new(false), //bus_request: 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<Bus>(&mut self, clock: Instant, bus: &mut Bus)
where
Bus: BusAccess<Z80Address, Instant = Instant>,
{
println!("Status: {:?}", self.state.status); println!("Status: {:?}", self.state.status);
println!("PC: {:#06x}", self.state.pc); println!("PC: {:#06x}", self.state.pc);
println!("SP: {:#06x}", self.state.sp); println!("SP: {:#06x}", self.state.sp);
@ -200,11 +207,13 @@ impl Z80 {
println!( println!(
"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!("Previous Instruction: {:?}", self.previous_cycle.decoder.instruction);
println!(); println!();
self.port.dump_memory(clock, self.state.sp as Address, 0x40); // TODO disabled until function is reimplemented
//self.port.dump_memory(clock, self.state.sp as Address, 0x40);
println!(); println!();
} }
} }

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 c17e364ed472fff774b1b38323a2c72936c7224c

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

@ -2,13 +2,13 @@ use std::mem;
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use femtos::Frequency; use femtos::{Instant, Frequency};
use moa_core::{System, Error, MemoryBlock, Bus, Address, Addressable, Device}; 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

@ -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-04-07 at commit 1c5ad3999afa5591ec8fcbcadf4797514c390031 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!
@ -476,7 +476,7 @@ d7.json completed, all passed!
d8.json completed, all passed! d8.json completed, all passed!
d9.json completed, all passed! d9.json completed, all passed!
da.json completed, all passed! da.json completed, all passed!
db.json completed, all passed! db.json completed: 6 passed, 994 FAILED
dc.json completed, all passed! dc.json completed, all passed!
dd 00.json completed, all passed! dd 00.json completed, all passed!
dd 01.json completed, all passed! dd 01.json completed, all passed!
@ -954,7 +954,7 @@ dd d7.json completed, all passed!
dd d8.json completed, all passed! dd d8.json completed, all passed!
dd d9.json completed, all passed! dd d9.json completed, all passed!
dd da.json completed, all passed! dd da.json completed, all passed!
dd db.json completed, all passed! dd db.json completed: 0 passed, 1000 FAILED
dd dc.json completed, all passed! dd dc.json completed, all passed!
dd de.json completed, all passed! dd de.json completed, all passed!
dd df.json completed, all passed! dd df.json completed, all passed!
@ -1003,7 +1003,7 @@ e9.json completed, all passed!
ea.json completed, all passed! ea.json completed, all passed!
eb.json completed, all passed! eb.json completed, all passed!
ec.json completed, all passed! ec.json completed, all passed!
ed 40.json completed, all passed! ed 40.json completed: 6 passed, 994 FAILED
ed 41.json completed, all passed! ed 41.json completed, all passed!
ed 42.json completed, all passed! ed 42.json completed, all passed!
ed 43.json completed, all passed! ed 43.json completed, all passed!
@ -1011,7 +1011,7 @@ ed 44.json completed, all passed!
ed 45.json completed, all passed! ed 45.json completed, all passed!
ed 46.json completed, all passed! ed 46.json completed, all passed!
ed 47.json completed, all passed! ed 47.json completed, all passed!
ed 48.json completed, all passed! ed 48.json completed: 4 passed, 996 FAILED
ed 49.json completed, all passed! ed 49.json completed, all passed!
ed 4a.json completed, all passed! ed 4a.json completed, all passed!
ed 4b.json completed, all passed! ed 4b.json completed, all passed!
@ -1019,7 +1019,7 @@ ed 4c.json completed, all passed!
ed 4d.json completed, all passed! ed 4d.json completed, all passed!
ed 4e.json completed, all passed! ed 4e.json completed, all passed!
ed 4f.json completed, all passed! ed 4f.json completed, all passed!
ed 50.json completed, all passed! ed 50.json completed: 6 passed, 994 FAILED
ed 51.json completed, all passed! ed 51.json completed, all passed!
ed 52.json completed, all passed! ed 52.json completed, all passed!
ed 53.json completed, all passed! ed 53.json completed, all passed!
@ -1027,7 +1027,7 @@ ed 54.json completed, all passed!
ed 55.json completed, all passed! ed 55.json completed, all passed!
ed 56.json completed, all passed! ed 56.json completed, all passed!
ed 57.json completed, all passed! ed 57.json completed, all passed!
ed 58.json completed, all passed! ed 58.json completed: 6 passed, 994 FAILED
ed 59.json completed, all passed! ed 59.json completed, all passed!
ed 5a.json completed, all passed! ed 5a.json completed, all passed!
ed 5b.json completed, all passed! ed 5b.json completed, all passed!
@ -1035,7 +1035,7 @@ ed 5c.json completed, all passed!
ed 5d.json completed, all passed! ed 5d.json completed, all passed!
ed 5e.json completed, all passed! ed 5e.json completed, all passed!
ed 5f.json completed, all passed! ed 5f.json completed, all passed!
ed 60.json completed, all passed! ed 60.json completed: 2 passed, 998 FAILED
ed 61.json completed, all passed! ed 61.json completed, all passed!
ed 62.json completed, all passed! ed 62.json completed, all passed!
ed 63.json completed: 0 passed, 1000 FAILED ed 63.json completed: 0 passed, 1000 FAILED
@ -1043,7 +1043,7 @@ ed 64.json completed, all passed!
ed 65.json completed, all passed! ed 65.json completed, all passed!
ed 66.json completed, all passed! ed 66.json completed, all passed!
ed 67.json completed, all passed! ed 67.json completed, all passed!
ed 68.json completed, all passed! ed 68.json completed: 4 passed, 996 FAILED
ed 69.json completed, all passed! ed 69.json completed, all passed!
ed 6a.json completed, all passed! ed 6a.json completed, all passed!
ed 6b.json completed: 0 passed, 1000 FAILED ed 6b.json completed: 0 passed, 1000 FAILED
@ -1059,7 +1059,7 @@ ed 74.json completed, all passed!
ed 75.json completed, all passed! ed 75.json completed, all passed!
ed 76.json completed, all passed! ed 76.json completed, all passed!
ed 77.json completed: 0 passed, 1000 FAILED ed 77.json completed: 0 passed, 1000 FAILED
ed 78.json completed, all passed! ed 78.json completed: 7 passed, 993 FAILED
ed 79.json completed, all passed! ed 79.json completed, all passed!
ed 7a.json completed, all passed! ed 7a.json completed, all passed!
ed 7b.json completed, all passed! ed 7b.json completed, all passed!
@ -1069,7 +1069,7 @@ ed 7e.json completed, all passed!
ed 7f.json completed: 0 passed, 1000 FAILED ed 7f.json completed: 0 passed, 1000 FAILED
ed a0.json completed, all passed! ed a0.json completed, all passed!
ed a1.json completed: 0 passed, 1000 FAILED ed a1.json completed: 0 passed, 1000 FAILED
ed a2.json completed: 13 passed, 987 FAILED ed a2.json completed: 0 passed, 1000 FAILED
ed a3.json completed: 0 passed, 1000 FAILED ed a3.json completed: 0 passed, 1000 FAILED
ed a8.json completed, all passed! ed a8.json completed, all passed!
ed a9.json completed: 0 passed, 1000 FAILED ed a9.json completed: 0 passed, 1000 FAILED
@ -1574,7 +1574,7 @@ fd d7.json completed, all passed!
fd d8.json completed, all passed! fd d8.json completed, all passed!
fd d9.json completed, all passed! fd d9.json completed, all passed!
fd da.json completed, all passed! fd da.json completed, all passed!
fd db.json completed, all passed! fd db.json completed: 4 passed, 996 FAILED
fd dc.json completed, all passed! fd dc.json completed, all passed!
fd de.json completed, all passed! fd de.json completed, all passed!
fd df.json completed, all passed! fd df.json completed, all passed!
@ -1611,5 +1611,5 @@ fd ff.json completed, all passed!
fe.json completed, all passed! fe.json completed, all passed!
ff.json completed, all passed! ff.json completed, all passed!
passed: 1574638, failed: 35362, total 98% passed: 1564670, failed: 45330, total 97%
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,16 +9,21 @@ 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, 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 {
/// Filter the tests by gzip file name /// Filter the tests by gzip file name
@ -43,6 +46,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,
@ -145,27 +151,33 @@ impl TestCase {
} }
fn init_execute_test(cputype: Z80Type, state: &TestState, ports: &[TestPort]) -> Result<(Z80, System, Rc<RefCell<Bus>>), Error> { fn init_execute_test(
let mut system = System::default(); cputype: Z80Type,
state: &TestState,
ports: &[TestPort],
) -> Result<(Z80<Instant>, MemoryBlock<Instant>, MemoryBlock<Instant>), Error> {
// 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 +187,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 +227,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 +244,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 +282,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 +309,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 clock_elapsed = cpu.step((memory, io))?;
let clock_elapsed = cpu
.step(Instant::START, memory)
.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 +336,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 +348,8 @@ 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); initial_cpu.dump_state(Instant::START, &mut memory);
cpu.dump_state(system.clock); cpu.dump_state(Instant::START, &mut memory);
} }
println!("FAILED: {:?}", err); println!("FAILED: {:?}", err);
} }
@ -361,6 +381,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);

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?