Raycaster Maze

Physics & Simulation

A grid raycaster — the technique behind Wolfenstein 3-D — running live on a Super Nintendo. A first-person camera auto-walks a 16×16 maze; for each of 64 screen columns a ray is marched cell-to-cell through the wall grid by integer DDA, and the wall slice is drawn at height screen ÷ distance — a per-column divide. Distance shades each slice, so the corridors read as 3-D. Written in C and compiled with the llvm-mos-based 65816 toolchain (+mos-a16, 16-bit accumulator). Runs automatically.

loading core…

Self-running demo — the camera wanders the maze automatically.

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

What it is

A raycaster fakes a 3-D world from a 2-D grid map. From the camera, one ray is cast per screen column; each ray walks the grid until it crosses into a wall cell, and the distance to that wall sets how tall to draw the vertical wall slice — near walls fill the screen, far walls shrink to a sliver. Sweep that across the columns and a flat grid becomes a corridor you can walk down.

The walk uses DDA (digital differential analysis): precompute how far the ray travels to cross one cell in x and one in y, then repeatedly step whichever grid line is nearer — a loop of integer compares and adds, no trigonometry inside it. The only trig is one sine-LUT lookup per column to build the ray direction.

Why it stresses the compiler

Almost every other demo in this battery is multiply-heavy or multiply-free. This one is the division member — the 65816 has no divide instruction, so each of the three per-column reciprocals (|1/rayDirX|, |1/rayDirY|, and the headline screen_h / dist) lowers to a __udivsi3 libcall. Sixty-four columns × three divides is a sustained exercise of the backend's 32-bit division path that no fractal or accumulator demo reaches.

Compiler stress-test

ItemWhat it exercises
DDA grid-castEach column marches a ray cell-to-cell through a 16×16 wall grid by integer DDA — a tight compare/add loop, no per-step trig — until it hits a wall
1/dist projectionThe headline divide: wall slice height = screen_height / perpendicular_distance, one __udivsi3 per column — the only division-bound demo in the battery
deltaDist reciprocalsThe DDA needs |1/rayDirX| and |1/rayDirY| (the per-axis step lengths) — two more __udivsi3 per column, so three divides per ray
Camera basisdir + plane·cameraX from a Q8.8 sine LUT gives the per-column ray (the camera-plane method), so perpendicular distance is fisheye-free

The gate casts a fixed 64-column fan and folds each ray's wall height + side into a rotating-XOR hash, asserting corpus_result (0xB200) is identical on host, default 8-bit SNES, +mos-a16, and +mos-xy16. (It earned its keep: an early build had a signed-overflow bug in the axis-aligned-ray case that the host and SNES optimised differently — the four-way differential caught it.) The Verify fidelity button reproduces the gate live in this tab via the same cycle-accurate bsnes-jg core used in CI.

Written in C with the llvm-mos 65816 toolchain. Hit Verify fidelity to reproduce the build gate's WRAM assert (corpus_result = 0xB200) live in this tab. No far pointers — the same logic checked four ways: host == default == +mos-a16 == +mos-xy16, with bsnes-jg as the in-browser confirmation.