chore: implement gamepad controls
This commit is contained in:
@@ -24,7 +24,7 @@ pub struct Bus {
|
||||
sound: Sound,
|
||||
high_ram: HighRam,
|
||||
serial: Serial,
|
||||
joypad: Joypad,
|
||||
pub joypad: Joypad,
|
||||
}
|
||||
|
||||
impl Default for Bus {
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
142
src/joypad.rs
142
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<JoypadStatus> 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<u8> for ButtonStatus {
|
||||
impl From<u8> for ButtonState {
|
||||
fn from(byte: u8) -> Self {
|
||||
match byte {
|
||||
0b00 => Self::Pressed,
|
||||
@@ -140,13 +102,13 @@ impl From<u8> for ButtonStatus {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ButtonStatus> for u8 {
|
||||
fn from(status: ButtonStatus) -> Self {
|
||||
impl From<ButtonState> for u8 {
|
||||
fn from(status: ButtonState) -> Self {
|
||||
status as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for ButtonStatus {
|
||||
impl From<bool> for ButtonState {
|
||||
fn from(value: bool) -> Self {
|
||||
match value {
|
||||
true => Self::Pressed,
|
||||
@@ -155,13 +117,13 @@ impl From<bool> for ButtonStatus {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ButtonRowStatus {
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
enum RowState {
|
||||
Selected,
|
||||
Deselected,
|
||||
}
|
||||
|
||||
impl From<u8> for ButtonRowStatus {
|
||||
impl From<u8> for RowState {
|
||||
fn from(byte: u8) -> Self {
|
||||
match byte {
|
||||
0b00 => Self::Selected,
|
||||
@@ -171,8 +133,8 @@ impl From<u8> for ButtonRowStatus {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ButtonRowStatus> for u8 {
|
||||
fn from(status: ButtonRowStatus) -> Self {
|
||||
impl From<RowState> for u8 {
|
||||
fn from(status: RowState) -> Self {
|
||||
status as u8
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
55
src/main.rs
55
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<Pixels<Window>> {
|
||||
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)
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user