Skip to main content

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}