fix(joypad): reimplement joypad

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-06-07 17:23:48 -05:00
parent 83994e6adc
commit 1449ed3765
4 changed files with 105 additions and 146 deletions

View File

@ -225,7 +225,7 @@ impl BusIo for Bus {
// Every address here starts with 0xFF so we can just check the // Every address here starts with 0xFF so we can just check the
// low byte to figure out which register it is // low byte to figure out which register it is
match addr & 0x00FF { match addr & 0x00FF {
0x00 => self.joypad.status.into(), 0x00 => self.joypad.status,
0x01 => self.serial.next, 0x01 => self.serial.next,
0x02 => self.serial.control.into(), 0x02 => self.serial.control.into(),
0x04 => (self.timer.divider >> 8) as u8, 0x04 => (self.timer.divider >> 8) as u8,
@ -334,7 +334,7 @@ impl BusIo for Bus {
// Every address here starts with 0xFF so we can just check the // Every address here starts with 0xFF so we can just check the
// low byte to figure out which register it is // low byte to figure out which register it is
match addr & 0x00FF { match addr & 0x00FF {
0x00 => self.joypad.status.update(byte), 0x00 => self.joypad.update(byte),
0x01 => self.serial.next = byte, 0x01 => self.serial.next = byte,
0x02 => self.serial.control = byte.into(), 0x02 => self.serial.control = byte.into(),
0x04 => self.timer.divider = 0x0000, 0x04 => self.timer.divider = 0x0000,

View File

@ -1,9 +1,20 @@
use bitfield::bitfield; use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType};
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy)]
pub struct Joypad { pub struct Joypad {
pub status: JoypadStatus, pub(crate) status: u8,
pub interrupt: bool, ext: JoypadState,
interrupt: bool,
}
impl Default for Joypad {
fn default() -> Self {
Self {
status: 0xFF,
ext: Default::default(),
interrupt: Default::default(),
}
}
} }
impl Joypad { impl Joypad {
@ -14,131 +25,119 @@ impl Joypad {
pub(crate) fn set_interrupt(&mut self, value: bool) { pub(crate) fn set_interrupt(&mut self, value: bool) {
self.interrupt = value; self.interrupt = value;
} }
}
bitfield! {
pub struct JoypadStatus(u8);
impl Debug;
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(crate) fn update(&mut self, byte: u8) { pub(crate) fn update(&mut self, byte: u8) {
// Bytes 3 -> 0 are Read Only let direction_row = (byte >> 4) & 0x01 == 0x00;
let mask = 0b00001111; let action_row = (byte >> 5) & 0x01 == 0x00;
let read_only = self.0 & mask; let updated = match (direction_row, action_row) {
self.0 = (byte & !mask) | read_only; (true, false) => (byte & 0x30) | self.ext.as_direction_bits(),
(false, true) => (byte & 0x30) | self.ext.as_action_bits(),
_ => {
// TODO: What if both or no rows are selected?
let merge = self.ext.as_action_bits() | self.ext.as_action_bits();
(byte & 0x30) | merge
}
};
self.status = updated
} }
} }
impl JoypadStatus { #[derive(Debug, Clone, Copy, Default)]
pub fn set_down_start(&mut self, state: ButtonState, int: &mut bool) { struct JoypadState {
if !(*int) { // Direction Row
*int = self.down_start() == ButtonState::Released && state == ButtonState::Pressed; dpad_down: ButtonEvent,
dpad_up: ButtonEvent,
dpad_left: ButtonEvent,
dpad_right: ButtonEvent,
// Action Row
start: ButtonEvent,
select: ButtonEvent,
south: ButtonEvent,
east: ButtonEvent,
} }
self._set_down_start(state); impl JoypadState {
fn as_direction_bits(&self) -> u8 {
(self.dpad_down as u8) << 3
| (self.dpad_up as u8) << 2
| (self.dpad_left as u8) << 1
| self.dpad_right as u8
} }
pub fn set_up_select(&mut self, state: ButtonState, int: &mut bool) { fn as_action_bits(&self) -> u8 {
if !(*int) { (self.start as u8) << 3
*int = self.up_select() == ButtonState::Released && state == ButtonState::Pressed; | (self.select as u8) << 2
} | (self.south as u8) << 1
| self.east as u8
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 {
fn default() -> Self {
Self(0xFF)
}
}
impl Copy for JoypadStatus {}
impl Clone for JoypadStatus {
fn clone(&self) -> Self {
*self
}
}
impl From<JoypadStatus> for u8 {
fn from(status: JoypadStatus) -> Self {
status.0
} }
} }
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum ButtonState { enum ButtonEvent {
Pressed = 0, Pressed = 0,
Released = 1, Standby = 1,
} }
impl From<u8> for ButtonState { // used in the context of is_pressed: bool
fn from(byte: u8) -> Self { impl From<bool> for ButtonEvent {
match byte & 0b01 {
0b00 => Self::Pressed,
0b01 => Self::Released,
_ => unreachable!("{:#04X} is not a valid value for ButtonStatus", byte),
}
}
}
impl From<ButtonState> for u8 {
fn from(status: ButtonState) -> Self {
status as u8
}
}
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,
false => Self::Released, false => Self::Standby,
} }
} }
} }
#[derive(Debug, Clone, Copy, PartialEq)] impl Default for ButtonEvent {
enum RowState { fn default() -> Self {
Selected, Self::Standby
Deselected, }
} }
impl From<u8> for RowState { impl ButtonEvent {
fn from(byte: u8) -> Self { fn update(&mut self, is_pressed: bool, irq: &mut bool) {
match byte & 0b01 { *self = is_pressed.into();
0b00 => Self::Selected,
0b01 => Self::Deselected, if let ButtonEvent::Pressed = *self {
_ => unreachable!("{:#04X} is not a valid value for ButtonRowStatus", byte), *irq = true;
} }
} }
} }
impl From<RowState> for u8 { pub fn handle_gamepad_input(pad: &mut Joypad, event: GamepadEvent) {
fn from(status: RowState) -> Self { use Button::*;
status as u8 use GamepadEventType::*;
let state = &mut pad.ext;
let irq = &mut pad.interrupt;
match event.event {
ButtonPressed(btn, _) => match btn {
DPadDown => state.dpad_down.update(true, irq),
DPadUp => state.dpad_up.update(true, irq),
DPadLeft => state.dpad_left.update(true, irq),
DPadRight => state.dpad_right.update(true, irq),
Start => state.start.update(true, irq),
Select => state.select.update(true, irq),
South => state.south.update(true, irq),
East => state.east.update(true, irq),
_ => {}
},
ButtonReleased(btn, _) => match btn {
DPadDown => state.dpad_down.update(false, irq),
DPadUp => state.dpad_up.update(false, irq),
DPadLeft => state.dpad_left.update(false, irq),
DPadRight => state.dpad_right.update(false, irq),
Start => state.start.update(false, irq),
Select => state.select.update(false, irq),
South => state.south.update(false, irq),
East => state.east.update(false, irq),
_ => {}
},
_ => {}
} }
} }

