From f3d1fd0ae2410a164c92150cc3caa0f525cbb4b7 Mon Sep 17 00:00:00 2001 From: transistor Date: Sat, 13 May 2023 19:41:20 -0700 Subject: [PATCH] Added IM, IFF1, and IFF2 checking to Z80 tests, and fixed issues with Z80 impl --- emulator/cpus/z80/src/decode.rs | 2 +- emulator/cpus/z80/src/execute.rs | 15 ++++++++++++--- emulator/cpus/z80/src/lib.rs | 1 + emulator/cpus/z80/src/state.rs | 28 +++++++++++++++++++++++----- tests/rad_tests/latest.txt | 12 ++++++------ tests/rad_tests/src/main.rs | 29 +++++++++++++++++++++++------ todo.txt | 4 ++++ 7 files changed, 70 insertions(+), 21 deletions(-) diff --git a/emulator/cpus/z80/src/decode.rs b/emulator/cpus/z80/src/decode.rs index bee6974..aa83307 100644 --- a/emulator/cpus/z80/src/decode.rs +++ b/emulator/cpus/z80/src/decode.rs @@ -461,7 +461,7 @@ impl Z80Decoder { 6 => { match get_ins_y(ins) & 0x03 { 0 => Ok(Instruction::IM(InterruptMode::Mode0)), - 1 => Ok(Instruction::IM(InterruptMode::Mode01)), + 1 => Ok(Instruction::IM(InterruptMode::Mode0)), 2 => Ok(Instruction::IM(InterruptMode::Mode1)), 3 => Ok(Instruction::IM(InterruptMode::Mode2)), _ => panic!("InternalError: impossible value"), diff --git a/emulator/cpus/z80/src/execute.rs b/emulator/cpus/z80/src/execute.rs index 4fe38b1..7f86a04 100644 --- a/emulator/cpus/z80/src/execute.rs +++ b/emulator/cpus/z80/src/execute.rs @@ -211,7 +211,8 @@ impl Z80 { self.set_target_value(target, result)?; }, Instruction::DI => { - self.state.interrupts_enabled = false; + self.state.iff1 = false; + self.state.iff2 = false; }, Instruction::DJNZ(offset) => { let value = self.get_register_value(Register::B); @@ -223,7 +224,8 @@ impl Z80 { } }, Instruction::EI => { - self.state.interrupts_enabled = true; + self.state.iff1 = true; + self.state.iff2 = true; }, Instruction::EXX => { for i in 0..6 { @@ -256,7 +258,7 @@ impl Z80 { self.state.pc -= 1; }, Instruction::IM(mode) => { - self.state.interrupt_mode = mode; + self.state.im = mode; }, Instruction::INC16(regpair) => { let value = self.get_register_pair_value(regpair); @@ -335,6 +337,11 @@ impl Z80 { Direction::FromAcc => { *addr = self.state.reg[Register::A as usize]; }, Direction::ToAcc => { self.state.reg[Register::A as usize] = *addr; }, } + let value = self.state.reg[Register::A as usize]; + self.set_numeric_flags(value as u16, Size::Byte); + self.set_flag(Flags::Parity, self.state.iff2); + self.set_flag(Flags::AddSubtract, false); + self.set_flag(Flags::HalfCarry, false); } Instruction::LDD | Instruction::LDDR | Instruction::LDI | Instruction::LDIR => { let diff = if self.decoder.instruction == Instruction::LDI || self.decoder.instruction == Instruction::LDIR { @@ -416,9 +423,11 @@ impl Z80 { }, Instruction::RETI => { self.state.pc = self.pop_word()?; + self.state.iff1 = self.state.iff2; }, Instruction::RETN => { self.state.pc = self.pop_word()?; + self.state.iff1 = self.state.iff2; }, Instruction::RETcc(cond) => { if self.get_current_condition(cond) { diff --git a/emulator/cpus/z80/src/lib.rs b/emulator/cpus/z80/src/lib.rs index 31a8302..892e953 100644 --- a/emulator/cpus/z80/src/lib.rs +++ b/emulator/cpus/z80/src/lib.rs @@ -5,4 +5,5 @@ pub mod execute; pub mod debugger; pub use self::state::{Z80, Z80Type}; +pub use self::state::InterruptMode; diff --git a/emulator/cpus/z80/src/state.rs b/emulator/cpus/z80/src/state.rs index e7a3524..a13c65a 100644 --- a/emulator/cpus/z80/src/state.rs +++ b/emulator/cpus/z80/src/state.rs @@ -22,9 +22,20 @@ pub enum Status { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum InterruptMode { Mode0, - Mode01, Mode1, Mode2, + Unknown(u8), +} + +impl From for InterruptMode { + fn from(im: u8) -> Self { + match im { + 0 => InterruptMode::Mode0, + 1 => InterruptMode::Mode1, + 2 => InterruptMode::Mode2, + _ => InterruptMode::Unknown(im), + } + } } #[repr(u8)] @@ -58,8 +69,6 @@ pub enum Register { #[derive(Clone, Debug, PartialEq, Eq)] pub struct Z80State { pub status: Status, - pub interrupts_enabled: bool, - pub interrupt_mode: InterruptMode, pub pc: u16, pub sp: u16, @@ -71,14 +80,16 @@ pub struct Z80State { pub i: u8, pub r: u8, + + pub iff1: bool, + pub iff2: bool, + pub im: InterruptMode, } impl Default for Z80State { fn default() -> Self { Self { status: Status::Init, - interrupts_enabled: false, - interrupt_mode: InterruptMode::Mode0, pc: 0, sp: 0, @@ -90,6 +101,10 @@ impl Default for Z80State { i: 0, r: 0, + + iff1: false, + iff2: false, + im: InterruptMode::Mode0, } } } @@ -153,6 +168,9 @@ impl Z80 { println!("D: {:#04x} E: {:#04x} D': {:#04x} E': {:#04x}", self.state.reg[Register::D as usize], self.state.reg[Register::E as usize], self.state.shadow_reg[Register::D as usize], self.state.shadow_reg[Register::E as usize]); println!("H: {:#04x} L: {:#04x} H': {:#04x} L': {:#04x}", self.state.reg[Register::H as usize], self.state.reg[Register::L as usize], self.state.shadow_reg[Register::H as usize], self.state.shadow_reg[Register::L as usize]); + 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); println!(); self.port.dump_memory(clock, self.state.sp as Address, 0x40); diff --git a/tests/rad_tests/latest.txt b/tests/rad_tests/latest.txt index 34c7fba..6f58ae6 100644 --- a/tests/rad_tests/latest.txt +++ b/tests/rad_tests/latest.txt @@ -1,4 +1,4 @@ -Last run on 2023-05-13 at commit f0cbf5f2cac41f03df031d136296e95fa1968e31 +Last run on 2023-05-13 at commit e61e0cfe8f79c42b3ec232ddb3c97e63dbb40864 00.json completed, all passed! 01.json completed, all passed! @@ -524,7 +524,7 @@ ed 43.json completed, all passed! 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 47.json completed: 34 passed, 966 FAILED ed 48.json completed, all passed! ed 49.json completed, all passed! ed 4a.json completed, all passed! @@ -532,7 +532,7 @@ ed 4b.json completed, all passed! 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 4f.json completed: 36 passed, 964 FAILED ed 50.json completed, all passed! ed 51.json completed, all passed! ed 52.json completed, all passed! @@ -540,7 +540,7 @@ ed 53.json completed, all passed! ed 54.json completed, all passed! ed 55.json completed, all passed! ed 56.json completed, all passed! -ed 57.json completed: 0 passed, 1000 FAILED +ed 57.json completed, all passed! ed 58.json completed, all passed! ed 59.json completed, all passed! ed 5a.json completed, all passed! @@ -643,5 +643,5 @@ fd f9.json completed, all passed! fe.json completed, all passed! ff.json completed, all passed! -passed: 624998, failed: 17002, total 97% -completed in 0m 30s +passed: 624068, failed: 17932, total 97% +completed in 0m 31s diff --git a/tests/rad_tests/src/main.rs b/tests/rad_tests/src/main.rs index abd35f4..09e77e2 100644 --- a/tests/rad_tests/src/main.rs +++ b/tests/rad_tests/src/main.rs @@ -15,7 +15,7 @@ use serde_derive::Deserialize; use moa_core::{System, Error, MemoryBlock, Bus, BusPort, Frequency, Address, Addressable, Steppable, wrap_transmutable}; -use moa_z80::{Z80, Z80Type}; +use moa_z80::{Z80, Z80Type, InterruptMode}; use moa_z80::state::Flags; use moa_z80::state::Status; @@ -71,8 +71,8 @@ struct TestState { f: u8, h: u8, l: u8, - //i: u8, - //r: u8, + i: u8, + r: u8, //ei: u8, //wz: u8, ix: u16, @@ -81,11 +81,11 @@ struct TestState { bc_: u16, de_: u16, hl_: u16, - //im: u8, + im: u8, //p: u8, //q: u8, - //iff1: u8, - //iff2: u8, + iff1: u8, + iff2: u8, ram: Vec<(u16, u8)>, } @@ -119,6 +119,8 @@ impl TestState { println!(" l: {:02x} l': {:02x}", self.l, self.hl_ & 0xff); println!("pc: {:04x} sp: {:04x}", self.pc, self.sp); println!("ix: {:04x} iy: {:04x}", self.ix, self.iy); + println!(" i: {:02x} r: {:02x}", self.i, self.r); + println!("im: {:02x} iff1: {:02x} iff2: {:02x}", self.im, self.iff1, self.iff2); println!("ram: "); for (addr, byte) in self.ram.iter() { @@ -199,6 +201,11 @@ fn load_state(cpu: &mut Z80, system: &mut System, io_bus: Rc>, init cpu.state.iy = initial.iy; cpu.state.sp = initial.sp; cpu.state.pc = initial.pc; + cpu.state.i = initial.i; + cpu.state.r = initial.r; + cpu.state.im = initial.im.into(); + cpu.state.iff1 = initial.iff1 != 0; + cpu.state.iff2 = initial.iff2 != 0; // Load data bytes into memory for (addr, byte) in initial.ram.iter() { @@ -241,6 +248,16 @@ fn assert_state(cpu: &Z80, system: &System, io_bus: Rc>, expected: assert_value(cpu.state.iy, expected.iy, "iy")?; assert_value(cpu.state.sp, expected.sp, "sp")?; assert_value(cpu.state.pc, expected.pc, "pc")?; + assert_value(cpu.state.i, expected.i, "i")?; + // TODO this isn't emulated yet, so it will cause all the tests to fail + //assert_value(cpu.state.r, expected.r, "r")?; + + let expected_im: InterruptMode = expected.im.into(); + if 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(); diff --git a/todo.txt b/todo.txt index 502c0c3..a80efb7 100644 --- a/todo.txt +++ b/todo.txt @@ -3,6 +3,10 @@ * you could have busport take a closure or something which translates the address, and returns an error that will be passed up if it occurs in order to implement the correct behaviour for address exceptions in 68k, transparently +* go through Z80 and see if you can replace functions with From<>/Into + +* go through flags on m68k and see if you can fix the remaining ones and/or clean up the implementation + * should you make Address a newtype and add From impls for each type of numeric, and add utils to wrap address at certain boundaries and such * should you make a means of storing different kinds of buses? * should you make buses hide their RcRefCell?