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
179 lines
7.0 KiB
C
179 lines
7.0 KiB
C
/* Player initialization and multi-player redraw paths from TDKPIN Code1. */
|
|
|
|
#include "tdkpin_player_state.h"
|
|
|
|
#include "tdkpin_arithmetic.h"
|
|
|
|
static Win16FarPtr caller_receiver(uint16_t caller_bp)
|
|
{
|
|
uint16_t offset = win16_read_stack_u16((uint16_t)(caller_bp + 6));
|
|
uint16_t selector = win16_read_stack_u16((uint16_t)(caller_bp + 8));
|
|
return win16_make_far_pointer(selector, offset);
|
|
}
|
|
|
|
/*
|
|
* 1000:a72c -- initialize collision mirrors and all configured player state.
|
|
* The collision field at record +43 is copied into four receiver rows even
|
|
* when fewer than four players are configured, exactly as the binary does.
|
|
*/
|
|
void tdkpin_initialize_player_state(uint16_t caller_bp)
|
|
{
|
|
Win16FarPtr receiver = caller_receiver(caller_bp);
|
|
for (uint16_t record_id = 1; record_id <= 175; record_id++) {
|
|
tdkpin_refresh_collision_record(caller_bp, record_id);
|
|
uint16_t mirrored = win16_read_u16(win16_dgroup_pointer((uint16_t)(
|
|
0x095b + record_id * 0x53u + 0x43u)));
|
|
for (uint16_t player = 1; player <= 4; player++) {
|
|
win16_write_u16(
|
|
receiver,
|
|
(uint16_t)(0x036c + player * 0x015eu + record_id * 2u),
|
|
mirrored);
|
|
}
|
|
}
|
|
for (uint16_t item = 1; item <= 5; item++) {
|
|
tdkpin_restore_record_b(caller_bp, (uint16_t)(1000 + item));
|
|
}
|
|
tdkpin_draw_score_from_caller_frame(caller_bp, 0, 0);
|
|
win16_write_u8(win16_dgroup_pointer(0x0874), 0);
|
|
|
|
for (uint8_t player = 1; player <= 4; player++) {
|
|
if (player <= g_player_count) {
|
|
g_current_player = player;
|
|
win16_write_u32(
|
|
win16_dgroup_pointer((uint16_t)(0x097e + player * 4u)),
|
|
0,
|
|
0);
|
|
win16_write_u32(
|
|
win16_dgroup_pointer((uint16_t)(0x098e + player * 4u)),
|
|
0,
|
|
0);
|
|
win16_write_u8(
|
|
win16_dgroup_pointer((uint16_t)(0x09a1 + player)), 1);
|
|
win16_write_u16(
|
|
win16_dgroup_pointer((uint16_t)(0x09a4 + player * 2u)),
|
|
0,
|
|
0);
|
|
tdkpin_add_score_from_caller_frame(caller_bp, 0);
|
|
win16_write_u8(
|
|
win16_dgroup_pointer((uint16_t)(0x0816 + player)), 1);
|
|
win16_write_u16(
|
|
win16_dgroup_pointer((uint16_t)(0x081f + player * 2u)),
|
|
0,
|
|
player < 2 ? 2 : 3);
|
|
tdkpin_refresh_player_progress_markers(caller_bp);
|
|
win16_write_u32(
|
|
win16_dgroup_pointer((uint16_t)(0x0835 + player * 4u)),
|
|
0,
|
|
1);
|
|
win16_write_u8(
|
|
win16_dgroup_pointer((uint16_t)(0x081a + player)), 1);
|
|
for (uint16_t item = 1; item <= 20; item++) {
|
|
win16_write_u8(
|
|
win16_far_add_offset(receiver, (uint16_t)(
|
|
0x0467 + player * 0x14u + item)),
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
|
|
g_current_player = 1;
|
|
win16_write_u8(win16_dgroup_pointer(0x07d2), 0);
|
|
for (uint16_t item = 1; item <= 20; item++) {
|
|
tdkpin_refresh_player_item(caller_bp, item);
|
|
}
|
|
}
|
|
|
|
/* 1000:acc2 -- redraw each configured player and emphasize the active row. */
|
|
void tdkpin_redraw_all_players(uint16_t caller_bp)
|
|
{
|
|
Win16FarPtr receiver = caller_receiver(caller_bp);
|
|
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
|
|
HDC16 dc = GetDC16(window);
|
|
uint8_t saved_player = g_current_player;
|
|
uint8_t count = g_player_count;
|
|
for (uint8_t player = 1; player <= count; player++) {
|
|
INT16 row_y = (INT16)((uint16_t)(player - 1u) * 44u + 276u);
|
|
tdkpin_restore_background_region_a(31, 139, row_y, 472, dc);
|
|
tdkpin_present_background_region(31, 139, row_y, 472, dc);
|
|
g_current_player = player;
|
|
INT16 marker_y = (INT16)((uint16_t)(player - 1u) * 44u + 280u);
|
|
if (player == saved_player) {
|
|
tdkpin_add_score_from_caller_frame(caller_bp, 0);
|
|
uint32_t secondary = win16_read_u32(
|
|
win16_dgroup_pointer((uint16_t)(0x098e + player * 4u)));
|
|
uint8_t multiplier = win16_read_u8(
|
|
win16_dgroup_pointer((uint16_t)(0x09a1 + player)));
|
|
int32_t shown = borland_multiply_i32(
|
|
(int32_t)secondary, multiplier);
|
|
tdkpin_draw_score_from_caller_frame(
|
|
caller_bp, (uint16_t)shown, (uint16_t)((uint32_t)shown >> 16));
|
|
uint16_t sprite = win16_read_u16(
|
|
win16_dgroup_pointer((uint16_t)(0x09a4 + player * 2u)));
|
|
tdkpin_composite_player_sprite(sprite, dc);
|
|
tdkpin_present_background_region(123, 42, 79, 55, dc);
|
|
tdkpin_restore_background_region_a(11, 11, marker_y, 372, dc);
|
|
} else {
|
|
tdkpin_add_score_from_caller_frame(caller_bp, -1);
|
|
tdkpin_restore_background_region_b(11, 11, marker_y, 372, dc);
|
|
}
|
|
tdkpin_present_background_region(11, 11, marker_y, 372, dc);
|
|
}
|
|
g_current_player = saved_player;
|
|
(void)ReleaseDC16(window, dc);
|
|
}
|
|
|
|
static Win16FarPtr active_secondary_score(void)
|
|
{
|
|
return win16_dgroup_pointer((uint16_t)(
|
|
0x098e + (uint16_t)g_current_player * 4u));
|
|
}
|
|
|
|
static uint8_t active_score_multiplier(void)
|
|
{
|
|
return win16_read_u8(win16_dgroup_pointer((uint16_t)(
|
|
0x09a1 + (uint16_t)g_current_player)));
|
|
}
|
|
|
|
static void draw_active_secondary_score(uint16_t caller_bp)
|
|
{
|
|
int32_t shown = borland_multiply_i32(
|
|
(int32_t)win16_read_u32(active_secondary_score()),
|
|
active_score_multiplier());
|
|
tdkpin_draw_score_from_caller_frame(
|
|
caller_bp, (uint16_t)shown, (uint16_t)((uint32_t)shown >> 16));
|
|
}
|
|
|
|
/* 1000:c4e1 -- apply the effect selected by the active player's sprite index. */
|
|
void tdkpin_apply_player_sprite_effect(
|
|
uint16_t caller_bp, int16_t argument)
|
|
{
|
|
uint16_t effect = win16_read_u16(win16_dgroup_pointer((uint16_t)(
|
|
0x09a4 + (uint16_t)g_current_player * 2u)));
|
|
static const uint32_t additions[5] = {
|
|
10000u, 20000u, 50000u, 100000u, 200000u};
|
|
if (effect >= 1 && effect <= 5) {
|
|
Win16FarPtr secondary = active_secondary_score();
|
|
win16_write_u32(
|
|
secondary,
|
|
0,
|
|
win16_read_u32(secondary) + additions[effect - 1u]);
|
|
draw_active_secondary_score(caller_bp);
|
|
} else if (effect == 6) {
|
|
int32_t transfer = borland_multiply_i32(
|
|
(int32_t)win16_read_u32(active_secondary_score()),
|
|
active_score_multiplier());
|
|
tdkpin_add_score_from_caller_frame(caller_bp, transfer);
|
|
win16_write_u32(active_secondary_score(), 0, 0);
|
|
tdkpin_draw_score_from_caller_frame(caller_bp, 0, 0);
|
|
} else if (effect == 7) {
|
|
uint16_t required = win16_read_u16(
|
|
win16_dgroup_pointer(0x399a));
|
|
if (required != 0 &&
|
|
(argument < 0 || required != (uint16_t)argument)) {
|
|
Win16FarPtr receiver = caller_receiver(caller_bp);
|
|
win16_write_u8(win16_far_add_offset(receiver, 0x0be0), 1);
|
|
win16_write_u8(win16_dgroup_pointer(0x0874), 1);
|
|
}
|
|
}
|
|
}
|