Raw 1000:061c-063b divides the idle tick by 144, copies the remainder from CX:BX into AX:DX, and only then divides by 8. Readable C and Rust instead used an unbounded quotient equivalent to tick/1152, making DAT801-809 advance far too slowly and suggesting a nonexistent negative long-idle index. Use `(tick % 144) / 8` to repeat item states 0,1..9,8..1 every 144 callbacks. Remove the disproven long-idle status crops and restore the ten-minute simulator ceiling. Extend the C and Rust phase tests across the forward, reverse, and wrap boundaries. Test Plan: - raw instruction review at `1000:061c-063b` -- remainder transfer confirmed - `cargo test --workspace --all-targets --all-features` -- 125 passed - `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed - `bash original/tools/test_reconstructed_c.sh` -- passed - `python3 original/tools/audit_reconstruction.py --require-complete` -- passed with zero incomplete or unclassified units - `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed - `git diff --cached --check` -- passed
562 lines
17 KiB
C
562 lines
17 KiB
C
#include "../tdkpin_timer_tick.h"
|
|
|
|
#include "../tdkpin_borland_runtime.h"
|
|
#include "../tdkpin_collision_records.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
typedef struct {
|
|
HDC16 destination;
|
|
INT16 x;
|
|
INT16 y;
|
|
INT16 width;
|
|
INT16 height;
|
|
HDC16 source;
|
|
INT16 source_x;
|
|
INT16 source_y;
|
|
uint32_t raster_operation;
|
|
} BlitCall;
|
|
|
|
static uint8_t g_dgroup[0x5000];
|
|
static uint8_t g_objects[0x1000];
|
|
static uint8_t g_stack[0x1000];
|
|
static Win16FarPtr g_window;
|
|
static uint16_t g_refresh_ids[200];
|
|
static size_t g_refresh_count;
|
|
static uint16_t g_restore_a_ids[32];
|
|
static size_t g_restore_a_id_count;
|
|
static uint16_t g_restore_b_ids[32];
|
|
static size_t g_restore_b_id_count;
|
|
static uint16_t g_save_slots[8];
|
|
static size_t g_save_count;
|
|
static uint16_t g_load_slots[8];
|
|
static size_t g_load_count;
|
|
static int32_t g_dynamic_records[8];
|
|
static size_t g_dynamic_count;
|
|
static uint16_t g_sounds[8];
|
|
static size_t g_sound_count;
|
|
static uint8_t g_claw_alternate;
|
|
static uint16_t g_claw_frame;
|
|
static unsigned g_claw_calls;
|
|
static unsigned g_render_ball_calls;
|
|
static unsigned g_rotate_calls;
|
|
static unsigned g_restore_previous_calls;
|
|
static unsigned g_finish_calls;
|
|
static unsigned g_simulate_calls;
|
|
static unsigned g_reset_calls;
|
|
static unsigned g_flipper_calls;
|
|
static unsigned g_drain_calls;
|
|
static unsigned g_initialize_calls;
|
|
static unsigned g_redraw_calls;
|
|
static unsigned g_progress_calls;
|
|
static unsigned g_panel_calls;
|
|
static int16_t g_last_item;
|
|
static unsigned g_item_calls;
|
|
static unsigned g_region_a_calls;
|
|
static unsigned g_region_b_calls;
|
|
static unsigned g_present_calls;
|
|
static BlitCall g_blits[16];
|
|
static size_t g_blit_count;
|
|
static unsigned g_get_dc_calls;
|
|
static unsigned g_release_dc_calls;
|
|
static unsigned g_create_dc_calls;
|
|
static unsigned g_create_bitmap_calls;
|
|
static unsigned g_select_calls;
|
|
static unsigned g_delete_object_calls;
|
|
static unsigned g_delete_dc_calls;
|
|
|
|
uint8_t g_borland_cpu_level = 2;
|
|
|
|
_Noreturn void borland_runtime_error(uint16_t code)
|
|
{
|
|
(void)code;
|
|
abort();
|
|
}
|
|
|
|
_Noreturn void win16_integer_divide_fault(void)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
void tdkpin_refresh_collision_record(uint16_t caller_bp, uint16_t id)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_refresh_ids[g_refresh_count++] = id;
|
|
}
|
|
|
|
void tdkpin_restore_record_or_item_a(uint16_t caller_bp, uint16_t id)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_restore_a_ids[g_restore_a_id_count++] = id;
|
|
}
|
|
|
|
void tdkpin_restore_record_b(uint16_t caller_bp, uint16_t id)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_restore_b_ids[g_restore_b_id_count++] = id;
|
|
}
|
|
|
|
void tdkpin_save_ball_slot(uint16_t caller_bp, uint16_t slot)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_save_slots[g_save_count++] = slot;
|
|
}
|
|
|
|
void tdkpin_load_ball_slot(uint16_t caller_bp, uint16_t slot)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_load_slots[g_load_count++] = slot;
|
|
}
|
|
|
|
void tdkpin_update_dynamic_ball_record(
|
|
uint16_t caller_bp, int32_t displacement)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_dynamic_records[g_dynamic_count++] = displacement;
|
|
}
|
|
|
|
void tdkpin_play_sound_if_not_tilted(uint16_t sound_number)
|
|
{
|
|
g_sounds[g_sound_count++] = sound_number;
|
|
}
|
|
|
|
void tdkpin_draw_claw_frame(
|
|
uint8_t alternate, uint16_t frame, HDC16 destination)
|
|
{
|
|
assert(destination == 0x3333);
|
|
g_claw_alternate = alternate;
|
|
g_claw_frame = frame;
|
|
g_claw_calls++;
|
|
}
|
|
|
|
void tdkpin_render_ball_from_caller_frame(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_render_ball_calls++;
|
|
}
|
|
|
|
void tdkpin_rotate_and_draw_targets(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_rotate_calls++;
|
|
}
|
|
|
|
void tdkpin_restore_previous_ball_region(
|
|
uint16_t caller_bp, uint8_t suppress_present)
|
|
{
|
|
assert(caller_bp == 0x0300 && suppress_present == 0);
|
|
g_restore_previous_calls++;
|
|
}
|
|
|
|
void tdkpin_finish_ball_or_advance_player(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_finish_calls++;
|
|
}
|
|
|
|
void tdkpin_simulate_ball_and_dispatch_collision(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_simulate_calls++;
|
|
}
|
|
|
|
void tdkpin_reset_ball_state(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_reset_calls++;
|
|
}
|
|
|
|
void tdkpin_update_flippers(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_flipper_calls++;
|
|
}
|
|
|
|
void tdkpin_drain_messages_except_input(void)
|
|
{
|
|
g_drain_calls++;
|
|
}
|
|
|
|
void tdkpin_initialize_player_state(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_initialize_calls++;
|
|
}
|
|
|
|
void tdkpin_redraw_all_players(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_redraw_calls++;
|
|
}
|
|
|
|
void tdkpin_refresh_player_progress_markers(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_progress_calls++;
|
|
}
|
|
|
|
void tdkpin_draw_panel_animation(uint16_t caller_bp)
|
|
{
|
|
assert(caller_bp == 0x0300);
|
|
g_panel_calls++;
|
|
}
|
|
|
|
void tdkpin_draw_item_status(int16_t item, HDC16 destination)
|
|
{
|
|
assert(destination == 0x3333);
|
|
g_last_item = item;
|
|
g_item_calls++;
|
|
}
|
|
|
|
void tdkpin_restore_background_region_a(
|
|
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
|
|
{
|
|
(void)height;
|
|
(void)width;
|
|
(void)y;
|
|
(void)x;
|
|
assert(dc == 0x3333);
|
|
g_region_a_calls++;
|
|
}
|
|
|
|
void tdkpin_restore_background_region_b(
|
|
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
|
|
{
|
|
(void)height;
|
|
(void)width;
|
|
(void)y;
|
|
(void)x;
|
|
assert(dc == 0x3333);
|
|
g_region_b_calls++;
|
|
}
|
|
|
|
void tdkpin_present_background_region(
|
|
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
|
|
{
|
|
(void)height;
|
|
(void)width;
|
|
(void)y;
|
|
(void)x;
|
|
assert(dc == 0x3333);
|
|
g_present_calls++;
|
|
}
|
|
|
|
HDC16 GetDC16(HWND16 window)
|
|
{
|
|
assert(window == 0x2222);
|
|
g_get_dc_calls++;
|
|
return 0x3333;
|
|
}
|
|
|
|
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
|
|
{
|
|
assert(window == 0x2222 && dc == 0x3333);
|
|
g_release_dc_calls++;
|
|
return 1;
|
|
}
|
|
|
|
HDC16 CreateCompatibleDC16(HDC16 dc)
|
|
{
|
|
assert(dc == 0x3333);
|
|
g_create_dc_calls++;
|
|
return g_create_dc_calls == 1 ? 0x4444 : 0x5555;
|
|
}
|
|
|
|
HBITMAP16 CreateCompatibleBitmap16(HDC16 dc, INT16 width, INT16 height)
|
|
{
|
|
assert(dc == 0x3333 && width == 30 && height == 284);
|
|
g_create_bitmap_calls++;
|
|
return 0x6666;
|
|
}
|
|
|
|
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
|
|
{
|
|
assert(dc == 0x4444 || dc == 0x5555);
|
|
(void)object;
|
|
g_select_calls++;
|
|
return (HGDIOBJ16)(0x7000 + g_select_calls);
|
|
}
|
|
|
|
BOOL16 BitBlt16(
|
|
HDC16 destination,
|
|
INT16 x,
|
|
INT16 y,
|
|
INT16 width,
|
|
INT16 height,
|
|
HDC16 source,
|
|
INT16 source_x,
|
|
INT16 source_y,
|
|
uint32_t raster_operation)
|
|
{
|
|
g_blits[g_blit_count++] = (BlitCall){
|
|
destination, x, y, width, height,
|
|
source, source_x, source_y, raster_operation,
|
|
};
|
|
return 1;
|
|
}
|
|
|
|
BOOL16 DeleteObject16(HGDIOBJ16 object)
|
|
{
|
|
assert(object == 0x6666);
|
|
g_delete_object_calls++;
|
|
return 1;
|
|
}
|
|
|
|
BOOL16 DeleteDC16(HDC16 dc)
|
|
{
|
|
assert(dc == 0x4444 || dc == 0x5555);
|
|
g_delete_dc_calls++;
|
|
return 1;
|
|
}
|
|
|
|
static Win16FarPtr record(uint16_t id)
|
|
{
|
|
return win16_dgroup_pointer((uint16_t)(
|
|
0x095b + id * sizeof(TdkpinCollisionRecord)));
|
|
}
|
|
|
|
static void prepare_fixture(void)
|
|
{
|
|
memset(g_dgroup, 0, sizeof(g_dgroup));
|
|
memset(g_objects, 0, sizeof(g_objects));
|
|
memset(g_stack, 0, sizeof(g_stack));
|
|
win16_reset_segment_bindings();
|
|
win16_bind_segment(0x7000, g_objects, sizeof(g_objects), true);
|
|
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
|
|
win16_bind_segment(0x7300, g_stack, sizeof(g_stack), true);
|
|
win16_set_dgroup_selector(0x7200);
|
|
win16_set_stack_state(0x7300, 0x0300);
|
|
g_window = win16_make_far_pointer(0x7000, 0);
|
|
win16_write_u16(g_window, 4, 0x2222);
|
|
win16_write_u8(win16_dgroup_pointer(0x07c4), 1);
|
|
memset(g_refresh_ids, 0, sizeof(g_refresh_ids));
|
|
memset(g_restore_a_ids, 0, sizeof(g_restore_a_ids));
|
|
memset(g_restore_b_ids, 0, sizeof(g_restore_b_ids));
|
|
g_refresh_count = g_restore_a_id_count = g_restore_b_id_count = 0;
|
|
g_save_count = g_load_count = g_dynamic_count = g_sound_count = 0;
|
|
g_claw_calls = g_render_ball_calls = g_rotate_calls = 0;
|
|
g_restore_previous_calls = g_finish_calls = g_simulate_calls = 0;
|
|
g_reset_calls = g_flipper_calls = g_drain_calls = 0;
|
|
g_initialize_calls = g_redraw_calls = g_progress_calls = 0;
|
|
g_panel_calls = g_item_calls = 0;
|
|
g_region_a_calls = g_region_b_calls = g_present_calls = 0;
|
|
g_blit_count = g_get_dc_calls = g_release_dc_calls = 0;
|
|
g_create_dc_calls = g_create_bitmap_calls = g_select_calls = 0;
|
|
g_delete_object_calls = g_delete_dc_calls = 0;
|
|
}
|
|
|
|
static void test_guard_and_refresh_epilogue(void)
|
|
{
|
|
prepare_fixture();
|
|
win16_write_u8(win16_dgroup_pointer(0x07c4), 0);
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(g_flipper_calls == 0 && g_drain_calls == 0);
|
|
|
|
prepare_fixture();
|
|
win16_write_u8(win16_dgroup_pointer(0x07c5), 1);
|
|
win16_write_u16(g_window, 0x5e, 2);
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(g_refresh_count == 175);
|
|
assert(g_refresh_ids[0] == 1 && g_refresh_ids[174] == 175);
|
|
assert(win16_read_u8(win16_dgroup_pointer(0x07c5)) == 0);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x0876)) == 1);
|
|
assert(g_flipper_calls == 1 && g_drain_calls == 1);
|
|
assert(win16_read_u8(win16_far_add_offset(g_window, 0x51)) == 0);
|
|
assert(win16_read_u16(win16_far_add_offset(g_window, 0x5e)) == 1);
|
|
}
|
|
|
|
static void test_claw_release(void)
|
|
{
|
|
prepare_fixture();
|
|
win16_write_u8(win16_far_add_offset(g_window, 0x0bdd), 1);
|
|
win16_write_u8(win16_far_add_offset(g_window, 0x0bdf), 1);
|
|
win16_write_u8(win16_dgroup_pointer(0x07ca), 1);
|
|
win16_write_u16(win16_dgroup_pointer(0x086d), 0, 2);
|
|
win16_write_u16(win16_dgroup_pointer(0x086f), 0, 2);
|
|
win16_write_u32(g_window, 0x56, 1000);
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(g_claw_calls == 1 && g_claw_alternate == 0 && g_claw_frame == 2);
|
|
assert(win16_read_u16(win16_dgroup_pointer(0x086f)) == 10);
|
|
assert((int32_t)win16_read_u32(win16_dgroup_pointer(0x07d3)) == 259000);
|
|
assert((int32_t)win16_read_u32(win16_dgroup_pointer(0x07d7)) == 81000);
|
|
assert((int32_t)win16_read_u32(
|
|
win16_far_add_offset(g_window, 0x0baa)) == -450);
|
|
assert((int32_t)win16_read_u32(
|
|
win16_far_add_offset(g_window, 0x0bae)) == 50);
|
|
assert(g_save_count == 1 && g_save_slots[0] == 1);
|
|
assert(g_render_ball_calls == 1);
|
|
assert(g_sound_count == 1 && g_sounds[0] == 16);
|
|
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdf)) == 0);
|
|
assert(g_simulate_calls == 1);
|
|
}
|
|
|
|
static void test_multiball_promotion(void)
|
|
{
|
|
prepare_fixture();
|
|
win16_write_u8(win16_far_add_offset(g_window, 0x0bdd), 1);
|
|
win16_write_u8(win16_far_add_offset(g_window, 0x0bdf), 1);
|
|
win16_write_u8(win16_far_add_offset(g_window, 0x61), 2);
|
|
win16_write_u8(win16_far_add_offset(g_window, 0x0be1), 1);
|
|
win16_write_u8(win16_dgroup_pointer(0x424c), 1);
|
|
win16_write_u32(win16_dgroup_pointer(0x07e7), 0, 0x11111111);
|
|
win16_write_u32(win16_dgroup_pointer(0x07ef), 0, 0x22222222);
|
|
win16_write_u32(win16_dgroup_pointer(0x07f7), 0, 0x33333333);
|
|
win16_write_u32(win16_dgroup_pointer(0x07ff), 0, 0x44444444);
|
|
win16_write_u32(win16_dgroup_pointer(0x0807), 0, 0x55555555);
|
|
for (uint16_t index = 0; index < 6; index++) {
|
|
win16_write_u8(
|
|
win16_dgroup_pointer((uint16_t)(0x0811 + index)),
|
|
(uint8_t)(0xa0 + index));
|
|
}
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(win16_read_u8(win16_far_add_offset(g_window, 0x61)) == 1);
|
|
assert(win16_read_u8(win16_dgroup_pointer(0x41f9)) == 1);
|
|
assert(win16_read_u8(win16_dgroup_pointer(0x424c)) == 0);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x07e3)) == 0x11111111);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x07eb)) == 0x22222222);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x07f3)) == 0x33333333);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x07fb)) == 0x44444444);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x0803)) == 0x55555555);
|
|
assert(g_load_count == 1 && g_load_slots[0] == 0);
|
|
assert(g_finish_calls == 0);
|
|
}
|
|
|
|
static void test_multiball_spawn(void)
|
|
{
|
|
prepare_fixture();
|
|
win16_write_u8(win16_far_add_offset(g_window, 0x0bdd), 1);
|
|
win16_write_u8(win16_far_add_offset(g_window, 0x0bdf), 1);
|
|
win16_write_u8(win16_far_add_offset(g_window, 0x0be0), 1);
|
|
win16_write_u8(win16_dgroup_pointer(0x07d1), 1);
|
|
win16_write_u32(win16_dgroup_pointer(0x3969), 0, 123000);
|
|
win16_write_u32(win16_dgroup_pointer(0x396d), 0, 456000);
|
|
win16_write_u32(g_window, 0x56, 1000);
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(g_save_count == 2 && g_save_slots[0] == 0 && g_save_slots[1] == 1);
|
|
assert(g_dynamic_count == 2 &&
|
|
g_dynamic_records[0] == 1 && g_dynamic_records[1] == 2);
|
|
assert(g_restore_previous_calls == 1);
|
|
assert(g_restore_b_id_count == 1 && g_restore_b_ids[0] == 1020);
|
|
assert((int32_t)win16_read_u32(win16_dgroup_pointer(0x07d3)) == 123000);
|
|
assert((int32_t)win16_read_u32(win16_dgroup_pointer(0x07d7)) == 456000);
|
|
assert((int32_t)win16_read_u32(
|
|
win16_far_add_offset(g_window, 0x0bae)) == 800);
|
|
assert(win16_read_u8(win16_far_add_offset(g_window, 0x61)) == 2);
|
|
assert(win16_read_u8(win16_dgroup_pointer(0x0874)) == 1);
|
|
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0be0)) == 0);
|
|
}
|
|
|
|
static void test_intro_tick_32_and_marquee(void)
|
|
{
|
|
prepare_fixture();
|
|
win16_write_u8(win16_dgroup_pointer(0x07c9), 1);
|
|
win16_write_u32(win16_dgroup_pointer(0x0876), 0, 31);
|
|
win16_write_u16(record(90), 0x4b, 10);
|
|
win16_write_u16(record(90), 0x4d, 20);
|
|
win16_write_u16(record(90), 0x4f, 50);
|
|
win16_write_u16(record(90), 0x51, 60);
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(g_restore_a_id_count == 5);
|
|
assert(g_restore_a_ids[0] == 140);
|
|
assert(g_restore_a_ids[1] == 1001);
|
|
assert(g_restore_a_ids[2] == 153 &&
|
|
g_restore_a_ids[3] == 154 && g_restore_a_ids[4] == 6);
|
|
assert(g_restore_b_id_count == 1 && g_restore_b_ids[0] == 1002);
|
|
assert(g_region_a_calls == 2 && g_region_b_calls == 1);
|
|
assert(g_present_calls == 3);
|
|
assert(g_item_calls == 1 && g_last_item == 4);
|
|
|
|
prepare_fixture();
|
|
win16_write_u8(win16_dgroup_pointer(0x07c9), 1);
|
|
win16_write_u32(win16_dgroup_pointer(0x0876), 0, 5);
|
|
win16_write_u16(win16_dgroup_pointer(0x085f), 0, 0x7777);
|
|
win16_write_u16(win16_dgroup_pointer(0x0861), 0, 0x8888);
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x087a)) == 1);
|
|
assert(g_create_dc_calls == 2 && g_create_bitmap_calls == 1);
|
|
assert(g_blit_count == 4);
|
|
assert(g_blits[0].destination == 0x5555 &&
|
|
g_blits[0].width == 30 && g_blits[0].height == 100 &&
|
|
g_blits[0].source_x == 473 && g_blits[0].source_y == 277);
|
|
assert(g_blits[2].height == 237 && g_blits[2].source_x == 400);
|
|
assert(g_blits[3].destination == 0x3333 &&
|
|
g_blits[3].x == 233 && g_blits[3].y == 372);
|
|
assert(g_select_calls == 5 && g_delete_object_calls == 1);
|
|
assert(g_delete_dc_calls == 2);
|
|
|
|
static const struct {
|
|
uint32_t before_tick;
|
|
int16_t expected_item;
|
|
} item_cases[] = {
|
|
{7, 1},
|
|
{71, 9},
|
|
{79, 8},
|
|
{135, 1},
|
|
{143, 0},
|
|
};
|
|
for (size_t index = 0;
|
|
index < sizeof(item_cases) / sizeof(item_cases[0]);
|
|
index++) {
|
|
prepare_fixture();
|
|
win16_write_u8(win16_dgroup_pointer(0x07c9), 1);
|
|
win16_write_u32(
|
|
win16_dgroup_pointer(0x0876), 0,
|
|
item_cases[index].before_tick);
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(g_item_calls == 1);
|
|
assert(g_last_item == item_cases[index].expected_item);
|
|
}
|
|
}
|
|
|
|
static void test_panel_and_game_initialization(void)
|
|
{
|
|
prepare_fixture();
|
|
win16_write_u8(win16_dgroup_pointer(0x07c8), 1);
|
|
win16_write_u32(win16_dgroup_pointer(0x0851), 0, 1);
|
|
for (uint16_t id = 129; id <= 133; id++) {
|
|
win16_write_u16(record(id), 0x43, 0xffff);
|
|
}
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdf)) == 1);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x0851)) == 2);
|
|
assert(g_panel_calls == 1);
|
|
for (uint16_t id = 129; id <= 133; id++) {
|
|
assert(win16_read_u16(win16_far_add_offset(record(id), 0x43)) == 0);
|
|
}
|
|
|
|
prepare_fixture();
|
|
win16_write_u8(win16_dgroup_pointer(0x07c8), 1);
|
|
win16_write_u32(win16_dgroup_pointer(0x0851), 0, 281);
|
|
win16_write_u8(win16_dgroup_pointer(0x07d1), 1);
|
|
for (uint16_t item = 15; item <= 19; item++) {
|
|
win16_write_u8(
|
|
win16_far_add_offset(g_window, (uint16_t)(0x47b + 20 + item)), 1);
|
|
}
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(win16_read_u8(win16_dgroup_pointer(0x07c8)) == 0);
|
|
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdf)) == 0);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x0851)) == 0);
|
|
assert(g_region_b_calls == 1 && g_present_calls == 1);
|
|
|
|
prepare_fixture();
|
|
win16_write_u16(win16_dgroup_pointer(0x081f), 0, 0xffff);
|
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
|
assert(g_initialize_calls == 1 && g_redraw_calls == 1);
|
|
assert(g_progress_calls == 1 && g_reset_calls == 1);
|
|
assert(g_region_b_calls == 1 && g_present_calls == 1);
|
|
assert(win16_read_u32(win16_dgroup_pointer(0x0876)) == 0);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
test_guard_and_refresh_epilogue();
|
|
test_claw_release();
|
|
test_multiball_promotion();
|
|
test_multiball_spawn();
|
|
test_intro_tick_32_and_marquee();
|
|
test_panel_and_game_initialization();
|
|
return 0;
|
|
}
|