OpenSNES
Modern Open-Source SNES Development SDK
Loading...
Searching...
No Matches
main.c File Reference

HDMA wave: build the table by hand, then let the hdma module do it. More...

#include <snes.h>
#include <snes/hdma.h>
#include <snes/input.h>
#include "res/water.inc"

Macros

#define AMP_DEFAULT   10
 Module-mode amplitude: default, bounds and step, in pixels.
#define AMP_MAX   24
#define AMP_MIN   2
#define AMP_STEP   2
#define CH_HAND   HDMA_CHANNEL_2 /* not 0 (dmaCopyVram) nor 1 (the NMI's text flush) */
 Channel of the hand-built table (krom's channel 0).
#define CH_LIB   HDMA_CHANNEL_6
 Channel of the hdma module's wave (7 belongs to the OAM DMA).
#define ENTRY_BYTES   3
 Bytes per HDMA entry in 1REG_2X mode: count + 16-bit value.
#define VRAM_BG1_GFX   0x0000
 VRAM word address of BG1 tile graphics (image start).
#define VRAM_BG1_GFX2   0x4000
 VRAM word address of the second tile half (bytes 32768+).
#define VRAM_BG1_MAP   0x7C00
 VRAM word address of BG1 tilemap — above the 57 KB of tiles, krom's layout (his BG1SC put the map at VRAM byte $F800).
#define WAVE_FREQ   10
 Module-mode frequency: period = 256 / 10 = 25.6 lines.
#define WAVE_WRAP   672
 Wrap length of the start-pointer animation, in table entries. krom's exact value: his sine's quasi-period is ~25.8 lines (non- integer), so his seamless wrap needs 672 entries; the table holds 224 more so any start phase has a full screen of valid lines.

Functions

static void hand_point (void)
 Point channel 0 at the hand-built table from the current phase.
static void lib_start (void)
 (Re)start the module's wave with the current amplitude.
int main (void)
 Entry point — hand-built HDMA table, then the hdma module.

Variables

static u8 amp
 Module-mode amplitude in pixels.
static u8 use_lib
 0 = hand-built table (channel 0), 1 = hdma module (channel 6)
static u16 wave_phase
 Current wave phase of the hand-built table, in entries.
u8 wavetable []
 Mode 3 image data (from data.asm; generated original art).

Detailed Description

HDMA wave: build the table by hand, then let the hdma module do it.

One water image, one per-scanline ripple, two ways to make it.

At boot the ripple comes from an HDMA table written BY HAND and animated the way krom's assembler demo does it: the table is never rewritten; each VBlank the table START POINTER advances one entry, so line L reads entry phase+L and the crests flow up the screen. That is the HDMA table format itself — [line-count, value...] entries and a terminator — and the cheapest animation there is.

Press A and the same ripple comes from the hdma module instead: hdmaWaveH() computes the sine table in RAM and hdmaWaveUpdate() animates it, with the amplitude now a parameter (LEFT/RIGHT). The defaults (amplitude 10, frequency 10: a 25.6-line period) sit close to krom's table (round(10 * sin), a ~25.8-line quasi-period), so the switch shows the helper reproducing what the hand-built table did. Press A again to go back.

C port of "SNES Wave HDMA Demo" by krom (Peter Lemon), github.com/PeterLemon/SNES, PPU/HDMA/WaveHDMA — technique reproduced on the snes/hdma.h API in the original demo configuration (BG Mode 3, full-screen 256-color image). Art is original: procedurally generated water caustics (res/water.bmp), no krom assets.

SNES Concepts
  • HDMA table format: count byte (1 = apply to one scanline) followed by the register payload (2 bytes for a write-twice register), then a 0x00 terminator byte
  • HDMA_MODE_1REG_2X: one register written twice per line — exactly what the 16-bit scroll registers ($210D BG1HOFS low/high) expect
  • Animation by START-POINTER repoint (hdmaSetup once per frame with table + phase*3): the table itself is immutable, so HDMA never observes a partially rewritten entry — no tearing, ~zero CPU cost
  • The same effect from the library: hdmaWaveH / hdmaWaveUpdate / hdmaWaveStop, tables computed and double-buffered in RAM
  • BG Mode 3 (8bpp, 256 colors) with a full-screen image — the same configuration as the original demo (~57 KB of unique tiles, split across two ROM banks; the tilemap sits above them at VRAM $7C00)
What to Observe
  • At boot: the water image in tight sine ripples flowing UPWARD at one scanline per frame — krom's exact table (896 entries, verbatim) and cadence (wrap at 672); the white rulers every 64px stay straight
  • Press A: the hdma module takes over (channel 6 instead of 0); the ripple looks nearly the same
  • LEFT/RIGHT (module mode): amplitude from 2 to 24 pixels, in steps of 2
  • Press A again: back to the hand-built table, from where it was
Modules Used
console, dma, background, input, hdma
See also
hdma.h, examples/hdma/hdma_helpers (the other hdma module effects)

Macro Definition Documentation

◆ AMP_DEFAULT

#define AMP_DEFAULT   10

Module-mode amplitude: default, bounds and step, in pixels.

◆ AMP_MAX

#define AMP_MAX   24

◆ AMP_MIN

#define AMP_MIN   2

◆ AMP_STEP

#define AMP_STEP   2

◆ CH_HAND

#define CH_HAND   HDMA_CHANNEL_2 /* not 0 (dmaCopyVram) nor 1 (the NMI's text flush) */

Channel of the hand-built table (krom's channel 0).

◆ CH_LIB

#define CH_LIB   HDMA_CHANNEL_6

Channel of the hdma module's wave (7 belongs to the OAM DMA).

◆ ENTRY_BYTES

#define ENTRY_BYTES   3

Bytes per HDMA entry in 1REG_2X mode: count + 16-bit value.

◆ VRAM_BG1_GFX

#define VRAM_BG1_GFX   0x0000

VRAM word address of BG1 tile graphics (image start).

◆ VRAM_BG1_GFX2

#define VRAM_BG1_GFX2   0x4000

VRAM word address of the second tile half (bytes 32768+).

◆ VRAM_BG1_MAP

#define VRAM_BG1_MAP   0x7C00

VRAM word address of BG1 tilemap — above the 57 KB of tiles, krom's layout (his BG1SC put the map at VRAM byte $F800).

◆ WAVE_FREQ

#define WAVE_FREQ   10

Module-mode frequency: period = 256 / 10 = 25.6 lines.

◆ WAVE_WRAP

#define WAVE_WRAP   672

Wrap length of the start-pointer animation, in table entries. krom's exact value: his sine's quasi-period is ~25.8 lines (non- integer), so his seamless wrap needs 672 entries; the table holds 224 more so any start phase has a full screen of valid lines.

Function Documentation

◆ hand_point()

void hand_point ( void )
static

Point channel 0 at the hand-built table from the current phase.

◆ lib_start()

void lib_start ( void )
static

(Re)start the module's wave with the current amplitude.

◆ main()

int main ( void )

Entry point — hand-built HDMA table, then the hdma module.

Init order per the SDK convention: console, VRAM, palette, BG pointers, mode, HDMA, screen on. The main loop repoints the hand-built table once per VBlank, or lets hdmaWaveUpdate() animate the module's wave.

Returns
Never returns (infinite loop)

Variable Documentation

◆ amp

u8 amp
static

Module-mode amplitude in pixels.

◆ use_lib

u8 use_lib
static

0 = hand-built table (channel 0), 1 = hdma module (channel 6)

◆ wave_phase

u16 wave_phase
static

Current wave phase of the hand-built table, in entries.

◆ wavetable

u8 wavetable[]
extern

Mode 3 image data (from data.asm; generated original art).

krom's exact HDMA table, in ROM (data.asm).

896 entries of [1][offset16] + terminator, extracted verbatim from the original demo: entry values are round(10*sin) samples with a quasi-period of ~25.8 lines. The table is never written: the animation only moves the start pointer (hdmaSetup reads the bank from the far pointer, so the table can sit in any ROM bank).