From 57f9f93cc9128deb5655cc4ed417762957a3a2b4 Mon Sep 17 00:00:00 2001 From: transistor Date: Sat, 13 May 2023 14:47:27 -0700 Subject: [PATCH] Fixed the basic IN/OUT instructions on Z80, and basic RETI/RETN --- emulator/core/src/system.rs | 2 + emulator/cpus/z80/src/execute.rs | 62 +- emulator/cpus/z80/src/state.rs | 4 +- emulator/cpus/z80/tests/decode_tests.rs | 2 +- emulator/cpus/z80/tests/execute_tests.rs | 2 +- emulator/systems/genesis/src/system.rs | 2 +- emulator/systems/trs80/src/system.rs | 3 +- tests/rad_tests/latest.txt | 56 +- ...5-13-1-first-run-without-undocumented.txt} | 0 ...-without-undocumented-instr-and-flags.txt} | 0 ...hout-undocumented-but-with-half-carry.txt} | 0 ...-13-4-without-undoc-fixed-in-out-instr.txt | 655 ++++++++++++++++++ tests/rad_tests/src/main.rs | 61 +- todo.txt | 7 + 14 files changed, 795 insertions(+), 61 deletions(-) rename tests/rad_tests/progress/{2023-05-13-first-run-without-undocumented.txt => 2023-05-13-1-first-run-without-undocumented.txt} (100%) rename tests/rad_tests/progress/{2023-05-13-without-undocumented-instr-and-flags.txt => 2023-05-13-2-without-undocumented-instr-and-flags.txt} (100%) rename tests/rad_tests/progress/{2023-05-13-without-undocumented-but-with-half-carry.txt => 2023-05-13-3-without-undocumented-but-with-half-carry.txt} (100%) create mode 100644 tests/rad_tests/progress/2023-05-13-4-without-undoc-fixed-in-out-instr.txt diff --git a/emulator/core/src/system.rs b/emulator/core/src/system.rs index 83eca33..1da0264 100644 --- a/emulator/core/src/system.rs +++ b/emulator/core/src/system.rs @@ -21,6 +21,7 @@ pub struct System { pub debugger: RefCell, pub bus: Rc>, + pub buses: HashMap>>, pub interrupt_controller: RefCell, pub break_signal: Option, @@ -37,6 +38,7 @@ impl Default for System { debugger: RefCell::new(Debugger::default()), bus: Rc::new(RefCell::new(Bus::default())), + buses: HashMap::new(), interrupt_controller: RefCell::new(InterruptController::default()), break_signal: None, diff --git a/emulator/cpus/z80/src/execute.rs b/emulator/cpus/z80/src/execute.rs index 7884e98..9f29e56 100644 --- a/emulator/cpus/z80/src/execute.rs +++ b/emulator/cpus/z80/src/execute.rs @@ -280,12 +280,24 @@ impl Z80 { //}, //Instruction::INIR => { //}, - //Instruction::INic(reg) => { - //}, + Instruction::INic(reg) => { + let b = self.get_register_value(Register::B); + let c = self.get_register_value(Register::C); + let value = self.get_ioport_value(b, c)?; + + self.set_register_value(reg, value); + self.set_numeric_flags(value as u16, Size::Byte); + self.set_parity_flags(value); + self.set_flag(Flags::HalfCarry, false); + self.set_flag(Flags::AddSubtract, false); + }, //Instruction::INicz => { //}, - //Instruction::INx(u8) => { - //}, + Instruction::INx(n) => { + let a = self.get_register_value(Register::A); + let value = self.get_ioport_value(a, n)?; + self.set_register_value(Register::A, value); + }, Instruction::JP(addr) => { self.state.pc = addr; }, @@ -368,13 +380,18 @@ impl Z80 { //}, //Instruction::OUTI => { //}, - //Instruction::OUTic(reg) => { - //}, + Instruction::OUTic(reg) => { + let b = self.get_register_value(Register::B); + let c = self.get_register_value(Register::C); + let value = self.get_register_value(reg); + self.set_ioport_value(b, c, value)?; + }, //Instruction::OUTicz => { //}, - Instruction::OUTx(_port) => { - // TODO this needs to be fixed - //println!("OUT ({:x}), {:x} {}", port, self.state.reg[Register::A as usize], self.state.reg[Register::A as usize] as char); + Instruction::OUTx(n) => { + let a = self.get_register_value(Register::A); + let value = self.get_register_value(Register::A); + self.set_ioport_value(a, n, value)?; }, Instruction::POP(regpair) => { let value = self.pop_word()?; @@ -395,10 +412,12 @@ impl Z80 { Instruction::RET => { self.state.pc = self.pop_word()?; }, - //Instruction::RETI => { - //}, - //Instruction::RETN => { - //}, + Instruction::RETI => { + self.state.pc = self.pop_word()?; + }, + Instruction::RETN => { + self.state.pc = self.pop_word()?; + }, Instruction::RETcc(cond) => { if self.get_current_condition(cond) { self.state.pc = self.pop_word()?; @@ -728,6 +747,23 @@ impl Z80 { Ok(()) } + fn get_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.current_clock, addr)?) + } else { + Ok(0) + } + } + + fn set_ioport_value(&mut self, upper: u8, lower: u8, value: u8) -> Result<(), Error> { + let addr = ((upper as Address) << 8) | (lower as Address); + if let Some(io) = self.ioport.as_mut() { + io.write_u8(self.current_clock, addr, value)? + } + Ok(()) + } + fn get_register_value(&mut self, reg: Register) -> u8 { self.state.reg[reg as usize] diff --git a/emulator/cpus/z80/src/state.rs b/emulator/cpus/z80/src/state.rs index bc8586d..e7a3524 100644 --- a/emulator/cpus/z80/src/state.rs +++ b/emulator/cpus/z80/src/state.rs @@ -112,13 +112,14 @@ pub struct Z80 { pub decoder: Z80Decoder, pub debugger: Z80Debugger, pub port: BusPort, + pub ioport: Option, pub reset: Signal, pub bus_request: Signal, pub current_clock: ClockTime, } impl Z80 { - pub fn new(cputype: Z80Type, frequency: Frequency, port: BusPort) -> Self { + pub fn new(cputype: Z80Type, frequency: Frequency, port: BusPort, ioport: Option) -> Self { Self { cputype, frequency, @@ -126,6 +127,7 @@ impl Z80 { decoder: Z80Decoder::default(), debugger: Z80Debugger::default(), port, + ioport, reset: Signal::new(false), bus_request: Signal::new(false), current_clock: ClockTime::START, diff --git a/emulator/cpus/z80/tests/decode_tests.rs b/emulator/cpus/z80/tests/decode_tests.rs index 1d3da3e..4ff84a2 100644 --- a/emulator/cpus/z80/tests/decode_tests.rs +++ b/emulator/cpus/z80/tests/decode_tests.rs @@ -14,7 +14,7 @@ fn init_decode_test() -> (Z80, System) { system.add_addressable_device(0x0000, wrap_transmutable(mem)).unwrap(); // Initialize the CPU and make sure it's in the expected state - let mut cpu = Z80::new(Z80Type::Z80, Frequency::from_mhz(4), BusPort::new(0, 16, 8, system.bus.clone())); + let mut cpu = Z80::new(Z80Type::Z80, Frequency::from_mhz(4), BusPort::new(0, 16, 8, system.bus.clone()), None); cpu.init().unwrap(); (cpu, system) diff --git a/emulator/cpus/z80/tests/execute_tests.rs b/emulator/cpus/z80/tests/execute_tests.rs index 7d2e6d2..2baf5a1 100644 --- a/emulator/cpus/z80/tests/execute_tests.rs +++ b/emulator/cpus/z80/tests/execute_tests.rs @@ -489,7 +489,7 @@ fn init_execute_test() -> (Z80, System) { system.add_addressable_device(0x0000, wrap_transmutable(mem)).unwrap(); // Initialize the CPU and make sure it's in the expected state - let mut cpu = Z80::new(Z80Type::Z80, Frequency::from_mhz(4), BusPort::new(0, 16, 8, system.bus.clone())); + let mut cpu = Z80::new(Z80Type::Z80, Frequency::from_mhz(4), BusPort::new(0, 16, 8, system.bus.clone()), None); cpu.init().unwrap(); (cpu, system) diff --git a/emulator/systems/genesis/src/system.rs b/emulator/systems/genesis/src/system.rs index cbf6e10..a057be3 100644 --- a/emulator/systems/genesis/src/system.rs +++ b/emulator/systems/genesis/src/system.rs @@ -82,7 +82,7 @@ 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 mut coproc = Z80::new(Z80Type::Z80, Frequency::from_hz(3_579_545), BusPort::new(0, 16, 8, coproc_bus)); + let mut coproc = Z80::new(Z80Type::Z80, Frequency::from_hz(3_579_545), BusPort::new(0, 16, 8, coproc_bus), None); coproc.set_debugging(true); let mut reset = coproc.reset.clone(); let mut bus_request = coproc.bus_request.clone(); diff --git a/emulator/systems/trs80/src/system.rs b/emulator/systems/trs80/src/system.rs index b76ddf4..99182c4 100644 --- a/emulator/systems/trs80/src/system.rs +++ b/emulator/systems/trs80/src/system.rs @@ -42,7 +42,8 @@ pub fn build_trs80(host: &mut H, options: Trs80Options) -> Result, } +#[derive(Debug, Deserialize)] +struct TestPort { + addr: u16, + value: u8, + atype: String, +} + #[derive(Debug, Deserialize)] struct TestCase { name: String, @@ -94,6 +103,8 @@ struct TestCase { initial_state: TestState, #[serde(rename(deserialize = "final"))] final_state: TestState, + #[serde(default)] + ports: Vec, } impl TestState { @@ -123,25 +134,36 @@ impl TestCase { self.initial_state.dump(); println!("final:"); self.final_state.dump(); + + println!("ports: "); + for port in self.ports.iter() { + println!("{:04x} {:02x} {}", port.addr, port.value, port.atype); + } } } -fn init_execute_test(cputype: Z80Type, state: &TestState) -> Result<(Z80, System), Error> { +fn init_execute_test(cputype: Z80Type, state: &TestState, ports: &[TestPort]) -> Result<(Z80, System, Rc>), Error> { let mut system = System::default(); // Insert basic initialization - let data = vec![0; 0x01000000]; - let mem = MemoryBlock::new(data); + let mem = MemoryBlock::new(vec![0; 0x1_0000]); system.add_addressable_device(0x00000000, wrap_transmutable(mem)).unwrap(); + // Set up IOREQ as memory space + let io_ram = wrap_transmutable(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.clone()); + let port = BusPort::new(0, 16, 8, system.bus.clone()); - let mut cpu = Z80::new(cputype, Frequency::from_mhz(10), port); + let ioport = BusPort::new(0, 16, 8, io_bus.clone()); + let mut cpu = Z80::new(cputype, Frequency::from_mhz(10), port, Some(ioport)); cpu.state.status = Status::Running; - load_state(&mut cpu, &mut system, state)?; + load_state(&mut cpu, &mut system, io_bus.clone(), state, ports)?; - Ok((cpu, system)) + Ok((cpu, system, io_bus)) } fn assert_value(actual: T, expected: T, message: &str) -> Result<(), Error> @@ -155,7 +177,7 @@ where } } -fn load_state(cpu: &mut Z80, system: &mut System, initial: &TestState) -> Result<(), Error> { +fn load_state(cpu: &mut Z80, system: &mut System, io_bus: Rc>, initial: &TestState, ports: &[TestPort]) -> Result<(), Error> { cpu.state.reg[0] = initial.b; cpu.state.reg[1] = initial.c; cpu.state.reg[2] = initial.d; @@ -183,12 +205,17 @@ fn load_state(cpu: &mut Z80, system: &mut System, initial: &TestState) -> Result system.get_bus().write_u8(system.clock, *addr as u64, *byte)?; } + // 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)?; + } + Ok(()) } const IGNORE_FLAG_MASK: u8 = Flags::F3 as u8 | Flags::F5 as u8; -fn assert_state(cpu: &Z80, system: &System, expected: &TestState, check_extra_flags: bool) -> Result<(), Error> { +fn assert_state(cpu: &Z80, system: &System, io_bus: Rc>, expected: &TestState, check_extra_flags: bool, ports: &[TestPort]) -> Result<(), Error> { assert_value(cpu.state.reg[0], expected.b, "b")?; assert_value(cpu.state.reg[1], expected.c, "c")?; assert_value(cpu.state.reg[2], expected.d, "d")?; @@ -223,22 +250,30 @@ fn assert_state(cpu: &Z80, system: &System, expected: &TestState, check_extra_fl 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)?; + assert_value(actual, port.value, &format!("port value at {:x}", port.addr))?; + } + } + Ok(()) } -fn step_cpu_and_assert(cpu: &mut Z80, system: &System, case: &TestCase, check_extra_flags: bool) -> Result<(), Error> { +fn step_cpu_and_assert(cpu: &mut Z80, system: &System, io_bus: Rc>, case: &TestCase, check_extra_flags: bool) -> Result<(), Error> { let _clock_elapsed = cpu.step(&system)?; - assert_state(&cpu, &system, &case.final_state, check_extra_flags)?; + assert_state(&cpu, &system, io_bus, &case.final_state, check_extra_flags, &case.ports)?; Ok(()) } fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> { - let (mut cpu, system) = init_execute_test(Z80Type::Z80, &case.initial_state).unwrap(); + let (mut cpu, system, io_bus) = 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, case, args.check_extra_flags); + let result = step_cpu_and_assert(&mut cpu, &system, io_bus, case, args.check_extra_flags); match result { Ok(()) => Ok(()), diff --git a/todo.txt b/todo.txt index 6789bb8..33c94f2 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,11 @@ +* I like making address adapters like this (below) +* 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 + +* should you make a means of storing different kinds of buses? +* should you make buses hide their RcRefCell? + * address repeater on ym2612 doesn't seem to work the same, when it's on the 68000 device. The Z80 device doesn't have an affect, but maybe it's not being used * sound doesn't work on a lot of games... is it a problem with the Z80 accessing the YM2612, or the lack of YM timers? or something else? * make the ym generate audio in sync so the DAC timings can be more accurate