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
306 lines
9.9 KiB
C
306 lines
9.9 KiB
C
#include "../tdkpin_player_state.h"
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
uint8_t g_player_count;
|
|
uint8_t g_current_player;
|
|
|
|
static uint8_t g_stack[0x400];
|
|
static uint8_t g_object[0x0c00];
|
|
static uint8_t g_dgroup[0x5000];
|
|
static Win16FarPtr g_receiver;
|
|
static unsigned g_collision_calls;
|
|
static unsigned g_restore_record_calls;
|
|
static unsigned g_zero_score_calls;
|
|
static unsigned g_add_score_calls;
|
|
static unsigned g_marker_calls;
|
|
static unsigned g_item_calls;
|
|
static unsigned g_restore_a_calls;
|
|
static unsigned g_restore_b_calls;
|
|
static unsigned g_present_calls;
|
|
static unsigned g_sprite_calls;
|
|
static unsigned g_get_dc_calls;
|
|
static unsigned g_release_dc_calls;
|
|
static uint16_t g_last_sprite;
|
|
static int32_t g_last_shown_score;
|
|
static int32_t g_added_delta[4];
|
|
static uint8_t g_add_player[4];
|
|
|
|
void tdkpin_refresh_collision_record(uint16_t caller_bp, uint16_t id)
|
|
{
|
|
assert(caller_bp == 0x0100);
|
|
assert(id == g_collision_calls + 1);
|
|
g_collision_calls++;
|
|
}
|
|
|
|
void tdkpin_restore_record_b(uint16_t caller_bp, uint16_t id)
|
|
{
|
|
assert(caller_bp == 0x0100);
|
|
assert(id == 1001 + g_restore_record_calls);
|
|
g_restore_record_calls++;
|
|
}
|
|
|
|
void tdkpin_draw_score_from_caller_frame(
|
|
uint16_t caller_bp, uint16_t low, uint16_t high)
|
|
{
|
|
assert(caller_bp == 0x0100);
|
|
int32_t value = (int32_t)((uint32_t)low | ((uint32_t)high << 16));
|
|
if (value == 0 && g_zero_score_calls == 0) {
|
|
g_zero_score_calls++;
|
|
} else {
|
|
g_last_shown_score = value;
|
|
}
|
|
}
|
|
|
|
void tdkpin_add_score_from_caller_frame(uint16_t caller_bp, int32_t delta)
|
|
{
|
|
assert(caller_bp == 0x0100 && g_add_score_calls < 4);
|
|
g_added_delta[g_add_score_calls] = delta;
|
|
g_add_player[g_add_score_calls] = g_current_player;
|
|
g_add_score_calls++;
|
|
}
|
|
|
|
void tdkpin_refresh_player_progress_markers(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0100);
|
|
assert(g_current_player == g_marker_calls + 1);
|
|
g_marker_calls++;
|
|
}
|
|
|
|
void tdkpin_refresh_player_item(uint16_t caller_bp, uint16_t item)
|
|
{
|
|
assert(caller_bp == 0x0100 && g_current_player == 1);
|
|
assert(item == g_item_calls + 1);
|
|
g_item_calls++;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void check_geometry(
|
|
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
|
|
{
|
|
assert(dc == 0x3333);
|
|
bool panel = height == 31 && width == 139 && x == 472;
|
|
bool marker = height == 11 && width == 11 && x == 372;
|
|
bool score_area = height == 123 && width == 42 && y == 79 && x == 55;
|
|
assert(panel || marker || score_area);
|
|
if (panel || marker) {
|
|
uint16_t base = panel ? 276 : 280;
|
|
assert(y >= (INT16)base && ((uint16_t)(y - base) % 44u) == 0);
|
|
}
|
|
}
|
|
|
|
void tdkpin_restore_background_region_a(
|
|
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
|
|
{
|
|
check_geometry(height, width, y, x, dc);
|
|
assert(height != 123);
|
|
g_restore_a_calls++;
|
|
}
|
|
|
|
void tdkpin_restore_background_region_b(
|
|
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
|
|
{
|
|
check_geometry(height, width, y, x, dc);
|
|
assert(height == 11);
|
|
g_restore_b_calls++;
|
|
}
|
|
|
|
void tdkpin_present_background_region(
|
|
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
|
|
{
|
|
check_geometry(height, width, y, x, dc);
|
|
g_present_calls++;
|
|
}
|
|
|
|
void tdkpin_composite_player_sprite(uint16_t sprite_index, HDC16 dc)
|
|
{
|
|
assert(dc == 0x3333);
|
|
g_last_sprite = sprite_index;
|
|
g_sprite_calls++;
|
|
}
|
|
|
|
int32_t borland_multiply_i32(int32_t left, int32_t right)
|
|
{
|
|
return (int32_t)((uint32_t)left * (uint32_t)right);
|
|
}
|
|
|
|
static void prepare_fixture(void)
|
|
{
|
|
memset(g_stack, 0, sizeof(g_stack));
|
|
memset(g_object, 0xa5, sizeof(g_object));
|
|
memset(g_dgroup, 0xcc, sizeof(g_dgroup));
|
|
win16_reset_segment_bindings();
|
|
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
|
|
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
|
|
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
|
|
win16_set_dgroup_selector(0x7200);
|
|
win16_set_stack_state(0x7000, 0);
|
|
g_receiver = win16_make_far_pointer(0x7100, 0);
|
|
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
|
|
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
|
|
win16_write_u16(g_receiver, 4, 0x2222);
|
|
g_collision_calls = 0;
|
|
g_restore_record_calls = 0;
|
|
g_zero_score_calls = 0;
|
|
g_add_score_calls = 0;
|
|
g_marker_calls = 0;
|
|
g_item_calls = 0;
|
|
g_restore_a_calls = 0;
|
|
g_restore_b_calls = 0;
|
|
g_present_calls = 0;
|
|
g_sprite_calls = 0;
|
|
g_get_dc_calls = 0;
|
|
g_release_dc_calls = 0;
|
|
g_last_sprite = 0;
|
|
g_last_shown_score = 0;
|
|
memset(g_added_delta, 0, sizeof(g_added_delta));
|
|
memset(g_add_player, 0, sizeof(g_add_player));
|
|
}
|
|
|
|
static void test_initialize_player_state(void)
|
|
{
|
|
prepare_fixture();
|
|
g_player_count = 2;
|
|
g_current_player = 4;
|
|
for (uint16_t id = 1; id <= 175; id++) {
|
|
win16_write_u16(
|
|
win16_dgroup_pointer((uint16_t)(
|
|
0x095b + id * 0x53u + 0x43u)),
|
|
0,
|
|
(uint16_t)(0x1000 + id));
|
|
}
|
|
tdkpin_initialize_player_state(0x0100);
|
|
assert(g_collision_calls == 175 && g_restore_record_calls == 5);
|
|
assert(g_zero_score_calls == 1);
|
|
for (uint16_t player = 1; player <= 4; player++) {
|
|
for (uint16_t id = 1; id <= 175; id++) {
|
|
assert(win16_read_u16(win16_far_add_offset(
|
|
g_receiver,
|
|
(uint16_t)(0x036c + player * 0x015eu + id * 2u))) ==
|
|
(uint16_t)(0x1000 + id));
|
|
}
|
|
}
|
|
assert(g_add_score_calls == 2 && g_marker_calls == 2);
|
|
assert(g_add_player[0] == 1 && g_add_player[1] == 2);
|
|
assert(g_added_delta[0] == 0 && g_added_delta[1] == 0);
|
|
for (uint16_t player = 1; player <= 2; player++) {
|
|
assert(win16_read_u32(win16_dgroup_pointer(
|
|
(uint16_t)(0x097e + player * 4u))) == 0);
|
|
assert(win16_read_u32(win16_dgroup_pointer(
|
|
(uint16_t)(0x098e + player * 4u))) == 0);
|
|
assert(win16_read_u8(win16_dgroup_pointer(
|
|
(uint16_t)(0x09a1 + player))) == 1);
|
|
assert(win16_read_u16(win16_dgroup_pointer(
|
|
(uint16_t)(0x09a4 + player * 2u))) == 0);
|
|
assert(win16_read_u8(win16_dgroup_pointer(
|
|
(uint16_t)(0x0816 + player))) == 1);
|
|
assert(win16_read_u16(win16_dgroup_pointer(
|
|
(uint16_t)(0x081f + player * 2u))) ==
|
|
(player == 1 ? 2 : 3));
|
|
assert(win16_read_u32(win16_dgroup_pointer(
|
|
(uint16_t)(0x0835 + player * 4u))) == 1);
|
|
assert(win16_read_u8(win16_dgroup_pointer(
|
|
(uint16_t)(0x081a + player))) == 1);
|
|
for (uint16_t item = 1; item <= 20; item++) {
|
|
assert(win16_read_u8(win16_far_add_offset(
|
|
g_receiver,
|
|
(uint16_t)(0x0467 + player * 0x14u + item))) == 0);
|
|
}
|
|
}
|
|
assert(g_current_player == 1 && g_item_calls == 20);
|
|
assert(win16_read_u8(win16_dgroup_pointer(0x07d2)) == 0);
|
|
assert(win16_read_u8(win16_dgroup_pointer(0x0874)) == 0);
|
|
}
|
|
|
|
static void test_redraw_all_players(void)
|
|
{
|
|
prepare_fixture();
|
|
g_player_count = 3;
|
|
g_current_player = 2;
|
|
win16_write_u32(win16_dgroup_pointer(0x0996), 0, 10);
|
|
win16_write_u8(win16_dgroup_pointer(0x09a3), 3);
|
|
win16_write_u16(win16_dgroup_pointer(0x09a8), 0, 5);
|
|
tdkpin_redraw_all_players(0x0100);
|
|
assert(g_add_score_calls == 3);
|
|
assert(g_add_player[0] == 1 && g_added_delta[0] == -1);
|
|
assert(g_add_player[1] == 2 && g_added_delta[1] == 0);
|
|
assert(g_add_player[2] == 3 && g_added_delta[2] == -1);
|
|
assert(g_last_shown_score == 30);
|
|
assert(g_sprite_calls == 1 && g_last_sprite == 5);
|
|
assert(g_restore_a_calls == 4 && g_restore_b_calls == 2);
|
|
assert(g_present_calls == 7);
|
|
assert(g_current_player == 2);
|
|
assert(g_get_dc_calls == 1 && g_release_dc_calls == 1);
|
|
}
|
|
|
|
static void set_effect(uint16_t effect)
|
|
{
|
|
win16_write_u16(
|
|
win16_dgroup_pointer((uint16_t)(0x09a4 + g_current_player * 2u)),
|
|
0,
|
|
effect);
|
|
}
|
|
|
|
static void test_player_sprite_effects(void)
|
|
{
|
|
static const uint32_t additions[5] = {
|
|
10000u, 20000u, 50000u, 100000u, 200000u};
|
|
for (uint16_t effect = 1; effect <= 5; effect++) {
|
|
prepare_fixture();
|
|
g_current_player = 2;
|
|
set_effect(effect);
|
|
win16_write_u32(win16_dgroup_pointer(0x0996), 0, 0xfffffff0u);
|
|
win16_write_u8(win16_dgroup_pointer(0x09a3), 3);
|
|
tdkpin_apply_player_sprite_effect(0x0100, 0);
|
|
uint32_t expected = 0xfffffff0u + additions[effect - 1u];
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x0996)) == expected);
|
|
assert((uint32_t)g_last_shown_score == expected * 3u);
|
|
assert(g_add_score_calls == 0);
|
|
}
|
|
|
|
prepare_fixture();
|
|
g_current_player = 2;
|
|
set_effect(6);
|
|
win16_write_u32(win16_dgroup_pointer(0x0996), 0, 12345);
|
|
win16_write_u8(win16_dgroup_pointer(0x09a3), 4);
|
|
tdkpin_apply_player_sprite_effect(0x0100, 0);
|
|
assert(g_add_score_calls == 1 && g_added_delta[0] == 49380);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x0996)) == 0);
|
|
assert(g_last_shown_score == 0);
|
|
|
|
prepare_fixture();
|
|
g_current_player = 2;
|
|
set_effect(7);
|
|
win16_write_u16(win16_dgroup_pointer(0x399a), 0, 55);
|
|
win16_write_u8(win16_dgroup_pointer(0x0874), 0);
|
|
win16_write_u8(win16_far_add_offset(g_receiver, 0x0be0), 0);
|
|
tdkpin_apply_player_sprite_effect(0x0100, 55);
|
|
assert(win16_read_u8(win16_dgroup_pointer(0x0874)) == 0);
|
|
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x0be0)) == 0);
|
|
tdkpin_apply_player_sprite_effect(0x0100, -1);
|
|
assert(win16_read_u8(win16_dgroup_pointer(0x0874)) == 1);
|
|
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x0be0)) == 1);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
test_initialize_player_state();
|
|
test_redraw_all_players();
|
|
test_player_sprite_effects();
|
|
return 0;
|
|
}
|