===========================================================================
  GUS16-V8 Toolchain Test 0 — romGus.s
  Benchmark: programa original de Jesús Arias portado a sintaxis GAS
             por Carlos Venegas, primeras pruebas
===========================================================================

FICHERO FUENTE: romGus.s
REFERENCIA:     romGUS.hex (salida del ensamblador original)

Programa de test para la CPU GUS16-V8 que incluye:
  - Vectores de interrupción (JMP)
  - Variables en RAM (FIFO para UART)
  - ISR para UART RX y TX
  - Main loop con hexdump interactivo
  - Subrutinas: putch, getch, puts, prthex, prthex8, putchp, hdump
  - String embebido "Hello Worm!\n"

---------------------------------------------------------------------------
RESUMEN DE FORMATOS DE SALIDA
---------------------------------------------------------------------------

  Herramienta      Flag       Formato          Uso
  ──────────────   ────────   ──────────────   ─────────────────────────────
  gus16_asm.py     -f bin     Binario crudo    Imagen plana de ROM
  gus16_asm.py     -f hex     $readmemh        Simulación Verilog (directo)
  gus16_asm.py     -f elf     ELF32 .o         Enlazar con gus16_ld.py
  elf2hex.py       -f mem     $readmemh        Simulación Verilog (via ELF)
  elf2hex.py       -f bin     Binario crudo    Imagen plana de ROM
  elf2hex.py       -f hex     Intel HEX        Programadores Flash/EPROM

  Para la FPGA (Verilog $readmemh): usar siempre -f hex (asm) o -f mem (elf2hex).
  Intel HEX es para herramientas externas (Minipro, OpenOCD, bootloaders).

---------------------------------------------------------------------------
1. COMPILAR A FORMATO PLANO HEX (para $readmemh en Verilog/FPGA)
---------------------------------------------------------------------------

  python3 llvm-gus16/reference/gus16_asm.py romGus.s -o romGus.hex -f hex

  Genera: romGus.hex
  Formato: @ADDR seguido de words hex de 16 bits (compatible $readmemh).
  IMPORTANTE: Las direcciones son de BYTE (V8 byte-addressed).

  En Verilog:
    reg [15:0] rom [0:4095];
    initial $readmemh("romGus.hex", rom);

---------------------------------------------------------------------------
2. COMPILAR A BINARIO CRUDO (imagen plana)
---------------------------------------------------------------------------

  python3 llvm-gus16/reference/gus16_asm.py romGus.s -o romGus.bin -f bin

  Genera: romGus.bin
  Formato: bytes little-endian contiguos desde dirección 0.
  Los gaps (.org) se rellenan con ceros.

---------------------------------------------------------------------------
3. COMPILAR A ELF RELOCATABLE (.o)
---------------------------------------------------------------------------

  python3 llvm-gus16/reference/gus16_asm.py romGus.s -o romGus.o -f elf

  Genera: romGus.o (ELF32, e_machine=0xF116)
  Útil para enlazar con otros módulos via gus16_ld.py.

---------------------------------------------------------------------------
4. DESENSAMBLAR
---------------------------------------------------------------------------

  Desde binario:
    python3 llvm-gus16/reference/gus16_disasm.py romGus.bin

  Desde hex:
    python3 llvm-gus16/reference/gus16_disasm.py romGus.hex -f hex

  Output: texto con dirección, hex word, y mnemónico GAS.
  Los pares LDPC+literal y JMP/JMPL+literal se muestran como unidad.

---------------------------------------------------------------------------
5. ENLAZAR (si hay múltiples .o)
---------------------------------------------------------------------------

  python3 llvm-gus16/reference/gus16_ld.py romGus.o -o romGus.elf --entry=inicio

  Genera: romGus.elf (ELF32 ejecutable)

---------------------------------------------------------------------------
6. CONVERTIR ELF A FORMATOS DE CARGA
---------------------------------------------------------------------------

  $readmemh (Verilog) — FORMATO PRINCIPAL para simulación/FPGA:
    python3 llvm-gus16/tools/gus16-elf2hex/elf2hex.py romGus.elf -f mem -o romGus.mem

  Binario crudo — imagen plana de ROM:
    python3 llvm-gus16/tools/gus16-elf2hex/elf2hex.py romGus.elf -f bin -o romGus.flat

  Intel HEX (I8HEX) — formato estándar para programadores de Flash/EPROM:
    python3 llvm-gus16/tools/gus16-elf2hex/elf2hex.py romGus.elf -f hex -o romGus.ihex

  NOTA sobre formatos "hex":
    - gus16_asm.py -f hex   -> $readmemh (Verilog): @ADDR + words
    - elf2hex.py -f mem     -> $readmemh (Verilog): mismo formato
    - elf2hex.py -f hex     -> Intel HEX (I8HEX): :LLAAAATTDD...CC
    El Intel HEX es un formato industrial para programadores de memorias
    (Minipro, TL866), bootloaders serie, y herramientas como OpenOCD.
    Para simulación Verilog con $readmemh, usar siempre -f mem.

