290. Controller definition

Define the pure NES Controller state object.

Lesson 290 of 356 · tests/chapter_07_controller_input/test_290_controller_definition.py

Files to create in this step

emulator/input/controller.py

Why this step exists

Before connecting controller input to CpuBus address $4016, we need a pure data model for the standard NES controller. This keeps the controller mechanism easy to test without involving pygame, CpuBus, ROM execution, or frame timing.

What is a standard NES controller?

The standard controller has eight buttons

References

https://www.nesdev.org/wiki/Standard_controller
https://www.nesdev.org/wiki/Controller_reading_code

A, B, Select, Start, Up, Down, Left, Right

The hardware serial read order is

first read  -> A
second read -> B
third read  -> Select
fourth read -> Start
fifth read  -> Up
sixth read  -> Down
seventh read -> Left
eighth read -> Right

Important distinction

Some NES assembly routines shift these reads into RAM so A ends up in bit 7 of a game-owned byte. That does not mean the hardware sends Right first. The emulator should model the hardware serial order.

Suggested implementation example

from dataclasses import dataclass


BUTTON_A = 1 << 0
BUTTON_B = 1 << 1
BUTTON_SELECT = 1 << 2
BUTTON_START = 1 << 3
BUTTON_UP = 1 << 4
BUTTON_DOWN = 1 << 5
BUTTON_LEFT = 1 << 6
BUTTON_RIGHT = 1 << 7


@dataclass
class Controller:
    a: bool = False
    b: bool = False
    select: bool = False
    start: bool = False
    up: bool = False
    down: bool = False
    left: bool = False
    right: bool = False

    strobe: bool = False
    captured_buttons: int = 0
    read_index: int = 0

Out 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_290_controller_definition.py -v