diff --git a/src/bus.rs b/src/bus.rs index 7f93031..91c9731 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -225,7 +225,7 @@ impl BusIo for Bus { // Every address here starts with 0xFF so we can just check the // low byte to figure out which register it is match addr & 0x00FF { - 0x00 => self.joypad.status.into(), + 0x00 => self.joypad.status, 0x01 => self.serial.next, 0x02 => self.serial.control.into(), 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 // low byte to figure out which register it is match addr & 0x00FF { - 0x00 => self.joypad.status.update(byte), + 0x00 => self.joypad.update(byte), 0x01 => self.serial.next = byte, 0x02 => self.serial.control = byte.into(), 0x04 => self.timer.divider = 0x0000, diff --git a/src/joypad.rs b/src/joypad.rs index 09e6dbb..f1db0ca 100644 --- a/src/joypad.rs +++ b/src/joypad.rs @@ -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 status: JoypadStatus, - pub interrupt: bool, + pub(crate) status: u8, + ext: JoypadState, + interrupt: bool, +} + +impl Default for Joypad { + fn default() -> Self { + Self { + status: 0xFF, + ext: Default::default(), + interrupt: Default::default(), + } + } } impl Joypad { @@ -14,131 +25,119 @@ impl Joypad { pub(crate) fn set_interrupt(&mut self, value: bool) { 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) { - // Bytes 3 -> 0 are Read Only - let mask = 0b00001111; + let direction_row = (byte >> 4) & 0x01 == 0x00; + let action_row = (byte >> 5) & 0x01 == 0x00; - let read_only = self.0 & mask; - self.0 = (byte & !mask) | read_only; + let updated = match (direction_row, action_row) { + (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 { - 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); - } +#[derive(Debug, Clone, Copy, Default)] +struct JoypadState { + // Direction Row + dpad_down: ButtonEvent, + dpad_up: ButtonEvent, + dpad_left: ButtonEvent, + dpad_right: ButtonEvent, + // Action Row + start: ButtonEvent, + select: ButtonEvent, + south: ButtonEvent, + east: ButtonEvent, } -impl Default for JoypadStatus { - fn default() -> Self { - Self(0xFF) +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 } -} -impl Copy for JoypadStatus {} -impl Clone for JoypadStatus { - fn clone(&self) -> Self { - *self - } -} - -impl From for u8 { - fn from(status: JoypadStatus) -> Self { - status.0 + fn as_action_bits(&self) -> u8 { + (self.start as u8) << 3 + | (self.select as u8) << 2 + | (self.south as u8) << 1 + | self.east as u8 } } #[derive(Debug, Clone, Copy, PartialEq)] -pub enum ButtonState { +enum ButtonEvent { Pressed = 0, - Released = 1, + Standby = 1, } -impl From for ButtonState { - fn from(byte: u8) -> Self { - match byte & 0b01 { - 0b00 => Self::Pressed, - 0b01 => Self::Released, - _ => unreachable!("{:#04X} is not a valid value for ButtonStatus", byte), - } - } -} - -impl From for u8 { - fn from(status: ButtonState) -> Self { - status as u8 - } -} - -impl From for ButtonState { +// used in the context of is_pressed: bool +impl From for ButtonEvent { fn from(value: bool) -> Self { match value { true => Self::Pressed, - false => Self::Released, + false => Self::Standby, } } } -#[derive(Debug, Clone, Copy, PartialEq)] -enum RowState { - Selected, - Deselected, +impl Default for ButtonEvent { + fn default() -> Self { + Self::Standby + } } -impl From for RowState { - fn from(byte: u8) -> Self { - match byte & 0b01 { - 0b00 => Self::Selected, - 0b01 => Self::Deselected, - _ => unreachable!("{:#04X} is not a valid value for ButtonRowStatus", byte), +impl ButtonEvent { + fn update(&mut self, is_pressed: bool, irq: &mut bool) { + *self = is_pressed.into(); + + if let ButtonEvent::Pressed = *self { + *irq = true; } } } -impl From for u8 { - fn from(status: RowState) -> Self { - status as u8 +pub fn handle_gamepad_input(pad: &mut Joypad, event: GamepadEvent) { + use Button::*; + 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), + _ => {} + }, + _ => {} } } diff --git a/src/lib.rs b/src/lib.rs index d6997d2..4605309 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ pub use cpu::Cpu as LR35902; pub use gui::Egui; pub use instruction::Cycle; -pub use joypad::ButtonState; +pub use joypad::handle_gamepad_input; #[cfg(feature = "debug")] pub use cpu::RegisterPair; diff --git a/src/main.rs b/src/main.rs index a7667dd..666288a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,8 @@ use anyhow::{anyhow, Result}; use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use gb::Egui; use gb::LR35902_CLOCK_SPEED; -use gb::{ButtonState, Cycle, LR35902}; -use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType, Gilrs}; +use gb::{handle_gamepad_input, Cycle, LR35902}; +use gilrs::Gilrs; use pixels::{Pixels, SurfaceTexture}; use std::time::{Duration, Instant}; use winit::dpi::LogicalSize; @@ -168,7 +168,7 @@ fn main() -> Result<()> { while elapsed_cycles <= pending_cycles { 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(); @@ -207,43 +207,3 @@ fn create_window(event_loop: &EventLoop<()>, title: &str) -> Result { .with_min_inner_size(size) .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) - } - _ => {} - }, - _ => {} - } -}