Cartridges and ROM files intro
Last updated:
What's in a cartridge
An NES game ships on a cartridge: a small circuit board with one or two ROM chips on it, and sometimes a bit of extra hardware.
But emulators do not have access to a physical cartridge, instead what they read is a ROM file (also called a ROM image, or just a dump).
A ROM file is a copy of a cartridge chips' contents written out as one file.
But since cartridges have multiple chips, that means there's no single way to store their contents. E.g. with chips A and B, you could store chip B's contents first, then chip A's.
To standardize this, emulator devs propose different formats to organize this data, and usually one or two become widely popular and de-facto.
One such format is:
iNES
Almost every dump you'll come across is in the iNES format, named
after the early emulator of the same name; the file extension is .nes.
Its successor, NES 2.0, is a backward-compatible extension of the same layout — for the fields we touch here, code that reads iNES reads NES 2.0 too.
The layout is just four parts, in order:
- Header, 16 bytes
- Trainer, optional (512 bytes or 0 bytes, assume 0 bytes for now)
- PRG-ROM (size depends on header)
- CHR-ROM (size depends on header)
Everything after the header is raw chip data with nothing between the sections — no offset table, no markers. You locate a section by adding up the sizes of everything before it, and those sizes come from the header.
The header
The header is the first 16 bytes of the file.
Here's the actual header of Thwaite, a homebrew NES game game by Damian Yerrick, released under GPLv3. I'll use it throughout the book.
byte 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
value 4E 45 53 1A 01 01 01 00 00 00 00 00 00 00 00 00
└────┬────┘ │ │ │ │
"NES\x1A" │ │ └──┴ Flags (we'll see this later)
(magic) │ └──── CHR-ROM size: 1 × 8 KiB
└─────── PRG-ROM size: 1 × 16 KiB
The bytes that matter, read against Thwaite's values:
Bytes 0–3 — the magic number NES\x1A. If it's not there, the file isn't
iNES; stop parsing.
Thwaite: 4E 45 53 1A
Byte 4 — PRG-ROM size in 16 KiB units. You don't run this code to draw
graphics, but you need its size to know where CHR-ROM begins.
Thwaite: 01 → 1 × 16 KiB
Byte 5 — CHR-ROM size in 8 KiB units. 0 means the cartridge uses CHR-RAM
and has no tile data in the file.
Thwaite: 01 → 1 × 8 KiB
Byte 6 and 7 — Flags that will explained later on, when they are necessary for progress.
Finding the CHR-ROM of a ROM... and why?
Knowing the address and length of CHR-ROM is central to the following chapter.
CHR-ROM starts right after PRG-ROM. Its start and length both come from the header:
chr_start = 16 + (trainer ? 512 : 0) + prg_16k_units * 16384
chr_len = chr_8k_units * 8192
Plugging in Thwaite — no trainer, one 16 KiB PRG bank, one 8 KiB CHR bank:
chr_start = 16 + 0 + 1 * 16384 = 16400 ($4010)
chr_len = 1 * 8192 = 8192 ($2000)
So CHR-ROM spans from $4010 to $600F.
That 8 KiB slice is the pattern-table data — the input to the next chapter, where we decode it and put it on screen.
Cart and .nes file
Both terms are used interchangeably.