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    /// A shift is owed to the CLK edge that ENDS the current read.
58    ///
59    /// `CLK` is low only while `$4016`/`$4017` is being read, and the shift
60    /// register advances on its LOW-TO-HIGH transition — i.e. when the read
61    /// ends, not when it begins (nesdev *Controller reading*). A run of
62    /// consecutive read cycles therefore holds `CLK` low throughout and
63    /// produces ONE rising edge, so it advances the register once and returns
64    /// the same bit each time. Shifting on the read instead made every read
65    /// its own clock, which is Famicom wiring, not NES.
66    ///
67    /// It is applied lazily, on the next read that is NOT a continuation of the
68    /// run, because a read is the only thing that can observe it. That makes it
69    /// state which outlives an instruction, so it is serialized.
70    pub(crate) pending_shift: bool,
71}
72
73impl Controller {
74    /// New controller with no buttons pressed.
75    #[must_use]
76    pub const fn new() -> Self {
77        Self {
78            buttons: Buttons::empty(),
79            shift: 0,
80            strobe: false,
81            pending_shift: false,
82        }
83    }
84
85    /// Set the current button state. Takes effect on the next strobe edge
86    /// (or immediately, while strobe is held high).
87    pub const fn set_buttons(&mut self, buttons: Buttons) {
88        self.buttons = buttons;
89        if self.strobe {
90            self.shift = buttons.bits();
91        }
92    }
93
94    /// Get the current button state.
95    #[must_use]
96    pub const fn buttons(&self) -> Buttons {
97        self.buttons
98    }
99
100    /// Handle a write to `$4016`. Only bit 0 matters for the standard
101    /// controller. While strobe is held high the shift register continuously
102    /// reloads from the live button state.
103    pub const fn write_strobe(&mut self, value: u8) {
104        let new_strobe = value & 1 != 0;
105        // Falling edge latches: while strobe was high, shift mirrors live
106        // buttons; on the falling edge, that snapshot becomes the value
107        // shifted out by subsequent reads.
108        if new_strobe {
109            self.shift = self.buttons.bits();
110        }
111        // Only an ACTUAL strobe drops an owed shift, and only because the
112        // reload leaves nothing for it to advance.
113        //
114        // This cleared unconditionally when `pending_shift` was introduced, so a
115        // write with bit 0 CLEAR -- not a strobe at all -- silently swallowed a
116        // shift the read run had already earned. That is wrong on the mechanism:
117        // `CLK` is low only while $4016/$4017 is being READ, so a write ENDS the
118        // run and produces exactly the rising edge the owed shift represents. A
119        // write cannot cancel it; if anything it is what causes it.
120        //
121        // Caught by the DUT, which models the edge directly and therefore could
122        // not reproduce this: at AccuracyCoin 25,196,442 a `$40` write lands
123        // between a consecutive read pair and the next read, and from there the
124        // two consoles' shift registers sat one bit apart. The co-simulation
125        // found a defect in the ORACLE, which is the direction that is supposed
126        // to be impossible and is the whole reason the DUT is worth building.
127        if new_strobe {
128            self.pending_shift = false;
129        }
130        self.strobe = new_strobe;
131    }
132
133    /// Handle a read of `$4016` / `$4017`. Returns the LSB of the shift
134    /// register and shifts. While strobe is held high, the LSB is always
135    /// the A button (bit 0 of `buttons`).
136    ///
137    /// Per the wiki, when the shift register has been emptied subsequent
138    /// reads return 1.
139    /// `continues_run` is true when the immediately preceding CPU cycle was
140    /// also a read of this same port — the case an absolute-indexed
141    /// read-modify-write (`SLO $4016,X`) and a DMC-DMA-interrupted read both
142    /// produce. `CLK` stays low across such a run, so the register does not
143    /// advance between the reads and both see the same bit.
144    pub const fn read(&mut self, continues_run: bool) -> u8 {
145        if self.strobe {
146            return self.buttons.bits() & 1;
147        }
148        if self.pending_shift && !continues_run {
149            // The previous run ended: its rising edge lands here.
150            // Shift in 1s from the left so post-empty reads yield 1.
151            self.shift = (self.shift >> 1) | 0x80;
152        }
153        self.pending_shift = true;
154        self.shift & 1
155    }
156
157    /// Side-effect-free sample of the next bit (debugger).
158    #[must_use]
159    pub const fn peek(&self) -> u8 {
160        if self.strobe {
161            self.buttons.bits() & 1
162        } else if self.pending_shift {
163            // A debugger peek must show what the NEXT read would return, and
164            // that read lands the owed edge first.
165            ((self.shift >> 1) | 0x80) & 1
166        } else {
167            self.shift & 1
168        }
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn empty_controller_reads_zero_then_ones() {
178        let mut c = Controller::new();
179        // Pulse strobe high then low to load.
180        c.write_strobe(1);
181        c.write_strobe(0);
182        for _ in 0..8 {
183            assert_eq!(c.read(false), 0);
184        }
185        // After 8 reads, ROMs see 1s.
186        for _ in 0..4 {
187            assert_eq!(c.read(false), 1);
188        }
189    }
190
191    #[test]
192    fn each_button_appears_in_canonical_shift_order() {
193        let mut c = Controller::new();
194        c.set_buttons(Buttons::A | Buttons::SELECT | Buttons::DOWN);
195        c.write_strobe(1);
196        c.write_strobe(0);
197        // A, B, Select, Start, Up, Down, Left, Right
198        let expected = [1u8, 0, 1, 0, 0, 1, 0, 0];
199        for &want in &expected {
200            assert_eq!(c.read(false), want);
201        }
202    }
203
204    #[test]
205    fn strobe_high_reads_a_button_repeatedly() {
206        let mut c = Controller::new();
207        c.set_buttons(Buttons::A);
208        c.write_strobe(1);
209        for _ in 0..16 {
210            assert_eq!(c.read(false), 1, "while strobing, $4016 returns A bit");
211        }
212    }
213
214    #[test]
215    fn buttons_set_during_strobe_reflect_immediately() {
216        let mut c = Controller::new();
217        c.write_strobe(1);
218        c.set_buttons(Buttons::A);
219        assert_eq!(c.read(false), 1);
220        c.set_buttons(Buttons::empty());
221        assert_eq!(c.read(false), 0);
222    }
223
224    #[test]
225    fn buttons_set_after_latch_take_effect_on_next_strobe() {
226        let mut c = Controller::new();
227        c.set_buttons(Buttons::A);
228        c.write_strobe(1);
229        c.write_strobe(0);
230        // Change buttons mid-readout — should NOT affect this scan.
231        c.set_buttons(Buttons::A | Buttons::B);
232        assert_eq!(c.read(false), 1, "A");
233        assert_eq!(c.read(false), 0, "B (latched as not pressed)");
234        // New strobe latches the new state.
235        c.write_strobe(1);
236        c.write_strobe(0);
237        assert_eq!(c.read(false), 1, "A");
238        assert_eq!(c.read(false), 1, "B (now latched as pressed)");
239    }
240}