Every SNES cartridge carries a header that the game’s developer wrote into the ROM,
ending just before the processor’s interrupt vectors. It is visible to the console at
$00FFC0–$00FFFF (the CPU’s bank 0) whatever the cartridge’s
memory layout, so its place in the file depends on that layout. This page follows the
SNESdev Wiki; offsets are given as the SNES addresses $FFB0–$FFFF,
and numbers are little-endian, as on the 65816.
Where the header is
| Layout | How the ROM is mapped | Header in the file | CPU address |
|---|---|---|---|
| LoROM | 32 KiB banks, from $800000 | 0x7FC0 | $00FFC0 |
| HiROM | 64 KiB banks, from $C00000 | 0xFFC0 | $00FFC0 |
| ExHiROM | 64 KiB banks from $C00000, continuing from $400000 after the first 4 MiB | 0x40FFC0 | $40FFC0 |
These offsets are for a file without a copier
header; with one, add 512. The file does not say which layout it uses, so readers
score each candidate location with the checks below. An
optional expanded header sits in the 16 bytes before the main one
($FFB0–$FFBF).
Header fields
| Address | Size | Field | Meaning |
|---|---|---|---|
$FFC0 | 21 | Title | ASCII $20–$7E (some Japanese titles use JIS X 0201), padded with spaces. |
$FFD5 | 1 | Speed and map mode | See map mode. |
$FFD6 | 1 | Chipset | RAM, battery and coprocessor. See chipset. |
$FFD7 | 1 | ROM size | 1 << N KiB, rounded up: 8 means 256 KiB, 12 means 4 MiB. |
$FFD8 | 1 | RAM size | 1 << N KiB of cartridge RAM: 1 means 2 KiB, 5 means 32 KiB. |
$FFD9 | 1 | Country | Region and video system. See country codes. |
$FFDA | 1 | Developer ID | The publisher’s code. $33 means the expanded header at $FFB0 is present. |
$FFDB | 1 | ROM version | 0 for the first release. |
$FFDC | 2 | Checksum complement | The checksum XOR $FFFF. |
$FFDE | 2 | Checksum | See checksum. |
$FFE0 | 32 | Interrupt vectors | See vectors. |
Map mode ($FFD5)
The bits are 001smmmm: bit 4 (s) is the ROM speed, 0 slow and
1 fast, and the low four bits are the map mode.
| Low nibble | Map mode | Slow byte | Fast byte |
|---|---|---|---|
$0 | LoROM | $20 | $30 |
$1 | HiROM | $21 | $31 |
$2 | S-DD1 | $22 | $32 |
$3 | SA-1 | $23 | $33 |
$5 | ExHiROM | $25 | $35 |
$A | SPC7110 | $2A | $3A |
The full bytes are worked out from the bit layout; the SNESdev Wiki lists the low-nibble values. Other low nibbles are not defined there.
Chipset ($FFD6)
| Low nibble | Hardware |
|---|---|
$0 | ROM only. |
$1 | ROM and RAM. |
$2 | ROM, RAM and battery. |
$3 | ROM and coprocessor. |
$4 | ROM, coprocessor and RAM. |
$5 | ROM, coprocessor, RAM and battery. |
$6 | ROM, coprocessor and battery. |
When the low nibble is 3 to 6, the high nibble names the coprocessor:
| High nibble | Coprocessor |
|---|---|
$0 | DSP (DSP-1, -2, -3 or -4). |
$1 | GSU (Super FX). |
$2 | OBC1. |
$3 | SA-1. |
$4 | S-DD1. |
$5 | S-RTC. |
$E | Other (Super Game Boy, Satellaview). |
$F | Custom: the chip is named by the subtype at $FFBF. |
$FFBF | Custom chip |
|---|---|
$00 | SPC7110. |
$01 | ST010 or ST011. |
$02 | ST018. |
$10 | CX4. |
Expanded header ($FFB0–$FFBF)
Present when the developer ID at $FFDA is $33.
| Address | Size | Field | Meaning |
|---|---|---|---|
$FFB0 | 2 | Maker code | Two upper-case ASCII characters. |
$FFB2 | 4 | Game code | Four upper-case ASCII characters; the last letter matches the country. |
$FFB6 | 6 | Reserved | Zero. |
$FFBC | 1 | Expansion flash size | 1 << N KiB. |
$FFBD | 1 | Expansion RAM size | RAM for the coprocessor, chiefly the GSU; see below. |
$FFBE | 1 | Special version | Usually 0. |
$FFBF | 1 | Chipset subtype | Used when the chipset is $F0–$FF. |
$FFBD | Expansion RAM |
|---|---|
$00 | None. |
$01 | 16 kbit. |
$02 | Undefined (probably 32 kbit). |
$03 | 64 kbit. |
$04 | Undefined (probably 64 kbit). |
$05 | 256 kbit. |
$06 | 512 kbit. |
$07 | 1 Mbit. |
SA-1 cartridges put $00 here and give their RAM size at
$FFD8. Some early games treat $FFBF as valid only when the
last title byte ($FFD4) is $00.
Country codes ($FFD9)
Codes $00 and $01 are NTSC; most others are PAL, though
the SNESdev Wiki notes exceptions. The letter is the one used at the end of the
expanded header’s game code.
| Code | Region | Game code letter |
|---|---|---|
$00 | Japan | J |
$01 | North America | E |
$02 | Europe | P |
$03 | Scandinavia | W |
$04 | Finland | — |
$05 | Denmark | — |
$06 | Europe (French) | F |
$07 | Dutch | H |
$08 | Spanish | S |
$09 | German | D |
$0A | Italian | I |
$0B | Chinese | C |
$0C | Indonesia | — |
$0D | South Korea | K |
$0E | Common | A |
$0F | Canada | N |
$10 | Brazil; also Nintendo Gateway System | B; G |
$11 | Australia | U |
$12 | Other variation | X |
$13 | Other variation | Y |
$14 | Other variation | Z |
Developer IDs other than $33 are not listed in the sources read for this
page.
Checksum
- Set the checksum at
$FFDEto$0000and the complement at$FFDCto$FFFF(the header is included in the sum, and these two values always add up to the same total). - Add every byte of the ROM, keeping 16 bits.
- Store the sum at
$FFDEand the sum XOR$FFFFat$FFDC.
The sum is taken as if the ROM were the power-of-two size the header gives. A ROM of another size is treated as a larger power-of-two part followed by a smaller part that is repeated until it fills the same size as the first: for example a 3 MiB ROM sums as 2 MiB plus the last 1 MiB twice. Emulators disagree about padding in odd cases, so the SNESdev Wiki recommends power-of-two files. A copier header is never included.
Interrupt vectors ($FFE0–$FFFF)
Sixteen 16-bit addresses in bank 0. The 65816 uses the first set in native mode and the second in emulation mode.
| Address | Native mode | Address | Emulation mode |
|---|---|---|---|
$FFE0–$FFE3 | not listed | $FFF0–$FFF3 | not listed |
$FFE4 | COP | $FFF4 | COP |
$FFE6 | BRK | $FFF6 | reserved |
$FFE8 | ABORT (not used by the SNES CPU) | $FFF8 | ABORT (not used) |
$FFEA | NMI | $FFFA | NMI |
$FFEC | reserved | $FFFC | RESET |
$FFEE | IRQ | $FFFE | IRQ and BRK |
Finding the header
Checks the SNESdev Wiki suggests for scoring a candidate location:
- The checksum and its complement add up to
$FFFF. - The map mode matches the location (LoROM at
0x7FC0, HiROM at0xFFC0, ExHiROM at0x40FFC0). - The ROM size given is not smaller than the file.
- The reset vector is
$8000or above. - The first instruction at the reset vector is a likely one (
sei,clc,sec,stz,jmp,jml) rather than an unlikely one (brk,cop,stp,wdm, or$FF). - The ROM and RAM sizes are reasonable.
- The title is ASCII (or JIS X 0201).
Sources
- ROM header – SNESdev Wiki: locations, every field, map modes, chipset and subtypes, expanded header, country codes, checksum and the checks for finding the header
- ROM file formats – SNESdev Wiki: how LoROM, HiROM and ExHiROM lay the ROM out in the file
- CPU vectors – SNESdev Wiki: native and emulation mode vectors
Nintendo’s own documentation was not among the sources read.