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?
NSFs have a set of addresses in them (START / PLAY / LOAD) that you have to use when disassembling an NSF.
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.