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