185. Jsr absolute

wire JSR absolute opcode $20.

Lesson 185 of 356 · tests/chapter_01_cpu/test_185_JSR_absolute.py

Prerequisite: step 184 added `jsr. In this step, add these changes in emulator/cpu/opcodes.py`:

from emulator.cpu.instructions import jsr
from emulator.cpu.addressing_modes import absolute

def jsr_absolute(cpu: CPU):
    addr = absolute(cpu)
    jsr(cpu, addr)

OPCODE_TABLE = {
    # existing entries...
    0x20: jsr_absolute,
}

Opcode

0x20 -> JSR $hhhh

Goal

create jsr_absolute(cpu), use absolute(cpu), then jsr(cpu, addr).

Student guidance

JSR absolute uses a 16-bit little-endian operand as the subroutine address.

Example

20 34 12 -> JSR $1234

Execution steps

1. CPU.step() fetches opcode 0x20.
2. absolute(cpu) fetches operand bytes 34 12 and returns $1234.
3. PC now points to the next instruction, $8003 in these tests.
4. jsr(cpu, $1234) pushes return address $8002.
5. PC becomes $1234.

Common mistake

Do not call jmp(cpu, addr). JSR must push the return address first.

Why this step exists

`absolute consumes the two-byte target, leaving PC at the next instruction so jsr` can push PC-minus-one and jump. Invariants: one step consumes opcode plus operand, pushes high then low, decrements S twice, changes PC to the target, and preserves status. Misconception: sharing JMP's addressing mode does not make JSR a plain jump.

Out of scope: `rts` and opcode $60 are steps 186-187. BRK, RTI, and later general stack helpers are not part of this transition.

Run this lesson

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