186. Instruction rts

add addressing-independent RTS behavior.

Lesson 186 of 356 · tests/chapter_01_cpu/test_186_instruction_rts.py

Prerequisite: steps 184-185 added JSR behavior and opcode wiring. In this step, add `emulator/cpu/instructions.py::rts`:

Instruction

RTS -> Return from Subroutine

Goal

implement rts(cpu) in instructions.py.

Student guidance

RTS is the matching return instruction for JSR. JSR saves a return address on the stack, and RTS pulls that address back, then adds 1 to continue execution after the original JSR instruction.

Important details

  • The stack lives in page $0100.
  • S is only the low byte of the stack address.
  • Pull increments S first, then reads from $0100 | S.
  • RTS pulls low byte first, then high byte.
  • RTS sets PC to pulled_address + 1.

Example

If the stack contains return address $8002:

    $01FC = $02  low byte
    $01FD = $80  high byte
    S = $FB

Then RTS pulls $8002 and sets PC to $8003.

Example implementation shape

cpu.s = (cpu.s + 1) & 0xFF
low = cpu.bus.read(0x0100 | cpu.s)

cpu.s = (cpu.s + 1) & 0xFF
high = cpu.bus.read(0x0100 | cpu.s)

addr = (high << 8) | low
cpu.pc = (addr + 1) & 0xFFFF

Why this step exists

JSR stored PC-minus-one, so RTS reconstructs that word and advances once to the continuation. Invariants: each pull increments 8-bit S before its read; low is pulled before high; final PC wraps to 16 bits; status, other registers, and memory are unchanged. Misconception: reading before incrementing S, or omitting the final PC increment, does not invert JSR's stack protocol.

Out of scope: importing/mapping opcode $60 is step 187. BRK/RTI and shared CPU stack helpers belong to later steps.

Run this lesson

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