mirror of
https://github.com/transistorfet/moa.git
synced 2024-11-26 22:50:25 +00:00
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:
parent
6e7e315808
commit
342bb8aa3d
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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"] }
|
||||||
|
@ -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>>>;
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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::{
|
||||||
|
@ -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>;
|
||||||
|
|
||||||
|
@ -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::*;
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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"]
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
174
emulator/cpus/z80/src/emuhal.rs
Normal file
174
emulator/cpus/z80/src/emuhal.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
152
emulator/cpus/z80/src/moa.rs
Normal file
152
emulator/cpus/z80/src/moa.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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]
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
@ -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"
|
||||||
|
@ -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> {
|
||||||
|
@ -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"] }
|
||||||
|
|
||||||
|
@ -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)?;
|
||||||
|
@ -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))?;
|
||||||
|
|
||||||
|
@ -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> {
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
|
18
todo.txt
18
todo.txt
@ -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?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user