291. Захват состояния геймпада и последовательное чтение
Захват кнопок геймпада и их последовательное чтение.
Урок 291 из 356 · tests/chapter_07_controller_input/test_291_controller_capture_and_serial_read.py
Файлы, создаваемые на этом шаге
emulator/input/controller.pyЗачем нужен этот шаг
CPU NES не получает состояние геймпада как целый байт из $4016. Он получает по одному биту кнопки за раз. Прежде чем встраивать этот протокол в CpuBus, чистый объект Controller должен уметь:
Ссылки
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 behaviorКлючевой термин: strobe Strobe — это управляющий сигнал, записываемый CPU. Для геймпада NES strobe определяет, продолжает ли геймпад захватывать текущее состояние кнопок или переходит к перебору захваченных последовательных битов.
Минимальный пример
controller.a = True
controller.write_strobe(1)
controller.write_strobe(0)
controller.read_bit() # returns A bitРаспространённое заблуждение
"The controller should return all buttons as one byte."Эмулятор может хранить захваченные кнопки внутри как один байт, но протокол со стороны CPU читает данные по одному биту за раз.
Пример предполагаемой реализации
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 bitВне рамок этого шага
- маршрутизация $4016 в CpuBus
- сопоставление клавиш pygame
- порт геймпада 2
- контроллеры расширения Famicom
- поведение сбоя чтения DMC/геймпада
Запустить этот урок
uv run pytest tests/chapter_07_controller_input/test_291_controller_capture_and_serial_read.py -v