docs(reverse): decode claw release arithmetic
Correct the coordinate model from guessed 16.16 values to the millipixel arithmetic shown by the 16-bit division routine. Decode the Borland Real48 direction constants and record the exact position and velocity tables used by every claw terminal. Test Plan: - git diff --cached --check - Compare constants against segment 1 disassembly at 9bca and eab1
This commit is contained in:
@@ -42,17 +42,22 @@ renamed.
|
|||||||
destination is `(238,47)`; closing rows begin at source y=0 and opening rows
|
destination is `(238,47)`; closing rows begin at source y=0 and opening rows
|
||||||
at source y=148.
|
at source y=148.
|
||||||
- Claw capture/contact uses sound 2015; release uses sound 2016.
|
- 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
|
## Explicit pending evidence
|
||||||
|
|
||||||
- Decode the runtime/FPU conversion used to project the claw's raw 16.16
|
- Differentially verify the recovered 10/20/30/40/50 ms timer choices against
|
||||||
release coordinates and non-frame-18 velocity into playfield pixels.
|
live original captures; the Rust port intentionally uses the default 30 ms
|
||||||
- Recover or differentially measure the timer cadence used for each claw frame.
|
claw cadence independently of render rate.
|
||||||
- Transcribe the full fixed-point flipper impulse calculation, then compare it
|
- Transcribe the full fixed-point flipper impulse calculation, then compare it
|
||||||
with the Rust floating-point collision response.
|
with the Rust floating-point collision response.
|
||||||
- Validate the reconstructed sequences against deterministic Rust traces and
|
- Validate the reconstructed sequences against deterministic Rust traces and
|
||||||
captured frames from the original executable.
|
captured frames from the original executable.
|
||||||
|
|
||||||
Those four items are intentionally not counted as semantic parity. The readable
|
Those items are intentionally not counted as semantic parity. The readable
|
||||||
C file preserves their raw constants and labels the unresolved conversion so a
|
C file preserves their raw constants and labels the unresolved conversion so a
|
||||||
later implementation cannot silently turn an estimate into claimed evidence.
|
later implementation cannot silently turn an estimate into claimed evidence.
|
||||||
|
|||||||
@@ -51,13 +51,18 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t low;
|
uint16_t low;
|
||||||
int16_t high;
|
int16_t high;
|
||||||
} Fixed32Words;
|
} MillipixelWords;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Fixed32Words x;
|
MillipixelWords x;
|
||||||
Fixed32Words y;
|
MillipixelWords y;
|
||||||
} RawClawReleasePosition;
|
} RawClawReleasePosition;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
} Vec2F;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FUN_1008_0002 at 1008:0002.
|
* FUN_1008_0002 at 1008:0002.
|
||||||
* The called loader/player resolves argument N as Win16 WAVE resource 2000+N.
|
* The called loader/player resolves argument N as Win16 WAVE resource 2000+N.
|
||||||
@@ -154,9 +159,10 @@ static void render_claw(uint16_t frame, ClawSpriteBank bank)
|
|||||||
static const uint16_t CLAW_TERMINAL_FRAMES[4] = {1, 6, 7, 18};
|
static const uint16_t CLAW_TERMINAL_FRAMES[4] = {1, 6, 7, 18};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Raw 16.16 positions assigned by export ordinal 10 at 1000:eab1.
|
* Raw signed 32-bit millipixel positions assigned by export ordinal 10 at
|
||||||
* These words are preserved until the original coordinate projection is
|
* 1000:eab1. FUN_1000_9bca adds 500, divides by 1000, then subtracts the
|
||||||
* fully decoded; do not substitute guessed screen pixels here.
|
* eight-pixel ball radius only for bitmap placement. The physics position is
|
||||||
|
* therefore the raw value divided by 1000, not a 16.16 fixed-point number.
|
||||||
*/
|
*/
|
||||||
static RawClawReleasePosition raw_claw_release_position(uint16_t frame)
|
static RawClawReleasePosition raw_claw_release_position(uint16_t frame)
|
||||||
{
|
{
|
||||||
@@ -186,6 +192,44 @@ static RawClawReleasePosition raw_claw_release_position(uint16_t frame)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int32_t join_millipixel_words(MillipixelWords value)
|
||||||
|
{
|
||||||
|
return ((int32_t)value.high << 16) | value.low;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vec2F claw_release_position_pixels(uint16_t frame)
|
||||||
|
{
|
||||||
|
RawClawReleasePosition raw = raw_claw_release_position(frame);
|
||||||
|
return (Vec2F){
|
||||||
|
join_millipixel_words(raw.x) / 1000.0f,
|
||||||
|
join_millipixel_words(raw.y) / 1000.0f,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Real48 constants loaded in 1000:eab1 after converting speed scalar 3800.
|
||||||
|
* The result is millipixels per timer tick. Only 1, 6, 7, and 18 are selected
|
||||||
|
* as random terminal frames, but all switch cases are retained here.
|
||||||
|
*/
|
||||||
|
static Vec2F claw_release_velocity_millipixels_per_tick(uint16_t frame)
|
||||||
|
{
|
||||||
|
switch (frame) {
|
||||||
|
case 1: return (Vec2F){3800.0f * -0.60f, 3800.0f * 0.00f};
|
||||||
|
case 2: return (Vec2F){3800.0f * -0.45f, 3800.0f * 0.05f};
|
||||||
|
case 3: return (Vec2F){3800.0f * -0.70f, 3800.0f * 0.30f};
|
||||||
|
case 4: return (Vec2F){3800.0f * -0.30f, 3800.0f * 0.20f};
|
||||||
|
case 5: return (Vec2F){3800.0f * -0.32f, 3800.0f * 0.22f};
|
||||||
|
case 6: return (Vec2F){3800.0f * -0.40f, 3800.0f * 0.60f};
|
||||||
|
case 7: return (Vec2F){3800.0f * -0.31f, 3800.0f * 0.70f};
|
||||||
|
case 8: return (Vec2F){3800.0f * -0.20f, 3800.0f * 0.80f};
|
||||||
|
case 9: return (Vec2F){3800.0f * -0.10f, 3800.0f * 0.90f};
|
||||||
|
case 18: return (Vec2F){0.0f, 1000.0f};
|
||||||
|
default:
|
||||||
|
unreachable_original_state();
|
||||||
|
return (Vec2F){0.0f, 0.0f};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Object-id 0x59 branch inside FUN_1000_c79c at 1000:c79c. */
|
/* Object-id 0x59 branch inside FUN_1000_c79c at 1000:c79c. */
|
||||||
static void begin_claw_capture(ClawState *claw, uint16_t random_0_to_3)
|
static void begin_claw_capture(ClawState *claw, uint16_t random_0_to_3)
|
||||||
{
|
{
|
||||||
@@ -231,13 +275,9 @@ static void update_claw(ClawState *claw)
|
|||||||
claw->target_frame = 10;
|
claw->target_frame = 10;
|
||||||
claw->bank = CLAW_OPENING_OR_IDLE;
|
claw->bank = CLAW_OPENING_OR_IDLE;
|
||||||
reset_ball_forces();
|
reset_ball_forces();
|
||||||
set_ball_position_raw(raw_claw_release_position(claw->frame));
|
set_ball_position_pixels(claw_release_position_pixels(claw->frame));
|
||||||
|
set_ball_velocity_millipixels_per_tick(
|
||||||
if (claw->frame == 18) {
|
claw_release_velocity_millipixels_per_tick(claw->frame));
|
||||||
set_ball_velocity_raw(0, 1000);
|
|
||||||
} else {
|
|
||||||
set_ball_velocity_from_original_speed_scalar(); /* projection pending */
|
|
||||||
}
|
|
||||||
|
|
||||||
commit_ball_position();
|
commit_ball_position();
|
||||||
play_sound_number(16);
|
play_sound_number(16);
|
||||||
|
|||||||
Reference in New Issue
Block a user