/* Address-linked keyboard handlers at 1000:638e and 1000:6bf8. */ #include "tdkpin_key_input.h" #include "tdkpin_application.h" #include "tdkpin_arithmetic.h" #include "tdkpin_random.h" #include "tdkpin_real48.h" #include "tdkpin_sound.h" static Win16FarPtr application_virtual_method( Win16FarPtr application, uint16_t slot) { uint16_t vmt = win16_read_u16(application); return win16_read_far_pointer(win16_dgroup_pointer(vmt), slot); } static bool no_auxiliary_windows(Win16FarPtr window) { return win16_read_u16(win16_far_add_offset(window, 0x41)) == 0 && win16_read_u16(win16_far_add_offset(window, 0x43)) == 0 && win16_read_u16(win16_far_add_offset(window, 0x45)) == 0 && win16_read_u16(win16_far_add_offset(window, 0x47)) == 0; } static uint16_t open_help_window(Win16FarPtr window) { Win16FarPtr child = tdkpin_full_child_window_construct( 0, 0x017e, win16_dgroup_pointer(0x0306), window); Win16FarPtr application = win16_read_far_pointer( win16_dgroup_pointer(0x064e), 0); child = win16_call_application_window_method( application_virtual_method(application, 0x34), application, child); win16_write_u32(window, 0x45, child); object_windows_show(win16_read_far_pointer(window, 0x45), 1); return win16_indeterminate_u16(); } static void apply_down_key(Win16FarPtr window) { int32_t scalar = (int32_t)win16_read_u32( win16_far_add_offset(window, 0x5a)); int32_t step = borland_multiply_i32(scalar, 50); uint32_t velocity = win16_read_u32( win16_far_add_offset(window, 0x0bae)); velocity -= (uint32_t)step; win16_write_u32(window, 0x0bae, velocity); win16_write_u32(window, 0x0baa, 0); if (win16_read_u8(win16_dgroup_pointer(0x07d0)) == 0) { return; } HWND16 handle = win16_read_u16(win16_far_add_offset(window, 4)); HDC16 dc = GetDC16(handle); INT16 y = 231; for (int32_t multiple = 1; multiple <= 10; multiple++) { int32_t threshold = borland_multiply_i32(step, -multiple); if (threshold < (int32_t)velocity) { y = (INT16)(multiple * 21); break; } } tdkpin_draw_board_decoration_row_on_dc(y, dc); (void)ReleaseDC16(handle, dc); } uint16_t tdkpin_handle_key_press( Win16FarPtr window, Win16FarPtr event) { if (no_auxiliary_windows(window)) { uint16_t scan_code = win16_read_u16( win16_far_add_offset(event, 8)) & 0x00ffu; if (scan_code == 0x3b) { return open_help_window(window); } if (win16_read_u8(win16_far_add_offset(window, 0x0bdd)) == 0 && win16_read_u8(win16_far_add_offset(window, 0x0bdf)) == 0 && scan_code == 0x50) { apply_down_key(window); } else if (win16_read_u8(win16_dgroup_pointer(0x07c6)) == 0) { if (scan_code == 0x1d) { win16_write_u8(win16_far_add_offset(window, 0x0bda), 1); } if (scan_code == 0x1c) { win16_write_u8(win16_far_add_offset(window, 0x0bdb), 1); } } } win16_write_u16(event, 0x0a, 0); win16_write_u16(event, 0x0c, 0); return 0; } static int32_t random_horizontal_nudge(int32_t scalar) { static const BorlandReal48 scale = {{0x85, 0x00, 0x00, 0x00, 0x00, 0x20}}; static const BorlandReal48 offset = {{0x84, 0x00, 0x00, 0x00, 0x00, 0x20}}; BorlandReal48 random = borland_random_real48_registers(); BorlandReal48 factor = borland_real48_subtract( borland_real48_multiply(random, scale), offset); return borland_real48_round_to_i32( borland_real48_multiply(borland_i32_to_real48(scalar), factor)); } static void apply_release_down_impulse(Win16FarPtr window) { int32_t scalar = (int32_t)win16_read_u32( win16_far_add_offset(window, 0x5a)); int32_t base = (int32_t)win16_read_u32( win16_far_add_offset(window, 0x56)); uint32_t velocity = win16_read_u32( win16_far_add_offset(window, 0x0bae)); velocity -= (uint32_t)borland_multiply_i32(scalar, 100); int32_t signed_velocity = (int32_t)velocity; uint32_t negative_base_bits = 0u - (uint32_t)base; int32_t negative_base = (int32_t)negative_base_bits; if (signed_velocity < negative_base) { velocity = negative_base_bits + borland_random_below((uint16_t)base) / 40u; } else { /* 1000:71eb loads Real48 0.6 as CX:SI:DI = * 9a80:9999:1999. Keep the exact target encoding here. */ static const BorlandReal48 factor_three_fifths = {{0x80, 0x9a, 0x99, 0x99, 0x99, 0x19}}; int32_t scaled_base = borland_real48_round_to_i32( borland_real48_multiply( borland_i32_to_real48(base), factor_three_fifths)); int32_t upper = (int32_t)(0u - (uint32_t)scaled_base); if (upper < signed_velocity) { velocity -= borland_random_below((uint16_t)base) / 40u; } } win16_write_u32(window, 0x0bae, velocity); win16_write_u32(window, 0x0baa, 0); win16_write_u32(win16_dgroup_pointer(0x07fb), 0, velocity); win16_write_u8(win16_far_add_offset(window, 0x0bdd), 1); win16_write_u8(win16_far_add_offset(window, 0x0bde), 0); tdkpin_play_sound_if_not_tilted(2); HWND16 handle = win16_read_u16(win16_far_add_offset(window, 4)); HDC16 dc = GetDC16(handle); tdkpin_blit_bitmap_and_present( win16_read_u16(win16_dgroup_pointer(0x0865)), 18, 21, 426, 314, dc); (void)ReleaseDC16(handle, dc); } static void add_wrap_to_dgroup(uint16_t address, int32_t amount) { Win16FarPtr value = win16_dgroup_pointer(address); win16_write_u32(value, 0, win16_read_u32(value) + (uint32_t)amount); } static void apply_nudge(Win16FarPtr window, uint16_t scan_code) { int32_t scalar = (int32_t)win16_read_u32( win16_far_add_offset(window, 0x5a)); tdkpin_play_sound_if_not_tilted(19); int32_t horizontal = 0; int32_t vertical = 0; if (scan_code == 0x2a || scan_code == 0x51) { int32_t amount = borland_multiply_i32( (int32_t)(50u - borland_random_below(20)), scalar); horizontal = scan_code == 0x2a ? -amount : amount; } else { horizontal = random_horizontal_nudge(scalar); vertical = (int32_t)(0u - (uint32_t)borland_multiply_i32( (int32_t)(60u - borland_random_below(20)), scalar)); } win16_write_u32( window, 0x0baa, win16_read_u32(win16_far_add_offset(window, 0x0baa)) + (uint32_t)horizontal); win16_write_u32( window, 0x0bae, win16_read_u32(win16_far_add_offset(window, 0x0bae)) + (uint32_t)vertical); if (win16_read_u8(win16_far_add_offset(window, 0x61)) > 1) { if (scan_code == 0x2a || scan_code == 0x51) { int32_t slot_amount = borland_multiply_i32(scalar, 40); if (scan_code == 0x2a) { slot_amount = (int32_t)(0u - (uint32_t)slot_amount); } add_wrap_to_dgroup(0x07f3, slot_amount); add_wrap_to_dgroup(0x07f7, slot_amount); } else { int32_t slot_x = borland_multiply_i32( (int32_t)borland_random_below(21) - 10, scalar); int32_t slot_y = (int32_t)(0u - (uint32_t)borland_multiply_i32( (int32_t)borland_random_below(100) + 50, scalar)); add_wrap_to_dgroup(0x07f3, slot_x); add_wrap_to_dgroup(0x07f7, slot_x); add_wrap_to_dgroup(0x07fb, slot_y); add_wrap_to_dgroup(0x07ff, slot_y); } } uint16_t counter = (uint16_t)( win16_read_u16(win16_far_add_offset(window, 0x5e)) + 25u); win16_write_u16(window, 0x5e, counter); uint16_t threshold = (uint16_t)(borland_random_below(10) + 30u); if (threshold < counter) { tdkpin_play_sound_if_not_tilted(20); win16_write_u8(win16_dgroup_pointer(0x07c6), 1); win16_write_u8(win16_far_add_offset(window, 0x0bda), 0); win16_write_u8(win16_far_add_offset(window, 0x0bdb), 0); HWND16 handle = win16_read_u16(win16_far_add_offset(window, 4)); HDC16 dc = GetDC16(handle); tdkpin_restore_background_region_a(20, 148, 243, 84, dc); tdkpin_present_background_region(20, 148, 243, 84, dc); (void)ReleaseDC16(handle, dc); } } static void toggle_sound(void) { uint8_t enabled = win16_read_u8(win16_dgroup_pointer(0x43f2)); if (enabled == 0) { if (win16_read_u16(win16_dgroup_pointer(0x43ee)) != 0) { win16_write_u8(win16_dgroup_pointer(0x43f2), 1); } } else { win16_write_u8(win16_dgroup_pointer(0x43f2), 0); } } static void add_player(Win16FarPtr window) { win16_write_u8(win16_dgroup_pointer(0x07c9), 0); tdkpin_play_sound_if_not_tilted(1); uint8_t player_count = (uint8_t)( win16_read_u8(win16_dgroup_pointer(0x07d0)) + 1u); win16_write_u8(win16_dgroup_pointer(0x07d0), player_count); win16_write_u8(win16_far_add_offset(window, 0x0bdd), 0); win16_write_u8(win16_far_add_offset(window, 0x0bdf), 0); win16_write_u8(win16_dgroup_pointer(0x07c5), 1); win16_write_u8(win16_far_add_offset(window, 0x61), 1); win16_write_u8(win16_dgroup_pointer(0x0874), 0); HWND16 handle = win16_read_u16(win16_far_add_offset(window, 4)); for (uint16_t player = 1; player <= 4; player++) { win16_write_u16( win16_dgroup_pointer((uint16_t)(0x081f + player * 2u)), 0, 0xffff); for (uint16_t id = 1; id <= 175; id++) { win16_write_u8( win16_far_add_offset(window, (uint16_t)( 0x0061 + player * 0x00afu + id)), win16_read_u8(win16_far_add_offset( window, (uint16_t)(0x03cc + id)))); win16_write_u16( window, (uint16_t)(0x036c + player * 0x015eu + id * 2u), win16_read_u16(win16_far_add_offset( window, (uint16_t)(0x0a42 + id * 2u)))); } HDC16 dc = GetDC16(handle); if (player_count < player) { win16_write_u32( win16_dgroup_pointer((uint16_t)(0x097e + player * 4u)), 0, 0); tdkpin_restore_background_region_b( 42, 284, (INT16)((player - 1u) * 45u + 270u), 348, dc); tdkpin_present_background_region( 42, 284, (INT16)((player - 1u) * 45u + 270u), 348, dc); } tdkpin_draw_item_status(0, dc); (void)ReleaseDC16(handle, dc); if (player == 1) { dc = GetDC16(handle); tdkpin_restore_background_region_a(30, 284, 232, 371, dc); tdkpin_present_background_region(30, 284, 232, 371, dc); tdkpin_draw_score(dc, 0); tdkpin_update_status_display(1, dc); (void)ReleaseDC16(handle, dc); } } for (uint16_t id = 1; id <= 175; id++) { Win16FarPtr record = win16_dgroup_pointer((uint16_t)( 0x095b + id * 0x53u)); win16_write_u8( win16_far_add_offset(record, 0x34), win16_read_u8(win16_far_add_offset( window, (uint16_t)(0x03cc + id)))); win16_write_u16( record, 0x43, win16_read_u16(win16_far_add_offset( window, (uint16_t)(0x0a42 + id * 2u)))); } win16_write_u8(win16_dgroup_pointer(0x07d1), 1); } uint16_t tdkpin_handle_key_release( Win16FarPtr window, Win16FarPtr event) { if (no_auxiliary_windows(window)) { uint16_t scan_code = win16_read_u16( win16_far_add_offset(event, 8)) & 0x00ffu; if (win16_read_u8(win16_dgroup_pointer(0x07cf)) != 0 && scan_code == 0x3c) { win16_write_u32(win16_dgroup_pointer(0x0855), 0, 0); win16_write_u8(win16_dgroup_pointer(0x07c7), 1); } if (win16_read_u8(win16_dgroup_pointer(0x07cf)) != 0 && scan_code == 0x41) { win16_write_u32( win16_dgroup_pointer(0x07d3), 0, win16_read_u32(win16_dgroup_pointer(0x3340))); win16_write_u32( win16_dgroup_pointer(0x07d7), 0, win16_read_u32(win16_dgroup_pointer(0x3344))); win16_write_u32(window, 0x0baa, 0); win16_write_u32(window, 0x0bae, 0); } if (win16_read_u8(win16_far_add_offset(window, 0x0bdd)) == 0 && win16_read_u8(win16_far_add_offset(window, 0x0bdf)) == 0 && scan_code == 0x50 && win16_read_u8(win16_dgroup_pointer(0x07d0)) != 0) { apply_release_down_impulse(window); } else if (scan_code == 0x1d) { win16_write_u8(win16_far_add_offset(window, 0x0bda), 0); } else if (scan_code == 0x1c) { win16_write_u8(win16_far_add_offset(window, 0x0bdb), 0); } else if (win16_read_u8(win16_dgroup_pointer(0x07c6)) == 0 && (scan_code == 0x2a || scan_code == 0x51 || scan_code == 0x39)) { apply_nudge(window, scan_code); } else if (scan_code == 0x58) { toggle_sound(); } else if (win16_read_u8( win16_far_add_offset(window, 0x0bde)) != 0 && (scan_code == 0x4e || scan_code == 0x1b) && win16_read_u8(win16_dgroup_pointer(0x07d0)) < 4) { add_player(window); } } win16_write_u16(event, 0x0a, 0); win16_write_u16(event, 0x0c, 0); return 0; }