062. SBC 指令

添加核心 SBC 指令。

62 / 356 · tests/chapter_01_cpu/test_062_instruction_sbc.py

要更新的文件

emulator/cpu/instructions.py

要添加的符号

instructions.sbc

为什么需要这一步

SBC 在接入任何操作码之前,先把减法作为一种基于数值的核心指令引入。6502 的 Carry 标志表示“无借位”,因此减法可以通过将操作数的八位补码与 Carry 相加来实现。

完整示例实现

# emulator/cpu/instructions.py
def sbc(cpu: CPU, value: int):
    carry = int(cpu.flags.get_carry_flag())
    a = cpu.a
    value_inverted = (~value) & 0xFF
    result = a + value_inverted + carry
    result_8 = result & 0xFF

    cpu.flags.set_carry_flag(result > 0xFF)
    cpu.flags.set_zero_flag(result_8 == 0)
    cpu.flags.set_negative_flag((result_8 & 0x80) != 0)
    overflow = ((result_8 ^ a) & (result_8 ^ value_inverted)) & 0x80
    cpu.flags.set_overflow_flag(overflow != 0)
    cpu.a = result_8

重要不变量

  • Carry 置位时只减去 value;Carry 清零时会多减一
  • A 被缩减为最终的八位结果
  • 当且仅当无借位时 Carry 才被置位
  • Zero 和 Negative 反映最终的八位结果
  • Overflow 反映有符号减法,而非无符号借位

常见误解

Python 中 ~value 的原始结果是一个负数,因为整数没有位数限制。相加之前要用 & 0xFF 对它做掩码处理;另外,不要把 Carry 当作常规的借位比特。

范围之外

  • 每个 SBC 操作码处理函数和操作码表条目
  • sbc 内部取操作数或选择寻址模式
  • 十进制模式运算和周期时序

参考

https://www.nesdev.org/wiki/Instruction_reference#SBC

运行本课

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