062. Instruction sbc

Add the core SBC instruction.

Lesson 62 of 356 · tests/chapter_01_cpu/test_062_instruction_sbc.py

File to update

emulator/cpu/instructions.py

Symbol to add

instructions.sbc

Why this step exists

SBC introduces subtraction as a value-based core instruction before any opcode is wired. The 6502 Carry flag means "no borrow", so subtraction can use addition of the operand's eight-bit complement plus Carry.

Complete example implementation

# 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

Important invariants

  • Carry set subtracts only value; Carry clear subtracts one extra
  • A is reduced to the final eight-bit result
  • Carry is set exactly when no borrow occurs
  • Zero and Negative follow the final eight-bit result
  • Overflow follows signed subtraction, not unsigned borrow

Common misconception

Python's raw ~value is negative because integers are unbounded. Mask it with & 0xFF before addition; also do not treat Carry as a conventional borrow bit.

Out of scope

  • every SBC opcode handler and opcode-table entry
  • fetching operands or selecting addressing modes inside sbc
  • decimal-mode arithmetic and cycle timing

Reference

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

Run this lesson

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