053. Instruction adc

Add the addressing-independent ADC instruction.

Lesson 53 of 356 · tests/chapter_01_cpu/test_053_instruction_adc.py

File to update

emulator/cpu/instructions.py

Symbol to create

instructions.adc(cpu, value)

Why this step exists

ADC is arithmetic behavior, not operand decoding. It accepts the value already resolved by an opcode handler, adds it to A with the incoming Carry, truncates the result to one byte, and updates C, Z, N, and V through the FlagsHandler from test 052.

Complete example implementation

# emulator/cpu/instructions.py
def adc(cpu, value: int):
    carry = int(cpu.flags.get_carry_flag())
    old_a = cpu.a
    total = old_a + value + carry
    result = total & 0xFF

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

Important invariants

  • incoming Carry is read before any flags are changed
  • A stores only the low eight bits of the full sum
  • Carry reports unsigned overflow; Overflow reports signed overflow
  • Zero and Negative are derived from the truncated result
  • flags not named above and registers X/Y remain unchanged

Common misconception

Carry and Overflow are not interchangeable. Carry comes from bit 8 of the unsigned sum, while Overflow occurs when equal-sign operands produce a result with the opposite sign.

Out of scope

  • every ADC addressing-mode handler and opcode-table entry
  • SBC and decimal-mode arithmetic
  • cycle accounting

Reference

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

Run this lesson

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