# Nes Format

> iNES, NES 2.0, and UNIF ROM format reference. Use when reading, writing, parsing, or debugging .nes/.unf ROM files, or handling NES ROM headers.

- Skill: `yuki001/nes-format` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add yuki001/nes-format`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yuki001/nes-format/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Yuki001 (https://skillmd.com/u/yuki001)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/yuki001/nes-format

---


# NES ROM Format Reference

Three ROM formats exist: **iNES**, **NES 2.0**, and **UNIF**. NES 2.0 is the current standard and recommended for new dumps. UNIF is deprecated.

## Format Detection Quick Guide

| Signature in file | Format |
|-------------------|--------|
| `NES` + `$1A` + byte 7 bits 3-2 = `10` | NES 2.0 |
| `NES` + `$1A` + byte 7 bits 3-2 ≠ `10` | iNES |
| `UNIF` | UNIF |

---

## iNES Format (.nes)

Created by Marat Fayzullin for the iNES emulator. Uses an 8-bit mapper number (0–255). Bytes 8–15 are loosely defined and vary between iNES revisions. NES 2.0 repurposes them with a formal specification.

### File Layout
```
[16-byte Header] [optional 512-byte Trainer] [PRG-ROM data] [CHR-ROM data]
```

Some ROM images also have a 128-byte title at the end of the file.

### Header Byte Map

| Byte | Field |
|------|-------|
| 0–3 | `$4E $45 $53 $1A` — "NES" + MS-DOS EOF |
| 4 | PRG-ROM size in 16KB units |
| 5 | CHR-ROM size in 8KB units (0 = CHR-RAM used) |
| 6 | Flags 6: `NNNN FTBM` |
| 7 | Flags 7: `NNNN xxTT` |
| 8 | PRG-RAM size in 8KB units (0 infers 8KB for compatibility) |
| 9 | TV system: bit 0 = 1 if PAL |
| 10 | Unofficial extension: TV system, PRG-RAM presence, bus conflicts |
| 11–15 | Padding (should be zero, but some tools wrote identifying strings here) |

### Byte 6 Flags (`NNNN FTBM`)

| Bit | Name | Meaning |
|-----|------|---------|
| 0 | M | Nametable arrangement: 0 = vertical mirroring, 1 = horizontal mirroring |
| 1 | B | Battery-backed PRG-RAM at $6000–$7FFF (or other persistent memory) |
| 2 | T | Trainer present between header and PRG-ROM (512 bytes at $7000–$71FF) |
| 3 | F | Alternative nametable layout (mapper-dependent, often 4-screen) |
| 7–4 | N | Mapper number low nibble (D3–D0) |

**Mirroring notes:**
- Mappers that control mirroring themselves (MMC1, MMC3, AxROM) ignore bit 0.
- Bit 3 does not always mean 4-screen — some mappers use different combinations (e.g., UNROM 512: `%....1..0` = 1-screen, `%....1..1` = 4-screen).
- Mappers with hard-wired 4-screen (Napoleon Senki, Vs. System, GTROM) may have bit 3 redundantly set.

**Trainer:** 512 bytes loaded at $7000–$71FF. Holds mapper translation / CHR-RAM caching code for early RAM cartridges and Nesticle. Not present on unmodified dumps of original cartridges.

### Byte 7 Flags (`NNNN xxTT`)

| Bit | Name | Meaning |
|-----|------|---------|
| 1–0 | T | Console type: 0 = NES/Famicom, 1 = Vs. System, 2 = PlayChoice-10 |
| 3–2 | x | NES 2.0 identifier: must be `10` (2) for NES 2.0 |
| 7–4 | N | Mapper number high nibble (D7–D4) |

**Mapper number** = (byte 7 bits 7–4) << 4 | (byte 6 bits 7–4) → 0–255 for iNES.

PlayChoice-10 games have 8KB of Hint Screen data stored after CHR data. Vs. games have a coin slot and different palettes.

See `references/mappers.md` for the full mapper reference with detailed specifications.

### Byte 10 (Unofficial Extension)

Not part of the original spec; few emulators honor it.

| Bits | Field |
|------|-------|
| 1–0 | TV system: 0 = NTSC, 2 = PAL, 1/3 = dual compatible |
| 5–4 | PRG-RAM: 0 = present, 1 = absent |
| 6 | Bus conflicts: 0 = none, 1 = has bus conflicts |

### Bytes 11–15

Should be zero. If the last 4 bytes are not all zero and this is not NES 2.0, an emulator should mask off the upper 4 bits of the mapper number or refuse to load. Some tools wrote identifying strings here (e.g., "DiskDude!").

### iNES Revision Detection

1. If byte 7 AND $0C = $08 → **NES 2.0**
2. If byte 7 AND $0C = $04 → **Archaic iNES** (no mapper high nibble)
3. If byte 7 AND $0C = $00, and bytes 12–15 are all zero → **iNES**
4. Otherwise → **iNES 0.7** or **Archaic iNES** (treat with caution)

---

## NES 2.0 Format (.nes)

Identified by `(byte 7 & $0C) == $08`. Backward-compatible with iNES for bytes 0–7. Extends the mapper number to 12 bits (0–4095), adds a submapper for disambiguating board variants, and formally specifies RAM/NVRAM sizing.

### File Layout
```
[16-byte Header] [optional Trainer] [PRG-ROM] [CHR-ROM] [optional Misc ROMs]
```

### Byte 8 — Mapper MSB / Submapper

| Bits | Field |
|------|-------|
| 3–0 | Mapper number D11–D8 (extends to 12 bits total, 0–4095) |
| 7–4 | Submapper number (0–15, disambiguates board/chip variants without relying on checksums) |

See `references/mappers.md` for the full mapper reference with detailed specifications.

### Byte 9 — ROM Size MSB

| Bits | Field |
|------|-------|
| 3–0 | PRG-ROM size MSB (together with byte 4 → 12-bit PRG size) |
| 7–4 | CHR-ROM size MSB (together with byte 5 → 12-bit CHR size) |

**Simple mode** (MSB nibble $0–$E): PRG = value × 16KB, CHR = value × 8KB. Max: ~63 MB PRG, ~31 MB CHR.

**Exponent-multiplier mode** (MSB nibble = $F): Used when simple notation cannot represent the exact size. Header bits are reinterpreted:
- Exponent E = bits D9–D4 (0–63)
- Multiplier MM = bits D1–D0, actual multiplier = MM×2+1 (1, 3, 5, or 7)
- Size = 2^E × (MM×2+1) bytes

If actual ROM size is odd and unrepresentable, pad to a representable size.

For Vs. Dual System: first half of PRG belongs to unit 1, second half to unit 2. Known splits exist for Vs. Gumshoe and Vs. Raid on Bungeling Bay.

### Byte 10 — PRG-RAM / NVRAM Size

| Bits | Field |
|------|-------|
| 4–0 | PRG-RAM (volatile) shift count |
| 7–5 | PRG-NVRAM/EEPROM (non-volatile) shift count |

Size formula: `64 << shift_count` bytes. Shift count 0 = absent (compatibility: 0 infers no RAM).

**Battery bit rule:** If the upper nibble (NVRAM) is non-zero, byte 6 bit 1 (B) must be set. The reverse also applies unless the only battery-backed memory is mapper-internal or PRG-ROM is self-flashable.

Excludes: mapper-internal memory not CPU-mapped, MMC5 EXRAM, self-flashable PRG-ROM, external storage (cassette, Turbo File).

### Byte 11 — CHR-RAM / CHR-NVRAM Size

| Bits | Field |
|------|-------|
| 4–0 | CHR-RAM (volatile) shift count |
| 7–5 | CHR-NVRAM (non-volatile) shift count |

Same encoding as byte 10. **Critical:** emulators must not assume 8KB CHR-RAM when CHR-ROM is absent — all CHR-RAM must be explicitly specified. Nametable memory (PPU $2000–$2FFF) is excluded from this count even if 4-screen bit is set.

### Byte 12 — CPU/PPU Timing

Two low bits:

| Value | Name | Meaning |
|-------|------|---------|
| 0 | RP2C02 | NTSC NES |
| 1 | RP2C07 | Licensed PAL NES |
| 2 | Multiple region | Self-adjusting to both NTSC and PAL |
| 3 | UA6538 | Dendy (PAL variant, 50.0063 Hz) |

**Convention for assigning the correct value:**
- Licensed games: 0 for JP/NA releases, 1 for EU/AU/OC/HK, 2 when the same PRG-ROM shipped in both regions, never 3.
- Unlicensed/homebrew: author designation first; 2 for self-adjusting; 3 for Chinese releases (Waixing etc.); default to 0.
- VTxx Famiclones only support RP2C02 or UA6538 → use 0, 2, or 3.

### Byte 13 — Vs. System / Extended Console Type

Interpretation depends on byte 7 bits 1–0 (Console Type):

**When Console Type = 1 (Vs. System):**

| Bits | Field |
|------|-------|
| 3–0 | Vs. PPU variant (RP2C03-xxxx, RP2C04-0001 through -0004, RC2C05-01 through -04) |
| 7–4 | Vs. Hardware type: 0=Normal, 1=RBI Baseball, 2=TKO Boxing, 3=Super Xevious, 4=Ice Climber, 5=Dual System Normal, 6=Dual System Raid on Bungeling Bay |

RP2C04/RC2C05 PPUs use different palettes for copy protection. RC2C05 swaps $2000/$2001 and returns a signature in $2002.

**When Console Type = 3 (Extended Console):**

| Value | Console |
|-------|---------|
| 0 | Regular NES/Famicom/Dendy |
| 1 | Nintendo Vs. System |
| 2 | PlayChoice-10 |
| 3 | Famiclone with Decimal Mode CPU |
| 4 | NES/Famicom with EPSM module |
| 5 | VT01 (red/cyan STN palette) |
| 6 | VT02 |
| 7 | VT03 |
| 8 | VT09 |
| 9 | VT32 |
| A | VT369 |
| B | UMC UM6578 |
| C | Famicom Network System |

### Byte 14 — Miscellaneous ROMs

Bits 1–0: count of extra ROMs after CHR-ROM (0–3). Defined uses:
- PlayChoice-10: INST-ROM (8KB) + PROM data (16 bytes) + PROM counter out (16 bytes) = 3 misc ROMs
- VT369: 4KB embedded ROM
- Mapper 086 submapper 1: speech data ROM
- Mapper 355: PIC16C54 microcontroller ROM for copy protection
- Mappers 561/562: non-standard trainers (different size/location than $7000)

### Byte 15 — Default Expansion Device

7-bit ID for the expected peripheral at $4016/$4017. $00 = unspecified, $01 = standard controllers. This typically denotes *the* device required to play the game; for optional devices it lists the device if having it connected doesn't preclude normal controllers. Does not cover cartridge-connected devices (those belong to the mapper definition).

See `references/device-ids.md` for the complete 80-entry device ID table.

---

## UNIF Format (.unf / .unif)

UNIF uses chunked TLV (Type–Length–Value) blocks, allowing flexible ordering. **Deprecated** — its originator abandoned the format in December 2008. NES 2.0 is recommended for new work. A few game rips still only exist as UNIF.

### File Layout
```
[32-byte Header] [Chunk] [Chunk] ...
```

**Header:**

| Offset | Size | Value |
|--------|------|-------|
| 0 | 4 | `UNIF` (literal ASCII) |
| 4 | 4 | LE32 — minimum version needed to parse all chunks |
| 8 | 24 | Null bytes |

**Each chunk:**

| Offset | Size | Value |
|--------|------|-------|
| 0 | 4 | Type ID (4 ASCII characters) |
| 4 | 4 | LE32 — content length |
| 8 | N | Content (encoding depends on type) |

### Required Chunk

| Type | Content | Notes |
|------|---------|-------|
| `MAPR` | Null-terminated UTF-8 | PCB identifier (e.g., "NES-SNROM", "HVC-UNROM"). Not a mapper number, not a free-form description. |

### ROM Data Chunks

| Type | Content | Min Ver |
|------|---------|---------|
| `PRG`_n_ | Raw binary, n-th PRG-ROM chip (_n_ in hex, 0-indexed) | 4 |
| `CHR`_n_ | Raw binary, n-th CHR-ROM chip | 4 |
| `PCK`_n_ | LE32 CRC-32 of n-th PRG-ROM | 5 |
| `CCK`_n_ | LE32 CRC-32 of n-th CHR-ROM | 5 |

### Optional Metadata Chunks

| Type | Content | Notes |
|------|---------|-------|
| `NAME` | Null-terminated UTF-8 | Game name |
| `READ` | Null-terminated UTF-8 | Comments / license info |
| `DINF` | 204-byte struct | Dumper name (100 bytes), date (day/month/year), tool name (100 bytes) |
| `BATR` | 1-byte boolean | Battery-backed RAM present (doesn't disambiguate which RAM) |
| `MIRR` | 1 byte | Mirroring: 0=horiz, 1=vert, 2=1scrA, 3=1scrB, 4=4scr, 5=mapper-controlled |
| `TVCI` | 1 byte | TV: 0=NTSC only, 1=PAL only, 2=compatible with both |
| `CTRL` | 1-byte bitmask | Controllers: 1=standard, 2=Zapper, 4=R.O.B., 8=Arkanoid, 16=Power Pad, 32=Four Score |
| `VROR` | 1 byte | Treat CHR-ROM as RAM (deprecated, ignored by most emulators) |

### Key UNIF Shortcomings

- **No PRG-RAM field** — two VRC4 games on identical PCBs can differ in PRG-RAM
- **Unordered chunks** make patching impossible without unpacking to an intermediate form
- **MAPR abuse** — users pick already-supported PCB names rather than creating accurate new ones for emulator compatibility
- **BATR ambiguity** — doesn't specify which RAM is battery-backed when multiple are present
- **CTRL limitations** — no port assignment, no Famicom-specific peripherals, no SNES controller via adapter, no mouse
- **No support** for fully describing Vs. System or PlayChoice-10 hardware
- **ENCODING** — before 2013, only 7-bit ASCII was assumed; UTF-8 adopted Q1 2013