---------------------------------------------------------------------------
7. PIPELINE COMPLETO (UN SOLO COMANDO)
---------------------------------------------------------------------------

  # Ensamblar directo a hex (lo más rápido para FPGA):
  python3 llvm-gus16/reference/gus16_asm.py romGus.s -o romGus.hex -f hex

  # O via ELF (si se necesita linkar con otros módulos):
  python3 llvm-gus16/reference/gus16_asm.py romGus.s -o romGus.o -f elf
  python3 llvm-gus16/reference/gus16_ld.py romGus.o -o romGus.elf --entry=inicio
  python3 llvm-gus16/tools/gus16-elf2hex/elf2hex.py romGus.elf -f mem -o romGus.mem

---------------------------------------------------------------------------
8. VERIFICACIÓN RÁPIDA
---------------------------------------------------------------------------

  # Comprobar que los vectores apuntan correctamente:
  head -8 romGus.hex
  # Debe mostrar:
  #   @0000
  #   78E4        (JMP opcode)
  #   009A        (-> start)
  #   78E4        (JMP opcode)
  #   005A        (-> irq0)
  #   78E4        (JMP opcode)
  #   0078        (-> irq1)

  # Comprobar el string embebido:
  strings romGus.bin
  # Debe mostrar "Hello Worm!" entre la salida

  # Comprobar tamaño:
  wc -c romGus.bin
  # ~465 bytes de código + datos, luego gap hasta RAMend (0x2000)

---------------------------------------------------------------------------
9. COMPARACIÓN CON ENSAMBLADOR LEGACY
---------------------------------------------------------------------------

  El hex legacy (legacy/romGUS.hex) fue generado por el ensamblador
  original de Jesús Arias. Diferencias de formato:

  - Legacy usa direcciones de WORD: @0020 = word 0x20 = byte 0x40
  - Nuestro usa direcciones de BYTE: @0040 = byte 0x40

  Para comparar: legacy_addr * 2 = nuestro_addr.

  Resultado de la comparación automática (script Python):

    Vectores (0x0000-0x000A):    IDÉNTICOS [OK]
    Variables (0x0040-0x0059):   IDÉNTICAS [OK]
    Instrucciones:               127 de 129 opcodes únicos IDÉNTICOS [OK]
    Diferencias:                 2 direcciones absolutas desplazadas +2 bytes

  Las 2 diferencias son direcciones absolutas (puntero a `txt` y a `puts`)
  desplazadas porque nuestro port añade `ADDI LR, 2` tras JMPL (requerido
  por el quirk de V8 rev.2 donde LR apunta al literal, no a la instrucción
  siguiente).

  La codificación de instrucciones es 100% correcta.
  Las diferencias son solo de layout por la convención JMPL de V8 rev.2.

  Script de comparación (ejecutar desde la raíz del proyecto):

    python3 -c "
    def parse_hex(path, word_addr=False):
        words = {}; addr = 0
        for line in open(path):
            line = line.strip()
            if not line: continue
            if line.startswith('@'):
                addr = int(line[1:], 16)
                if word_addr: addr *= 2
            else:
                words[addr] = int(line, 16); addr += 2
        return words

    legacy = parse_hex('legacy/romGUS.hex', word_addr=True)
    ours = parse_hex('romGus.hex', word_addr=False)
    all_addrs = sorted(set(list(legacy.keys()) + list(ours.keys())))
    diffs = sum(1 for a in all_addrs
                if legacy.get(a) != ours.get(a) and a in legacy and a in ours)
    matches = sum(1 for a in all_addrs
                  if legacy.get(a) == ours.get(a) and a in legacy and a in ours)
    print(f'Matches: {matches}, Diffs: {diffs}')
    "

---------------------------------------------------------------------------
10. NOTAS DE PORTADO (original -> GAS)
---------------------------------------------------------------------------

  Cambios de sintaxis aplicados:

  ORIGINAL (V8 asm)          ->  GAS (toolchain Python/LLVM)
  ─────────────────────      ─────────────────────────────
  ORG 0                      ->  .org 0
  WORD value                 ->  .word value
  label= expr                ->  .equ label, expr
  ASCIIZ "text"              ->  .asciz "text"
  LD R0,(R1+4)               ->  LD R0, 4(R1)
  ST (SP+0),R0               ->  ST R0, 0(SP)
  JMP / WORD target          ->  JMP target  (emite ambas words)
  JMPL / WORD target         ->  JMPL target  (emite ambas words)
  JIND LR                    ->  JIND LR  (o RET)
  LDI R1,<fifo               ->  LDI R1, fifo  (si cabe en 8 bits)
                                 o LI R1, fifo  (si >255)

  Quirks preservados del código original:
  - ADDI LR, 2 antes de cada entry point de subrutina (convención
    del autor para llamadas via JMPL; en V8 rev.2 es obligatorio)
  - Stack trick: SP = RAMend + 0x40 (usa espacio I/O como RAM)
  - FIFO circular para UART RX con interrupciones
  - Polling para UART TX (versión FIFO comentada en el original)

---------------------------------------------------------------------------
11. FICHEROS EN 
---------------------------------------------------------------------------

  romGus.s         Código fuente portado a sintaxis GAS
  romGus.hex       Salida $readmemh (byte-addressed)
  romGus.bin       Imagen binaria plana
  romGus.o         ELF32 relocatable
  romGus.elf       ELF32 ejecutable (linkado)
  romGus.mem       Verilog $readmemh (via ELF->elf2hex)
