#include "../tdkpin_game_setup.h" #include #include #include static uint8_t g_dgroup[0x5000]; static uint8_t g_window_bytes[0x0c00]; static uint8_t g_code[0x300]; static Win16FarPtr g_window; static uint32_t g_heap_free; static uint16_t g_language; static uint16_t g_speed; static Win16FarPtr g_timer_result; static unsigned g_setup_window_calls; static unsigned g_palette_realize_calls; static unsigned g_palette_load_calls; static unsigned g_palette_create_calls; static unsigned g_timer_calls; static unsigned g_collision_calls; static unsigned g_quad_calls; static unsigned g_message_calls; static unsigned g_close_calls; static uint16_t g_collision_ids[177]; static uint8_t g_final_active[176]; static uint16_t g_final_contact[176]; static TdkpinWordQuad g_quads[21]; static Win16FarPtr g_last_message_text; static HWND16 g_last_message_owner; static jmp_buf g_exit_jump; static bool g_expect_exit; uint32_t borland_heap_free_space(void) { return g_heap_free; } void object_windows_setup_window(Win16FarPtr window) { assert(window == g_window); g_setup_window_calls++; } void object_windows_close_if_allowed(Win16FarPtr window) { assert(window == g_window); g_close_calls++; } _Noreturn void borland_runtime_exit(uint16_t status) { assert(g_expect_exit && status == 0); longjmp(g_exit_jump, 1); } INT16 win16_call_message_box_far( Win16FarPtr procedure, HWND16 owner, Win16FarPtr text, Win16FarPtr caption, UINT16 type) { assert(procedure == win16_make_far_pointer(0x8000, 0x1111)); assert(caption == win16_dgroup_pointer(0x02aa)); assert(type == 0x2010); g_last_message_owner = owner; g_last_message_text = text; g_message_calls++; return 1; } HCURSOR16 LoadCursor16(HINSTANCE16 instance, Win16FarPtr resource) { assert(instance == 0 && resource == win16_make_far_pointer(0, 0x7f02)); return 0x4444; } HCURSOR16 SetCursor16(HCURSOR16 cursor) { assert(cursor == 0x4444); return 0x5555; } void tdkpin_realize_nocollapse_palette(HWND16 window) { assert(window == 0x2222); g_palette_realize_calls++; } void tdkpin_load_palette_resource( Win16FarPtr destination, Win16FarPtr resource_name, HINSTANCE16 instance) { assert(destination == win16_dgroup_pointer(0x4450)); assert(resource_name == win16_make_far_pointer(0, 999)); assert(instance == 0x1234); g_palette_load_calls++; } void tdkpin_create_palette_from_rgb_table(void) { g_palette_create_calls++; } uint32_t GetTickCount16(void) { return 0x11223344u; } BorlandDivI32Result borland_divide_i32(int32_t dividend, int32_t divisor) { assert(divisor == 1000); return (BorlandDivI32Result){dividend / divisor, dividend % divisor}; } void borland_parameter_string(uint16_t index, Win16FarPtr destination) { static const char module[] = "C:\\TDKPIN.EXE"; assert(index == 0); win16_write_u8(destination, (uint8_t)(sizeof(module) - 1)); for (uint16_t byte = 0; byte < sizeof(module) - 1; byte++) { win16_write_u8( win16_far_add_offset(destination, (uint16_t)(byte + 1)), (uint8_t)module[byte]); } } Win16FarPtr borland_short_to_c_string( Win16FarPtr destination, Win16FarPtr source) { uint8_t length = win16_read_u8(source); for (uint16_t byte = 0; byte < length; byte++) { win16_write_u8( win16_far_add_offset(destination, byte), win16_read_u8(win16_far_add_offset(source, (uint16_t)(byte + 1)))); } win16_write_u8(win16_far_add_offset(destination, length), 0); return destination; } uint16_t borland_short_position(Win16FarPtr pattern, Win16FarPtr text) { assert(pattern == win16_make_far_pointer(0x7300, 0x026e)); assert(win16_read_u8(text) == 13); return 10; } Win16FarPtr borland_far_strncpy( Win16FarPtr destination, Win16FarPtr source, uint16_t maximum) { assert(maximum == 10); for (uint16_t byte = 0; byte < maximum; byte++) { win16_write_u8( win16_far_add_offset(destination, byte), win16_read_u8(win16_far_add_offset(source, byte))); } win16_write_u8(win16_far_add_offset(destination, maximum), 0); return destination; } Win16FarPtr borland_far_strcat(Win16FarPtr destination, Win16FarPtr source) { uint16_t end = 0; while (win16_read_u8(win16_far_add_offset(destination, end)) != 0) { end++; } uint16_t byte = 0; do { uint8_t value = win16_read_u8(win16_far_add_offset(source, byte)); win16_write_u8( win16_far_add_offset(destination, (uint16_t)(end + byte)), value); byte++; if (value == 0) { break; } } while (true); return destination; } static void assert_far_c_string(Win16FarPtr string, const char *expected) { for (uint16_t index = 0;; index++) { uint8_t actual = win16_read_u8(win16_far_add_offset(string, index)); assert(actual == (uint8_t)expected[index]); if (actual == 0) { break; } } } UINT16 GetPrivateProfileInt16( Win16FarPtr section, Win16FarPtr key, INT16 default_value, Win16FarPtr file_name) { assert(section == win16_dgroup_pointer(0x02c7)); assert_far_c_string(file_name, "C:\\TDKPIN.INI"); if (key == win16_dgroup_pointer(0x02d0)) { assert(default_value == 1); return g_language; } assert(key == win16_dgroup_pointer(0x02d9) && default_value == 3); return g_speed; } Win16FarPtr tdkpin_system_timer_construct( Win16FarPtr object, uint16_t vmt, uint8_t one_shot, uint16_t resolution_ms, uint16_t delay_ms, HWND16 recipient) { assert(object == 0 && vmt == 0x05c0 && one_shot == 0); assert(resolution_ms == 1 && delay_ms == 20 && recipient == 0x2222); g_timer_calls++; return g_timer_result; } void tdkpin_initialize_collision_record( const TdkpinCollisionInitArgs *arguments) { assert(g_collision_calls < 177 && arguments->index <= 175); g_collision_ids[g_collision_calls++] = arguments->index; g_final_active[arguments->index] = (uint8_t)arguments->active; g_final_contact[arguments->index] = arguments->contact_state; Win16FarPtr target = win16_dgroup_pointer((uint16_t)( 0x095b + arguments->index * 0x53u)); win16_write_u8(win16_far_add_offset(target, 0x34), (uint8_t)arguments->active); win16_write_u16(target, 0x43, arguments->contact_state); } void tdkpin_set_word_quad( uint16_t source_word_6, uint16_t source_word_4, uint16_t source_word_2, uint16_t source_word_0, uint16_t index) { assert(index == g_quad_calls + 1 && index <= 20); g_quads[index] = (TdkpinWordQuad){ source_word_0, source_word_2, source_word_4, source_word_6}; g_quad_calls++; } static void prepare_fixture(void) { memset(g_dgroup, 0, sizeof(g_dgroup)); memset(g_window_bytes, 0xa5, sizeof(g_window_bytes)); memset(g_code, 0, sizeof(g_code)); memset(g_collision_ids, 0, sizeof(g_collision_ids)); memset(g_final_active, 0, sizeof(g_final_active)); memset(g_final_contact, 0, sizeof(g_final_contact)); memset(g_quads, 0, sizeof(g_quads)); win16_reset_segment_bindings(); win16_bind_segment(0x7100, g_window_bytes, sizeof(g_window_bytes), true); win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true); win16_bind_ne_segment(1, 0x7300, g_code, sizeof(g_code), false); win16_set_dgroup_selector(0x7200); uint8_t stack_anchor = 0; uintptr_t stack_base = (uintptr_t)&stack_anchor - 0x8000u; win16_bind_segment(0x7000, (void *)stack_base, 0x10000, true); g_window = win16_make_far_pointer(0x7100, 0); win16_write_u16(g_window, 4, 0x2222); win16_write_u16(win16_dgroup_pointer(0x06fe), 0, 0x1234); win16_write_u32( win16_dgroup_pointer(0x0666), 0, win16_make_far_pointer(0x8000, 0x1111)); memcpy(&g_dgroup[0x02aa], "TDK Pinball Machine 1.00", 25); memcpy(&g_dgroup[0x02c3], "INI", 4); memcpy(&g_dgroup[0x02c7], "Settings", 9); memcpy(&g_dgroup[0x02d0], "Language", 9); memcpy(&g_dgroup[0x02d9], "Speed", 6); memcpy(&g_dgroup[0x02df], "No timer available!", 20); g_heap_free = UINT32_C(0x00400000); g_language = 7; g_speed = 4; g_timer_result = win16_make_far_pointer(0x7400, 0x0100); g_setup_window_calls = 0; g_palette_realize_calls = 0; g_palette_load_calls = 0; g_palette_create_calls = 0; g_timer_calls = 0; g_collision_calls = 0; g_quad_calls = 0; g_message_calls = 0; g_close_calls = 0; g_last_message_text = 0; g_last_message_owner = 0; g_expect_exit = false; } static void test_successful_setup(void) { prepare_fixture(); tdkpin_setup_game_window(g_window); assert(g_setup_window_calls == 1); assert(g_palette_realize_calls == 1 && g_palette_load_calls == 1); assert(g_palette_create_calls == 1 && g_timer_calls == 1); assert(g_collision_calls == 177 && g_quad_calls == 20); assert(g_collision_ids[0] == 1 && g_collision_ids[1] == 2); assert(g_collision_ids[2] == 169 && g_collision_ids[3] == 170); assert(g_collision_ids[4] == 3); assert(g_collision_ids[170] == 169 && g_collision_ids[171] == 170); assert(g_collision_ids[176] == 175); assert(win16_read_u16(win16_dgroup_pointer(0x07cd)) == 1); assert(win16_read_u8(win16_far_add_offset(g_window, 0x60)) == 4); assert(win16_read_u16(win16_dgroup_pointer(0x0871)) == 2); assert(win16_read_far_pointer(g_window, 0x49) == g_timer_result); assert(win16_read_u32(win16_dgroup_pointer(0x0720)) == 0x11223344u); assert(win16_read_u32(win16_dgroup_pointer(0x0829)) == 140000u); assert(win16_read_u32(win16_dgroup_pointer(0x082d)) == 650000u); assert(win16_read_u32(win16_dgroup_pointer(0x0831)) == 1300000u); assert(win16_read_u32(win16_dgroup_pointer(0x0835)) == 4000000u); assert(win16_read_u32(win16_far_add_offset(g_window, 0x0ba2)) == 325u); assert(win16_read_u32(win16_far_add_offset(g_window, 0x0ba6)) == 413u); assert(g_quads[1].word_0 == 0x0067 && g_quads[1].word_6 == 0x0143); assert(g_quads[20].word_0 == 0x0009 && g_quads[20].word_6 == 0x001e); for (uint16_t id = 1; id <= 175; id++) { assert(win16_read_u8(win16_far_add_offset( g_window, (uint16_t)(0x03cc + id))) == g_final_active[id]); assert(win16_read_u16(win16_far_add_offset( g_window, (uint16_t)(0x0a42 + id * 2u))) == g_final_contact[id]); assert(win16_read_u8(win16_far_add_offset( g_window, (uint16_t)(0x61 + id))) == 0); } assert(g_message_calls == 0 && g_close_calls == 0); } static void test_low_memory_failure(void) { prepare_fixture(); g_heap_free = UINT32_C(0x003fffff); g_expect_exit = true; if (setjmp(g_exit_jump) == 0) { tdkpin_setup_game_window(g_window); assert(false); } assert(g_message_calls == 1 && g_last_message_owner == 0); assert(g_last_message_text == win16_dgroup_pointer(0x0260)); assert(g_close_calls == 1 && g_setup_window_calls == 0); assert(g_collision_calls == 0); } static void test_timer_failure(void) { prepare_fixture(); g_timer_result = 0; g_expect_exit = true; if (setjmp(g_exit_jump) == 0) { tdkpin_setup_game_window(g_window); assert(false); } assert(g_message_calls == 1 && g_last_message_owner == 0x2222); assert(g_last_message_text == win16_dgroup_pointer(0x02df)); assert(g_close_calls == 0 && g_collision_calls == 0); } int main(void) { test_successful_setup(); test_low_memory_failure(); test_timer_failure(); return 0; }