From 4b2c02381f94c28eaf9409b0c534703dce14c892 Mon Sep 17 00:00:00 2001 From: transistor Date: Sun, 31 Mar 2024 21:17:54 -0700 Subject: [PATCH 1/5] Converted Z80 to use emulator-hal traits --- emulator/cpus/m68k/src/debugger.rs | 12 +- emulator/cpus/m68k/src/decode.rs | 2 +- emulator/cpus/m68k/src/execute.rs | 13 +- emulator/cpus/m68k/src/memory.rs | 7 +- emulator/cpus/m68k/src/moa.rs | 12 +- emulator/cpus/m68k/src/state.rs | 4 +- emulator/cpus/m68k/tests/decode_tests.rs | 2 +- emulator/cpus/m68k/tests/execute_tests.rs | 3 +- .../cpus/m68k/tests/musashi_timing_tests.rs | 2 +- emulator/cpus/m68k/tests/timing_tests.rs | 2 +- emulator/cpus/z80/Cargo.toml | 4 +- emulator/cpus/z80/src/debugger.rs | 55 +--- emulator/cpus/z80/src/decode.rs | 256 +++++++++-------- emulator/cpus/z80/src/emuhal.rs | 61 +++++ emulator/cpus/z80/src/execute.rs | 257 ++++++++++-------- emulator/cpus/z80/src/lib.rs | 22 +- emulator/cpus/z80/src/moa.rs | 106 ++++++++ emulator/cpus/z80/src/state.rs | 66 +++-- tests/rad_tests/Cargo.toml | 3 +- tests/rad_tests/latest.txt | 68 ++--- tests/rad_tests/run_all.sh | 5 +- tests/rad_tests/src/main.rs | 116 ++++---- 22 files changed, 640 insertions(+), 438 deletions(-) create mode 100644 emulator/cpus/z80/src/emuhal.rs create mode 100644 emulator/cpus/z80/src/moa.rs 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..d7b4b13 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()), @@ -99,8 +99,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..8dde2b1 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,110 @@ 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)?; - Ok(()) + 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 decode_one(&mut self, memory: &mut dyn Addressable) -> Result { - let ins = self.read_instruction_byte(memory)?; - self.decode_bare(memory, ins, 0) + /* + pub fn format_instruction_bytes(&mut self) -> String { + let mut ins_data = String::new(); + for offset in 0..self.decoder.end.saturating_sub(self.decoder.start) { + write!(ins_data, "{:02x} ", self.bus.read_u8(self.clock, self.decoder.start + offset).unwrap()).unwrap() + } + ins_data + } + + pub fn dump_decoded(&mut self) { + let ins_data = self.format_instruction_bytes(); + println!("{:#06x}: {}\n\t{:?}\n", self.decoder.start, ins_data, self.decoder.instruction); + } + + pub fn dump_disassembly(&mut self, start: Z80Address, length: Z80Address) { + let mut next = start; + while next < (start + length) { + match self.decode_at(self.clock, next) { + Ok(()) => { + self.dump_decoded(); + next = self.decoder.end; + }, + Err(err) => { + println!("{:?}", err); + return; + }, + } + } + } + */ +} + +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_bare( &mut self, - memory: &mut dyn Addressable, ins: u8, extra_instruction_bytes: u16, ) -> Result { - self.extra_instruction_bytes = extra_instruction_bytes; + 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 +154,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 +174,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 +220,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 +244,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 +253,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 +274,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 +285,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 +302,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 +332,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 +395,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 +411,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 +427,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 +490,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 +508,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 +525,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,7 +553,7 @@ 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"), } @@ -514,7 +561,6 @@ impl Z80Decoder { fn decode_index_target( &mut self, - memory: &mut dyn Addressable, index_reg: IndexRegister, z: u8, ) -> Result, Z80Error> { @@ -522,7 +568,7 @@ impl Z80Decoder { 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 +577,21 @@ 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..82154f1 --- /dev/null +++ b/emulator/cpus/z80/src/emuhal.rs @@ -0,0 +1,61 @@ + +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)?; + executor.step_one()?; + self.previous_cycle = executor.end(); + // TODO fix this + Ok(now) + } +} + +/* +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)?; + executor.step_one()?; + self.previous_cycle = executor.end(); + // TODO fix this + Ok(now) + } +} +*/ + diff --git a/emulator/cpus/z80/src/execute.rs b/emulator/cpus/z80/src/execute.rs index 581b8f4..171705d 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, 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, + 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, + 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(mut self) -> Z80Cycle { + self.cycle + } + + pub(crate) fn step_one(&mut self) -> Result { + // TODO restore the reset and bus request signals + //let clocks = if self.reset.get() { + // self.reset()? + //} else if self.bus_request.get() { + // 4 + //} else { + // self.step_internal(self.cycle.current_clock)? + //}; + + //Ok(self.frequency.period_duration() * clocks as u64) + + // TODO remove this when done + let clocks = self.step_internal(self.cycle.current_clock)?; + Ok(clocks) + } + + fn step_internal(&mut self, clock: Instant) -> 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,8 @@ 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 +724,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 +851,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 +1009,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 +1126,62 @@ 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 { + 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 +1222,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 +1235,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..f7e857d 100644 --- a/emulator/cpus/z80/src/lib.rs +++ b/emulator/cpus/z80/src/lib.rs @@ -1,8 +1,16 @@ -pub mod debugger; -pub mod decode; -pub mod execute; -pub mod instructions; -pub mod state; -pub mod timing; +mod debugger; +mod decode; +mod execute; +mod instructions; +mod state; +mod timing; +mod moa; +mod emuhal; -pub use self::state::{Z80, Z80Type, Z80Error}; +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..d8e573f --- /dev/null +++ b/emulator/cpus/z80/src/moa.rs @@ -0,0 +1,106 @@ + +use femtos::{Instant, Duration}; +use emulator_hal::{BusAdapter, Instant as EmuInstant}; + +use moa_core::{System, Error, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable}; + +use crate::{Z80, Z80Error, Z80Decoder}; +use crate::instructions::Register; + +impl Steppable for Z80 +where + Instant: EmuInstant, +{ + fn step(&mut self, system: &System) -> Result { + let bus = &mut *system.bus.borrow_mut(); + let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err))); + + let mut executor = self.begin(system.clock, &mut adapter)?; + let clocks = executor.step_one()?; + self.previous_cycle = executor.end(); + Ok(Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32) + } + + 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), + } + } +} + +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> { + 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.state.pc)?; + // TODO disabled until decoder is fixed + //self.decoder.dump_decoded(&mut self.port); + self.dump_state(system.clock); + Ok(()) + } + + fn print_disassembly(&mut self, _system: &System, addr: Address, count: usize) { + // TODO disabled until decoder is fixed + //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) + } +} + + diff --git a/emulator/cpus/z80/src/state.rs b/emulator/cpus/z80/src/state.rs index 2b40704..6091980 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; 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}; @@ -104,33 +104,44 @@ 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 port: BusPort, + //pub ioport: Option, + // 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 /*, port: BusPort, ioport: Option*/) -> 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), + //port, + //ioport, + //reset: Signal::new(false), + //bus_request: Signal::new(false), } } @@ -145,8 +156,8 @@ impl Z80 { 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)), + //BusPort::new(addr_offset, 16, 8, bus), + //io_bus.map(|(io_bus, io_offset)| BusPort::new(io_offset, 16, 8, io_bus)), ), } } @@ -154,9 +165,7 @@ impl Z80 { #[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) { @@ -198,13 +207,16 @@ impl Z80 { println!("I: {:#04x} R: {:#04x}", self.state.i, self.state.r); println!("IM: {:?} IFF1: {:?} IFF2: {:?}", self.state.im, self.state.iff1, self.state.iff2); - println!( - "Current Instruction: {} {:?}", - self.decoder.format_instruction_bytes(&mut self.port), - self.decoder.instruction - ); + // TODO disabled until function is reimplemented + //println!( + // "Current Instruction: {} {:?}", + // self.decoder.format_instruction_bytes(&mut self.port), + // self.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/tests/rad_tests/Cargo.toml b/tests/rad_tests/Cargo.toml index 5f507ed..be74c88 100644 --- a/tests/rad_tests/Cargo.toml +++ b/tests/rad_tests/Cargo.toml @@ -5,7 +5,8 @@ edition = "2021" [dependencies] femtos = "0.1" -moa-core = { path = "../../emulator/core" } +emulator-hal = { path = "../../emulator/libraries/emulator-hal/emulator-hal" } +emulator-hal-memory = { path = "../../emulator/libraries/emulator-hal/emulator-hal-memory" } moa-z80 = { path = "../../emulator/cpus/z80" } serde = "1.0" serde_json = "1.0" diff --git a/tests/rad_tests/latest.txt b/tests/rad_tests/latest.txt index 4f91649..56b4166 100644 --- a/tests/rad_tests/latest.txt +++ b/tests/rad_tests/latest.txt @@ -1,4 +1,4 @@ -Last run on 2023-06-10 at commit cbcfb26f49c23414fe00317fddc65ffcbb087b18 +Last run on 2024-03-31 at commit 6e7e315808228e03eaf8ad2e8152c087710f1d28 with flags --check-undocumented --check-timings 00.json completed, all passed! 01.json completed, all passed! @@ -476,7 +476,7 @@ d7.json completed, all passed! d8.json completed, all passed! d9.json completed, all passed! da.json completed, all passed! -db.json completed, all passed! +db.json completed: 6 passed, 994 FAILED dc.json completed, all passed! dd 00.json completed, all passed! dd 01.json completed, all passed! @@ -614,7 +614,7 @@ dd 82.json completed, all passed! dd 83.json completed, all passed! dd 84.json completed, all passed! dd 85.json completed, all passed! -dd 86.json completed: 0 passed, 1000 FAILED +dd 86.json completed, all passed! dd 87.json completed, all passed! dd 88.json completed, all passed! dd 89.json completed, all passed! @@ -622,7 +622,7 @@ dd 8a.json completed, all passed! dd 8b.json completed, all passed! dd 8c.json completed, all passed! dd 8d.json completed, all passed! -dd 8e.json completed: 0 passed, 1000 FAILED +dd 8e.json completed, all passed! dd 8f.json completed, all passed! dd 90.json completed, all passed! dd 91.json completed, all passed! @@ -630,7 +630,7 @@ dd 92.json completed, all passed! dd 93.json completed, all passed! dd 94.json completed, all passed! dd 95.json completed, all passed! -dd 96.json completed: 0 passed, 1000 FAILED +dd 96.json completed, all passed! dd 97.json completed, all passed! dd 98.json completed, all passed! dd 99.json completed, all passed! @@ -638,7 +638,7 @@ dd 9a.json completed, all passed! dd 9b.json completed, all passed! dd 9c.json completed, all passed! dd 9d.json completed, all passed! -dd 9e.json completed: 0 passed, 1000 FAILED +dd 9e.json completed, all passed! dd 9f.json completed, all passed! dd a0.json completed, all passed! dd a1.json completed, all passed! @@ -646,7 +646,7 @@ dd a2.json completed, all passed! dd a3.json completed, all passed! dd a4.json completed, all passed! dd a5.json completed, all passed! -dd a6.json completed: 0 passed, 1000 FAILED +dd a6.json completed, all passed! dd a7.json completed, all passed! dd a8.json completed, all passed! dd a9.json completed, all passed! @@ -654,7 +654,7 @@ dd aa.json completed, all passed! dd ab.json completed, all passed! dd ac.json completed, all passed! dd ad.json completed, all passed! -dd ae.json completed: 0 passed, 1000 FAILED +dd ae.json completed, all passed! dd af.json completed, all passed! dd b0.json completed, all passed! dd b1.json completed, all passed! @@ -662,7 +662,7 @@ dd b2.json completed, all passed! dd b3.json completed, all passed! dd b4.json completed, all passed! dd b5.json completed, all passed! -dd b6.json completed: 0 passed, 1000 FAILED +dd b6.json completed, all passed! dd b7.json completed, all passed! dd b8.json completed, all passed! dd b9.json completed, all passed! @@ -670,7 +670,7 @@ dd ba.json completed, all passed! dd bb.json completed, all passed! dd bc.json completed, all passed! dd bd.json completed, all passed! -dd be.json completed: 0 passed, 1000 FAILED +dd be.json completed, all passed! dd bf.json completed, all passed! dd c0.json completed, all passed! dd c1.json completed, all passed! @@ -954,7 +954,7 @@ dd d7.json completed, all passed! dd d8.json completed, all passed! dd d9.json completed, all passed! dd da.json completed, all passed! -dd db.json completed, all passed! +dd db.json completed: 0 passed, 1000 FAILED dd dc.json completed, all passed! dd de.json completed, all passed! dd df.json completed, all passed! @@ -1003,7 +1003,7 @@ e9.json completed, all passed! ea.json completed, all passed! eb.json completed, all passed! ec.json completed, all passed! -ed 40.json completed, all passed! +ed 40.json completed: 6 passed, 994 FAILED ed 41.json completed, all passed! ed 42.json completed, all passed! ed 43.json completed, all passed! @@ -1011,7 +1011,7 @@ ed 44.json completed, all passed! ed 45.json completed, all passed! ed 46.json completed, all passed! ed 47.json completed, all passed! -ed 48.json completed, all passed! +ed 48.json completed: 4 passed, 996 FAILED ed 49.json completed, all passed! ed 4a.json completed, all passed! ed 4b.json completed, all passed! @@ -1019,7 +1019,7 @@ ed 4c.json completed, all passed! ed 4d.json completed, all passed! ed 4e.json completed, all passed! ed 4f.json completed, all passed! -ed 50.json completed, all passed! +ed 50.json completed: 6 passed, 994 FAILED ed 51.json completed, all passed! ed 52.json completed, all passed! ed 53.json completed, all passed! @@ -1027,7 +1027,7 @@ ed 54.json completed, all passed! ed 55.json completed, all passed! ed 56.json completed, all passed! ed 57.json completed, all passed! -ed 58.json completed, all passed! +ed 58.json completed: 6 passed, 994 FAILED ed 59.json completed, all passed! ed 5a.json completed, all passed! ed 5b.json completed, all passed! @@ -1035,18 +1035,18 @@ ed 5c.json completed, all passed! ed 5d.json completed, all passed! ed 5e.json completed, all passed! ed 5f.json completed, all passed! -ed 60.json completed, all passed! +ed 60.json completed: 2 passed, 998 FAILED ed 61.json completed, all passed! ed 62.json completed, all passed! -ed 63.json completed: 0 passed, 1000 FAILED +ed 63.json completed, all passed! ed 64.json completed, all passed! ed 65.json completed, all passed! ed 66.json completed, all passed! ed 67.json completed, all passed! -ed 68.json completed, all passed! +ed 68.json completed: 4 passed, 996 FAILED ed 69.json completed, all passed! ed 6a.json completed, all passed! -ed 6b.json completed: 0 passed, 1000 FAILED +ed 6b.json completed, all passed! ed 6c.json completed, all passed! ed 6d.json completed, all passed! ed 6e.json completed, all passed! @@ -1058,18 +1058,18 @@ ed 73.json completed, all passed! ed 74.json completed, all passed! ed 75.json completed, all passed! ed 76.json completed, all passed! -ed 77.json completed: 0 passed, 1000 FAILED -ed 78.json completed, all passed! +ed 77.json completed, all passed! +ed 78.json completed: 7 passed, 993 FAILED ed 79.json completed, all passed! ed 7a.json completed, all passed! ed 7b.json completed, all passed! ed 7c.json completed, all passed! ed 7d.json completed, all passed! ed 7e.json completed, all passed! -ed 7f.json completed: 0 passed, 1000 FAILED +ed 7f.json completed, all passed! ed a0.json completed, all passed! ed a1.json completed: 0 passed, 1000 FAILED -ed a2.json completed: 13 passed, 987 FAILED +ed a2.json completed: 0 passed, 1000 FAILED ed a3.json completed: 0 passed, 1000 FAILED ed a8.json completed, all passed! ed a9.json completed: 0 passed, 1000 FAILED @@ -1234,7 +1234,7 @@ fd 82.json completed, all passed! fd 83.json completed, all passed! fd 84.json completed, all passed! fd 85.json completed, all passed! -fd 86.json completed: 0 passed, 1000 FAILED +fd 86.json completed, all passed! fd 87.json completed, all passed! fd 88.json completed, all passed! fd 89.json completed, all passed! @@ -1242,7 +1242,7 @@ fd 8a.json completed, all passed! fd 8b.json completed, all passed! fd 8c.json completed, all passed! fd 8d.json completed, all passed! -fd 8e.json completed: 0 passed, 1000 FAILED +fd 8e.json completed, all passed! fd 8f.json completed, all passed! fd 90.json completed, all passed! fd 91.json completed, all passed! @@ -1250,7 +1250,7 @@ fd 92.json completed, all passed! fd 93.json completed, all passed! fd 94.json completed, all passed! fd 95.json completed, all passed! -fd 96.json completed: 0 passed, 1000 FAILED +fd 96.json completed, all passed! fd 97.json completed, all passed! fd 98.json completed, all passed! fd 99.json completed, all passed! @@ -1258,7 +1258,7 @@ fd 9a.json completed, all passed! fd 9b.json completed, all passed! fd 9c.json completed, all passed! fd 9d.json completed, all passed! -fd 9e.json completed: 0 passed, 1000 FAILED +fd 9e.json completed, all passed! fd 9f.json completed, all passed! fd a0.json completed, all passed! fd a1.json completed, all passed! @@ -1266,7 +1266,7 @@ fd a2.json completed, all passed! fd a3.json completed, all passed! fd a4.json completed, all passed! fd a5.json completed, all passed! -fd a6.json completed: 0 passed, 1000 FAILED +fd a6.json completed, all passed! fd a7.json completed, all passed! fd a8.json completed, all passed! fd a9.json completed, all passed! @@ -1274,7 +1274,7 @@ fd aa.json completed, all passed! fd ab.json completed, all passed! fd ac.json completed, all passed! fd ad.json completed, all passed! -fd ae.json completed: 0 passed, 1000 FAILED +fd ae.json completed, all passed! fd af.json completed, all passed! fd b0.json completed, all passed! fd b1.json completed, all passed! @@ -1282,7 +1282,7 @@ fd b2.json completed, all passed! fd b3.json completed, all passed! fd b4.json completed, all passed! fd b5.json completed, all passed! -fd b6.json completed: 0 passed, 1000 FAILED +fd b6.json completed, all passed! fd b7.json completed, all passed! fd b8.json completed, all passed! fd b9.json completed, all passed! @@ -1290,7 +1290,7 @@ fd ba.json completed, all passed! fd bb.json completed, all passed! fd bc.json completed, all passed! fd bd.json completed, all passed! -fd be.json completed: 0 passed, 1000 FAILED +fd be.json completed, all passed! fd bf.json completed, all passed! fd c0.json completed, all passed! fd c1.json completed, all passed! @@ -1574,7 +1574,7 @@ fd d7.json completed, all passed! fd d8.json completed, all passed! fd d9.json completed, all passed! fd da.json completed, all passed! -fd db.json completed, all passed! +fd db.json completed: 4 passed, 996 FAILED fd dc.json completed, all passed! fd de.json completed, all passed! fd df.json completed, all passed! @@ -1611,5 +1611,5 @@ fd ff.json completed, all passed! fe.json completed, all passed! ff.json completed, all passed! -passed: 1574638, failed: 35362, total 98% -completed in 1m 19s +passed: 1584670, failed: 25330, total 98% +completed in 0m 8s diff --git a/tests/rad_tests/run_all.sh b/tests/rad_tests/run_all.sh index 8152dc8..7bca526 100755 --- a/tests/rad_tests/run_all.sh +++ b/tests/rad_tests/run_all.sh @@ -2,10 +2,11 @@ COMMIT=$(git rev-parse HEAD) DATE=$(date --iso) LOCATION=$(dirname ${BASH_SOURCE[0]}) +FLAGS=("--check-undocumented" "--check-timings") RESULTS=latest.txt { cd $LOCATION - echo "Last run on $DATE at commit $COMMIT" | tee $RESULTS + echo "Last run on $DATE at commit $COMMIT" with flags ${FLAGS[@]} | tee $RESULTS echo "" | tee -a $RESULTS - cargo run -- -q --testsuite "../jsmoo/misc/tests/GeneratedTests/z80/v1/" --check-undocumented --check-timings | tee -a $RESULTS + cargo run -- -q --testsuite "../jsmoo/misc/tests/GeneratedTests/z80/v1/" ${FLAGS[@]} | tee -a $RESULTS } diff --git a/tests/rad_tests/src/main.rs b/tests/rad_tests/src/main.rs index f4df1d8..7cbe137 100644 --- a/tests/rad_tests/src/main.rs +++ b/tests/rad_tests/src/main.rs @@ -1,7 +1,5 @@ const DEFAULT_RAD_TESTS: &str = "tests/jsmoo/misc/tests/GeneratedTests/z80/v1/"; -use std::rc::Rc; -use std::cell::RefCell; use std::io::prelude::*; use std::fmt::{Debug, UpperHex}; use std::path::PathBuf; @@ -11,16 +9,21 @@ use std::fs::{self, File}; use clap::Parser; use flate2::read::GzDecoder; use serde_derive::Deserialize; -use femtos::Frequency; +use femtos::{Instant, Frequency}; -use moa_core::{System, Error, MemoryBlock, Bus, BusPort, Address, Addressable, Steppable, Device}; +use emulator_hal::{Step, BusAccess}; +use emulator_hal_memory::MemoryBlock; -use moa_z80::{Z80, Z80Type}; -use moa_z80::instructions::InterruptMode; -use moa_z80::state::Flags; -use moa_z80::state::Status; +use moa_z80::{Z80, Z80Type, InterruptMode, Flags, Status}; +#[derive(Clone, Debug)] +enum Error { + Assertion(String), + Bus(String), + Step(String), +} + #[derive(Parser)] struct Args { /// Filter the tests by gzip file name @@ -145,27 +148,29 @@ impl TestCase { } -fn init_execute_test(cputype: Z80Type, state: &TestState, ports: &[TestPort]) -> 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 +180,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 +220,14 @@ 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 +236,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 +274,23 @@ 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 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 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,34 +299,37 @@ 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!( - "expected instruction to take {} cycles, but took {}", - case.cycles.len(), - cycles - ))); - } + // TODO re-enable. not sure why it can't divide here + //let cycles = clock_elapsed / cpu.frequency.period_duration(); + //if cycles != case.cycles.len() { + // return Err(Error::Assertion(format!( + // "expected instruction to take {} cycles, but took {}", + // case.cycles.len(), + // cycles + // ))); + //} } Ok(()) } 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 +338,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); + cpu.dump_state(Instant::START); } println!("FAILED: {:?}", err); } From 1bb66e330884644fa0443fcfc14fb8dcbe7b6888 Mon Sep 17 00:00:00 2001 From: transistor Date: Sun, 31 Mar 2024 22:33:51 -0700 Subject: [PATCH 2/5] Updated emulator-hal --- README.md | 6 ++++-- emulator/libraries/emulator-hal | 2 +- todo.txt | 18 ++++++++++++------ 3 files changed, 17 insertions(+), 9 deletions(-) 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/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/todo.txt b/todo.txt index 5609095..dca2502 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 dumping functions +* 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? + +* 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? From 59199533ebebe92bd4e4875d9952503c369f07c3 Mon Sep 17 00:00:00 2001 From: transistor Date: Sun, 7 Apr 2024 10:38:41 -0700 Subject: [PATCH 3/5] Added a hacky Signalable trait to replace the Z80 signals --- Cargo.lock | 3 +- emulator/core/src/devices.rs | 15 ++++ emulator/core/src/lib.rs | 5 +- emulator/core/src/memory.rs | 26 +++++-- emulator/cpus/z80/src/decode.rs | 69 ++++++++++--------- emulator/cpus/z80/src/emuhal.rs | 12 ++-- emulator/cpus/z80/src/execute.rs | 55 ++++++++------- emulator/cpus/z80/src/lib.rs | 4 +- emulator/cpus/z80/src/moa.rs | 46 +++++++++---- emulator/cpus/z80/src/state.rs | 47 ++++++------- .../genesis/src/peripherals/coprocessor.rs | 30 +++++--- emulator/systems/genesis/src/system.rs | 18 ++--- emulator/systems/trs80/src/system.rs | 2 +- tests/rad_tests/src/main.rs | 22 ++++-- 14 files changed, 215 insertions(+), 139 deletions(-) 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/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/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/z80/src/decode.rs b/emulator/cpus/z80/src/decode.rs index 8dde2b1..330ed77 100644 --- a/emulator/cpus/z80/src/decode.rs +++ b/emulator/cpus/z80/src/decode.rs @@ -1,7 +1,7 @@ use core::fmt::Write; use emulator_hal::{BusAccess, Instant as EmuInstant}; -use crate::state::{Z80Error, Z80Address} ; +use crate::state::{Z80Error, Z80Address}; use crate::instructions::{ Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, LoadTarget, UndocumentedCopy, Instruction, @@ -55,27 +55,16 @@ impl Z80Decoder { Ok(decoder.decoder) } - /* - pub fn format_instruction_bytes(&mut self) -> String { - let mut ins_data = String::new(); - for offset in 0..self.decoder.end.saturating_sub(self.decoder.start) { - write!(ins_data, "{:02x} ", self.bus.read_u8(self.clock, self.decoder.start + offset).unwrap()).unwrap() - } - ins_data - } - - pub fn dump_decoded(&mut self) { - let ins_data = self.format_instruction_bytes(); - println!("{:#06x}: {}\n\t{:?}\n", self.decoder.start, ins_data, self.decoder.instruction); - } - - pub fn dump_disassembly(&mut self, start: Z80Address, length: Z80Address) { + pub fn dump_disassembly(bus: &mut Bus, start: Z80Address, length: Z80Address) + where + Bus: BusAccess, + { let mut next = start; while next < (start + length) { - match self.decode_at(self.clock, next) { - Ok(()) => { - self.dump_decoded(); - next = self.decoder.end; + match Z80Decoder::decode_at(bus, Bus::Instant::START, next) { + Ok(mut decoder) => { + decoder.dump_decoded(bus); + next = decoder.end; }, Err(err) => { println!("{:?}", err); @@ -84,7 +73,25 @@ impl Z80Decoder { } } } - */ + + 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> @@ -107,11 +114,7 @@ where Ok(()) } - pub fn decode_bare( - &mut self, - ins: u8, - extra_instruction_bytes: u16, - ) -> Result { + 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) { @@ -559,11 +562,7 @@ where } } - fn decode_index_target( - &mut self, - 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))), @@ -578,7 +577,9 @@ where fn read_instruction_byte(&mut self) -> Result { - let byte = self.bus.read_u8(self.clock, self.decoder.end) + 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) @@ -587,7 +588,9 @@ where 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) + *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); } diff --git a/emulator/cpus/z80/src/emuhal.rs b/emulator/cpus/z80/src/emuhal.rs index 82154f1..1651ee2 100644 --- a/emulator/cpus/z80/src/emuhal.rs +++ b/emulator/cpus/z80/src/emuhal.rs @@ -1,4 +1,3 @@ - use emulator_hal::{BusAccess, Instant as EmuInstant, Error as EmuError, Step, Inspect, Debug, IntoAddress}; use crate::state::{Z80, Z80Error, Z80Address, Status}; @@ -23,10 +22,9 @@ where fn step(&mut self, now: Self::Instant, bus: &mut Bus) -> Result { let mut executor = self.begin(now, bus)?; - executor.step_one()?; + let clocks = executor.step_one()?; self.previous_cycle = executor.end(); - // TODO fix this - Ok(now) + Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32) } } @@ -51,11 +49,9 @@ where fn step(&mut self, now: Self::Instant, bus: (&mut MemBus, &mut IoBus)) -> Result { let executor = self.begin(now, bus)?; - executor.step_one()?; + let clocks = executor.step_one()?; self.previous_cycle = executor.end(); - // TODO fix this - Ok(now) + 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 171705d..7475ed5 100644 --- a/emulator/cpus/z80/src/execute.rs +++ b/emulator/cpus/z80/src/execute.rs @@ -5,7 +5,7 @@ use crate::instructions::{ Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister, IndexRegisterHalf, Size, Direction, UndocumentedCopy, }; -use crate::state::{Z80, Z80Error, Z80State, Z80Address, Status, Flags}; +use crate::state::{Z80, Z80Error, Z80State, Z80Signals, Z80Address, Status, Flags}; use crate::timing::Z80InstructionCycles; use crate::debugger::Z80Debugger; @@ -41,12 +41,17 @@ impl Z80 where Instant: EmuInstant, { - pub(crate) fn begin<'a, Bus>(&'a mut self, clock: Instant, bus: &'a mut Bus) -> Result, Z80Error> + 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, @@ -61,6 +66,7 @@ where Bus: BusAccess, { state: &'a mut Z80State, + signals: &'a mut Z80Signals, debugger: &'a mut Z80Debugger, cycle: Z80Cycle, bus: Bus, @@ -71,28 +77,22 @@ where Bus: BusAccess, Instant: EmuInstant, { - pub(crate) fn end(mut self) -> Z80Cycle { + pub(crate) fn end(self) -> Z80Cycle { self.cycle } pub(crate) fn step_one(&mut self) -> Result { - // TODO restore the reset and bus request signals - //let clocks = if self.reset.get() { - // self.reset()? - //} else if self.bus_request.get() { - // 4 - //} else { - // self.step_internal(self.cycle.current_clock)? - //}; - - //Ok(self.frequency.period_duration() * clocks as u64) - - // TODO remove this when done - let clocks = self.step_internal(self.cycle.current_clock)?; + let clocks = if self.signals.reset { + self.reset()? + } else if self.signals.bus_request { + 4 + } else { + self.step_internal()? + }; Ok(clocks) } - fn step_internal(&mut self, clock: Instant) -> Result { + fn step_internal(&mut self) -> Result { match self.state.status { Status::Init => self.init(), Status::Halted => Err(Z80Error::Halted), @@ -630,7 +630,9 @@ where let parity = if count != 0 { Flags::Parity as u8 } else { 0 }; self.set_flags(mask, parity); - if (self.cycle.decoder.instruction == Instruction::LDIR || self.cycle.decoder.instruction == Instruction::LDDR) && count != 0 { + if (self.cycle.decoder.instruction == Instruction::LDIR || self.cycle.decoder.instruction == Instruction::LDDR) + && count != 0 + { self.cycle.took_branch = true; self.state.pc -= 2; } @@ -1126,13 +1128,17 @@ where fn read_port_u8(&mut self, addr: u16) -> Result { self.increment_refresh(1); - Ok(self.bus.read_u8(self.cycle.current_clock, addr as Z80Address) + 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.bus.write_u8(self.cycle.current_clock, addr as Z80Address, value) + Ok(self + .bus + .write_u8(self.cycle.current_clock, addr as Z80Address, value) .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?) } @@ -1144,7 +1150,9 @@ where 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) + *byte = self + .bus + .read_u8(self.cycle.current_clock, addr & 0xFFFF) .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; addr = addr.wrapping_add(1); } @@ -1159,7 +1167,8 @@ where 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) + self.bus + .write_u8(self.cycle.current_clock, addr & 0xFFFF, *byte) .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; addr = addr.wrapping_add(1); } @@ -1172,7 +1181,7 @@ where //if let Some(io) = self.ioport.as_mut() { // Ok(io.read_u8(self.cycle.current_clock, addr)?) //} else { - Ok(0) + Ok(0) //} } diff --git a/emulator/cpus/z80/src/lib.rs b/emulator/cpus/z80/src/lib.rs index f7e857d..bbaa30f 100644 --- a/emulator/cpus/z80/src/lib.rs +++ b/emulator/cpus/z80/src/lib.rs @@ -1,11 +1,11 @@ mod debugger; mod decode; +mod emuhal; mod execute; mod instructions; +mod moa; mod state; mod timing; -mod moa; -mod emuhal; pub use crate::state::{Z80, Z80Type, Z80Error, Z80State, Status, Flags}; pub use crate::decode::Z80Decoder; diff --git a/emulator/cpus/z80/src/moa.rs b/emulator/cpus/z80/src/moa.rs index d8e573f..ef99dca 100644 --- a/emulator/cpus/z80/src/moa.rs +++ b/emulator/cpus/z80/src/moa.rs @@ -1,8 +1,8 @@ - +use std::any::Any; use femtos::{Instant, Duration}; use emulator_hal::{BusAdapter, Instant as EmuInstant}; -use moa_core::{System, Error, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable}; +use moa_core::{System, Error, Address, Steppable, Addressable, Interruptable, Signalable, Signal, Debuggable, Transmutable}; use crate::{Z80, Z80Error, Z80Decoder}; use crate::instructions::Register; @@ -22,13 +22,32 @@ where } fn on_error(&mut self, system: &System) { - self.dump_state(system.clock); + let bus = &mut *system.bus.borrow_mut(); + let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err))); + self.dump_state(system.clock, &mut adapter); } } impl Interruptable for Z80 {} +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 Z80 { fn as_steppable(&mut self) -> Option<&mut dyn Steppable> { Some(self) @@ -41,6 +60,11 @@ impl Transmutable for Z80 { 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 { @@ -80,16 +104,16 @@ impl Debuggable for Z80 { 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.state.pc)?; - // TODO disabled until decoder is fixed - //self.decoder.dump_decoded(&mut self.port); - self.dump_state(system.clock); + self.previous_cycle.decoder.dump_decoded(&mut adapter); + self.dump_state(system.clock, &mut adapter); Ok(()) } - fn print_disassembly(&mut self, _system: &System, addr: Address, count: usize) { - // TODO disabled until decoder is fixed - //let mut decoder = Z80Decoder::default(); - //decoder.dump_disassembly(&mut self.port, addr as u16, count as u16); + 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 { @@ -102,5 +126,3 @@ impl Debuggable for Z80 { Ok(false) } } - - diff --git a/emulator/cpus/z80/src/state.rs b/emulator/cpus/z80/src/state.rs index 6091980..614c9e2 100644 --- a/emulator/cpus/z80/src/state.rs +++ b/emulator/cpus/z80/src/state.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use std::cell::RefCell; use femtos::{Instant, Frequency}; -use emulator_hal::Instant as EmuInstant; +use emulator_hal::{Instant as EmuInstant, BusAccess}; use moa_core::{Address, Bus, BusPort}; use moa_signals::Signal; @@ -92,6 +92,12 @@ impl Z80State { } } +#[derive(Clone, Debug, Default)] +pub struct Z80Signals { + pub reset: bool, + pub bus_request: bool, +} + #[derive(Clone, Debug, thiserror::Error)] pub enum Z80Error /* */ { #[error("cpu halted")] @@ -120,8 +126,7 @@ pub struct Z80 { pub state: Z80State, pub debugger: Z80Debugger, pub previous_cycle: Z80Cycle, - //pub port: BusPort, - //pub ioport: Option, + pub signals: Z80Signals, // TODO activate later //pub reset: Signal, //pub bus_request: Signal, @@ -131,34 +136,22 @@ impl Z80 where Instant: EmuInstant, { - pub fn new(cputype: Z80Type, frequency: Frequency /*, port: BusPort, ioport: Option*/) -> Self { + pub fn new(cputype: Z80Type, frequency: Frequency) -> Self { Self { cputype, frequency, state: Z80State::default(), debugger: Z80Debugger::default(), previous_cycle: Z80Cycle::at_time(Instant::START), - //port, - //ioport, + 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), } } @@ -168,7 +161,10 @@ where self.debugger = Z80Debugger::default(); } - 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); @@ -207,12 +203,11 @@ where println!("I: {:#04x} R: {:#04x}", self.state.i, self.state.r); println!("IM: {:?} IFF1: {:?} IFF2: {:?}", self.state.im, self.state.iff1, self.state.iff2); - // TODO disabled until function is reimplemented - //println!( - // "Current Instruction: {} {:?}", - // self.decoder.format_instruction_bytes(&mut self.port), - // self.decoder.instruction - //); + println!( + "Current Instruction: {} {:?}", + self.previous_cycle.decoder.format_instruction_bytes(bus), + self.previous_cycle.decoder.instruction + ); println!("Previous Instruction: {:?}", self.previous_cycle.decoder.instruction); println!(); // TODO disabled until function is reimplemented diff --git a/emulator/systems/genesis/src/peripherals/coprocessor.rs b/emulator/systems/genesis/src/peripherals/coprocessor.rs index ab3cf8a..89a2986 100644 --- a/emulator/systems/genesis/src/peripherals/coprocessor.rs +++ b/emulator/systems/genesis/src/peripherals/coprocessor.rs @@ -1,23 +1,27 @@ use std::rc::Rc; use std::cell::{Cell, RefCell}; +use std::any::Any; use femtos::Instant; -use moa_core::{Bus, Error, Address, Addressable, Transmutable}; -use moa_signals::Signal; +use moa_core::{Bus, Device, Error, Address, Addressable, Signal, Transmutable}; +//use moa_signals::Signal; +use moa_z80::Z80; const DEV_NAME: &str = "coprocessor"; pub struct CoprocessorCoordinator { - bus_request: Signal, - reset: Signal, + z80: Device, + //bus_request: Signal, + //reset: Signal, } impl CoprocessorCoordinator { - pub fn new(reset: Signal, bus_request: Signal) -> Self { + pub fn new(z80: Device) -> Self { Self { - bus_request, - reset, + z80, + //bus_request, + //reset, } } } @@ -30,7 +34,9 @@ impl Addressable for CoprocessorCoordinator { fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { 0x100 => { - data[0] = if self.bus_request.get() && self.reset.get() { + let mut device = self.z80.borrow_mut(); + let z80 = device.as_signalable().unwrap(); + data[0] = if z80.signal(Signal::BusRequest).unwrap_or(false) && z80.signal(Signal::Reset).unwrap_or(false) { 0x01 } else { 0x00 @@ -49,10 +55,14 @@ impl Addressable for CoprocessorCoordinator { match addr { 0x000 => { /* ROM vs DRAM mode */ }, 0x100 => { - self.bus_request.set(data[0] != 0); + let mut device = self.z80.borrow_mut(); + let z80 = device.as_signalable().unwrap(); + z80.set_signal(Signal::BusRequest, data[0] == 0)?; }, 0x200 => { - self.reset.set(data[0] == 0); + let mut device = self.z80.borrow_mut(); + let z80 = device.as_signalable().unwrap(); + z80.set_signal(Signal::Reset, data[0] == 0)?; }, _ => { log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); diff --git a/emulator/systems/genesis/src/system.rs b/emulator/systems/genesis/src/system.rs index d57e2e5..e9cffec 100644 --- a/emulator/systems/genesis/src/system.rs +++ b/emulator/systems/genesis/src/system.rs @@ -2,7 +2,7 @@ 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; @@ -68,11 +68,13 @@ 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(); - reset.set(true); - bus_request.set(true); + let coproc = Z80::from_type(Z80Type::Z80, Frequency::from_hz(3_579_545)); + //let mut reset = coproc.reset.clone(); + //let mut bus_request = coproc.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,14 +82,14 @@ 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)?; let interrupt = controllers.get_interrupt_signal(); system.add_addressable_device(0x00a10000, Device::new(controllers))?; - let coproc = CoprocessorCoordinator::new(reset, bus_request); + let coproc = CoprocessorCoordinator::new(coproc); system.add_addressable_device(0x00a11000, Device::new(coproc))?; let vdp = Ym7101::new(host, interrupt, coproc_sn_sound)?; diff --git a/emulator/systems/trs80/src/system.rs b/emulator/systems/trs80/src/system.rs index d692853..fd4c9ce 100644 --- a/emulator/systems/trs80/src/system.rs +++ b/emulator/systems/trs80/src/system.rs @@ -44,7 +44,7 @@ pub fn build_trs80(host: &mut H, options: Trs80Options) -> Result Result<(Z80, MemoryBlock, MemoryBlock), Error> { +fn init_execute_test( + cputype: Z80Type, + state: &TestState, + ports: &[TestPort], +) -> Result<(Z80, MemoryBlock, MemoryBlock), Error> { // Insert basic initialization let len = 0x1_0000; let mut data = Vec::with_capacity(len); @@ -220,7 +224,8 @@ fn load_state( // Load data bytes into memory for (addr, byte) in initial.ram.iter() { - memory.write_u8(Instant::START, *addr, *byte) + memory + .write_u8(Instant::START, *addr, *byte) .map_err(|err| Error::Bus(format!("{:?}", err)))?; } @@ -281,7 +286,8 @@ fn assert_state( // Load data bytes into memory for (addr, byte) in expected.ram.iter() { - let actual = memory.read_u8(Instant::START, *addr) + let actual = memory + .read_u8(Instant::START, *addr) .map_err(|err| Error::Bus(format!("{:?}", err)))?; assert_value(actual, *byte, &format!("ram at {:x}", addr))?; } @@ -289,7 +295,8 @@ fn assert_state( // Load data bytes into io space for port in ports.iter() { if port.atype == "w" { - let actual = io.read_u8(Instant::START, port.addr) + 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))?; } @@ -306,7 +313,8 @@ fn step_cpu_and_assert( args: &Args, ) -> Result<(), Error> { //let clock_elapsed = cpu.step((memory, io))?; - let clock_elapsed = cpu.step(Instant::START, memory) + let clock_elapsed = cpu + .step(Instant::START, memory) .map_err(|err| Error::Step(format!("{:?}", err)))?; assert_state(cpu, memory, io, &case.final_state, args.check_extra_flags, &case.ports)?; @@ -338,8 +346,8 @@ fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> { if args.debug { case.dump(); println!(); - initial_cpu.dump_state(Instant::START); - cpu.dump_state(Instant::START); + initial_cpu.dump_state(Instant::START, &mut memory); + cpu.dump_state(Instant::START, &mut memory); } println!("FAILED: {:?}", err); } From 1c5ad3999afa5591ec8fcbcadf4797514c390031 Mon Sep 17 00:00:00 2001 From: transistor Date: Sun, 7 Apr 2024 19:52:10 -0700 Subject: [PATCH 4/5] Minor fixes --- emulator/core/src/error.rs | 6 ++++++ emulator/cpus/m68k/src/moa.rs | 15 +++++++++++---- emulator/frontends/minifb/src/bin/moa-trs80.rs | 2 +- todo.txt | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) 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/cpus/m68k/src/moa.rs b/emulator/cpus/m68k/src/moa.rs index d7b4b13..e65d939 100644 --- a/emulator/cpus/m68k/src/moa.rs +++ b/emulator/cpus/m68k/src/moa.rs @@ -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(()) } 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/todo.txt b/todo.txt index dca2502..fdaee8f 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,7 @@ -* fix the Z80 dumping functions * 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 From 471695aff58e3980c03da1512319d0830628c857 Mon Sep 17 00:00:00 2001 From: transistor Date: Sun, 7 Apr 2024 20:16:55 -0700 Subject: [PATCH 5/5] Fixed timing tests and added no io tests option --- tests/rad_tests/latest.txt | 46 ++++++++++++++++++------------------- tests/rad_tests/src/main.rs | 28 +++++++++++++--------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/tests/rad_tests/latest.txt b/tests/rad_tests/latest.txt index 56b4166..3fd91ed 100644 --- a/tests/rad_tests/latest.txt +++ b/tests/rad_tests/latest.txt @@ -1,4 +1,4 @@ -Last run on 2024-03-31 at commit 6e7e315808228e03eaf8ad2e8152c087710f1d28 with flags --check-undocumented --check-timings +Last run on 2024-04-07 at commit 1c5ad3999afa5591ec8fcbcadf4797514c390031 with flags --check-undocumented --check-timings 00.json completed, all passed! 01.json completed, all passed! @@ -614,7 +614,7 @@ dd 82.json completed, all passed! dd 83.json completed, all passed! dd 84.json completed, all passed! dd 85.json completed, all passed! -dd 86.json completed, all passed! +dd 86.json completed: 0 passed, 1000 FAILED dd 87.json completed, all passed! dd 88.json completed, all passed! dd 89.json completed, all passed! @@ -622,7 +622,7 @@ dd 8a.json completed, all passed! dd 8b.json completed, all passed! dd 8c.json completed, all passed! dd 8d.json completed, all passed! -dd 8e.json completed, all passed! +dd 8e.json completed: 0 passed, 1000 FAILED dd 8f.json completed, all passed! dd 90.json completed, all passed! dd 91.json completed, all passed! @@ -630,7 +630,7 @@ dd 92.json completed, all passed! dd 93.json completed, all passed! dd 94.json completed, all passed! dd 95.json completed, all passed! -dd 96.json completed, all passed! +dd 96.json completed: 0 passed, 1000 FAILED dd 97.json completed, all passed! dd 98.json completed, all passed! dd 99.json completed, all passed! @@ -638,7 +638,7 @@ dd 9a.json completed, all passed! dd 9b.json completed, all passed! dd 9c.json completed, all passed! dd 9d.json completed, all passed! -dd 9e.json completed, all passed! +dd 9e.json completed: 0 passed, 1000 FAILED dd 9f.json completed, all passed! dd a0.json completed, all passed! dd a1.json completed, all passed! @@ -646,7 +646,7 @@ dd a2.json completed, all passed! dd a3.json completed, all passed! dd a4.json completed, all passed! dd a5.json completed, all passed! -dd a6.json completed, all passed! +dd a6.json completed: 0 passed, 1000 FAILED dd a7.json completed, all passed! dd a8.json completed, all passed! dd a9.json completed, all passed! @@ -654,7 +654,7 @@ dd aa.json completed, all passed! dd ab.json completed, all passed! dd ac.json completed, all passed! dd ad.json completed, all passed! -dd ae.json completed, all passed! +dd ae.json completed: 0 passed, 1000 FAILED dd af.json completed, all passed! dd b0.json completed, all passed! dd b1.json completed, all passed! @@ -662,7 +662,7 @@ dd b2.json completed, all passed! dd b3.json completed, all passed! dd b4.json completed, all passed! dd b5.json completed, all passed! -dd b6.json completed, all passed! +dd b6.json completed: 0 passed, 1000 FAILED dd b7.json completed, all passed! dd b8.json completed, all passed! dd b9.json completed, all passed! @@ -670,7 +670,7 @@ dd ba.json completed, all passed! dd bb.json completed, all passed! dd bc.json completed, all passed! dd bd.json completed, all passed! -dd be.json completed, all passed! +dd be.json completed: 0 passed, 1000 FAILED dd bf.json completed, all passed! dd c0.json completed, all passed! dd c1.json completed, all passed! @@ -1038,7 +1038,7 @@ ed 5f.json completed, all passed! ed 60.json completed: 2 passed, 998 FAILED ed 61.json completed, all passed! ed 62.json completed, all passed! -ed 63.json completed, all passed! +ed 63.json completed: 0 passed, 1000 FAILED ed 64.json completed, all passed! ed 65.json completed, all passed! ed 66.json completed, all passed! @@ -1046,7 +1046,7 @@ ed 67.json completed, all passed! ed 68.json completed: 4 passed, 996 FAILED ed 69.json completed, all passed! ed 6a.json completed, all passed! -ed 6b.json completed, all passed! +ed 6b.json completed: 0 passed, 1000 FAILED ed 6c.json completed, all passed! ed 6d.json completed, all passed! ed 6e.json completed, all passed! @@ -1058,7 +1058,7 @@ ed 73.json completed, all passed! ed 74.json completed, all passed! ed 75.json completed, all passed! ed 76.json completed, all passed! -ed 77.json completed, all passed! +ed 77.json completed: 0 passed, 1000 FAILED ed 78.json completed: 7 passed, 993 FAILED ed 79.json completed, all passed! ed 7a.json completed, all passed! @@ -1066,7 +1066,7 @@ ed 7b.json completed, all passed! ed 7c.json completed, all passed! ed 7d.json completed, all passed! ed 7e.json completed, all passed! -ed 7f.json completed, all passed! +ed 7f.json completed: 0 passed, 1000 FAILED ed a0.json completed, all passed! ed a1.json completed: 0 passed, 1000 FAILED ed a2.json completed: 0 passed, 1000 FAILED @@ -1234,7 +1234,7 @@ fd 82.json completed, all passed! fd 83.json completed, all passed! fd 84.json completed, all passed! fd 85.json completed, all passed! -fd 86.json completed, all passed! +fd 86.json completed: 0 passed, 1000 FAILED fd 87.json completed, all passed! fd 88.json completed, all passed! fd 89.json completed, all passed! @@ -1242,7 +1242,7 @@ fd 8a.json completed, all passed! fd 8b.json completed, all passed! fd 8c.json completed, all passed! fd 8d.json completed, all passed! -fd 8e.json completed, all passed! +fd 8e.json completed: 0 passed, 1000 FAILED fd 8f.json completed, all passed! fd 90.json completed, all passed! fd 91.json completed, all passed! @@ -1250,7 +1250,7 @@ fd 92.json completed, all passed! fd 93.json completed, all passed! fd 94.json completed, all passed! fd 95.json completed, all passed! -fd 96.json completed, all passed! +fd 96.json completed: 0 passed, 1000 FAILED fd 97.json completed, all passed! fd 98.json completed, all passed! fd 99.json completed, all passed! @@ -1258,7 +1258,7 @@ fd 9a.json completed, all passed! fd 9b.json completed, all passed! fd 9c.json completed, all passed! fd 9d.json completed, all passed! -fd 9e.json completed, all passed! +fd 9e.json completed: 0 passed, 1000 FAILED fd 9f.json completed, all passed! fd a0.json completed, all passed! fd a1.json completed, all passed! @@ -1266,7 +1266,7 @@ fd a2.json completed, all passed! fd a3.json completed, all passed! fd a4.json completed, all passed! fd a5.json completed, all passed! -fd a6.json completed, all passed! +fd a6.json completed: 0 passed, 1000 FAILED fd a7.json completed, all passed! fd a8.json completed, all passed! fd a9.json completed, all passed! @@ -1274,7 +1274,7 @@ fd aa.json completed, all passed! fd ab.json completed, all passed! fd ac.json completed, all passed! fd ad.json completed, all passed! -fd ae.json completed, all passed! +fd ae.json completed: 0 passed, 1000 FAILED fd af.json completed, all passed! fd b0.json completed, all passed! fd b1.json completed, all passed! @@ -1282,7 +1282,7 @@ fd b2.json completed, all passed! fd b3.json completed, all passed! fd b4.json completed, all passed! fd b5.json completed, all passed! -fd b6.json completed, all passed! +fd b6.json completed: 0 passed, 1000 FAILED fd b7.json completed, all passed! fd b8.json completed, all passed! fd b9.json completed, all passed! @@ -1290,7 +1290,7 @@ fd ba.json completed, all passed! fd bb.json completed, all passed! fd bc.json completed, all passed! fd bd.json completed, all passed! -fd be.json completed, all passed! +fd be.json completed: 0 passed, 1000 FAILED fd bf.json completed, all passed! fd c0.json completed, all passed! fd c1.json completed, all passed! @@ -1611,5 +1611,5 @@ fd ff.json completed, all passed! fe.json completed, all passed! ff.json completed, all passed! -passed: 1584670, failed: 25330, total 98% -completed in 0m 8s +passed: 1564670, failed: 45330, total 97% +completed in 0m 9s diff --git a/tests/rad_tests/src/main.rs b/tests/rad_tests/src/main.rs index 82d7eb7..ef42939 100644 --- a/tests/rad_tests/src/main.rs +++ b/tests/rad_tests/src/main.rs @@ -46,6 +46,9 @@ struct Args { /// Check instruction timings #[clap(short = 't', long)] check_timings: bool, + /// Don't check I/O instructions + #[clap(short = 'i', long)] + no_check_io: bool, /// Directory to the test suite to run #[clap(long, default_value = DEFAULT_RAD_TESTS)] testsuite: String, @@ -284,7 +287,7 @@ fn assert_state( assert_value(cpu.state.iff1 as u8, expected.iff1, "iff1")?; assert_value(cpu.state.iff2 as u8, expected.iff2, "iff2")?; - // Load data bytes into memory + // Compare data bytes in memory for (addr, byte) in expected.ram.iter() { let actual = memory .read_u8(Instant::START, *addr) @@ -292,7 +295,7 @@ fn assert_state( 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 @@ -319,15 +322,14 @@ fn step_cpu_and_assert( assert_state(cpu, memory, io, &case.final_state, args.check_extra_flags, &case.ports)?; if args.check_timings { - // TODO re-enable. not sure why it can't divide here - //let cycles = clock_elapsed / cpu.frequency.period_duration(); - //if cycles != case.cycles.len() { - // return Err(Error::Assertion(format!( - // "expected instruction to take {} cycles, but took {}", - // case.cycles.len(), - // cycles - // ))); - //} + 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 + ))); + } } Ok(()) @@ -379,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);