rustynes_core/controller.rs
1//! Standard NES controller (4016/4017) shift-register state.
2//!
3//! Per <https://www.nesdev.org/wiki/Standard_controller>:
4//!
5//! - Writing `$4016` with bit 0 set holds the controllers in *strobe* mode:
6//! the shift register is continuously reloaded with the current button
7//! state, and a read of `$4016` / `$4017` returns the state of the **A**
8//! button (the LSB of the latch).
9//! - Writing `$4016` with bit 0 clear takes the controllers out of strobe
10//! mode; the latched button state remains in the shift register and is
11//! shifted out one bit per `$4016`/`$4017` read in the order
12//! `A, B, Select, Start, Up, Down, Left, Right`.
13//! - After all eight buttons have been read, subsequent reads return `1`
14//! (open-bus + a stuck-high data line on the standard pad).
15//!
16//! Frontends update the *current* button state via
17//! [`Controller::set_buttons`]; the bus latches that state into the shift
18//! register on the rising edge of strobe (and continuously while strobe is
19//! held high).
20
21use bitflags::bitflags;
22
23bitflags! {
24 /// Standard NES controller buttons. Bits ordered to match the wire
25 /// shift order (LSB first): A, B, Select, Start, Up, Down, Left, Right.
26 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
27 pub struct Buttons: u8 {
28 /// A button.
29 const A = 1 << 0;
30 /// B button.
31 const B = 1 << 1;
32 /// Select button.
33 const SELECT = 1 << 2;
34 /// Start button.
35 const START = 1 << 3;
36 /// D-pad up.
37 const UP = 1 << 4;
38 /// D-pad down.
39 const DOWN = 1 << 5;
40 /// D-pad left.
41 const LEFT = 1 << 6;
42 /// D-pad right.
43 const RIGHT = 1 << 7;
44 }
45}
46
47/// One standard NES controller plugged into `$4016` (player 1) or `$4017`
48/// (player 2).
49#[derive(Clone, Copy, Debug, Default)]
50pub struct Controller {
51 /// Current button state — set externally by the frontend.
52 pub(crate) buttons: Buttons,
53 /// Latched shift register: shifted right on each read.
54 pub(crate) shift: u8,
55 /// Strobe state (last bit-0 written to `$4016`).
56 pub(crate) strobe: bool,
57}
58
59impl Controller {
60 /// New controller with no buttons pressed.
61 #[must_use]
62 pub const fn new() -> Self {
63 Self {
64 buttons: Buttons::empty(),
65 shift: 0,
66 strobe: false,
67 }
68 }
69
70 /// Set the current button state. Takes effect on the next strobe edge
71 /// (or immediately, while strobe is held high).
72 pub const fn set_buttons(&mut self, buttons: Buttons) {
73 self.buttons = buttons;
74 if self.strobe {
75 self.shift = buttons.bits();
76 }
77 }
78
79 /// Get the current button state.
80 #[must_use]
81 pub const fn buttons(&self) -> Buttons {
82 self.buttons
83 }
84
85 /// Handle a write to `$4016`. Only bit 0 matters for the standard
86 /// controller. While strobe is held high the shift register continuously
87 /// reloads from the live button state.
88 pub const fn write_strobe(&mut self, value: u8) {
89 let new_strobe = value & 1 != 0;
90 // Falling edge latches: while strobe was high, shift mirrors live
91 // buttons; on the falling edge, that snapshot becomes the value
92 // shifted out by subsequent reads.
93 if new_strobe {
94 self.shift = self.buttons.bits();
95 }
96 self.strobe = new_strobe;
97 }
98
99 /// Handle a read of `$4016` / `$4017`. Returns the LSB of the shift
100 /// register and shifts. While strobe is held high, the LSB is always
101 /// the A button (bit 0 of `buttons`).
102 ///
103 /// Per the wiki, when the shift register has been emptied subsequent
104 /// reads return 1.
105 pub const fn read(&mut self) -> u8 {
106 if self.strobe {
107 self.buttons.bits() & 1
108 } else {
109 let bit = self.shift & 1;
110 // Shift in 1s from the left so post-empty reads yield 1.
111 self.shift = (self.shift >> 1) | 0x80;
112 bit
113 }
114 }
115
116 /// Side-effect-free sample of the next bit (debugger).
117 #[must_use]
118 pub const fn peek(&self) -> u8 {
119 if self.strobe {
120 self.buttons.bits() & 1
121 } else {
122 self.shift & 1
123 }
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn empty_controller_reads_zero_then_ones() {
133 let mut c = Controller::new();
134 // Pulse strobe high then low to load.
135 c.write_strobe(1);
136 c.write_strobe(0);
137 for _ in 0..8 {
138 assert_eq!(c.read(), 0);
139 }
140 // After 8 reads, ROMs see 1s.
141 for _ in 0..4 {
142 assert_eq!(c.read(), 1);
143 }
144 }
145
146 #[test]
147 fn each_button_appears_in_canonical_shift_order() {
148 let mut c = Controller::new();
149 c.set_buttons(Buttons::A | Buttons::SELECT | Buttons::DOWN);
150 c.write_strobe(1);
151 c.write_strobe(0);
152 // A, B, Select, Start, Up, Down, Left, Right
153 let expected = [1u8, 0, 1, 0, 0, 1, 0, 0];
154 for &want in &expected {
155 assert_eq!(c.read(), want);
156 }
157 }
158
159 #[test]
160 fn strobe_high_reads_a_button_repeatedly() {
161 let mut c = Controller::new();
162 c.set_buttons(Buttons::A);
163 c.write_strobe(1);
164 for _ in 0..16 {
165 assert_eq!(c.read(), 1, "while strobing, $4016 returns A bit");
166 }
167 }
168
169 #[test]
170 fn buttons_set_during_strobe_reflect_immediately() {
171 let mut c = Controller::new();
172 c.write_strobe(1);
173 c.set_buttons(Buttons::A);
174 assert_eq!(c.read(), 1);
175 c.set_buttons(Buttons::empty());
176 assert_eq!(c.read(), 0);
177 }
178
179 #[test]
180 fn buttons_set_after_latch_take_effect_on_next_strobe() {
181 let mut c = Controller::new();
182 c.set_buttons(Buttons::A);
183 c.write_strobe(1);
184 c.write_strobe(0);
185 // Change buttons mid-readout — should NOT affect this scan.
186 c.set_buttons(Buttons::A | Buttons::B);
187 assert_eq!(c.read(), 1, "A");
188 assert_eq!(c.read(), 0, "B (latched as not pressed)");
189 // New strobe latches the new state.
190 c.write_strobe(1);
191 c.write_strobe(0);
192 assert_eq!(c.read(), 1, "A");
193 assert_eq!(c.read(), 1, "B (now latched as pressed)");
194 }
195}