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 SubroutineGoal
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) & 0xFFFFWhy 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