tests.rsannotatedtests.rssource167 lines · 5.4 KB · raw
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}