Files
tdkpin/original/reconstructed/tdkpin_gameplay_helpers.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

859 lines
32 KiB
C

/* Small address-linked gameplay rendering and transition helpers. */
#include "tdkpin_gameplay_helpers.h"
#include "tdkpin_arithmetic.h"
#include "tdkpin_sound.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);
}
static uint16_t caller_stack_word(uint16_t caller_bp, int16_t displacement)
{
return win16_read_stack_u16((uint16_t)(caller_bp + displacement));
}
static void write_caller_stack_word(
uint16_t caller_bp, int16_t displacement, uint16_t value)
{
win16_write_stack_u16((uint16_t)(caller_bp + displacement), value);
}
/* 1000:635c -- update the backing bitmap and immediately present it. */
void tdkpin_blit_bitmap_and_present(
HBITMAP16 bitmap,
INT16 height,
INT16 width,
INT16 y,
INT16 x,
HDC16 dc)
{
tdkpin_blit_bitmap_to_background(bitmap, height, width, y, x, dc);
tdkpin_present_background_region(height, width, y, x, dc);
}
/*
* 1000:6b37 -- nested helper used by 6bf8 to composite one 21x18 board
* decoration row. Its first argument is the caller's BP, so the four GDI
* temporaries deliberately occupy SS:[BP-0e..BP-14] in the parent frame.
*/
void tdkpin_draw_board_decoration_row(uint16_t caller_bp, INT16 y)
{
HDC16 target_dc = caller_stack_word(caller_bp, -0x0c);
HDC16 background_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x0e, background_dc);
HDC16 decoration_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x10, decoration_dc);
HBITMAP16 background = win16_read_u16(
win16_dgroup_pointer(0x085b));
HGDIOBJ16 old_background = SelectObject16(background_dc, background);
write_caller_stack_word(caller_bp, -0x12, old_background);
HBITMAP16 decoration = win16_read_u16(
win16_dgroup_pointer(0x0865));
HGDIOBJ16 old_decoration = SelectObject16(decoration_dc, decoration);
write_caller_stack_word(caller_bp, -0x14, old_decoration);
(void)BitBlt16(
background_dc, 0, y, 21, 18,
decoration_dc, 314, 426, 0x00cc0020u);
(void)SelectObject16(
decoration_dc, caller_stack_word(caller_bp, -0x14));
(void)SelectObject16(
background_dc, caller_stack_word(caller_bp, -0x12));
(void)DeleteDC16(decoration_dc);
(void)DeleteDC16(background_dc);
tdkpin_present_background_region(18, 21, y, 0, target_dc);
}
/* Same binary operation when the enclosing 6bf8 frame already owns the DC. */
void tdkpin_draw_board_decoration_row_on_dc(INT16 y, HDC16 dc)
{
HDC16 background_dc = CreateCompatibleDC16(dc);
HDC16 decoration_dc = CreateCompatibleDC16(dc);
HGDIOBJ16 old_background = SelectObject16(
background_dc, win16_read_u16(win16_dgroup_pointer(0x085b)));
HGDIOBJ16 old_decoration = SelectObject16(
decoration_dc, win16_read_u16(win16_dgroup_pointer(0x0865)));
(void)BitBlt16(
background_dc, 0, y, 21, 18,
decoration_dc, 314, 426, 0x00cc0020u);
(void)SelectObject16(decoration_dc, old_decoration);
(void)SelectObject16(background_dc, old_background);
(void)DeleteDC16(decoration_dc);
(void)DeleteDC16(background_dc);
tdkpin_present_background_region(18, 21, y, 0, dc);
}
/*
* 1000:9bca -- composite the 19x19 ball mask/image over the background and
* present the clipped result. All locals live in the enclosing caller frame.
*/
void tdkpin_render_ball_from_caller_frame(uint16_t caller_bp)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
(void)GetClientRect16(
window,
win16_current_stack_address((uint16_t)(caller_bp - 0x1a)));
HDC16 target_dc = GetDC16(window);
write_caller_stack_word(caller_bp, -0x0c, target_dc);
HDC16 background_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x0e, background_dc);
HDC16 composite_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x10, composite_dc);
HDC16 unused_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x12, unused_dc);
HBITMAP16 composite_bitmap =
CreateCompatibleBitmap16(target_dc, 100, 100);
write_caller_stack_word(caller_bp, -0x08, composite_bitmap);
int32_t pixel_x = borland_divide_i32(
(int32_t)((uint32_t)g_ball_x_milli + 500u), 1000).quotient - 8;
int32_t pixel_y = borland_divide_i32(
(int32_t)((uint32_t)g_ball_y_milli + 500u), 1000).quotient - 8;
win16_write_u32(receiver, 0x0ba2, (uint32_t)pixel_x);
win16_write_u32(receiver, 0x0ba6, (uint32_t)pixel_y);
INT16 destination_x = (INT16)(pixel_x - 1);
INT16 destination_y = (INT16)(pixel_y - 1);
INT16 source_x = 1;
INT16 source_y = 1;
INT16 width = 19;
INT16 height = 19;
write_caller_stack_word(caller_bp, -0x20, (uint16_t)destination_x);
write_caller_stack_word(caller_bp, -0x1c, (uint16_t)source_x);
write_caller_stack_word(caller_bp, -0x22, (uint16_t)destination_y);
write_caller_stack_word(caller_bp, -0x1e, (uint16_t)source_y);
write_caller_stack_word(caller_bp, -0x28, (uint16_t)width);
write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height);
HBITMAP16 background = win16_read_u16(
win16_dgroup_pointer(0x085b));
HGDIOBJ16 old_background = SelectObject16(background_dc, background);
write_caller_stack_word(caller_bp, -0x02, old_background);
HGDIOBJ16 old_composite = SelectObject16(composite_dc, composite_bitmap);
write_caller_stack_word(caller_bp, -0x04, old_composite);
(void)BitBlt16(
composite_dc, 0, 0, width, height,
background_dc, destination_x, destination_y, 0x00cc0020u);
(void)SelectObject16(
background_dc,
win16_read_u16(win16_far_add_offset(receiver, 0x54)));
(void)BitBlt16(
composite_dc, source_x, source_y, width, height,
background_dc, 0, 0, 0x008800c6u);
(void)SelectObject16(
background_dc,
win16_read_u16(win16_far_add_offset(receiver, 0x52)));
(void)BitBlt16(
composite_dc, source_x, source_y, width, height,
background_dc, 0, 0, 0x00ee0086u);
if ((int16_t)(destination_y + height) > 442) {
height = destination_y <= 442
? (INT16)(443 - destination_y)
: 0;
write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height);
}
(void)BitBlt16(
target_dc, destination_x, destination_y, width, height,
composite_dc, 0, 0, 0x00cc0020u);
(void)SelectObject16(
background_dc, caller_stack_word(caller_bp, -0x02));
(void)SelectObject16(
composite_dc, caller_stack_word(caller_bp, -0x04));
(void)SelectObject16(
unused_dc, caller_stack_word(caller_bp, -0x06));
(void)DeleteObject16(composite_bitmap);
(void)DeleteDC16(background_dc);
(void)DeleteDC16(composite_dc);
(void)DeleteDC16(unused_dc);
(void)ReleaseDC16(window, target_dc);
}
/*
* 1000:a38e -- restore the background under the receiver's previous ball
* position. The low byte of the second ABI word suppresses presentation.
*/
void tdkpin_restore_previous_ball_region(
uint16_t caller_bp, uint8_t suppress_present)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
(void)GetClientRect16(
window,
win16_current_stack_address((uint16_t)(caller_bp - 0x1a)));
HDC16 target_dc = GetDC16(window);
write_caller_stack_word(caller_bp, -0x0c, target_dc);
HDC16 background_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x0e, background_dc);
HDC16 saved_region_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x10, saved_region_dc);
HDC16 unused_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x12, unused_dc);
HBITMAP16 saved_region = CreateCompatibleBitmap16(target_dc, 100, 100);
write_caller_stack_word(caller_bp, -0x08, saved_region);
int32_t milli_x = (int32_t)win16_read_u32(
win16_far_add_offset(receiver, 0x0bc2));
int32_t milli_y = (int32_t)win16_read_u32(
win16_far_add_offset(receiver, 0x0bc6));
int32_t pixel_x = borland_divide_i32(
(int32_t)((uint32_t)milli_x + 500u), 1000).quotient - 8;
int32_t pixel_y = borland_divide_i32(
(int32_t)((uint32_t)milli_y + 500u), 1000).quotient - 8;
win16_write_u32(receiver, 0x0ba2, (uint32_t)pixel_x);
win16_write_u32(receiver, 0x0ba6, (uint32_t)pixel_y);
INT16 destination_x = (INT16)(pixel_x - 1);
INT16 destination_y = (INT16)(pixel_y - 1);
INT16 width = 19;
INT16 height = 19;
write_caller_stack_word(caller_bp, -0x20, (uint16_t)destination_x);
write_caller_stack_word(caller_bp, -0x1c, 1);
write_caller_stack_word(caller_bp, -0x22, (uint16_t)destination_y);
write_caller_stack_word(caller_bp, -0x1e, 1);
write_caller_stack_word(caller_bp, -0x28, (uint16_t)width);
write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height);
win16_write_u32(receiver, 0x0baa, 0);
win16_write_u32(receiver, 0x0bae, 0);
HGDIOBJ16 old_background = SelectObject16(
background_dc,
win16_read_u16(win16_dgroup_pointer(0x085b)));
write_caller_stack_word(caller_bp, -0x02, old_background);
HGDIOBJ16 old_saved_region = SelectObject16(saved_region_dc, saved_region);
write_caller_stack_word(caller_bp, -0x04, old_saved_region);
(void)BitBlt16(
saved_region_dc, 0, 0, width, height,
background_dc, destination_x, destination_y, 0x00cc0020u);
if ((int16_t)(destination_y + height) > 442) {
height = destination_y <= 442
? (INT16)(443 - destination_y)
: 0;
write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height);
}
if (suppress_present == 0) {
(void)BitBlt16(
target_dc, destination_x, destination_y, width, height,
saved_region_dc, 0, 0, 0x00cc0020u);
}
(void)SelectObject16(
background_dc, caller_stack_word(caller_bp, -0x02));
(void)SelectObject16(
saved_region_dc, caller_stack_word(caller_bp, -0x04));
(void)SelectObject16(
unused_dc, caller_stack_word(caller_bp, -0x06));
(void)DeleteObject16(saved_region);
(void)DeleteDC16(background_dc);
(void)DeleteDC16(saved_region_dc);
(void)DeleteDC16(unused_dc);
(void)ReleaseDC16(window, target_dc);
}
static uint32_t wrapping_magnitude_i32(int32_t value)
{
return value < 0 ? 0u - (uint32_t)value : (uint32_t)value;
}
/*
* 1000:9f73 -- composite and present the swept rectangle between the previous
* and current ball positions, retaining signed delta and 16-bit clip rules.
*/
void tdkpin_render_ball_swept_region(uint16_t caller_bp)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
(void)GetClientRect16(
window,
win16_current_stack_address((uint16_t)(caller_bp - 0x1a)));
HDC16 target_dc = GetDC16(window);
write_caller_stack_word(caller_bp, -0x0c, target_dc);
HDC16 background_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x0e, background_dc);
HDC16 swept_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x10, swept_dc);
HDC16 unused_dc = CreateCompatibleDC16(target_dc);
write_caller_stack_word(caller_bp, -0x12, unused_dc);
HBITMAP16 swept_bitmap = CreateCompatibleBitmap16(target_dc, 100, 100);
write_caller_stack_word(caller_bp, -0x08, swept_bitmap);
int32_t previous_x = (int32_t)win16_read_u32(
win16_far_add_offset(receiver, 0x0bc2));
int32_t previous_y = (int32_t)win16_read_u32(
win16_far_add_offset(receiver, 0x0bc6));
int32_t pixel_x = borland_divide_i32(
(int32_t)((uint32_t)g_ball_x_milli + 500u), 1000).quotient - 8;
int32_t pixel_y = borland_divide_i32(
(int32_t)((uint32_t)g_ball_y_milli + 500u), 1000).quotient - 8;
win16_write_u32(receiver, 0x0ba2, (uint32_t)pixel_x);
win16_write_u32(receiver, 0x0ba6, (uint32_t)pixel_y);
int32_t delta_x = borland_divide_i32(
(int32_t)((uint32_t)g_ball_x_milli - (uint32_t)previous_x),
1000).quotient;
int32_t delta_y = borland_divide_i32(
(int32_t)((uint32_t)g_ball_y_milli - (uint32_t)previous_y),
1000).quotient;
win16_write_u32(receiver, 0x0bb2, (uint32_t)delta_x);
win16_write_u32(receiver, 0x0bb6, (uint32_t)delta_y);
INT16 destination_x;
INT16 source_x;
if (delta_x > 0) {
destination_x = (INT16)((uint32_t)pixel_x - (uint32_t)delta_x - 1u);
source_x = (INT16)((uint32_t)delta_x + 1u);
} else {
destination_x = (INT16)((uint32_t)pixel_x - 1u);
source_x = 1;
}
INT16 destination_y;
INT16 source_y;
if (delta_y > 0) {
destination_y = (INT16)((uint32_t)pixel_y - (uint32_t)delta_y - 1u);
source_y = (INT16)((uint32_t)delta_y + 1u);
} else {
destination_y = (INT16)((uint32_t)pixel_y - 1u);
source_y = 1;
}
INT16 width = (INT16)(uint16_t)(wrapping_magnitude_i32(delta_x) + 19u);
INT16 height = (INT16)(uint16_t)(wrapping_magnitude_i32(delta_y) + 19u);
write_caller_stack_word(caller_bp, -0x20, (uint16_t)destination_x);
write_caller_stack_word(caller_bp, -0x1c, (uint16_t)source_x);
write_caller_stack_word(caller_bp, -0x22, (uint16_t)destination_y);
write_caller_stack_word(caller_bp, -0x1e, (uint16_t)source_y);
write_caller_stack_word(caller_bp, -0x28, (uint16_t)width);
write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height);
HGDIOBJ16 old_background = SelectObject16(
background_dc,
win16_read_u16(win16_dgroup_pointer(0x085b)));
write_caller_stack_word(caller_bp, -0x02, old_background);
HGDIOBJ16 old_swept = SelectObject16(swept_dc, swept_bitmap);
write_caller_stack_word(caller_bp, -0x04, old_swept);
(void)BitBlt16(
swept_dc, 0, 0, width, height,
background_dc, destination_x, destination_y, 0x00cc0020u);
(void)SelectObject16(
background_dc,
win16_read_u16(win16_far_add_offset(receiver, 0x54)));
(void)BitBlt16(
swept_dc, source_x, source_y, 19, 19,
background_dc, 0, 0, 0x008800c6u);
(void)SelectObject16(
background_dc,
win16_read_u16(win16_far_add_offset(receiver, 0x52)));
(void)BitBlt16(
swept_dc, source_x, source_y, 19, 19,
background_dc, 0, 0, 0x00ee0086u);
if ((int16_t)(destination_y + height) > 442) {
height = destination_y <= 442
? (INT16)(443 - destination_y)
: 0;
write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height);
}
(void)BitBlt16(
target_dc, destination_x, destination_y, width, height,
swept_dc, 0, 0, 0x00cc0020u);
(void)SelectObject16(
background_dc, caller_stack_word(caller_bp, -0x02));
(void)SelectObject16(swept_dc, caller_stack_word(caller_bp, -0x04));
(void)SelectObject16(unused_dc, caller_stack_word(caller_bp, -0x06));
(void)DeleteObject16(swept_bitmap);
(void)DeleteDC16(background_dc);
(void)DeleteDC16(swept_dc);
(void)DeleteDC16(unused_dc);
(void)ReleaseDC16(window, target_dc);
}
/*
* 1000:beff -- retain the current position, apply wrapping milli-coordinate
* deltas (Y in the final four-byte ABI argument), then render the swept move.
*/
void tdkpin_move_ball_and_render(
uint16_t caller_bp, int32_t delta_x, int32_t delta_y)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
win16_write_u32(receiver, 0x0bc2, (uint32_t)g_ball_x_milli);
win16_write_u32(receiver, 0x0bc6, (uint32_t)g_ball_y_milli);
g_ball_y_milli = (int32_t)((uint32_t)g_ball_y_milli + (uint32_t)delta_y);
g_ball_x_milli = (int32_t)((uint32_t)g_ball_x_milli + (uint32_t)delta_x);
tdkpin_render_ball_swept_region(caller_bp);
}
/* 1000:9e85 -- reset ball/game fields, then redraw through 1000:9bca. */
void tdkpin_reset_ball_state(uint16_t caller_bp)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
static const uint16_t completion_words[5] = {
0x3371, 0x33c4, 0x3417, 0x346a, 0x34bd};
bool complete = true;
for (uint16_t index = 0; index < 5; index++) {
if (win16_read_u16(
win16_dgroup_pointer(completion_words[index])) == 0) {
complete = false;
break;
}
}
if (complete) {
win16_write_u16(win16_dgroup_pointer(0x0851), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x0853), 0, 0);
win16_write_u8(win16_dgroup_pointer(0x07c8), 1);
win16_write_u8(win16_far_add_offset(receiver, 0x0bdd), 0);
}
win16_write_u8(win16_dgroup_pointer(0x07c6), 0);
win16_write_u16(receiver, 0x5e, 0);
g_ball_x_milli = 325000;
g_ball_y_milli = 413000;
win16_write_u32(receiver, 0x0bba, (uint32_t)g_ball_x_milli);
win16_write_u32(receiver, 0x0bbe, (uint32_t)g_ball_y_milli);
win16_write_u32(receiver, 0x0bc2, (uint32_t)g_ball_x_milli);
win16_write_u32(receiver, 0x0bc6, (uint32_t)g_ball_y_milli);
win16_write_u32(receiver, 0x0bae, 0);
win16_write_u32(receiver, 0x0baa, 0);
win16_write_u16(receiver, 0x0bce, 0);
win16_write_u16(receiver, 0x0bd0, 0);
win16_write_u16(receiver, 0x0bd2, 0);
win16_write_u32(receiver, 0x0bca, 0);
win16_write_u8(win16_far_add_offset(receiver, 0x0bdd), 0);
tdkpin_render_ball_from_caller_frame(caller_bp);
}
/* 1000:a604 -- redraw the active player's six five-pixel progress markers. */
void tdkpin_refresh_player_progress_markers(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);
INT16 threshold = (INT16)win16_read_u16(
win16_dgroup_pointer((uint16_t)(
0x081f + (uint16_t)g_current_player * 2u)));
INT16 player_y = (INT16)(
(uint16_t)(g_current_player - 1u) * 44u + 298u);
for (int32_t marker = 1; marker <= 6; marker++) {
INT16 marker_x = (INT16)(
(uint16_t)borland_multiply_i32(marker - 1, 8) + 391u);
if (marker <= threshold) {
tdkpin_restore_background_region_a(
5, 5, player_y, marker_x, dc);
} else {
tdkpin_restore_background_region_b(
5, 5, player_y, marker_x, dc);
}
tdkpin_present_background_region(
5, 5, player_y, marker_x, dc);
}
(void)ReleaseDC16(window, dc);
}
/* 1000:0002 -- composite one indexed 400-series sprite into the background. */
void tdkpin_composite_player_sprite(
uint16_t sprite_index, HDC16 reference_dc)
{
HDC16 background_dc = CreateCompatibleDC16(reference_dc);
HDC16 sprite_dc = CreateCompatibleDC16(reference_dc);
HDC16 saved_region_dc = CreateCompatibleDC16(reference_dc);
HBITMAP16 saved_region =
CreateCompatibleBitmap16(reference_dc, 14, 14);
HGDIOBJ16 old_background = SelectObject16(
background_dc,
win16_read_u16(win16_dgroup_pointer(0x085b)));
HGDIOBJ16 old_sprite = SelectObject16(
sprite_dc,
win16_read_u16(win16_dgroup_pointer((uint16_t)(
0x430b + sprite_index * 2u))));
HGDIOBJ16 old_saved_region =
SelectObject16(saved_region_dc, saved_region);
(void)BitBlt16(
saved_region_dc, 0, 0, 14, 14,
background_dc, 84, 77, 0x00cc0020u);
(void)BitBlt16(
background_dc, 55, 79, 42, 123,
sprite_dc, 0, 0, 0x00cc0020u);
(void)BitBlt16(
background_dc, 84, 77, 14, 14,
saved_region_dc, 0, 0, 0x00cc0020u);
(void)SelectObject16(saved_region_dc, old_saved_region);
(void)SelectObject16(sprite_dc, old_sprite);
(void)SelectObject16(background_dc, old_background);
(void)DeleteObject16(saved_region);
(void)DeleteDC16(saved_region_dc);
(void)DeleteDC16(sprite_dc);
(void)DeleteDC16(background_dc);
}
static Win16FarPtr active_player_dword(uint16_t index_zero_base)
{
return win16_dgroup_pointer((uint16_t)(
index_zero_base + (uint16_t)g_current_player * 4u));
}
/*
* 1000:bc36 -- add a score delta, award crossed marker thresholds, and redraw.
* Delta -1 is the explicit redraw-only sentinel; tilt suppresses all mutation.
*/
void tdkpin_add_score_from_caller_frame(
uint16_t caller_bp, int32_t delta)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
HDC16 dc = GetDC16(window);
if (delta != -1 &&
win16_read_u8(win16_dgroup_pointer(0x07c6)) == 0) {
if (win16_read_u8(win16_dgroup_pointer(0x0874)) != 0) {
delta = borland_multiply_i32(delta, 2);
}
if (win16_read_u8(
win16_far_add_offset(receiver, 0x0bdc)) != 0) {
delta = borland_multiply_i32(delta, 2);
}
Win16FarPtr score = active_player_dword(0x097e);
uint32_t updated = win16_read_u32(score) + (uint32_t)delta;
win16_write_u32(score, 0, updated);
Win16FarPtr marker_level = active_player_dword(0x0835);
int32_t level = (int32_t)win16_read_u32(marker_level);
if (level <= 4) {
uint32_t threshold = win16_read_u32(
win16_dgroup_pointer((uint16_t)(0x0825 +
(uint16_t)level * 4u)));
if ((int16_t)(updated >> 16) > (int16_t)(threshold >> 16) ||
((int16_t)(updated >> 16) ==
(int16_t)(threshold >> 16) &&
(uint16_t)updated >= (uint16_t)threshold)) {
tdkpin_play_sound_if_not_tilted(7);
win16_write_u32(marker_level, 0, (uint32_t)level + 1u);
Win16FarPtr markers = win16_dgroup_pointer((uint16_t)(
0x081f + (uint16_t)g_current_player * 2u));
win16_write_u16(
markers, 0, (uint16_t)(win16_read_u16(markers) + 1u));
tdkpin_refresh_player_progress_markers(caller_bp);
}
}
tdkpin_update_status_display(g_current_player, dc);
}
tdkpin_draw_score(dc, (int32_t)g_current_player);
(void)ReleaseDC16(window, dc);
}
static void restore_bounds(
bool overlay_a,
INT16 bottom,
INT16 right,
INT16 top,
INT16 left,
HDC16 dc)
{
INT16 height = (INT16)(uint16_t)(
(uint16_t)bottom - (uint16_t)top + 1);
INT16 width = (INT16)(uint16_t)(
(uint16_t)right - (uint16_t)left + 1);
if (overlay_a) {
tdkpin_restore_background_region_a(height, width, top, left, dc);
} else {
tdkpin_restore_background_region_b(height, width, top, left, dc);
}
tdkpin_present_background_region(height, width, top, left, dc);
}
/* 1000:b2c8 -- inclusive rectangle restore from overlay A. */
void tdkpin_restore_bounds_overlay_a(
uint16_t ignored_static_link,
INT16 bottom,
INT16 right,
INT16 top,
INT16 left,
HDC16 dc)
{
(void)ignored_static_link;
restore_bounds(true, bottom, right, top, left, dc);
}
/* 1000:b3b0 -- inclusive rectangle restore from overlay B. */
void tdkpin_restore_bounds_overlay_b(
uint16_t ignored_static_link,
INT16 bottom,
INT16 right,
INT16 top,
INT16 left,
HDC16 dc)
{
(void)ignored_static_link;
restore_bounds(false, bottom, right, top, left, dc);
}
/*
* 1000:bda2 -- caller-frame helper. SS:[caller BP+6] is the far receiver;
* its HWND at +4 owns the transient DC used for the 32-bit score value.
*/
void tdkpin_draw_score_from_caller_frame(
uint16_t caller_bp, uint16_t value_low, uint16_t value_high)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
HDC16 dc = GetDC16(window);
int32_t value = (int32_t)(
(uint32_t)value_low | ((uint32_t)value_high << 16));
tdkpin_draw_score(dc, value);
(void)ReleaseDC16(window, dc);
}
/* 1000:a70e -- clear the player counter and set object/global transition bytes. */
void tdkpin_set_gameplay_transition_flags(uint16_t caller_bp)
{
g_player_count = 0;
Win16FarPtr receiver = caller_receiver(caller_bp);
win16_write_u8(win16_far_add_offset(receiver, 0x0bde), 1);
g_gameplay_transition_pending = 1;
}
static uint16_t point_in_bounds_with_residue(
int32_t x,
int32_t y,
int32_t maximum_y,
int32_t maximum_x,
int32_t minimum_y,
int32_t minimum_x)
{
uint16_t ax_residue = (uint16_t)x;
bool inside = x >= minimum_x;
if (inside) {
ax_residue = (uint16_t)y;
inside = y >= minimum_y;
}
if (inside) {
ax_residue = (uint16_t)x;
inside = x <= maximum_x;
}
if (inside) {
ax_residue = (uint16_t)y;
inside = y <= maximum_y;
}
return (ax_residue & 0xff00u) | (inside ? 1u : 0u);
}
/* 1000:9ae0 -- test the receiver's current milli-coordinate against bounds. */
uint16_t tdkpin_receiver_point_in_bounds(
uint16_t caller_bp,
int32_t maximum_y,
int32_t maximum_x,
int32_t minimum_y,
int32_t minimum_x)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
int32_t x = (int32_t)win16_read_u32(
win16_far_add_offset(receiver, 0x0bba));
int32_t y = (int32_t)win16_read_u32(
win16_far_add_offset(receiver, 0x0bbe));
return point_in_bounds_with_residue(
x, y, maximum_y, maximum_x, minimum_y, minimum_x);
}
/* 1000:9b69 -- same inclusive rectangle test for the global ball position. */
uint16_t tdkpin_ball_in_bounds(
uint16_t ignored_static_link,
int32_t maximum_y,
int32_t maximum_x,
int32_t minimum_y,
int32_t minimum_x)
{
(void)ignored_static_link;
return point_in_bounds_with_residue(
g_ball_x_milli,
g_ball_y_milli,
maximum_y,
maximum_x,
minimum_y,
minimum_x);
}
/* 1000:bdde -- center dynamic record 173+displacement on the current ball. */
void tdkpin_update_dynamic_ball_record(
uint16_t ignored_static_link, int32_t record_displacement)
{
(void)ignored_static_link;
uint16_t index = (uint16_t)(record_displacement + 0x00ad);
uint16_t base = (uint16_t)(0x095b + (uint16_t)(index * 0x53u));
Win16FarPtr record = win16_dgroup_pointer(base);
win16_write_u32(record, 0x12, (uint32_t)g_ball_x_milli);
win16_write_u32(record, 0x16, (uint32_t)g_ball_y_milli);
win16_write_u32(
record, 0x02, (uint32_t)((uint32_t)g_ball_x_milli - 21000u));
win16_write_u32(
record, 0x06, (uint32_t)((uint32_t)g_ball_y_milli - 21000u));
win16_write_u32(
record, 0x0a, (uint32_t)((uint32_t)g_ball_x_milli + 21000u));
win16_write_u32(
record, 0x0e, (uint32_t)((uint32_t)g_ball_y_milli + 21000u));
}
static void restore_record_bounds(
bool overlay_a, uint16_t caller_bp, Win16FarPtr bounds)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
HDC16 dc = GetDC16(window);
uint16_t bottom = win16_read_u16(win16_far_add_offset(bounds, 6));
uint16_t right = win16_read_u16(win16_far_add_offset(bounds, 4));
uint16_t top = win16_read_u16(win16_far_add_offset(bounds, 2));
uint16_t left = win16_read_u16(bounds);
if (overlay_a) {
tdkpin_restore_bounds_overlay_a(
caller_bp, bottom, right, top, left, dc);
} else {
tdkpin_restore_bounds_overlay_b(
caller_bp, bottom, right, top, left, dc);
}
(void)ReleaseDC16(window, dc);
}
/* 1000:b30b -- restore collision/quad bounds from overlay A or draw item 1..9. */
void tdkpin_restore_record_or_item_a(uint16_t caller_bp, uint16_t id)
{
if (id <= 175) {
uint16_t offset = (uint16_t)(0x095b + (uint16_t)(id * 0x53u) + 0x4b);
restore_record_bounds(true, caller_bp, win16_dgroup_pointer(offset));
} else if (id >= 1006 && id <= 1014) {
Win16FarPtr receiver = caller_receiver(caller_bp);
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
HDC16 dc = GetDC16(window);
tdkpin_draw_item_status((INT16)(id - 1005), dc);
(void)ReleaseDC16(window, dc);
} else {
uint16_t index = (uint16_t)(id - 1000);
uint16_t offset = (uint16_t)(0x4263 + (uint16_t)(index * 8u));
restore_record_bounds(true, caller_bp, win16_dgroup_pointer(offset));
}
}
/* 1000:b3f3 -- restore collision or word-quad bounds from overlay B. */
void tdkpin_restore_record_b(uint16_t caller_bp, uint16_t id)
{
Win16FarPtr bounds;
if (id <= 175) {
bounds = win16_dgroup_pointer(
(uint16_t)(0x095b + (uint16_t)(id * 0x53u) + 0x4b));
} else {
uint16_t index = (uint16_t)(id - 1000);
bounds = win16_dgroup_pointer(
(uint16_t)(0x4263 + (uint16_t)(index * 8u)));
}
restore_record_bounds(false, caller_bp, bounds);
}
/* 1000:7e12 -- redraw records whose flag 80/100 encodes overlay polarity. */
void tdkpin_refresh_collision_record(uint16_t caller_bp, uint16_t id)
{
if (id == 0 || id >= 176) {
return;
}
Win16FarPtr record = win16_dgroup_pointer(
(uint16_t)(0x095b + (uint16_t)(id * 0x53u)));
uint16_t flags = win16_read_u16(win16_far_add_offset(record, 0x41));
bool active = win16_read_u8(win16_far_add_offset(record, 0x34)) != 0;
if ((flags & 0x0080u) != 0) {
if (active) {
tdkpin_restore_record_b(caller_bp, id);
} else {
tdkpin_restore_record_or_item_a(caller_bp, id);
}
} else if ((flags & 0x0100u) != 0) {
if (active) {
tdkpin_restore_record_or_item_a(caller_bp, id);
} else {
tdkpin_restore_record_b(caller_bp, id);
}
}
}
/* 1000:7e92 -- refresh one player item through its per-player 20-byte row. */
void tdkpin_refresh_player_item(uint16_t caller_bp, uint16_t item)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
uint16_t offset = (uint16_t)(
(uint16_t)g_current_player * 0x14u + item + 0x0467u);
if (win16_read_u8(win16_far_add_offset(receiver, offset)) != 0) {
tdkpin_restore_record_or_item_a(
caller_bp, (uint16_t)(item + 1000));
} else {
tdkpin_restore_record_b(
caller_bp, (uint16_t)(item + 1000));
}
}
/* 1000:c36b -- load one indexed ball state into active globals/object fields. */
void tdkpin_load_ball_slot(uint16_t caller_bp, uint16_t slot)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
uint16_t word4 = (uint16_t)(slot * 4u);
uint16_t word6 = (uint16_t)(slot * 6u);
g_ball_x_milli = (int32_t)win16_read_u32(
win16_dgroup_pointer((uint16_t)(0x07e3 + word4)));
g_ball_y_milli = (int32_t)win16_read_u32(
win16_dgroup_pointer((uint16_t)(0x07eb + word4)));
win16_write_u32(
receiver,
0x0baa,
win16_read_u32(win16_dgroup_pointer((uint16_t)(0x07f3 + word4))));
win16_write_u32(
receiver,
0x0bae,
win16_read_u32(win16_dgroup_pointer((uint16_t)(0x07fb + word4))));
for (uint16_t index = 0; index < 6; index++) {
win16_write_u8(
win16_far_add_offset(receiver, (uint16_t)(0x0bce + index)),
win16_read_u8(win16_dgroup_pointer(
(uint16_t)(0x080b + word6 + index))));
}
win16_write_u32(
receiver,
0x0bca,
win16_read_u32(win16_dgroup_pointer((uint16_t)(0x0803 + word4))));
}
/* 1000:c426 -- save active globals/object fields back into one ball slot. */
void tdkpin_save_ball_slot(uint16_t caller_bp, uint16_t slot)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
uint16_t word4 = (uint16_t)(slot * 4u);
uint16_t word6 = (uint16_t)(slot * 6u);
win16_write_u32(
win16_dgroup_pointer((uint16_t)(0x07e3 + word4)),
0,
(uint32_t)g_ball_x_milli);
win16_write_u32(
win16_dgroup_pointer((uint16_t)(0x07eb + word4)),
0,
(uint32_t)g_ball_y_milli);
win16_write_u32(
win16_dgroup_pointer((uint16_t)(0x07f3 + word4)),
0,
win16_read_u32(win16_far_add_offset(receiver, 0x0baa)));
win16_write_u32(
win16_dgroup_pointer((uint16_t)(0x07fb + word4)),
0,
win16_read_u32(win16_far_add_offset(receiver, 0x0bae)));
for (uint16_t index = 0; index < 6; index++) {
win16_write_u8(
win16_dgroup_pointer((uint16_t)(0x080b + word6 + index)),
win16_read_u8(win16_far_add_offset(
receiver, (uint16_t)(0x0bce + index))));
}
win16_write_u32(
win16_dgroup_pointer((uint16_t)(0x0803 + word4)),
0,
win16_read_u32(win16_far_add_offset(receiver, 0x0bca)));
}