SNES ROM layout
Talk0this wiki
The SNES ROM layout describes how the ROM banks appear in a ROM image and in the SNES address space. Most games use either LoROM or HiROM layout; some emulators or ROM tools will tell you whether a ROM image uses LoROM or HiROM. Assemblers and emulators use LoROM or HiROM to map between ROM offsets and SNES addresses.
Contents |
Banks
Edit
The maximum size of a LoROM or HiROM image is 4 MB. All ROM banks within the image have the same size.
- Each LoROM bank is $8000 bytes (32 kilobytes).
- Each HiROM bank is $10000 bytes (64 kilobytes) in HiROM.
- An image contains only LoROM banks or only HiROM banks, not both.
Some tools will try to handle any size ROM (not above the 4 MB limit), but a better way is to have a whole number of ROM banks, and the best way is to match the number of ROM banks with the ROM size byte at address $ffd7 in the SNES header, which would imply that the number of ROM banks is a power of 2. The next table shows the number of ROM banks in common ROM sizes.
| ROM size byte | number of ROM banks | |
|---|---|---|
| $08 (256 kilobytes) | 8 LoROM banks | 4 HiROM banks |
| $09 (512 kilobytes) | 16 LoROM banks | 8 HiROM banks |
| $0a (1 megabyte) | 32 LoROM banks | 16 HiROM banks |
| $0b (2 megabytes) | 64 LoROM banks | 32 HiROM banks |
| $0c (4 megabytes) | 128 LoROM banks | 64 HiROM banks |
LoROM
Edit
LoROM maps each ROM bank into the upper half (being addresses $8000 to $ffff) of each SNES bank, starting with SNES bank $00, and starting again with SNES bank $80.
| offsets in headerless ROM image | SNES addresses First mirror | SNES addresses Second mirror |
|---|---|---|
| x000000 to x007fff | $008000 to $00ffff | $808000 to $80ffff |
| x008000 to x00ffff | $018000 to $01ffff | $818000 to $81ffff |
| x010000 to x017fff | $028000 to $028fff | $828000 to $82ffff |
| x018000 to x018fff | $038000 to $038fff | $838000 to $83ffff |
| ... | ||
| x3e0000 to x3e7fff | $7c8000 to $7cffff | $fc8000 to $fcffff |
| x3e8000 to x3effff | $7d8000 to $7dffff | $fd8000 to $fdffff |
| x3f0000 to x3f7fff | replaced with RAM | $fe8000 to $feffff |
| x3f8000 to x3fffff | replaced with RAM | $ff8000 to $ffffff |
The system RAM always appears in SNES banks $7e and $7f. If you want to access ROM at offsets x3f0000 to x3fffff, then you must use the second mirror at SNES banks $fe and $ff.
RAM in LoROM cartridge
Edit
If the cartridge contains its own RAM or save-RAM, then LoROM will map this RAM at the start of bank $70, and again and the start of bank $f0. The maximum size of the RAM is 32 kilobytes. This does not overlap the mapped ROM.
| offsets in SRM image | SNES addresses First mirror | SNES addresses Second mirror |
|---|---|---|
| x0000 to x7fff | $700000 to $707fff | $f00000 to $f07ffff |
HiROM
Edit
HiROM maps each ROM bank into the whole (being addresses $0000 to $ffff) of each SNES bank, starting with SNES bank $40, and starting again with SNES bank $80.
| offsets in headerless ROM image | SNES addresses First mirror | SNES addresses Second mirror |
|---|---|---|
| x000000 to x00ffff | $400000 to $40ffff | $c00000 to $c0ffff |
| x010000 to x01ffff | $410000 to $41ffff | $c10000 to $c1ffff |
| x020000 to x02ffff | $420000 to $42ffff | $c20000 to $c2ffff |
| x030000 to x03ffff | $430000 to $43ffff | $c30000 to $c3ffff |
| ... | ||
| x3c0000 to x3cffff | $7c0000 to $7cffff | $fc0000 to $fcffff |
| x3d0000 to x3dffff | $7d0000 to $7dffff | $fd0000 to $fdffff |
| x3e0000 to x3effff | replaced with RAM | $fe0000 to $feffff |
| x3f0000 to x3fffff | replaced with RAM | $ff0000 to $ffffff |
The system RAM always appears in SNES banks $7e and $7f. If you want to access ROM at offsets x3e0000 to x3fffff, then you must use the second mirror at SNES banks $fe and $ff.
The upper half of each ROM bank has two more mirrors in HiROM. The upper halves ($8000 to $ffff) of SNES banks $c0 to $ff have mirrors in SNES banks $80 to $bf and again in SNES banks $00 to $3f.
| SNES addresses Second mirror | SNES addresses Third mirror | SNES addresses Fourth mirror |
|---|---|---|
| $c08000 to $c0ffff | $008000 to $00ffff | $808000 to $80ffff |
| $c18000 to $c1ffff | $018000 to $01ffff | $810000 to $81ffff |
| ... | ||
| $fe8000 to $feffff | $3e8000 to $3effff | $be8000 to $beffff |
| $ff8000 to $ffffff | $3f8000 to $3fffff | $bf8000 to $bfffff |
Of the upper-half mirrors, the most important one is $008000 to $00ffff, because the SNES header and the interrupt handlers need to be in SNES bank $00. If you put the interrupt handlers in the upper half of SNES bank $40 or SNES bank $c0, then they will appear in SNES bank $00. The SNES header will appear at $00ffc0, $40ffc0, $80ffc0 and $c0ffc0.
RAM in HiROM cartridge
Edit
If the cartridge contains its own RAM or save-RAM, then HiROM will map this RAM at the start of bank $20, and again and the start of bank $a0. The maximum size of the RAM is 32 kilobytes. This does not overlap the mapped ROM.
| offsets in SRM image | SNES addresses First mirror | SNES addresses Second mirror |
|---|---|---|
| x0000 to x7fff | $200000 to $207fff | $a00000 to $a07ffff |
ExLoROM and ExHiROM
Edit
This wiki does not yet document ExLoROM and ExHiROM, which are layouts to allow ROM size being larger than 4 MB.
ROM status $ffd5
Edit
Address $ffd5 in the SNES header has bit $01 set for HiROM or clear for LoROM. Bit $20 is always set. Thus the value of $ffd5 tends to be $20 for LoROM or $21 for HiROM. Some users refer to LoROM as "mode 20" and to HiROM as "mode 21".
FastROM also sets bit $10, so the value may become $30 for LoROM or $31 for HiROM.
Formulas to convert addresses
Edit
The source code for xkas, in file xkas/libxkas/arch/snes.cpu.cpp, has these C++ formulas to convert between ROM image offsets "fileaddr" and SNES addresses "archaddr". In this code, header is true if and only if the image has SMC header.
unsigned xkasSNESCPU::archaddr(unsigned addr) {
if(header) addr -= 512;
switch(mapper) {
case LoROM: {
addr = ((addr & 0x7f8000) << 1) + 0x8000 + (addr & 0x7fff);
} break;
case HiROM: {
addr = 0xc00000 + (addr & 0x3fffff);
} break;
}
return addr;
}
unsigned xkasSNESCPU::fileaddr(unsigned addr) {
switch(mapper) {
case LoROM: {
addr = ((addr & 0x7f0000) >> 1) + (addr & 0x7fff);
} break;
case HiROM: {
addr = addr & 0x3fffff;
} break;
}
if(header) addr += 512;
return addr;
}
References
Edit
- xkas formulas shown on this page
- memmap.cpp in the snes9x-gtk source code
- snesmem.zip from Zophar's Domain