chore: implement gamepad controls

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-05-03 23:11:39 -05:00
parent 1e19854ab0
commit 4abb2833c4
7 changed files with 333 additions and 94 deletions

222
Cargo.lock generated
View File

@ -66,6 +66,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "base-x"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
[[package]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.2" version = "0.5.2"
@ -200,6 +206,16 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" 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]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.7.0" version = "0.7.0"
@ -220,6 +236,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "core-foundation-sys"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.7.0" version = "0.7.0"
@ -339,6 +361,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "discard"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]] [[package]]
name = "dispatch" name = "dispatch"
version = "0.2.0" version = "0.2.0"
@ -514,6 +542,7 @@ dependencies = [
"anyhow", "anyhow",
"bitfield", "bitfield",
"clap", "clap",
"gilrs",
"pixels", "pixels",
"winit", "winit",
"winit_input_helper", "winit_input_helper",
@ -665,6 +694,38 @@ dependencies = [
"slab", "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]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.18" version = "0.1.18"
@ -695,6 +756,16 @@ dependencies = [
"cfg-if 1.0.0", "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]] [[package]]
name = "iovec" name = "iovec"
version = "0.1.4" version = "0.1.4"
@ -704,6 +775,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]] [[package]]
name = "jni-sys" name = "jni-sys"
version = "0.3.0" version = "0.3.0"
@ -776,6 +853,16 @@ dependencies = [
"winapi 0.3.9", "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]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.3" version = "0.4.3"
@ -794,6 +881,15 @@ dependencies = [
"cfg-if 1.0.0", "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]] [[package]]
name = "malloc_buf" name = "malloc_buf"
version = "0.0.6" version = "0.0.6"
@ -1181,6 +1277,15 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]] [[package]]
name = "rusttype" name = "rusttype"
version = "0.9.2" version = "0.9.2"
@ -1191,6 +1296,23 @@ dependencies = [
"owned_ttf_parser", "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]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -1212,12 +1334,55 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 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]] [[package]]
name = "serde" name = "serde"
version = "1.0.125" version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" 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]] [[package]]
name = "slab" name = "slab"
version = "0.4.3" version = "0.4.3"
@ -1270,6 +1435,57 @@ dependencies = [
"num-traits", "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]] [[package]]
name = "storage-map" name = "storage-map"
version = "0.3.0" version = "0.3.0"
@ -1399,6 +1615,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "uuid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
[[package]] [[package]]
name = "vec_map" name = "vec_map"
version = "0.8.2" version = "0.8.2"

View File

@ -10,6 +10,7 @@ edition = "2018"
anyhow = "^1.0" anyhow = "^1.0"
bitfield = "^0.13" bitfield = "^0.13"
clap = "^2.33" clap = "^2.33"
gilrs = "^0.8"
pixels = "^0.2" pixels = "^0.2"
winit = "^0.24" winit = "^0.24"
winit_input_helper = "^0.9" winit_input_helper = "^0.9"

View File

@ -24,7 +24,7 @@ pub struct Bus {
sound: Sound, sound: Sound,
high_ram: HighRam, high_ram: HighRam,
serial: Serial, serial: Serial,
joypad: Joypad, pub joypad: Joypad,
} }
impl Default for Bus { impl Default for Bus {

View File

@ -238,8 +238,8 @@ impl MemoryBankController for NoMbc {
MbcResult::Address(addr) MbcResult::Address(addr)
} }
fn handle_write(&mut self, _addr: u16, byte: u8) { fn handle_write(&mut self, _addr: u16, _byte: u8) {
eprintln!("Tried to write {:#04X} to a read-only cartridge", byte); // eprintln!("Tried to write {:#04X} to a read-only cartridge", byte);
} }
} }

View File

@ -3,7 +3,7 @@ use bitfield::bitfield;
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct Joypad { pub struct Joypad {
pub status: JoypadStatus, pub status: JoypadStatus,
interrupt: bool, pub interrupt: bool,
} }
impl Joypad { impl Joypad {
@ -19,12 +19,46 @@ impl Joypad {
bitfield! { bitfield! {
pub struct JoypadStatus(u8); pub struct JoypadStatus(u8);
impl Debug; impl Debug;
from into ButtonRowStatus, action_row, set_action_row: 5, 5; from into RowState, action_row, set_action_row: 5, 5;
from into ButtonRowStatus, direction_row, set_direction_row: 4, 4; from into RowState, direction_row, set_direction_row: 4, 4;
from into ButtonStatus, _down, _set_down: 3, 3; from into ButtonState, down_start, _set_down_start: 3, 3;
from into ButtonStatus, _up, _set_up: 2, 2; from into ButtonState, up_select, _set_up_select: 2, 2;
from into ButtonStatus, _left, _set_left: 1, 1; from into ButtonState, left_b, _set_left_b: 1, 1;
from into ButtonStatus, _right, _set_right: 0, 0; 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 { impl Default for JoypadStatus {
@ -52,85 +86,13 @@ impl From<JoypadStatus> for u8 {
} }
} }
impl JoypadStatus { #[derive(Debug, Clone, Copy, PartialEq)]
pub fn set_down(&mut self, is_pressed: bool) { pub enum ButtonState {
// 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 {
Pressed = 0, Pressed = 0,
Released = 1, Released = 1,
} }
impl From<u8> for ButtonStatus { impl From<u8> for ButtonState {
fn from(byte: u8) -> Self { fn from(byte: u8) -> Self {
match byte { match byte {
0b00 => Self::Pressed, 0b00 => Self::Pressed,
@ -140,13 +102,13 @@ impl From<u8> for ButtonStatus {
} }
} }
impl From<ButtonStatus> for u8 { impl From<ButtonState> for u8 {
fn from(status: ButtonStatus) -> Self { fn from(status: ButtonState) -> Self {
status as u8 status as u8
} }
} }
impl From<bool> for ButtonStatus { impl From<bool> for ButtonState {
fn from(value: bool) -> Self { fn from(value: bool) -> Self {
match value { match value {
true => Self::Pressed, true => Self::Pressed,
@ -155,13 +117,13 @@ impl From<bool> for ButtonStatus {
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq)]
enum ButtonRowStatus { enum RowState {
Selected, Selected,
Deselected, Deselected,
} }
impl From<u8> for ButtonRowStatus { impl From<u8> for RowState {
fn from(byte: u8) -> Self { fn from(byte: u8) -> Self {
match byte { match byte {
0b00 => Self::Selected, 0b00 => Self::Selected,
@ -171,8 +133,8 @@ impl From<u8> for ButtonRowStatus {
} }
} }
impl From<ButtonRowStatus> for u8 { impl From<RowState> for u8 {
fn from(status: ButtonRowStatus) -> Self { fn from(status: RowState) -> Self {
status as u8 status as u8
} }
} }

View File

@ -1,5 +1,6 @@
pub use cpu::Cpu as LR35902; pub use cpu::Cpu as LR35902;
pub use instruction::Cycle; pub use instruction::Cycle;
pub use joypad::ButtonState;
pub const GB_WIDTH: usize = 160; pub const GB_WIDTH: usize = 160;
pub const GB_HEIGHT: usize = 144; pub const GB_HEIGHT: usize = 144;

View File

@ -1,7 +1,8 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
use gb::LR35902_CLOCK_SPEED; 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 pixels::{Pixels, SurfaceTexture};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use winit::dpi::LogicalSize; use winit::dpi::LogicalSize;
@ -61,6 +62,10 @@ fn main() -> Result<()> {
let default_title = "DMG-01 Emulator"; let default_title = "DMG-01 Emulator";
let cartridge_title = game_boy.rom_title().unwrap_or(&default_title); 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 // Initialize GUI
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let mut input = WinitInputHelper::new(); let mut input = WinitInputHelper::new();
@ -91,6 +96,10 @@ fn main() -> Result<()> {
pixels.resize(size.width, size.height); pixels.resize(size.width, size.height);
} }
if active_gamepad.is_none() {
active_gamepad = gilrs.next_event().map(|e| e.id);
}
// Emulate Game Boy // Emulate Game Boy
let delta = now.elapsed().subsec_nanos(); let delta = now.elapsed().subsec_nanos();
now = Instant::now(); now = Instant::now();
@ -100,6 +109,10 @@ fn main() -> Result<()> {
let mut elapsed_cycles: Cycle = Default::default(); let mut elapsed_cycles: Cycle = Default::default();
while elapsed_cycles <= pending_cycles { while elapsed_cycles <= pending_cycles {
if let Some(event) = gilrs.next_event() {
handle_gamepad_input(&mut game_boy, event);
}
elapsed_cycles += game_boy.step(); elapsed_cycles += game_boy.step();
} }
@ -131,3 +144,43 @@ fn create_pixels(window: &Window) -> Result<Pixels<Window>> {
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, window); let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, window);
Ok(Pixels::new(GB_WIDTH, GB_HEIGHT, surface_texture)?) 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)
}
_ => {}
},
_ => {}
}
}