SNES S-DD1: Context-Adaptive Decompression and ROM Bus Mapping
Explore the SNES S-DD1 decompressor's adaptive bitstream state, cartridge mapping, DMA flow, and reproducible tests for graphics corruption.
The SNES S-DD1 is a cartridge coprocessor that decompresses graphics data as the console accesses it. It is not a patch file or a host-side archive decompressor: it participates in the cartridge’s memory and DMA behavior, transforming encoded data into bytes that the PPU can consume. Titles that use the chip can show corrupted sprites or fail to update graphics if an emulator loads the ROM correctly but does not model the chip’s stream state and mapping behavior.
The S-DD1 is particularly interesting because its compression is context-adaptive. The decoder maintains probability/context state while reading a bitstream, reconstructs data through an algorithm described in emulator research, and exposes decompressed bytes through the cartridge’s address space. The SNES CPU can also access ordinary ROM data while decompression proceeds, so the chip’s data path and the 5A22 bus must not be collapsed into one synchronous “decompress this entire ROM” operation.
Separate ROM mapping from decompression state
Cartridge mapping determines which ROM bank is visible in the S-DD1 address window. The S-DD1 has registers that select ROM banks for its mapped regions; reset and state restore must rebuild those mappings. The Snes9x implementation’s memory-map code is useful evidence for the relationship between bank registers and ROM pointers, but an emulator should preserve guest-visible address decoding rather than copying a host pointer arrangement verbatim.
The compressed stream itself has internal state: input bits, context states, bitplane mode, prior decoded bits, and related decoder variables. Those fields reveal why a decompression call cannot be treated as stateless per byte. If the CPU or DMA reads a stream in multiple chunks, the decoder must continue from the exact state left by the previous transfer. Resetting context for every chunk produces output that can look almost correct for the first bytes and then diverge.
In the published Snes9x implementation, the decoder maintains context-state and most-probable-symbol arrays, previous-bit history, bit counters, and tables that evolve the probability state. That is concrete evidence of an adaptive decoder rather than a fixed substitution table. Keep these variables in per-device state in a modern implementation; file-static global variables make two emulated SNES instances interfere and complicate deterministic tests. Initialize every state field at the hardware reset boundary and serialize it if the emulator supports mid-stream save states.
Keep the chip’s visible registers, bank mapping, stream decoder state, and DMA channel state distinct. A wrong bank can feed valid but unrelated compressed bytes into a correct decoder. A correct bank with stale decoder context can produce a different failure pattern. Log the cartridge address, selected bank, input bits consumed, and output position when debugging.
Adaptive decoding and output ordering
The S-DD1 algorithm uses context state to interpret a compressed bitstream. The emulator implementation maintains multiple contexts and an evolution table that updates probability state as symbols are decoded. The resulting data is organized into bitplanes and reconstructed in an order expected by the graphics path. Those internal states are not the same thing as a general-purpose DEFLATE or LZ stream; substituting a familiar compressor will not reproduce S-DD1 output.
Decoder state must advance only when the emulated hardware consumes input and produces output. If the emulator decompresses eagerly during ROM load, it risks assuming a fixed access order that guest DMA does not follow. It can also incorrectly expose decompressed bytes to CPU reads that should use ordinary ROM mapping. Model the access path the chip actually intercepts, and verify which bus/DMA transactions use compressed versus uncompressed regions.
The implementation’s bitplane and context selection steps are especially sensitive to bit order. A decoder can produce the right number of output bytes but transpose or reorder pixels when it interprets stream bits in the wrong direction. Test a short stream with a known decoded byte sequence, then test the same content when reads are split at different DMA boundaries. If chunking changes the result, the decoder’s state transition or read interception is wrong.
Output ordering matters because the PPU and DMA engines consume bytes under timing constraints. A decompressed sprite tile arriving one DMA block too early may still render a static test screen but fail mid-frame when game code changes destination or starts a second transfer. Make the transfer boundary and decoder continuation state explicit.
Why old graphics-pack workarounds are weak evidence
Early emulation workarounds sometimes supplied pre-decompressed graphics packs or patched data. Such a workaround can make a game visually playable without emulating the S-DD1 algorithm. It does not validate stream state, bank registers, DMA timing, or CPU-visible behavior. Treat a graphics replacement as compatibility data, not proof that the hardware model is correct.
For archival and debugging, identify the exact cartridge dump and preserve its original checksums. Do not distribute copyrighted ROM content in test reports. A small synthetic or legally created stream, register trace, and expected decoded-byte hash are usually better evidence. If comparing a patched image, document exactly how it was transformed and keep the original hash distinct from the derivative.
Diagnose graphics corruption by stage
When a game using the chip shows broken sprite data, collect the CPU/DMA source address, bank-register values, read type, decoder state reset point, number of compressed input bytes consumed, number of output bytes produced, and target PPU memory range. Compare a small output window with an independently verified reference or known test data.
Corruption that begins immediately at a bank boundary suggests mapping or address-wrap errors. Corruption that begins after a transfer split suggests lost context or incorrect continuation. Correct decoded bytes at the wrong destination suggest DMA programming or transfer-size issues. Correct VRAM data with wrong pixels suggests a downstream tile/palette interpretation bug rather than S-DD1. This staged diagnosis prevents changing the decompressor when the actual defect is address mapping.
Test reset, bank switching, CPU reads during active decompression, DMA transfer splits, save-state capture mid-stream, and invalid/truncated input. Include each known S-DD1 title only as an integration test; targeted tests should isolate a single mapping or decoder behavior. If the implementation cannot serialize a mid-stream decoder, synchronize state capture to a safe boundary or mark the limitation explicitly.
Do not use a regenerated “decompressed ROM” as the only regression fixture unless the transformation and address mapping are fully recorded. A raw output fixture can check the algorithm, but it does not test that guest-visible reads select the right stream and bank. Maintain two levels of tests: decoder unit vectors that start from an explicit bitstream state, and machine-level tests that reach the chip through the SNES bus and DMA programming sequence.
For each unit vector, record initial context state, compressed bytes, expected output bytes, and final decoder state. The final-state assertion matters because two decoders can produce the same short prefix yet diverge on the next read. Keep outputs small and synthetic where possible, then use copyrighted game media only in local integration validation under the site’s lawful-use policy.
Validation and acceptance
The accepted behavior should include correct default bank mapping, register-controlled remapping, deterministic decompression output, preserved context across chunk boundaries, correct bus access distinctions, and coherent save-state restore. Compare output byte-for-byte before evaluating pixels. Then run a game integration test with a known scene and verify that DMA and PPU writes occur in the expected order.
Record the ROM hash, emulator revision, decoder source revision, test stream or test ROM provenance, relevant register values, and expected output hash. The S-DD1 algorithm was reconstructed through hardware-data research, and later emulator implementations are valuable but not substitutes for identifying the original observation behind an edge-case claim. Mark ambiguous behavior and seek corroboration rather than inventing a rule.
The S-DD1 is both a decoder and a cartridge-bus participant. Its adaptive state, bank mapping, and DMA-visible output must agree. Modeling those boundaries independently turns “the graphics are scrambled” into a set of measurable byte and timing questions.
Related:
- SNES HDMA Raster Effects: Tables, Line Counters, and Safe Updates
- SNES SPC700 IPL Upload Protocol: Boot Handshake and ARAM Transfer
Sources: