163. Relative addressing

decode an eight-bit relative branch offset.

Lesson 163 of 356 · tests/chapter_01_cpu/test_163_relative_addressing.py

Why this step exists

A branch stores a signed displacement from the PC after its operand rather than a full target, so decoding must be separate from the branch decision.

In this step, add only this implementation to `emulator/cpu/addressing_modes.py::relative`:

def relative(cpu: CPU) -> int:
    offset = cpu.fetch_byte()
    # Transform to signed integer
    # 0x7F -> +127
    # 0x80 -> -128
    # 0xFF -> -1
    if offset & 0x80: # offset has bit 7 active, is negative
        offset -= 0x100
    return offset

`CPU.fetch_byte reads the operand and advances PC once; subtracting 0x100 for bit-7-set values maps unsigned 0x80..0xFF to signed -128..-1 while preserving 0x00..0x7F as 0..127`.

Invariants: this helper always consumes exactly one byte and never changes PC by the returned offset. It does not inspect flags or alter registers, memory, or status. Misconception: `relative` decodes a displacement; it neither decides whether a branch is taken nor computes an absolute target itself.

Out of scope: `instructions.bcc` belongs to lesson 164. Other branch semantics are lessons 165-171, and opcode handlers/table entries are lessons 172-179.

Run this lesson

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