4 ms·
Very carefully scheduling NMI and IRQ updates to achieve a virtual sound channel on an old NES, resulting in a sawtooth bassline with volume control. This steal
by zeta0134 4y ago
Very carefully scheduling NMI and IRQ updates to achieve a virtual sound channel on an old NES, resulting in a sawtooth bassline with volume control. This steals about 13% of the CPU time. The biggest hack in the arrangement is the scheduling of OAM DMA to sneakily just barely fit between timed audio updates, since it pauses the CPU and would otherwise cause an audible glitch. Among other things, if NMI notices that it interrupted IRQ, it apologizes profusely and exits immediately, which is a rather unusual workaround on 6502 systems.
I've open sourced the technique with a more detailed writeup [1] and I like to think the game I created with it is pretty fun, [2] but of course that's subjective. You can try it in your browser here. [3]
[1] https://github.com/zeta0134/z-saw https://github.com/zeta0134/z-saw
[2] https://zeta0134.itch.io/tactus https://zeta0134.itch.io/tactus
[3] https://rusticnes.reploid.cafe/wasm/?cartridge=tactus.nes https://rusticnes.reploid.cafe/wasm/?cartridge=tactus.nes
- superdisk 4y agoCool. Kind of unrelated, but is there anything stopping someone from just generating a sawtooth DPCM sample at runtime and using the channel as normal without having to use crazy timing stuff?
- zeta0134 4y agoYes, it comes down to how the DPCM channel encodes sound data. A sawtooth gets most it's energy and harmonics from the sudden jump at one end. This impulse is impossible to encode in 1-bit delta encoding, the closest you can get is a slightly steeper slope. The audible effect is similar to a low pass, with the strength dependent on the intended amplitude, so instead of a nice sharp saw, you get a muddy not quite triangle. A few commercial games did use the DPCM channel melodically, but the main drawback is the large size of the samples relative to the commonly available ROM chips of the era. Price is a big factor when you need to put one chip in every game. Here's how that sounded in practice: https://www.youtube.com/watch?v=LEgoYUzwabI https://www.youtube.com/watch?v=LEgoYUzwabI
- superdisk 4y agoAh sure, but how about just generating a triangle using a small block of code in RAM? Then you're not wasting any storage.
- zeta0134 4y agoFurther problems! The DPCM channel can only address memory from the region 0xC000 - 0xFFFF, due to how its "address" byte is interpreted. Most cartridges cannot place RAM in this memory region, so you're stuck baking the samples into ROM space. Many cartridges cannot even bank switch this region, so the samples additionally cut into your "fixed" ROM that is usually precious space for interrupt service routines and common framework code.
- superdisk 4y agoAh very interesting, thanks for the explanation! I'll definitely check out your lib if I ever get around to writing a homebrew NES game, I love quirky audio tricks :)