fix(joypad): reimplement joypad
This commit is contained in:
parent
83994e6adc
commit
1449ed3765
|
@ -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,
|
||||
|
|
199
src/joypad.rs
199
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;
|
||||
#[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,
|
||||
}
|
||||
|
||||
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) {
|
||||
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 {
|
||||
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
|
||||
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<u8> 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<ButtonState> for u8 {
|
||||
fn from(status: ButtonState) -> Self {
|
||||
status as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for ButtonState {
|
||||
// used in the context of is_pressed: bool
|
||||
impl From<bool> 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<u8> 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<RowState> 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),
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
46
src/main.rs
46
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<Window> {
|
|||
.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)
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue