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
214 lines
7.8 KiB
C
214 lines
7.8 KiB
C
/* Collision-record geometry mover at 1000:8b0d. */
|
|
|
|
#include "tdkpin_flippers.h"
|
|
|
|
#include "tdkpin_arithmetic.h"
|
|
#include "tdkpin_render.h"
|
|
|
|
enum {
|
|
COLLISION_RECORDS_BASE = 0x095b,
|
|
COLLISION_RECORD_BYTES = 0x53,
|
|
COLLISION_BOUNDS_MIN_X = 0x02,
|
|
COLLISION_BOUNDS_MIN_Y = 0x06,
|
|
COLLISION_BOUNDS_MAX_X = 0x0a,
|
|
COLLISION_BOUNDS_MAX_Y = 0x0e,
|
|
COLLISION_POINT1_X = 0x12,
|
|
COLLISION_POINT1_Y = 0x16,
|
|
COLLISION_POINT2_X = 0x1a,
|
|
COLLISION_POINT2_Y = 0x1e,
|
|
COLLISION_BOUNDS_PADDING = 5000,
|
|
};
|
|
|
|
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 Win16FarPtr collision_record(uint16_t id)
|
|
{
|
|
return win16_dgroup_pointer((uint16_t)(
|
|
COLLISION_RECORDS_BASE + id * COLLISION_RECORD_BYTES));
|
|
}
|
|
|
|
static int32_t read_record_i32(Win16FarPtr record, uint16_t offset)
|
|
{
|
|
return (int32_t)win16_read_u32(win16_far_add_offset(record, offset));
|
|
}
|
|
|
|
static void write_record_i32(
|
|
Win16FarPtr record, uint16_t offset, int32_t value)
|
|
{
|
|
win16_write_u32(record, offset, (uint32_t)value);
|
|
}
|
|
|
|
static int32_t add_wrap_i32(int32_t left, int32_t right)
|
|
{
|
|
return (int32_t)((uint32_t)left + (uint32_t)right);
|
|
}
|
|
|
|
static int32_t subtract_wrap_i32(int32_t left, int32_t right)
|
|
{
|
|
return (int32_t)((uint32_t)left - (uint32_t)right);
|
|
}
|
|
|
|
/* The binary spells several of these as Mul32 -> I32ToReal48 -> Round.
|
|
* Every int32 is exact in Real48, so the observable result is the wrapped
|
|
* low dword from the first operation. */
|
|
static int32_t scaled_delta(int32_t delta, int32_t scale)
|
|
{
|
|
return borland_multiply_i32(delta, scale);
|
|
}
|
|
|
|
static void add_scaled(
|
|
Win16FarPtr record, uint16_t offset, int32_t delta, int32_t scale)
|
|
{
|
|
write_record_i32(
|
|
record,
|
|
offset,
|
|
add_wrap_i32(
|
|
read_record_i32(record, offset), scaled_delta(delta, scale)));
|
|
}
|
|
|
|
static void subtract_scaled(
|
|
Win16FarPtr record, uint16_t offset, int32_t delta, int32_t scale)
|
|
{
|
|
write_record_i32(
|
|
record,
|
|
offset,
|
|
subtract_wrap_i32(
|
|
read_record_i32(record, offset), scaled_delta(delta, scale)));
|
|
}
|
|
|
|
static void recompute_segment_bounds(Win16FarPtr record)
|
|
{
|
|
int32_t point1_x = read_record_i32(record, COLLISION_POINT1_X);
|
|
int32_t point1_y = read_record_i32(record, COLLISION_POINT1_Y);
|
|
int32_t point2_x = read_record_i32(record, COLLISION_POINT2_X);
|
|
int32_t point2_y = read_record_i32(record, COLLISION_POINT2_Y);
|
|
int32_t minimum_x = point1_x < point2_x ? point1_x : point2_x;
|
|
int32_t maximum_x = point1_x < point2_x ? point2_x : point1_x;
|
|
int32_t minimum_y = point1_y < point2_y ? point1_y : point2_y;
|
|
int32_t maximum_y = point1_y < point2_y ? point2_y : point1_y;
|
|
write_record_i32(
|
|
record,
|
|
COLLISION_BOUNDS_MIN_X,
|
|
subtract_wrap_i32(minimum_x, COLLISION_BOUNDS_PADDING));
|
|
write_record_i32(
|
|
record,
|
|
COLLISION_BOUNDS_MIN_Y,
|
|
subtract_wrap_i32(minimum_y, COLLISION_BOUNDS_PADDING));
|
|
write_record_i32(
|
|
record,
|
|
COLLISION_BOUNDS_MAX_X,
|
|
add_wrap_i32(maximum_x, COLLISION_BOUNDS_PADDING));
|
|
write_record_i32(
|
|
record,
|
|
COLLISION_BOUNDS_MAX_Y,
|
|
add_wrap_i32(maximum_y, COLLISION_BOUNDS_PADDING));
|
|
}
|
|
|
|
static void update_left_flipper_records(int32_t delta)
|
|
{
|
|
Win16FarPtr outer_segment = collision_record(66);
|
|
add_scaled(outer_segment, COLLISION_POINT1_X, delta, 14000);
|
|
add_scaled(outer_segment, COLLISION_POINT1_Y, delta, 4000);
|
|
add_scaled(outer_segment, COLLISION_POINT2_X, delta, 10000);
|
|
add_scaled(outer_segment, COLLISION_POINT2_Y, delta, 44000);
|
|
recompute_segment_bounds(outer_segment);
|
|
|
|
Win16FarPtr swept_edge = collision_record(67);
|
|
add_scaled(swept_edge, COLLISION_POINT1_X, delta, 1000);
|
|
add_scaled(swept_edge, COLLISION_POINT1_Y, delta, 42000);
|
|
add_scaled(swept_edge, COLLISION_BOUNDS_MIN_X, delta, 2000);
|
|
add_scaled(swept_edge, COLLISION_BOUNDS_MIN_Y, delta, 43000);
|
|
subtract_scaled(swept_edge, COLLISION_BOUNDS_MAX_X, delta, 2000);
|
|
subtract_scaled(swept_edge, COLLISION_BOUNDS_MAX_Y, delta, 43000);
|
|
|
|
Win16FarPtr inner_segment = collision_record(68);
|
|
subtract_scaled(inner_segment, COLLISION_POINT1_X, delta, 7000);
|
|
add_scaled(inner_segment, COLLISION_POINT1_Y, delta, 43000);
|
|
subtract_scaled(inner_segment, COLLISION_POINT2_X, delta, 19000);
|
|
add_scaled(inner_segment, COLLISION_POINT2_Y, delta, 6000);
|
|
recompute_segment_bounds(inner_segment);
|
|
}
|
|
|
|
static void update_right_flipper_records(int32_t delta)
|
|
{
|
|
Win16FarPtr outer_segment = collision_record(83);
|
|
subtract_scaled(outer_segment, COLLISION_POINT2_X, delta, 16000);
|
|
add_scaled(outer_segment, COLLISION_POINT2_Y, delta, 3000);
|
|
subtract_scaled(outer_segment, COLLISION_POINT1_X, delta, 11000);
|
|
add_scaled(outer_segment, COLLISION_POINT1_Y, delta, 45000);
|
|
recompute_segment_bounds(outer_segment);
|
|
|
|
Win16FarPtr swept_edge = collision_record(82);
|
|
subtract_scaled(swept_edge, COLLISION_POINT1_X, delta, 2000);
|
|
add_scaled(swept_edge, COLLISION_POINT1_Y, delta, 43000);
|
|
subtract_scaled(swept_edge, COLLISION_BOUNDS_MIN_X, delta, 2000);
|
|
add_scaled(swept_edge, COLLISION_BOUNDS_MIN_Y, delta, 43000);
|
|
add_scaled(swept_edge, COLLISION_BOUNDS_MAX_X, delta, 2000);
|
|
subtract_scaled(swept_edge, COLLISION_BOUNDS_MAX_Y, delta, 43000);
|
|
|
|
Win16FarPtr inner_segment = collision_record(81);
|
|
add_scaled(inner_segment, COLLISION_POINT2_X, delta, 6000);
|
|
add_scaled(inner_segment, COLLISION_POINT2_Y, delta, 49000);
|
|
add_scaled(inner_segment, COLLISION_POINT1_X, delta, 19000);
|
|
add_scaled(inner_segment, COLLISION_POINT1_Y, delta, 6000);
|
|
recompute_segment_bounds(inner_segment);
|
|
}
|
|
|
|
static void move_flipper_bitmap_geometry_with_dc(
|
|
Win16FarPtr receiver,
|
|
int32_t delta,
|
|
uint16_t flipper,
|
|
HDC16 dc)
|
|
{
|
|
if ((uint8_t)flipper == 1) {
|
|
update_left_flipper_records(delta);
|
|
if (win16_read_u16(win16_far_add_offset(receiver, 0x0bd4)) == 0) {
|
|
tdkpin_restore_background_region_b(52, 47, 373, 93, dc);
|
|
} else {
|
|
tdkpin_restore_background_region_a(52, 47, 373, 93, dc);
|
|
}
|
|
tdkpin_present_background_region(52, 47, 373, 93, dc);
|
|
} else if ((uint8_t)flipper == 2) {
|
|
update_right_flipper_records(delta);
|
|
if (win16_read_u16(win16_far_add_offset(receiver, 0x0bd6)) == 0) {
|
|
tdkpin_restore_background_region_b(53, 45, 372, 175, dc);
|
|
} else {
|
|
tdkpin_restore_background_region_a(53, 45, 372, 175, dc);
|
|
}
|
|
tdkpin_present_background_region(53, 45, 372, 175, dc);
|
|
}
|
|
}
|
|
|
|
void tdkpin_move_flipper_bitmap_geometry_for_receiver(
|
|
Win16FarPtr receiver, int32_t delta, uint16_t flipper)
|
|
{
|
|
HWND16 handle = win16_read_u16(win16_far_add_offset(receiver, 4));
|
|
HDC16 dc = GetDC16(handle);
|
|
move_flipper_bitmap_geometry_with_dc(receiver, delta, flipper, dc);
|
|
(void)ReleaseDC16(handle, dc);
|
|
}
|
|
|
|
/* 1000:8b0d. parent_bp is the hidden static link to 1000:99f0. */
|
|
void tdkpin_move_flipper_bitmap_geometry(
|
|
uint16_t parent_bp, int32_t delta, uint8_t flipper)
|
|
{
|
|
uint16_t caller_bp = win16_read_stack_u16((uint16_t)(parent_bp + 6));
|
|
Win16FarPtr receiver = caller_receiver(caller_bp);
|
|
HWND16 handle = win16_read_u16(win16_far_add_offset(receiver, 4));
|
|
HDC16 dc = GetDC16(handle);
|
|
win16_write_stack_u16((uint16_t)(parent_bp - 4), 0);
|
|
win16_write_stack_u16((uint16_t)(parent_bp - 2), 0);
|
|
uint32_t vertical_step = delta == 1 ? 43000u : (uint32_t)-43000;
|
|
win16_write_stack_u16(
|
|
(uint16_t)(parent_bp - 8), (uint16_t)vertical_step);
|
|
win16_write_stack_u16(
|
|
(uint16_t)(parent_bp - 6), (uint16_t)(vertical_step >> 16));
|
|
move_flipper_bitmap_geometry_with_dc(receiver, delta, flipper, dc);
|
|
(void)ReleaseDC16(handle, dc);
|
|
}
|