189. Instrucción brk

añadir el comportamiento de BRK independiente del modo de direccionamiento.

Lección 189 de 356 · tests/chapter_01_cpu/test_189_instruction_brk.py

Requisito previo: el paso 188 añadió los ayudantes para I, B y el bit no usado. En este paso, añade esta implementación completa de `emulator/cpu/instructions.py::brk`:

def brk(cpu: CPU):
    return_addr = (cpu.pc + 1) & 0xFFFF
    STACK_BASE = 0x0100
    high = (return_addr >> 8) & 0xFF
    low = return_addr & 0xFF

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

    cpu.flags.set_break_flag(True)
    cpu.flags.set_one_flag(True)
    cpu.bus.write(STACK_BASE | cpu.s, cpu.p)
    cpu.s = (cpu.s - 1) & 0xFF
    cpu.flags.set_interrupt_disable_flag(True)
    cpu.flags.set_break_flag(False)
    cpu.flags.set_one_flag(False)

    low = cpu.bus.read(0xFFFE)
    high = cpu.bus.read(0xFFFF)
    cpu.pc = (high << 8) | low

Instrucción

BRK -> Force Interrupt / Software Interrupt

Objetivo

implementar brk(cpu) en instructions.py.

Guía para el estudiante

BRK es una de las instrucciones del 6502 más fáciles de malinterpretar.

El opcode ocupa un byte

00 -> BRK

Pero la CPU trata BRK como una instrucción de 2 bytes. El byte que sigue al opcode $00 se omite. Ese byte puede tener cualquier valor. A veces se le llama byte de relleno o byte de firma.

Cronología importante

If BRK is stored at $8000:

    $8000: 00    BRK opcode
    $8001: XX    padding/signature byte, ignored by normal BRK behavior
    $8002: ...   next real instruction

CPU.step() fetches opcode $00 and increments PC to $8001.
Then brk(cpu) must add one more to produce return address $8002.

BRK debe

1. Compute return address as PC + 1.
2. Push return address high byte.
3. Push return address low byte.
4. Push status with Break flag set and ONE/unused bit set.
5. Set Interrupt Disable flag.
6. Clear Break again if your emulator models B only in the pushed status byte.
7. Load PC from IRQ/BRK vector $FFFE/$FFFF.

Errores comunes

  • Apilar $8001 en lugar de $8002.
  • Pensar que el byte de relleno debe ser $00. Puede ser cualquier valor.
  • Escribir el estado en $0100 | P en lugar de $0100 | S.
  • Cargar solo un byte del vector desde $FFFE.

Por qué existe este paso

Las llamadas directas entran con PC en el byte de relleno porque la obtención del opcode ya lo avanzó; BRK guarda la dirección de retorno posterior al relleno y el estado previo a I, y luego salta a través de $FFFE/$FFFF en little-endian. Invariantes: los apilamientos son PC alto, PC bajo, estado; S se envuelve después de cada uno; el P apilado tiene B/ONE activados; el P en vivo termina con I activado y B/ONE desactivados. Concepto erróneo: BRK no ejecuta ni requiere un byte de relleno con valor cero, y B es una propiedad de la copia de estado apilada.

Fuera de alcance: importar/mapear el opcode $00 es el paso 190. RTI, la entrada de IRQ/NMI por hardware, y los futuros ayudantes `CPU.push_byte/CPU.push_word` no deben introducirse aquí.

Ejecutar esta lección

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