gb/joypad.rs
1//! The eight buttons.
2//!
3//! [`read`] returns a [`Buttons`], a bit per button and set where held. The
4//! hardware is the other way round, clearing the bit of a button that is down.
5//! See <https://gbdev.io/pandocs/Joypad_Input.html>.
6//!
7//! [`Pad`] is the usual way in. It keeps the previous reading, which turns
8//! "what is held" into "what changed".
9//!
10//! ```ignore
11//! let vblank = unsafe { ppu::Vblank::listen() };
12//! let mut pad = Pad::new();
13//! loop {
14//! vblank.wait();
15//! pad.poll();
16//!
17//! if pad.just_pressed.a() {
18//! jump();
19//! }
20//! x = x.wrapping_add_signed(pad.pressed.x() as i8);
21//! }
22//! ```
23//!
24//! # Reading from a handler
25//!
26//! [`read`] selects a row, reads it, then selects the other and reads that. A
27//! handler reading the joypad in between finishes with both rows off, and the
28//! half of the interrupted read that has not happened yet comes back as nothing
29//! pressed. Nothing reports that, so poll in one place.
30
31use bitfield_struct::bitfield;
32
33use crate::mmio::{JOYP, Joypad};
34
35/// The eight buttons, set where held.
36///
37/// The d-pad occupies the low nibble and the action buttons the high one, which
38/// is the layout GBDK's `J_` constants use.
39#[bitfield(u8)]
40#[derive(PartialEq, Eq)]
41pub struct Buttons {
42 /// Right on the d-pad.
43 pub right: bool,
44 /// Left on the d-pad.
45 pub left: bool,
46 /// Up on the d-pad.
47 pub up: bool,
48 /// Down on the d-pad.
49 pub down: bool,
50 /// The A button.
51 pub a: bool,
52 /// The B button.
53 pub b: bool,
54 /// The Select button.
55 pub select: bool,
56 /// The Start button.
57 pub start: bool,
58}
59
60/// Where the d-pad points left or right, as a number to add to a coordinate.
61///
62/// ```ignore
63/// x = x.wrapping_add_signed(pad.pressed.x() as i8 * SPEED);
64/// ```
65#[repr(i8)]
66#[derive(Clone, Copy, PartialEq, Eq, Debug)]
67pub enum DPadX {
68 /// Left, negative because screen coordinates grow to the right.
69 Left = -1,
70 /// Neither, or both at once.
71 Neutral = 0,
72 /// Right.
73 Right = 1,
74}
75
76/// Where the d-pad points up or down, as a number to add to a coordinate.
77#[repr(i8)]
78#[derive(Clone, Copy, PartialEq, Eq, Debug)]
79pub enum DPadY {
80 /// Up, negative because screen coordinates grow downwards.
81 Up = -1,
82 /// Neither, or both at once.
83 Neutral = 0,
84 /// Down.
85 Down = 1,
86}
87
88impl Buttons {
89 /// Where the d-pad points left or right.
90 #[inline]
91 pub const fn x(self) -> DPadX {
92 match (self.left(), self.right()) {
93 (true, false) => DPadX::Left,
94 (false, true) => DPadX::Right,
95 _ => DPadX::Neutral,
96 }
97 }
98
99 /// Where the d-pad points up or down.
100 #[inline]
101 pub const fn y(self) -> DPadY {
102 match (self.up(), self.down()) {
103 (true, false) => DPadY::Up,
104 (false, true) => DPadY::Down,
105 _ => DPadY::Neutral,
106 }
107 }
108}
109
110// Active low, so clearing a select bit is what turns that row on.
111const DPAD: Joypad = Joypad::new().with_buttons(true);
112const BUTTONS: Joypad = Joypad::new().with_dpad(true);
113const NEITHER: Joypad = Joypad::new().with_dpad(true).with_buttons(true);
114
115/// Read the buttons.
116pub fn read() -> Buttons {
117 // A select line takes time to settle, so a row is read more than once and
118 // only the last read counts. The counts are GBDK's. The second row gets more
119 // because both select lines move for it where only one moves for the first.
120 JOYP.write(DPAD);
121 JOYP.read();
122 let dpad = JOYP.read().into_bits() & 0x0F;
123
124 JOYP.write(BUTTONS);
125 JOYP.read();
126 JOYP.read();
127 JOYP.read();
128 JOYP.read();
129 JOYP.read();
130 let buttons = JOYP.read().into_bits() & 0x0F;
131
132 // With both rows off no input line can fall, so a press between calls
133 // cannot reach the joypad interrupt.
134 JOYP.write(NEITHER);
135
136 Buttons::from_bits(!((buttons << 4) | dpad))
137}
138
139/// The buttons, and what changed at the last [`poll`](Self::poll).
140pub struct Pad {
141 /// Held now.
142 pub pressed: Buttons,
143 /// Went down at the last poll.
144 pub just_pressed: Buttons,
145 /// Came up at the last poll.
146 pub just_released: Buttons,
147}
148
149impl Pad {
150 /// A pad with nothing held.
151 ///
152 /// A button already down when the first poll runs counts as newly pressed.
153 pub const fn new() -> Self {
154 Pad {
155 pressed: Buttons::new(),
156 just_pressed: Buttons::new(),
157 just_released: Buttons::new(),
158 }
159 }
160
161 /// Read the buttons and work out what changed.
162 #[inline]
163 pub fn poll(&mut self) {
164 let now = read().into_bits();
165 let was = self.pressed.into_bits();
166 self.just_pressed = Buttons::from_bits(now & !was);
167 self.just_released = Buttons::from_bits(!now & was);
168 self.pressed = Buttons::from_bits(now);
169 }
170}
171
172impl Default for Pad {
173 fn default() -> Self {
174 Self::new()
175 }
176}
177
178const _: () = {
179 assert!(DPAD.into_bits() == 0b0010_0000);
180 assert!(BUTTONS.into_bits() == 0b0001_0000);
181 assert!(NEITHER.into_bits() == 0b0011_0000);
182 assert!(Buttons::new().with_right(true).into_bits() == 0b0000_0001);
183 assert!(Buttons::new().with_a(true).into_bits() == 0b0001_0000);
184 assert!(Buttons::new().with_start(true).into_bits() == 0b1000_0000);
185};