Files
tdkpin/original/reconstructed/tests/test_key_release.c
T
ddidderr 8b99e9607c feat(reconstruction): complete binary-backed C recovery
Replace the partial mechanics transcriptions with a separate, readable C11
reconstruction of the complete Win16 image while preserving the original raw
Ghidra export as immutable evidence. Cover all ordinary and overlapping entry
points, Borland runtime behavior, Win16 imports, segmented data, callbacks,
resources, indirect control flow, physics, rendering, persistence, and
startup/shutdown lifecycles.

Add deterministic extraction and audit tooling plus address-linked ledgers for
functions, imports, DGROUP ranges and objects, relocations, resources, and
callbacks. The final gate records zero raw, partial, restored, unknown,
blocked, or unclassified required units. Keep the semantic-fidelity boundary
explicit: the portable C is not claimed to reproduce a byte-identical Borland
NE build.

Add strict focused harnesses for every reconstructed C unit, exact resource
round-trip checks, and a 16-bit Borland Real48 reference probe. No Rust source
or Cargo metadata is changed in this phase.

Test Plan:
- `bash original/tools/test_reconstructed_c.sh` -- passed
- `bash original/tools/probe_real48_reference.sh` -- passed bit-for-bit
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed
- `git diff --cached --check` -- passed
- `git diff HEAD -- '*.rs' Cargo.toml Cargo.lock` -- empty
2026-08-23 16:41:17 +02:00

500 lines
17 KiB
C

