ADPCM Waverider

Algorithms & Numeric

An IMA-ADPCM audio decoder — the compression used by countless game samples. Each 4-bit code nudges a predictor that is clamped with saturating add/sub (G_SADDSAT/ G_SSUBSAT), and the step size walks an 89-entry table. It's a true serial feedback loop: every sample depends on the last. Drawn as a scrolling oscilloscope. Compiler stress-test #89.

loading core…

Self-running — a decoded ADPCM waveform scrolls across the scope.

Click the screen to focus, then watch. Tab away and it pauses.

What it is

A pseudo-random 4-bit ADPCM code stream is decoded into a 16-bit waveform and plotted across the scope. Because the predictor accumulates, the trace wanders and then rails against the saturation limits at the top and bottom — exactly where the saturating add/sub keeps it from wrapping into a glitch.

// IMA-ADPCM: predictor updated with SATURATING add/sub, inside a serial feedback loop.
int16_t decode(uint8_t n) {
  int16_t step = STEP[idx];
  int32_t diff = step>>3;
  if (n&4) diff+=step; if (n&2) diff+=step>>1; if (n&1) diff+=step>>2;
  pred = (n&8) ? __builtin_elementwise_sub_sat(pred, diff)   // G_SSUBSAT
               : __builtin_elementwise_add_sat(pred, diff);  // G_SADDSAT
  idx += IDX[n]; clamp(idx, 0, 88);   // step-size LUT walk
  return pred;                         // next output depends on THIS pred (feedback)
}

The feedback recurrence is the hard part for a compiler: the saturating clamp sits on the critical path of a loop that can't be reordered, so the backend must get both the saturating opcode and the dependency chain right. The host oracle checks the decoded waveform is bit-identical.

Compiler stress-test

ItemWhat it exercises
saturating feedbackThe predictor is clamped every sample with __builtin_elementwise_add_sat/_sub_sat (G_SADDSAT/G_SSUBSAT) so a loud passage rails at ±full-scale instead of wrapping to the opposite sign — a decode wrap would be an audible click. The clamp is inside a serial recurrence, so a miscompiled saturate would corrupt the entire rest of the waveform.
serial recurrenceEach output sample is pred += ±diff of the previous sample — a true feedback loop that cannot be vectorized or reordered. The step size also walks an 89-entry table indexed by a running state updated every nibble.
host vs targetThe host oracle (gcc) has no elementwise saturating builtins and uses a bit-identical manual clamp; the 65816 target (clang) uses the real intrinsics. The differential gate confirms both produce the same waveform CRC.
distinct from #48 / #67#48 IIR is a wrapping fixed-point feedback filter (no saturation). #67 huffman has no feedback at all. This is the saturating-arithmetic-in-a-feedback-loop member of the battery.

Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg and MAME. Hit Verify fidelity to reproduce the build gate's WRAM assert (ADPCM CRC 0xCA56 — a fold of the decoded predictor stream) live in this tab. No far pointers — host == default == +mos-a16 == +mos-xy16, -verify clean.