291. Controller capture and serial read
Capture controller buttons and read them serially.
Lesson 291 of 356 · tests/chapter_07_controller_input/test_291_controller_capture_and_serial_read.py
Files created in this step
emulator/input/controller.pyWhy this step exists
The NES CPU does not receive the controller state as a whole byte from $4016. It receives one button bit at a time. Before wiring that protocol into CpuBus, the pure Controller object should know how to:
References
https://www.nesdev.org/wiki/Standard_controller
https://www.nesdev.org/wiki/Controller_reading_code
1. capture the current button booleans into a stable snapshot
2. expose the captured bits in NES serial order
3. handle strobe high vs strobe low behaviorKey term: strobe A strobe is a control signal written by the CPU. For the NES controller, strobe controls whether the controller keeps capturing live button state or advances through the captured serial bits.
Minimal example
controller.a = True
controller.write_strobe(1)
controller.write_strobe(0)
controller.read_bit() # returns A bitCommon misconception
"The controller should return all buttons as one byte."The emulator may store captured buttons as one byte internally, but the CPU-facing protocol reads one bit at a time.
Suggested implementation example
def capture_buttons(self) -> None:
value = 0
if self.a:
value |= BUTTON_A
if self.b:
value |= BUTTON_B
if self.select:
value |= BUTTON_SELECT
if self.start:
value |= BUTTON_START
if self.up:
value |= BUTTON_UP
if self.down:
value |= BUTTON_DOWN
if self.left:
value |= BUTTON_LEFT
if self.right:
value |= BUTTON_RIGHT
self.captured_buttons = value
self.read_index = 0
def write_strobe(self, value: int) -> None:
self.strobe = (value & 1) == 1
if self.strobe:
self.capture_buttons()
def read_bit(self) -> int:
if self.strobe:
self.capture_buttons()
if self.read_index >= 8:
return 1
bit = (self.captured_buttons >> self.read_index) & 1
self.read_index += 1
return bitOut of scope
- CpuBus $4016 routing
- pygame keyboard mapping
- controller port 2
- Famicom expansion controllers
- DMC/controller read glitch behavior
Run this lesson
uv run pytest tests/chapter_07_controller_input/test_291_controller_capture_and_serial_read.py -v