016. Refactorización de la tabla de opcodes
Sustituir las ramas de CPU.step por una tabla de opcodes.
Lección 16 de 356 · tests/chapter_01_cpu/test_016_opcode_table_refactor.py
Archivo a crear
emulator/cpu/opcodes.pyArchivo a actualizar
emulator/cpu/cpu.pyUbicaciones
opcodes.lda_immediate
opcodes.lda_zero_page
opcodes.lda_absolute
opcodes.OPCODE_TABLE
CPU.stepPor qué existe este paso
Las ramas directas funcionan para tres opcodes, pero escalan mal a lo largo de todo el conjunto de instrucciones del 6502. Una tabla hace que el despacho de byte a handler esté dirigido por datos, a la vez que conserva los límites existentes entre direccionamiento e instrucción.
Implementación de ejemplo completa
# 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)Límite importante
CPU.step despacha; un handler de opcode coordina el direccionamiento, el acceso a memoria y la instrucción; lda realiza la transición de estado de registros y flags.
Error común
Un handler de opcode no es lo mismo que una instrucción. Tres opcodes de LDA comparten una única instrucción lda, pero usan mecanismos distintos de adquisición del operando.
Fuera de alcance
- handlers de LDA indexada
- valores de retorno del número de ciclos
- un objeto decodificador de instrucciones genérico
Ejecutar esta lección
uv run pytest tests/chapter_01_cpu/test_016_opcode_table_refactor.py -v