Complete reference for all SNES hardware registers commonly used in game development.
Table of Contents
PPU Registers ($2100-$213F)
Display Control
$2100 - INIDISP (Display Control)
| Bit | Name | Description |
| 7 | Force Blank | 1 = Screen off (black), VRAM/OAM/CGRAM accessible |
| 6-4 | - | Unused |
| 3-0 | Brightness | 0 = Black, 15 = Full brightness |
#define REG_INIDISP
Display control (W)
Definition registers.h:49
$2101 - OBJSEL (Sprite Settings)
| Bit | Name | Description |
| 7-5 | Size | Sprite size combination (see table) |
| 4-3 | Name Select | Sprite tile address offset |
| 2-0 | Base | Sprite tile base address (/8192) |
Sprite Sizes:
| Value | Small | Large |
| 0 | 8x8 | 16x16 |
| 1 | 8x8 | 32x32 |
| 2 | 8x8 | 64x64 |
| 3 | 16x16 | 32x32 |
| 4 | 16x16 | 64x64 |
| 5 | 32x32 | 64x64 |
| 6 | 16x32 | 32x64 |
| 7 | 16x32 | 32x32 |
#define REG_OBJSEL
Object (sprite) size and base (W)
Definition registers.h:52
OAM (Sprite Memory)
$2102-$2103 - OAMADDL/H (OAM Address)
| Register | Bits | Description |
| $2102 | 7-0 | OAM address low byte |
| $2103 | 7 | OAM priority rotation |
| $2103 | 0 | OAM address bit 9 |
$2104 - OAMDATA (OAM Data Write)
Write sprite data to OAM. Address auto-increments.
#define REG_OAMDATA
OAM data write (W)
Definition registers.h:61
#define REG_OAMADDH
OAM address high (W)
Definition registers.h:58
#define REG_OAMADDL
OAM address low (W)
Definition registers.h:55
Background Mode
$2105 - BGMODE (Background Mode)
| Bit | Name | Description |
| 7 | BG4 Tile Size | 0 = 8x8, 1 = 16x16 |
| 6 | BG3 Tile Size | 0 = 8x8, 1 = 16x16 |
| 5 | BG2 Tile Size | 0 = 8x8, 1 = 16x16 |
| 4 | BG1 Tile Size | 0 = 8x8, 1 = 16x16 |
| 3 | Mode 1 BG3 Priority | 0 = Normal, 1 = High |
| 2-0 | BG Mode | 0-7 (see table below) |
Background Modes:
| Mode | BG1 | BG2 | BG3 | BG4 | Notes |
| 0 | 4-color | 4-color | 4-color | 4-color | 4 layers |
| 1 | 16-color | 16-color | 4-color | - | Most common |
| 2 | 16-color | 16-color | - | - | Offset-per-tile |
| 3 | 256-color | 16-color | - | - | Direct color |
| 4 | 256-color | 4-color | - | - | Offset-per-tile |
| 5 | 16-color | 4-color | - | - | Hi-res 512px |
| 6 | 16-color | - | - | - | Hi-res + offset |
| 7 | 256-color | - | - | - | Rotation/scaling |
#define REG_BGMODE
BG mode and tile size (W)
Definition registers.h:64
Background Tilemap
$2107-$210A - BG1SC-BG4SC (Tilemap Address/Size)
| Bit | Name | Description |
| 7-2 | Address | Tilemap VRAM address (/1024) |
| 1-0 | Size | 00=32x32, 01=64x32, 10=32x64, 11=64x64 |
#define REG_BG1SC
BG1 tilemap address (W)
Definition registers.h:70
$210B-$210C - BG12NBA/BG34NBA (Tile Data Address)
| Bit | Name | Description |
| 7-4 | BG2/BG4 | Tile data address (/4096) |
| 3-0 | BG1/BG3 | Tile data address (/4096) |
#define REG_BG12NBA
BG1/2 tile data address (W)
Definition registers.h:82
Background Scroll
$210D-$2114 - BGnHOFS/BGnVOFS (Scroll Registers)
Write twice: first low byte, then high byte.
#define REG_BG1VOFS
BG1 vertical scroll (W, 2x write)
Definition registers.h:91
#define REG_BG1HOFS
BG1 horizontal scroll (W, 2x write)
Definition registers.h:88
VRAM Access
$2115 - VMAIN (VRAM Address Increment)
| Bit | Name | Description |
| 7 | Increment | 0 = After $2118, 1 = After $2119 |
| 3-2 | Translation | Address translation mode |
| 1-0 | Step | 00 = +1, 01 = +32, 10 = +128, 11 = +128 |
#define REG_VMAIN
VRAM address increment mode (W)
Definition registers.h:112
$2116-$2117 - VMADDL/H (VRAM Address)
Word address (not byte address).
#define REG_VMADDH
VRAM address high (W)
Definition registers.h:118
#define REG_VMADDL
VRAM address low (W)
Definition registers.h:115
$2118-$2119 - VMDATAL/H (VRAM Data Write)
#define REG_VMDATAL
VRAM data write low (W)
Definition registers.h:121
#define REG_VMDATAH
VRAM data write high (W)
Definition registers.h:124
Color (CGRAM)
$2121 - CGADD (CGRAM Address)
Color index (0-255).
#define REG_CGADD
CGRAM address (W)
Definition registers.h:148
$2122 - CGDATA (CGRAM Data Write)
15-bit color: 0BBBBBGGGGGRRRRR
#define REG_CGDATA
CGRAM data write (W)
Definition registers.h:151
Screen Enable
$212C - TM (Main Screen Enable)
| Bit | Name | Description |
| 4 | OBJ | Enable sprites on main screen |
| 3 | BG4 | Enable BG4 on main screen |
| 2 | BG3 | Enable BG3 on main screen |
| 1 | BG2 | Enable BG2 on main screen |
| 0 | BG1 | Enable BG1 on main screen |
#define REG_TM
Main screen designation (W)
Definition registers.h:181
$212D - TS (Sub Screen Enable)
Same bit layout as TM, for sub screen (color math).
CPU Registers ($4200-$421F)
Interrupt Control
$4200 - NMITIMEN (Interrupt Enable)
| Bit | Name | Description |
| 7 | NMI Enable | Enable VBlank NMI |
| 5 | V-IRQ | Enable V-counter IRQ |
| 4 | H-IRQ | Enable H-counter IRQ |
| 0 | Joypad | Enable auto-joypad read |
#define REG_NMITIMEN
Interrupt enable (W)
Definition registers.h:253
$4207-$420A - HTIMEx/VTIMEx (IRQ Timer)
H position (0-339) and V position (0-261/311) for IRQ trigger.
#define REG_VTIMEL
V-count timer low (W)
Definition registers.h:280
#define REG_HTIMEH
H-count timer high (W)
Definition registers.h:277
#define REG_VTIMEH
V-count timer high (W)
Definition registers.h:283
#define REG_HTIMEL
H-count timer low (W)
Definition registers.h:274
$4210 - RDNMI (NMI Status)
| Bit | Name | Description |
| 7 | NMI | 1 = NMI occurred (cleared on read) |
| 3-0 | Version | CPU version |
IMPORTANT: Read this register in NMI handler to acknowledge NMI!
static u16 bx
Definition main.c:159
#define REG_RDNMI
NMI flag and version (R)
Definition registers.h:295
unsigned char u8
8-bit unsigned integer (0 to 255)
Definition types.h:46
$4211 - TIMEUP (IRQ Status)
| Bit | Name | Description |
| 7 | IRQ | 1 = IRQ occurred (cleared on read) |
IMPORTANT: Read this register in IRQ handler to acknowledge IRQ!
$4212 - HVBJOY (Blank/Joypad Status)
| Bit | Name | Description |
| 7 | VBlank | 1 = In VBlank |
| 6 | HBlank | 1 = In HBlank |
| 0 | Joypad | 1 = Auto-joypad read in progress |
#define REG_HVBJOY
H/V blank and joypad status (R)
Definition registers.h:301
Math Hardware
$4202-$4203 - WRMPYA/B (Multiply)
8-bit x 8-bit = 16-bit multiplication.
#define REG_WRMPYA
Multiplicand A (W)
Definition registers.h:259
#define REG_RDMPYH
Multiplication result high (R)
Definition registers.h:316
#define REG_WRMPYB
Multiplicand B (W)
Definition registers.h:262
#define REG_RDMPYL
Multiplication result low (R)
Definition registers.h:313
unsigned short u16
16-bit unsigned integer (0 to 65535)
Definition types.h:52
$4204-$4206 - WRDIV (Divide)
16-bit / 8-bit division with remainder.
#define REG_WRDIVB
Divisor (W)
Definition registers.h:271
#define REG_WRDIVL
Dividend low (W)
Definition registers.h:265
#define REG_RDDIVH
Division result high (R)
Definition registers.h:310
#define REG_WRDIVH
Dividend high (W)
Definition registers.h:268
#define REG_RDDIVL
Division result low (R)
Definition registers.h:307
DMA Control
$420B - MDMAEN (General DMA Enable)
Write a bit mask to start DMA on channels 0-7.
#define REG_MDMAEN
DMA enable (W)
Definition registers.h:286
$420C - HDMAEN (HDMA Enable)
Enable HDMA on channels 0-7. HDMA runs automatically each scanline.
DMA Registers ($4300-$437F)
Each channel has 16 bytes of registers at $43x0-$43xF where x = channel (0-7).
Channel Configuration
$43x0 - DMAPx (DMA Parameters)
| Bit | Name | Description |
| 7 | Direction | 0 = A→B (CPU→PPU), 1 = B→A |
| 6 | HDMA Mode | 0 = Absolute, 1 = Indirect |
| 5 | - | Unused |
| 4 | Auto Inc | 0 = Auto, 1 = Fixed source |
| 3 | Fixed/Dec | 0 = Increment, 1 = Decrement |
| 2-0 | Mode | Transfer mode (see table) |
Transfer Modes:
| Mode | Pattern | Description |
| 0 | 1 byte | Write to single register |
| 1 | 2 bytes | Write to reg, reg+1 alternating |
| 2 | 2 bytes | Write to reg twice |
| 3 | 4 bytes | Write to reg, reg+1 x2 each |
| 4 | 4 bytes | Write to reg, reg+1, reg+2, reg+3 |
#define REG_DMAP(n)
DMA parameters for channel n.
Definition registers.h:361
$43x1 - BBADx (B-Bus Address)
PPU register address (low byte only, $21xx).
#define REG_BBAD(n)
DMA B-bus address for channel n.
Definition registers.h:364
$43x2-$43x4 - A1TxL/H/B (Source Address)
24-bit source address (CPU side).
#define REG_A1TL(n)
DMA A-bus address low for channel n.
Definition registers.h:367
#define REG_A1TH(n)
DMA A-bus address high for channel n.
Definition registers.h:370
#define REG_A1B(n)
DMA A-bus bank for channel n.
Definition registers.h:373
$43x5-$43x6 - DASxL/H (Transfer Size)
Number of bytes to transfer (0 = 65536).
#define REG_DASL(n)
DMA size low for channel n.
Definition registers.h:376
#define REG_DASH(n)
DMA size high for channel n.
Definition registers.h:379
DMA Example
Joypad Registers
$4218-$421F - JOY1-4 (Auto-Read Results)
After auto-joypad read completes (check $4212 bit 0):
| Register | Description |
| $4218 | Joypad 1 low byte |
| $4219 | Joypad 1 high byte |
| $421A | Joypad 2 low byte |
| $421B | Joypad 2 high byte |
| $421C-$421F | Joypads 3-4 (multitap) |
Button Bit Layout:
| Byte | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
| Low ($4218) | B | Y | Select | Start | Up | Down | Left | Right |
| High ($4219) | A | X | L | R | - | - | - | - |
#define REG_JOY1H
Joypad 1 data high (R)
Definition registers.h:328
#define REG_JOY1L
Joypad 1 data low (R)
Definition registers.h:325
OpenSNES Register Macros
The SDK provides convenient macros in <snes/registers.h>:
Further Reading
- Memory Map - Full address space layout
- OAM - Sprite attribute memory
- Fullsnes - Complete technical reference
SA-1 Registers ($2200-$230E)
The SA-1 is a second 65816 CPU at 10.74 MHz. These registers control inter-processor communication and memory protection. For the full register set and programming details, see .claude/SA-1.md.
| Register | Address | R/W | Description |
| CCNT | $2200 | W | SA-1 CPU control: reset, IRQ, NMI, 4-bit message to SA-1 |
| CRV | $2203-$2204 | W | SA-1 Reset Vector (address SA-1 jumps to on release from reset) |
| SIWP | $2229 | W | SNES I-RAM Write Protection (bit=1 means page is writable) |
| CIWP | $222A | W | SA-1 I-RAM Write Protection (bit=1 means page is writable) |
| SFR | $2300 | R | Status Flags: SA-1 IRQ/NMI pending, 4-bit message from SA-1 |
Typical boot sequence:
; Set SA-1 reset vector to our coprocessor entry point
lda.w #sa1_entry
sta.l $2203 ; CRV low
lda.w #sa1_entry>>8
sta.l $2204 ; CRV high (bank handled by mapping)
; Enable I-RAM writes for both CPUs
lda #$FF
sta.l $2229 ; SIWP — all pages writable by SNES
sta.l $222A ; CIWP — all pages writable by SA-1
; Release SA-1 from reset
lda #$00
sta.l $2200 ; CCNT — clear reset bit, SA-1 starts running
SuperFX / GSU Registers ($3000-$303F)
The SuperFX (Graphics Support Unit) is a custom RISC processor with 16 general-purpose registers, hardware multiply, and a built-in pixel PLOT engine. GSU code runs from cartridge ROM/cache and renders into a shared SRAM framebuffer.
| Register | Address | R/W | Description |
| R0-R15 | $3000-$301F | R/W | General-purpose registers (16-bit each). Writing R15 starts the GSU! |
| SFR | $3030-$3031 | R | Status/Flag Register: GO bit (bit 5) = GSU is running |
| PBR | $3034 | R/W | Program Bank Register (GSU code bank) |
| SCBR | $3038 | W | Screen Base Register (framebuffer base address in SRAM) |
| SCMR | $303A | W | Screen Mode: pixel depth + bus ownership (RAN=SRAM, RON=ROM access) |
| VCR | $303B | R | Version Code Register (read-only, identifies GSU revision) |
Key concepts:
- R15 is the program counter — writing to it triggers GSU execution
- SFR GO bit — poll
$3030 bit 5 to wait for GSU to finish (STOP instruction clears it)
- SCMR bus ownership — SNES and GSU cannot access ROM/SRAM simultaneously; set RAN/RON bits to hand off bus access between frames
- PLOT — GSU instruction that writes a pixel to the framebuffer at coordinates set by R1 (X) and R2 (Y), using the color in the COLOR register
; Wait for GSU to finish (poll SFR GO bit)
- lda.l $3030
and #$20 ; bit 5 = GO
bne - ; loop while GSU is running
; Start GSU: write entry address to R15
lda.w #gsu_entry
sta.l $301E ; R15 low/high — GSU begins execution