016. Рефакторинг таблицы опкодов

Заменить ветвления в CPU.step таблицей опкодов.

Урок 16 из 356 · tests/chapter_01_cpu/test_016_opcode_table_refactor.py

Файл для создания

emulator/cpu/opcodes.py

Файл для обновления

emulator/cpu/cpu.py

Расположения

opcodes.lda_immediate
opcodes.lda_zero_page
opcodes.lda_absolute
opcodes.OPCODE_TABLE
CPU.step

Почему этот шаг важен

Прямые ветвления работают для трёх опкодов, но плохо масштабируются на весь набор инструкций 6502. Таблица делает диспетчеризацию от байта к обработчику управляемой данными, сохраняя при этом существующие границы между адресацией и инструкциями.

Полный пример реализации

# emulator/cpu/opcodes.py
from emulator.cpu.addressing_modes import absolute, immediate, zero_page
from emulator.cpu.instructions import lda


def lda_immediate(cpu) -> None:
    lda(cpu, immediate(cpu))


def lda_zero_page(cpu) -> None:
    address = zero_page(cpu)
    lda(cpu, cpu.bus.read(address))


def lda_absolute(cpu) -> None:
    address = absolute(cpu)
    lda(cpu, cpu.bus.read(address))


OPCODE_TABLE = {
    0xA9: lda_immediate,
    0xA5: lda_zero_page,
    0xAD: lda_absolute,
}


# emulator/cpu/cpu.py
from emulator.cpu.opcodes import OPCODE_TABLE


class CPU:
    def step(self) -> None:
        opcode = self.fetch_byte()
        handler = OPCODE_TABLE.get(opcode)

        if handler is None:
            raise NotImplementedError(
                f"Opcode {opcode:02X} not implemented"
            )

        return handler(self)

Важная граница

CPU.step выполняет диспетчеризацию; обработчик опкода координирует адресацию, доступ к памяти и инструкцию; lda выполняет переход состояния регистров и флагов.

Распространённое заблуждение

Обработчик опкода — это не то же самое, что инструкция. Три опкода LDA используют одну общую инструкцию lda, но разные механизмы получения операнда.

Вне рамок

  • обработчики индексированной LDA
  • возвращаемые значения количества циклов
  • универсальный объект-декодер инструкций

Запустить этот урок

uv run pytest tests/chapter_01_cpu/test_016_opcode_table_refactor.py -v