From 4abb2833c48559c413a4bef9c4a0c940e2f5b887 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Mon, 3 May 2021 23:11:39 -0500 Subject: [PATCH] chore: implement gamepad controls --- Cargo.lock | 222 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/bus.rs | 2 +- src/cartridge.rs | 4 +- src/joypad.rs | 142 +++++++++++------------------- src/lib.rs | 1 + src/main.rs | 55 +++++++++++- 7 files changed, 333 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc4f684..bf38c45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,6 +66,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + [[package]] name = "bit-set" version = "0.5.2" @@ -200,6 +206,16 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" +[[package]] +name = "core-foundation" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +dependencies = [ + "core-foundation-sys 0.6.2", + "libc", +] + [[package]] name = "core-foundation" version = "0.7.0" @@ -220,6 +236,12 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" + [[package]] name = "core-foundation-sys" version = "0.7.0" @@ -339,6 +361,12 @@ dependencies = [ "syn", ] +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + [[package]] name = "dispatch" version = "0.2.0" @@ -514,6 +542,7 @@ dependencies = [ "anyhow", "bitfield", "clap", + "gilrs", "pixels", "winit", "winit_input_helper", @@ -665,6 +694,38 @@ dependencies = [ "slab", ] +[[package]] +name = "gilrs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e986f911d937f4395dfc2a39618dcef452773d32dcdbe0828c623f76588f749" +dependencies = [ + "fnv", + "gilrs-core", + "log", + "uuid", + "vec_map", +] + +[[package]] +name = "gilrs-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5e5bb97bf9a0d9519a28cf38839cf1d6d9bb572b48e3c67202271fec2ed5e7" +dependencies = [ + "core-foundation 0.6.4", + "io-kit-sys", + "libc", + "libudev-sys", + "log", + "nix 0.20.0", + "rusty-xinput", + "stdweb", + "uuid", + "vec_map", + "winapi 0.3.9", +] + [[package]] name = "hermit-abi" version = "0.1.18" @@ -695,6 +756,16 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "io-kit-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21dcc74995dd4cd090b147e79789f8d65959cbfb5f0b118002db869ea3bd0a0" +dependencies = [ + "core-foundation-sys 0.6.2", + "mach", +] + [[package]] name = "iovec" version = "0.1.4" @@ -704,6 +775,12 @@ dependencies = [ "libc", ] +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + [[package]] name = "jni-sys" version = "0.3.0" @@ -776,6 +853,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "lock_api" version = "0.4.3" @@ -794,6 +881,15 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "mach" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" +dependencies = [ + "libc", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1181,6 +1277,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + [[package]] name = "rusttype" version = "0.9.2" @@ -1191,6 +1296,23 @@ dependencies = [ "owned_ttf_parser", ] +[[package]] +name = "rusty-xinput" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2aa654bc32eb9ca14cce1a084abc9dfe43949a4547c35269a094c39272db3bb" +dependencies = [ + "lazy_static", + "log", + "winapi 0.3.9", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + [[package]] name = "same-file" version = "1.0.6" @@ -1212,12 +1334,55 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +[[package]] +name = "serde_derive" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + [[package]] name = "slab" version = "0.4.3" @@ -1270,6 +1435,57 @@ dependencies = [ "num-traits", ] +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "serde", + "serde_json", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + [[package]] name = "storage-map" version = "0.3.0" @@ -1399,6 +1615,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" + [[package]] name = "vec_map" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index df6ddb6..992cbd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" anyhow = "^1.0" bitfield = "^0.13" clap = "^2.33" +gilrs = "^0.8" pixels = "^0.2" winit = "^0.24" winit_input_helper = "^0.9" diff --git a/src/bus.rs b/src/bus.rs index bdeca75..61fe178 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -24,7 +24,7 @@ pub struct Bus { sound: Sound, high_ram: HighRam, serial: Serial, - joypad: Joypad, + pub joypad: Joypad, } impl Default for Bus { diff --git a/src/cartridge.rs b/src/cartridge.rs index 8adf70b..1485c46 100644 --- a/src/cartridge.rs +++ b/src/cartridge.rs @@ -238,8 +238,8 @@ impl MemoryBankController for NoMbc { MbcResult::Address(addr) } - fn handle_write(&mut self, _addr: u16, byte: u8) { - eprintln!("Tried to write {:#04X} to a read-only cartridge", byte); + fn handle_write(&mut self, _addr: u16, _byte: u8) { + // eprintln!("Tried to write {:#04X} to a read-only cartridge", byte); } } diff --git a/src/joypad.rs b/src/joypad.rs index 85d5c45..73a7639 100644 --- a/src/joypad.rs +++ b/src/joypad.rs @@ -3,7 +3,7 @@ use bitfield::bitfield; #[derive(Debug, Clone, Copy, Default)] pub struct Joypad { pub status: JoypadStatus, - interrupt: bool, + pub interrupt: bool, } impl Joypad { @@ -19,12 +19,46 @@ impl Joypad { bitfield! { pub struct JoypadStatus(u8); impl Debug; - from into ButtonRowStatus, action_row, set_action_row: 5, 5; - from into ButtonRowStatus, direction_row, set_direction_row: 4, 4; - from into ButtonStatus, _down, _set_down: 3, 3; - from into ButtonStatus, _up, _set_up: 2, 2; - from into ButtonStatus, _left, _set_left: 1, 1; - from into ButtonStatus, _right, _set_right: 0, 0; + from into RowState, action_row, set_action_row: 5, 5; + from into RowState, direction_row, set_direction_row: 4, 4; + from into ButtonState, down_start, _set_down_start: 3, 3; + from into ButtonState, up_select, _set_up_select: 2, 2; + from into ButtonState, left_b, _set_left_b: 1, 1; + from into ButtonState, right_a, _set_right_a: 0, 0; +} + +impl JoypadStatus { + pub fn set_down_start(&mut self, state: ButtonState, int: &mut bool) { + if !(*int) { + *int = self.down_start() == ButtonState::Released && state == ButtonState::Pressed; + } + + self._set_down_start(state); + } + + pub fn set_up_select(&mut self, state: ButtonState, int: &mut bool) { + if !(*int) { + *int = self.up_select() == ButtonState::Released && state == ButtonState::Pressed; + } + + self._set_up_select(state); + } + + pub fn set_left_b(&mut self, state: ButtonState, int: &mut bool) { + if !(*int) { + *int = self.left_b() == ButtonState::Released && state == ButtonState::Pressed; + } + + self._set_left_b(state); + } + + pub fn set_right_a(&mut self, state: ButtonState, int: &mut bool) { + if !(*int) { + *int = self.right_a() == ButtonState::Released && state == ButtonState::Pressed; + } + + self._set_right_a(state); + } } impl Default for JoypadStatus { @@ -52,85 +86,13 @@ impl From for u8 { } } -impl JoypadStatus { - pub fn set_down(&mut self, is_pressed: bool) { - // if is_pressed { - // self.select_direction_row(); - // } - self._set_down(is_pressed.into()); - } - - pub fn set_start(&mut self, is_pressed: bool) { - // if is_pressed { - // self.select_action_row(); - // } - self._set_down(is_pressed.into()); - } - - pub fn set_up(&mut self, is_pressed: bool) { - // if is_pressed { - // self.select_direction_row(); - // } - self._set_up(is_pressed.into()); - } - - pub fn set_select(&mut self, is_pressed: bool) { - // if is_pressed { - // self.select_action_row(); - // } - self._set_up(is_pressed.into()); - } - - pub fn set_left(&mut self, is_pressed: bool) { - // if is_pressed { - // self.select_direction_row(); - // } - self._set_left(is_pressed.into()); - } - - pub fn set_b(&mut self, is_pressed: bool) { - // if is_pressed { - // self.select_action_row(); - // } - self._set_left(is_pressed.into()); - } - - pub fn set_right(&mut self, is_pressed: bool) { - // if is_pressed { - // self.select_direction_row(); - // } - self._set_right(is_pressed.into()); - } - - pub fn set_a(&mut self, is_pressed: bool) { - // if is_pressed { - // self.select_action_row(); - // } - self._set_right(is_pressed.into()); - } - - pub fn select_direction_row(&mut self) { - use ButtonRowStatus::*; - - self.set_direction_row(Selected); - self.set_action_row(Deselected); - } - - pub fn select_action_row(&mut self) { - use ButtonRowStatus::*; - - self.set_action_row(Selected); - self.set_direction_row(Deselected); - } -} - -#[derive(Debug, Clone, Copy)] -enum ButtonStatus { +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ButtonState { Pressed = 0, Released = 1, } -impl From for ButtonStatus { +impl From for ButtonState { fn from(byte: u8) -> Self { match byte { 0b00 => Self::Pressed, @@ -140,13 +102,13 @@ impl From for ButtonStatus { } } -impl From for u8 { - fn from(status: ButtonStatus) -> Self { +impl From for u8 { + fn from(status: ButtonState) -> Self { status as u8 } } -impl From for ButtonStatus { +impl From for ButtonState { fn from(value: bool) -> Self { match value { true => Self::Pressed, @@ -155,13 +117,13 @@ impl From for ButtonStatus { } } -#[derive(Debug, Clone, Copy)] -enum ButtonRowStatus { +#[derive(Debug, Clone, Copy, PartialEq)] +enum RowState { Selected, Deselected, } -impl From for ButtonRowStatus { +impl From for RowState { fn from(byte: u8) -> Self { match byte { 0b00 => Self::Selected, @@ -171,8 +133,8 @@ impl From for ButtonRowStatus { } } -impl From for u8 { - fn from(status: ButtonRowStatus) -> Self { +impl From for u8 { + fn from(status: RowState) -> Self { status as u8 } } diff --git a/src/lib.rs b/src/lib.rs index e31cba8..b72fc61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub use cpu::Cpu as LR35902; pub use instruction::Cycle; +pub use joypad::ButtonState; pub const GB_WIDTH: usize = 160; pub const GB_HEIGHT: usize = 144; diff --git a/src/main.rs b/src/main.rs index 506ed42..f4fa524 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ use anyhow::{anyhow, Result}; use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use gb::LR35902_CLOCK_SPEED; -use gb::{Cycle, LR35902}; +use gb::{ButtonState, Cycle, LR35902}; +use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType, Gamepad, Gilrs}; use pixels::{Pixels, SurfaceTexture}; use std::time::{Duration, Instant}; use winit::dpi::LogicalSize; @@ -61,6 +62,10 @@ fn main() -> Result<()> { let default_title = "DMG-01 Emulator"; let cartridge_title = game_boy.rom_title().unwrap_or(&default_title); + // Initialize Gamepad Support + let mut gilrs = Gilrs::new().expect("Failed to initialize Gilrs"); + let mut active_gamepad = None; + // Initialize GUI let event_loop = EventLoop::new(); let mut input = WinitInputHelper::new(); @@ -91,6 +96,10 @@ fn main() -> Result<()> { pixels.resize(size.width, size.height); } + if active_gamepad.is_none() { + active_gamepad = gilrs.next_event().map(|e| e.id); + } + // Emulate Game Boy let delta = now.elapsed().subsec_nanos(); now = Instant::now(); @@ -100,6 +109,10 @@ fn main() -> Result<()> { let mut elapsed_cycles: Cycle = Default::default(); while elapsed_cycles <= pending_cycles { + if let Some(event) = gilrs.next_event() { + handle_gamepad_input(&mut game_boy, event); + } + elapsed_cycles += game_boy.step(); } @@ -131,3 +144,43 @@ fn create_pixels(window: &Window) -> Result> { let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, window); Ok(Pixels::new(GB_WIDTH, GB_HEIGHT, surface_texture)?) } + +fn handle_gamepad_input(game_boy: &mut LR35902, event: GamepadEvent) { + use GamepadEventType::*; + let joypad_status = &mut game_boy.bus.joypad.status; + let interrupt = &mut game_boy.bus.joypad.interrupt; + + match event.event { + ButtonPressed(button, _) => match button { + Button::DPadDown | Button::Start => { + joypad_status.set_down_start(ButtonState::Pressed, interrupt) + } + Button::DPadUp | Button::Select => { + joypad_status.set_up_select(ButtonState::Pressed, interrupt) + } + Button::DPadLeft | Button::South => { + joypad_status.set_left_b(ButtonState::Pressed, interrupt) + } + Button::DPadRight | Button::East => { + joypad_status.set_right_a(ButtonState::Pressed, interrupt) + } + _ => {} + }, + ButtonReleased(button, _) => match button { + Button::DPadDown | Button::Start => { + joypad_status.set_down_start(ButtonState::Released, interrupt) + } + Button::DPadUp | Button::Select => { + joypad_status.set_up_select(ButtonState::Released, interrupt) + } + Button::DPadLeft | Button::South => { + joypad_status.set_left_b(ButtonState::Released, interrupt) + } + Button::DPadRight | Button::East => { + joypad_status.set_right_a(ButtonState::Released, interrupt) + } + _ => {} + }, + _ => {} + } +}