Native / Interactive case study / 20 min read

DX-Ball: can you hear which side the brick is on?

Use REA to investigate DX-Ball’s stereo pan function. Follow instructions and constants, try a sound demo, and test the recovered calculation.

On this page

01. Try it before taking it apart

Press Left, then Play a short tone. Repeat on the right. Now set the scale to −1: the position stays put while the sound changes sides. Scale 0 centers it. The number above is the recovered calculation; the audio below is an illustration.

The stereo experimentPLAY / OBSERVE / EXPLAIN
SCREEN X 320INTEGER PAN 0
L
R

trunc((320 × 1.5625 − 500) × 1) = 0

Use headphones to hear the position. Sound starts only when you press Play.

The numeric rule follows the report. Sound is a normalized Web Audio illustration, not the original audio engine.

What you will build

A position-to-pan function, an audible illustration, and a plan for comparing a reconstruction with original machine code.

Before the investigation

Headphones help with the lab. To follow the binary investigation, you need your own English DX-Ball 1.07 executable and a native analysis provider. The report supplies the target digest and recorded evidence. Set up REA →

02. Follow the brick hit to the sound call

Start with a visible event: a brick breaks on one side of the screen. Work backward from the sound call and identify what value the caller passes. A number that looks like a coordinate could instead be a tile index, an offset or a scaled position.

Use the exact release identity in the upstream report before reusing its addresses. The reported target helper begins at 0x406400. If your executable differs, locate its corresponding behavior rather than assuming the same address works.

Copy this investigation prompt
Inspect my matching DX-Ball 1.07 executable with REA. Start from the pan helper identified in the official report and inspect its brick-hit caller. Explain how the caller constructs the argument. Cite both call-site instructions and the helper’s input load. Do not assume the decompiler signature is complete.

03. When pseudocode loses the argument, read the instructions

A decompiler can produce a short, unhelpful body even when the machine instructions are available. Inspect the stack load, floating-point operations, referenced data and integer conversion together. Ask for the original bytes of each constant, including its type and byte order.

The documented calculation is trunc((x × 1.5625 − 500) × scale). The caller supplies horizontal position. Notice the conversion at the end: truncation toward zero matters for negative values and is different from rounding down.

  • Where does the input enter the function?
  • Which referenced bytes decode to each floating-point constant?
  • Is scale a parameter, stored state or a fixed literal?
  • Does conversion truncate, round, saturate or overflow on the tested input range?

04. Make the number audible, carefully

First implement the numeric function and check left, center and right. Then add nearby positions that produce a fraction. At x = 319 and scale = 1, the raw value is −1.5625 and the integer result is −1. A floor operation would incorrectly produce −2.

Our sound button maps pan / 500 into a Web Audio stereo panner and clamps that preview to −1…1. This makes direction easy to hear; it does not reproduce DirectSound’s volume curve or the original game audio. The displayed integer is not clamped.

Copy this investigation prompt
Implement the documented position-to-pan calculation independently. Test x = 0, 1, 319, 320, 321 and 640 at scales 0, 0.5, 1, 20 and −1. Preserve conversion toward zero. Build a user-triggered tone preview, label its normalization separately, and never use the preview’s clipping as the recovered numeric behavior.

05. Understand what 3,205 passing checks prove

The project reports comparing 641 integer positions with five scale settings against the original x86 function. It separately reports a 63-byte compiler match in its specified historical environment. Those are two different checks: matching behavior for selected inputs, and matching generated machine code.

For your own investigation, retain each input pair and both outputs. If a mismatch appears, reduce it to the smallest failing case. Only attempt a byte match once the compiler version, options and relocation treatment are explicit. Our JavaScript tests cover the documented arithmetic; they do not repeat that original-binary comparison.

06. Compare known results

Use these as explicit checkpoints. Matching them supports this small rule; it does not establish whole-application compatibility.

Screen X / scaleInteger panWhy it matters
0 / 1−500Left edge
319 / 1−1Negative truncation
320 / 10Center
321 / 11Positive truncation
640 / −1−500Reversed scale
640 / 2010,000Numeric result exceeds audio preview range

Save both files in the same folder. Run the checks with Node.js; no package installation is needed.

Terminal
node check-case-labs.mjs

07. What was actually verified?

The 3,205 original-x86 comparisons and 63 matching bytes are upstream results for one function. This site tests a separate arithmetic implementation over the same documented position/scale grid using a rational-number reference. It does not run DXBALL.EXE, rebuild the complete game, or reproduce its audio engine.

Read this site’s recorded numeric test results →

Sources: REA’s DX-Ball evidence report · Pinned maintained pan function. Tutorial, illustrations and browser experiments independently authored.

Next experimentWhy does 200 + 10% equal 220? →

New to the tool? What is REA? · How we check our guides