184. jsr 指令

添加与寻址方式无关的 JSR 行为。

184 / 356 · tests/chapter_01_cpu/test_184_instruction_jsr.py

在本步骤中,向 `emulator/cpu/instructions.py::jsr` 添加以下内容:

指令

JSR -> Jump to Subroutine

目标

在 instructions.py 中实现 jsr(cpu, addr)。

学习指引

JSR 与 JMP 类似,但它还会把返回地址保存到栈上,以便 RTS 之后能够返回。

重要细节

  • 栈位于 $0100 页。
  • S 只是栈地址的低字节。
  • 先压入高字节,再压入低字节。
  • 每次压栈后 S 递减。
  • 在 jsr(cpu, addr) 执行时,PC 已经指向下一条指令。
  • JSR 压入的是 PC - 1,因为 RTS 会对取出的返回地址加一。

示例实现结构

return_addr = (cpu.pc - 1) & 0xFFFF
high = (return_addr >> 8) & 0xFF
low = return_addr & 0xFF

cpu.bus.write(0x0100 | cpu.s, high)
cpu.s = (cpu.s - 1) & 0xFF

cpu.bus.write(0x0100 | cpu.s, low)
cpu.s = (cpu.s - 1) & 0xFF

cpu.pc = addr & 0xFFFF

本步骤存在的原因

操作数解码已经把 PC 移过了 JSR 本身,而 6502 的栈协议要求存入的地址比未来 RTS 的继续执行地址小 1。不变量:写入顺序为先高字节后低字节,地址为 `$0100 | S`;每次写入后 S 递减并回绕;PC 与传入地址均为 16 位;标志位与其他寄存器保持不变。常见误解:压入当前 PC,或先压入低字节,都会产生与之不兼容的返回帧。

本步骤范围之外:在第 185 步之前,不导入操作码、不添加 `jsr_absolute 处理函数,也不添加 $20 的表项。rts` 直到第 186 步才存在;这里虽然说明了“PC 减一”的约定,但不能在此实现。

运行本课

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