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
573 lines
20 KiB
C
573 lines
20 KiB
C
/*
|
|
* Borland TPWinCrt window runtime reconstructed from TDKPIN.EXE.
|
|
*
|
|
* Target: a9022f1894e3e6e21fc42e8f6c932f7c549ca77f63aaa0c488bb9d55d9d0174c
|
|
* Function: 1008:2219 (NE export ordinal 119)
|
|
* Registration: WNDCLASS16 at DGROUP 1028:0562, relocation source 1028:0564
|
|
*
|
|
* The original callback is a 10-byte far-Pascal WNDPROC16. Therefore its
|
|
* source parameters are HWND16, UINT16, WPARAM16, and a 32-bit LPARAM—not the
|
|
* five guessed parameters shown by an untyped decompiler signature.
|
|
*/
|
|
|
|
#include "tdkpin_runtime_ui.h"
|
|
#include "tdkpin_borland_runtime.h"
|
|
|
|
/* Address-linked dependencies reconstructed in later rows of the same module. */
|
|
void tpwincrt_on_create(void); /* 1008:1d77 */
|
|
void tpwincrt_on_paint(void); /* 1008:1dcc */
|
|
void tpwincrt_handle_scroll(uint16_t thumb_position,
|
|
uint16_t command,
|
|
uint16_t vertical); /* 1008:1f0c */
|
|
void tpwincrt_on_size(int16_t client_width, int16_t client_height); /* 1008:1f68 */
|
|
void tpwincrt_update_scrollbars(void); /* 1008:1865 */
|
|
void tpwincrt_fill_minmax_info(Win16FarPtr minmax_info); /* 1008:1fe8 */
|
|
void tpwincrt_queue_character(uint8_t character); /* 1008:211a */
|
|
void tpwincrt_handle_keydown(uint8_t virtual_key); /* 1008:2147 */
|
|
void tpwincrt_on_set_focus(void); /* 1008:21ba */
|
|
void tpwincrt_on_kill_focus(void); /* 1008:21ce */
|
|
void tpwincrt_on_destroy(void); /* 1008:21e2 */
|
|
|
|
void tpwincrt_abort_on_ctrl_c(void); /* 1008:18d0 */
|
|
void tpwincrt_create_caret(void); /* 1008:1818 */
|
|
void tpwincrt_destroy_caret(void); /* 1008:185b */
|
|
/* 1008:172f and 1008:1754 — signed 16-bit bounds helpers. */
|
|
int16_t tpwincrt_min_i16(int16_t left, int16_t right)
|
|
{
|
|
return right < left ? right : left;
|
|
}
|
|
|
|
int16_t tpwincrt_max_i16(int16_t left, int16_t right)
|
|
{
|
|
return left < right ? right : left;
|
|
}
|
|
|
|
/*
|
|
* 1008:1e8a — apply one Win16 scrollbar command.
|
|
*
|
|
* The binary receives a near SS pointer to 1008:1f0c's BP and reads the two
|
|
* incoming message words at +4/+6. This typed view preserves that intentional
|
|
* caller-frame aliasing while all additions/subtractions retain 16-bit wrap.
|
|
*/
|
|
int16_t tpwincrt_apply_scroll_command(
|
|
const TpWinCrtScrollMessage *message,
|
|
int16_t maximum,
|
|
int16_t page,
|
|
int16_t current)
|
|
{
|
|
switch (message->command) {
|
|
case TPWINCRT_SB_LINEUP:
|
|
return (int16_t)((uint16_t)current - 1);
|
|
case TPWINCRT_SB_LINEDOWN:
|
|
return (int16_t)((uint16_t)current + 1);
|
|
case TPWINCRT_SB_PAGEUP:
|
|
return (int16_t)((uint16_t)current - (uint16_t)page);
|
|
case TPWINCRT_SB_PAGEDOWN:
|
|
return (int16_t)((uint16_t)current + (uint16_t)page);
|
|
case TPWINCRT_SB_TOP:
|
|
return 0;
|
|
case TPWINCRT_SB_BOTTOM:
|
|
return maximum;
|
|
case TPWINCRT_SB_THUMBPOSITION:
|
|
return message->thumb_position;
|
|
default:
|
|
return current;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 1008:19f8 — address one character in the circular screen buffer.
|
|
*
|
|
* The row adjustment and all address arithmetic wrap at 16 bits. The original
|
|
* performs one signed wrap test only and deliberately leaves the selector
|
|
* unchanged when the computed offset wraps.
|
|
*/
|
|
Win16FarPtr tpwincrt_cell_pointer(int16_t logical_row, int16_t column)
|
|
{
|
|
int16_t physical_row =
|
|
(int16_t)((uint16_t)logical_row + g_tpwincrt_ring_row_origin);
|
|
if (physical_row >= (int16_t)g_tpwincrt_rows) {
|
|
physical_row = (int16_t)(physical_row - (int16_t)g_tpwincrt_rows);
|
|
}
|
|
|
|
uint16_t relative = (uint16_t)(
|
|
(uint16_t)((uint16_t)physical_row * g_tpwincrt_columns) +
|
|
(uint16_t)column);
|
|
uint16_t target_offset =
|
|
(uint16_t)(win16_far_offset(g_tpwincrt_screen_buffer) + relative);
|
|
return win16_make_far_pointer(
|
|
win16_far_selector(g_tpwincrt_screen_buffer), target_offset);
|
|
}
|
|
|
|
/*
|
|
* 1008:18ee — clamp and apply the text viewport origin.
|
|
*
|
|
* The pixel deltas use unsigned MUL and retain AX only. Passing those bits as
|
|
* INT16 preserves Win16 ScrollWindow's signed interpretation after wrap.
|
|
*/
|
|
void tpwincrt_scroll_to(int16_t target_column, int16_t target_row)
|
|
{
|
|
if (!g_tpwincrt_window_created) {
|
|
return;
|
|
}
|
|
|
|
int16_t new_column = tpwincrt_max_i16(
|
|
tpwincrt_min_i16((int16_t)g_tpwincrt_max_view_column, target_column), 0);
|
|
int16_t new_row = tpwincrt_max_i16(
|
|
tpwincrt_min_i16((int16_t)g_tpwincrt_max_view_row, target_row), 0);
|
|
|
|
if (new_column == (int16_t)g_tpwincrt_view_column &&
|
|
new_row == (int16_t)g_tpwincrt_view_row) {
|
|
return;
|
|
}
|
|
if (new_column != (int16_t)g_tpwincrt_view_column) {
|
|
(void)SetScrollPos16(g_tpwincrt_window, 0, new_column, 1);
|
|
}
|
|
if (new_row != (int16_t)g_tpwincrt_view_row) {
|
|
(void)SetScrollPos16(g_tpwincrt_window, 1, new_row, 1);
|
|
}
|
|
|
|
int16_t delta_x = (int16_t)(uint16_t)(
|
|
(uint16_t)(g_tpwincrt_view_column - (uint16_t)new_column) *
|
|
g_tpwincrt_character_width);
|
|
int16_t delta_y = (int16_t)(uint16_t)(
|
|
(uint16_t)(g_tpwincrt_view_row - (uint16_t)new_row) *
|
|
g_tpwincrt_character_height);
|
|
(void)ScrollWindow16(g_tpwincrt_window, delta_x, delta_y, 0, 0);
|
|
|
|
g_tpwincrt_view_column = (uint16_t)new_column;
|
|
g_tpwincrt_view_row = (uint16_t)new_row;
|
|
(void)UpdateWindow16(g_tpwincrt_window);
|
|
}
|
|
|
|
/*
|
|
* 1008:1f0c — decode WM_HSCROLL/WM_VSCROLL and move the matching axis.
|
|
* The non-moving axis is copied unchanged before the common scroll routine.
|
|
*/
|
|
void tpwincrt_handle_scroll(
|
|
uint16_t thumb_position, uint16_t command, uint16_t vertical)
|
|
{
|
|
TpWinCrtScrollMessage message = {
|
|
.thumb_position = (int16_t)thumb_position,
|
|
.command = command,
|
|
};
|
|
int16_t column = (int16_t)g_tpwincrt_view_column;
|
|
int16_t row = (int16_t)g_tpwincrt_view_row;
|
|
|
|
if (vertical == 0) {
|
|
column = tpwincrt_apply_scroll_command(
|
|
&message,
|
|
(int16_t)g_tpwincrt_max_view_column,
|
|
(int16_t)((int16_t)g_tpwincrt_visible_columns / 2),
|
|
column);
|
|
} else if (vertical == 1) {
|
|
row = tpwincrt_apply_scroll_command(
|
|
&message,
|
|
(int16_t)g_tpwincrt_max_view_row,
|
|
(int16_t)g_tpwincrt_visible_rows,
|
|
row);
|
|
}
|
|
tpwincrt_scroll_to(column, row);
|
|
}
|
|
|
|
/*
|
|
* 1008:1f68 — WM_SIZE handler.
|
|
* Signed IDIV derives visible character dimensions. Subsequent subtraction
|
|
* wraps at 16 bits before the signed max/min bounds are applied.
|
|
*/
|
|
void tpwincrt_on_size(int16_t client_width, int16_t client_height)
|
|
{
|
|
if (g_tpwincrt_has_focus && g_tpwincrt_caret_enabled) {
|
|
tpwincrt_destroy_caret();
|
|
}
|
|
|
|
g_tpwincrt_visible_columns =
|
|
(uint16_t)(client_width / (int16_t)g_tpwincrt_character_width);
|
|
g_tpwincrt_visible_rows =
|
|
(uint16_t)(client_height / (int16_t)g_tpwincrt_character_height);
|
|
g_tpwincrt_max_view_column = (uint16_t)tpwincrt_max_i16(
|
|
(int16_t)(g_tpwincrt_columns - g_tpwincrt_visible_columns), 0);
|
|
g_tpwincrt_max_view_row = (uint16_t)tpwincrt_max_i16(
|
|
(int16_t)(g_tpwincrt_rows - g_tpwincrt_visible_rows), 0);
|
|
g_tpwincrt_view_column = (uint16_t)tpwincrt_min_i16(
|
|
(int16_t)g_tpwincrt_max_view_column, (int16_t)g_tpwincrt_view_column);
|
|
g_tpwincrt_view_row = (uint16_t)tpwincrt_min_i16(
|
|
(int16_t)g_tpwincrt_max_view_row, (int16_t)g_tpwincrt_view_row);
|
|
|
|
tpwincrt_update_scrollbars();
|
|
if (g_tpwincrt_has_focus && g_tpwincrt_caret_enabled) {
|
|
tpwincrt_create_caret();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 1008:1d77 — WM_CREATE handler.
|
|
* Both size calculations intentionally retain only the low word of unsigned
|
|
* MUL. When Ctrl-C checking is disabled, the system Close command is disabled.
|
|
*/
|
|
void tpwincrt_on_create(void)
|
|
{
|
|
g_tpwincrt_window_created = 1;
|
|
uint16_t buffer_bytes =
|
|
(uint16_t)((uint32_t)g_tpwincrt_columns * g_tpwincrt_rows);
|
|
g_tpwincrt_screen_buffer = borland_get_mem(buffer_bytes);
|
|
borland_fill_bytes(g_tpwincrt_screen_buffer, buffer_bytes, ' ');
|
|
|
|
if (!g_tpwincrt_break_check_enabled) {
|
|
HMENU16 system_menu = GetSystemMenu16(g_tpwincrt_window, 0);
|
|
(void)EnableMenuItem16(system_menu, 0xf060, 3);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 1008:1779 — acquire and configure a TPWinCrt drawing context.
|
|
* WM_PAINT uses BeginPaint16 and the persistent PAINTSTRUCT16 at 1028:4ad6;
|
|
* non-paint drawing uses GetDC16. The selected stock font is restored later.
|
|
*/
|
|
void tpwincrt_begin_drawing(void)
|
|
{
|
|
enum {
|
|
SYSTEM_FIXED_FONT = 0x10,
|
|
COLOR_WINDOW = 5,
|
|
COLOR_WINDOWTEXT = 8,
|
|
DGROUP_PAINT_STRUCT = 0x4ad6,
|
|
};
|
|
|
|
if (g_tpwincrt_paint_message_active) {
|
|
g_tpwincrt_paint_dc = BeginPaint16(
|
|
g_tpwincrt_window, win16_dgroup_pointer(DGROUP_PAINT_STRUCT));
|
|
} else {
|
|
g_tpwincrt_paint_dc = GetDC16(g_tpwincrt_window);
|
|
}
|
|
HGDIOBJ16 font = GetStockObject16(SYSTEM_FIXED_FONT);
|
|
g_tpwincrt_previous_font = SelectObject16(g_tpwincrt_paint_dc, font);
|
|
(void)SetTextColor16(
|
|
g_tpwincrt_paint_dc, GetSysColor16(COLOR_WINDOWTEXT));
|
|
(void)SetBkColor16(g_tpwincrt_paint_dc, GetSysColor16(COLOR_WINDOW));
|
|
}
|
|
|
|
/* 1008:17e2 — restore the font and release the matching DC acquisition path. */
|
|
void tpwincrt_end_drawing(void)
|
|
{
|
|
enum { DGROUP_PAINT_STRUCT = 0x4ad6 };
|
|
|
|
(void)SelectObject16(g_tpwincrt_paint_dc, g_tpwincrt_previous_font);
|
|
if (g_tpwincrt_paint_message_active) {
|
|
(void)EndPaint16(
|
|
g_tpwincrt_window, win16_dgroup_pointer(DGROUP_PAINT_STRUCT));
|
|
} else {
|
|
(void)ReleaseDC16(g_tpwincrt_window, g_tpwincrt_paint_dc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 1008:1dcc — WM_PAINT handler for the circular character buffer.
|
|
* Paint bounds are converted with signed IDIV; right/bottom use the original
|
|
* add-(cell-1) rounding with 16-bit wrap before division.
|
|
*/
|
|
void tpwincrt_on_paint(void)
|
|
{
|
|
g_tpwincrt_paint_message_active = 1;
|
|
tpwincrt_begin_drawing();
|
|
|
|
int16_t first_column = tpwincrt_max_i16(
|
|
(int16_t)((uint16_t)(g_tpwincrt_paint.paint.left /
|
|
(int16_t)g_tpwincrt_character_width) +
|
|
g_tpwincrt_view_column),
|
|
0);
|
|
int16_t rounded_right = (int16_t)(
|
|
(uint16_t)g_tpwincrt_paint.paint.right + g_tpwincrt_character_width - 1);
|
|
int16_t past_last_column = tpwincrt_min_i16(
|
|
(int16_t)g_tpwincrt_columns,
|
|
(int16_t)((uint16_t)(rounded_right /
|
|
(int16_t)g_tpwincrt_character_width) +
|
|
g_tpwincrt_view_column));
|
|
|
|
int16_t first_row = tpwincrt_max_i16(
|
|
(int16_t)((uint16_t)(g_tpwincrt_paint.paint.top /
|
|
(int16_t)g_tpwincrt_character_height) +
|
|
g_tpwincrt_view_row),
|
|
0);
|
|
int16_t rounded_bottom = (int16_t)(
|
|
(uint16_t)g_tpwincrt_paint.paint.bottom + g_tpwincrt_character_height - 1);
|
|
int16_t past_last_row = tpwincrt_min_i16(
|
|
(int16_t)g_tpwincrt_rows,
|
|
(int16_t)((uint16_t)(rounded_bottom /
|
|
(int16_t)g_tpwincrt_character_height) +
|
|
g_tpwincrt_view_row));
|
|
|
|
for (int16_t row = first_row; row < past_last_row;
|
|
row = (int16_t)((uint16_t)row + 1)) {
|
|
int16_t x = (int16_t)(uint16_t)(
|
|
(uint16_t)(first_column - (int16_t)g_tpwincrt_view_column) *
|
|
g_tpwincrt_character_width);
|
|
int16_t y = (int16_t)(uint16_t)(
|
|
(uint16_t)(row - (int16_t)g_tpwincrt_view_row) *
|
|
g_tpwincrt_character_height);
|
|
Win16FarPtr text = tpwincrt_cell_pointer(row, first_column);
|
|
int16_t count = (int16_t)(past_last_column - first_column);
|
|
(void)TextOut16(g_tpwincrt_paint_dc, x, y, text, count);
|
|
}
|
|
|
|
tpwincrt_end_drawing();
|
|
g_tpwincrt_paint_message_active = 0;
|
|
}
|
|
|
|
/*
|
|
* 1008:1fe8 — WM_GETMINMAXINFO handler and font-metric initialization.
|
|
* Only six words of the caller's MINMAXINFO16 are written, matching the binary;
|
|
* ptReserved and ptMaxPosition are intentionally left untouched.
|
|
*/
|
|
void tpwincrt_fill_minmax_info(Win16FarPtr minmax_info)
|
|
{
|
|
enum {
|
|
SM_CXSCREEN = 0,
|
|
SM_CYSCREEN = 1,
|
|
SM_CXVSCROLL = 2,
|
|
SM_CYHSCROLL = 3,
|
|
SM_CYCAPTION = 4,
|
|
SM_CXFRAME = 32,
|
|
SM_CYFRAME = 33,
|
|
};
|
|
|
|
tpwincrt_begin_drawing();
|
|
TEXTMETRIC16 metrics;
|
|
(void)GetTextMetrics16(
|
|
g_tpwincrt_paint_dc, win16_stack_pointer(&metrics));
|
|
g_tpwincrt_character_width = (uint16_t)metrics.maximum_character_width;
|
|
g_tpwincrt_character_height =
|
|
(uint16_t)(metrics.height + metrics.external_leading);
|
|
g_tpwincrt_caret_y_offset = (uint16_t)metrics.ascent;
|
|
|
|
uint16_t frame_width_twice =
|
|
(uint16_t)((uint16_t)GetSystemMetrics16(SM_CXFRAME) << 1);
|
|
uint16_t maximum_client_width = (uint16_t)(
|
|
(uint16_t)(g_tpwincrt_columns * g_tpwincrt_character_width) +
|
|
(uint16_t)GetSystemMetrics16(SM_CXVSCROLL));
|
|
uint16_t maximum_width = (uint16_t)(
|
|
(uint16_t)tpwincrt_min_i16(
|
|
(int16_t)maximum_client_width,
|
|
GetSystemMetrics16(SM_CXSCREEN)) +
|
|
frame_width_twice);
|
|
|
|
uint16_t frame_height_twice =
|
|
(uint16_t)((uint16_t)GetSystemMetrics16(SM_CYFRAME) << 1);
|
|
uint16_t caption_height = (uint16_t)GetSystemMetrics16(SM_CYCAPTION);
|
|
uint16_t maximum_client_height = (uint16_t)(
|
|
(uint16_t)(g_tpwincrt_rows * g_tpwincrt_character_height) +
|
|
(uint16_t)GetSystemMetrics16(SM_CYHSCROLL) + caption_height);
|
|
uint16_t maximum_height = (uint16_t)(
|
|
(uint16_t)tpwincrt_min_i16(
|
|
(int16_t)maximum_client_height,
|
|
GetSystemMetrics16(SM_CYSCREEN)) +
|
|
frame_height_twice);
|
|
|
|
uint16_t minimum_width = (uint16_t)(
|
|
(uint16_t)(g_tpwincrt_character_width << 4) +
|
|
(uint16_t)GetSystemMetrics16(SM_CXVSCROLL) + frame_width_twice);
|
|
uint16_t minimum_height = (uint16_t)(
|
|
(uint16_t)(g_tpwincrt_character_height << 2) +
|
|
(uint16_t)GetSystemMetrics16(SM_CYHSCROLL) +
|
|
(uint16_t)((uint16_t)GetSystemMetrics16(SM_CYFRAME) << 1) +
|
|
caption_height);
|
|
|
|
win16_write_u16(minmax_info, 4, maximum_width);
|
|
win16_write_u16(minmax_info, 6, maximum_height);
|
|
win16_write_u16(minmax_info, 12, minimum_width);
|
|
win16_write_u16(minmax_info, 14, minimum_height);
|
|
win16_write_u16(minmax_info, 16, maximum_width);
|
|
win16_write_u16(minmax_info, 18, maximum_height);
|
|
tpwincrt_end_drawing();
|
|
}
|
|
|
|
/* 1008:2147 — translate one WM_KEYDOWN through the exact 12-entry table. */
|
|
void tpwincrt_handle_keydown(uint8_t virtual_key)
|
|
{
|
|
if (g_tpwincrt_break_check_enabled && virtual_key == 3) {
|
|
tpwincrt_abort_on_ctrl_c();
|
|
}
|
|
|
|
uint8_t control_down = GetKeyState16(0x11) < 0;
|
|
for (uint16_t index = 0; index < 12; index++) {
|
|
const TpWinCrtKeyBinding *binding = &g_tpwincrt_key_bindings[index];
|
|
if (binding->virtual_key == virtual_key &&
|
|
binding->control_required == control_down) {
|
|
tpwincrt_handle_scroll(0, binding->scroll_command, binding->vertical);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 1008:1865 — publish horizontal and vertical scrollbar ranges/positions. */
|
|
void tpwincrt_update_scrollbars(void)
|
|
{
|
|
int16_t horizontal_max =
|
|
tpwincrt_max_i16((int16_t)g_tpwincrt_max_view_column, 1);
|
|
(void)SetScrollRange16(g_tpwincrt_window, 0, 0, horizontal_max, 0);
|
|
(void)SetScrollPos16(
|
|
g_tpwincrt_window, 0, (int16_t)g_tpwincrt_view_column, 1);
|
|
|
|
int16_t vertical_max =
|
|
tpwincrt_max_i16((int16_t)g_tpwincrt_max_view_row, 1);
|
|
(void)SetScrollRange16(g_tpwincrt_window, 1, 0, vertical_max, 0);
|
|
(void)SetScrollPos16(
|
|
g_tpwincrt_window, 1, (int16_t)g_tpwincrt_view_row, 1);
|
|
}
|
|
|
|
/* 1008:19b7 — scroll only far enough to keep the cursor visible. */
|
|
void tpwincrt_scroll_cursor_into_view(void)
|
|
{
|
|
int16_t leftmost = (int16_t)(
|
|
(uint16_t)(g_tpwincrt_cursor_column - g_tpwincrt_visible_columns) + 1);
|
|
int16_t target_column = tpwincrt_max_i16(
|
|
tpwincrt_min_i16(
|
|
(int16_t)g_tpwincrt_cursor_column, (int16_t)g_tpwincrt_view_column),
|
|
leftmost);
|
|
|
|
int16_t topmost = (int16_t)(
|
|
(uint16_t)(g_tpwincrt_cursor_row - g_tpwincrt_visible_rows) + 1);
|
|
int16_t target_row = tpwincrt_max_i16(
|
|
tpwincrt_min_i16(
|
|
(int16_t)g_tpwincrt_cursor_row, (int16_t)g_tpwincrt_view_row),
|
|
topmost);
|
|
|
|
tpwincrt_scroll_to(target_column, target_row);
|
|
}
|
|
|
|
/*
|
|
* 1008:2219 — process-wide TPWinCrt WNDPROC16.
|
|
*
|
|
* Every handled message returns zero, exactly as the zeroed DX:AX local in the
|
|
* binary. Only unhandled messages preserve DefWindowProc16's 32-bit result.
|
|
* WM_SIZE carries width in LOWORD(lParam) and height in HIWORD(lParam).
|
|
*/
|
|
LRESULT16 tpwincrt_window_proc(
|
|
HWND16 window, UINT16 message, WPARAM16 wparam, LPARAM16 lparam)
|
|
{
|
|
g_tpwincrt_window = window;
|
|
|
|
switch (message) {
|
|
case WIN16_WM_CREATE:
|
|
tpwincrt_on_create();
|
|
break;
|
|
case WIN16_WM_PAINT:
|
|
tpwincrt_on_paint();
|
|
break;
|
|
case WIN16_WM_VSCROLL:
|
|
tpwincrt_handle_scroll((uint16_t)lparam, wparam, 1);
|
|
break;
|
|
case WIN16_WM_HSCROLL:
|
|
tpwincrt_handle_scroll((uint16_t)lparam, wparam, 0);
|
|
break;
|
|
case WIN16_WM_SIZE:
|
|
tpwincrt_on_size((int16_t)lparam, (int16_t)((uint32_t)lparam >> 16));
|
|
break;
|
|
case WIN16_WM_GETMINMAXINFO:
|
|
tpwincrt_fill_minmax_info((Win16FarPtr)lparam);
|
|
break;
|
|
case WIN16_WM_CHAR:
|
|
tpwincrt_queue_character((uint8_t)wparam);
|
|
break;
|
|
case WIN16_WM_KEYDOWN:
|
|
tpwincrt_handle_keydown((uint8_t)wparam);
|
|
break;
|
|
case WIN16_WM_SETFOCUS:
|
|
tpwincrt_on_set_focus();
|
|
break;
|
|
case WIN16_WM_KILLFOCUS:
|
|
tpwincrt_on_kill_focus();
|
|
break;
|
|
case WIN16_WM_DESTROY:
|
|
tpwincrt_on_destroy();
|
|
break;
|
|
default:
|
|
return DefWindowProc16(window, message, wparam, lparam);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* 1008:211a — append one WM_CHAR byte to the fixed 64-byte input queue. */
|
|
void tpwincrt_queue_character(uint8_t character)
|
|
{
|
|
if (g_tpwincrt_break_check_enabled && character == 3) {
|
|
tpwincrt_abort_on_ctrl_c();
|
|
}
|
|
if ((int16_t)g_tpwincrt_input_count < 64) {
|
|
g_tpwincrt_input_buffer[g_tpwincrt_input_count] = character;
|
|
g_tpwincrt_input_count++;
|
|
}
|
|
}
|
|
|
|
/* 1008:21ba — WM_SETFOCUS handler. */
|
|
void tpwincrt_on_set_focus(void)
|
|
{
|
|
g_tpwincrt_has_focus = 1;
|
|
if (g_tpwincrt_caret_enabled) {
|
|
tpwincrt_create_caret();
|
|
}
|
|
}
|
|
|
|
/* 1008:21ce — WM_KILLFOCUS handler. */
|
|
void tpwincrt_on_kill_focus(void)
|
|
{
|
|
if (g_tpwincrt_caret_enabled) {
|
|
tpwincrt_destroy_caret();
|
|
}
|
|
g_tpwincrt_has_focus = 0;
|
|
}
|
|
|
|
/*
|
|
* 1008:21e2 — WM_DESTROY handler.
|
|
* MUL leaves the low 16 bits in AX, so the buffer byte count intentionally
|
|
* wraps at 16 bits before it is passed to Borland's far-heap release routine.
|
|
*/
|
|
void tpwincrt_on_destroy(void)
|
|
{
|
|
uint16_t buffer_bytes =
|
|
(uint16_t)((uint32_t)g_tpwincrt_columns * g_tpwincrt_rows);
|
|
|
|
borland_free_mem(g_tpwincrt_screen_buffer, buffer_bytes);
|
|
g_tpwincrt_cursor_column = 0;
|
|
g_tpwincrt_cursor_row = 0;
|
|
g_tpwincrt_view_column = 0;
|
|
g_tpwincrt_view_row = 0;
|
|
PostQuitMessage16(0);
|
|
g_tpwincrt_window_created = 0;
|
|
}
|
|
|
|
/*
|
|
* 1008:1818 — create, position, and show the text caret.
|
|
* Both coordinate multiplications intentionally retain only AX, matching the
|
|
* unsigned 16-bit MUL instructions at 1818:1836 and 1818:1842.
|
|
*/
|
|
void tpwincrt_create_caret(void)
|
|
{
|
|
uint16_t x = (uint16_t)(
|
|
(uint16_t)(g_tpwincrt_cursor_column - g_tpwincrt_view_column) *
|
|
g_tpwincrt_character_width);
|
|
uint16_t y = (uint16_t)(
|
|
(uint16_t)(g_tpwincrt_cursor_row - g_tpwincrt_view_row) *
|
|
g_tpwincrt_character_height +
|
|
g_tpwincrt_caret_y_offset);
|
|
|
|
(void)CreateCaret16(
|
|
g_tpwincrt_window, 0, (INT16)g_tpwincrt_character_width, 2);
|
|
(void)SetCaretPos16((INT16)x, (INT16)y);
|
|
(void)ShowCaret16(g_tpwincrt_window);
|
|
}
|
|
|
|
/* 1008:185b — destroy the process caret; the BOOL16 result is ignored. */
|
|
void tpwincrt_destroy_caret(void)
|
|
{
|
|
(void)DestroyCaret16();
|
|
}
|
|
|
|
/* 1008:18d0 — Ctrl-C termination path. */
|
|
_Noreturn void tpwincrt_abort_on_ctrl_c(void)
|
|
{
|
|
if (g_tpwincrt_has_focus && g_tpwincrt_caret_enabled) {
|
|
tpwincrt_destroy_caret();
|
|
}
|
|
borland_runtime_exit(0xff);
|
|
}
|