On this page
  1. Super Metroid
  2. Related
  3. Games
  4. References
  5. Sources

HDMA turns a raster effect into data. Each of the 8 DMA channels can run during horizontal blank, writing values from a table to a register, and each table entry either writes once and waits or writes a new value on every line for a count1. An indirect table holds pointers to the values instead of the values1.

  • Color gradientSuper Metroid (1994). Ceres station's haze steps a color down the screen in 8-line bands.2
  • Scroll waveSuper Metroid (1994). BG2 below the waterline ripples a pixel either way in an 8-line pattern that moves every 6 frames.3

#Super Metroid

  • Ripples. In rooms whose water is set to wave, BG2’s scroll goes through an indirect table with one entry per line. Lines below the water walk a ring of 16 scroll values with a displacement of 0, 1, 1, 0, 0, −1, −1, 0 pixels3, so the background shifts a pixel either way; the pattern moves one line every 6 frames3.
  • Haze. Ceres station’s haze writes the color used for color math: a 64-line band, then 15 bands of 8 lines whose intensities rise down the screen, filled in over 16 frames as the room fades in2.

Games

GameYearHowNote
Super Metroid1994Color gradientCeres station's haze steps a color down the screen in 8-line bands
Super Metroid1994Scroll waveBG2 below the waterline ripples a pixel either way in an 8-line pattern that moves every 6 frames
Every claim about this mechanic, by game (3)

References

  1. ^ abThe SNES has 8 DMA channels, each of which can run as HDMA, writing values from a table to a PPU register at set scanlines. Each table entry starts with a byte that either writes once and waits that many lines ($01-$80) or writes a new value on each of the next n−128 lines ($81-$FF); $00 ends the table. An indirect table holds pointers to the data instead of the data.WikiSNESdev Wiki, “DMA registers”
    How we know
    The SNES also features HDMA (Horizontal-blank DMA) which runs in the background and can be set up to automatically write values to hardware registers at specific scanlines, allowing for effects.
    lead · archived copy ↗
    The SNES contains 8 separate DMA "channels"
    DMA channels · archived copy ↗
    $00: Stop processing HDMA on that channel for the rest of the frame. $01-$80: Write once, then wait for X scanlines $81-$FF: Write every scanline for X-$80 scanlines, repeat mode
    Line count, repeat mode byte · archived copy ↗
    The tables can either directly contain the values (Direct mode) or specify 16-bit pointers that are then used to get the values (Indirect mode).
    HDMA table format · archived copy ↗

    Checked against the evidence by its author, an AI model, 2026-10-21.

    Checked by a second AI model, 2026-10-21.

    In the claim ledger

  2. ^ abThe Ceres haze is an indirect HDMA table on the sub-screen backdrop color, used for color math. Its first entry covers 64 lines and 15 more cover 8 lines each; while the room fades in, the routine raises the bands' intensities over 16 frames, ending with 0 at the top and 1 to 15 in the bands below, a gradient down the screen.CodeInsaneFirebat/sm_disassembly
    How we know
    10016    JSL.L Spawn_HDMAObject                                               ;88DDD0;
    10017    db $40,$32                                                           ;88DDD4;
    10018    dw InstList_CeresHaze_ColorMathSubScnBackColor_RidleyAlive_0         ;88DDD6;
    
    src/bank_88.asm lines 10011–10018 at 7af131e · upstream ↗
    990;;         $32: Colour math subscreen backdrop colour (8-bit, sometimes used with write-twice)
    
    src/bank_88.asm lines 979–990 at 7af131e · upstream ↗
    10098PreInstruction_CeresHaze_ColorMathSubScnBackColor_FadingIn:
    ⋮
    10109    LDA.W HDMAObject_Var0,X                                              ;88DE3F;
    10110    CMP.W #$0010                                                         ;88DE42;
    10111    BEQ .done                                                            ;88DE45;
    ⋮
    10115    LDA.W HDMAObject_Var1,X                                              ;88DE4B;
    10116    STA.B DP_Temp14                                                      ;88DE4E;
    10117    LDA.W HDMAObject_Var0,X                                              ;88DE50;
    10118    LDX.B #$0F                                                           ;88DE53;
    ⋮
    10120  .loop:
    10121    STA.B DP_Temp12                                                      ;88DE55;
    10122    ORA.B DP_Temp14                                                      ;88DE57;
    10123    STA.L HazeColorMathSubscreenBackdropColorHDMADataTable,X             ;88DE59;
    ⋮
    
    src/bank_88.asm lines 10097–10134 at 7af131e · upstream ↗
    10247IndirectHDMATable_CeresHaze_ColorMathSubScnBackdropColor:
    10248    db $40 : dw HazeColorMathSubscreenBackdropColorHDMADataTable         ;88DF03;
    10249    db $08 : dw HazeColorMathSubscreenBackdropColorHDMADataTable+1
    ⋮
    10263    db $08 : dw HazeColorMathSubscreenBackdropColorHDMADataTable+$F
    10264    db $00
    
    src/bank_88.asm lines 10246–10264 at 7af131e · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-25.

    Checked by a second AI model, 2026-10-21.

    Checked by a second AI model, 2026-10-25.

    In the claim ledger

  3. ^ abcIn rooms whose water is set to wave, an indirect HDMA channel writes BG2's X scroll on every line. Above the water each line points at the plain scroll; below it the lines walk through a ring of 16 values, the scroll plus a displacement of 0, 1, 1, 0, 0, −1, −1, 0 repeating, so the background ripples a pixel either way in an 8-line pattern that shifts one line every 6 frames.CodeInsaneFirebat/sm_disassembly
    How we know
    LDA.W FX_LiquidOptions … AND.W #$0002 … BEQ .notWavy … JSL.L Spawn_HDMAObject … db $42,$0F … dw InstList_Water_BG2_Xscroll
    src/bank_88.asm#L7229-L7234 · upstream ↗
    982;;         $0F: BG2 X scroll (16-bit, write-twice)
    
    src/bank_88.asm lines 980–982 at 7af131e · upstream ↗
    7303;;; $C46E: Wave displacement table - water ;;;
    7304WaveDisplacementTable_Water:
    
    src/bank_88.asm lines 7303–7306 at 7af131e · upstream ↗
    7524Setup_Water_BG2_Xscroll_DataTable_Wavy:
    ⋮
    7554  .loop:
    
    src/bank_88.asm lines 7523–7570 at 7af131e · upstream ↗
    7468; the calculation 1FFh - [A] at $C5CE is equivalent to FFh + (Y position of screen on FX)
    
    src/bank_88.asm lines 7465–7472 at 7af131e · upstream ↗
    8365;;; $CF46: Indirect HDMA table - water BG2 X scroll ;;;
    8366IndirectHDMATable_WaterBG2XScroll:
    ⋮
    8368    db $81 : dw WaterBG2XScrollHDMADataTable-4
    
    src/bank_88.asm lines 8365–8368 at 7af131e · upstream ↗
    8623    db $81 : dw WaterBG2XScrollHDMADataTable
    ⋮
    8638    db $81 : dw WaterBG2XScrollHDMADataTable+$1E
    
    src/bank_88.asm lines 8623–8638 at 7af131e · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-21.

    Checked by a second AI model, 2026-10-21.

    In the claim ledger

Sources

Code

Secondary