From 54ebcce94c7042b64540db54bee5a0cb9d73655f Mon Sep 17 00:00:00 2001 From: transistor Date: Sun, 3 Mar 2024 13:26:15 -0800 Subject: [PATCH] Fixed m68k tests after refactor --- docs/log.txt | 13 +++++++++ emulator/cpus/m68k/src/execute.rs | 18 ++++++++---- emulator/cpus/m68k/tests/decode_tests.rs | 16 +++++------ emulator/cpus/m68k/tests/execute_tests.rs | 28 ++++++++++--------- .../cpus/m68k/tests/musashi_timing_tests.rs | 26 ++++++++--------- emulator/cpus/m68k/tests/timing_tests.rs | 24 ++++++++-------- 6 files changed, 74 insertions(+), 51 deletions(-) diff --git a/docs/log.txt b/docs/log.txt index e6938f1..b335bdc 100644 --- a/docs/log.txt +++ b/docs/log.txt @@ -466,3 +466,16 @@ General Work shift instructions pass now, but there are lots but fewer than before failures for the asl and asr instructions, some movem and movep errors and some bcd instruction errors, mostly +2024-03-02 +- I'm trying to extract the memory/bus interface, and pass it in at the start of each cycle instead + of having the BusPort permanently embedded, which will allow migrating to emulator-hal. + +- The functional way would be argument drilling; passing an extra argument to each function in the + entire execution core. The problem is that it's messy, so a solution that is still functional is to + implement all of the execution logic on a newtype that contains a reference to the mutable state and + the owned cycle data, and at the end of the cycle, decompose the M68kCycleGuard that holds the + reference, and keep the cycle data for debugging purposes. + +- So far it's going quite well. I really like the pattern of making the cycle be like a transaction, + and making it possible to decompose it, especially for testing. I still need to fix the tests +- next step is to push System up from the interrupt handling code diff --git a/emulator/cpus/m68k/src/execute.rs b/emulator/cpus/m68k/src/execute.rs index 900eff9..2928d75 100644 --- a/emulator/cpus/m68k/src/execute.rs +++ b/emulator/cpus/m68k/src/execute.rs @@ -50,7 +50,7 @@ impl M68kCycle { } } - pub fn new(cpu: &mut M68k, clock: Instant) -> Self { + pub fn new(cpu: &M68k, clock: Instant) -> Self { let is_supervisor = cpu.state.sr & (Flags:: Supervisor as u16) != 0; let pc = cpu.state.pc; let data_width = cpu.port.data_width(); @@ -99,17 +99,17 @@ impl<'a> M68kCycleGuard<'a> { println!(); } - pub fn finalize(self) -> M68kCycle { + pub fn end(self) -> M68kCycle { self.cycle } } impl Steppable for M68k { fn step(&mut self, system: &System) -> Result { - let mut cycle = M68kCycle::new(self, system.clock); + let cycle = M68kCycle::new(self, system.clock); let mut execution = cycle.begin(self); let clocks = execution.step(system)?; - self.cycle = execution.finalize(); + self.cycle = execution.end(); Ok(self.frequency.period_duration() * clocks as u64) } @@ -193,8 +193,7 @@ impl<'a> M68kCycleGuard<'a> { pub fn cycle_one(&mut self, system: &System) -> Result { self.check_breakpoints()?; - self.decode_next()?; - self.execute_current()?; + self.decode_and_execute()?; self.check_pending_interrupts(system)?; Ok(self.cycle.timing.calculate_clocks(false, 1)) @@ -299,6 +298,13 @@ impl<'a> M68kCycleGuard<'a> { Ok(()) } + #[inline] + pub fn decode_and_execute(&mut self) -> Result<(), M68kError> { + self.decode_next()?; + self.execute_current()?; + Ok(()) + } + #[inline] pub fn decode_next(&mut self) -> Result<(), M68kError> { let is_supervisor = self.is_supervisor(); diff --git a/emulator/cpus/m68k/tests/decode_tests.rs b/emulator/cpus/m68k/tests/decode_tests.rs index 61a4f69..93b2d00 100644 --- a/emulator/cpus/m68k/tests/decode_tests.rs +++ b/emulator/cpus/m68k/tests/decode_tests.rs @@ -80,7 +80,7 @@ fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) { assert_eq!(cpu.state.pc, INIT_ADDR as u32); assert_eq!(cpu.state.ssp, INIT_STACK as u32); - let cycle = M68kCycle::new(cpu, system.clock); + let cycle = M68kCycle::new(&cpu, system.clock); assert_eq!(cycle.decoder.start, INIT_ADDR as u32); assert_eq!(cycle.decoder.instruction, Instruction::NOP); (cpu, cycle, system) @@ -95,18 +95,18 @@ fn load_memory(system: &System, data: &[u16]) { } fn run_decode_test(case: &TestCase) { - let (mut cpu, mut cycle, system) = init_decode_test(case.cpu); + let (mut cpu, cycle, system) = init_decode_test(case.cpu); load_memory(&system, case.data); match &case.ins { Some(ins) => { - let mut execution = cycle.begin(cpu); - execution.decode_next().unwrap(); - assert_eq!(cpu.decoder.instruction, ins.clone()); + let mut executor = cycle.begin(&mut cpu); + executor.decode_next().unwrap(); + assert_eq!(executor.cycle.decoder.instruction, ins.clone()); }, None => { - let mut execution = cycle.begin(cpu); - let next = execution.decode_next(); - println!("{:?}", cpu.decoder.instruction); + let mut executor = cycle.begin(&mut cpu); + let next = executor.decode_next(); + println!("{:?}", executor.cycle.decoder.instruction); assert!(next.is_err()); }, } diff --git a/emulator/cpus/m68k/tests/execute_tests.rs b/emulator/cpus/m68k/tests/execute_tests.rs index 27fe112..8d7c55e 100644 --- a/emulator/cpus/m68k/tests/execute_tests.rs +++ b/emulator/cpus/m68k/tests/execute_tests.rs @@ -5,7 +5,7 @@ use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Steppable, De use moa_m68k::{M68k, M68kType}; use moa_m68k::state::M68kState; -use moa_m68k::execute::M68kCycle; +use moa_m68k::execute::{M68kCycle, M68kCycleGuard}; use moa_m68k::instructions::{Instruction, Target, Size, Sign, Direction, Condition}; const INIT_STACK: Address = 0x00002000; @@ -37,7 +37,7 @@ struct TestCase { fn run_execute_test(cputype: M68kType, mut test_func: F) where - F: FnMut(M68kCycle, System), + F: FnMut(M68kCycleGuard, System), { let mut system = System::default(); @@ -51,12 +51,14 @@ where let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); cpu.step(&system).unwrap(); - let cycle = M68kCycle::new(cpu); - assert_eq!(cycle.state.pc, INIT_ADDR as u32); - assert_eq!(cycle.state.ssp, INIT_STACK as u32); - assert_eq!(cycle.decoder.instruction, Instruction::NOP); + let cycle = M68kCycle::new(&cpu, system.clock); + let mut executor = cycle.begin(&mut cpu); - test_func(cycle, system) + assert_eq!(executor.state.pc, INIT_ADDR as u32); + assert_eq!(executor.state.ssp, INIT_STACK as u32); + assert_eq!(executor.cycle.decoder.instruction, Instruction::NOP); + + test_func(executor, system) } fn build_state(state: &TestState) -> M68kState { @@ -79,19 +81,19 @@ fn load_memory(system: &System, data: &[u16]) { } fn run_test(case: &TestCase) { - run_execute_test(case.cputype, |mut cycle, system| { + run_execute_test(case.cputype, |mut executor, system| { let init_state = build_state(&case.init); let expected_state = build_state(&case.fini); system.get_bus().write_beu32(system.clock, MEM_ADDR as Address, case.init.mem).unwrap(); load_memory(&system, case.data); - *cycle.state = init_state; + *executor.state = init_state; - cycle.decode_next().unwrap(); - assert_eq!(cycle.decoder.instruction, case.ins); + executor.decode_next().unwrap(); + assert_eq!(executor.cycle.decoder.instruction, case.ins); - cycle.execute_current().unwrap(); - assert_eq!(*cycle.state, expected_state); + executor.execute_current().unwrap(); + assert_eq!(*executor.state, expected_state); let mem = system.get_bus().read_beu32(system.clock, MEM_ADDR as Address).unwrap(); assert_eq!(mem, case.fini.mem); diff --git a/emulator/cpus/m68k/tests/musashi_timing_tests.rs b/emulator/cpus/m68k/tests/musashi_timing_tests.rs index ab81461..c22bb9b 100644 --- a/emulator/cpus/m68k/tests/musashi_timing_tests.rs +++ b/emulator/cpus/m68k/tests/musashi_timing_tests.rs @@ -1,17 +1,18 @@ use femtos::{Instant, Frequency}; -use moa_core::{System, Error, MemoryBlock, BusPort, Address, Addressable, Device}; +use moa_core::{System, Error, MemoryBlock, Address, Addressable, Device}; use moa_m68k::{M68k, M68kType}; use moa_m68k::instructions::{Instruction, Target, Size, Sign, Condition, XRegister, BaseRegister, IndexRegister, Direction}; use moa_m68k::timing::M68kInstructionTiming; +use moa_m68k::execute::M68kCycle; const INIT_STACK: Address = 0x00002000; const INIT_ADDR: Address = 0x00000010; -fn init_decode_test(cputype: M68kType) -> (M68k, System) { +fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) { let mut system = System::default(); // Insert basic initialization @@ -22,15 +23,13 @@ fn init_decode_test(cputype: M68kType) -> (M68k, System) { system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap(); // Initialize the CPU and make sure it's in the expected state - let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); - cpu.init_cycle(Instant::START); + let cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); + let cycle = M68kCycle::new(&cpu, system.clock); assert_eq!(cpu.state.pc, INIT_ADDR as u32); assert_eq!(cpu.state.ssp, INIT_STACK as u32); - - cpu.decoder.init(true, INIT_ADDR as u32); - assert_eq!(cpu.decoder.start, INIT_ADDR as u32); - assert_eq!(cpu.decoder.instruction, Instruction::NOP); - (cpu, system) + assert_eq!(cycle.decoder.start, INIT_ADDR as u32); + assert_eq!(cycle.decoder.instruction, Instruction::NOP); + (cpu, cycle, system) } fn load_memory(system: &System, data: &[u16]) { @@ -42,14 +41,15 @@ fn load_memory(system: &System, data: &[u16]) { } fn run_timing_test(case: &TimingCase) -> Result<(), Error> { - let (mut cpu, system) = init_decode_test(case.cpu); + let (mut cpu, cycle, system) = init_decode_test(case.cpu); + let mut executor = cycle.begin(&mut cpu); let mut timing = M68kInstructionTiming::new(case.cpu, 16); load_memory(&system, case.data); - cpu.decode_next().unwrap(); - assert_eq!(cpu.decoder.instruction, case.ins.clone()); + executor.decode_next().unwrap(); + assert_eq!(executor.cycle.decoder.instruction, case.ins.clone()); - timing.add_instruction(&cpu.decoder.instruction); + timing.add_instruction(&executor.cycle.decoder.instruction); let result = timing.calculate_clocks(false, 1); let expected = match case.cpu { M68kType::MC68000 => case.timing.0, diff --git a/emulator/cpus/m68k/tests/timing_tests.rs b/emulator/cpus/m68k/tests/timing_tests.rs index 9bf4231..489b557 100644 --- a/emulator/cpus/m68k/tests/timing_tests.rs +++ b/emulator/cpus/m68k/tests/timing_tests.rs @@ -6,6 +6,7 @@ use moa_core::{System, Error, MemoryBlock, BusPort, Address, Addressable, Device use moa_m68k::{M68k, M68kType}; use moa_m68k::instructions::{Instruction, Target, Size}; use moa_m68k::timing::M68kInstructionTiming; +use moa_m68k::execute::M68kCycle; const INIT_STACK: Address = 0x00002000; const INIT_ADDR: Address = 0x00000010; @@ -23,7 +24,7 @@ const TIMING_TESTS: &'static [TimingCase] = &[ ]; -fn init_decode_test(cputype: M68kType) -> (M68k, System) { +fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) { let mut system = System::default(); // Insert basic initialization @@ -34,15 +35,15 @@ fn init_decode_test(cputype: M68kType) -> (M68k, System) { system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap(); // Initialize the CPU and make sure it's in the expected state - let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); - cpu.reset_cpu().unwrap(); + let cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); + //cpu.reset_cpu().unwrap(); assert_eq!(cpu.state.pc, INIT_ADDR as u32); assert_eq!(cpu.state.ssp, INIT_STACK as u32); - cpu.decoder.init(true, INIT_ADDR as u32); - assert_eq!(cpu.decoder.start, INIT_ADDR as u32); - assert_eq!(cpu.decoder.instruction, Instruction::NOP); - (cpu, system) + let cycle = M68kCycle::new(&cpu, system.clock); + assert_eq!(cycle.decoder.start, INIT_ADDR as u32); + assert_eq!(cycle.decoder.instruction, Instruction::NOP); + (cpu, cycle, system) } fn load_memory(system: &System, data: &[u16]) { @@ -54,14 +55,15 @@ fn load_memory(system: &System, data: &[u16]) { } fn run_timing_test(case: &TimingCase) -> Result<(), Error> { - let (mut cpu, system) = init_decode_test(case.cpu); + let (mut cpu, cycle, system) = init_decode_test(case.cpu); + let mut executor = cycle.begin(&mut cpu); let mut timing = M68kInstructionTiming::new(case.cpu, 16); load_memory(&system, case.data); - cpu.decode_next().unwrap(); - assert_eq!(cpu.decoder.instruction, case.ins.clone()); + executor.decode_next().unwrap(); + assert_eq!(executor.cycle.decoder.instruction, case.ins.clone()); - timing.add_instruction(&cpu.decoder.instruction); + timing.add_instruction(&executor.cycle.decoder.instruction); let result = timing.calculate_clocks(false, 1); let expected = match case.cpu { M68kType::MC68000 => case.timing.0,