diff --git a/Cargo.lock b/Cargo.lock index c8201a5..be66a2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1247,9 +1247,10 @@ name = "rad-tests" version = "0.1.0" dependencies = [ "clap 3.2.25", + "emulator-hal", + "emulator-hal-memory", "femtos", "flate2", - "moa-core", "moa-z80", "serde", "serde_derive", diff --git a/README.md b/README.md index 3e2bfbe..8fb516c 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,10 @@ For more about the Sega Genesis support, check out this series I wrote about imp I've also generated rustdocs of the workspace. All the various crates within moa are listed in the crates section in the sidebar on the left. There's not a lot of doc comments in the code yet but I plan to eventually write more: -[rustdocs for moa_core](http://jabberwocky.ca/moa/doc/moa_core/) -[rustdocs for ym2612](http://jabberwocky.ca/moa/doc/moa_peripherals_yamaha/ym2612/index.html) +[moa_core](http://jabberwocky.ca/moa/doc/moa_core/) +[moa_m68k](http://jabberwocky.ca/moa/doc/moa_m68k/) +[moa_z80](http://jabberwocky.ca/moa/doc/moa_z80/) +[ym2612](http://jabberwocky.ca/moa/doc/moa_peripherals_yamaha/ym2612/index.html) This repository uses submodules, so make sure to clone with ```sh diff --git a/emulator/core/Cargo.toml b/emulator/core/Cargo.toml index ec434d7..fa3cda6 100644 --- a/emulator/core/Cargo.toml +++ b/emulator/core/Cargo.toml @@ -8,4 +8,4 @@ log = "0.4" femtos = "0.1" thiserror = "1.0" moa-host = { path = "../libraries/host" } -emulator-hal = { path = "../libraries/emulator-hal/emulator-hal" } +emulator-hal = { path = "../libraries/emulator-hal/emulator-hal", features = ["femtos"] } diff --git a/emulator/core/src/devices.rs b/emulator/core/src/devices.rs index 6ffae86..3f46c4c 100644 --- a/emulator/core/src/devices.rs +++ b/emulator/core/src/devices.rs @@ -171,6 +171,16 @@ pub trait Inspectable { fn inspect(&mut self, system: &System, args: &[&str]) -> Result<(), Error>; } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Signal { + Reset, + BusRequest, +} + +pub trait Signalable { + fn set_signal(&mut self, signal: Signal, flag: bool) -> Result<(), Error>; + fn signal(&mut self, signal: Signal) -> Option; +} pub trait Transmutable { #[inline] @@ -197,6 +207,11 @@ pub trait Transmutable { fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> { None } + + #[inline] + fn as_signalable(&mut self) -> Option<&mut dyn Signalable> { + None + } } pub type TransmutableBox = Rc>>; diff --git a/emulator/core/src/error.rs b/emulator/core/src/error.rs index ff1ea22..6cdc985 100644 --- a/emulator/core/src/error.rs +++ b/emulator/core/src/error.rs @@ -71,3 +71,9 @@ impl From> for Error { Self::Other("other".to_string()) } } + +impl From for Error { + fn from(err: fmt::Error) -> Self { + Self::Other(format!("{:?}", err)) + } +} diff --git a/emulator/core/src/lib.rs b/emulator/core/src/lib.rs index c0c332f..a13dc08 100644 --- a/emulator/core/src/lib.rs +++ b/emulator/core/src/lib.rs @@ -7,7 +7,8 @@ mod memory; mod system; pub use crate::devices::{ - Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Device, + Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Signalable, Signal, Transmutable, TransmutableBox, + Device, }; pub use crate::devices::{ read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable, @@ -17,4 +18,4 @@ pub use crate::interrupts::InterruptController; pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice, dump_memory}; pub use crate::system::System; -pub use emulator_hal::bus::{BusAccess}; +pub use emulator_hal::BusAccess; diff --git a/emulator/core/src/memory.rs b/emulator/core/src/memory.rs index b5e545b..fd5d38d 100644 --- a/emulator/core/src/memory.rs +++ b/emulator/core/src/memory.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use std::cell::RefCell; use std::fmt::Write; use femtos::Instant; +use emulator_hal::{self, BusAccess, Error as EmuError}; use crate::error::Error; use crate::devices::{Address, Addressable, Transmutable, Device, read_beu16}; @@ -236,7 +237,7 @@ impl Bus { let to = if count < 16 { count / 2 } else { 8 }; for _ in 0..to { - let word = self.read_beu16(clock, addr); + let word = Addressable::read_beu16(self, clock, addr); if word.is_err() { println!("{}", line); return; @@ -353,7 +354,7 @@ impl Addressable for BusPort { for i in (0..data.len()).step_by(self.data_width as usize) { let addr_index = (addr + i as Address) & self.address_mask; let end = cmp::min(i + self.data_width as usize, data.len()); - subdevice.read(clock, addr_index, &mut data[i..end])?; + Addressable::read(&mut *subdevice, clock, addr_index, &mut data[i..end])?; } Ok(()) } @@ -364,7 +365,7 @@ impl Addressable for BusPort { for i in (0..data.len()).step_by(self.data_width as usize) { let addr_index = (addr + i as Address) & self.address_mask; let end = cmp::min(i + self.data_width as usize, data.len()); - subdevice.write(clock, addr_index, &data[i..end])?; + Addressable::write(&mut *subdevice, clock, addr_index, &data[i..end])?; } Ok(()) } @@ -412,9 +413,7 @@ where } } -use emulator_hal::bus::{self, BusAccess}; - -impl bus::Error for Error {} +impl EmuError for Error {} impl BusAccess for &mut dyn Addressable { type Instant = Instant; @@ -430,3 +429,18 @@ impl BusAccess for &mut dyn Addressable { Ok(data.len()) } } + +impl BusAccess for Bus { + type Instant = Instant; + type Error = Error; + + fn read(&mut self, now: Instant, addr: Address, data: &mut [u8]) -> Result { + Addressable::read(self, now, addr, data)?; + Ok(data.len()) + } + + fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result { + Addressable::write(self, now, addr, data)?; + Ok(data.len()) + } +} diff --git a/emulator/cpus/m68k/src/debugger.rs b/emulator/cpus/m68k/src/debugger.rs index d438944..0606541 100644 --- a/emulator/cpus/m68k/src/debugger.rs +++ b/emulator/cpus/m68k/src/debugger.rs @@ -2,9 +2,7 @@ use core::fmt; -use emulator_hal::time; -use emulator_hal::bus::{self, BusAccess}; -use emulator_hal::step::{Inspect, Debug}; +use emulator_hal::{Instant as BusInstant, Error as ErrorType, BusAccess, Inspect, Debug}; use crate::{M68k, M68kError, M68kAddress, M68kCycleExecutor}; @@ -28,10 +26,10 @@ pub enum M68kInfo { State, } -impl Inspect for M68k +impl Inspect for M68k where Bus: BusAccess, - BusError: bus::Error, + BusError: ErrorType, Writer: fmt::Write, { type InfoType = M68kInfo; @@ -60,8 +58,8 @@ where impl Debug for M68k where Bus: BusAccess, - BusError: bus::Error, - Instant: time::Instant, + BusError: ErrorType, + Instant: BusInstant, Writer: fmt::Write, { // TODO this should be a new type diff --git a/emulator/cpus/m68k/src/decode.rs b/emulator/cpus/m68k/src/decode.rs index fd2c891..c419d01 100644 --- a/emulator/cpus/m68k/src/decode.rs +++ b/emulator/cpus/m68k/src/decode.rs @@ -1,7 +1,7 @@ // Instruction Decoding use core::marker::PhantomData; -use emulator_hal::bus::BusAccess; +use emulator_hal::{Instant as BusInstant, Error as BusError, BusAccess, Step}; use crate::{M68kType, M68kError, M68kBusPort, M68kAddress, Exceptions}; use crate::instructions::{ diff --git a/emulator/cpus/m68k/src/execute.rs b/emulator/cpus/m68k/src/execute.rs index e2fadb7..be59b48 100644 --- a/emulator/cpus/m68k/src/execute.rs +++ b/emulator/cpus/m68k/src/execute.rs @@ -1,8 +1,6 @@ // Instruction Execution -use emulator_hal::time; -use emulator_hal::step::Step; -use emulator_hal::bus::{self, BusAccess}; +use emulator_hal::{Instant as BusInstant, Error, BusAccess, Step}; use crate::{M68k, M68kType, M68kError, M68kState}; use crate::state::{Status, Flags, Exceptions, InterruptPriority}; @@ -35,7 +33,7 @@ pub struct M68kCycle { impl M68kCycle where - Instant: time::Instant, + Instant: BusInstant, { #[inline] pub fn default(cputype: M68kType, data_width: u8) -> Self { @@ -74,12 +72,13 @@ where } } -impl Step for M68k +impl Step for M68k where Bus: BusAccess, - BusError: bus::Error, - Instant: time::Instant, + BusError: Error, + Instant: BusInstant, { + type Instant = Instant; type Error = M68kError; fn is_running(&mut self) -> bool { diff --git a/emulator/cpus/m68k/src/memory.rs b/emulator/cpus/m68k/src/memory.rs index f95f194..429ba2f 100644 --- a/emulator/cpus/m68k/src/memory.rs +++ b/emulator/cpus/m68k/src/memory.rs @@ -1,7 +1,6 @@ use core::cmp; use core::fmt::Write; -use emulator_hal::time; -use emulator_hal::bus::BusAccess; +use emulator_hal::{Instant as BusInstant, BusAccess}; use crate::{M68kError, CpuInfo}; use crate::state::Exceptions; @@ -65,7 +64,7 @@ impl FunctionCode { impl Default for MemoryRequest where - Instant: time::Instant, + Instant: BusInstant, { fn default() -> Self { Self { @@ -138,7 +137,7 @@ pub struct M68kBusPort { impl Default for M68kBusPort where - Instant: time::Instant, + Instant: BusInstant, { fn default() -> Self { Self { diff --git a/emulator/cpus/m68k/src/moa.rs b/emulator/cpus/m68k/src/moa.rs index 7ab9703..e65d939 100644 --- a/emulator/cpus/m68k/src/moa.rs +++ b/emulator/cpus/m68k/src/moa.rs @@ -1,5 +1,5 @@ use femtos::{Instant, Duration}; -use emulator_hal::bus; +use emulator_hal::{Error as ErrorType, BusAdapter}; use moa_core::{System, Error, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable}; @@ -10,8 +10,8 @@ impl Steppable for M68k { let cycle = M68kCycle::new(self, system.clock); let mut bus = system.bus.borrow_mut(); - let mut adapter: bus::BusAdapter = - bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err); + let mut adapter: BusAdapter = + BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err); let mut executor = cycle.begin(self, &mut adapter); executor.check_breakpoints()?; @@ -60,7 +60,7 @@ impl From for M68kError { } } -impl From> for Error { +impl From> for Error { fn from(err: M68kError) -> Self { match err { M68kError::Halted => Self::Other("cpu halted".to_string()), @@ -86,11 +86,18 @@ impl Debuggable for M68k { } } - fn print_current_step(&mut self, _system: &System) -> Result<(), Error> { + fn print_current_step(&mut self, system: &System) -> Result<(), Error> { + let mut bus = system.bus.borrow_mut(); + let mut adapter: BusAdapter = + BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err); + // TODO this is called by the debugger, but should be called some other way - //let _ = self.decoder.decode_at(&mut self.bus, true, self.state.pc); - //self.decoder.dump_decoded(&mut self.bus); - //self.dump_state(); + let mut decoder = M68kDecoder::new(self.info.chip, true, self.state.pc); + decoder.decode_at(&mut adapter, &mut M68kBusPort::default(), true, self.state.pc)?; + decoder.dump_decoded(system.clock, &mut adapter); + let mut writer = String::new(); + self.dump_state(&mut writer)?; + println!("{}", writer); Ok(()) } @@ -99,8 +106,8 @@ impl Debuggable for M68k { let mut memory = M68kBusPort::from_info(&self.info, system.clock); let mut bus = system.bus.borrow_mut(); - let mut adapter: bus::BusAdapter = - bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err); + let mut adapter: BusAdapter = + BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err); decoder.dump_disassembly(&mut adapter, &mut memory, addr as u32, count as u32); } diff --git a/emulator/cpus/m68k/src/state.rs b/emulator/cpus/m68k/src/state.rs index 4dbe34a..1dfdac5 100644 --- a/emulator/cpus/m68k/src/state.rs +++ b/emulator/cpus/m68k/src/state.rs @@ -2,7 +2,7 @@ use femtos::Frequency; use core::fmt::{self, Write}; -use emulator_hal::time; +use emulator_hal::Instant as BusInstant; use crate::{M68kDebugger, M68kCycle}; use crate::instructions::Target; @@ -243,7 +243,7 @@ impl M68kState { impl M68k where - Instant: time::Instant, + Instant: BusInstant, { pub fn new(info: CpuInfo) -> Self { M68k { diff --git a/emulator/cpus/m68k/tests/decode_tests.rs b/emulator/cpus/m68k/tests/decode_tests.rs index b0843b9..06212a4 100644 --- a/emulator/cpus/m68k/tests/decode_tests.rs +++ b/emulator/cpus/m68k/tests/decode_tests.rs @@ -1,5 +1,5 @@ use femtos::{Instant, Frequency}; -use emulator_hal::bus::BusAccess; +use emulator_hal::BusAccess; use emulator_hal_memory::MemoryBlock; use moa_m68k::{M68k, M68kType, M68kAddress}; diff --git a/emulator/cpus/m68k/tests/execute_tests.rs b/emulator/cpus/m68k/tests/execute_tests.rs index a26e221..c96e00e 100644 --- a/emulator/cpus/m68k/tests/execute_tests.rs +++ b/emulator/cpus/m68k/tests/execute_tests.rs @@ -1,6 +1,5 @@ use femtos::{Instant, Frequency}; -use emulator_hal::bus::BusAccess; -use emulator_hal::step::Step; +use emulator_hal::{BusAccess, Step}; use emulator_hal_memory::MemoryBlock; use moa_m68k::{M68k, M68kType, M68kAddress}; diff --git a/emulator/cpus/m68k/tests/musashi_timing_tests.rs b/emulator/cpus/m68k/tests/musashi_timing_tests.rs index 696915f..125cb90 100644 --- a/emulator/cpus/m68k/tests/musashi_timing_tests.rs +++ b/emulator/cpus/m68k/tests/musashi_timing_tests.rs @@ -1,5 +1,5 @@ use femtos::{Instant, Frequency}; -use emulator_hal::bus::BusAccess; +use emulator_hal::BusAccess; use emulator_hal_memory::MemoryBlock; use moa_m68k::{M68k, M68kType, M68kAddress}; diff --git a/emulator/cpus/m68k/tests/timing_tests.rs b/emulator/cpus/m68k/tests/timing_tests.rs index 8e00d8a..2537d82 100644 --- a/emulator/cpus/m68k/tests/timing_tests.rs +++ b/emulator/cpus/m68k/tests/timing_tests.rs @@ -1,5 +1,5 @@ use femtos::{Instant, Frequency}; -use emulator_hal::bus::BusAccess; +use emulator_hal::BusAccess; use emulator_hal_memory::MemoryBlock; use moa_m68k::{M68k, M68kType, M68kAddress}; diff --git a/emulator/cpus/z80/Cargo.toml b/emulator/cpus/z80/Cargo.toml index 29a194d..faf5588 100644 --- a/emulator/cpus/z80/Cargo.toml +++ b/emulator/cpus/z80/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" log = "0.4" thiserror = "1.0" femtos = "0.1" +emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal", features = ["femtos"] } + +# TODO the goal is to make these optional, or remove them entirely moa-core = { path = "../../core" } moa-signals = { path = "../../libraries/signals" } -emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal" } diff --git a/emulator/cpus/z80/src/debugger.rs b/emulator/cpus/z80/src/debugger.rs index c05e70a..f21f245 100644 --- a/emulator/cpus/z80/src/debugger.rs +++ b/emulator/cpus/z80/src/debugger.rs @@ -1,9 +1,4 @@ -use moa_core::{System, Error, Address, Debuggable}; - -use crate::state::{Z80, Z80Error}; -use crate::decode::Z80Decoder; -use crate::instructions::Register; - +use crate::state::{Z80Error, Z80Address}; #[derive(Clone, Default)] pub struct Z80Debugger { @@ -11,49 +6,15 @@ pub struct Z80Debugger { pub(crate) breakpoints: Vec, } -impl Debuggable for Z80 { - fn add_breakpoint(&mut self, addr: Address) { - self.debugger.breakpoints.push(addr as u16); - } - - fn remove_breakpoint(&mut self, addr: Address) { - if let Some(index) = self.debugger.breakpoints.iter().position(|a| *a == addr as u16) { - self.debugger.breakpoints.remove(index); - } - } - - fn print_current_step(&mut self, system: &System) -> Result<(), Error> { - self.decoder.decode_at(&mut self.port, system.clock, self.state.pc)?; - self.decoder.dump_decoded(&mut self.port); - self.dump_state(system.clock); - Ok(()) - } - - fn print_disassembly(&mut self, _system: &System, addr: Address, count: usize) { - let mut decoder = Z80Decoder::default(); - decoder.dump_disassembly(&mut self.port, addr as u16, count as u16); - } - - fn run_command(&mut self, _system: &System, args: &[&str]) -> Result { - match args[0] { - "l" => self.state.reg[Register::L as usize] = 0x05, - _ => { - return Ok(true); - }, - } - Ok(false) - } -} - -impl Z80 { - pub fn check_breakpoints(&mut self) -> Result<(), Z80Error> { - for breakpoint in &self.debugger.breakpoints { - if *breakpoint == self.state.pc { - if self.debugger.skip_breakpoint > 0 { - self.debugger.skip_breakpoint -= 1; +impl Z80Debugger { + pub fn check_breakpoints(&mut self, pc: Z80Address) -> Result<(), Z80Error> { + for breakpoint in &self.breakpoints { + if *breakpoint == pc { + if self.skip_breakpoint > 0 { + self.skip_breakpoint -= 1; return Ok(()); } else { - self.debugger.skip_breakpoint = 1; + self.skip_breakpoint = 1; return Err(Z80Error::Breakpoint); } } diff --git a/emulator/cpus/z80/src/decode.rs b/emulator/cpus/z80/src/decode.rs index 2e47774..330ed77 100644 --- a/emulator/cpus/z80/src/decode.rs +++ b/emulator/cpus/z80/src/decode.rs @@ -1,9 +1,7 @@ use core::fmt::Write; -use femtos::Instant; +use emulator_hal::{BusAccess, Instant as EmuInstant}; -use moa_core::{Address, Addressable}; - -use crate::state::Z80Error; +use crate::state::{Z80Error, Z80Address}; use crate::instructions::{ Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, LoadTarget, UndocumentedCopy, Instruction, @@ -15,9 +13,8 @@ use crate::instructions::{ #[derive(Clone)] pub struct Z80Decoder { - pub clock: Instant, - pub start: u16, - pub end: u16, + pub start: Z80Address, + pub end: Z80Address, pub extra_instruction_bytes: u16, pub instruction: Instruction, } @@ -25,7 +22,6 @@ pub struct Z80Decoder { impl Default for Z80Decoder { fn default() -> Self { Self { - clock: Instant::START, start: 0, end: 0, extra_instruction_bytes: 0, @@ -34,59 +30,113 @@ impl Default for Z80Decoder { } } -/* - fn read_test(&mut self, device: &mut B) -> Result - where - B: BusAccess, - { - device.read_u8(self.clock, (false, self.end as u16)) - .map_err(|err| Z80Error::BusError(format!("butts"))) +impl Z80Decoder { + fn new(start: Z80Address) -> Self { + Self { + start, + end: start, + extra_instruction_bytes: 0, + instruction: Instruction::NOP, + } } -*/ +} impl Z80Decoder { - pub fn decode_at(&mut self, memory: &mut dyn Addressable, clock: Instant, start: u16) -> Result<(), Z80Error> { - self.clock = clock; - self.start = start; - self.end = start; - self.extra_instruction_bytes = 0; - self.instruction = self.decode_one(memory)?; + pub fn decode_at(bus: &mut Bus, clock: Bus::Instant, start: Z80Address) -> Result + where + Bus: BusAccess, + { + let mut decoder: DecodeNext<'_, Bus, Bus::Instant> = DecodeNext { + clock, + bus, + decoder: Z80Decoder::new(start), + }; + decoder.decode_one()?; + Ok(decoder.decoder) + } + + pub fn dump_disassembly(bus: &mut Bus, start: Z80Address, length: Z80Address) + where + Bus: BusAccess, + { + let mut next = start; + while next < (start + length) { + match Z80Decoder::decode_at(bus, Bus::Instant::START, next) { + Ok(mut decoder) => { + decoder.dump_decoded(bus); + next = decoder.end; + }, + Err(err) => { + println!("{:?}", err); + return; + }, + } + } + } + + pub fn dump_decoded(&mut self, bus: &mut Bus) + where + Bus: BusAccess, + { + let ins_data = self.format_instruction_bytes(bus); + println!("{:#06x}: {}\n\t{:?}\n", self.start, ins_data, self.instruction); + } + + pub fn format_instruction_bytes(&mut self, bus: &mut Bus) -> String + where + Bus: BusAccess, + { + let mut ins_data = String::new(); + for offset in 0..self.end.saturating_sub(self.start) { + write!(ins_data, "{:02x} ", bus.read_u8(Bus::Instant::START, self.start + offset).unwrap()).unwrap() + } + ins_data + } +} + +pub struct DecodeNext<'a, Bus, Instant> +where + Bus: BusAccess, +{ + clock: Instant, + bus: &'a mut Bus, + decoder: Z80Decoder, +} + +impl<'a, Bus, Instant> DecodeNext<'a, Bus, Instant> +where + Bus: BusAccess, + Instant: EmuInstant, +{ + pub fn decode_one(&mut self) -> Result<(), Z80Error> { + let ins = self.read_instruction_byte()?; + self.decoder.instruction = self.decode_bare(ins, 0)?; Ok(()) } - pub fn decode_one(&mut self, memory: &mut dyn Addressable) -> Result { - let ins = self.read_instruction_byte(memory)?; - self.decode_bare(memory, ins, 0) - } - - pub fn decode_bare( - &mut self, - memory: &mut dyn Addressable, - ins: u8, - extra_instruction_bytes: u16, - ) -> Result { - self.extra_instruction_bytes = extra_instruction_bytes; + pub fn decode_bare(&mut self, ins: u8, extra_instruction_bytes: u16) -> Result { + self.decoder.extra_instruction_bytes = extra_instruction_bytes; match get_ins_x(ins) { 0 => match get_ins_z(ins) { 0 => match get_ins_y(ins) { 0 => Ok(Instruction::NOP), 1 => Ok(Instruction::EXafaf), 2 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::DJNZ(offset)) }, 3 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::JR(offset)) }, y => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::JRcc(get_condition(y - 4), offset)) }, }, 1 => { if get_ins_q(ins) == 0 { - let data = self.read_instruction_word(memory)?; + let data = self.read_instruction_word()?; Ok(Instruction::LD( LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))), LoadTarget::ImmediateWord(data), @@ -107,7 +157,7 @@ impl Z80Decoder { true => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), target)), } } else { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; match (ins >> 3) & 0x03 { 0 => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(RegisterPair::HL))), 1 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(addr))), @@ -127,7 +177,7 @@ impl Z80Decoder { 4 => Ok(Instruction::INC8(get_register(get_ins_y(ins)))), 5 => Ok(Instruction::DEC8(get_register(get_ins_y(ins)))), 6 => { - let data = self.read_instruction_byte(memory)?; + let data = self.read_instruction_byte()?; Ok(Instruction::LD(to_load_target(get_register(get_ins_y(ins))), LoadTarget::ImmediateByte(data))) }, 7 => match get_ins_y(ins) { @@ -173,21 +223,21 @@ impl Z80Decoder { } }, 2 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; Ok(Instruction::JPcc(get_condition(get_ins_y(ins)), addr)) }, 3 => match get_ins_y(ins) { 0 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; Ok(Instruction::JP(addr)) }, - 1 => self.decode_prefix_cb(memory), + 1 => self.decode_prefix_cb(), 2 => { - let port = self.read_instruction_byte(memory)?; + let port = self.read_instruction_byte()?; Ok(Instruction::OUTx(port)) }, 3 => { - let port = self.read_instruction_byte(memory)?; + let port = self.read_instruction_byte()?; Ok(Instruction::INx(port)) }, 4 => Ok(Instruction::EXsp(RegisterPair::HL)), @@ -197,7 +247,7 @@ impl Z80Decoder { _ => panic!("InternalError: impossible value"), }, 4 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; Ok(Instruction::CALLcc(get_condition(get_ins_y(ins)), addr)) }, 5 => { @@ -206,18 +256,18 @@ impl Z80Decoder { } else { match get_ins_p(ins) { 0 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; Ok(Instruction::CALL(addr)) }, - 1 => self.decode_prefix_dd_fd(memory, IndexRegister::IX), - 2 => self.decode_prefix_ed(memory), - 3 => self.decode_prefix_dd_fd(memory, IndexRegister::IY), + 1 => self.decode_prefix_dd_fd(IndexRegister::IX), + 2 => self.decode_prefix_ed(), + 3 => self.decode_prefix_dd_fd(IndexRegister::IY), _ => panic!("InternalError: impossible value"), } } }, 6 => { - let data = self.read_instruction_byte(memory)?; + let data = self.read_instruction_byte()?; Ok(get_alu_instruction(get_ins_y(ins), Target::Immediate(data))) }, 7 => Ok(Instruction::RST(get_ins_y(ins) * 8)), @@ -227,8 +277,8 @@ impl Z80Decoder { } } - pub fn decode_prefix_cb(&mut self, memory: &mut dyn Addressable) -> Result { - let ins = self.read_instruction_byte(memory)?; + pub fn decode_prefix_cb(&mut self) -> Result { + let ins = self.read_instruction_byte()?; match get_ins_x(ins) { 0 => Ok(get_rot_instruction(get_ins_y(ins), get_register(get_ins_z(ins)), None)), 1 => Ok(Instruction::BIT(get_ins_y(ins), get_register(get_ins_z(ins)))), @@ -238,9 +288,9 @@ impl Z80Decoder { } } - pub fn decode_sub_prefix_cb(&mut self, memory: &mut dyn Addressable, reg: IndexRegister) -> Result { - let offset = self.read_instruction_byte(memory)? as i8; - let ins = self.read_instruction_byte(memory)?; + pub fn decode_sub_prefix_cb(&mut self, reg: IndexRegister) -> Result { + let offset = self.read_instruction_byte()? as i8; + let ins = self.read_instruction_byte()?; let opt_copy = match get_ins_z(ins) { 6 => None, //Some(Target::DirectReg(Register::F)), z => Some(get_register(z)), @@ -255,8 +305,8 @@ impl Z80Decoder { } } - pub fn decode_prefix_ed(&mut self, memory: &mut dyn Addressable) -> Result { - let ins = self.read_instruction_byte(memory)?; + pub fn decode_prefix_ed(&mut self) -> Result { + let ins = self.read_instruction_byte()?; match get_ins_x(ins) { 0 => Ok(Instruction::NOP), @@ -285,7 +335,7 @@ impl Z80Decoder { } }, 3 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; if get_ins_q(ins) == 0 { Ok(Instruction::LD( LoadTarget::IndirectWord(addr), @@ -348,11 +398,11 @@ impl Z80Decoder { } } - pub fn decode_prefix_dd_fd(&mut self, memory: &mut dyn Addressable, index_reg: IndexRegister) -> Result { - let ins = self.read_instruction_byte(memory)?; + pub fn decode_prefix_dd_fd(&mut self, index_reg: IndexRegister) -> Result { + let ins = self.read_instruction_byte()?; if ins == 0xCB { - return self.decode_sub_prefix_cb(memory, index_reg); + return self.decode_sub_prefix_cb(index_reg); } match get_ins_x(ins) { @@ -364,11 +414,11 @@ impl Z80Decoder { match get_ins_p(ins) { 2 => match get_ins_z(ins) { 1 => { - let data = self.read_instruction_word(memory)?; + let data = self.read_instruction_word()?; Ok(Instruction::LD(LoadTarget::DirectRegWord(index_reg.into()), LoadTarget::ImmediateWord(data))) }, 2 => { - let addr = self.read_instruction_word(memory)?; + let addr = self.read_instruction_word()?; let regpair = index_reg.into(); match get_ins_q(ins) != 0 { false => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(regpair))), @@ -380,50 +430,50 @@ impl Z80Decoder { true => Ok(Instruction::DEC16(index_reg.into())), }, 4 => { - self.extra_instruction_bytes = 4; + self.decoder.extra_instruction_bytes = 4; let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins))); Ok(Instruction::INC8(half_target)) }, 5 => { - self.extra_instruction_bytes = 4; + self.decoder.extra_instruction_bytes = 4; let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins))); Ok(Instruction::DEC8(half_target)) }, 6 => { - self.extra_instruction_bytes = 4; + self.decoder.extra_instruction_bytes = 4; let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins))); - let data = self.read_instruction_byte(memory)?; + let data = self.read_instruction_byte()?; Ok(Instruction::LD(to_load_target(half_target), LoadTarget::ImmediateByte(data))) }, - _ => self.decode_bare(memory, ins, 4), + _ => self.decode_bare(ins, 4), }, 3 => match ins { 0x34 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::INC8(Target::IndirectOffset(index_reg, offset))) }, 0x35 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::DEC8(Target::IndirectOffset(index_reg, offset))) }, 0x36 => { - let offset = self.read_instruction_byte(memory)? as i8; - let immediate = self.read_instruction_byte(memory)?; + let offset = self.read_instruction_byte()? as i8; + let immediate = self.read_instruction_byte()?; Ok(Instruction::LD( LoadTarget::IndirectOffsetByte(index_reg, offset), LoadTarget::ImmediateByte(immediate), )) }, - _ => self.decode_bare(memory, ins, 4), + _ => self.decode_bare(ins, 4), }, - _ => self.decode_bare(memory, ins, 4), + _ => self.decode_bare(ins, 4), } }, 1 => match get_ins_p(ins) { 0 | 1 => { - let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? { + let target = match self.decode_index_target(index_reg, get_ins_z(ins))? { Some(target) => target, - None => return self.decode_bare(memory, ins, 4), + None => return self.decode_bare(ins, 4), }; match (ins & 0x18) >> 3 { @@ -443,7 +493,7 @@ impl Z80Decoder { 4 => Target::DirectRegHalf(get_index_register_half(index_reg, 0)), 5 => Target::DirectRegHalf(get_index_register_half(index_reg, 1)), 6 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; let src = to_load_target(Target::IndirectOffset(index_reg, offset)); if get_ins_q(ins) == 0 { return Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::H), src)); @@ -461,15 +511,15 @@ impl Z80Decoder { 3 => { if get_ins_q(ins) == 0 { if get_ins_z(ins) == 6 { - return self.decode_bare(memory, ins, 4); + return self.decode_bare(ins, 4); } let src = get_register(get_ins_z(ins)); - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Ok(Instruction::LD(LoadTarget::IndirectOffsetByte(index_reg, offset), to_load_target(src))) } else { - let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? { + let target = match self.decode_index_target(index_reg, get_ins_z(ins))? { Some(target) => target, - None => return self.decode_bare(memory, ins, 4), + None => return self.decode_bare(ins, 4), }; Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), to_load_target(target))) @@ -478,11 +528,11 @@ impl Z80Decoder { _ => panic!("InternalError: impossible value"), }, 2 => { - self.extra_instruction_bytes = 4; + self.decoder.extra_instruction_bytes = 4; - let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? { + let target = match self.decode_index_target(index_reg, get_ins_z(ins))? { Some(target) => target, - None => return self.decode_bare(memory, ins, 4), + None => return self.decode_bare(ins, 4), }; match get_ins_y(ins) { @@ -506,23 +556,18 @@ impl Z80Decoder { LoadTarget::DirectRegWord(RegisterPair::SP), LoadTarget::DirectRegWord(index_reg.into()), )), - _ => self.decode_bare(memory, ins, 4), + _ => self.decode_bare(ins, 4), }, _ => panic!("InternalError: impossible value"), } } - fn decode_index_target( - &mut self, - memory: &mut dyn Addressable, - index_reg: IndexRegister, - z: u8, - ) -> Result, Z80Error> { + fn decode_index_target(&mut self, index_reg: IndexRegister, z: u8) -> Result, Z80Error> { let result = match z { 4 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 0))), 5 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 1))), 6 => { - let offset = self.read_instruction_byte(memory)? as i8; + let offset = self.read_instruction_byte()? as i8; Some(Target::IndirectOffset(index_reg, offset)) }, _ => None, @@ -531,45 +576,25 @@ impl Z80Decoder { } - fn read_instruction_byte(&mut self, device: &mut dyn Addressable) -> Result { - let byte = device.read_u8(self.clock, self.end as Address)?; - self.end = self.end.wrapping_add(1); + fn read_instruction_byte(&mut self) -> Result { + let byte = self + .bus + .read_u8(self.clock, self.decoder.end) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; + self.decoder.end = self.decoder.end.wrapping_add(1); Ok(byte) } - fn read_instruction_word(&mut self, device: &mut dyn Addressable) -> Result { - let word = device.read_leu16(self.clock, self.end as Address)?; - self.end = self.end.wrapping_add(2); - Ok(word) - } - - pub fn format_instruction_bytes(&mut self, memory: &mut dyn Addressable) -> String { - let mut ins_data = String::new(); - for offset in 0..self.end.saturating_sub(self.start) { - write!(ins_data, "{:02x} ", memory.read_u8(self.clock, (self.start + offset) as Address).unwrap()).unwrap() - } - ins_data - } - - pub fn dump_decoded(&mut self, memory: &mut dyn Addressable) { - let ins_data = self.format_instruction_bytes(memory); - println!("{:#06x}: {}\n\t{:?}\n", self.start, ins_data, self.instruction); - } - - pub fn dump_disassembly(&mut self, memory: &mut dyn Addressable, start: u16, length: u16) { - let mut next = start; - while next < (start + length) { - match self.decode_at(memory, self.clock, next) { - Ok(()) => { - self.dump_decoded(memory); - next = self.end; - }, - Err(err) => { - println!("{:?}", err); - return; - }, - } + fn read_instruction_word(&mut self) -> Result { + let mut bytes = [0; 2]; + for byte in bytes.iter_mut() { + *byte = self + .bus + .read_u8(self.clock, self.decoder.end & 0xFFFF) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; + self.decoder.end = self.decoder.end.wrapping_add(1); } + Ok(u16::from_le_bytes(bytes)) } } diff --git a/emulator/cpus/z80/src/emuhal.rs b/emulator/cpus/z80/src/emuhal.rs new file mode 100644 index 0000000..1651ee2 --- /dev/null +++ b/emulator/cpus/z80/src/emuhal.rs @@ -0,0 +1,57 @@ +use emulator_hal::{BusAccess, Instant as EmuInstant, Error as EmuError, Step, Inspect, Debug, IntoAddress}; +use crate::state::{Z80, Z80Error, Z80Address, Status}; + +impl EmuError for Z80Error {} + +impl Step for Z80 +where + Instant: EmuInstant, + Bus: BusAccess, +{ + type Instant = Instant; + type Error = Z80Error; + + fn is_running(&mut self) -> bool { + self.state.status == Status::Running + } + + fn reset(&mut self, _now: Self::Instant, _bus: &mut Bus) -> Result<(), Self::Error> { + self.clear_state(); + Ok(()) + } + + fn step(&mut self, now: Self::Instant, bus: &mut Bus) -> Result { + let mut executor = self.begin(now, bus)?; + let clocks = executor.step_one()?; + self.previous_cycle = executor.end(); + Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32) + } +} + +/* +impl Step<(&mut MemBus, &mut IoBus)> for Z80 +where + Instant: EmuInstant, + MemBus: BusAccess, + IoBus: BusAccess, +{ + type Instant = Instant; + type Error = Z80Error; + + fn is_running(&mut self) -> bool { + self.state.status == Status::Running + } + + fn reset(&mut self, _now: Self::Instant, _bus: (&mut MemBus, &mut IoBus)) -> Result<(), Self::Error> { + self.clear_state(); + Ok(()) + } + + fn step(&mut self, now: Self::Instant, bus: (&mut MemBus, &mut IoBus)) -> Result { + let executor = self.begin(now, bus)?; + let clocks = executor.step_one()?; + self.previous_cycle = executor.end(); + Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32) + } +} +*/ diff --git a/emulator/cpus/z80/src/execute.rs b/emulator/cpus/z80/src/execute.rs index 581b8f4..630233e 100644 --- a/emulator/cpus/z80/src/execute.rs +++ b/emulator/cpus/z80/src/execute.rs @@ -1,13 +1,13 @@ -use femtos::{Instant, Duration}; - -use moa_core::{System, Error, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16}; +use emulator_hal::{BusAccess, Instant as EmuInstant}; +use crate::decode::Z80Decoder; use crate::instructions::{ Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister, IndexRegisterHalf, Size, Direction, UndocumentedCopy, }; -use crate::state::{Z80, Z80Error, Status, Flags}; +use crate::state::{Z80, Z80Error, Z80State, Z80Signals, Z80Address, Status, Flags}; use crate::timing::Z80InstructionCycles; +use crate::debugger::Z80Debugger; const FLAGS_NUMERIC: u8 = 0xC0; @@ -20,79 +20,79 @@ enum RotateType { Bit9, } -impl Steppable for Z80 { - fn step(&mut self, system: &System) -> Result { - let clocks = if self.reset.get() { - self.reset()? - } else if self.bus_request.get() { - 4 - } else { - self.step_internal(system)? - }; - - Ok(self.frequency.period_duration() * clocks as u64) - } - - fn on_error(&mut self, system: &System) { - self.dump_state(system.clock); - } -} - -impl Interruptable for Z80 {} - - -impl Transmutable for Z80 { - fn as_steppable(&mut self) -> Option<&mut dyn Steppable> { - Some(self) - } - - fn as_interruptable(&mut self) -> Option<&mut dyn Interruptable> { - Some(self) - } - - fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> { - Some(self) - } -} - -impl From for Error { - fn from(err: Z80Error) -> Self { - match err { - Z80Error::Halted => Self::Other("cpu halted".to_string()), - Z80Error::Breakpoint => Self::Breakpoint("breakpoint".to_string()), - Z80Error::Unimplemented(instruction) => Self::new(format!("unimplemented instruction {:?}", instruction)), - Z80Error::BusError(msg) => Self::Other(msg), - } - } -} - -impl From for Z80Error { - fn from(err: Error) -> Self { - match err { - Error::Processor(ex) => Z80Error::BusError(format!("processor error {}", ex)), - Error::Breakpoint(_) => Z80Error::Breakpoint, - Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => Z80Error::BusError(msg), - } - } -} #[derive(Clone)] -pub struct Z80Executor { +pub struct Z80Cycle { pub current_clock: Instant, + pub decoder: Z80Decoder, pub took_branch: bool, } -impl Z80Executor { +impl Z80Cycle { pub fn at_time(current_clock: Instant) -> Self { Self { current_clock, + decoder: Default::default(), took_branch: false, } } } -impl Z80 { - pub fn step_internal(&mut self, system: &System) -> Result { - self.executor = Z80Executor::at_time(system.clock); +impl Z80 +where + Instant: EmuInstant, +{ + pub(crate) fn begin<'a, Bus>( + &'a mut self, + clock: Instant, + bus: &'a mut Bus, + ) -> Result, Z80Error> + where + Bus: BusAccess, + { + let executor = ExecuteNext { + state: &mut self.state, + signals: &mut self.signals, + debugger: &mut self.debugger, + cycle: Z80Cycle::at_time(clock), + bus, + }; + + Ok(executor) + } +} + +pub(crate) struct ExecuteNext<'a, Bus, Instant> +where + Bus: BusAccess, +{ + state: &'a mut Z80State, + signals: &'a mut Z80Signals, + debugger: &'a mut Z80Debugger, + cycle: Z80Cycle, + bus: Bus, +} + +impl<'a, Bus, Instant> ExecuteNext<'a, Bus, Instant> +where + Bus: BusAccess, + Instant: EmuInstant, +{ + pub(crate) fn end(self) -> Z80Cycle { + self.cycle + } + + pub(crate) fn step_one(&mut self) -> Result { + let clocks = if self.signals.reset.get() { + self.reset()? + } else if self.signals.bus_request.get() { + 4 + } else { + self.step_internal()? + }; + Ok(clocks) + } + + fn step_internal(&mut self) -> Result { match self.state.status { Status::Init => self.init(), Status::Halted => Err(Z80Error::Halted), @@ -103,38 +103,37 @@ impl Z80 { } } - pub fn init(&mut self) -> Result { + fn init(&mut self) -> Result { self.state.pc = 0; self.state.status = Status::Running; Ok(16) } - pub fn reset(&mut self) -> Result { - self.clear_state(); + fn reset(&mut self) -> Result { + *self.state = Default::default(); Ok(16) } - pub fn cycle_one(&mut self) -> Result { - self.check_breakpoints()?; + fn cycle_one(&mut self) -> Result { + self.debugger.check_breakpoints(self.state.pc)?; self.decode_next()?; self.execute_current()?; Ok( - Z80InstructionCycles::from_instruction(&self.decoder.instruction, self.decoder.extra_instruction_bytes)? - .calculate_cycles(self.executor.took_branch), + Z80InstructionCycles::from_instruction(&self.cycle.decoder.instruction, self.cycle.decoder.extra_instruction_bytes)? + .calculate_cycles(self.cycle.took_branch), ) } - pub fn decode_next(&mut self) -> Result<(), Z80Error> { - self.decoder - .decode_at(&mut self.port, self.executor.current_clock, self.state.pc)?; - self.increment_refresh(self.decoder.end.saturating_sub(self.decoder.start) as u8); - self.state.pc = self.decoder.end; + fn decode_next(&mut self) -> Result<(), Z80Error> { + self.cycle.decoder = Z80Decoder::decode_at(&mut self.bus, self.cycle.current_clock, self.state.pc)?; + self.increment_refresh(self.cycle.decoder.end.saturating_sub(self.cycle.decoder.start) as u8); + self.state.pc = self.cycle.decoder.end; Ok(()) } - pub fn execute_current(&mut self) -> Result<(), Z80Error> { - match self.decoder.instruction { + fn execute_current(&mut self) -> Result<(), Z80Error> { + match self.cycle.decoder.instruction { Instruction::ADCa(target) => self.execute_adca(target), Instruction::ADC16(dest_pair, src_pair) => self.execute_adc16(dest_pair, src_pair), Instruction::ADDa(target) => self.execute_adda(target), @@ -230,7 +229,7 @@ impl Z80 { Instruction::SRL(target, opt_copy) => self.execute_srl(target, opt_copy), Instruction::SUB(target) => self.execute_sub(target), Instruction::XOR(target) => self.execute_xor(target), - _ => Err(Z80Error::Unimplemented(self.decoder.instruction.clone())), + _ => Err(Z80Error::Unimplemented(self.cycle.decoder.instruction.clone())), } } @@ -310,15 +309,15 @@ impl Z80 { } fn execute_call(&mut self, addr: u16) -> Result<(), Z80Error> { - self.push_word(self.decoder.end)?; + self.push_word(self.cycle.decoder.end)?; self.state.pc = addr; Ok(()) } fn execute_callcc(&mut self, cond: Condition, addr: u16) -> Result<(), Z80Error> { if self.get_current_condition(cond) { - self.executor.took_branch = true; - self.push_word(self.decoder.end)?; + self.cycle.took_branch = true; + self.push_word(self.cycle.decoder.end)?; self.state.pc = addr; } Ok(()) @@ -434,7 +433,7 @@ impl Z80 { self.set_register_value(Register::B, result); if result != 0 { - self.executor.took_branch = true; + self.cycle.took_branch = true; self.state.pc = self.state.pc.wrapping_add_signed(offset as i16); } Ok(()) @@ -567,7 +566,7 @@ impl Z80 { fn execute_jpcc(&mut self, cond: Condition, addr: u16) -> Result<(), Z80Error> { if self.get_current_condition(cond) { - self.executor.took_branch = true; + self.cycle.took_branch = true; self.state.pc = addr; } Ok(()) @@ -580,7 +579,7 @@ impl Z80 { fn execute_jrcc(&mut self, cond: Condition, offset: i8) -> Result<(), Z80Error> { if self.get_current_condition(cond) { - self.executor.took_branch = true; + self.cycle.took_branch = true; self.state.pc = self.state.pc.wrapping_add_signed(offset as i16); } Ok(()) @@ -616,7 +615,7 @@ impl Z80 { } fn execute_ldx(&mut self) -> Result<(), Z80Error> { - let diff = if self.decoder.instruction == Instruction::LDI || self.decoder.instruction == Instruction::LDIR { + let diff = if self.cycle.decoder.instruction == Instruction::LDI || self.cycle.decoder.instruction == Instruction::LDIR { 1 } else { -1 @@ -631,8 +630,10 @@ impl Z80 { let parity = if count != 0 { Flags::Parity as u8 } else { 0 }; self.set_flags(mask, parity); - if (self.decoder.instruction == Instruction::LDIR || self.decoder.instruction == Instruction::LDDR) && count != 0 { - self.executor.took_branch = true; + if (self.cycle.decoder.instruction == Instruction::LDIR || self.cycle.decoder.instruction == Instruction::LDDR) + && count != 0 + { + self.cycle.took_branch = true; self.state.pc -= 2; } Ok(()) @@ -725,7 +726,7 @@ impl Z80 { fn execute_retcc(&mut self, cond: Condition) -> Result<(), Z80Error> { if self.get_current_condition(cond) { - self.executor.took_branch = true; + self.cycle.took_branch = true; self.state.pc = self.pop_word()?; } Ok(()) @@ -852,7 +853,7 @@ impl Z80 { } fn execute_rst(&mut self, addr: u8) -> Result<(), Z80Error> { - self.push_word(self.decoder.end)?; + self.push_word(self.cycle.decoder.end)?; self.state.pc = addr as u16; Ok(()) } @@ -1010,8 +1011,8 @@ impl Z80 { _ => panic!("RegPair is not supported by inc/dec"), }; - let result = (read_beu16(addr) as i16).wrapping_add(value) as u16; - write_beu16(addr, result); + let result = (u16::from_be_bytes(addr.try_into().unwrap()) as i16).wrapping_add(value) as u16; + addr.copy_from_slice(&result.to_be_bytes()[..]); result } @@ -1127,38 +1128,69 @@ impl Z80 { fn read_port_u8(&mut self, addr: u16) -> Result { self.increment_refresh(1); - Ok(self.port.read_u8(self.executor.current_clock, addr as Address)?) + Ok(self + .bus + .read_u8(self.cycle.current_clock, addr as Z80Address) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?) } fn write_port_u8(&mut self, addr: u16, value: u8) -> Result<(), Z80Error> { self.increment_refresh(1); - Ok(self.port.write_u8(self.executor.current_clock, addr as Address, value)?) + Ok(self + .bus + .write_u8(self.cycle.current_clock, addr as Z80Address, value) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?) } - fn read_port_u16(&mut self, addr: u16) -> Result { - self.increment_refresh(2); - Ok(self.port.read_leu16(self.executor.current_clock, addr as Address)?) + /// Read a u16 value through this CPU's memory port + /// + /// Since the memory port is only able to read 8 bits at a time, this does two reads + /// in little endian byte order + fn read_port_u16(&mut self, mut addr: u16) -> Result { + let mut bytes = [0; 2]; + for byte in bytes.iter_mut() { + self.increment_refresh(1); + *byte = self + .bus + .read_u8(self.cycle.current_clock, addr & 0xFFFF) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; + addr = addr.wrapping_add(1); + } + Ok(u16::from_le_bytes(bytes)) } - fn write_port_u16(&mut self, addr: u16, value: u16) -> Result<(), Z80Error> { - self.increment_refresh(2); - Ok(self.port.write_leu16(self.executor.current_clock, addr as Address, value)?) + /// Write a u16 value through this CPU's memory port + /// + /// Since the memory port is only able to read 8 bits at a time, this does two writes + /// in little endian byte order + fn write_port_u16(&mut self, mut addr: u16, value: u16) -> Result<(), Z80Error> { + let mut bytes = value.to_le_bytes(); + for byte in bytes.iter_mut() { + self.increment_refresh(1); + self.bus + .write_u8(self.cycle.current_clock, addr & 0xFFFF, *byte) + .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; + addr = addr.wrapping_add(1); + } + Ok(()) } fn read_ioport_value(&mut self, upper: u8, lower: u8) -> Result { - let addr = ((upper as Address) << 8) | (lower as Address); - if let Some(io) = self.ioport.as_mut() { - Ok(io.read_u8(self.executor.current_clock, addr)?) - } else { - Ok(0) - } + let addr = ((upper as Z80Address) << 8) | (lower as Z80Address); + // TODO restore this eventually + //if let Some(io) = self.ioport.as_mut() { + // Ok(io.read_u8(self.cycle.current_clock, addr)?) + //} else { + Ok(0) + //} } fn write_ioport_value(&mut self, upper: u8, lower: u8, value: u8) -> Result<(), Z80Error> { - let addr = ((upper as Address) << 8) | (lower as Address); - if let Some(io) = self.ioport.as_mut() { - io.write_u8(self.executor.current_clock, addr, value)? - } + let addr = ((upper as Z80Address) << 8) | (lower as Z80Address); + // TODO restore this eventually + //if let Some(io) = self.ioport.as_mut() { + // io.write_u8(self.cycle.current_clock, addr, value)? + //} Ok(()) } @@ -1199,10 +1231,10 @@ impl Z80 { fn get_register_pair_value(&mut self, regpair: RegisterPair) -> u16 { match regpair { - RegisterPair::BC => read_beu16(&self.state.reg[0..2]), - RegisterPair::DE => read_beu16(&self.state.reg[2..4]), - RegisterPair::HL => read_beu16(&self.state.reg[4..6]), - RegisterPair::AF => read_beu16(&self.state.reg[6..8]), + RegisterPair::BC => u16::from_be_bytes(self.state.reg[0..2].try_into().unwrap()), + RegisterPair::DE => u16::from_be_bytes(self.state.reg[2..4].try_into().unwrap()), + RegisterPair::HL => u16::from_be_bytes(self.state.reg[4..6].try_into().unwrap()), + RegisterPair::AF => u16::from_be_bytes(self.state.reg[6..8].try_into().unwrap()), RegisterPair::SP => self.state.sp, RegisterPair::IX => self.state.ix, RegisterPair::IY => self.state.iy, @@ -1212,16 +1244,16 @@ impl Z80 { fn set_register_pair_value(&mut self, regpair: RegisterPair, value: u16) { match regpair { RegisterPair::BC => { - write_beu16(&mut self.state.reg[0..2], value); + (&mut self.state.reg[0..2]).copy_from_slice(&value.to_be_bytes()[..]); }, RegisterPair::DE => { - write_beu16(&mut self.state.reg[2..4], value); + (&mut self.state.reg[2..4]).copy_from_slice(&value.to_be_bytes()[..]); }, RegisterPair::HL => { - write_beu16(&mut self.state.reg[4..6], value); + (&mut self.state.reg[4..6]).copy_from_slice(&value.to_be_bytes()[..]); }, RegisterPair::AF => { - write_beu16(&mut self.state.reg[6..8], value); + (&mut self.state.reg[6..8]).copy_from_slice(&value.to_be_bytes()[..]); }, RegisterPair::SP => { self.state.sp = value; diff --git a/emulator/cpus/z80/src/lib.rs b/emulator/cpus/z80/src/lib.rs index e95fcbe..00e053b 100644 --- a/emulator/cpus/z80/src/lib.rs +++ b/emulator/cpus/z80/src/lib.rs @@ -1,8 +1,19 @@ -pub mod debugger; -pub mod decode; -pub mod execute; -pub mod instructions; -pub mod state; -pub mod timing; +mod debugger; +mod decode; +mod emuhal; +mod execute; +mod instructions; +mod state; +mod timing; -pub use self::state::{Z80, Z80Type, Z80Error}; +//#[cfg(feature = "moa")] +pub mod moa; +pub use crate::moa::MoaZ80; + +pub use crate::state::{Z80, Z80Type, Z80Error, Z80State, Status, Flags}; +pub use crate::decode::Z80Decoder; +pub use crate::execute::Z80Cycle; +pub use crate::instructions::{ + Size, Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, + LoadTarget, UndocumentedCopy, Instruction, +}; diff --git a/emulator/cpus/z80/src/moa.rs b/emulator/cpus/z80/src/moa.rs new file mode 100644 index 0000000..ea12221 --- /dev/null +++ b/emulator/cpus/z80/src/moa.rs @@ -0,0 +1,138 @@ +use std::rc::Rc; +use std::cell::RefCell; +use femtos::{Instant, Duration}; +use emulator_hal::{BusAdapter, Instant as EmuInstant}; + +use moa_core::{System, Error, Bus, Address, Steppable, Addressable, Interruptable, Signalable, Signal, Debuggable, Transmutable}; + +use crate::{Z80, Z80Error, Z80Decoder}; +use crate::instructions::Register; + +pub struct MoaZ80 +where + Instant: EmuInstant, +{ + pub bus: Rc>, + pub cpu: Z80, +} + +impl Steppable for MoaZ80 +where + Instant: EmuInstant, +{ + fn step(&mut self, system: &System) -> Result { + let mut bus = &mut *self.bus.borrow_mut(); + let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err))); + + let mut executor = self.cpu.begin(system.clock, &mut adapter)?; + let clocks = executor.step_one()?; + self.cpu.previous_cycle = executor.end(); + Ok(Instant::hertz_to_duration(self.cpu.frequency.as_hz() as u64) * clocks as u32) + } + + fn on_error(&mut self, system: &System) { + let bus = &mut *system.bus.borrow_mut(); + let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err))); + self.cpu.dump_state(system.clock, &mut adapter); + } +} + +impl Interruptable for MoaZ80 {} + +/* +impl Signalable for Z80 { + fn set_signal(&mut self, signal: Signal, flag: bool) -> Result<(), Error> { + match signal { + Signal::Reset => self.signals.reset = flag, + Signal::BusRequest => self.signals.bus_request = flag, + } + Ok(()) + } + + fn signal(&mut self, signal: Signal) -> Option { + match signal { + Signal::Reset => Some(self.signals.reset), + Signal::BusRequest => Some(self.signals.bus_request), + } + } +} +*/ + +impl Transmutable for MoaZ80 { + fn as_steppable(&mut self) -> Option<&mut dyn Steppable> { + Some(self) + } + + fn as_interruptable(&mut self) -> Option<&mut dyn Interruptable> { + Some(self) + } + + fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> { + Some(self) + } + + //#[inline] + //fn as_signalable(&mut self) -> Option<&mut dyn Signalable> { + // Some(self) + //} +} + +impl From for Error { + fn from(err: Z80Error) -> Self { + match err { + Z80Error::Halted => Self::Other("cpu halted".to_string()), + Z80Error::Breakpoint => Self::Breakpoint("breakpoint".to_string()), + Z80Error::Unimplemented(instruction) => Self::new(format!("unimplemented instruction {:?}", instruction)), + Z80Error::BusError(msg) => Self::Other(msg), + } + } +} + +impl From for Z80Error { + fn from(err: Error) -> Self { + match err { + Error::Processor(ex) => Z80Error::BusError(format!("processor error {}", ex)), + Error::Breakpoint(_) => Z80Error::Breakpoint, + Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => Z80Error::BusError(msg), + } + } +} + +impl Debuggable for MoaZ80 { + fn add_breakpoint(&mut self, addr: Address) { + self.cpu.debugger.breakpoints.push(addr as u16); + } + + fn remove_breakpoint(&mut self, addr: Address) { + if let Some(index) = self.cpu.debugger.breakpoints.iter().position(|a| *a == addr as u16) { + self.cpu.debugger.breakpoints.remove(index); + } + } + + fn print_current_step(&mut self, system: &System) -> Result<(), Error> { + let bus = &mut *system.bus.borrow_mut(); + let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err))); + + let decoder = Z80Decoder::decode_at(&mut adapter, system.clock, self.cpu.state.pc)?; + self.cpu.previous_cycle.decoder.dump_decoded(&mut adapter); + self.cpu.dump_state(system.clock, &mut adapter); + Ok(()) + } + + fn print_disassembly(&mut self, system: &System, addr: Address, count: usize) { + let bus = &mut *system.bus.borrow_mut(); + let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err))); + + Z80Decoder::dump_disassembly(&mut adapter, addr as u16, count as u16); + } + + fn run_command(&mut self, _system: &System, args: &[&str]) -> Result { + match args[0] { + "l" => self.cpu.state.reg[Register::L as usize] = 0x05, + _ => { + return Ok(true); + }, + } + Ok(false) + } +} diff --git a/emulator/cpus/z80/src/state.rs b/emulator/cpus/z80/src/state.rs index 2b40704..7a6be75 100644 --- a/emulator/cpus/z80/src/state.rs +++ b/emulator/cpus/z80/src/state.rs @@ -1,13 +1,13 @@ use std::rc::Rc; use std::cell::RefCell; use femtos::{Instant, Frequency}; +use emulator_hal::{Instant as EmuInstant, BusAccess}; use moa_core::{Address, Bus, BusPort}; use moa_signals::Signal; -use crate::decode::Z80Decoder; use crate::debugger::Z80Debugger; -use crate::execute::Z80Executor; +use crate::execute::Z80Cycle; use crate::instructions::{Instruction, Register, InterruptMode}; @@ -92,6 +92,14 @@ impl Z80State { } } +#[derive(Clone, Debug, Default)] +pub struct Z80Signals { + //pub reset: bool, + //pub bus_request: bool, + pub reset: Signal, + pub bus_request: Signal, +} + #[derive(Clone, Debug, thiserror::Error)] pub enum Z80Error /* */ { #[error("cpu halted")] @@ -104,62 +112,61 @@ pub enum Z80Error /* */ { BusError(String /* B */), } + +pub type Z80Address = u16; +pub type Z80IOAddress = u16; + +pub enum Z80AddressSpace { + Memory(Z80Address), + IO(Z80IOAddress), +} + #[derive(Clone)] -pub struct Z80 { +pub struct Z80 { pub cputype: Z80Type, pub frequency: Frequency, pub state: Z80State, - pub decoder: Z80Decoder, pub debugger: Z80Debugger, - pub executor: Z80Executor, - pub port: BusPort, - pub ioport: Option, - pub reset: Signal, - pub bus_request: Signal, + pub previous_cycle: Z80Cycle, + pub signals: Z80Signals, + // TODO activate later + //pub reset: Signal, + //pub bus_request: Signal, } -impl Z80 { - pub fn new(cputype: Z80Type, frequency: Frequency, port: BusPort, ioport: Option) -> Self { +impl Z80 +where + Instant: EmuInstant, +{ + pub fn new(cputype: Z80Type, frequency: Frequency) -> Self { Self { cputype, frequency, state: Z80State::default(), - decoder: Z80Decoder::default(), debugger: Z80Debugger::default(), - executor: Z80Executor::at_time(Instant::START), - port, - ioport, - reset: Signal::new(false), - bus_request: Signal::new(false), + previous_cycle: Z80Cycle::at_time(Instant::START), + signals: Z80Signals::default(), + //reset: Signal::new(false), + //bus_request: Signal::new(false), } } - pub fn from_type( - cputype: Z80Type, - frequency: Frequency, - bus: Rc>, - addr_offset: Address, - io_bus: Option<(Rc>, Address)>, - ) -> Self { + pub fn from_type(cputype: Z80Type, frequency: Frequency) -> Self { match cputype { - Z80Type::Z80 => Self::new( - cputype, - frequency, - BusPort::new(addr_offset, 16, 8, bus), - io_bus.map(|(io_bus, io_offset)| BusPort::new(io_offset, 16, 8, io_bus)), - ), + Z80Type::Z80 => Self::new(cputype, frequency), } } #[allow(dead_code)] pub fn clear_state(&mut self) { self.state = Z80State::default(); - self.decoder = Z80Decoder::default(); self.debugger = Z80Debugger::default(); - self.executor = Z80Executor::at_time(Instant::START); } - pub fn dump_state(&mut self, clock: Instant) { + pub fn dump_state(&mut self, clock: Instant, bus: &mut Bus) + where + Bus: BusAccess, + { println!("Status: {:?}", self.state.status); println!("PC: {:#06x}", self.state.pc); println!("SP: {:#06x}", self.state.sp); @@ -200,11 +207,13 @@ impl Z80 { println!( "Current Instruction: {} {:?}", - self.decoder.format_instruction_bytes(&mut self.port), - self.decoder.instruction + self.previous_cycle.decoder.format_instruction_bytes(bus), + self.previous_cycle.decoder.instruction ); + println!("Previous Instruction: {:?}", self.previous_cycle.decoder.instruction); println!(); - self.port.dump_memory(clock, self.state.sp as Address, 0x40); + // TODO disabled until function is reimplemented + //self.port.dump_memory(clock, self.state.sp as Address, 0x40); println!(); } } diff --git a/emulator/frontends/minifb/src/bin/moa-trs80.rs b/emulator/frontends/minifb/src/bin/moa-trs80.rs index f5cc7a1..0b1ef3a 100644 --- a/emulator/frontends/minifb/src/bin/moa-trs80.rs +++ b/emulator/frontends/minifb/src/bin/moa-trs80.rs @@ -8,7 +8,7 @@ fn main() { Arg::new("ROM") .short('r') .long("rom") - .action(ArgAction::SetTrue) + .action(ArgAction::Set) .value_name("FILE") .help("ROM file to load at the start of memory"), ) diff --git a/emulator/libraries/emulator-hal b/emulator/libraries/emulator-hal index 2391a32..c17e364 160000 --- a/emulator/libraries/emulator-hal +++ b/emulator/libraries/emulator-hal @@ -1 +1 @@ -Subproject commit 2391a324376bdd9fa1ae9801bbe3d12f2e69fa62 +Subproject commit c17e364ed472fff774b1b38323a2c72936c7224c diff --git a/emulator/libraries/signals/src/lib.rs b/emulator/libraries/signals/src/lib.rs index 7c9f331..a79e495 100644 --- a/emulator/libraries/signals/src/lib.rs +++ b/emulator/libraries/signals/src/lib.rs @@ -19,7 +19,7 @@ type Input = Signal; #[allow(dead_code)] type TriState = Signal; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Signal(Rc>); impl Signal { diff --git a/emulator/systems/genesis/src/system.rs b/emulator/systems/genesis/src/system.rs index d57e2e5..bcc4b5c 100644 --- a/emulator/systems/genesis/src/system.rs +++ b/emulator/systems/genesis/src/system.rs @@ -2,13 +2,13 @@ use std::mem; use std::rc::Rc; use std::cell::RefCell; -use femtos::Frequency; +use femtos::{Instant, Frequency}; use moa_core::{System, Error, MemoryBlock, Bus, Address, Addressable, Device}; use moa_host::Host; use moa_m68k::{M68k, M68kType}; -use moa_z80::{Z80, Z80Type}; +use moa_z80::{MoaZ80, Z80, Z80Type}; use moa_peripherals_yamaha::Ym2612; use moa_peripherals_yamaha::Sn76489; @@ -68,11 +68,16 @@ pub fn build_genesis(host: &mut H, mut options: SegaGenesisOptions) -> coproc_bus.borrow_mut().insert(0x6000, coproc_register.clone()); coproc_bus.borrow_mut().insert(0x7f11, coproc_sn_sound.clone()); coproc_bus.borrow_mut().insert(0x8000, coproc_area); - let coproc = Z80::from_type(Z80Type::Z80, Frequency::from_hz(3_579_545), coproc_bus, 0, None); - let mut reset = coproc.reset.clone(); - let mut bus_request = coproc.bus_request.clone(); + let coproc = Z80::from_type(Z80Type::Z80, Frequency::from_hz(3_579_545)); + let coproc = MoaZ80 { + bus: coproc_bus, + cpu: coproc, + }; + let mut reset = coproc.cpu.signals.reset.clone(); + let mut bus_request = coproc.cpu.signals.bus_request.clone(); reset.set(true); bus_request.set(true); + let coproc = Device::new(coproc); // Add coprocessor devices to the system bus so the 68000 can access them too system.add_addressable_device(0x00a00000, coproc_ram)?; @@ -80,7 +85,7 @@ pub fn build_genesis(host: &mut H, mut options: SegaGenesisOptions) -> system.add_addressable_device(0x00a06000, coproc_register)?; //system.add_addressable_device(0x00c00010, coproc_sn_sound)?; system.add_device("sn_sound", coproc_sn_sound.clone())?; - system.add_device("coproc", Device::new(coproc))?; + system.add_device("coproc", coproc.clone())?; let controllers = GenesisControllers::new(host)?; diff --git a/emulator/systems/trs80/src/system.rs b/emulator/systems/trs80/src/system.rs index d692853..8e4cdee 100644 --- a/emulator/systems/trs80/src/system.rs +++ b/emulator/systems/trs80/src/system.rs @@ -3,7 +3,7 @@ use femtos::Frequency; use moa_core::{System, Error, MemoryBlock, Device}; use moa_host::Host; -use moa_z80::{Z80, Z80Type}; +use moa_z80::{MoaZ80, Z80, Z80Type}; use crate::peripherals::model1::{Model1Keyboard, Model1Video}; @@ -44,7 +44,11 @@ pub fn build_trs80(host: &mut H, options: Trs80Options) -> Result Result<(Z80, System, Rc>), Error> { - let mut system = System::default(); - +fn init_execute_test( + cputype: Z80Type, + state: &TestState, + ports: &[TestPort], +) -> Result<(Z80, MemoryBlock, MemoryBlock), Error> { // Insert basic initialization - let mem = MemoryBlock::new(vec![0; 0x1_0000]); - system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); + let len = 0x1_0000; + let mut data = Vec::with_capacity(len); + unsafe { + data.set_len(len); + } + let mut memory = MemoryBlock::::from(data); // Set up IOREQ as memory space - let io_ram = Device::new(MemoryBlock::new(vec![0; 0x10000])); - let io_bus = Rc::new(RefCell::new(Bus::default())); - io_bus.borrow_mut().set_ignore_unmapped(true); - io_bus.borrow_mut().insert(0x0000, io_ram); + let len = 0x1_0000; + let mut data = Vec::with_capacity(len); + unsafe { + data.set_len(len); + } + let mut io = MemoryBlock::::from(data); - let port = BusPort::new(0, 16, 8, system.bus.clone()); - let ioport = BusPort::new(0, 16, 8, io_bus.clone()); - let mut cpu = Z80::new(cputype, Frequency::from_mhz(10), port, Some(ioport)); + let mut cpu = Z80::new(cputype, Frequency::from_mhz(10)); cpu.state.status = Status::Running; - load_state(&mut cpu, &mut system, io_bus.clone(), state, ports)?; + load_state(&mut cpu, &mut memory, &mut io, state, ports)?; - Ok((cpu, system, io_bus)) + Ok((cpu, memory, io)) } fn assert_value(actual: T, expected: T, message: &str) -> Result<(), Error> @@ -175,14 +187,14 @@ where if actual == expected { Ok(()) } else { - Err(Error::assertion(format!("{:#X} != {:#X}, {}", actual, expected, message))) + Err(Error::Assertion(format!("{:#X} != {:#X}, {}", actual, expected, message))) } } fn load_state( - cpu: &mut Z80, - system: &mut System, - io_bus: Rc>, + cpu: &mut Z80, + memory: &mut MemoryBlock, + io: &mut MemoryBlock, initial: &TestState, ports: &[TestPort], ) -> Result<(), Error> { @@ -215,12 +227,15 @@ fn load_state( // Load data bytes into memory for (addr, byte) in initial.ram.iter() { - system.get_bus().write_u8(system.clock, *addr as u64, *byte)?; + memory + .write_u8(Instant::START, *addr, *byte) + .map_err(|err| Error::Bus(format!("{:?}", err)))?; } // Load data bytes into io space for port in ports.iter() { - io_bus.borrow_mut().write_u8(system.clock, port.addr as u64, port.value)?; + io.write_u8(Instant::START, port.addr, port.value) + .map_err(|err| Error::Bus(format!("{:?}", err)))?; } Ok(()) @@ -229,9 +244,9 @@ fn load_state( const IGNORE_FLAG_MASK: u8 = Flags::F3 as u8 | Flags::F5 as u8; fn assert_state( - cpu: &Z80, - system: &System, - io_bus: Rc>, + cpu: &Z80, + memory: &mut MemoryBlock, + io: &mut MemoryBlock, expected: &TestState, check_extra_flags: bool, ports: &[TestPort], @@ -267,23 +282,25 @@ fn assert_state( let expected_im: InterruptMode = expected.im.into(); if cpu.state.im != expected_im { - return Err(Error::assertion(format!("{:?} != {:?}, im", cpu.state.im, expected_im))); + return Err(Error::Assertion(format!("{:?} != {:?}, im", cpu.state.im, expected_im))); } assert_value(cpu.state.iff1 as u8, expected.iff1, "iff1")?; assert_value(cpu.state.iff2 as u8, expected.iff2, "iff2")?; - let addr_mask = cpu.port.address_mask(); - - // Load data bytes into memory + // Compare data bytes in memory for (addr, byte) in expected.ram.iter() { - let actual = system.get_bus().read_u8(system.clock, *addr as Address & addr_mask)?; + let actual = memory + .read_u8(Instant::START, *addr) + .map_err(|err| Error::Bus(format!("{:?}", err)))?; assert_value(actual, *byte, &format!("ram at {:x}", addr))?; } - // Load data bytes into io space + // Compare data bytes in io space for port in ports.iter() { if port.atype == "w" { - let actual = io_bus.borrow_mut().read_u8(system.clock, port.addr as u64)?; + let actual = io + .read_u8(Instant::START, port.addr) + .map_err(|err| Error::Bus(format!("{:?}", err)))?; assert_value(actual, port.value, &format!("port value at {:x}", port.addr))?; } } @@ -292,19 +309,22 @@ fn assert_state( } fn step_cpu_and_assert( - cpu: &mut Z80, - system: &System, - io_bus: Rc>, + cpu: &mut Z80, + memory: &mut MemoryBlock, + io: &mut MemoryBlock, case: &TestCase, args: &Args, ) -> Result<(), Error> { - let clock_elapsed = cpu.step(system)?; + //let clock_elapsed = cpu.step((memory, io))?; + let clock_elapsed = cpu + .step(Instant::START, memory) + .map_err(|err| Error::Step(format!("{:?}", err)))?; - assert_state(cpu, system, io_bus, &case.final_state, args.check_extra_flags, &case.ports)?; + assert_state(cpu, memory, io, &case.final_state, args.check_extra_flags, &case.ports)?; if args.check_timings { - let cycles = clock_elapsed / cpu.frequency.period_duration(); - if cycles != case.cycles.len() as Address { - return Err(Error::assertion(format!( + let cycles = clock_elapsed.as_duration() / cpu.frequency.period_duration(); + if cycles != case.cycles.len() as u64 { + return Err(Error::Assertion(format!( "expected instruction to take {} cycles, but took {}", case.cycles.len(), cycles @@ -316,10 +336,10 @@ fn step_cpu_and_assert( } fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> { - let (mut cpu, system, io_bus) = init_execute_test(Z80Type::Z80, &case.initial_state, &case.ports).unwrap(); + let (mut cpu, mut memory, mut io) = init_execute_test(Z80Type::Z80, &case.initial_state, &case.ports).unwrap(); let mut initial_cpu = cpu.clone(); - let result = step_cpu_and_assert(&mut cpu, &system, io_bus, case, args); + let result = step_cpu_and_assert(&mut cpu, &mut memory, &mut io, case, args); match result { Ok(()) => Ok(()), @@ -328,8 +348,8 @@ fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> { if args.debug { case.dump(); println!(); - initial_cpu.dump_state(system.clock); - cpu.dump_state(system.clock); + initial_cpu.dump_state(Instant::START, &mut memory); + cpu.dump_state(Instant::START, &mut memory); } println!("FAILED: {:?}", err); } @@ -361,6 +381,10 @@ fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) { } } + if args.no_check_io && !case.ports.is_empty() { + continue; + } + // Sort the ram memory for debugging help if args.debug { case.initial_state.ram.sort_by_key(|(addr, _)| *addr); diff --git a/todo.txt b/todo.txt index 5609095..fdaee8f 100644 --- a/todo.txt +++ b/todo.txt @@ -1,14 +1,20 @@ -* decide if you should continue expecting Instant to usable through the trait alone, despite issues -* fix it to use the full 68k address space, and maybe see if it's possible to make the address translation cleaner/nicer/simpler/faster -* now that you have Instant as generic in m68k, try making it an associated type to see if it's possible to hide it away +* fix the Z80 reset and bus_request signals +* the emulator_hal_memory should throw an error when an access will straddle the end of memory? Or should it autowrap? +* fix the m68k dumping functions + +* convert computie system to use the new moa-system library crate to replace the old core crate +* change package names to drop the 's', so moa-systems-computie becomes moa-system-computie -* try using the debug and inspect traits elsewhere in moa * convert all code to use fmt::Writer instead of println +* fix it to use the full 68k address space, and maybe see if it's possible to make the address translation cleaner/nicer/simpler/faster -* figure out how to do interrupts, and add them to emulator-hal, implement them in m68k * convert the Z80 * convert peripherals to use BusAccess and Step +* try using the debug and inspect traits elsewhere in moa + +* decide if you should continue expecting Instant to usable through the trait alone, despite issues +* figure out how to do interrupts, and add them to emulator-hal, implement them in m68k * replace Addressable/Steppable and modify Transmutable to use the emulator-hal traits * remove the custom moa impls from m68k if possible at this point * publish the emulator-hal crate @@ -79,7 +85,7 @@ * add doc strings everywhere * get rustfmt, rustdoc, and clippy working in some kind of semi-automatic fashion -* you really need a full web-based debugger +* you really need a full web-based debugger; look into egui as the basis for it?