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.
16 directions, spaced 16 angle units apart.
Angle diagram, not game footage. Mouse aiming uses an approximate atan2 conversion; the original uses iatan2.
An interactive angle diagram and a small ring calculation with explicit rounding and wraparound checks.
Before the investigationThe 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.
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.
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.
| Inputs | Expected angles | What it catches |
|---|---|---|
| count 16; aim 0; rotation 0 | 0, 16, 32, …, 240 | Even spacing |
| count 12; aim 0; rotation 0 | 0, 21, 42, 64, …, 234 | Multiply before divide |
| count 16; aim 250; rotation 0 | 250, 10, 26, …, 234 | Byte wraparound |
| count 4; aim 0; rotation −1 | 255, 63, 127, 191 | Negative rotation normalization |
Save both files in the same folder. Run the checks with Node.js; no package installation is needed.
node check-case-labs.mjs07. 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.
New to the tool? What is REA? · How we check our guides