ACIDCAT . FILE FORMAT REFERENCE

NSF Anatomy

Nintendo NES / Famicom2A03 APU . 6502
rev 2026.08
magic NESM 1A
endian little
header 128 bytes
payload 6502 code

An .nsf does not describe music. It carries the original 6502 program that produced it, lifted out of a cartridge, behind a 128-byte header saying where to put that code and which two addresses to call. Playing one means running it. The header is the only part a reader can parse; everything after 0x80 is a ROM image whose meaning belongs to the console.

lineage

The format's author put it plainly: NSF is “somewhat sorta based on the PSID file format for C64 music/sound”. The inheritance shows in the shape -- a fixed header in front of a raw machine-code image, an init entry point and a play entry point, a subsong index -- and in small conventions like the literal <?> for an unknown author, which NSF, SAP and PSID all share.

the sound hardware

The console's own audio lives inside the 2A03, the chip that is also the CPU: a 6502 with decimal mode disabled and an audio unit bolted alongside. It offers five voices -- two pulse channels with four duty cycles each, one triangle fixed at 4-bit and stepped through 32 values, one noise channel driven by a shift register with two tap modes, and a DMC channel playing 7-bit delta-modulated samples. That is the whole palette every stock NSF is written for.

Everything in the expansion byte below is a cartridge adding a sixth voice and upward. That was a Famicom capability rather than an NES one: the Famicom's cartridge connector carries an audio-in pin that mixes cartridge sound back into the console's output, and the western NES cartridge slot does not route it. Expansion audio therefore plays on a Famicom, on an emulator, or on a modified NES -- which is why so much of the music below was never heard outside Japan on real hardware.

the header — identification and entry points

Offsets 0x00 through 0x0D. Every multi-byte value in an NSF header is little-endian, matching the 6502 it describes. This is worth stating only because its sibling formats do not: a SID header is big-endian on a little-endian target.

▸the version byte0x05 . 1 byte

Two values have ever been defined: 1 for NSF and 2 for NSF2. There is no 1.01, 1.02 or 1.03. Those numbers belong to the revision history of the specification document, which was edited seven times between 1999 and 2000 while the header layout never changed; and to PSID, which does have a genuine version ladder with fields reserved in v1 and given meaning later.

A version byte of 3 or higher is therefore not a newer file. It is damage, or a different format that happened to match five bytes.

▸songs are 1-based here and 0-based in NSFe0x06 . 0x07

songs is a count: 1 means one song. startSong is an index into it, also 1-based, so a valid file satisfies 1 <= startSong <= songs.

NSFe stores the same two numbers in its INFO chunk and makes the starting song zero-based. Converting between the two containers without adjusting is the most common off-by-one in tooling that reads both.

the tail — timing, banks, hardware

Offsets 0x6E through 0x7F. The two speed words are microseconds between calls to the play routine, not a frequency: 16666 is 60 Hz and 20000 is 50 Hz. A player uses the word matching the region it is emulating and ignores the other.

the load address is not always an address

This is the header's one real trap. When bankswitching is in use, the low twelve bits of loadAddress stop being a location and become a count of padding bytes at the start of the ROM image. load & 0x0FFF is that count.

Nothing flags the change. There is no bit anywhere saying “banked”: the signal is the eight bank bytes at 0x70 being not all zero. A file whose bank array is entirely zero is unbanked and its load address means what it says.

▸the bank registers0x70 . 8 bytes

Byte 0x70 + i is the initial value written to bank register 0x5FF8 + i. The eight registers window 4 KB pages into 0x8000-0x8FFF through 0xF000-0xFFFF in order, so the array is a map of the address space at the moment the tune starts.

FDS files are the exception. Two further registers at 0x5FF6 and 0x5FF7 map 0x6000-0x6FFF and 0x7000-0x7FFF, and header bytes 0x76 and 0x77 initialise both those and the top of the address space. The same bank appears in two places, so a bank-to-address diagram drawn without that exception is wrong for every FDS rip.

the region byte
the expansion byte

Each bit claims one cartridge sound chip, on top of the five stock voices. More than one bit may be set: no cartridge ever carried two of these, but the container allows it and files built to exercise emulators do it deliberately.

the three text fields

Title, artist and copyright each occupy a fixed 32-byte slot at 0x0E, 0x2E and 0x4E. The text inside is NUL-terminated, so at most 31 characters, but the slot is always 32 bytes wide regardless.

Both halves of that sentence matter. Treating the field as fixed-width alone loses the terminator that separates text from padding; treating it as NUL-terminated alone lets a malformed slot with 32 non-NUL bytes run the title straight into the artist.

▸what the bytes after the terminator meannothing, and that is useful

Padding after the NUL is unspecified. It is usually zero, but nothing requires it, and non-zero bytes there are frequently the remains of a longer string a tool wrote earlier. That residue is not damage and does not affect playback.

No byte anywhere declares the encoding. The fields are nominally ASCII, but files carrying Japanese titles in Shift-JIS exist and so, more rarely, do CP-1252 ones. There is no way to tell from the file which you are holding. NSF2 and NSFe both recommend UTF-8 for new files, which does not help with old ones.

Three fields all reading <?> is a convention meaning unknown, not corruption.

NSF2 — the reserved bytes given meaning

The original specification ended the header with “4 extra bytes for expansion (must be 00h)” at 0x7C. Those four were later split: one byte of feature flags, and a 24-bit length.

This was done without a version bump, and safely, because the rule is explicit: if the version byte is 1, the flags at 0x7C must be ignored. The length at 0x7D is the interesting half -- a version-1 file may legitimately carry a non-zero length there to mark where its program data ends and appended metadata begins. Old players load that metadata as part of the ROM and are unharmed by it.

the appended metadata

When dataLength is non-zero, NSFe chunks may follow the program data at 0x80 + dataLength. Two differences from a standalone NSFe: there is no NSFE signature -- the first bytes are the first chunk's length -- and the INFO, DATA, BANK and NSF2 chunks must not appear, because that information is already in the header above.

Bit 7 of the NSF2 flags is the one that changes a reader's obligations: it says the trailer may contain a chunk that is required for correct playback. A player that cannot parse the trailer cannot claim to have understood the file.

NSF file layout 0x00 header 128 bytes, little-endian 0x00 magic "NESM" 0x1A 0x05 version 1 or 2, nothing else 0x06 songs / start 1-based count, 1-based index 0x08 load / init / play 0x0E title / artist / copyright 3 x 32-byte slots 0x6E NTSC speed microseconds per play call 0x70 bank init 8 bytes; all zero means unbanked 0x78 PAL speed 0x7A region bit0 PAL, bit1 dual 0x7B expansion one bit per cartridge sound chip 0x7C NSF2 flags reserved when version is 1 0x7D data length 24-bit; 0 means "to end of file" 0x80 program 6502 code and its data ... metadata NSFe chunks, when data length is set