From 2a91d40f4d75ec5affdbd63b95815f14d7a62884 Mon Sep 17 00:00:00 2001 From: libretroadmin Date: Sun, 14 Jun 2026 16:57:27 +0000 Subject: [PATCH] fds_apu: correct modulator bias values at table indices 3 and 5 The FDS modulator unit's per-step bias table is documented on the nesdev wiki and used in MDFN, NSFPlay, and negativeExponent's fceumm_next as { 0, +1, +2, +4, RESET, -4, -2, -1 }. The conditional form in this file expanded to { 0, +1, +2, +3, RESET, -3, -2, -1 } - producing the wrong magnitude at indices 3 and 5 where real hardware takes a power-of-2 step. Replace the conditional with a literal lookup table matching the hardware. Bit-identical for mod table values 0, 1, 2, 4, 6, 7; only indices 3 and 5 change. This is a step toward #560 (VS Excitebike FDS triangle pitch) but unlikely to resolve it on its own. The modulator's deeper bug is that this file is missing the sweep_bias integrator entirely: real hardware accumulates the bias steps over time and the resulting running total scales the carrier frequency adjustment, whereas this implementation applies each mod table entry's bias instantaneously to the b8shiftreg88 sub-clock gating without accumulation. Tracked separately; the structural fix needs the modulator unit rebuilt around an sweep_bias accumulator (or a wholesale backport of _next's fds_apu rewrite). --- src/fds_apu.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/fds_apu.c b/src/fds_apu.c index f379e79..dbc5bff 100644 --- a/src/fds_apu.c +++ b/src/fds_apu.c @@ -166,14 +166,21 @@ static INLINE void ClockRise(void) { if (!(SPSG[0x7] & 0x80)) { int t = fdso.mwave[(b17latch76 >> 13) & 0x1F] & 7; int t2 = amplitude[1]; - int adj = 0; - - if ((t & 3)) { - if ((t & 4)) - adj -= (t2 * ((4 - (t & 3)))); - else - adj += (t2 * ((t & 3))); - } + int adj; + /* Per-step bias the modulator unit applies for each + * 3-bit mod table entry. Real hardware uses powers of + * 2 ({-4,-2,-1,0,+1,+2,+4} + RESET); the conditional + * form previously here computed { +/- (4 - (t & 3)) } + * for the negate cases and { +/- (t & 3) } for the + * positive cases, yielding ±3 instead of ±4 at table + * positions 3 and 5. Documented at nesdev wiki + * "FDS audio" / Modulator unit; same table is used in + * MDFN/NSFPlay and negativeExponent's fceumm_next. */ + static const int8_t mod_bias_tab[8] = { + 0, 1, 2, 4, + 0, -4, -2, -1 + }; + adj = t2 * mod_bias_tab[t]; adj *= 2; if (adj > 0x7F) adj = 0x7F; if (adj < -0x80) adj = -0x80;