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
242 lines
6.1 KiB
C
242 lines
6.1 KiB
C
/* Portable selector-backed implementation of explicit Win16 memory access. */
|
|
|
|
#include "tdkpin_segmented.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
enum { MAX_BOUND_SEGMENTS = 32 };
|
|
|
|
typedef struct {
|
|
uint16_t ne_segment;
|
|
uint16_t selector;
|
|
uint8_t *bytes;
|
|
size_t size;
|
|
bool writable;
|
|
} BoundSegment;
|
|
|
|
static BoundSegment g_segments[MAX_BOUND_SEGMENTS];
|
|
static size_t g_segment_count;
|
|
static uint16_t g_dgroup_selector;
|
|
static uint16_t g_stack_selector;
|
|
static uint16_t g_stack_pointer_offset;
|
|
static Win16FarPtr g_current_return_address;
|
|
static uint32_t g_dos_time_words;
|
|
static uint16_t g_indeterminate_u16;
|
|
|
|
static BoundSegment *find_segment(uint16_t selector)
|
|
{
|
|
for (size_t index = 0; index < g_segment_count; index++) {
|
|
if (g_segments[index].selector == selector) {
|
|
return &g_segments[index];
|
|
}
|
|
}
|
|
abort(); /* Invalid selectors fault in the original Win16 environment. */
|
|
}
|
|
|
|
static uint8_t *resolve(Win16FarPtr address, size_t bytes, bool write)
|
|
{
|
|
BoundSegment *segment = find_segment(win16_far_selector(address));
|
|
size_t offset = win16_far_offset(address);
|
|
if (offset + bytes > segment->size || (write && !segment->writable)) {
|
|
abort();
|
|
}
|
|
return segment->bytes + offset;
|
|
}
|
|
|
|
void win16_reset_segment_bindings(void)
|
|
{
|
|
g_segment_count = 0;
|
|
g_dgroup_selector = 0;
|
|
g_stack_selector = 0;
|
|
g_stack_pointer_offset = 0;
|
|
g_current_return_address = 0;
|
|
g_dos_time_words = 0;
|
|
g_indeterminate_u16 = 0;
|
|
}
|
|
|
|
void win16_bind_segment(
|
|
uint16_t selector, void *bytes, size_t size, bool writable)
|
|
{
|
|
win16_bind_ne_segment(0, selector, bytes, size, writable);
|
|
}
|
|
|
|
void win16_bind_ne_segment(
|
|
uint16_t ne_segment,
|
|
uint16_t selector,
|
|
void *bytes,
|
|
size_t size,
|
|
bool writable)
|
|
{
|
|
if (g_segment_count == MAX_BOUND_SEGMENTS || size > 0x10000) {
|
|
abort();
|
|
}
|
|
g_segments[g_segment_count++] =
|
|
(BoundSegment){ne_segment, selector, bytes, size, writable};
|
|
}
|
|
|
|
void win16_set_dgroup_selector(uint16_t selector)
|
|
{
|
|
(void)find_segment(selector);
|
|
g_dgroup_selector = selector;
|
|
}
|
|
|
|
void win16_set_current_return_address(Win16FarPtr address)
|
|
{
|
|
g_current_return_address = address;
|
|
}
|
|
|
|
void win16_set_dos_time_words(uint32_t words)
|
|
{
|
|
g_dos_time_words = words;
|
|
}
|
|
|
|
void win16_set_indeterminate_u16(uint16_t value)
|
|
{
|
|
g_indeterminate_u16 = value;
|
|
}
|
|
|
|
void win16_set_stack_state(uint16_t selector, uint16_t stack_pointer)
|
|
{
|
|
(void)find_segment(selector);
|
|
g_stack_selector = selector;
|
|
g_stack_pointer_offset = stack_pointer;
|
|
}
|
|
|
|
Win16FarPtr win16_dgroup_pointer(uint16_t offset)
|
|
{
|
|
if (g_dgroup_selector == 0) {
|
|
abort();
|
|
}
|
|
return win16_make_far_pointer(g_dgroup_selector, offset);
|
|
}
|
|
|
|
Win16FarPtr win16_relocated_code_pointer(uint16_t ne_segment, uint16_t offset)
|
|
{
|
|
for (size_t index = 0; index < g_segment_count; index++) {
|
|
if (g_segments[index].ne_segment == ne_segment) {
|
|
return win16_make_far_pointer(g_segments[index].selector, offset);
|
|
}
|
|
}
|
|
abort();
|
|
}
|
|
|
|
Win16FarPtr win16_stack_pointer(void *pointer)
|
|
{
|
|
uintptr_t value = (uintptr_t)pointer;
|
|
for (size_t index = 0; index < g_segment_count; index++) {
|
|
uintptr_t base = (uintptr_t)g_segments[index].bytes;
|
|
if (value >= base && value < base + g_segments[index].size) {
|
|
return win16_make_far_pointer(
|
|
g_segments[index].selector, (uint16_t)(value - base));
|
|
}
|
|
}
|
|
abort();
|
|
}
|
|
|
|
Win16FarPtr win16_current_stack_address(uint16_t offset)
|
|
{
|
|
if (g_stack_selector == 0) {
|
|
abort();
|
|
}
|
|
return win16_make_far_pointer(g_stack_selector, offset);
|
|
}
|
|
|
|
uint8_t win16_read_u8(Win16FarPtr address)
|
|
{
|
|
return *resolve(address, 1, false);
|
|
}
|
|
|
|
uint16_t win16_read_u16(Win16FarPtr address)
|
|
{
|
|
return (uint16_t)(
|
|
win16_read_u8(address) |
|
|
((uint16_t)win16_read_u8(win16_far_add_offset(address, 1)) << 8));
|
|
}
|
|
|
|
uint32_t win16_read_u32(Win16FarPtr address)
|
|
{
|
|
return (uint32_t)win16_read_u16(address) |
|
|
((uint32_t)win16_read_u16(win16_far_add_offset(address, 2)) << 16);
|
|
}
|
|
|
|
void win16_write_u8(Win16FarPtr address, uint8_t value)
|
|
{
|
|
*resolve(address, 1, true) = value;
|
|
}
|
|
|
|
Win16FarPtr win16_read_far_pointer(Win16FarPtr base, uint16_t byte_offset)
|
|
{
|
|
Win16FarPtr address = win16_far_add_offset(base, byte_offset);
|
|
uint16_t offset = win16_read_u16(address);
|
|
uint16_t selector = win16_read_u16(win16_far_add_offset(address, 2));
|
|
return win16_make_far_pointer(selector, offset);
|
|
}
|
|
|
|
void win16_write_u16(Win16FarPtr base, uint16_t byte_offset, uint16_t value)
|
|
{
|
|
Win16FarPtr address = win16_far_add_offset(base, byte_offset);
|
|
win16_write_u8(address, (uint8_t)value);
|
|
win16_write_u8(win16_far_add_offset(address, 1), (uint8_t)(value >> 8));
|
|
}
|
|
|
|
void win16_write_u32(Win16FarPtr base, uint16_t byte_offset, uint32_t value)
|
|
{
|
|
win16_write_u16(base, byte_offset, (uint16_t)value);
|
|
win16_write_u16(base, (uint16_t)(byte_offset + 2), (uint16_t)(value >> 16));
|
|
}
|
|
|
|
void win16_fill_bytes(Win16FarPtr destination, uint16_t count, uint8_t value)
|
|
{
|
|
for (uint16_t index = 0; index != count; index++) {
|
|
win16_write_u8(win16_far_add_offset(destination, index), value);
|
|
}
|
|
}
|
|
|
|
uint16_t win16_indeterminate_u16(void)
|
|
{
|
|
return g_indeterminate_u16;
|
|
}
|
|
|
|
uint32_t win16_dos_time_words(void)
|
|
{
|
|
return g_dos_time_words;
|
|
}
|
|
|
|
Win16FarPtr win16_current_return_address(void)
|
|
{
|
|
return g_current_return_address;
|
|
}
|
|
|
|
uint16_t win16_current_stack_pointer(void)
|
|
{
|
|
if (g_stack_selector == 0) {
|
|
abort();
|
|
}
|
|
return g_stack_pointer_offset;
|
|
}
|
|
|
|
uint16_t win16_read_stack_u16(uint16_t offset)
|
|
{
|
|
if (g_stack_selector == 0) {
|
|
abort();
|
|
}
|
|
return win16_read_u16(win16_make_far_pointer(g_stack_selector, offset));
|
|
}
|
|
|
|
void win16_write_stack_u16(uint16_t offset, uint16_t value)
|
|
{
|
|
if (g_stack_selector == 0) {
|
|
abort();
|
|
}
|
|
win16_write_u16(
|
|
win16_make_far_pointer(g_stack_selector, offset), 0, value);
|
|
}
|
|
|
|
uint16_t win16_exchange_u16(Win16FarPtr address, uint16_t value)
|
|
{
|
|
uint16_t previous = win16_read_u16(address);
|
|
win16_write_u16(address, 0, value);
|
|
return previous;
|
|
}
|