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