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
|
// 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,
|
||||||
|
|
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 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,
|
||||||
self._set_down_start(state);
|
dpad_right: ButtonEvent,
|
||||||
}
|
// Action Row
|
||||||
|
start: ButtonEvent,
|
||||||
pub fn set_up_select(&mut self, state: ButtonState, int: &mut bool) {
|
select: ButtonEvent,
|
||||||
if !(*int) {
|
south: ButtonEvent,
|
||||||
*int = self.up_select() == ButtonState::Released && state == ButtonState::Pressed;
|
east: ButtonEvent,
|
||||||
}
|
|
||||||
|
|
||||||
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 JoypadState {
|
||||||
fn default() -> Self {
|
fn as_direction_bits(&self) -> u8 {
|
||||||
Self(0xFF)
|
(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 {}
|
fn as_action_bits(&self) -> u8 {
|
||||||
impl Clone for JoypadStatus {
|
(self.start as u8) << 3
|
||||||
fn clone(&self) -> Self {
|
| (self.select as u8) << 2
|
||||||
*self
|
| (self.south as u8) << 1
|
||||||
}
|
| self.east as u8
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
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 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)
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue