| Level 0 | Level 1 | Level 2 | Level 3 | Level 4 |
| Level 5 | Level 6 | Level 7 | Level 8 | Level 9 |
| Level 10 | Level 11 | Level 12 | Level 13 | Level 14 |
| Level 15 | Level 16 | Level 17 | Level 18 | Level 19 |
A good deal of Level 14 can be used at any stage of the NSF ripper's current skill level. However, some things that I will show you in this level will require a bit of effort to make an NSF rip clean and as compatible with many different players and emulators as possible. You can use this level with your own rips and/or to repair other older rips or ones that are not quite complete or compatible with other players.
Making sure that a NSF header is clean and of precise size can indeed make the difference for a properly ripped NSF. As far as the size of the header is concerned, it's 128 bytes or 7Fh in hexidecimal, so the range of a NSF header is 0h - 7F in a hex editor. The data of the NSF starts at 80h, and if the header is not of proper size, the address space is thrown off by how many bytes the header is off or over sized at.
The next thing to check is that the NESM, 1A is in the right place in the NSF header, use the specs for reference. Also, the NSF version number must be correct, the NSF I'm looking at now states it's 01. The starting tune must always start at 1, the number of total tunes should also be correct and at the right Offset in the header. The next six bytes are the Load, Init and Play address calls respectively, you should check to make sure these are correct, they make the difference between a playing and non-playing NSF commonly.
Sometimes you'll notice that a rip plays a bit too fast. In this case it's a wise idea to check the header at location 7Ah. This is the byte that determines if the rip is NTSC or PAL, or even dual NTSC/PAL. 00 is NTSC, 01 is PAL, 02 is dual NTSC/PAL. It is important to make sure that 7B is set appropiately depending on what type of rip this is. Games that are designated as (E) are almost always PAL. Some Chinese pirate games are PAL or SECAM, SECAM from what I understand is a format designed by the French or in use by the French that is near NTSC speeds, so SECAM can use NTSC setting, the speed can also be adjusted.
Depending on what type of rip the game is in regards to NTSC or PAL, you can adjust the PB (Play Back) speed in the header. By aware that some players ignore this setting. The setting should be 411A for 60HZ PB, for NTSC tunes, use the formula in the NSF spec to calculate a different HZ playback speeds.
Now it's time for Extra Sound Chip Support. If you have a normal NSF that does not use expansion sound, 7Bh in the header should always be 00. According to the NSF spec, depending on which bit is set in the byte that is at 7Bh, that determines what expansion sound chip is in use for the rip. To my knowledge, there are no commercial games that have used more than one expansion chip, that being said, only one sound chip will be used per rip and the header should reflect this. There are some Homebrew NSFs out there that use two or more expansion sound chips, the header is set appropriately.
Now we will deal with 70h - 77h, which are the bankswitching bytes in the header. The bytes in this range should always be all 00 if we are dealing with a non-bankswitched tune or a rip that is not using paging optimization. In a continuous NSF rip, starting at the load address, the 4K banks shall be loaded sequencially and therefore would require no bankswitching. If the rip uses bankswitching, then you would have to debug the rip and determine what banks are used and how they are loaded. You use the bankswitching bytes to set up the address space and then you write code that switches the banks based on the tune that you're playing. Currently this is out of the scope of this level but is mentioned so that 70h - 77h should be clean and set properly.
Last but not least is the name of the game, composer, company credit fields. These must be included in the rip and when one of these fields is not known, < ? > must be filled in this field. Also, I might mention that you have a limit as to how many characters that you can use for each of the three information fields, the last byte must always be null terminate or 00, cannot be over-written.
As I had stated earlier, having a clean and properly set header can make the difference in a properly ripped NSF.
The data section of the NSF should be clean and isolated from the rest of the game play code and data. Given that statement, the following should be known for having a clean rip. A NSF does not use the standard vectored interrupt table of the NES/6502 CPU, RTI should be changed to RTS should most of the init and/or play code be contained within any of the interrupts, or rewritten to properly isolate the sound code.
An NSF has the following illegal address ranges that must be changed and/or removed in order to conform to the spec and for proper playback. Commonly these illegal address ranges are write only, in some cases even reading can be considered illegal. For example, reading any of the graphical display registers is considered illegal.
All data and code that is associated with the illegal address ranges should be changed or isolated for a proper rip, that is the main point of this section. Another example, if you have data in the sound driver that is being written and/or read in the $0800 - $5FFF range, these addresses must be changed to somewhere within the legal WRAM area, maybe to an addresses unmirrored counterpart, perhaps?
One big problem with many NSFs that don't play in all players and emulators, is timing out. When an NSF times out, this means that somewhere the NSF is stuck in a loop or in a bunch of data and does not return, nor is the operation halted as it's supposed to, for example in the init code.
I have listed a few reasons why an NSF could time out. Not to worry, usually there is a solution if you debug the rip. Here are the possible causes of timing out and not returning. Stack underflow or overflow, maybe the possibility of missing a PHA or a PLA, these two opcodes work in pairs commonly. If you are missing one of these opcodes in the code somewhere, add one where you think one is missing and see if this solves the problem. When you return from a routine, then you wind up in an address area that you had not intended which strands you out in the middle of no where in the middle of some data that continues on for seemingly forever. That is another possible cause of missing one of the PHA/PLA pair.
I have also ran into a few games where the game was not coded correctly, you would have for example JSR $xxxx, random data after the JSR. After the call was executed, of course you could possibly return and when you do, you run into a big mess. This is solved by remapping the call to some freespace and placing an RTS at the end in most cases. If the data is irrelevant to the sound driver, then you could place a RTS right after the JSR $xxxx operation.
Last but not least of this timing out problem is the infinite loop. You can change the code and NOP the branch out or add an RTS, this solves the problem but not always. Another solution is to limit the number of times that a game would loop. I sometimes do it by using a compare value, the compare value will be how many times the loop will be executed. The loop using the compare value will set a flag once the value is equal or greater. A branch afterwards will not branch and so you keep the code from going into an infinite loop. Try the NSF again to see if it times out.
CMP #$10 BCC cmp_10
This is the basics of the code and perhaps you may have to design it to be a little more complex to mesh with the code. There are other ways of dealing with infinite loop problems, it will be your job to figure this out. Also, you can use SlickNSF to log an NSF. If the log is too large to open in NotePad, I recommend PFE (Programmer's File Editor).
When ripping an NSF, I often check "Don't wait for Play Return" in Config 3 in NotSoFatSo. This way you have a better chance of finding the init/play address, you can clean up the routine after you find it. Make sure that you don't forget to uncheck that box to find out if the NSF will play, when it does play you'll know that your NSF plays without timing out.
You have other boxes and they are the following.
The original purpose of this config was to provide some compatibility with many NSF rips, mostly the older rips. However, I use it to trouble shoot NSFs. If I need to check any of those boxes to make an NSF work, then I need to debug the rip and find out what is wrong. Sometimes a re-rip is in order in more extreme cases if an NSF does not work properly.
There is the possibility that an NSF is missing some songs. Sometimes correcting the LUT (Look Up Table) will help add these missing tunes. Sometimes a bank is missing that contains the data needed to play the tunes missing. It is your job to figure out why the tunes are missing. If the NSF is missing some tunes, indicate the NSF as incomplete, later on the possibility of someone adding the tunes may decide to repair the NSF.
Last but not least is the frame sequencer. The frame sequencer often makes a big difference when activated by writing to $4017. Sometimes you'll notice a big difference when you force a write in NotSoFatSo's Config #3, or when writing some additional code in your init code. Many times $4017 is written to in a game to time the music in the game to a certain speed. You can read up on this register in a few documents about sound and NES architecture. In games where the sound driver does not have a write to $4017, I include in my init code. You can write 00h or 80h.
LDA #$80 STA $4017