197. Instruction php

implement PHP behavior.

Lesson 197 of 356 · tests/chapter_01_cpu/test_197_instruction_php.py

In this step, change only `emulator/cpu/instructions.py by adding php(cpu). Prerequisite: FlagsHandler.set_break_flag` already exists.

Why this step exists

PHP pushes P with the emulator's Break marker set, then clears that transient marker from live CPU state. As with every push, it writes to $0100 | S before decrementing the 8-bit S.

Suggested implementation

def php(cpu: CPU):
    cpu.flags.set_break_flag(True)
    cpu.flags.set_one_flag(True)
    cpu.bus.write(STACK_BASE | cpu.s, cpu.p)
    cpu.flags.set_break_flag(False)
    cpu.flags.set_one_flag(False)
    cpu.s = (cpu.s - 1) & 0xFF

Invariants: preserve every pre-existing status bit; set Break and ONE in the stacked copy; clear both from live P afterward; write before decrementing and wrap S; do not derive Z or N from the status byte.

Misconception: Break is not left enabled in `cpu.p` after PHP merely because it is set in the byte written to the stack.

Out of scope: opcode $08 registration belongs to step 198. Any later changes to the pushed status-byte rules belong to their own numbered steps.

Run this lesson

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