diff --git a/src/app/game.rs b/src/app/game.rs index aad68ba..8960450 100644 --- a/src/app/game.rs +++ b/src/app/game.rs @@ -211,8 +211,8 @@ impl GameWindow { } }); ui.menu("Emulation", || { - let has_game = self.client.has_game(); - if self.client.is_running() { + let has_game = self.client.has_game(self.sim_id); + if self.client.is_running(self.sim_id) { if ui.menu_item_config("Pause").enabled(has_game).build() { self.client.send_command(EmulatorCommand::Pause); } @@ -220,7 +220,8 @@ impl GameWindow { self.client.send_command(EmulatorCommand::Resume); } if ui.menu_item_config("Reset").enabled(has_game).build() { - self.client.send_command(EmulatorCommand::Reset); + self.client + .send_command(EmulatorCommand::Reset(self.sim_id)); } }); ui.menu("Video", || { @@ -243,11 +244,23 @@ impl GameWindow { } }); ui.menu("Multiplayer", || { - if self.sim_id == SimId::Player1 && ui.menu_item("Open Player 2") { + if self.sim_id == SimId::Player1 + && !self.client.has_player_2() + && ui.menu_item("Open Player 2") + { self.client .send_command(EmulatorCommand::StartSecondSim(None)); self.proxy.send_event(UserEvent::OpenPlayer2Window).unwrap(); } + if self.client.has_player_2() { + let linked = self.client.are_sims_linked(); + if linked && ui.menu_item("Unlink") { + self.client.send_command(EmulatorCommand::Unlink); + } + if !linked && ui.menu_item("Link") { + self.client.send_command(EmulatorCommand::Link); + } + } }); }); @@ -326,7 +339,7 @@ impl AppWindow for GameWindow { WindowEvent::Resized(size) => { self.window.handle_resize(size); if self.window.minimized { - if self.client.is_running() { + if self.client.is_running(self.sim_id) { self.client.send_command(EmulatorCommand::Pause); self.paused_due_to_minimize = true; } diff --git a/src/emulator.rs b/src/emulator.rs index 027df7b..c0638a8 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -3,7 +3,7 @@ use std::{ fs, path::{Path, PathBuf}, sync::{ - atomic::{AtomicBool, Ordering}, + atomic::{AtomicBool, AtomicUsize, Ordering}, mpsc::{self, RecvError, TryRecvError}, Arc, }, @@ -20,8 +20,10 @@ mod shrooms_vb_core; pub struct EmulatorBuilder { rom: Option, commands: mpsc::Receiver, - running: Arc, - has_game: Arc, + sim_count: Arc, + running: Arc<[AtomicBool; 2]>, + has_game: Arc<[AtomicBool; 2]>, + linked: Arc, } #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] @@ -47,13 +49,17 @@ impl EmulatorBuilder { let builder = Self { rom: None, commands, - running: Arc::new(AtomicBool::new(false)), - has_game: Arc::new(AtomicBool::new(false)), + sim_count: Arc::new(AtomicUsize::new(0)), + running: Arc::new([AtomicBool::new(false), AtomicBool::new(false)]), + has_game: Arc::new([AtomicBool::new(false), AtomicBool::new(false)]), + linked: Arc::new(AtomicBool::new(false)), }; let client = EmulatorClient { queue, + sim_count: builder.sim_count.clone(), running: builder.running.clone(), has_game: builder.has_game.clone(), + linked: builder.linked.clone(), }; (builder, client) } @@ -66,9 +72,15 @@ impl EmulatorBuilder { } pub fn build(self) -> Result { - let mut emulator = Emulator::new(self.commands, self.running, self.has_game)?; + let mut emulator = Emulator::new( + self.commands, + self.sim_count, + self.running, + self.has_game, + self.linked, + )?; if let Some(path) = self.rom { - emulator.load_rom_from_file(SimId::Player1, &path)?; + emulator.load_rom(SimId::Player1, &path)?; } Ok(emulator) } @@ -78,73 +90,112 @@ pub struct Emulator { sims: Vec, audio: Audio, commands: mpsc::Receiver, + sim_count: Arc, + running: Arc<[AtomicBool; 2]>, + has_game: Arc<[AtomicBool; 2]>, + linked: Arc, renderers: HashMap, - running: Arc, - has_game: Arc, } impl Emulator { fn new( commands: mpsc::Receiver, - running: Arc, - has_game: Arc, + sim_count: Arc, + running: Arc<[AtomicBool; 2]>, + has_game: Arc<[AtomicBool; 2]>, + linked: Arc, ) -> Result { Ok(Self { sims: vec![], audio: Audio::init()?, commands, - renderers: HashMap::new(), + sim_count, running, has_game, + linked, + renderers: HashMap::new(), }) } - pub fn load_rom_from_file(&mut self, sim_id: SimId, path: &Path) -> Result<()> { + pub fn load_rom(&mut self, sim_id: SimId, path: &Path) -> Result<()> { let bytes = fs::read(path)?; - self.load_rom(sim_id, bytes)?; + self.reset_sim(sim_id, Some(bytes))?; Ok(()) } pub fn start_second_sim(&mut self, rom: Option) -> Result<()> { let bytes = if let Some(path) = rom { - fs::read(path)? - } else if let Some(rom) = self.sims.first().and_then(|s| s.clone_rom()) { - rom + Some(fs::read(path)?) } else { - return Ok(()); + self.sims.first().and_then(|s| s.clone_rom()) }; - self.load_rom(SimId::Player2, bytes)?; - let (first, second) = self.sims.split_at_mut(1); - first[0].link(&mut second[0]); + self.reset_sim(SimId::Player2, bytes)?; + self.link_sims(); Ok(()) } - fn load_rom(&mut self, sim_id: SimId, bytes: Vec) -> Result<()> { - while self.sims.len() <= sim_id.to_index() { + fn reset_sim(&mut self, sim_id: SimId, new_rom: Option>) -> Result<()> { + let index = sim_id.to_index(); + while self.sims.len() <= index { self.sims.push(Sim::new()); } - let sim = &mut self.sims[sim_id.to_index()]; + self.sim_count.store(self.sims.len(), Ordering::Relaxed); + let sim = &mut self.sims[index]; sim.reset(); - sim.load_rom(bytes)?; - self.has_game.store(true, Ordering::Release); - self.running.store(true, Ordering::Release); + if let Some(bytes) = new_rom { + sim.load_rom(bytes)?; + self.has_game[index].store(true, Ordering::Release); + } + if self.has_game[index].load(Ordering::Acquire) { + self.running[index].store(true, Ordering::Release); + } Ok(()) } + fn link_sims(&mut self) { + let (first, second) = self.sims.split_at_mut(1); + let Some(first) = first.first_mut() else { + return; + }; + let Some(second) = second.first_mut() else { + return; + }; + first.link(second); + self.linked.store(true, Ordering::Release); + } + + fn unlink_sims(&mut self) { + let Some(first) = self.sims.first_mut() else { + return; + }; + first.unlink(); + self.linked.store(false, Ordering::Release); + } + pub fn stop_second_sim(&mut self) { self.renderers.remove(&SimId::Player2); self.sims.truncate(1); + self.sim_count.store(self.sims.len(), Ordering::Relaxed); + self.running[SimId::Player2.to_index()].store(false, Ordering::Release); + self.has_game[SimId::Player2.to_index()].store(false, Ordering::Release); + self.linked.store(false, Ordering::Release); } pub fn run(&mut self) { let mut eye_contents = vec![0u8; 384 * 224 * 2]; let mut audio_samples = vec![]; loop { - let mut idle = true; - if self.running.load(Ordering::Acquire) { - idle = false; + let p1_running = self.running[SimId::Player1.to_index()].load(Ordering::Acquire); + let p2_running = self.running[SimId::Player2.to_index()].load(Ordering::Acquire); + let mut idle = p1_running || p2_running; + if p1_running && p2_running { Sim::emulate_many(&mut self.sims); + } else if p1_running { + self.sims[SimId::Player1.to_index()].emulate(); + } else if p2_running { + self.sims[SimId::Player2.to_index()].emulate(); } + for sim_id in SimId::values() { let Some(renderer) = self.renderers.get_mut(&sim_id) else { continue; @@ -198,7 +249,7 @@ impl Emulator { self.renderers.insert(sim_id, renderer); } EmulatorCommand::LoadGame(sim_id, path) => { - if let Err(error) = self.load_rom_from_file(sim_id, &path) { + if let Err(error) = self.load_rom(sim_id, &path) { eprintln!("error loading rom: {}", error); } } @@ -211,19 +262,27 @@ impl Emulator { self.stop_second_sim(); } EmulatorCommand::Pause => { - self.running.store(false, Ordering::Release); + for sim in SimId::values() { + self.running[sim.to_index()].store(false, Ordering::Release); + } } EmulatorCommand::Resume => { - if self.has_game.load(Ordering::Acquire) { - self.running.store(true, Ordering::Relaxed); + for sim_id in SimId::values() { + let index = sim_id.to_index(); + if self.has_game[index].load(Ordering::Acquire) { + self.running[index].store(true, Ordering::Relaxed); + } } } - EmulatorCommand::Reset => { - for sim in self.sims.iter_mut() { - sim.reset(); - } - if !self.sims.is_empty() { - self.running.store(true, Ordering::Release); + EmulatorCommand::Link => { + self.link_sims(); + } + EmulatorCommand::Unlink => { + self.unlink_sims(); + } + EmulatorCommand::Reset(sim_id) => { + if let Err(error) = self.reset_sim(sim_id, None) { + eprintln!("error resetting sim: {}", error); } } EmulatorCommand::SetKeys(sim_id, keys) => { @@ -243,23 +302,33 @@ pub enum EmulatorCommand { StopSecondSim, Pause, Resume, - Reset, + Link, + Unlink, + Reset(SimId), SetKeys(SimId, VBKey), } #[derive(Clone)] pub struct EmulatorClient { queue: mpsc::Sender, - running: Arc, - has_game: Arc, + sim_count: Arc, + running: Arc<[AtomicBool; 2]>, + has_game: Arc<[AtomicBool; 2]>, + linked: Arc, } impl EmulatorClient { - pub fn is_running(&self) -> bool { - self.running.load(Ordering::Acquire) + pub fn has_player_2(&self) -> bool { + self.sim_count.load(Ordering::Acquire) == 2 } - pub fn has_game(&self) -> bool { - self.has_game.load(Ordering::Acquire) + pub fn is_running(&self, sim_id: SimId) -> bool { + self.running[sim_id.to_index()].load(Ordering::Acquire) + } + pub fn has_game(&self, sim_id: SimId) -> bool { + self.has_game[sim_id.to_index()].load(Ordering::Acquire) + } + pub fn are_sims_linked(&self) -> bool { + self.linked.load(Ordering::Acquire) } pub fn send_command(&self, command: EmulatorCommand) { if let Err(err) = self.queue.send(command) { diff --git a/src/emulator/shrooms_vb_core.rs b/src/emulator/shrooms_vb_core.rs index 8a96ffe..0d7593b 100644 --- a/src/emulator/shrooms_vb_core.rs +++ b/src/emulator/shrooms_vb_core.rs @@ -58,6 +58,8 @@ type OnFrame = extern "C" fn(sim: *mut VB) -> c_int; #[link(name = "vb")] extern "C" { + #[link_name = "vbEmulate"] + fn vb_emulate(sim: *mut VB, cycles: *mut u32) -> c_int; #[link_name = "vbEmulateEx"] fn vb_emulate_ex(sims: *mut *mut VB, count: c_uint, cycles: *mut u32) -> c_int; #[link_name = "vbGetCartROM"] @@ -202,6 +204,19 @@ impl Sim { unsafe { vb_set_peer(self.sim, peer.sim) }; } + pub fn unlink(&mut self) { + let peer = unsafe { vb_get_peer(self.sim) }; + if !peer.is_null() { + unsafe { vb_set_peer(peer, ptr::null_mut()) }; + unsafe { vb_set_peer(self.sim, ptr::null_mut()) }; + } + } + + pub fn emulate(&mut self) { + let mut cycles = 20_000_000; + unsafe { vb_emulate(self.sim, &mut cycles) }; + } + pub fn emulate_many(sims: &mut [Sim]) { let mut cycles = 20_000_000; let count = sims.len() as c_uint;