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

104 lines
3.2 KiB
C

/* Borland Pascal object construction and destruction protocol. */
#include "tdkpin_borland_objects.h"
/*
* 1020:03ef — constructor entry helper.
*
* The VMT is a near DGROUP pointer. Its first word is the instance size, and
* DI in the register ABI supplies the offset of the object's VMT field. For an
* already allocated object the helper clears the caller's hidden VMT argument;
* compiler-generated failure cleanup uses that mutation to avoid freeing an
* object which it did not allocate.
*/
bool borland_enter_object_constructor(
BorlandObjectCallFrame *frame, uint16_t vmt_field_offset)
{
uint16_t vmt = frame->vmt_argument;
if (vmt == 0) {
return true;
}
if (frame->object != 0) {
frame->vmt_argument = 0;
} else {
uint16_t object_size = win16_read_u16(win16_dgroup_pointer(vmt));
BorlandAllocationAttempt allocation =
borland_try_get_mem(object_size);
if (allocation.failed) {
borland_runtime_error(203);
}
if (allocation.block == 0) {
return false;
}
frame->object = allocation.block;
}
win16_write_u16(
frame->object, vmt_field_offset, vmt);
return true;
}
/*
* 1020:0439 — destructor exit helper. A nonzero hidden VMT argument says that
* this destructor invocation owns the allocation. The size is recovered from
* the VMT currently installed in the object, then the caller's object pointer
* is cleared after a successful release.
*/
void borland_finish_object_destructor(
BorlandObjectCallFrame *frame, uint16_t vmt_field_offset)
{
if (frame->vmt_argument == 0) {
return;
}
uint16_t vmt = win16_read_u16(win16_far_add_offset(
frame->object, vmt_field_offset));
uint16_t object_size = win16_read_u16(win16_dgroup_pointer(vmt));
if (borland_try_free_mem(frame->object, object_size)) {
borland_runtime_error(204);
}
frame->object = 0;
}
/* 1008:2d99 — the root object's constructor has its VMT field at offset zero. */
Win16FarPtr borland_default_object_constructor(
Win16FarPtr object, uint16_t vmt)
{
BorlandObjectCallFrame frame = {object, vmt};
borland_enter_object_constructor(&frame, 0);
return frame.object;
}
/* 1008:2dcd — root destructor cleanup with the VMT field at offset zero. */
void borland_default_object_destructor(Win16FarPtr object, uint16_t vmt)
{
BorlandObjectCallFrame frame = {object, vmt};
borland_finish_object_destructor(&frame, 0);
}
/*
* 1008:2db3 — Pascal Dispose: call VMT slot +8 with hidden dispose mode 1.
* The virtual destructor itself ultimately reaches 1020:0439 and frees the
* allocation according to the VMT size.
*/
void borland_dispose_object(Win16FarPtr object)
{
uint16_t vmt = win16_read_u16(object);
Win16FarPtr destructor =
win16_read_far_pointer(win16_dgroup_pointer(vmt), 8);
win16_call_object_destructor(destructor, object, 1);
}
/* 1008:2d87 — Borland's abstract virtual-method trap. */
_Noreturn void borland_abstract_method_error(void)
{
borland_runtime_error(211);
}
/* 1018:0d8a — ObjectWindows abstract slot forwarding to the Borland trap. */
_Noreturn void object_windows_abstract_method(void)
{
borland_abstract_method_error();
}