The main idea is to use a mapper that supports timed IRQs that are fired at a programmable arbitrary rate. I think VRC mappers, and the FME-7 supports this, and the Famicom Disk System. Perhaps other more obscure mappers as well, I don't know. The rate can be anything really.
Now, if we can generate IRQs periodically, it's possible to write to $4011 periodically without monopolizing the CPU for this task alone, and thus get an extra sound channel, that doesn't sound terrible like DMC does. The problem is that doing it the "trivial" way is just copying data from ROM to $4011, but it wastes tremendous amount of ROM to replay samples, so this is not practical for a NES game - only for tech demoes.
Another idea would be to synthesize sound, but this requires CPU power, and the NES bascially doesn't have that. But I think I found a way to synthesize sound using very low CPU. The idea is to generate sine waves, and take values from a sine table which is stored in 256 bytes of ROM. Sine waves have no harmonics, and no harmonics aliasing, so it's very easy to resample them to any rate with a fixed sample rate, and it will continue to sound good, unlike any other waveform such as square or saw waves.
So the IRQ mixing code looks like something like that:
Code: Select all
IRQ: pha txa pha tya pha lda #$00 sta Temp ldy #nchans - lda PhaseL,Y clc adc FreqL,Y sta PhaseL,Y lda PhaseH,Y adc FreqH,Y sta PhaseH,Y tax lda SineTable,X clc adc Temp sta Temp dey bne - lda Temp lsr A ; Optional - convert 8-bit to 7 bit unsigned sta $4011 pla tay pla tax pla rti
Code: Select all
ldx #Chan1 ldy #Chan2 lda FreqL,X sta FreqL,Y lda FreqH,X sta FreqH,Y lda PhaseL,X sta PhaseL,Y lda PhaseH,X clc adc Volume sta PhaseH,Y
I didn't mention it, but of course the sine table is pre-shifted so that it doesn't overflow, so maybe it's range will be -64..64 or something like that instead of -256..256.