290. 手柄定义

定义纯粹的 NES Controller 状态对象。

290 / 356 · tests/chapter_07_controller_input/test_290_controller_definition.py

本步骤要创建的文件

emulator/input/controller.py

为什么需要这一步

在把手柄输入接入 CpuBus 地址 $4016 之前,我们需要为标准 NES 手柄建立一个纯数据模型。这样可以让手柄机制易于测试,而不需要涉及 pygame、CpuBus、ROM 执行或帧时序。

什么是标准 NES 手柄?

标准手柄有八个按钮

参考资料

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

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

硬件串行读取顺序是

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

重要区别

一些 NES 汇编例程会把这些读取值移位存入 RAM,使 A 键最终位于某个游戏自有字节的第 7 位。这并不意味着硬件是先发送 Right 的。模拟器应该按照硬件的串行顺序来建模。

建议的实现示例

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

范围之外

  • CpuBus $4016 路由
  • pygame 键盘映射
  • 手柄端口 2
  • Famicom 扩展手柄
  • DMC/手柄读取故障行为

运行本课

uv run pytest tests/chapter_07_controller_input/test_290_controller_definition.py -v