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.
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 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.
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.
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 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.
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.
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.
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.
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.
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.
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.
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.
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.