Files
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

439 lines
14 KiB
C

/* Borland Pascal generic file open/close dispatcher. */
#include "tdkpin_borland_files.h"
enum {
FILE_MODE_OFFSET = 2,
FILE_BUFFER_POSITION_OFFSET = 8,
FILE_BUFFER_END_OFFSET = 10,
FILE_OPEN_PROCEDURE_OFFSET = 0x10,
FILE_IN_OUT_PROCEDURE_OFFSET = 0x14,
FILE_CLOSE_PROCEDURE_OFFSET = 0x1c,
IO_ERROR_NOT_CLOSED = 0x66,
IO_ERROR_NOT_OPEN = 0x67,
};
/* 1020:05c7 — invoke one far FileProc and publish nonzero AX to IOResult. */
uint16_t borland_call_file_procedure(
Win16FarPtr record, uint16_t procedure_offset)
{
Win16FarPtr procedure =
win16_read_far_pointer(record, procedure_offset);
uint16_t result = win16_call_file_procedure(procedure, record);
if (result != 0) {
atomic_store(&g_borland_io_result, result);
}
return result;
}
/* 1020:0586 — close input/output files; output files flush first. */
void borland_close_file(Win16FarPtr record)
{
uint16_t mode = win16_read_u16(win16_far_add_offset(
record, FILE_MODE_OFFSET));
if (mode == BORLAND_FILE_OUTPUT) {
(void)borland_call_file_procedure(
record, FILE_IN_OUT_PROCEDURE_OFFSET);
} else if (mode != BORLAND_FILE_INPUT) {
atomic_store(&g_borland_io_result, IO_ERROR_NOT_OPEN);
return;
}
(void)borland_call_file_procedure(record, FILE_CLOSE_PROCEDURE_OFFSET);
win16_write_u16(record, FILE_MODE_OFFSET, BORLAND_FILE_CLOSED);
}
/* 1020:0534 — common Reset/Rewrite entry selected by DX. */
void borland_open_file(Win16FarPtr record, uint16_t mode)
{
uint16_t current = win16_read_u16(win16_far_add_offset(
record, FILE_MODE_OFFSET));
if (current == BORLAND_FILE_INPUT || current == BORLAND_FILE_OUTPUT) {
borland_close_file(record);
} else if (current != BORLAND_FILE_CLOSED) {
atomic_store(&g_borland_io_result, IO_ERROR_NOT_CLOSED);
return;
}
win16_write_u16(record, FILE_MODE_OFFSET, mode);
win16_write_u16(record, FILE_BUFFER_POSITION_OFFSET, 0);
win16_write_u16(record, FILE_BUFFER_END_OFFSET, 0);
if (borland_call_file_procedure(
record, FILE_OPEN_PROCEDURE_OFFSET) != 0) {
win16_write_u16(record, FILE_MODE_OFFSET, BORLAND_FILE_CLOSED);
}
}
/* 1020:0527/052c — five-byte mode-selecting tail entries. */
void borland_reset_file(Win16FarPtr record)
{
borland_open_file(record, BORLAND_FILE_INPUT);
}
void borland_rewrite_file(Win16FarPtr record)
{
borland_open_file(record, BORLAND_FILE_OUTPUT);
}
/* 1020:05d8 — DOS AH=3f buffered read. */
uint16_t borland_file_read_buffer(Win16FarPtr record)
{
uint16_t handle = win16_read_u16(record);
uint16_t count = win16_read_u16(win16_far_add_offset(record, 4));
Win16FarPtr buffer = win16_read_far_pointer(record, 12);
Win16DosResult result = win16_dos_read(handle, buffer, count);
win16_write_u16(record, 10, result.carry ? 0 : result.ax);
win16_write_u16(record, 8, 0);
return result.carry ? result.ax : 0;
}
static Win16DosResult write_pending_bytes(Win16FarPtr record)
{
uint16_t count = win16_exchange_u16(
win16_far_add_offset(record, 8), 0);
uint16_t handle = win16_read_u16(record);
Win16FarPtr buffer = win16_read_far_pointer(record, 12);
Win16DosResult result = win16_dos_write(handle, buffer, count);
if (!result.carry && result.ax != count) {
result.ax = 0x65;
result.carry = true;
}
return result;
}
/* 1020:0608 — buffered flush with short-write detection. */
uint16_t borland_file_flush_buffer(Win16FarPtr record)
{
Win16DosResult result = write_pending_bytes(record);
return result.carry ? result.ax : 0;
}
/* 1020:062d — same DOS write, but only carry is an error. */
uint16_t borland_file_write_buffer(Win16FarPtr record)
{
uint16_t count = win16_exchange_u16(
win16_far_add_offset(record, 8), 0);
Win16DosResult result = win16_dos_write(
win16_read_u16(record), win16_read_far_pointer(record, 12), count);
return result.carry ? result.ax : 0;
}
/* 1020:064d — preserve DOS standard handles 0..4; close larger handles. */
uint16_t borland_file_close_handle(Win16FarPtr record)
{
uint16_t handle = win16_read_u16(record);
if (handle <= 4) {
return 0;
}
Win16DosResult result = win16_dos_close(handle);
return result.carry ? result.ax : 0;
}
/* 1020:0751 — generic files must be in open sentinel mode 0xd7b3. */
bool borland_validate_open_file(Win16FarPtr record)
{
if (win16_read_u16(win16_far_add_offset(record, 2)) == 0xd7b3) {
return true;
}
atomic_store(&g_borland_io_result, 0x67);
return false;
}
/* 1020:0710 — DOS zero-byte write truncates at the current file position. */
void borland_truncate_file(Win16FarPtr record)
{
if (!borland_validate_open_file(record)) {
return;
}
uint16_t indeterminate = win16_indeterminate_u16();
Win16DosResult result = win16_dos_write(
win16_read_u16(record),
win16_make_far_pointer(indeterminate, indeterminate),
0);
if (result.carry) {
atomic_store(&g_borland_io_result, result.ax);
}
}
/* 1020:072c — close a valid generic file and always mark it closed. */
void borland_close_generic_file(Win16FarPtr record)
{
if (!borland_validate_open_file(record)) {
return;
}
uint16_t handle = win16_read_u16(record);
if (handle > 4) {
Win16DosResult result = win16_dos_close(handle);
if (result.carry) {
atomic_store(&g_borland_io_result, result.ax);
}
}
win16_write_u16(record, 2, BORLAND_FILE_CLOSED);
}
static void transfer_one_record(
Win16FarPtr buffer, Win16FarPtr record, bool write)
{
if (!borland_validate_open_file(record)) {
return;
}
uint16_t bytes = win16_read_u16(win16_far_add_offset(record, 4));
Win16DosResult result = write
? win16_dos_write(win16_read_u16(record), buffer, bytes)
: win16_dos_read(win16_read_u16(record), buffer, bytes);
if (result.carry) {
atomic_store(&g_borland_io_result, result.ax);
} else if (result.ax != bytes) {
atomic_store(&g_borland_io_result, write ? 0x65 : 0x64);
}
}
void borland_file_read_record(Win16FarPtr buffer, Win16FarPtr record)
{
transfer_one_record(buffer, record, false);
}
void borland_file_write_record(Win16FarPtr buffer, Win16FarPtr record)
{
transfer_one_record(buffer, record, true);
}
static void transfer_record_block(
Win16FarPtr result_count,
uint16_t requested_records,
Win16FarPtr buffer,
Win16FarPtr record,
bool write)
{
uint16_t records_transferred = 0;
if (borland_validate_open_file(record)) {
uint16_t record_size =
win16_read_u16(win16_far_add_offset(record, 4));
if (requested_records != 0) {
uint16_t byte_count =
(uint16_t)((uint32_t)requested_records * record_size);
Win16DosResult result = write
? win16_dos_write(
win16_read_u16(record), buffer, byte_count)
: win16_dos_read(
win16_read_u16(record), buffer, byte_count);
if (result.carry) {
atomic_store(&g_borland_io_result, result.ax);
goto publish;
}
if (record_size == 0) {
win16_integer_divide_fault();
}
records_transferred = (uint16_t)(result.ax / record_size);
}
if (result_count == 0 && records_transferred != requested_records) {
atomic_store(&g_borland_io_result, write ? 0x65 : 0x64);
}
}
publish:
if (result_count != 0) {
win16_write_u16(result_count, 0, records_transferred);
}
}
void borland_file_block_read(
Win16FarPtr result_count,
uint16_t requested_records,
Win16FarPtr buffer,
Win16FarPtr record)
{
transfer_record_block(
result_count, requested_records, buffer, record, false);
}
void borland_file_block_write(
Win16FarPtr result_count,
uint16_t requested_records,
Win16FarPtr buffer,
Win16FarPtr record)
{
transfer_record_block(
result_count, requested_records, buffer, record, true);
}
void borland_file_seek_record(uint32_t record_index, Win16FarPtr record)
{
if (!borland_validate_open_file(record)) {
return;
}
uint16_t record_size =
win16_read_u16(win16_far_add_offset(record, 4));
uint32_t byte_offset = record_index * record_size;
Win16DosResult result =
win16_dos_seek(win16_read_u16(record), byte_offset, 0);
if (result.carry) {
atomic_store(&g_borland_io_result, result.ax);
}
}
static void open_or_create_generic_file(
uint16_t record_size, Win16FarPtr record, bool create)
{
uint16_t mode = win16_read_u16(win16_far_add_offset(record, 2));
if (mode == 0xd7b3) {
borland_close_generic_file(record);
} else if (mode != BORLAND_FILE_CLOSED) {
atomic_store(&g_borland_io_result, 0x66);
return;
}
Win16FarPtr name = win16_far_add_offset(record, 48);
Win16DosResult result;
if (win16_read_u8(name) == 0) {
result = (Win16DosResult){create ? 1 : 0, false};
} else {
result = create
? win16_dos_create(name, 0)
: win16_dos_open(name, g_borland_file_mode);
if (result.carry) {
atomic_store(&g_borland_io_result, result.ax);
return;
}
}
win16_write_u16(record, 2, 0xd7b3);
win16_write_u16(record, 0, result.ax);
win16_write_u16(record, 4, record_size);
}
void borland_open_generic_file(uint16_t record_size, Win16FarPtr record)
{
open_or_create_generic_file(record_size, record, false);
}
void borland_create_generic_file(uint16_t record_size, Win16FarPtr record)
{
open_or_create_generic_file(record_size, record, true);
}
/*
* 1020:0c76 -- when Append opens an ordinary DOS file, inspect only its last
* 128 bytes and remove the first DOS text EOF marker (Ctrl-Z) found there.
* Every INT 21h error is deliberately ignored by the original helper.
*/
void borland_text_truncate_at_ctrl_z(Win16FarPtr record)
{
uint16_t handle = win16_read_u16(record);
Win16DosExtendedResult end =
win16_dos_seek_extended(handle, 0, 2);
uint32_t file_size = ((uint32_t)end.dx << 16) | end.ax;
uint32_t scan_start = file_size >= 128 ? file_size - 128 : 0;
(void)win16_dos_seek_extended(handle, scan_start, 0);
Win16FarPtr buffer = win16_far_add_offset(record, 0x80);
Win16DosResult read = win16_dos_read(handle, buffer, 128);
uint16_t bytes_read = read.carry ? 0 : read.ax;
for (uint16_t index = 0; index != bytes_read; index++) {
if (win16_read_u8(win16_far_add_offset(buffer, index)) != 0x1a) {
continue;
}
/* CX:DX = FFFF:(index-bytes_read), exactly as the 16-bit SUB forms it. */
uint32_t relative_offset =
0xffff0000u | (uint16_t)(index - bytes_read);
Win16DosExtendedResult position =
win16_dos_seek_extended(handle, relative_offset, 2);
Win16FarPtr ignored_buffer = win16_make_far_pointer(
win16_far_selector(record), position.dx);
(void)win16_dos_write(handle, ignored_buffer, 0);
return;
}
}
static void write_text_procedure(
Win16FarPtr record, uint16_t offset, Win16FarPtr procedure)
{
win16_write_u16(record, offset, win16_far_offset(procedure));
win16_write_u16(
record,
(uint16_t)(offset + 2),
win16_far_selector(procedure));
}
/*
* 1020:0be9 -- TextRec OpenFunc. The incoming mode selects Reset (input),
* Rewrite (create output), or Append (read/write output). On success it
* installs the relocated Borland buffer callbacks used by the generic text
* dispatcher. DOS character devices use the unbuffered write callback in
* both InOutFunc and FlushFunc; ordinary files use the buffered flush callback
* in InOutFunc and a null FlushFunc.
*/
uint16_t borland_open_text_record(Win16FarPtr record)
{
enum {
TEXTREC_MODE_OFFSET = 2,
TEXTREC_NAME_OFFSET = 0x30,
TEXTREC_IN_OUT_PROCEDURE_OFFSET = 0x14,
TEXTREC_FLUSH_PROCEDURE_OFFSET = 0x18,
TEXTREC_CLOSE_PROCEDURE_OFFSET = 0x1c,
BORLAND_READ_BUFFER_OFFSET = 0x05d8,
BORLAND_FLUSH_BUFFER_OFFSET = 0x0608,
BORLAND_WRITE_BUFFER_OFFSET = 0x062d,
BORLAND_CLOSE_HANDLE_OFFSET = 0x064d,
};
uint16_t mode = win16_read_u16(
win16_far_add_offset(record, TEXTREC_MODE_OFFSET));
uint16_t default_handle = mode == BORLAND_FILE_INPUT ? 0 : 1;
win16_write_u16(record, 0, default_handle);
Win16FarPtr name = win16_far_add_offset(record, TEXTREC_NAME_OFFSET);
if (win16_read_u8(name) != 0) {
Win16DosResult opened;
if (mode == BORLAND_FILE_INPUT) {
opened = win16_dos_open(name, 0);
} else if (mode == BORLAND_TEXT_APPEND) {
opened = win16_dos_open(name, 2);
} else {
opened = win16_dos_create(name, 0);
}
if (opened.carry) {
return opened.ax;
}
win16_write_u16(record, 0, opened.ax);
}
Win16FarPtr in_out_procedure = win16_relocated_code_pointer(
5, BORLAND_READ_BUFFER_OFFSET);
Win16FarPtr flush_procedure = 0;
if (mode != BORLAND_FILE_INPUT) {
Win16DosExtendedResult device = win16_dos_get_device_info(
win16_read_u16(record));
if ((device.dx & 0x0080u) != 0) {
in_out_procedure = win16_relocated_code_pointer(
5, BORLAND_WRITE_BUFFER_OFFSET);
flush_procedure = in_out_procedure;
} else {
if (mode == BORLAND_TEXT_APPEND) {
borland_text_truncate_at_ctrl_z(record);
}
in_out_procedure = win16_relocated_code_pointer(
5, BORLAND_FLUSH_BUFFER_OFFSET);
}
win16_write_u16(record, TEXTREC_MODE_OFFSET, BORLAND_FILE_OUTPUT);
}
write_text_procedure(
record, TEXTREC_IN_OUT_PROCEDURE_OFFSET, in_out_procedure);
write_text_procedure(
record, TEXTREC_FLUSH_PROCEDURE_OFFSET, flush_procedure);
write_text_procedure(
record,
TEXTREC_CLOSE_PROCEDURE_OFFSET,
win16_relocated_code_pointer(5, BORLAND_CLOSE_HANDLE_OFFSET));
return 0;
}
/* 1008:2374 — stored FileProc callback; receiver ignored, AX=0. */
uint16_t tdkpin_file_close_noop(Win16FarPtr record)
{
(void)record;
return 0;
}