Basic ca65 Usage for SNES Programming

First Example

The first example is a minimal example of setting up the ROM header, vectors, etc.

Usage:

ca65 ca65.s
ld65 -C lorom128.cfg -o ca65.smc ca65.o

lorom128.cfg

# ca65 linker config for 128K SMC

# Physical areas of memory
# Names need not match, but it makes it easier to remember if they do.
MEMORY {
    ZEROPAGE:   start =      0, size =  $100;
    BSS:        start =   $200, size = $1800;
    ROM:        start =  $8000, size = $8000, fill = yes;
    BANK1:      start = $18000, size = $8000, fill = yes;
    BANK2:      start = $28000, size = $8000, fill = yes;
    BANK3:      start = $38000, size = $8000, fill = yes;
}

# Logical areas code/data can be put into.
SEGMENTS {
    ZEROPAGE:   load = ZEROPAGE,    type = zp;
    BSS:        load = BSS,         type = bss, align = $100;

    CODE:       load = ROM,         align = $8000;
    RODATA:     load = ROM;
    HEADER:     load = ROM,         start =  $FFC0;
    ROMINFO:    load = ROM,         start =  $FFD5, optional = yes;
    VECTORS:    load = ROM,         start =  $FFE0;

    # The extra three banks
    BANK1:      load = BANK1,       align = $8000, optional = yes;
    BANK2:      load = BANK2,       align = $8000, optional = yes;
    BANK3:      load = BANK3,       align = $8000, optional = yes;
}

ca65.s

; Minimal example of using ca65 to build SNES ROM.
;
; ca65 ca65.s
; ld65 -C lorom128.cfg -o ca65.smc ca65.o

.p816   ; 65816 processor
.i16    ; X/Y are 16 bits
.a8     ; A is 8 bits

.segment "HEADER"        ; +$7FE0 in file
    .byte "CA65 EXAMPLE" ; ROM name

.segment "ROMINFO"       ; +$7FD5 in file
    .byte $30            ; LoROM, fast-capable
    .byte 0              ; no battery RAM
    .byte $07            ; 128K ROM
    .byte 0,0,0,0
    .word $AAAA,$5555    ; dummy checksum and complement

.segment "VECTORS"
    .word 0, 0, 0, 0, 0, 0, 0, 0
    .word 0, 0, 0, 0, 0, 0, reset, 0

.segment "CODE"

reset:
    clc             ; native mode
    xce
    rep #$10        ; X/Y 16-bit
    sep #$20        ; A 8-bit

    ; Clear PPU registers
    ldx #$33
@loop:  stz $2100,x
    stz $4200,x
    dex
    bpl @loop

    ; Set background color to $03E0
    lda #$E0
    sta $2122
    lda #$03
    sta $2122

    ; Maximum screen brightness
    lda #$0F
    sta $2100

forever:
    jmp forever

Second Example

The second example hides the ROM boilerplate in another file and focuses on showing a green screen. It's useful as a fallback to see whether code is running at all in a given situation. It's about the simplest "hello world" style program that gives feedback (the absolute simplest is to enable interlace mode and stare closely at your CRT, an approach I used when first running code on the SNES).

Usage:

ca65 green.s
ld65 -C lorom128.cfg -o green.smc green.o

green.s

; Displays green screen
;
; ca65 green.s
; ld65 -C lorom128.cfg -o green.smc green.o

.define ROM_NAME "GREEN"
.include "lorom128.inc"

reset:
    init_cpu

    ; Clear PPU registers
    ldx #$33
@loop:  stz $2100,x
    stz $4200,x
    dex
    bpl @loop

    ; Set background color to $03E0
    lda #$E0
    sta $2122
    lda #$03
    sta $2122

    ; Maximum screen brightness
    lda #$0F
    sta $2100

forever:
    jmp forever

lowrom128.inc

; Sets up processor basics and ROM headers/vectors

.p816   ; 65816 processor
.i16    ; X/Y are 16 bits
.a8     ; A is 8 bits

.segment "HEADER"     ; +$7FE0 in file
    .byte ROM_NAME

.segment "ROMINFO"    ; +$7FD5 in file
    .byte $30         ; LoROM, fast-capable
    .byte 0           ; no battery RAM
    .byte $07         ; 128K ROM
    .byte 0,0,0,0
    .word $AAAA,$5555 ; dummy checksum and complement

.segment "VECTORS"
    .word 0, 0, 0, 0, 0, 0, 0, 0
    .word 0, 0, 0, 0, 0, 0, reset, 0

.code

.macro init_cpu
    clc
    xce
    rep #$10        ; X/Y 16-bit
    sep #$20        ; A 8-bit
.endmacro

Downloads

ca65_template.7z
green_screen.7z

Written by blargg on 2013-08-22.