/* * 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); }