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

229 lines
7.6 KiB
C

/* Borland far-string and memory helpers reconstructed from Code2. */
#include "tdkpin_strings.h"
#include <stdbool.h>
typedef struct {
Win16FarPtr after;
uint16_t remaining;
bool found;
} FarNulScan;
static FarNulScan scan_nul(Win16FarPtr string, uint16_t maximum)
{
Win16FarPtr current = string;
uint16_t remaining = maximum;
bool found = false;
while (remaining != 0) {
uint8_t value = win16_read_u8(current);
current = win16_far_add_offset(current, 1);
remaining--;
if (value == 0) {
found = true;
break;
}
}
return (FarNulScan){current, remaining, found};
}
/* 1020:0891 — unconditional forward CLD/REP MOVSB far copy. */
void borland_far_copy(
Win16FarPtr destination, Win16FarPtr source, uint16_t count)
{
for (uint16_t index = 0; index != count; index++) {
win16_write_u8(
win16_far_add_offset(destination, index),
win16_read_u8(win16_far_add_offset(source, index)));
}
}
/* 1008:2e92 — REPNE SCASB strlen with CX=ffff. */
uint16_t borland_far_strlen(Win16FarPtr string)
{
FarNulScan scan = scan_nul(string, 0xffff);
return (uint16_t)(0xfffe - scan.remaining);
}
/* 1008:2ea9 — pointer to the NUL found by the same bounded scan. */
Win16FarPtr borland_far_strend(Win16FarPtr string)
{
FarNulScan scan = scan_nul(string, 0xffff);
return win16_far_add_offset(scan.after, 0xffff);
}
/*
* 1008:2ec0 — far memmove.
* Direction is selected solely by the unsigned source/destination offsets;
* selectors are deliberately not compared, exactly like CMP SI,DI.
*/
Win16FarPtr borland_far_memmove(
Win16FarPtr destination, Win16FarPtr source, uint16_t count)
{
if (win16_far_offset(source) < win16_far_offset(destination)) {
for (uint16_t remaining = count; remaining != 0; remaining--) {
uint16_t index = (uint16_t)(remaining - 1);
win16_write_u8(
win16_far_add_offset(destination, index),
win16_read_u8(win16_far_add_offset(source, index)));
}
} else {
for (uint16_t index = 0; index != count; index++) {
win16_write_u8(
win16_far_add_offset(destination, index),
win16_read_u8(win16_far_add_offset(source, index)));
}
}
return destination;
}
/* 1008:2ee5 — copy through and including the bounded NUL. */
Win16FarPtr borland_far_strcpy(Win16FarPtr destination, Win16FarPtr source)
{
FarNulScan scan = scan_nul(source, 0xffff);
uint16_t count = (uint16_t)~scan.remaining;
for (uint16_t index = 0; index != count; index++) {
win16_write_u8(
win16_far_add_offset(destination, index),
win16_read_u8(win16_far_add_offset(source, index)));
}
return destination;
}
/*
* 1008:2f07 — bounded copy followed by an unconditional extra NUL.
* If the source NUL lies within maximum, that NUL is copied and a second one is
* appended; if not, maximum bytes plus one NUL are written.
*/
Win16FarPtr borland_far_strncpy(
Win16FarPtr destination, Win16FarPtr source, uint16_t maximum)
{
FarNulScan scan = scan_nul(source, maximum);
uint16_t count = (uint16_t)(maximum - scan.remaining);
for (uint16_t index = 0; index != count; index++) {
win16_write_u8(
win16_far_add_offset(destination, index),
win16_read_u8(win16_far_add_offset(source, index)));
}
win16_write_u8(win16_far_add_offset(destination, count), 0);
return destination;
}
/* 1008:2f2f — Turbo Pascal short string to NUL-terminated far string. */
Win16FarPtr borland_short_to_c_string(
Win16FarPtr destination, Win16FarPtr short_string)
{
uint8_t length = win16_read_u8(short_string);
for (uint16_t index = 0; index < length; index++) {
win16_write_u8(
win16_far_add_offset(destination, index),
win16_read_u8(win16_far_add_offset(short_string, (uint16_t)(index + 1))));
}
win16_write_u8(win16_far_add_offset(destination, length), 0);
return destination;
}
/* 1008:2f4d — append source at destination's bounded NUL. */
Win16FarPtr borland_far_strcat(Win16FarPtr destination, Win16FarPtr source)
{
(void)borland_far_strcpy(borland_far_strend(destination), source);
return destination;
}
static uint8_t ascii_upper(uint8_t value)
{
return value >= 'a' && value <= 'z' ? (uint8_t)(value - 0x20) : value;
}
/*
* 1008:2f70 — ASCII-only case-insensitive comparison of at most maximum bytes.
* Comparison length is min(maximum, strlen(second)+1), so the second string's
* NUL participates when it lies inside the bound.
*/
int16_t borland_far_strnicmp(
Win16FarPtr first, Win16FarPtr second, uint16_t maximum)
{
if (maximum == 0) {
return 0;
}
FarNulScan scan = scan_nul(second, maximum);
uint16_t count = (uint16_t)(maximum - scan.remaining);
for (uint16_t index = 0; index < count; index++) {
uint8_t first_byte = win16_read_u8(win16_far_add_offset(first, index));
uint8_t second_byte = win16_read_u8(win16_far_add_offset(second, index));
if (first_byte != second_byte) {
int16_t difference =
(int16_t)ascii_upper(first_byte) - ascii_upper(second_byte);
if (difference != 0) {
return difference;
}
}
}
return 0;
}
/* 1008:2fbb — forward character search including the terminating NUL. */
Win16FarPtr borland_far_strchr(Win16FarPtr string, uint8_t character)
{
FarNulScan scan = scan_nul(string, 0xffff);
uint16_t count = (uint16_t)~scan.remaining;
for (uint16_t index = 0; index < count; index++) {
Win16FarPtr current = win16_far_add_offset(string, index);
if (win16_read_u8(current) == character) {
return current;
}
}
return 0;
}
/* 1008:2fe3 — reverse character search including the terminating NUL. */
Win16FarPtr borland_far_strrchr(Win16FarPtr string, uint8_t character)
{
FarNulScan scan = scan_nul(string, 0xffff);
uint16_t count = (uint16_t)~scan.remaining;
Win16FarPtr current = win16_far_add_offset(scan.after, 0xffff);
for (uint16_t remaining = count; remaining != 0; remaining--) {
if (win16_read_u8(current) == character) {
return current;
}
current = win16_far_add_offset(current, 0xffff);
}
return 0;
}
/*
* 1008:300a — NUL-terminated far string to Turbo Pascal short string.
* CL alone becomes the length byte, but REP MOVSB still uses the full 16-bit
* length; this preserves the original overflow behavior for strings >255.
*/
void borland_c_to_short_string(Win16FarPtr destination, Win16FarPtr source)
{
FarNulScan scan = scan_nul(source, 0xffff);
uint16_t length = (uint16_t)(~scan.remaining - 1);
win16_write_u8(destination, (uint8_t)length);
for (uint16_t index = 0; index < length; index++) {
win16_write_u8(
win16_far_add_offset(destination, (uint16_t)(index + 1)),
win16_read_u8(win16_far_add_offset(source, index)));
}
}
/* 1008:302c — allocate and copy one nonempty far C string. */
Win16FarPtr borland_far_strdup(Win16FarPtr source)
{
if (source == 0 || win16_read_u8(source) == 0) {
return 0;
}
uint16_t bytes = (uint16_t)(borland_far_strlen(source) + 1);
Win16FarPtr copy = borland_get_mem(bytes);
return copy == 0 ? 0 : borland_far_memmove(copy, source, bytes);
}
/* 1008:3099 — release one allocated far C string including its NUL. */
void borland_far_strdispose(Win16FarPtr string)
{
if (string != 0) {
borland_free_mem(string, (uint16_t)(borland_far_strlen(string) + 1));
}
}