188. 标志位处理:中断标志与 Break 标志

添加 I、B 以及未使用位的标志位辅助函数。

188 / 356 · tests/chapter_01_cpu/test_188_flags_handler_interrupt_and_break_flags.py

在本步骤中,将 Break 定义为第 4 位,将压栈状态字中未使用的位定义为第 5 位。向 `emulator/cpu/flags_handler.py` 添加:

INTERRUPT_FLAG = 1 << 2
B_FLAG = 1 << 4
ONE_FLAG = 1 << 5

def set_interrupt_disable_flag(self, enabled: bool):
    if enabled:
        self.cpu.p |= INTERRUPT_FLAG
    else:
        self.cpu.p &= ~INTERRUPT_FLAG

def set_break_flag(self, enabled: bool):
    if enabled:
        self.cpu.p |= B_FLAG
    else:
        self.cpu.p &= ~B_FLAG

def set_one_flag(self, enabled: bool):
    if enabled:
        self.cpu.p |= ONE_FLAG
    else:
        self.cpu.p &= ~ONE_FLAG

def get_interrupt_disable_flag(self) -> bool:
    return bool(self.cpu.p & INTERRUPT_FLAG)

def get_break_flag(self) -> bool:
    return bool(self.cpu.p & B_FLAG)

def get_one_flag(self) -> bool:
    return bool(self.cpu.p & ONE_FLAG)

本步骤存在的原因

我们正在为实现 BRK 做准备。

BRK 是一条软件中断指令。为了清晰地实现它,CPU 需要针对两个状态标志位提供辅助函数:

I flag -> Interrupt Disable, bit 2
B flag -> Break, bit 4
ONE flag -> unused/status bit 5 used in pushed status bytes

BRK 需要它们,原因是

  • 进入中断处理程序后,它会设置中断禁止标志
  • 它会压入一个已设置 Break 标志的状态字节

设计目标

把位操作集中封装在 FlagsHandler 内部,而不是把对 cpu.p 的原始位操作分散到各条指令代码中。

重要术语

Interrupt Disable flag:
    When set, maskable IRQ interrupts are disabled.

Break flag:
    Used to mark that the pushed status byte came from BRK.

常见错误

不要把 BRK 操作码与 Break 标志混淆。

BRK opcode: 0x00, the instruction byte in memory
B flag:     bit 4 inside the status byte pushed to the stack
ONE flag:   bit 5, usually set in pushed status bytes

设计依据:具名的设置函数把对 P 的独立读-改-写操作集中起来,取值函数则以布尔值形式对外暴露。不变量:每个设置函数只改变各自对应的位;I、B、ONE 以及所有已有标志位彼此独立。常见误解:B 与压栈状态字中始终为一的那一位并不是同一位,操作码 $00 也不是它们中的任何一个。

本步骤范围之外:本步骤不实现 BRK,也不决定这些位何时被设置,那属于第 189-190 步。RTI/PHP/PLP、NMI 行为以及 CPU 栈辅助函数仍属于后续工作。

运行本课

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