Manual Completo de Programación para ROMs de SNES

Introducción

Este manual cubre desde lo más básico hasta conceptos avanzados para programar ROMs de Super Nintendo Entertainment System (SNES).

Requisitos

1. Arquitectura del SNES

1.1. CPU Ricoh 5A22

El SNES utiliza una CPU basada en el procesador WDC 65C816:

1.2. Memoria

        Rango de memoria   | Descripción
        -------------------|-------------------------------
        $0000-$1FFF        | WRAM (Work RAM, 128KB)
        $2100-$21FF        | Registros PPU
        $4200-$44FF        | Registros de CPU y DMA
        $7E0000-$7FFFFF    | WRAM mapeada (128KB)
        $8000-$FFFF        | ROM del cartucho (banco 0)
        

1.3. PPU (Picture Processing Unit)

Responsable de los gráficos:

2. Configuración Básica de un Proyecto

2.1. Estructura del ROM

Un ROM de SNES típico tiene esta estructura:

        ; Cabecera del ROM (Header)
        .org $FFB0
        ROMHeader:
            .db "TITLE OF GAME    "  ; Título de 21 caracteres
            .db $30                  ; Map mode (SlowROM)
            .db $02                  ; Tipo de cartucho (ROM+RAM+Battery)
            .db $0A                  ; Tamaño del ROM (4MB)
            .db $03                  ; Tamaño de la SRAM (64KB)
            .db $00                  ; País (Japón)
            .db $00                  ; Licencia code
            .db $00                  ; Versión
            .dw $0000                ; Checksum complement
            .dw $FFFF                ; Checksum
            ; ... más campos de cabecera
        

2.2. Archivo de configuración básico (WLA-DX)

        ; Ejemplo de archivo .cfg para WLA-DX
        [OBJECT]
        OUTPUT = game.smc
        NAME = GAMEPROJECT
        
        [MEMORY]
        ROM0:  start=$008000 size=$8000 fill=yes
        ROM1:  start=$018000 size=$8000 fill=yes
        ; ... más bancos según sea necesario
        
        [BANKS]
        0: start=$008000 size=$8000
        1: start=$018000 size=$8000
        
        [PATHS]
        .
        
        [SYMBOLS]
        

3. Programación en 65816 Assembler

3.1. Registros

        ; Registros principales
        A  = Acumulador (8/16 bits)
        X  = Registro índice X (8/16 bits)
        Y  = Registro índice Y (8/16 bits)
        D  = Registro direct page (16 bits)
        DB = Registro data bank (8 bits)
        P  = Registro de estado (8 bits)
        S  = Stack pointer (16 bits)
        PC = Program counter (24 bits)
        

3.2. Modos de direccionamiento

        ; Ejemplos de modos de direccionamiento
        LDA #$42       ; Inmediato
        LDA $00        ; Direct page
        LDA $00,X      ; Indexado por X
        LDA [$00],Y    ; Indirecto indexado largo
        LDA $123456    ; Absoluto largo
        

3.3. Ejemplo de código básico

        ; Inicialización básica del sistema
        .include "header.inc"
        
        .bank 0
        .org $8000
        
        Reset:
            SEI          ; Deshabilitar interrupciones
            CLC          ; Limpiar carry
            XCE          ; Cambiar a modo nativo (65816)
            
            REP #$30     ; Registros A/X/Y de 16 bits
            LDA #$0000
            TCD          ; Establecer direct page a $0000
            LDX #$1FFF
            TXS          ; Establecer stack pointer
            
            ; Limpiar WRAM
            LDA #$0000
            LDX #$0000
            .ClearWRAM:
                STA $7E0000,X
                INX
                INX
                CPX #$8000
                BNE .ClearWRAM
            
            ; Inicialización del PPU
            JSR InitPPU
            
            ; Habilitar pantalla
            LDA #$0F
            STA $2100    ; Brightness = 15, pantalla habilitada
            
            ; Bucle principal
            .MainLoop:
                WAI      ; Esperar interrupción VBlank
                JSR ReadControllers
                JSR UpdateGame
                BRA .MainLoop
        

4. Gráficos y Sprites

4.1. Modos gráficos (BG Mode)

El SNES tiene 8 modos gráficos principales (0-7):

        ; Configurar modo gráfico (registro $2105)
        LDA #$01       ; Modo 1, 16 colores por tile
        STA $2105
        
        ; Modos comunes:
        ; $00: 4 capas de 4 colores
        ; $01: Capa 1/2: 16 colores, Capa 3: 4 colores
        ; $07: Modo 7 (para efectos de rotación/escala)
        

4.2. Carga de tiles

        ; Cargar tiles en VRAM
        ; Asumimos que los tiles están en ROM en $018000
        LDA #$1801      ; DMA: Transferencia de CPU a PPU, incremento de 1
        STA $4300
        LDA #.loword(Tiles)
        STA $4302
        LDX #^Tiles     ; Banco de los datos
        STX $4304
        LDA #$1000      ; VRAM destino: $1000
        STA $2116
        LDA #$2000      ; Tamaño: $2000 bytes (64 tiles de 8x8 4bpp)
        STA $4305
        LDX #$01
        STX $420B       ; Iniciar DMA canal 0
        
        Tiles:
            .incbin "tiles.bin"
        

