Inside the Whirlpool.
How an early 1990s PC produced fluid, a 2D polar-mapped vortex and two-layer palette compositing.
The interactive preview illustrates polar lookup and nibble packing with procedural textures. It does not execute the original routines, reproduce their timing, or implement the original temporal lookup feedback. The DAC matrix is an illustrative palette, not a captured palette from a particular frame.
Interactive Pipeline Inspector
Rendering Pipeline Stages
Click a stage to inspect its specific math and memory registers.
Precompute Polar Grid & Alpha Matrix
160×100 coordinate map with aspect ratio correction and 64 KiB 2D alpha transition lookup table.
fn_21701 @ 0x21701 · DS:[0x21974] & DS:[0x5fa84]Dual Sine Domain Warping
Layer 1 uses a single harmonic wave; Layer 2 uses dual-frequency Lissajous modulation with prime step counters.
fn_60d8b & fn_60df8 · Sine tables DS:[0x2198c] / DS:[0x2298c]Polar Tunnel Sampling & Viscosity Blend
Sample texture in (r, θ) space and blend with preceding frame using the 64 KiB transition matrix.
fn_60eda (2×) & fn_60e96 · 0x5fa74 / 0x5fa7cNibble-Split Packing & 32-bit Bus Writes
Pack (Layer 2 << 4) | Layer 1. Pack 4 screen pixels into a 32-bit dword per write to video memory.
0x60c1f · SS:[0xa0c80] · 80 rows × 80 dword writes (320 output pixels)Hardware Additive DAC & CRTC Scanline Doubling
CRTC Reg 9 repeats scanlines vertically. The DAC looks up a color already summed by the CPU during palette initialization.
OUT 0x3d4, 0x4309 · 256-color DAC at 0x603f7How 2-Layer Transparency Works Without CPU Cost Nibble-Split DAC
A conventional per-pixel blend computes a weighted sum (which can use integer arithmetic): C = α·A + (1-α)·B.
Nation0 avoids a per-pixel RGB sum in the final compositor by encoding two 4-bit layer indices into one 8-bit byte, and precalculating the 256 VGA DAC palette registers as a precomputed color addition table:
Below is an illustrative 16×16 = 256 additive palette matrix using the same indexing scheme. The horizontal axis is Layer 1 (0..15), and the vertical axis is Layer 2 (0..15):
The Mathematics of the Vortex Polar Transform & Lissajous
The illusion of rotational vortex depth and liquid distortion is created through three mathematical layers:
How (r, θ) Maps to 2D Texture Space & The Zero-Arithmetic Sampler Loop fn_60eda / 0x60eda
In 2D linear memory, texel (U, V) in a 256 × 256 texture has address offset: Offset = (V × 256) + U = (V << 8) | U.
Nation0 takes advantage of this power-of-two alignment: by scaling radius r ∈ [0, 255] into the high byte (V) and angle θ ∈ [0, 255] into the low byte (U), every 16-bit polar word is already the exact physical memory offset into the 64 KiB texture buffer.
Advancing texture data horizontally across columns (U-axis) shifts angles θ → θ + Δ θ, producing continuous vortex spin.
Advancing texture data vertically down rows (V-axis) shifts radii r → r + Δ r, producing dynamic tunnel inward/outward suction.
Shifting rows via sine tables in fn_60d8b and fn_60df8 before polar sampling transforms straight waves into twisting fluid currents.
VGA CRTC Scanline Doubling & 32-bit Bus Optimization CRTC Register 0x09
The rasterizer reduces the number of generated samples and groups its framebuffer writes; an exact bandwidth or frame-rate claim depends on the machine. Nation0 uses two hardware-level speedups:
The routine reprograms VGA CRTC Register 0x09 (Maximum Scan Line register) with value 0x43. This hardware register instructs the VGA CRT controller to repeat each row multiple times on scanout. The CPU only generates 80 scanlines, each is repeated four times in 400-line scanout, equivalent to 160 logical rows with 20-row borders in the 320×200 player.
In the raster loop (0x60c75), two consecutive 8-bit pixels (lower and upper) are packed into a 32-bit dword: dword = lower | (lower << 8) | (upper << 16) | (upper << 24). A single 32-bit write writes 4 pixels to the screen at once.
Complete Frame Pipeline Overview
256-Color DAC Table fn_21701 @ 0x21701
Layer 1 & Layer 2 fn_60d8b & fn_60df8
64 KiB Motion Blend fn_60eda & fn_60e96
4 Pixels per 32-bit Write 0x60c1f @ SS:[A0C80]
+ CRTC Scanline Repeat OUT 0x3d4, 0x4309