1use super::*; 2use crate::api::GenesisHardware; 3use bincode::{Decode, Encode}; 4use genesis_config::GenesisEmulatorConfig; 5use jgenesis_common::frontend::SaveWriter; 6use s32x_core::api::debug::Dummy32XDebugger; 7 8struct NullOutput; 9 10#[allow(unused_variables)] 11impl SaveWriter for NullOutput { 12 type Err = String; 13 14 fn load_bytes(&mut self, extension: &str) -> Result<Vec<u8>, Self::Err> { 15 Err(String::new()) 16 } 17 18 fn persist_bytes(&mut self, extension: &str, bytes: &[u8]) -> Result<(), Self::Err> { 19 Ok(()) 20 } 21 22 fn load_serialized<D: Decode<()>>(&mut self, extension: &str) -> Result<D, Self::Err> { 23 Err(String::new()) 24 } 25 26 fn persist_serialized<E: Encode>(&mut self, extension: &str, data: E) -> Result<(), Self::Err> { 27 Ok(()) 28 } 29}
This test is meant to be run in miri: cargo +nightly miri test -p genesis-core
The 32X code and the Genesis+32X debugger code make moderate use of unsafe to avoid having to put a lifetime on the SH-2 bus structs, which would be excessively annoying to deal with due to how the SH-2 opcode lookup table is implemented. This test is not completely exhaustive but should hit the major code paths that use unsafe.
Warning, this is very slow in miri (takes 2-3 minutes on my machine)
40#[test] 41fn check_for_memory_model_violations() { 42 const CPUS: [WhichCpu; 2] = [WhichCpu::Master, WhichCpu::Slave]; 43 44 let mut emulator = GenesisEmulator::create( 45 GenesisHardware::SegaCd32X, 46 None, 47 Some(vec![0xFF; 128 * 1024]), 48 None, 49 GenesisEmulatorConfig::default(), 50 &mut NullOutput, 51 ) 52 .expect("Failed to create emulator"); 53 54 let (state_sender, _state_receiver) = jgenesis_common::sync::new_shared_var(); 55 let (mut debugger, debugger_handle) = GenesisDebugger::new(state_sender); 56 57 let sega_32x = emulator.bus.sega_32x.as_mut().unwrap(); 58 59 // No debugger 60 for which in CPUS { 61 sega_32x.simulate_bus_interactions::<false>( 62 &mut Dummy32XDebugger, 63 which, 64 &[0x02000000, 0x00004020], 65 &[(0x06000000, 0), (0x00004020, 0)], 66 ); 67 } 68 69 // With debugger, no breakpoints 70 let mut debugger_with_cpus = debugger.with_cpus(&mut emulator.m68k, &mut emulator.z80); 71 let mut s32x_debugger = debugger_with_cpus 72 .for_32x(emulator.bus.sega_cd.as_mut(), genesis_components!(emulator.bus)); 73 for which in CPUS { 74 sega_32x.simulate_bus_interactions::<true>( 75 &mut *s32x_debugger, 76 which, 77 &[0x02000000, 0x00004020], 78 &[(0x06000000, 0), (0x00004020, 0)], 79 ); 80 } 81 82 for which in CPUS { 83 debugger_handle 84 .command_sender 85 .send(GenesisDebugCommand::UpdateSh2Breakpoints( 86 which, 87 Sh2Breakpoints { 88 memory: vec![ 89 Sh2Breakpoint { 90 start_address: 0x02000000, 91 end_address: 0x03000000, 92 read: true, 93 write: false, 94 execute: false, 95 }, 96 Sh2Breakpoint { 97 start_address: 0x06000000, 98 end_address: 0x07000000, 99 read: false, 100 write: true, 101 execute: false, 102 }, 103 ], 104 interrupt: vec![], 105 }, 106 )) 107 .unwrap(); 108 } 109 110 debugger.process_commands(&mut emulator.as_debug_view()); 111 112 let sega_32x = emulator.bus.sega_32x.as_mut().unwrap(); 113 let mut debugger_with_cpus = debugger.with_cpus(&mut emulator.m68k, &mut emulator.z80); 114 let mut s32x_debugger = debugger_with_cpus 115 .for_32x(emulator.bus.sega_cd.as_mut(), genesis_components!(emulator.bus)); 116 117 // With debugger, with breakpoints, no memory edits 118 for which in CPUS { 119 // Expected to trigger 2 breakpoints 120 for _ in 0..2 { 121 debugger_handle.send_command(GenesisDebugCommand::BreakResume).unwrap(); 122 } 123 124 sega_32x.simulate_bus_interactions::<true>( 125 &mut *s32x_debugger, 126 which, 127 &[0x02000000, 0x00004020], 128 &[(0x06000000, 0), (0x00004020, 0)], 129 ); 130 } 131 132 // With debugger, with breakpoints, with memory edits 133 for which in CPUS { 134 for memory_area in GenesisMemoryArea::ALL { 135 debugger_handle 136 .command_sender 137 .send(GenesisDebugCommand::EditMemory(memory_area, 0, 0)) 138 .unwrap(); 139 } 140 141 for memory_area in SegaCdMemoryArea::ALL { 142 debugger_handle 143 .command_sender 144 .send(GenesisDebugCommand::EditSegaCdMemory(memory_area, 0, 0)) 145 .unwrap(); 146 } 147 148 for memory_area in S32XMemoryArea::ALL { 149 debugger_handle 150 .command_sender 151 .send(GenesisDebugCommand::Edit32XMemory(memory_area, 0, 0)) 152 .unwrap(); 153 } 154 155 // Expected to trigger 2 breakpoints 156 for _ in 0..2 { 157 debugger_handle.send_command(GenesisDebugCommand::BreakResume).unwrap(); 158 } 159 160 sega_32x.simulate_bus_interactions::<true>( 161 &mut *s32x_debugger, 162 which, 163 &[0x02000000, 0x00004020], 164 &[(0x06000000, 0), (0x00004020, 0)], 165 ); 166 } 167}