4.3. Sprites (OBJ)

        ; Configurar sprites
        LDA #$01
        STA $2101       ; Tamaño de sprite: 8x8 y 16x16
        
        ; Estructura de OAM (Object Attribute Memory)
        ; Cada sprite usa 4 bytes:
        ; Byte 0: Posición X (bit 0-7)
        ; Byte 1: Posición Y
        ; Byte 2: Tile number
        ; Byte 3: Atributos (prioridad, paleta, flip, etc.)
        
        ; Ejemplo: Configurar sprite 0
        STZ $2102       ; Resetear OAM address
        STZ $2103
        
        LDA #$50        ; Posición Y = 80
        STA $2104
        LDA #$60        ; Posición X = 96 (bit 0-7)
        STA $2104
        LDA #$00        ; Tile 0
        STA $2104
        LDA #%00110000  ; Prioridad 3, paleta 0, no flip
        STA $2104
        LDA #$00        ; Posición X bit 8 (para X > 255)
        STA $2104
        

5. Entrada de controles

5.1. Lectura de controles

        ReadControllers:
            LDA #$01
            STA $4016    ; Iniciar lectura
            STZ $4016
            
            ; Leer controles del jugador 1
            LDX #$00
            .ReadLoop:
                LDA $4016
                LSR A
                ROL $00   ; Almacenar en $00 (botones bajos)
                LDA $4017
                LSR A
                ROL $02   ; Almacenar en $02 (botones altos)
                INX
                CPX #$10
                BNE .ReadLoop
            
            ; Combinar resultados
            LDA $00
            STA Controller1
            LDA $02
            STA Controller1+1
            RTS
            
        Controller1: .dw $0000  ; 16 bits para los botones
        

5.2. Mapeo de botones

        ; Bits en Controller1:
        ; Bit 0: B
        ; Bit 1: Y
        ; Bit 2: Select
        ; Bit 3: Start
        ; Bit 4: Up
        ; Bit 5: Down
        ; Bit 6: Left
        ; Bit 7: Right
        ; Bit 8: A
        ; Bit 9: X
        ; Bit 10: L
        ; Bit 11: R
        

6. Sonido (SPC700)

6.1. Arquitectura del SPC700

6.2. Comunicación con el SPC700

        ; Ejemplo de carga de música
        LoadMusic:
            ; Esperar a que el SPC esté listo
            .WaitReady:
                LDA $2140
                CMP #$AA
                BNE .WaitReady
            
            ; Iniciar transferencia
            LDA #$01
            STA $2141    ; Comando: cargar música
            
            ; Transferir datos del DSP
            ; (código omitido por brevedad)
            
            ; Ejecutar código en SPC
            LDA #$CC
            STA $2140    ; Señal de ejecución
            RTS
        

7. Técnicas Avanzadas

7.1. DMA (Direct Memory Access)

        ; Ejemplo de transferencia DMA
        DoDMA:
            ; Configurar DMA
            LDA #$01      ; DMA control: transferir de CPU a PPU
            STA $4300
            LDA #$18      ; PPU destino: $2118 (VRAM data)
            STA $4301
            LDA #.loword(SourceData)
            STA $4302     ; Dirección baja
            LDA #^SourceData
            STA $4304     ; Banco
            LDA #$1000    ; Tamaño: $1000 bytes
            STA $4305
            LDA #$01      ; Habilitar DMA canal 0
            STA $420B
            RTS
            
        SourceData: .incbin "graphics.bin"
        

7.2. HDMA (HBlank DMA)

        ; Ejemplo de HDMA para efectos de parallax
        SetupHDMA:
            ; Tabla HDMA para scroll horizontal
            LDA #$42      ; HDMA control: 1 registro, write twice
            STA $4300
            LDA #$0D      ; PPU destino: $210D (BG1HOFS)
            STA $4301
            LDA #.loword(HDMATable)
            STA $4302
            LDA #^HDMATable
            STA $4304
            LDA #$01      ; Habilitar HDMA canal 0
            STA $420C
            RTS
            
        HDMATable:
            .db $20 : .dw $0100  ; 32 líneas, scroll = $0100
            .db $30 : .dw $0110  ; 48 líneas, scroll = $0110
            .db $00              ; Fin de tabla
        

7.3. Modo 7 (Rotación/Escala)

        ; Configurar modo 7
        SetupMode7:
            LDA #$07
            STA $2105      ; Modo gráfico 7
            
            ; Matriz de transformación
            LDA #$00
            STA $211B      ; Matriz A baja
            LDA #$01
            STA $211B      ; Matriz A alta
            STA $211C      ; Matriz B = 1
            
            LDA #$00
            STA $211D      ; Matriz C = 0
            STA $211E      ; Matriz D baja
            LDA #$01
            STA $211E      ; Matriz D alta
            
            ; Centro de rotación
            LDA #$80
            STA $211F      ; Centro X
            STA $2120      ; Centro Y
            RTS
        

8. Depuración y Optimización

8.1. Técnicas de depuración

8.2. Optimización de código

        ; Ejemplo de optimización de bucle
        ; Versión no optimizada:
        LDX #$00
        .LoopSlow:
            LDA Data,X
            STA $7E0000,X
            INX
            CPX #$80
            BNE .LoopSlow
            
        ; Versión optimizada:
        LDX #$7F        ; Contador descendente
        .LoopFast:
            LDA Data,X
            STA $7E0000,X
            DEX
            BPL .LoopFast  ; Repetir hasta X = $FF
        

9. Recursos y Referencias

9.1. Documentación oficial

9.2. Herramientas recomendadas

9.3. Comunidades

Conclusión

Programar para SNES es un desafío gratificante que combina conocimientos de hardware de bajo nivel con creatividad. Este manual cubre los conceptos fundamentales, pero la mejor manera de aprender es experimentar y estudiar ROMs existentes.

Nota: Este documento es una introducción. La programación real de SNES requiere estudio profundo de la documentación técnica y mucha práctica.