#include "../tdkpin_key_input.h"
#include "../tdkpin_random.h"
#include "../tdkpin_real48.h"
#include <assert.h>
#include <string.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_objects[0x2000];
static Win16FarPtr g_window;
static Win16FarPtr g_event;
static uint16_t g_random_values[16];
static uint16_t g_random_bounds[16];
static size_t g_random_count;
static size_t g_random_index;
static uint16_t g_sounds[8];
static size_t g_sound_count;
static unsigned g_get_dc_calls;
static unsigned g_release_dc_calls;
static unsigned g_blit_calls;
static unsigned g_restore_a_calls;
static unsigned g_restore_b_calls;
static unsigned g_present_calls;
static unsigned g_draw_item_calls;
static unsigned g_draw_score_calls;
static unsigned g_status_calls;
typedef enum {
REAL_MODE_NONE,
REAL_MODE_UPPER_CLAMP,
REAL_MODE_HORIZONTAL_NUDGE,
} RealMode;
static RealMode g_real_mode;
static unsigned g_real_multiply_calls;
static const BorlandReal48 g_real_random =
{{0x80, 1, 2, 3, 4, 5}};
static const BorlandReal48 g_real_product =
{{0x81, 6, 7, 8, 9, 10}};
static const BorlandReal48 g_real_factor =
{{0x82, 11, 12, 13, 14, 15}};
static const BorlandReal48 g_real_integer =
{{0x83, 16, 17, 18, 19, 20}};
static const BorlandReal48 g_real_result =
{{0x84, 21, 22, 23, 24, 25}};
static void assert_real48(BorlandReal48 actual, BorlandReal48 expected)
{
assert(memcmp(actual.bytes, expected.bytes, sizeof(actual.bytes)) == 0);
}
int32_t borland_multiply_i32(int32_t left, int32_t right)
{
return (int32_t)((uint32_t)left * (uint32_t)right);
}
uint16_t borland_random_below(uint16_t upper_bound)
{
assert(g_random_index < g_random_count);
assert(upper_bound == g_random_bounds[g_random_index]);
return g_random_values[g_random_index++];
}
BorlandReal48 borland_random_real48_registers(void)
{
assert(g_real_mode == REAL_MODE_HORIZONTAL_NUDGE);
return g_real_random;
}
BorlandReal48 borland_i32_to_real48(int32_t value)
{
if (g_real_mode == REAL_MODE_UPPER_CLAMP) {
assert(value == 1000);
} else {
assert(g_real_mode == REAL_MODE_HORIZONTAL_NUDGE);
assert(value == 3);
}
return g_real_integer;
}
BorlandReal48 borland_real48_multiply(
BorlandReal48 left, BorlandReal48 right)
{
static const BorlandReal48 three_fifths =
{{0x80, 0x9a, 0x99, 0x99, 0x99, 0x19}};
static const BorlandReal48 nudge_scale =
{{0x85, 0, 0, 0, 0, 0x20}};
if (g_real_mode == REAL_MODE_UPPER_CLAMP) {
assert(g_real_multiply_calls++ == 0);
assert_real48(left, g_real_integer);
assert_real48(right, three_fifths);
return g_real_result;
}
assert(g_real_mode == REAL_MODE_HORIZONTAL_NUDGE);
if (g_real_multiply_calls++ == 0) {
assert_real48(left, g_real_random);
assert_real48(right, nudge_scale);
return g_real_product;
}
assert_real48(left, g_real_integer);
assert_real48(right, g_real_factor);
return g_real_result;
}
BorlandReal48 borland_real48_subtract(
BorlandReal48 left, BorlandReal48 right)
{
static const BorlandReal48 nudge_offset =
{{0x84, 0, 0, 0, 0, 0x20}};
assert(g_real_mode == REAL_MODE_HORIZONTAL_NUDGE);
assert_real48(left, g_real_product);
assert_real48(right, nudge_offset);
return g_real_factor;
}
int32_t borland_real48_round_to_i32(BorlandReal48 value)
{
assert_real48(value, g_real_result);
return g_real_mode == REAL_MODE_UPPER_CLAMP ? 600 : 7;
}
void tdkpin_play_sound_if_not_tilted(uint16_t sound_number)
{
assert(g_sound_count < sizeof(g_sounds) / sizeof(g_sounds[0]));
g_sounds[g_sound_count++] = sound_number;
}
HDC16 GetDC16(HWND16 window)
{
assert(window == 0x2222);
g_get_dc_calls++;
return 0x3333;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == 0x2222 && dc == 0x3333);
g_release_dc_calls++;
return 1;
}
void tdkpin_blit_bitmap_and_present(
HBITMAP16 bitmap,
INT16 height,
INT16 width,
INT16 y,
INT16 x,
HDC16 dc)
{
assert(bitmap == 0x4444);
assert(height == 18 && width == 21);
assert(y == 426 && x == 314 && dc == 0x3333);
g_blit_calls++;
}
void tdkpin_restore_background_region_a(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(dc == 0x3333);
if (height == 20) {
assert(width == 148 && y == 243 && x == 84);
} else {
assert(height == 30 && width == 284 && y == 232 && x == 371);
}
g_restore_a_calls++;
}
void tdkpin_restore_background_region_b(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height == 42 && width == 284 && x == 348 && dc == 0x3333);
assert(y == 360 || y == 405);
g_restore_b_calls++;
}
void tdkpin_present_background_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(dc == 0x3333);
if (height == 20) {
assert(width == 148 && y == 243 && x == 84);
} else if (height == 30) {
assert(width == 284 && y == 232 && x == 371);
} else {
assert(height == 42 && width == 284 && x == 348);
assert(y == 360 || y == 405);
}
g_present_calls++;
}
void tdkpin_draw_item_status(int16_t item, HDC16 dc)
{
assert(item == 0 && dc == 0x3333);
g_draw_item_calls++;
}
void tdkpin_draw_score(HDC16 dc, int32_t value_or_player)
{
assert(dc == 0x3333 && value_or_player == 0);
g_draw_score_calls++;
}
void tdkpin_update_status_display(uint8_t player, HDC16 dc)
{
assert(player == 1 && dc == 0x3333);
g_status_calls++;
}
static void set_random_script(
const uint16_t *bounds, const uint16_t *values, size_t count)
{
assert(count <= sizeof(g_random_values) / sizeof(g_random_values[0]));
memcpy(g_random_bounds, bounds, count * sizeof(bounds[0]));
memcpy(g_random_values, values, count * sizeof(values[0]));
g_random_count = count;
g_random_index = 0;
}
static void prepare_fixture(uint16_t scan_code)
{
memset(g_dgroup, 0, sizeof(g_dgroup));
memset(g_objects, 0, sizeof(g_objects));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_objects, sizeof(g_objects), true);
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
g_window = win16_make_far_pointer(0x7000, 0);
g_event = win16_make_far_pointer(0x7000, 0x1000);
win16_write_u16(g_window, 4, 0x2222);
win16_write_u16(g_event, 8, scan_code);
win16_write_u16(g_event, 0x0a, 0xaaaa);
win16_write_u16(g_event, 0x0c, 0xbbbb);
win16_write_u16(win16_dgroup_pointer(0x0865), 0, 0x4444);
g_random_count = 0;
g_random_index = 0;
g_sound_count = 0;
g_get_dc_calls = 0;
g_release_dc_calls = 0;
g_blit_calls = 0;
g_restore_a_calls = 0;
g_restore_b_calls = 0;
g_present_calls = 0;
g_draw_item_calls = 0;
g_draw_score_calls = 0;
g_status_calls = 0;
g_real_mode = REAL_MODE_NONE;
g_real_multiply_calls = 0;
}
static void assert_event_consumed(void)
{
assert(win16_read_u16(win16_far_add_offset(g_event, 0x0a)) == 0);
assert(win16_read_u16(win16_far_add_offset(g_event, 0x0c)) == 0);
}
static void test_debug_keys_and_auxiliary_gate(void)
{
prepare_fixture(0x3c);
win16_write_u8(win16_dgroup_pointer(0x07cf), 1);
win16_write_u32(win16_dgroup_pointer(0x0855), 0, 0x12345678);
assert(tdkpin_handle_key_release(g_window, g_event) == 0);
assert(win16_read_u32(win16_dgroup_pointer(0x0855)) == 0);
assert(win16_read_u8(win16_dgroup_pointer(0x07c7)) == 1);
assert_event_consumed();
prepare_fixture(0x41);
win16_write_u8(win16_dgroup_pointer(0x07cf), 1);
win16_write_u32(win16_dgroup_pointer(0x3340), 0, 0x12345678);
win16_write_u32(win16_dgroup_pointer(0x3344), 0, 0x90abcdef);
win16_write_u32(g_window, 0x0baa, 1);
win16_write_u32(g_window, 0x0bae, 2);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u32(win16_dgroup_pointer(0x07d3)) == 0x12345678);
assert(win16_read_u32(win16_dgroup_pointer(0x07d7)) == 0x90abcdef);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 0);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0bae)) == 0);
prepare_fixture(0x3c);
win16_write_u8(win16_dgroup_pointer(0x07cf), 1);
win16_write_u16(g_window, 0x41, 1);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_dgroup_pointer(0x07c7)) == 0);
assert_event_consumed();
}
static void test_release_down_impulse_clamps(void)
{
static const uint16_t lower_bounds[] = {1000};
static const uint16_t lower_values[] = {80};
prepare_fixture(0x50);
win16_write_u8(win16_dgroup_pointer(0x07d0), 1);
win16_write_u32(g_window, 0x56, 1000);
win16_write_u32(g_window, 0x5a, 2);
win16_write_u32(g_window, 0x0baa, 0x12345678);
win16_write_u32(g_window, 0x0bae, (uint32_t)-2000);
set_random_script(lower_bounds, lower_values, 1);
tdkpin_handle_key_release(g_window, g_event);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == -998);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 0);
assert(win16_read_u32(win16_dgroup_pointer(0x07fb)) == (uint32_t)-998);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdd)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bde)) == 0);
assert(g_random_index == 1);
assert(g_sound_count == 1 && g_sounds[0] == 2);
assert(g_blit_calls == 1 && g_get_dc_calls == 1 && g_release_dc_calls == 1);
static const uint16_t upper_bounds[] = {1000};
static const uint16_t upper_values[] = {80};
prepare_fixture(0x50);
win16_write_u8(win16_dgroup_pointer(0x07d0), 1);
win16_write_u32(g_window, 0x56, 1000);
win16_write_u32(g_window, 0x5a, 2);
win16_write_u32(g_window, 0x0bae, (uint32_t)-399);
g_real_mode = REAL_MODE_UPPER_CLAMP;
set_random_script(upper_bounds, upper_values, 1);
tdkpin_handle_key_release(g_window, g_event);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == -601);
assert(g_real_multiply_calls == 1 && g_random_index == 1);
}
static void test_flipper_key_release(void)
{
prepare_fixture(0x1d);
win16_write_u8(win16_far_add_offset(g_window, 0x0bda), 1);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bda)) == 0);
prepare_fixture(0x1c);
win16_write_u8(win16_far_add_offset(g_window, 0x0bdb), 1);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdb)) == 0);
}
static void test_directional_and_multiball_nudges(void)
{
static const uint16_t left_bounds[] = {20, 10};
static const uint16_t left_values[] = {10, 9};
prepare_fixture(0x2a);
win16_write_u32(g_window, 0x5a, 3);
win16_write_u32(g_window, 0x0baa, 1000);
set_random_script(left_bounds, left_values, 2);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 880);
assert(win16_read_u16(win16_far_add_offset(g_window, 0x5e)) == 25);
assert(g_sound_count == 1 && g_sounds[0] == 19);
static const uint16_t right_bounds[] = {20, 10};
static const uint16_t right_values[] = {10, 9};
prepare_fixture(0x51);
win16_write_u32(g_window, 0x5a, 3);
win16_write_u8(win16_far_add_offset(g_window, 0x61), 2);
win16_write_u32(win16_dgroup_pointer(0x07f3), 0, 1000);
win16_write_u32(win16_dgroup_pointer(0x07f7), 0, 2000);
set_random_script(right_bounds, right_values, 2);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 120);
assert(win16_read_u32(win16_dgroup_pointer(0x07f3)) == 1120);
assert(win16_read_u32(win16_dgroup_pointer(0x07f7)) == 2120);
static const uint16_t space_bounds[] = {20, 21, 100, 10};
static const uint16_t space_values[] = {10, 12, 20, 9};
prepare_fixture(0x39);
win16_write_u32(g_window, 0x5a, 3);
win16_write_u8(win16_far_add_offset(g_window, 0x61), 2);
win16_write_u32(g_window, 0x0baa, 100);
win16_write_u32(g_window, 0x0bae, 200);
win16_write_u32(win16_dgroup_pointer(0x07f3), 0, 1000);
win16_write_u32(win16_dgroup_pointer(0x07f7), 0, 2000);
win16_write_u32(win16_dgroup_pointer(0x07fb), 0, 3000);
win16_write_u32(win16_dgroup_pointer(0x07ff), 0, 4000);
g_real_mode = REAL_MODE_HORIZONTAL_NUDGE;
set_random_script(space_bounds, space_values, 4);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 107);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == 50);
assert(win16_read_u32(win16_dgroup_pointer(0x07f3)) == 1006);
assert(win16_read_u32(win16_dgroup_pointer(0x07f7)) == 2006);
assert(win16_read_u32(win16_dgroup_pointer(0x07fb)) == 2790);
assert(win16_read_u32(win16_dgroup_pointer(0x07ff)) == 3790);
assert(g_real_multiply_calls == 2 && g_random_index == 4);
}
static void test_tilt_threshold(void)
{
static const uint16_t bounds[] = {20, 10};
static const uint16_t values[] = {10, 0};
prepare_fixture(0x2a);
win16_write_u32(g_window, 0x5a, 1);
win16_write_u16(g_window, 0x5e, 14);
win16_write_u8(win16_far_add_offset(g_window, 0x0bda), 1);
win16_write_u8(win16_far_add_offset(g_window, 0x0bdb), 1);
set_random_script(bounds, values, 2);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u16(win16_far_add_offset(g_window, 0x5e)) == 39);
assert(win16_read_u8(win16_dgroup_pointer(0x07c6)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bda)) == 0);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdb)) == 0);
assert(g_sound_count == 2 && g_sounds[0] == 19 && g_sounds[1] == 20);
assert(g_restore_a_calls == 1 && g_present_calls == 1);
assert(g_get_dc_calls == 1 && g_release_dc_calls == 1);
}
static void test_sound_toggle(void)
{
prepare_fixture(0x58);
win16_write_u16(win16_dgroup_pointer(0x43ee), 0, 1);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_dgroup_pointer(0x43f2)) == 1);
prepare_fixture(0x58);
win16_write_u8(win16_dgroup_pointer(0x43f2), 1);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_dgroup_pointer(0x43f2)) == 0);
prepare_fixture(0x58);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_dgroup_pointer(0x43f2)) == 0);
}
static void test_add_player_copies_all_state(void)
{
prepare_fixture(0x4e);
win16_write_u8(win16_far_add_offset(g_window, 0x0bde), 1);
win16_write_u8(win16_dgroup_pointer(0x07d0), 1);
win16_write_u32(win16_dgroup_pointer(0x0982), 0, 0x11111111);
win16_write_u32(win16_dgroup_pointer(0x0986), 0, 0x22222222);
win16_write_u32(win16_dgroup_pointer(0x098a), 0, 0x33333333);
win16_write_u32(win16_dgroup_pointer(0x098e), 0, 0x44444444);
for (uint16_t id = 1; id <= 175; id++) {
win16_write_u8(
win16_far_add_offset(g_window, (uint16_t)(0x03cc + id)),
(uint8_t)(id ^ 0x5a));
win16_write_u16(
g_window,
(uint16_t)(0x0a42 + id * 2u),
(uint16_t)(id * 257u));
}
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_dgroup_pointer(0x07d0)) == 2);
assert(win16_read_u8(win16_dgroup_pointer(0x07c9)) == 0);
assert(win16_read_u8(win16_dgroup_pointer(0x07c5)) == 1);
assert(win16_read_u8(win16_dgroup_pointer(0x0874)) == 0);
assert(win16_read_u8(win16_dgroup_pointer(0x07d1)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x61)) == 1);
assert(g_sound_count == 1 && g_sounds[0] == 1);
for (uint16_t player = 1; player <= 4; player++) {
assert(win16_read_u16(win16_dgroup_pointer(
(uint16_t)(0x081f + player * 2u))) == 0xffff);
for (uint16_t id = 1; id <= 175; id++) {
uint8_t expected_byte = (uint8_t)(id ^ 0x5a);
uint16_t expected_word = (uint16_t)(id * 257u);
assert(win16_read_u8(win16_far_add_offset(
g_window,
(uint16_t)(0x0061 + player * 0x00afu + id))) ==
expected_byte);
assert(win16_read_u16(win16_far_add_offset(
g_window,
(uint16_t)(0x036c + player * 0x015eu + id * 2u))) ==
expected_word);
}
}
for (uint16_t id = 1; id <= 175; id++) {
Win16FarPtr record = win16_dgroup_pointer(
(uint16_t)(0x095b + id * 0x53u));
assert(win16_read_u8(win16_far_add_offset(record, 0x34)) ==
(uint8_t)(id ^ 0x5a));
assert(win16_read_u16(win16_far_add_offset(record, 0x43)) ==
(uint16_t)(id * 257u));
}
assert(win16_read_u32(win16_dgroup_pointer(0x0982)) == 0x11111111);
assert(win16_read_u32(win16_dgroup_pointer(0x0986)) == 0x22222222);
assert(win16_read_u32(win16_dgroup_pointer(0x098a)) == 0);
assert(win16_read_u32(win16_dgroup_pointer(0x098e)) == 0);
assert(g_restore_b_calls == 2 && g_restore_a_calls == 1);
assert(g_present_calls == 3 && g_draw_item_calls == 4);
assert(g_draw_score_calls == 1 && g_status_calls == 1);
assert(g_get_dc_calls == 5 && g_release_dc_calls == 5);
}
int main(void)
{
test_debug_keys_and_auxiliary_gate();
test_release_down_impulse_clamps();
test_flipper_key_release();
test_directional_and_multiball_nudges();
test_tilt_threshold();
test_sound_toggle();
test_add_player_copies_all_state();
return 0;
}