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.
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
| Item | What it exercises |
|---|---|
| DDA grid-cast | Each 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 projection | The headline divide: wall slice height = screen_height / perpendicular_distance, one __udivsi3 per column — the only division-bound demo in the battery |
| deltaDist reciprocals | The DDA needs |1/rayDirX| and |1/rayDirY| (the per-axis step lengths) — two more __udivsi3 per column, so three divides per ray |
| Camera basis | dir + 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.