/* Small address-linked gameplay rendering and transition helpers. */ #include "tdkpin_gameplay_helpers.h" #include "tdkpin_arithmetic.h" #include "tdkpin_sound.h" static Win16FarPtr caller_receiver(uint16_t caller_bp) { uint16_t offset = win16_read_stack_u16((uint16_t)(caller_bp + 6)); uint16_t selector = win16_read_stack_u16((uint16_t)(caller_bp + 8)); return win16_make_far_pointer(selector, offset); } static uint16_t caller_stack_word(uint16_t caller_bp, int16_t displacement) { return win16_read_stack_u16((uint16_t)(caller_bp + displacement)); } static void write_caller_stack_word( uint16_t caller_bp, int16_t displacement, uint16_t value) { win16_write_stack_u16((uint16_t)(caller_bp + displacement), value); } /* 1000:635c -- update the backing bitmap and immediately present it. */ void tdkpin_blit_bitmap_and_present( HBITMAP16 bitmap, INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc) { tdkpin_blit_bitmap_to_background(bitmap, height, width, y, x, dc); tdkpin_present_background_region(height, width, y, x, dc); } /* * 1000:6b37 -- nested helper used by 6bf8 to composite one 21x18 board * decoration row. Its first argument is the caller's BP, so the four GDI * temporaries deliberately occupy SS:[BP-0e..BP-14] in the parent frame. */ void tdkpin_draw_board_decoration_row(uint16_t caller_bp, INT16 y) { HDC16 target_dc = caller_stack_word(caller_bp, -0x0c); HDC16 background_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x0e, background_dc); HDC16 decoration_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x10, decoration_dc); HBITMAP16 background = win16_read_u16( win16_dgroup_pointer(0x085b)); HGDIOBJ16 old_background = SelectObject16(background_dc, background); write_caller_stack_word(caller_bp, -0x12, old_background); HBITMAP16 decoration = win16_read_u16( win16_dgroup_pointer(0x0865)); HGDIOBJ16 old_decoration = SelectObject16(decoration_dc, decoration); write_caller_stack_word(caller_bp, -0x14, old_decoration); (void)BitBlt16( background_dc, 0, y, 21, 18, decoration_dc, 314, 426, 0x00cc0020u); (void)SelectObject16( decoration_dc, caller_stack_word(caller_bp, -0x14)); (void)SelectObject16( background_dc, caller_stack_word(caller_bp, -0x12)); (void)DeleteDC16(decoration_dc); (void)DeleteDC16(background_dc); tdkpin_present_background_region(18, 21, y, 0, target_dc); } /* Same binary operation when the enclosing 6bf8 frame already owns the DC. */ void tdkpin_draw_board_decoration_row_on_dc(INT16 y, HDC16 dc) { HDC16 background_dc = CreateCompatibleDC16(dc); HDC16 decoration_dc = CreateCompatibleDC16(dc); HGDIOBJ16 old_background = SelectObject16( background_dc, win16_read_u16(win16_dgroup_pointer(0x085b))); HGDIOBJ16 old_decoration = SelectObject16( decoration_dc, win16_read_u16(win16_dgroup_pointer(0x0865))); (void)BitBlt16( background_dc, 0, y, 21, 18, decoration_dc, 314, 426, 0x00cc0020u); (void)SelectObject16(decoration_dc, old_decoration); (void)SelectObject16(background_dc, old_background); (void)DeleteDC16(decoration_dc); (void)DeleteDC16(background_dc); tdkpin_present_background_region(18, 21, y, 0, dc); } /* * 1000:9bca -- composite the 19x19 ball mask/image over the background and * present the clipped result. All locals live in the enclosing caller frame. */ void tdkpin_render_ball_from_caller_frame(uint16_t caller_bp) { Win16FarPtr receiver = caller_receiver(caller_bp); HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); (void)GetClientRect16( window, win16_current_stack_address((uint16_t)(caller_bp - 0x1a))); HDC16 target_dc = GetDC16(window); write_caller_stack_word(caller_bp, -0x0c, target_dc); HDC16 background_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x0e, background_dc); HDC16 composite_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x10, composite_dc); HDC16 unused_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x12, unused_dc); HBITMAP16 composite_bitmap = CreateCompatibleBitmap16(target_dc, 100, 100); write_caller_stack_word(caller_bp, -0x08, composite_bitmap); int32_t pixel_x = borland_divide_i32( (int32_t)((uint32_t)g_ball_x_milli + 500u), 1000).quotient - 8; int32_t pixel_y = borland_divide_i32( (int32_t)((uint32_t)g_ball_y_milli + 500u), 1000).quotient - 8; win16_write_u32(receiver, 0x0ba2, (uint32_t)pixel_x); win16_write_u32(receiver, 0x0ba6, (uint32_t)pixel_y); INT16 destination_x = (INT16)(pixel_x - 1); INT16 destination_y = (INT16)(pixel_y - 1); INT16 source_x = 1; INT16 source_y = 1; INT16 width = 19; INT16 height = 19; write_caller_stack_word(caller_bp, -0x20, (uint16_t)destination_x); write_caller_stack_word(caller_bp, -0x1c, (uint16_t)source_x); write_caller_stack_word(caller_bp, -0x22, (uint16_t)destination_y); write_caller_stack_word(caller_bp, -0x1e, (uint16_t)source_y); write_caller_stack_word(caller_bp, -0x28, (uint16_t)width); write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height); HBITMAP16 background = win16_read_u16( win16_dgroup_pointer(0x085b)); HGDIOBJ16 old_background = SelectObject16(background_dc, background); write_caller_stack_word(caller_bp, -0x02, old_background); HGDIOBJ16 old_composite = SelectObject16(composite_dc, composite_bitmap); write_caller_stack_word(caller_bp, -0x04, old_composite); (void)BitBlt16( composite_dc, 0, 0, width, height, background_dc, destination_x, destination_y, 0x00cc0020u); (void)SelectObject16( background_dc, win16_read_u16(win16_far_add_offset(receiver, 0x54))); (void)BitBlt16( composite_dc, source_x, source_y, width, height, background_dc, 0, 0, 0x008800c6u); (void)SelectObject16( background_dc, win16_read_u16(win16_far_add_offset(receiver, 0x52))); (void)BitBlt16( composite_dc, source_x, source_y, width, height, background_dc, 0, 0, 0x00ee0086u); if ((int16_t)(destination_y + height) > 442) { height = destination_y <= 442 ? (INT16)(443 - destination_y) : 0; write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height); } (void)BitBlt16( target_dc, destination_x, destination_y, width, height, composite_dc, 0, 0, 0x00cc0020u); (void)SelectObject16( background_dc, caller_stack_word(caller_bp, -0x02)); (void)SelectObject16( composite_dc, caller_stack_word(caller_bp, -0x04)); (void)SelectObject16( unused_dc, caller_stack_word(caller_bp, -0x06)); (void)DeleteObject16(composite_bitmap); (void)DeleteDC16(background_dc); (void)DeleteDC16(composite_dc); (void)DeleteDC16(unused_dc); (void)ReleaseDC16(window, target_dc); } /* * 1000:a38e -- restore the background under the receiver's previous ball * position. The low byte of the second ABI word suppresses presentation. */ void tdkpin_restore_previous_ball_region( uint16_t caller_bp, uint8_t suppress_present) { Win16FarPtr receiver = caller_receiver(caller_bp); HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); (void)GetClientRect16( window, win16_current_stack_address((uint16_t)(caller_bp - 0x1a))); HDC16 target_dc = GetDC16(window); write_caller_stack_word(caller_bp, -0x0c, target_dc); HDC16 background_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x0e, background_dc); HDC16 saved_region_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x10, saved_region_dc); HDC16 unused_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x12, unused_dc); HBITMAP16 saved_region = CreateCompatibleBitmap16(target_dc, 100, 100); write_caller_stack_word(caller_bp, -0x08, saved_region); int32_t milli_x = (int32_t)win16_read_u32( win16_far_add_offset(receiver, 0x0bc2)); int32_t milli_y = (int32_t)win16_read_u32( win16_far_add_offset(receiver, 0x0bc6)); int32_t pixel_x = borland_divide_i32( (int32_t)((uint32_t)milli_x + 500u), 1000).quotient - 8; int32_t pixel_y = borland_divide_i32( (int32_t)((uint32_t)milli_y + 500u), 1000).quotient - 8; win16_write_u32(receiver, 0x0ba2, (uint32_t)pixel_x); win16_write_u32(receiver, 0x0ba6, (uint32_t)pixel_y); INT16 destination_x = (INT16)(pixel_x - 1); INT16 destination_y = (INT16)(pixel_y - 1); INT16 width = 19; INT16 height = 19; write_caller_stack_word(caller_bp, -0x20, (uint16_t)destination_x); write_caller_stack_word(caller_bp, -0x1c, 1); write_caller_stack_word(caller_bp, -0x22, (uint16_t)destination_y); write_caller_stack_word(caller_bp, -0x1e, 1); write_caller_stack_word(caller_bp, -0x28, (uint16_t)width); write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height); win16_write_u32(receiver, 0x0baa, 0); win16_write_u32(receiver, 0x0bae, 0); HGDIOBJ16 old_background = SelectObject16( background_dc, win16_read_u16(win16_dgroup_pointer(0x085b))); write_caller_stack_word(caller_bp, -0x02, old_background); HGDIOBJ16 old_saved_region = SelectObject16(saved_region_dc, saved_region); write_caller_stack_word(caller_bp, -0x04, old_saved_region); (void)BitBlt16( saved_region_dc, 0, 0, width, height, background_dc, destination_x, destination_y, 0x00cc0020u); if ((int16_t)(destination_y + height) > 442) { height = destination_y <= 442 ? (INT16)(443 - destination_y) : 0; write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height); } if (suppress_present == 0) { (void)BitBlt16( target_dc, destination_x, destination_y, width, height, saved_region_dc, 0, 0, 0x00cc0020u); } (void)SelectObject16( background_dc, caller_stack_word(caller_bp, -0x02)); (void)SelectObject16( saved_region_dc, caller_stack_word(caller_bp, -0x04)); (void)SelectObject16( unused_dc, caller_stack_word(caller_bp, -0x06)); (void)DeleteObject16(saved_region); (void)DeleteDC16(background_dc); (void)DeleteDC16(saved_region_dc); (void)DeleteDC16(unused_dc); (void)ReleaseDC16(window, target_dc); } static uint32_t wrapping_magnitude_i32(int32_t value) { return value < 0 ? 0u - (uint32_t)value : (uint32_t)value; } /* * 1000:9f73 -- composite and present the swept rectangle between the previous * and current ball positions, retaining signed delta and 16-bit clip rules. */ void tdkpin_render_ball_swept_region(uint16_t caller_bp) { Win16FarPtr receiver = caller_receiver(caller_bp); HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); (void)GetClientRect16( window, win16_current_stack_address((uint16_t)(caller_bp - 0x1a))); HDC16 target_dc = GetDC16(window); write_caller_stack_word(caller_bp, -0x0c, target_dc); HDC16 background_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x0e, background_dc); HDC16 swept_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x10, swept_dc); HDC16 unused_dc = CreateCompatibleDC16(target_dc); write_caller_stack_word(caller_bp, -0x12, unused_dc); HBITMAP16 swept_bitmap = CreateCompatibleBitmap16(target_dc, 100, 100); write_caller_stack_word(caller_bp, -0x08, swept_bitmap); int32_t previous_x = (int32_t)win16_read_u32( win16_far_add_offset(receiver, 0x0bc2)); int32_t previous_y = (int32_t)win16_read_u32( win16_far_add_offset(receiver, 0x0bc6)); int32_t pixel_x = borland_divide_i32( (int32_t)((uint32_t)g_ball_x_milli + 500u), 1000).quotient - 8; int32_t pixel_y = borland_divide_i32( (int32_t)((uint32_t)g_ball_y_milli + 500u), 1000).quotient - 8; win16_write_u32(receiver, 0x0ba2, (uint32_t)pixel_x); win16_write_u32(receiver, 0x0ba6, (uint32_t)pixel_y); int32_t delta_x = borland_divide_i32( (int32_t)((uint32_t)g_ball_x_milli - (uint32_t)previous_x), 1000).quotient; int32_t delta_y = borland_divide_i32( (int32_t)((uint32_t)g_ball_y_milli - (uint32_t)previous_y), 1000).quotient; win16_write_u32(receiver, 0x0bb2, (uint32_t)delta_x); win16_write_u32(receiver, 0x0bb6, (uint32_t)delta_y); INT16 destination_x; INT16 source_x; if (delta_x > 0) { destination_x = (INT16)((uint32_t)pixel_x - (uint32_t)delta_x - 1u); source_x = (INT16)((uint32_t)delta_x + 1u); } else { destination_x = (INT16)((uint32_t)pixel_x - 1u); source_x = 1; } INT16 destination_y; INT16 source_y; if (delta_y > 0) { destination_y = (INT16)((uint32_t)pixel_y - (uint32_t)delta_y - 1u); source_y = (INT16)((uint32_t)delta_y + 1u); } else { destination_y = (INT16)((uint32_t)pixel_y - 1u); source_y = 1; } INT16 width = (INT16)(uint16_t)(wrapping_magnitude_i32(delta_x) + 19u); INT16 height = (INT16)(uint16_t)(wrapping_magnitude_i32(delta_y) + 19u); write_caller_stack_word(caller_bp, -0x20, (uint16_t)destination_x); write_caller_stack_word(caller_bp, -0x1c, (uint16_t)source_x); write_caller_stack_word(caller_bp, -0x22, (uint16_t)destination_y); write_caller_stack_word(caller_bp, -0x1e, (uint16_t)source_y); write_caller_stack_word(caller_bp, -0x28, (uint16_t)width); write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height); HGDIOBJ16 old_background = SelectObject16( background_dc, win16_read_u16(win16_dgroup_pointer(0x085b))); write_caller_stack_word(caller_bp, -0x02, old_background); HGDIOBJ16 old_swept = SelectObject16(swept_dc, swept_bitmap); write_caller_stack_word(caller_bp, -0x04, old_swept); (void)BitBlt16( swept_dc, 0, 0, width, height, background_dc, destination_x, destination_y, 0x00cc0020u); (void)SelectObject16( background_dc, win16_read_u16(win16_far_add_offset(receiver, 0x54))); (void)BitBlt16( swept_dc, source_x, source_y, 19, 19, background_dc, 0, 0, 0x008800c6u); (void)SelectObject16( background_dc, win16_read_u16(win16_far_add_offset(receiver, 0x52))); (void)BitBlt16( swept_dc, source_x, source_y, 19, 19, background_dc, 0, 0, 0x00ee0086u); if ((int16_t)(destination_y + height) > 442) { height = destination_y <= 442 ? (INT16)(443 - destination_y) : 0; write_caller_stack_word(caller_bp, -0x2a, (uint16_t)height); } (void)BitBlt16( target_dc, destination_x, destination_y, width, height, swept_dc, 0, 0, 0x00cc0020u); (void)SelectObject16( background_dc, caller_stack_word(caller_bp, -0x02)); (void)SelectObject16(swept_dc, caller_stack_word(caller_bp, -0x04)); (void)SelectObject16(unused_dc, caller_stack_word(caller_bp, -0x06)); (void)DeleteObject16(swept_bitmap); (void)DeleteDC16(background_dc); (void)DeleteDC16(swept_dc); (void)DeleteDC16(unused_dc); (void)ReleaseDC16(window, target_dc); } /* * 1000:beff -- retain the current position, apply wrapping milli-coordinate * deltas (Y in the final four-byte ABI argument), then render the swept move. */ void tdkpin_move_ball_and_render( uint16_t caller_bp, int32_t delta_x, int32_t delta_y) { Win16FarPtr receiver = caller_receiver(caller_bp); win16_write_u32(receiver, 0x0bc2, (uint32_t)g_ball_x_milli); win16_write_u32(receiver, 0x0bc6, (uint32_t)g_ball_y_milli); g_ball_y_milli = (int32_t)((uint32_t)g_ball_y_milli + (uint32_t)delta_y); g_ball_x_milli = (int32_t)((uint32_t)g_ball_x_milli + (uint32_t)delta_x); tdkpin_render_ball_swept_region(caller_bp); } /* 1000:9e85 -- reset ball/game fields, then redraw through 1000:9bca. */ void tdkpin_reset_ball_state(uint16_t caller_bp) { Win16FarPtr receiver = caller_receiver(caller_bp); static const uint16_t completion_words[5] = { 0x3371, 0x33c4, 0x3417, 0x346a, 0x34bd}; bool complete = true; for (uint16_t index = 0; index < 5; index++) { if (win16_read_u16( win16_dgroup_pointer(completion_words[index])) == 0) { complete = false; break; } } if (complete) { win16_write_u16(win16_dgroup_pointer(0x0851), 0, 0); win16_write_u16(win16_dgroup_pointer(0x0853), 0, 0); win16_write_u8(win16_dgroup_pointer(0x07c8), 1); win16_write_u8(win16_far_add_offset(receiver, 0x0bdd), 0); } win16_write_u8(win16_dgroup_pointer(0x07c6), 0); win16_write_u16(receiver, 0x5e, 0); g_ball_x_milli = 325000; g_ball_y_milli = 413000; win16_write_u32(receiver, 0x0bba, (uint32_t)g_ball_x_milli); win16_write_u32(receiver, 0x0bbe, (uint32_t)g_ball_y_milli); win16_write_u32(receiver, 0x0bc2, (uint32_t)g_ball_x_milli); win16_write_u32(receiver, 0x0bc6, (uint32_t)g_ball_y_milli); win16_write_u32(receiver, 0x0bae, 0); win16_write_u32(receiver, 0x0baa, 0); win16_write_u16(receiver, 0x0bce, 0); win16_write_u16(receiver, 0x0bd0, 0); win16_write_u16(receiver, 0x0bd2, 0); win16_write_u32(receiver, 0x0bca, 0); win16_write_u8(win16_far_add_offset(receiver, 0x0bdd), 0); tdkpin_render_ball_from_caller_frame(caller_bp); } /* 1000:a604 -- redraw the active player's six five-pixel progress markers. */ void tdkpin_refresh_player_progress_markers(uint16_t caller_bp) { Win16FarPtr receiver = caller_receiver(caller_bp); HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(window); INT16 threshold = (INT16)win16_read_u16( win16_dgroup_pointer((uint16_t)( 0x081f + (uint16_t)g_current_player * 2u))); INT16 player_y = (INT16)( (uint16_t)(g_current_player - 1u) * 44u + 298u); for (int32_t marker = 1; marker <= 6; marker++) { INT16 marker_x = (INT16)( (uint16_t)borland_multiply_i32(marker - 1, 8) + 391u); if (marker <= threshold) { tdkpin_restore_background_region_a( 5, 5, player_y, marker_x, dc); } else { tdkpin_restore_background_region_b( 5, 5, player_y, marker_x, dc); } tdkpin_present_background_region( 5, 5, player_y, marker_x, dc); } (void)ReleaseDC16(window, dc); } /* 1000:0002 -- composite one indexed 400-series sprite into the background. */ void tdkpin_composite_player_sprite( uint16_t sprite_index, HDC16 reference_dc) { HDC16 background_dc = CreateCompatibleDC16(reference_dc); HDC16 sprite_dc = CreateCompatibleDC16(reference_dc); HDC16 saved_region_dc = CreateCompatibleDC16(reference_dc); HBITMAP16 saved_region = CreateCompatibleBitmap16(reference_dc, 14, 14); HGDIOBJ16 old_background = SelectObject16( background_dc, win16_read_u16(win16_dgroup_pointer(0x085b))); HGDIOBJ16 old_sprite = SelectObject16( sprite_dc, win16_read_u16(win16_dgroup_pointer((uint16_t)( 0x430b + sprite_index * 2u)))); HGDIOBJ16 old_saved_region = SelectObject16(saved_region_dc, saved_region); (void)BitBlt16( saved_region_dc, 0, 0, 14, 14, background_dc, 84, 77, 0x00cc0020u); (void)BitBlt16( background_dc, 55, 79, 42, 123, sprite_dc, 0, 0, 0x00cc0020u); (void)BitBlt16( background_dc, 84, 77, 14, 14, saved_region_dc, 0, 0, 0x00cc0020u); (void)SelectObject16(saved_region_dc, old_saved_region); (void)SelectObject16(sprite_dc, old_sprite); (void)SelectObject16(background_dc, old_background); (void)DeleteObject16(saved_region); (void)DeleteDC16(saved_region_dc); (void)DeleteDC16(sprite_dc); (void)DeleteDC16(background_dc); } static Win16FarPtr active_player_dword(uint16_t index_zero_base) { return win16_dgroup_pointer((uint16_t)( index_zero_base + (uint16_t)g_current_player * 4u)); } /* * 1000:bc36 -- add a score delta, award crossed marker thresholds, and redraw. * Delta -1 is the explicit redraw-only sentinel; tilt suppresses all mutation. */ void tdkpin_add_score_from_caller_frame( uint16_t caller_bp, int32_t delta) { Win16FarPtr receiver = caller_receiver(caller_bp); HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(window); if (delta != -1 && win16_read_u8(win16_dgroup_pointer(0x07c6)) == 0) { if (win16_read_u8(win16_dgroup_pointer(0x0874)) != 0) { delta = borland_multiply_i32(delta, 2); } if (win16_read_u8( win16_far_add_offset(receiver, 0x0bdc)) != 0) { delta = borland_multiply_i32(delta, 2); } Win16FarPtr score = active_player_dword(0x097e); uint32_t updated = win16_read_u32(score) + (uint32_t)delta; win16_write_u32(score, 0, updated); Win16FarPtr marker_level = active_player_dword(0x0835); int32_t level = (int32_t)win16_read_u32(marker_level); if (level <= 4) { uint32_t threshold = win16_read_u32( win16_dgroup_pointer((uint16_t)(0x0825 + (uint16_t)level * 4u))); if ((int16_t)(updated >> 16) > (int16_t)(threshold >> 16) || ((int16_t)(updated >> 16) == (int16_t)(threshold >> 16) && (uint16_t)updated >= (uint16_t)threshold)) { tdkpin_play_sound_if_not_tilted(7); win16_write_u32(marker_level, 0, (uint32_t)level + 1u); Win16FarPtr markers = win16_dgroup_pointer((uint16_t)( 0x081f + (uint16_t)g_current_player * 2u)); win16_write_u16( markers, 0, (uint16_t)(win16_read_u16(markers) + 1u)); tdkpin_refresh_player_progress_markers(caller_bp); } } tdkpin_update_status_display(g_current_player, dc); } tdkpin_draw_score(dc, (int32_t)g_current_player); (void)ReleaseDC16(window, dc); } static void restore_bounds( bool overlay_a, INT16 bottom, INT16 right, INT16 top, INT16 left, HDC16 dc) { INT16 height = (INT16)(uint16_t)( (uint16_t)bottom - (uint16_t)top + 1); INT16 width = (INT16)(uint16_t)( (uint16_t)right - (uint16_t)left + 1); if (overlay_a) { tdkpin_restore_background_region_a(height, width, top, left, dc); } else { tdkpin_restore_background_region_b(height, width, top, left, dc); } tdkpin_present_background_region(height, width, top, left, dc); } /* 1000:b2c8 -- inclusive rectangle restore from overlay A. */ void tdkpin_restore_bounds_overlay_a( uint16_t ignored_static_link, INT16 bottom, INT16 right, INT16 top, INT16 left, HDC16 dc) { (void)ignored_static_link; restore_bounds(true, bottom, right, top, left, dc); } /* 1000:b3b0 -- inclusive rectangle restore from overlay B. */ void tdkpin_restore_bounds_overlay_b( uint16_t ignored_static_link, INT16 bottom, INT16 right, INT16 top, INT16 left, HDC16 dc) { (void)ignored_static_link; restore_bounds(false, bottom, right, top, left, dc); } /* * 1000:bda2 -- caller-frame helper. SS:[caller BP+6] is the far receiver; * its HWND at +4 owns the transient DC used for the 32-bit score value. */ void tdkpin_draw_score_from_caller_frame( uint16_t caller_bp, uint16_t value_low, uint16_t value_high) { Win16FarPtr receiver = caller_receiver(caller_bp); HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(window); int32_t value = (int32_t)( (uint32_t)value_low | ((uint32_t)value_high << 16)); tdkpin_draw_score(dc, value); (void)ReleaseDC16(window, dc); } /* 1000:a70e -- clear the player counter and set object/global transition bytes. */ void tdkpin_set_gameplay_transition_flags(uint16_t caller_bp) { g_player_count = 0; Win16FarPtr receiver = caller_receiver(caller_bp); win16_write_u8(win16_far_add_offset(receiver, 0x0bde), 1); g_gameplay_transition_pending = 1; } static uint16_t point_in_bounds_with_residue( int32_t x, int32_t y, int32_t maximum_y, int32_t maximum_x, int32_t minimum_y, int32_t minimum_x) { uint16_t ax_residue = (uint16_t)x; bool inside = x >= minimum_x; if (inside) { ax_residue = (uint16_t)y; inside = y >= minimum_y; } if (inside) { ax_residue = (uint16_t)x; inside = x <= maximum_x; } if (inside) { ax_residue = (uint16_t)y; inside = y <= maximum_y; } return (ax_residue & 0xff00u) | (inside ? 1u : 0u); } /* 1000:9ae0 -- test the receiver's current milli-coordinate against bounds. */ uint16_t tdkpin_receiver_point_in_bounds( uint16_t caller_bp, int32_t maximum_y, int32_t maximum_x, int32_t minimum_y, int32_t minimum_x) { Win16FarPtr receiver = caller_receiver(caller_bp); int32_t x = (int32_t)win16_read_u32( win16_far_add_offset(receiver, 0x0bba)); int32_t y = (int32_t)win16_read_u32( win16_far_add_offset(receiver, 0x0bbe)); return point_in_bounds_with_residue( x, y, maximum_y, maximum_x, minimum_y, minimum_x); } /* 1000:9b69 -- same inclusive rectangle test for the global ball position. */ uint16_t tdkpin_ball_in_bounds( uint16_t ignored_static_link, int32_t maximum_y, int32_t maximum_x, int32_t minimum_y, int32_t minimum_x) { (void)ignored_static_link; return point_in_bounds_with_residue( g_ball_x_milli, g_ball_y_milli, maximum_y, maximum_x, minimum_y, minimum_x); } /* 1000:bdde -- center dynamic record 173+displacement on the current ball. */ void tdkpin_update_dynamic_ball_record( uint16_t ignored_static_link, int32_t record_displacement) { (void)ignored_static_link; uint16_t index = (uint16_t)(record_displacement + 0x00ad); uint16_t base = (uint16_t)(0x095b + (uint16_t)(index * 0x53u)); Win16FarPtr record = win16_dgroup_pointer(base); win16_write_u32(record, 0x12, (uint32_t)g_ball_x_milli); win16_write_u32(record, 0x16, (uint32_t)g_ball_y_milli); win16_write_u32( record, 0x02, (uint32_t)((uint32_t)g_ball_x_milli - 21000u)); win16_write_u32( record, 0x06, (uint32_t)((uint32_t)g_ball_y_milli - 21000u)); win16_write_u32( record, 0x0a, (uint32_t)((uint32_t)g_ball_x_milli + 21000u)); win16_write_u32( record, 0x0e, (uint32_t)((uint32_t)g_ball_y_milli + 21000u)); } static void restore_record_bounds( bool overlay_a, uint16_t caller_bp, Win16FarPtr bounds) { Win16FarPtr receiver = caller_receiver(caller_bp); HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(window); uint16_t bottom = win16_read_u16(win16_far_add_offset(bounds, 6)); uint16_t right = win16_read_u16(win16_far_add_offset(bounds, 4)); uint16_t top = win16_read_u16(win16_far_add_offset(bounds, 2)); uint16_t left = win16_read_u16(bounds); if (overlay_a) { tdkpin_restore_bounds_overlay_a( caller_bp, bottom, right, top, left, dc); } else { tdkpin_restore_bounds_overlay_b( caller_bp, bottom, right, top, left, dc); } (void)ReleaseDC16(window, dc); } /* 1000:b30b -- restore collision/quad bounds from overlay A or draw item 1..9. */ void tdkpin_restore_record_or_item_a(uint16_t caller_bp, uint16_t id) { if (id <= 175) { uint16_t offset = (uint16_t)(0x095b + (uint16_t)(id * 0x53u) + 0x4b); restore_record_bounds(true, caller_bp, win16_dgroup_pointer(offset)); } else if (id >= 1006 && id <= 1014) { Win16FarPtr receiver = caller_receiver(caller_bp); HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(window); tdkpin_draw_item_status((INT16)(id - 1005), dc); (void)ReleaseDC16(window, dc); } else { uint16_t index = (uint16_t)(id - 1000); uint16_t offset = (uint16_t)(0x4263 + (uint16_t)(index * 8u)); restore_record_bounds(true, caller_bp, win16_dgroup_pointer(offset)); } } /* 1000:b3f3 -- restore collision or word-quad bounds from overlay B. */ void tdkpin_restore_record_b(uint16_t caller_bp, uint16_t id) { Win16FarPtr bounds; if (id <= 175) { bounds = win16_dgroup_pointer( (uint16_t)(0x095b + (uint16_t)(id * 0x53u) + 0x4b)); } else { uint16_t index = (uint16_t)(id - 1000); bounds = win16_dgroup_pointer( (uint16_t)(0x4263 + (uint16_t)(index * 8u))); } restore_record_bounds(false, caller_bp, bounds); } /* 1000:7e12 -- redraw records whose flag 80/100 encodes overlay polarity. */ void tdkpin_refresh_collision_record(uint16_t caller_bp, uint16_t id) { if (id == 0 || id >= 176) { return; } Win16FarPtr record = win16_dgroup_pointer( (uint16_t)(0x095b + (uint16_t)(id * 0x53u))); uint16_t flags = win16_read_u16(win16_far_add_offset(record, 0x41)); bool active = win16_read_u8(win16_far_add_offset(record, 0x34)) != 0; if ((flags & 0x0080u) != 0) { if (active) { tdkpin_restore_record_b(caller_bp, id); } else { tdkpin_restore_record_or_item_a(caller_bp, id); } } else if ((flags & 0x0100u) != 0) { if (active) { tdkpin_restore_record_or_item_a(caller_bp, id); } else { tdkpin_restore_record_b(caller_bp, id); } } } /* 1000:7e92 -- refresh one player item through its per-player 20-byte row. */ void tdkpin_refresh_player_item(uint16_t caller_bp, uint16_t item) { Win16FarPtr receiver = caller_receiver(caller_bp); uint16_t offset = (uint16_t)( (uint16_t)g_current_player * 0x14u + item + 0x0467u); if (win16_read_u8(win16_far_add_offset(receiver, offset)) != 0) { tdkpin_restore_record_or_item_a( caller_bp, (uint16_t)(item + 1000)); } else { tdkpin_restore_record_b( caller_bp, (uint16_t)(item + 1000)); } } /* 1000:c36b -- load one indexed ball state into active globals/object fields. */ void tdkpin_load_ball_slot(uint16_t caller_bp, uint16_t slot) { Win16FarPtr receiver = caller_receiver(caller_bp); uint16_t word4 = (uint16_t)(slot * 4u); uint16_t word6 = (uint16_t)(slot * 6u); g_ball_x_milli = (int32_t)win16_read_u32( win16_dgroup_pointer((uint16_t)(0x07e3 + word4))); g_ball_y_milli = (int32_t)win16_read_u32( win16_dgroup_pointer((uint16_t)(0x07eb + word4))); win16_write_u32( receiver, 0x0baa, win16_read_u32(win16_dgroup_pointer((uint16_t)(0x07f3 + word4)))); win16_write_u32( receiver, 0x0bae, win16_read_u32(win16_dgroup_pointer((uint16_t)(0x07fb + word4)))); for (uint16_t index = 0; index < 6; index++) { win16_write_u8( win16_far_add_offset(receiver, (uint16_t)(0x0bce + index)), win16_read_u8(win16_dgroup_pointer( (uint16_t)(0x080b + word6 + index)))); } win16_write_u32( receiver, 0x0bca, win16_read_u32(win16_dgroup_pointer((uint16_t)(0x0803 + word4)))); } /* 1000:c426 -- save active globals/object fields back into one ball slot. */ void tdkpin_save_ball_slot(uint16_t caller_bp, uint16_t slot) { Win16FarPtr receiver = caller_receiver(caller_bp); uint16_t word4 = (uint16_t)(slot * 4u); uint16_t word6 = (uint16_t)(slot * 6u); win16_write_u32( win16_dgroup_pointer((uint16_t)(0x07e3 + word4)), 0, (uint32_t)g_ball_x_milli); win16_write_u32( win16_dgroup_pointer((uint16_t)(0x07eb + word4)), 0, (uint32_t)g_ball_y_milli); win16_write_u32( win16_dgroup_pointer((uint16_t)(0x07f3 + word4)), 0, win16_read_u32(win16_far_add_offset(receiver, 0x0baa))); win16_write_u32( win16_dgroup_pointer((uint16_t)(0x07fb + word4)), 0, win16_read_u32(win16_far_add_offset(receiver, 0x0bae))); for (uint16_t index = 0; index < 6; index++) { win16_write_u8( win16_dgroup_pointer((uint16_t)(0x080b + word6 + index)), win16_read_u8(win16_far_add_offset( receiver, (uint16_t)(0x0bce + index)))); } win16_write_u32( win16_dgroup_pointer((uint16_t)(0x0803 + word4)), 0, win16_read_u32(win16_far_add_offset(receiver, 0x0bca))); }