Newer
Older
//! Gamepad related functions and structures.
use std::{
fmt::{self, Display, Formatter},
io::Cursor,
};
use crate::{
mmu::BusComponent,
state::{StateComponent, StateFormat},
warnln,
};
use boytacean_common::{
data::{read_u8, write_u8},
error::Error,
};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
impl PadSelection {
pub fn description(&self) -> &'static str {
match self {
PadSelection::None => "None",
PadSelection::Action => "Action",
PadSelection::Direction => "Direction",
}
}
pub fn from_u8(value: u8) -> Self {
match value {
0x00 => PadSelection::None,
0x01 => PadSelection::Action,
0x02 => PadSelection::Direction,
_ => panic!("Invalid pad selection value: {value}"),
}
}
pub fn into_u8(self) -> u8 {
match self {
PadSelection::None => 0x00,
PadSelection::Action => 0x01,
PadSelection::Direction => 0x02,
}
}
}
impl Display for PadSelection {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl From<u8> for PadSelection {
fn from(value: u8) -> Self {
Self::from_u8(value)
}
}
impl From<PadSelection> for u8 {
fn from(value: PadSelection) -> Self {
value.into_u8()
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub enum PadKey {
Up,
Down,
Left,
Right,
Start,
Select,
A,
B,
}
impl PadKey {
pub fn from_u8(value: u8) -> Self {
match value {
1 => PadKey::Up,
2 => PadKey::Down,
3 => PadKey::Left,
4 => PadKey::Right,
5 => PadKey::Start,
6 => PadKey::Select,
7 => PadKey::A,
8 => PadKey::B,
impl From<u8> for PadKey {
fn from(value: u8) -> Self {
Self::from_u8(value)
}
}
pub struct Pad {
down: bool,
up: bool,
left: bool,
right: bool,
start: bool,
select: bool,
b: bool,
a: bool,
selection: PadSelection,
impl Pad {
pub fn new() -> Self {
Self {
down: false,
up: false,
left: false,
right: false,
start: false,
select: false,
b: false,
a: false,
match addr {
// 0xFF00 — P1/JOYP: Joypad
0xff00 => {
PadSelection::Action =>
{
#[allow(clippy::bool_to_int_with_if)]
| if self.b { 0x00 } else { 0x02 }
| if self.select { 0x00 } else { 0x04 }
PadSelection::Direction =>
{
#[allow(clippy::bool_to_int_with_if)]
| if self.left { 0x00 } else { 0x02 }
| if self.up { 0x00 } else { 0x04 }
value |= match self.selection {
PadSelection::Action => 0x10,
PadSelection::Direction => 0x20,
PadSelection::None => 0x30,
_ => {
warnln!("Reading from unknown Pad location 0x{:04x}", addr);
}
}
pub fn write(&mut self, addr: u16, value: u8) {
match addr {
// 0xFF00 — P1/JOYP: Joypad
0xff00 => {
self.selection = match value & 0x30 {
0x10 => PadSelection::Action,
0x20 => PadSelection::Direction,
0x30 => PadSelection::None,
_ => PadSelection::None,
};
_ => warnln!("Writing to unknown Pad location 0x{:04x}", addr),
pub fn key_press(&mut self, key: PadKey) {
match key {
PadKey::Up => self.up = true,
PadKey::Down => self.down = true,
PadKey::Left => self.left = true,
PadKey::Right => self.right = true,
PadKey::Start => self.start = true,
PadKey::Select => self.select = true,
PadKey::A => self.a = true,
PadKey::B => self.b = true,
}
// signals that a JoyPad interrupt is pending to be
// handled as a key press has been performed
}
pub fn key_lift(&mut self, key: PadKey) {
match key {
PadKey::Up => self.up = false,
PadKey::Down => self.down = false,
PadKey::Left => self.left = false,
PadKey::Right => self.right = false,
PadKey::Start => self.start = false,
PadKey::Select => self.select = false,
PadKey::A => self.a = false,
PadKey::B => self.b = false,
}
}
pub fn int_pad(&self) -> bool {
self.int_pad
}
pub fn set_int_pad(&mut self, value: bool) {
self.int_pad = value;
}
pub fn ack_pad(&mut self) {
self.set_int_pad(false);
}
impl BusComponent for Pad {
self.read(addr)
}
fn write(&mut self, addr: u16, value: u8) {
self.write(addr, value);
}
}
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
impl StateComponent for Pad {
fn state(&self, _format: Option<StateFormat>) -> Result<Vec<u8>, Error> {
let mut cursor = Cursor::new(vec![]);
write_u8(&mut cursor, self.down as u8)?;
write_u8(&mut cursor, self.up as u8)?;
write_u8(&mut cursor, self.left as u8)?;
write_u8(&mut cursor, self.right as u8)?;
write_u8(&mut cursor, self.start as u8)?;
write_u8(&mut cursor, self.select as u8)?;
write_u8(&mut cursor, self.b as u8)?;
write_u8(&mut cursor, self.a as u8)?;
write_u8(&mut cursor, self.selection.into())?;
write_u8(&mut cursor, self.int_pad as u8)?;
Ok(cursor.into_inner())
}
fn set_state(&mut self, data: &[u8], _format: Option<StateFormat>) -> Result<(), Error> {
let mut cursor: Cursor<&[u8]> = Cursor::new(data);
self.down = read_u8(&mut cursor)? != 0;
self.up = read_u8(&mut cursor)? != 0;
self.left = read_u8(&mut cursor)? != 0;
self.right = read_u8(&mut cursor)? != 0;
self.start = read_u8(&mut cursor)? != 0;
self.select = read_u8(&mut cursor)? != 0;
self.b = read_u8(&mut cursor)? != 0;
self.a = read_u8(&mut cursor)? != 0;
self.selection = read_u8(&mut cursor)?.into();
self.int_pad = read_u8(&mut cursor)? != 0;
Ok(())
}
}
impl Default for Pad {
fn default() -> Self {
Self::new()
}
}
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#[cfg(test)]
mod tests {
use crate::state::StateComponent;
use super::{Pad, PadSelection};
#[test]
fn test_state_and_set_state() {
let pad = Pad {
down: true,
up: false,
left: true,
right: false,
start: true,
select: false,
b: true,
a: false,
selection: PadSelection::Action,
int_pad: true,
};
let state = pad.state(None).unwrap();
assert_eq!(state.len(), 10);
let mut new_pad = Pad::new();
new_pad.set_state(&state, None).unwrap();
assert!(new_pad.down);
assert!(!new_pad.up);
assert!(new_pad.left);
assert!(!new_pad.right);
assert!(new_pad.start);
assert!(!new_pad.select);
assert!(new_pad.b);
assert!(!new_pad.a);
assert_eq!(new_pad.selection, PadSelection::Action);
assert!(new_pad.int_pad);
}
}