"Unusual" pitch lookup table organization in older titles

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
"Unusual" pitch lookup table organization in older titles
by on (#211021)
Recently I've been going through a bunch of NSF files from games contemporary to the Famicom/NES's lifespan and digging up their pitch tables—usually by opening the file open in a hex editor and searching for bytes or pairs of bytes that tend to come up (e.g. "00 FD" or "00 FE" for A440Hz). I'm interested in seeing what sorts of discrepancies exist among developers, or even in the music of the same developer, in the early days of the system.

After looking over doppleganger's disassembly of SMB, I was able to find the lookup table in the NSF and make some edits (changing a few notes to make the music play back in C minor instead of C major) to confirm that I was looking at the right thing. I've since been able to do the same with several other files; there have been some differences in ordering of pitches, and some developers prefer putting the most significant byte second, rather than first (David Wise, for example), but nothing too taxing to figure out. I haven't seen any examples in older titles that put all of the most significant bytes in one group and all of the least significant bytes in another, but I'm aware of that possibility.

Then I started looking at Donkey Kong 3, and I couldn't make heads or tales of it. I'd play the NSF back in NSFPlay and see everything playing back correctly—"Oh, there's the pitch 00 FE—440.4 Hz, and there it is showing up at the proper place in address space in 'Memory' mode"—but a search through the hex file didn't yield any instances of "FE" that seemed to correlate to the structures of pitch tables I had seen elsewhere. Some bytes associated with certain pitches weren't even showing up at all!

Given my *very* limited knowledge of 6502 assembly (knowledge of a few opcodes and a rudimentary understanding of program flow), in addition to some web searches I've done (which led me back to these forums for the first time in years), I'm thinking that one of a few things could be happening:

*The developers are not using a "complete" pitch table; they are only including part of an octave and doing bit-shifting or some such to calculate the other pitches
*The bit order of various pitches may be reversed in some playback engines
*There's some other means for calculating pitch that I haven't accounted for yet

Nuts and Milk and Championship Lode Runner have posed the same issues; strangely, Lode Runner uses a more "conventional" lookup table, whereas Championship Lode Runner uses some other structure (and different frequencies for the pitches themselves—00 FE, rather than 00 FD, for the A above middle C) despite being almost the exact same game.

My question, then, is this: how should I go about my search for lookup tables (or alternate means of pitch calculation) that differ from the "norm" I've seen in other NSFs so far? I know I could just open a file in NSFPlay and transcribe the hex values of pitches as they appear, but I'm interested specifically in how these things are laid out in code, and not just in the pitches themselves. I was just reading the FCEUX help files and came across the section about setting breakpoints... forgive me if I sound like an idiot for asking this, but if I run the original ROM for a game in FCEUX, set a breakpoint to watch for writes to addresses $4002/3 and $4006/7, analyze the surrounding code, and compare this to the analogous code in the NSF (which is basically just the audio portion of the original ROM data with a header at the beginning), am I likely to turn up anything useful?
Re: "Unusual" pitch lookup table organization in older title
by on (#211023)
Yeah, I've seen a few cases where they do something like store 1 octave of 12 pitches and the other octaves are bit shifted down from that one. (Not common, I think because rounding precision issues are annoying to deal with.)

I've also seen striped tables, like where the high bytes all go in one table and the low bytes go in their own separate table.

Yes, you can use FCEUX's debugger to find out where pitches come from. You can put a breakpoint on writes to $4002/3, but once you find it you need to work backwards. Like if it's a STA $4002 you need to find out where the value in A came from. Generally, I start by logging a trace to file (there's a trace logger in the menu). This trace will write down every single instruction as if you were stepping through with the debugger. After the trace has started, you can run until you hit that breakpoint.

At that point you can stop logging the trace, and start at the bottom. By scanning upwards you can rewind time instruction by instruction until you figure out where the values came from. Eventually you'll find the instructions that fetched a pitch from the table, and that will let you know where those tables are. (Trace logs get big fast, BTW. It can help if you advance frame until just before a note, then start the trace there, so you only do like 1 or 2 frames before the breakpoint.)
Re: "Unusual" pitch lookup table organization in older title
by on (#211036)
rainwarrior wrote:
Yeah, I've seen a few cases where they do something like store 1 octave of 12 pitches and the other octaves are bit shifted down from that one. (Not common, I think because rounding precision issues are annoying to deal with.)

NSD.lib stores 1 octave of 96 pitches spaced 12.5 cents apart. I guess it simplifies calculating linear-pitch portamento and vibrato because bit shifting for an octave is faster than multiplying the "cents" value by the difference between two adjacent pitches.

rainwarrior wrote:
I've also seen striped tables, like where the high bytes all go in one table and the low bytes go in their own separate table.

Pently uses this structure. (This driver is used in Concentration Room, Thwaite, Double Action Blaster Guys, Sliding Blaster, RHDE: Furniture Fight, the Action 53 menu, and the VGBS volume 1 cart.) So does anything else using the period table generator that I developed for Pently.