Native / Interactive case study / 22 min read

TH04: how do 16 bullets know where you are?

Explore TH04 bullet rings with REA. Trace byte angles, aim and rotation, then test a small reconstruction in an interactive diagram.

On this page

01. Try it before taking it apart

Drag the diamond around the center, then fire a ring. The pale ray marks the first bullet. Change the count from 16 to 12: integer angle steps no longer divide the circle evenly. Use the sliders if you prefer a keyboard.

The aiming experimentPLAY / OBSERVE / EXPLAIN
BYTE ANGLE 32FULL TURN 256
DRAG THE DIAMOND TO AIM0 → / 64 ↓

16 directions, spaced 16 angle units apart.

Angle diagram, not game footage. Mouse aiming uses an approximate atan2 conversion; the original uses iatan2.

What you will build

An interactive angle diagram and a small ring calculation with explicit rounding and wraparound checks.

Before the investigation

The diagram runs immediately. To repeat binary inspection, use your own matching TH04 MAIN.EXE and a configured Ghidra provider. You also need the pinned reconstruction source linked below. Set up REA →

02. Get the executable’s address system right

TH04 is a PC-98 DOS program. Before following a function address, establish how the loader maps the file into memory. Save the executable hash, load segment, code mode and address-to-file mapping with your notes. An address without its load profile can send you to the wrong bytes.

The official report provides a known helper and a pinned reconstruction to orient the investigation. Using that anchor is a guided exercise; it is not evidence that an agent discovered the entire program by itself.

Copy this investigation prompt
Use REA to open my TH04 MAIN.EXE with Ghidra. Verify the DOS MZ load profile and artifact identity against the linked TH04 evidence report. If they match, inspect the reported bullet_velocity_and_angle_set helper and map its address back to file bytes. If the artifact or load profile differs, stop using the historical addresses and explain how to locate an equivalent helper.

03. Recover one direction before drawing a ring

The key question is the order of operations. Work through the index, count, aim and rotation separately. For this ring branch, the readable model is floor(index × 256 / count), plus the aim and rotation, wrapped into one byte. Angles increase clockwise on the screen.

Try twelve bullets. Multiplying first and then dividing gives offsets 0, 21, 42, 64… Multiplying the index by floor(256 / count) would give 0, 21, 42, 63… Those paths agree initially, then drift. A visually plausible circle can hide the mistake.

  • 16 bullets, no aim: 0, 16, 32, …, 240.
  • 12 bullets: index 3 should be 64, not 63.
  • Aim 250 plus an offset of 16 wraps to 10.
  • Right is 0, down is 64, left is 128 and up is 192.

04. Separate the ring rule from the aiming helper

Trace where the player and origin coordinates enter the direction helper. Then find where the template rotation is added and where the final byte is stored. Keep these as separate findings so you can test each stage.

Our diagram uses JavaScript atan2 rounded to the nearest byte angle for mouse aiming. The original uses iatan2. Exact agreement at every player coordinate would require recovering and testing that helper too; this diagram demonstrates the ring arithmetic without claiming that equivalence.

Copy this investigation prompt
Recover only the aimed-ring branch. Show the multiply-before-divide instruction sequence, the call that derives aim, the template rotation addition and final byte store. Implement the ring arithmetic independently. Use a clearly labeled approximate aim function for the diagram until the original iatan2 behavior is separately established. Test 12 and 16 bullets, negative rotation and wraparound.

05. Test the angles before judging the picture

Export numeric angles for a few deliberately awkward inputs. A screenshot can make a one-unit error look correct. Test uneven counts, an aim near 255 and a rotation that crosses zero. Check the final byte range, not only the spacing.

If the first ray misses the pointer slightly, inspect angle quantization before changing the ring calculation. If the diagram mirrors vertically, check your coordinate convention. If the native call graph looks inconsistent, inspect the real-mode segment mapping and instruction evidence.

06. Compare known results

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

InputsExpected anglesWhat it catches
count 16; aim 0; rotation 00, 16, 32, …, 240Even spacing
count 12; aim 0; rotation 00, 21, 42, 64, …, 234Multiply before divide
count 16; aim 250; rotation 0250, 10, 26, …, 234Byte wraparound
count 4; aim 0; rotation −1255, 63, 127, 191Negative rotation normalization

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?

This site tests the extracted ring arithmetic and renders a new diagram. It does not execute the original DOS program, reproduce its iatan2 helper, or replay the historical compiler checks. Sixteen is the selected teaching count, not a promise about every game difficulty or firing state.

Read this site’s recorded numeric test results →

Sources: REA’s TH04 evidence report · Pinned reconstruction ring macro. Tutorial, illustrations and browser experiments independently authored.

Next experimentCan you hear which side the brick is on? →

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