NSF disassembly help

Discuss NSF files, FamiTracker, MML tools, or anything else related to NES music.

Moderator: Moderators

Post Reply
Posts: 3
Joined: Sat Dec 01, 2018 10:45 am

NSF disassembly help

Post by StarCaptain524 » Sat Dec 01, 2018 11:11 am

So I'm trying to create a program to turn the 'music data' in the NSF file into the human-readable assembly language, but I don't understand how to interpret the actually data. I've tried linking the data after the 128th byte to this table based off the hex value of the operation codes, but that didn't seem to work as most of the bytes in the file did not have corresponding opcodes. Example: I'm using Castlevania.nsf for testing. The very first byte is value 135, which does not have any opcodes associated with it. The file does work when I put it into an NSF player, however.

So what am I doing wrong? How do I disassemble the code of a NSF back into assembly?

Posts: 9384
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: NSF disassembly help

Post by lidnariq » Sat Dec 01, 2018 1:06 pm

NSFs have a set of addresses in them (START / PLAY / LOAD) that you have to use when disassembling an NSF.

User avatar
Posts: 11692
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: NSF disassembly help

Post by tokumaru » Sat Dec 01, 2018 1:39 pm

Binary programs, as seen in .nes and .nsf files, are a mix of instructions and data. Instructions in a sound engine do things like keep track of time, start and stop notes, update the console's audio registers with the current frequencies, volumes and waveforms, etc., based on the DATA that describes the songs. Different sound engines work with data in different formats, but things like note sequences, durations, instruments, etc. must be defined somehow.

If you try to interpret data as if it was code, the output will make no sense, because those bytes do not represent instructions, they describe properties of the songs in various ways. The NSF header specifies which addresses are guaranteed to contain code, which are the START and PLAY addresses, and those are the places where you should start disassembling from (after correctly mapping the binary into the LOAD address).

But even then there are ramifications that can only be found when the program is run. For example, if the code puts an address in RAM and does an indirect jump to that address (i.e. JMP ($XXXX)), there's no way to know the target of that jump during static disassembly, you'd have to trace the program as it runs in order to catch all the instructions that are executed.

Post Reply