View File

@ -1,7 +1,7 @@
pub use cpu::Cpu as LR35902; pub use cpu::Cpu as LR35902;
pub use gui::Egui; pub use gui::Egui;
pub use instruction::Cycle; pub use instruction::Cycle;
pub use joypad::ButtonState; pub use joypad::handle_gamepad_input;
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
pub use cpu::RegisterPair; pub use cpu::RegisterPair;

View File

@ -2,8 +2,8 @@ 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::Egui; use gb::Egui;
use gb::LR35902_CLOCK_SPEED; use gb::LR35902_CLOCK_SPEED;
use gb::{ButtonState, Cycle, LR35902}; use gb::{handle_gamepad_input, Cycle, LR35902};
use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType, Gilrs}; use gilrs::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;
@ -168,7 +168,7 @@ fn main() -> Result<()> {
while elapsed_cycles <= pending_cycles { while elapsed_cycles <= pending_cycles {
if let Some(event) = gilrs.next_event() { if let Some(event) = gilrs.next_event() {
handle_gamepad_input(&mut game_boy, event); handle_gamepad_input(&mut game_boy.bus.joypad, event);
} }
elapsed_cycles += game_boy.step(); elapsed_cycles += game_boy.step();
@ -207,43 +207,3 @@ fn create_window(event_loop: &EventLoop<()>, title: &str) -> Result<Window> {
.with_min_inner_size(size) .with_min_inner_size(size)
.build(event_loop)?) .build(event_loop)?)
} }
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)
}
_ => {}
},
_ => {}
}
}