Files
tdkpin/original/MECHANICS_PROGRESS.md
T
ddidderr bf5b855f44 fix(game): restore swept target sensors
Move claw contact and all eleven initially active type-4 circle records onto a
swept millipixel sensor test with per-object enter latching. Preserve the exact
left-bank and top-gate coordinates, radii, and scores from the initialized
object ledger.

Remove the inferred top-bank completion rule. Each original top target now
scores 500 and independently starts recovered magnetic-gate timing instead of
requiring all three for an invented bonus.

Test Plan:
- `cargo test --all-targets` -- 44 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- top sensor contact-latch and re-entry test -- passed
- 20-second deterministic launch remained in active table motion
- `git diff --cached --check` -- passed
2026-08-22 21:12:41 +02:00

8.3 KiB

Mechanics reconstruction progress

This ledger scopes the current reverse-engineering pass to the claw and flippers. The preserved executable is the authority; TDKPIN_GHIDRA_RAW.c remains immutable raw evidence, while TDKPIN_MECHANICS_RESTORED.c carries reviewed names and control flow.

Target: TDKPIN.EXE SHA-256 a9022f1894e3e6e21fc42e8f6c932f7c549ca77f63aaa0c488bb9d55d9d0174c.

Function inventory

Address Raw name Restored role Status
1000:638e FUN_1000_638e key-release flag updates reviewed
1000:6bf8 FUN_1000_6bf8 key-press flag updates reviewed
1000:7ed9 FUN_1000_7ed9 flipper collision movement and sound reviewed; inner fixed-point geometry still raw
1000:8b0d FUN_1000_8b0d flipper bitmap/collider movement reviewed; inner fixed-point geometry still raw
1000:99f0 FUN_1000_99f0 binary flipper transition state machine restored
1000:c79c FUN_1000_c79c collision dispatch, including claw object 0x59 claw branch restored
1000:eab1 exported ordinal 10 timer tick, claw release and return claw branch restored
1008:0002 FUN_1008_0002 sound-enabled dispatch restored
1008:0e08 FUN_1008_0e08 claw background restore and sprite slicing restored

Inventory coverage is 9/9 functions inspected for this mechanism pass. This is not a claim that all instructions in the two large physics functions have been renamed.

Proven behavior now available to the Rust port

  • Left Control (scan 0x1d) and keypad Enter (scan 0x1c) set input flags; the timer changes each flipper position only between 0 and 1.
  • Every flipper movement edge, including return, invokes sound number 21, Win16 WAVE resource 2021.
  • Claw collision object 0x59 suspends the ball, zeros its live motion, disables collision records 12 through 20, and selects terminal frame 1, 6, 7, or 18.
  • The claw advances exactly one frame per timer tick to its terminal, releases the ball, then advances one frame per tick back to frame 10 before clearing the overlay and restoring collision records 12 through 20.
  • Resource 900 is sliced into 100x74 frames, nine columns by four rows. The destination is (238,47); closing rows begin at source y=0 and opening rows at source y=148.
  • Claw capture/contact uses sound 2015; release uses sound 2016.
  • The release-coordinate projection is (raw + 500) / 1000 for rendering; decoded physics centers are (258,78), (270,94), (275,97), and (325,92) for the four selectable terminal frames.
  • The speed scalar is 3800 millipixels per tick. Its Borland Real48 direction constants are decoded for all release cases in the readable C companion.

Explicit pending evidence

  • Differentially verify the recovered 10/20/30/40/50 ms timer choices against live original captures; the Rust port intentionally uses the default 30 ms claw cadence independently of render rate.
  • Transcribe the full fixed-point flipper impulse calculation, then compare it with the Rust floating-point collision response.
  • Force each chosen claw terminal in the live original and compare its complete frame/trajectory sequence with the deterministic Rust trace. A controlled collision probe has covered terminal 18; terminals 1, 6, and 7 remain.

Those items are intentionally not counted as semantic parity. The readable C file preserves their raw constants and labels the unresolved conversion so a later implementation cannot silently turn an estimate into claimed evidence.

Runtime comparison on 2026-08-22

