Loading...
Searching...
No Matches
SNES Hardware Register Reference

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
// Turn screen on with full brightness
REG_INIDISP = 0x0F;
// Force blank (screen off)
REG_INIDISP = 0x80;
#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
// 8x8 small, 16x16 large sprites, tiles at $0000
REG_OBJSEL = 0x00;
// 16x16 small, 32x32 large sprites
REG_OBJSEL = 0x60;
#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.

// Set sprite 0 position
REG_OAMDATA = 100; // X position
REG_OAMDATA = 80; // Y position
REG_OAMDATA = 0; // Tile number
REG_OAMDATA = 0; // Attributes
#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
// Mode 1 with 8x8 tiles
REG_BGMODE = 0x01;
// Mode 1 with 16x16 BG1 tiles
REG_BGMODE = 0x11;
#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
// BG1 tilemap at $7000 (32x32 tiles)
REG_BG1SC = 0x70;
// BG1 tilemap at $7000 (64x64 tiles)
REG_BG1SC = 0x73;
#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)
// BG1 tiles at $0000, BG2 at $4000
REG_BG12NBA = 0x40;
#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.

// Scroll BG1 horizontally by 100 pixels
REG_BG1HOFS = 100 & 0xFF;
REG_BG1HOFS = 100 >> 8;
// Scroll BG1 vertically by 50 pixels
REG_BG1VOFS = 50 & 0xFF;
REG_BG1VOFS = 50 >> 8;
#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
// Increment after high byte write, step by 1
REG_VMAIN = 0x80;
#define REG_VMAIN
VRAM address increment mode (W)
Definition registers.h:112

$2116-$2117 - VMADDL/H (VRAM Address)

Word address (not byte address).

// Set VRAM address to $4000 (word address $2000)
REG_VMADDL = 0x00;
REG_VMADDH = 0x20;
#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)

// Write word $1234 to VRAM
REG_VMDATAL = 0x34; // Low byte
REG_VMDATAH = 0x12; // High byte (triggers increment)
#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).

// Set palette address to color 0
// Set palette address to sprite palette 0 (color 128)
REG_CGADD = 128;
#define REG_CGADD
CGRAM address (W)
Definition registers.h:148

$2122 - CGDATA (CGRAM Data Write)

15-bit color: 0BBBBBGGGGGRRRRR

// Set color to white ($7FFF)
REG_CGDATA = 0xFF; // Low byte
REG_CGDATA = 0x7F; // High byte
// Set color to red ($001F)
REG_CGDATA = 0x1F;
REG_CGDATA = 0x00;
// Set color to green ($03E0)
REG_CGDATA = 0xE0;
REG_CGDATA = 0x03;
// Set color to blue ($7C00)
REG_CGDATA = 0x00;
REG_CGDATA = 0x7C;
#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
// Enable BG1 and sprites
REG_TM = 0x11;
// Enable BG1, BG2, and sprites
REG_TM = 0x13;
#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
// Enable NMI and auto-joypad
REG_NMITIMEN = 0x81;
// Enable NMI, V-IRQ, and auto-joypad
REG_NMITIMEN = 0xA1;
#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.

// Trigger IRQ at scanline 128
REG_VTIMEL = 128;
#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!

// In NMI handler:
volatile u8 dummy = REG_RDNMI; // 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
// Wait for auto-joypad to complete
while (REG_HVBJOY & 0x01) {}
#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.

// Wait 8 cycles
u16 result = REG_RDMPYL | (REG_RDMPYH << 8); // = 100
#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.

REG_WRDIVL = 100 & 0xFF;
REG_WRDIVH = 100 >> 8;
// Wait 16 cycles
u16 quotient = REG_RDDIVL | (REG_RDDIVH << 8); // = 14
u16 remainder = REG_RDMPYL | (REG_RDMPYH << 8); // = 2
#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.

// Start DMA on channel 0
REG_MDMAEN = 0x01;
// Start DMA on channels 0 and 1
REG_MDMAEN = 0x03;
#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
// Mode 1 (for VRAM): write to $2118/$2119 alternating
REG_DMAP(0) = 0x01;
// Mode 0 (for CGRAM): write to $2122
REG_DMAP(0) = 0x00;
#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).

// DMA to VRAM data port ($2118)
REG_BBAD(0) = 0x18;
// DMA to CGRAM data port ($2122)
REG_BBAD(0) = 0x22;
// DMA to OAM data port ($2104)
REG_BBAD(0) = 0x04;
#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).

// Source at $7E:2000
REG_A1TL(0) = 0x00;
REG_A1TH(0) = 0x20;
REG_A1B(0) = 0x7E;
#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).

// Transfer 1024 bytes
REG_DASL(0) = 0x00;
REG_DASH(0) = 0x04;
#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

// DMA 2048 bytes to VRAM at $0000
void dmaToVRAM(const void* src, u16 vramAddr, u16 size) {
// Set VRAM address
REG_VMAIN = 0x80;
REG_VMADDL = vramAddr & 0xFF;
// Configure DMA channel 0
REG_DMAP(0) = 0x01; // Mode 1 (2-register)
REG_BBAD(0) = 0x18; // Destination: $2118 (VMDATAL)
REG_A1TL(0) = (u16)src & 0xFF;
REG_A1TH(0) = (u16)src >> 8;
REG_A1B(0) = 0x7E; // Source bank
REG_DASL(0) = size & 0xFF;
REG_DASH(0) = size >> 8;
// Start DMA
REG_MDMAEN = 0x01;
}

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 - - - -
// Read joypad 1
while (REG_HVBJOY & 0x01) {} // Wait for auto-read
// Check buttons
if (joy & 0x0080) { /* A pressed */ }
if (joy & 0x8000) { /* B pressed */ }
if (joy & 0x0800) { /* Up pressed */ }
#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>:

// Direct register access
REG_INIDISP = 0x0F;
REG_BGMODE = 0x01;
REG_TM = 0x11;
// DMA channel macros
REG_DMAP(0) = 0x01; // Channel 0
REG_BBAD(1) = 0x22; // Channel 1
REG_A1TL(2) = 0x00; // Channel 2
// PPU status (read-only)
if (REG_HVBJOY & 0x80) { /* In VBlank */ }

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