062. Instruction sbc
Добавляем базовую инструкцию SBC.
Урок 62 из 356 · tests/chapter_01_cpu/test_062_instruction_sbc.py
Файл для обновления
emulator/cpu/instructions.pyСимвол для добавления
instructions.sbcЗачем нужен этот шаг
SBC вводится как базовая инструкция вычитания, работающая со значением, до подключения какого-либо опкода. Флаг Carry в 6502 означает «нет заёма», поэтому вычитание можно реализовать как сложение восьмибитного дополнения операнда с 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 следует из знакового вычитания, а не из беззнакового заёма
Распространённое заблуждение
Необработанное ~value в Python отрицательно, поскольку целые числа не ограничены. Замаскируйте его через & 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