use super::ControllerInput;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JoypadState {
pub strobe: bool,
pub button_index: u8,
pub button_states: u8,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Button {
A = 0,
B = 1,
Select = 2,
Start = 3,
Up = 4,
Down = 5,
Left = 6,
Right = 7,
}
pub struct NesJoypad {
strobe: bool,
button_index: u8,
button_states: u8, }
impl Default for NesJoypad {
fn default() -> Self {
Self::new()
}
}
impl NesJoypad {
pub fn new() -> Self {
Self {
strobe: false,
button_index: 0,
button_states: 0,
}
}
pub fn write_strobe(&mut self, value: u8) {
let new_strobe = value & 0x01 != 0;
if self.strobe && !new_strobe {
self.button_index = 0;
}
self.strobe = new_strobe;
}
pub fn read(&mut self, is_dummy_read: bool) -> u8 {
if self.button_index >= 8 {
return 1;
}
let mut cleaned_states = self.button_states;
let left_mask = 1 << (Button::Left as u8);
let right_mask = 1 << (Button::Right as u8);
if (cleaned_states & left_mask) != 0 && (cleaned_states & right_mask) != 0 {
cleaned_states &= !(left_mask | right_mask);
}
let up_mask = 1 << (Button::Up as u8);
let down_mask = 1 << (Button::Down as u8);
if (cleaned_states & up_mask) != 0 && (cleaned_states & down_mask) != 0 {
cleaned_states &= !(up_mask | down_mask);
}
let response = (cleaned_states >> self.button_index) & 0x01;
if !self.strobe && !is_dummy_read {
self.button_index += 1;
}
response
}
pub fn set_button(&mut self, button: Button, pressed: bool) {
let bit = button as u8;
if pressed {
self.button_states |= 1 << bit;
} else {
self.button_states &= !(1 << bit);
}
}
pub fn capture_state(&self) -> JoypadState {
JoypadState {
strobe: self.strobe,
button_index: self.button_index,
button_states: self.button_states,
}
}
pub fn restore_state(&mut self, state: &JoypadState) {
self.strobe = state.strobe;
self.button_index = state.button_index;
self.button_states = state.button_states;
}
}
impl crate::input::Controller for NesJoypad {
fn write_strobe(&mut self, value: u8) {
self.write_strobe(value)
}
fn read(&mut self, is_dummy_read: bool) -> u8 {
self.read(is_dummy_read)
}
fn capture_state(&self) -> crate::input::ControllerState {
crate::input::ControllerState::Joypad(self.capture_state())
}
fn restore_state(&mut self, state: &crate::input::ControllerState) {
if let crate::input::ControllerState::Joypad(joypad_state) = state {
self.restore_state(joypad_state);
}
}
fn set_button(&mut self, button: crate::input::Button, pressed: bool) -> bool {
self.set_button(button, pressed);
true
}
fn set_mouse_x_position(&mut self, _position: u8) -> bool {
false }
fn set_mouse_y_position(&mut self, _position: u8) -> bool {
false }
fn set_mouse_left_button(&mut self, _pressed: bool) -> bool {
false }
fn input_type(&self) -> ControllerInput {
crate::input::controller_input_type(crate::input::ControllerType::Joypad)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_joypad() {
let joypad = NesJoypad::new();
assert!(!joypad.strobe);
assert_eq!(joypad.button_index, 0);
assert_eq!(joypad.button_states, 0);
}
#[test]
fn test_strobe_reset() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::A, true);
joypad.read(false);
assert_eq!(joypad.button_index, 1);
joypad.read(false);
assert_eq!(joypad.button_index, 2);
joypad.write_strobe(1);
joypad.write_strobe(0);
assert_eq!(joypad.button_index, 0);
}
#[test]
fn test_sequential_button_reading() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::A, true);
joypad.set_button(Button::Start, true);
joypad.set_button(Button::Right, true);
assert_eq!(joypad.read(false), 1); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 1); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 1); }
#[test]
fn test_ninth_read_returns_one() {
let mut joypad = NesJoypad::new();
for _ in 0..8 {
joypad.read(false);
}
assert_eq!(joypad.read(false), 1);
assert_eq!(joypad.read(false), 1);
assert_eq!(joypad.read(false), 1);
}
#[test]
fn test_strobe_holds_same_button() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::B, true);
joypad.write_strobe(1);
assert_eq!(joypad.read(false), 0); assert_eq!(joypad.button_index, 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.button_index, 0);
joypad.write_strobe(0);
assert_eq!(joypad.read(false), 0); assert_eq!(joypad.button_index, 1);
assert_eq!(joypad.read(false), 1); assert_eq!(joypad.button_index, 2);
}
#[test]
fn test_button_state_changes() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::A, true);
assert_eq!(joypad.read(false), 1);
joypad.write_strobe(1);
joypad.write_strobe(0);
joypad.set_button(Button::A, false);
joypad.set_button(Button::B, true);
assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 1); }
#[test]
fn test_all_buttons() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::A, true);
joypad.set_button(Button::B, true);
joypad.set_button(Button::Select, true);
joypad.set_button(Button::Start, true);
joypad.set_button(Button::Up, true);
joypad.set_button(Button::Down, true);
joypad.set_button(Button::Left, true);
joypad.set_button(Button::Right, true);
assert_eq!(joypad.read(false), 1); assert_eq!(joypad.read(false), 1); assert_eq!(joypad.read(false), 1); assert_eq!(joypad.read(false), 1); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); }
#[test]
fn test_dummy_read_does_not_advance_button_index() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::A, true);
assert_eq!(joypad.read(false), 1);
assert_eq!(joypad.read(true), 0);
assert_eq!(joypad.read(false), 0);
}
#[test]
fn test_left_right_simultaneous_cancels_both() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::Left, true);
joypad.set_button(Button::Right, true);
assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); }
#[test]
fn test_up_down_simultaneous_cancels_both() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::Up, true);
joypad.set_button(Button::Down, true);
assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); }
#[test]
fn test_left_only_works() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::Left, true);
for _ in 0..6 {
joypad.read(false);
}
assert_eq!(joypad.read(false), 1); assert_eq!(joypad.read(false), 0); }
#[test]
fn test_right_only_works() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::Right, true);
for _ in 0..6 {
joypad.read(false);
}
assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 1); }
#[test]
fn test_up_only_works() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::Up, true);
for _ in 0..4 {
joypad.read(false);
}
assert_eq!(joypad.read(false), 1); assert_eq!(joypad.read(false), 0); }
#[test]
fn test_down_only_works() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::Down, true);
for _ in 0..4 {
joypad.read(false);
}
assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 1); }
#[test]
fn test_socd_with_other_buttons() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::A, true);
joypad.set_button(Button::Left, true);
joypad.set_button(Button::Right, true);
assert_eq!(joypad.read(false), 1); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); assert_eq!(joypad.read(false), 0); }
#[test]
fn test_socd_in_strobe_mode() {
let mut joypad = NesJoypad::new();
joypad.set_button(Button::Left, true);
joypad.set_button(Button::Right, true);
for _ in 0..6 {
joypad.read(false);
}
assert_eq!(joypad.read(true), 0); assert_eq!(joypad.read(true), 0); assert_eq!(joypad.read(true), 0);
assert_eq!(joypad.read(false), 0); }
}