Wine 11.15 (staging) ran the preserved 16-bit NE executable from this tree. Keypad + was sent to the real window and produced the one-player started state. Down was then held for 1.1 seconds and released; captures confirmed that the original plunger advances while held and launches only on release.

The Rust validation mode captured the corresponding started and half-charge states plus closing and release frames for claw terminal 6. That original live session did not reach the claw; the controlled tracing described below later captured an original terminal-18 sequence.

Live fixed-point tracing

Wine's remote debugger changes the Win16 exception path and is not suitable for continuous gameplay traces. On Linux, tools/run_traceable_original.sh starts the unmodified executable with a same-user ptrace opt-in. While it is waiting at the launcher, tools/trace_original_state.py locates the live DGROUP and game object by recovered binary/state signatures and records their fixed-point state:

original/tools/run_traceable_original.sh
original/tools/trace_original_state.py --duration 5 --output /tmp/launch.csv

The sampler records original millipixel positions, predicted positions, velocities, flipper inputs and states, and claw animation state whenever any of them changes. It neither patches TDKPIN.EXE nor pauses Wine. The tracing helper is Linux-only development infrastructure; it is not part of the Rust replica.

tools/inject_original_state.py updates all recovered copies of the ball position and velocity between the original 30 ms callbacks. This permits repeatable probes at individual walls, circles, gates, and mechanism triggers:

original/tools/inject_original_state.py --x 289 --y 94 --vx 0 --vy 0

The executable image remains unchanged; only the current disposable Wine process is modified. Run the injector while the ball is waiting in the launcher so it can identify the live game object, or supply the previously reported --object-base to both the injector and tracer during a sequence of active probes. --substeps 1 isolates one collision integration step; omitting it retains the original configured detail level. --stop-process is available for single probes, but repeated whole-process stops can strand Wine while a Win16 mutex is held and therefore are not the default.

A probe at (289,94) captured the original terminal-18 path. The collision suspended the ball while frames advanced from 10 through 18 at the configured 30 ms cadence. Release assigned (325000,92000) and (0,1000) millipixels, then cleared suspension during the same timer callback. The three default physics substeps each added 15 millipixels of vertical velocity, confirming the observed total gravity increment of 45 per timer tick.

A one-substep probe against the vertical shooter walls recovered the type-2 response arithmetic. For unit tangent t from the registered start to end and left normal n, an incoming contact with vn = dot(v,n) and vt = dot(v,t) uses vn' = -0.6 * vn and vt' = vt + 0.1 * vn for the ordinary rail coefficients stored in its two Real48 fields. The new position is the previous position plus this response velocity; the original does not separate a circle from the line with a modern capsule solver.

Fresh one-substep probes recovered type-1 response in the radial basis. With outward normal n, clockwise tangent t, entering speed vn < 0, and tangent speed vt, ordinary object 155 produced vn' = -0.6 * vn and vt' = vt - 0.1 * vn. Bumper 51 additionally added 0.4 * 3800 along n. The exact captured transitions were (0,-940) -> (94,564) and (0,-1970) -> (197,3096) millipixels per substep.

Live key edges recovered all moving flipper geometry. The raised left records are line 66 (98,382)->(131,369), circle 67 (133,377), and line 68 (137,384)->(116,405). The raised right records are line 81 (197,405)->(171,378), circle 82 (181,376), and line 83 (183,368)->(217,383). Resting records retain their registration-table coordinates. Edge-time position and velocity transfer is tracked separately from the record geometry. Three live left-edge probes at (115,390), (120,390), and (125,390) produced post-edge positions (128.718,365), (133.397,360), and (138.182,355) with raw velocities (1932,-3523), (2079,-4658), and (2217,-5889). The Rust upward stroke fits those states; the full raw calculation and return-stroke transfer remain pending.

The type-4 rule dispatcher at 1000:b476 shows that records 150-152 are three independent 500-point sensors. Each sets its own ten-callback timer and enables the associated magnetic gate; there is no recovered 1,500-per-target plus 5,000 completion rule. Records 140-147 carry their exact 1,000 through 3,000 scores and use the same enter-once contact bit, which clears after the ball leaves their radius.