012. Рефакторинг флагов Zero и Negative
Выделить общую логику обновления флагов Zero и Negative.
Урок 12 из 356 · tests/chapter_01_cpu/test_012_refactor_zero_and_negative_flags.py
Файл для обновления
emulator/cpu/cpu.pyРасположения
CPU._update_zero_and_negative_flags
CPU.step, existing $A9 and $AD branchesПочему этот шаг важен
Непосредственная и абсолютная LDA в текущем виде дублируют одинаковые изменения флагов. Одна вспомогательная функция реализует этот инвариант единожды и явно принимает результирующее значение, чтобы последующие инструкции могли переиспользовать её, не завися от аккумулятора A.
Полный пример реализации
ZERO_FLAG = 1 << 1
NEGATIVE_FLAG = 1 << 7
class CPU:
def _update_zero_and_negative_flags(self, value: int) -> None:
if value == 0:
self.p |= ZERO_FLAG
else:
self.p &= ~ZERO_FLAG
if value & NEGATIVE_FLAG:
self.p |= NEGATIVE_FLAG
else:
self.p &= ~NEGATIVE_FLAG
def step(self) -> None:
opcode = self.fetch_byte()
if opcode == 0xA9:
self.a = self.fetch_byte()
elif opcode == 0xAD:
address = self.fetch_word()
self.a = self.bus.read(address)
else:
raise NotImplementedError(
f"Opcode {opcode:02X} not implemented"
)
self._update_zero_and_negative_flags(self.a)Важные инварианты
- флаги вычисляются на основе переданного значения, а не неявно из cpu.a
- изменяются только Z и N
- существующее поведение LDA после рефакторинга остаётся неизменным
Распространённое заблуждение
Рефакторинг — не повод менять поведение. Тесты 010–011 по-прежнему задают поведенческий контракт; этот шаг лишь централизует их механизм.
Вне рамок
- вынесение логики адресации из CPU.step
- перенос поведения LDA в instructions.py
Запустить этот урок
uv run pytest tests/chapter_01_cpu/test_012_refactor_zero_and_negative_flags.py -v