feat(reconstruction): complete binary-backed C recovery

Replace the partial mechanics transcriptions with a separate, readable C11
reconstruction of the complete Win16 image while preserving the original raw
Ghidra export as immutable evidence. Cover all ordinary and overlapping entry
points, Borland runtime behavior, Win16 imports, segmented data, callbacks,
resources, indirect control flow, physics, rendering, persistence, and
startup/shutdown lifecycles.

Add deterministic extraction and audit tooling plus address-linked ledgers for
functions, imports, DGROUP ranges and objects, relocations, resources, and
callbacks. The final gate records zero raw, partial, restored, unknown,
blocked, or unclassified required units. Keep the semantic-fidelity boundary
explicit: the portable C is not claimed to reproduce a byte-identical Borland
NE build.

Add strict focused harnesses for every reconstructed C unit, exact resource
round-trip checks, and a 16-bit Borland Real48 reference probe. No Rust source
or Cargo metadata is changed in this phase.

Test Plan:
- `bash original/tools/test_reconstructed_c.sh` -- passed
- `bash original/tools/probe_real48_reference.sh` -- passed bit-for-bit
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed
- `git diff --cached --check` -- passed
- `git diff HEAD -- '*.rs' Cargo.toml Cargo.lock` -- empty
This commit is contained in:
ddidderr committed 2026-08-23 16:41:17 +02:00
1 parent aef404c834
commit 8b99e9607c
253 files changed
+79031 -51

No files matched your search

@@ -0,0 +1,200 @@
#include "../tdkpin_application.h"
#include <assert.h>
Win16FarPtr g_object_windows_application;
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
uint16_t g_win16_show_command;
Win16FarPtr g_object_windows_dispatch_proc;
uint16_t g_object_windows_thunk_code_selector;
Win16FarPtr g_object_windows_free_thunk;
static Win16FarPtr g_make_source;
static HINSTANCE16 g_make_instance;
static Win16FarPtr g_freed_proc;
static Win16FarPtr g_first_method;
static Win16FarPtr g_second_method;
static unsigned g_method_calls;
static bool g_first_sets_error;
static unsigned g_heap_reserve_initializations;
static Win16FarPtr g_freed_object;
static uint16_t g_freed_size;
static Win16FarPtr g_setup_method;
static Win16FarPtr g_create_main_method;
static Win16FarPtr g_created_main_window;
static HWND16 g_shown_window;
static INT16 g_show_command;
BorlandAllocationAttempt borland_try_get_mem(uint16_t bytes)
{
(void)bytes;
return (BorlandAllocationAttempt){0, true};
}
bool borland_try_free_mem(Win16FarPtr object, uint16_t bytes)
{
g_freed_object = object;
g_freed_size = bytes;
return false;
}
_Noreturn void borland_runtime_error(uint16_t code)
{
(void)code;
assert(false);
__builtin_unreachable();
}
Win16FarPtr MakeProcInstance16(
Win16FarPtr procedure, HINSTANCE16 instance)
{
g_make_source = procedure;
g_make_instance = instance;
return win16_make_far_pointer(0x6200, 0x2222);
}
void FreeProcInstance16(Win16FarPtr procedure)
{
g_freed_proc = procedure;
}
void borland_initialize_heap_reserve(void)
{
g_heap_reserve_initializations++;
}
void win16_call_object_method(
Win16FarPtr procedure, Win16FarPtr object)
{
g_method_calls++;
if (procedure == g_setup_method) {
return;
}
if (procedure == g_first_method) {
if (g_first_sets_error) {
win16_write_u16(object, 2, 5);
}
return;
}
assert(procedure == g_second_method);
}
Win16FarPtr win16_call_object_far_method(
Win16FarPtr procedure, Win16FarPtr object)
{
(void)object;
assert(procedure == g_create_main_method);
return g_created_main_window;
}
BOOL16 ShowWindow16(HWND16 window, INT16 command)
{
g_shown_window = window;
g_show_command = command;
return 1;
}
static void reset_calls(void)
{
g_method_calls = 0;
g_first_sets_error = false;
g_heap_reserve_initializations = 0;
g_freed_proc = 0;
g_freed_object = 0;
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t code[0x200];
static uint8_t object_bytes[64];
static uint8_t main_window_bytes[32];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_ne_segment(4, 0x6000, code, sizeof(code), false);
win16_bind_segment(
0x7000, object_bytes, sizeof(object_bytes), true);
win16_bind_segment(
0x7100, main_window_bytes, sizeof(main_window_bytes), true);
uint16_t vmt = 0x0200;
win16_write_u16(win16_dgroup_pointer(vmt), 0, 18);
g_first_method = win16_make_far_pointer(0x6100, 0x1000);
g_second_method = win16_make_far_pointer(0x6100, 0x2000);
g_setup_method = win16_make_far_pointer(0x6100, 0x3000);
g_create_main_method = win16_make_far_pointer(0x6100, 0x4000);
win16_write_u16(win16_dgroup_pointer((uint16_t)(vmt + 0x10)), 0,
win16_far_offset(g_first_method));
win16_write_u16(win16_dgroup_pointer((uint16_t)(vmt + 0x12)), 0,
win16_far_selector(g_first_method));
win16_write_u16(win16_dgroup_pointer((uint16_t)(vmt + 0x14)), 0,
win16_far_offset(g_second_method));
win16_write_u16(win16_dgroup_pointer((uint16_t)(vmt + 0x16)), 0,
win16_far_selector(g_second_method));
win16_write_u16(win16_dgroup_pointer((uint16_t)(vmt + 0x18)), 0,
win16_far_offset(g_setup_method));
win16_write_u16(win16_dgroup_pointer((uint16_t)(vmt + 0x1a)), 0,
win16_far_selector(g_setup_method));
win16_write_u16(win16_dgroup_pointer((uint16_t)(vmt + 0x34)), 0,
win16_far_offset(g_create_main_method));
win16_write_u16(win16_dgroup_pointer((uint16_t)(vmt + 0x36)), 0,
win16_far_selector(g_create_main_method));
Win16FarPtr object = win16_make_far_pointer(0x7000, 4);
Win16FarPtr name = win16_make_far_pointer(0x5000, 0x0300);
g_win16_instance = 0x1111;
g_win16_previous_instance = 0;
reset_calls();
assert(object_windows_application_construct(object, vmt, name) == object);
assert(win16_read_u16(object) == vmt);
assert(win16_read_far_pointer(object, 4) == name);
assert(g_object_windows_application == object);
assert(g_make_source == win16_make_far_pointer(0x6000, 0x0133));
assert(g_make_instance == 0x1111);
assert(g_object_windows_dispatch_proc ==
win16_make_far_pointer(0x6200, 0x2222));
assert(g_heap_reserve_initializations == 1 && g_method_calls == 2);
assert(!object_windows_application_idle_default(object));
object_windows_application_init_application_default(object);
Win16FarPtr dialog = win16_make_far_pointer(0x7200, 0x3456);
object_windows_application_set_active_dialog(object, dialog);
assert(win16_read_far_pointer(object, 0x0e) == dialog);
g_created_main_window = win16_make_far_pointer(0x7100, 2);
win16_write_u16(g_created_main_window, 4, 0x0088);
g_win16_show_command = 7;
g_shown_window = 0;
object_windows_application_initialize(object);
assert(win16_read_far_pointer(object, 8) == g_created_main_window);
assert(g_shown_window == 0x0088 && g_show_command == 7);
g_created_main_window = 0;
win16_write_u16(object, 2, 0);
object_windows_application_initialize(object);
assert(win16_read_far_pointer(object, 8) == 0);
assert(win16_read_u16(win16_far_add_offset(object, 2)) == 0xfffb);
g_shown_window = 0;
object_windows_show(win16_make_far_pointer(0x7100, 10), 3);
assert(g_shown_window == 0);
reset_calls();
g_win16_previous_instance = 9;
assert(object_windows_application_construct(object, vmt, name) == object);
assert(g_method_calls == 1);
/* With InitApplication skipped, the first observed call is InitInstance. */
assert(g_second_method != g_first_method);
reset_calls();
g_win16_previous_instance = 0;
g_first_sets_error = true;
(void)object_windows_application_construct(object, vmt, name);
assert(g_method_calls == 1);
assert(win16_read_u16(win16_far_add_offset(object, 2)) == 5);
win16_write_u16(object, 0, vmt);
object_windows_application_destruct(object, 1);
assert(g_freed_proc == g_object_windows_dispatch_proc);
assert(g_freed_object == object && g_freed_size == 18);
return 0;
}
@@ -0,0 +1,274 @@
#include "../tdkpin_application.h"
#include <assert.h>
#include <string.h>
Win16FarPtr g_object_windows_application;
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
Win16FarPtr g_object_windows_dispatch_proc;
uint16_t g_object_windows_thunk_code_selector;
Win16FarPtr g_object_windows_free_thunk;
Win16FarPtr g_object_windows_message_hook;
static MSG16 g_messages[4];
static unsigned g_message_index;
static unsigned g_message_count;
static unsigned g_empty_before_messages;
static unsigned g_wait_calls;
static unsigned g_translate_calls;
static unsigned g_dispatch_calls;
static bool g_idle_result;
static bool g_main_filter_result;
static Win16FarPtr g_filter_order[4];
static unsigned g_filter_calls;
static Win16FarPtr g_true_filter;
static BOOL16 g_dialog_result;
static HWND16 g_dialog_window;
static INT16 g_accelerator_result;
static HWND16 g_accelerator_window;
static HACCEL16 g_accelerator;
static BOOL16 g_mdi_result;
static HWND16 g_mdi_window;
static Win16FarPtr g_client_object;
static Win16FarPtr g_client_getter;
static Win16FarPtr g_bool_procedure;
static Win16FarPtr g_void_method;
static Win16FarPtr g_error_method;
static uint16_t g_error_argument;
BOOL16 PeekMessage16(
MSG16 *message,
HWND16 window,
UINT16 minimum_message,
UINT16 maximum_message,
UINT16 remove_message)
{
assert(window == 0 && minimum_message == 0 && maximum_message == 0);
assert(remove_message == 1);
if (g_empty_before_messages != 0) {
g_empty_before_messages--;
return 0;
}
assert(g_message_index < g_message_count);
*message = g_messages[g_message_index++];
return 1;
}
BOOL16 WaitMessage16(void)
{
g_wait_calls++;
return 1;
}
BOOL16 TranslateMessage16(const MSG16 *message)
{
assert(message->message != 0x12);
g_translate_calls++;
return 1;
}
LRESULT16 DispatchMessage16(const MSG16 *message)
{
assert(message->message != 0x12);
g_dispatch_calls++;
return 0;
}
BOOL16 IsDialogMessage16(HWND16 dialog, MSG16 *message)
{
(void)message;
g_dialog_window = dialog;
return g_dialog_result;
}
INT16 TranslateAccelerator16(
HWND16 window, HACCEL16 accelerator, MSG16 *message)
{
(void)message;
g_accelerator_window = window;
g_accelerator = accelerator;
return g_accelerator_result;
}
BOOL16 TranslateMDISysAccel16(HWND16 client, MSG16 *message)
{
(void)message;
g_mdi_window = client;
return g_mdi_result;
}
bool win16_call_object_bool_method(
Win16FarPtr procedure, Win16FarPtr object)
{
(void)procedure;
(void)object;
g_bool_procedure = procedure;
return g_idle_result;
}
bool win16_call_application_message_filter(
Win16FarPtr procedure, Win16FarPtr application, MSG16 *message)
{
(void)application;
(void)message;
g_filter_order[g_filter_calls++] = procedure;
if (g_filter_calls == 1 && g_main_filter_result) {
return true;
}
return procedure == g_true_filter;
}
Win16FarPtr win16_call_object_far_method(
Win16FarPtr procedure, Win16FarPtr object)
{
assert(procedure == g_client_getter);
(void)object;
return g_client_object;
}
void win16_call_object_method(
Win16FarPtr procedure, Win16FarPtr object)
{
(void)object;
g_void_method = procedure;
}
void win16_call_object_u16_method(
Win16FarPtr procedure, Win16FarPtr object, uint16_t argument)
{
(void)object;
g_error_method = procedure;
g_error_argument = argument;
}
static void write_far(uint16_t selector, uint16_t offset, Win16FarPtr value)
{
Win16FarPtr base = win16_make_far_pointer(selector, offset);
win16_write_u16(base, 0, win16_far_offset(value));
win16_write_u16(base, 2, win16_far_selector(value));
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t application_bytes[64];
static uint8_t window_bytes[64];
static uint8_t dialog_bytes[32];
static uint8_t client_bytes[32];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(
0x7000, application_bytes, sizeof(application_bytes), true);
win16_bind_segment(0x7100, window_bytes, sizeof(window_bytes), true);
win16_bind_segment(0x7200, dialog_bytes, sizeof(dialog_bytes), true);
win16_bind_segment(0x7300, client_bytes, sizeof(client_bytes), true);
Win16FarPtr application = win16_make_far_pointer(0x7000, 2);
Win16FarPtr main_window = win16_make_far_pointer(0x7100, 2);
Win16FarPtr dialog = win16_make_far_pointer(0x7200, 2);
g_client_object = win16_make_far_pointer(0x7300, 2);
const uint16_t app_vmt = 0x0100;
const uint16_t window_vmt = 0x0200;
win16_write_u16(application, 0, app_vmt);
win16_write_u16(main_window, 0, window_vmt);
write_far(0x7000, 10, main_window);
write_far(0x7000, 16, dialog);
win16_write_u16(main_window, 4, 0x1111);
win16_write_u16(dialog, 4, 0x2222);
win16_write_u16(g_client_object, 4, 0x3333);
Win16FarPtr idle_proc = win16_make_far_pointer(0x6000, 0x1000);
Win16FarPtr main_filter = win16_make_far_pointer(0x6000, 0x2000);
Win16FarPtr run_proc = win16_make_far_pointer(0x6000, 0x2100);
Win16FarPtr error_proc = win16_make_far_pointer(0x6000, 0x2200);
Win16FarPtr filters[3] = {
win16_make_far_pointer(0x6000, 0x3000),
win16_make_far_pointer(0x6000, 0x4000),
win16_make_far_pointer(0x6000, 0x5000),
};
write_far(0x5000, app_vmt + 0x0c, idle_proc);
write_far(0x5000, app_vmt + 0x24, main_filter);
write_far(0x5000, app_vmt + 0x20, run_proc);
write_far(0x5000, app_vmt + 0x40, error_proc);
write_far(0x5000, app_vmt + 0x28, filters[0]);
write_far(0x5000, app_vmt + 0x30, filters[1]);
write_far(0x5000, app_vmt + 0x2c, filters[2]);
g_client_getter = win16_make_far_pointer(0x6100, 0x6000);
Win16FarPtr can_close = win16_make_far_pointer(0x6100, 0x7000);
write_far(0x5000, window_vmt + 0x30, g_client_getter);
write_far(0x5000, window_vmt + 0x3c, can_close);
memset(g_messages, 0, sizeof(g_messages));
g_messages[0].message = 0x0100;
g_messages[1].message = 0x0012;
g_messages[1].wparam = 0x55aa;
g_message_count = 2;
g_message_index = 0;
g_empty_before_messages = 1;
g_wait_calls = 0;
g_translate_calls = 0;
g_dispatch_calls = 0;
g_filter_calls = 0;
g_idle_result = false;
g_main_filter_result = false;
g_true_filter = 0;
object_windows_application_message_loop(application);
assert(g_wait_calls == 1);
assert(g_translate_calls == 1 && g_dispatch_calls == 1);
assert(g_filter_calls == 1 && g_filter_order[0] == main_filter);
assert(win16_read_u16(win16_far_add_offset(application, 2)) == 0x55aa);
MSG16 message = {0};
g_filter_calls = 0;
g_true_filter = filters[1];
assert(object_windows_application_preprocess_message(
application, &message));
assert(g_filter_calls == 2);
assert(g_filter_order[0] == filters[0] &&
g_filter_order[1] == filters[1]);
g_filter_calls = 0;
g_true_filter = 0;
assert(!object_windows_application_preprocess_message(
application, &message));
assert(g_filter_calls == 3);
g_dialog_result = 1;
assert(object_windows_application_process_dialog_message(
application, &message) == 0xff01);
assert(g_dialog_window == 0x2222);
g_dialog_result = 0;
assert(object_windows_application_process_dialog_message(
application, &message) == 0);
win16_write_u16(application, 0x0c, 0x4444);
g_accelerator_result = 1;
assert(object_windows_application_process_accelerator(
application, &message));
assert(g_accelerator_window == 0x1111 && g_accelerator == 0x4444);
g_mdi_result = 1;
assert(object_windows_application_process_mdi_accelerator(
application, &message));
assert(g_mdi_window == 0x3333);
g_client_object = 0;
assert(!object_windows_application_process_mdi_accelerator(
application, &message));
assert(object_windows_window_get_mdi_client_default(main_window) == 0);
win16_write_u16(application, 2, 0);
g_void_method = 0;
object_windows_application_run(application);
assert(g_void_method == run_proc);
win16_write_u16(application, 2, 0xfffb);
g_error_method = 0;
object_windows_application_run(application);
assert(g_error_method == error_proc && g_error_argument == 0xfffb);
g_idle_result = true;
assert(object_windows_application_can_close(application));
assert(g_bool_procedure == can_close);
g_idle_result = false;
assert(!object_windows_application_can_close(application));
return 0;
}
@@ -0,0 +1,215 @@
#include "../tdkpin_application.h"
#include <assert.h>
#include <setjmp.h>
#include <stdio.h>
#include <string.h>
Win16FarPtr g_object_windows_application;
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
uint16_t g_win16_show_command;
Win16FarPtr g_object_windows_dispatch_proc;
uint16_t g_object_windows_thunk_code_selector;
Win16FarPtr g_object_windows_free_thunk;
Win16FarPtr g_object_windows_message_hook;
Win16FarPtr g_win16_message_box;
static bool g_reserve_unallocated;
static unsigned g_ensure_calls;
static Win16FarPtr g_disposed;
static uint16_t g_error;
static Win16FarPtr g_validator_result;
static bool g_create_result;
static int16_t g_execute_result;
static Win16FarPtr g_validate_proc;
static Win16FarPtr g_error_proc;
static Win16FarPtr g_create_proc;
static Win16FarPtr g_execute_proc;
static INT16 g_message_box_response;
static char g_message_text[32];
static uint16_t g_exit_status;
static jmp_buf g_exit_jump;
bool borland_heap_reserve_is_unallocated(void)
{
return g_reserve_unallocated;
}
void borland_ensure_heap_reserve(void)
{
g_ensure_calls++;
}
void borland_dispose_object(Win16FarPtr object)
{
g_disposed = object;
}
void win16_call_object_u16_method(
Win16FarPtr procedure, Win16FarPtr object, uint16_t argument)
{
(void)object;
assert(procedure == g_error_proc);
g_error = argument;
}
Win16FarPtr win16_call_application_window_method(
Win16FarPtr procedure, Win16FarPtr application, Win16FarPtr window)
{
(void)application;
(void)window;
assert(procedure == g_validate_proc);
return g_validator_result;
}
bool win16_call_object_bool_method(
Win16FarPtr procedure, Win16FarPtr object)
{
(void)object;
assert(procedure == g_create_proc);
return g_create_result;
}
int16_t win16_call_object_int_method(
Win16FarPtr procedure, Win16FarPtr object)
{
(void)object;
assert(procedure == g_execute_proc);
return g_execute_result;
}
INT16 wvsprintf16(
char *output, const char *format, const uint16_t *arguments)
{
assert(strcmp(format, "Error code = %d. Continue?") == 0);
return (INT16)snprintf(
output, 32, format, (int16_t)arguments[0]);
}
INT16 win16_call_message_box(
Win16FarPtr procedure,
HWND16 owner,
const char *text,
const char *caption,
UINT16 type)
{
assert(procedure == g_win16_message_box);
assert(owner == 0 && type == 0x14);
assert(strcmp(caption, "Application Error") == 0);
strcpy(g_message_text, text);
return g_message_box_response;
}
_Noreturn void borland_runtime_exit(uint16_t status)
{
g_exit_status = status;
longjmp(g_exit_jump, 1);
}
static void write_far(uint16_t offset, Win16FarPtr pointer)
{
win16_write_u16(
win16_dgroup_pointer(offset), 0, win16_far_offset(pointer));
win16_write_u16(
win16_dgroup_pointer((uint16_t)(offset + 2)),
0,
win16_far_selector(pointer));
}
static void reset_state(void)
{
g_reserve_unallocated = false;
g_ensure_calls = 0;
g_disposed = 0;
g_error = 0;
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t application_bytes[64];
static uint8_t window_bytes[64];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(
0x7000, application_bytes, sizeof(application_bytes), true);
win16_bind_segment(0x7100, window_bytes, sizeof(window_bytes), true);
Win16FarPtr application = win16_make_far_pointer(0x7000, 2);
Win16FarPtr window = win16_make_far_pointer(0x7100, 2);
uint16_t app_vmt = 0x0100;
uint16_t window_vmt = 0x0200;
win16_write_u16(application, 0, app_vmt);
win16_write_u16(window, 0, window_vmt);
g_validate_proc = win16_make_far_pointer(0x6000, 0x1000);
g_error_proc = win16_make_far_pointer(0x6000, 0x2000);
g_create_proc = win16_make_far_pointer(0x6100, 0x3000);
g_execute_proc = win16_make_far_pointer(0x6100, 0x4000);
g_win16_message_box = win16_make_far_pointer(0x6200, 0x5000);
write_far(app_vmt + 0x3c, g_validate_proc);
write_far(app_vmt + 0x40, g_error_proc);
write_far(window_vmt + 0x20, g_create_proc);
write_far(window_vmt + 0x4c, g_execute_proc);
reset_state();
assert(object_windows_application_validate_window(application, 0) == 0);
win16_write_u16(window, 2, 0);
assert(object_windows_application_validate_window(application, window) ==
window);
reset_state();
win16_write_u16(window, 2, 5);
assert(object_windows_application_validate_window(application, window) == 0);
assert(g_error == 5 && g_disposed == window && g_ensure_calls == 0);
reset_state();
g_reserve_unallocated = true;
win16_write_u16(window, 2, 0);
assert(object_windows_application_validate_window(application, window) == 0);
assert(g_error == 0xfffe && g_disposed == window && g_ensure_calls == 1);
reset_state();
g_validator_result = window;
g_create_result = true;
assert(object_windows_application_make_window(application, window) == window);
assert(g_disposed == 0);
reset_state();
g_validator_result = window;
g_create_result = false;
win16_write_u16(window, 2, 7);
assert(object_windows_application_make_window(application, window) == 0);
assert(g_error == 7 && g_disposed == window);
reset_state();
g_validator_result = 0;
assert(object_windows_application_make_window(application, window) == 0);
assert(g_disposed == 0);
reset_state();
g_validator_result = window;
g_execute_result = 4;
assert(object_windows_application_exec_dialog(application, window) == 4);
assert(g_disposed == window && g_error == 0);
reset_state();
g_validator_result = window;
g_execute_result = -1;
assert(object_windows_application_exec_dialog(application, window) == 2);
assert(g_error == 0xffff && g_disposed == window);
reset_state();
g_validator_result = 0;
assert(object_windows_application_exec_dialog(application, window) == 2);
assert(g_disposed == 0);
g_message_box_response = 6;
object_windows_application_error(application, 123);
assert(strcmp(g_message_text, "Error code = 123. Continue?") == 0);
g_message_box_response = 7;
if (setjmp(g_exit_jump) == 0) {
object_windows_application_error(application, 0x8123);
assert(false);
}
assert(g_exit_status == 0x8123);
assert(strcmp(g_message_text, "Error code = -32477. Continue?") == 0);
return 0;
}
@@ -0,0 +1,54 @@
#include "../tdkpin_arithmetic.h"
#include <assert.h>
#include <limits.h>
#include <setjmp.h>
#include <stdbool.h>
uint8_t g_borland_cpu_level;
static jmp_buf g_fault_jump;
static uint16_t g_runtime_code;
static bool g_divide_fault;
_Noreturn void borland_runtime_error(uint16_t code)
{
g_runtime_code = code;
longjmp(g_fault_jump, 1);
}
_Noreturn void win16_integer_divide_fault(void)
{
g_divide_fault = true;
longjmp(g_fault_jump, 1);
}
int main(void)
{
assert(borland_multiply_i32(123456, -789) == -97406784);
assert((uint32_t)borland_multiply_i32(INT32_MAX, INT32_MAX) == 1);
assert((uint32_t)borland_multiply_i32(INT32_MIN, 2) == 0);
BorlandDivI32Result result = borland_divide_i32(-100, 7);
assert(result.quotient == -14 && result.remainder == -2);
result = borland_divide_i32(100, -7);
assert(result.quotient == -14 && result.remainder == 2);
result = borland_divide_i32(-100, -7);
assert(result.quotient == 14 && result.remainder == -2);
g_borland_cpu_level = 1;
result = borland_divide_i32(INT32_MIN, -1);
assert(result.quotient == INT32_MIN && result.remainder == 0);
g_runtime_code = 0;
if (setjmp(g_fault_jump) == 0) {
(void)borland_divide_i32(1, 0);
assert(false);
}
assert(g_runtime_code == 200);
g_borland_cpu_level = 2;
g_divide_fault = false;
if (setjmp(g_fault_jump) == 0) {
(void)borland_divide_i32(INT32_MIN, -1);
assert(false);
}
assert(g_divide_fault);
return 0;
}
@@ -0,0 +1,128 @@
#include "../tdkpin_game_assets.h"
#include "../tdkpin_borland_objects.h"
#include <assert.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_window_bytes[0x0c00];
static Win16FarPtr g_window;
static Win16FarPtr g_owned;
static HGDIOBJ16 g_deleted[32];
static unsigned g_delete_count;
static unsigned g_step;
static Win16FarPtr dgroup_at(uint16_t offset)
{
return win16_make_far_pointer(0x5000, offset);
}
HCURSOR16 LoadCursor16(HINSTANCE16 instance, Win16FarPtr resource)
{
assert(++g_step == 1 && instance == 0);
assert(resource == win16_make_far_pointer(0, 0x7f02));
return 0x1111;
}
HCURSOR16 SetCursor16(HCURSOR16 cursor)
{
if (++g_step == 2) {
assert(cursor == 0x1111);
return 0x2222;
}
assert(cursor == 0x2222);
return 0;
}
void tdkpin_unload_sounds(void)
{
assert(++g_step == 3);
}
void borland_dispose_object(Win16FarPtr object)
{
assert(++g_step == 4 && object == g_owned);
}
void tdkpin_drain_messages_except_input(void)
{
assert(++g_step == 5);
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(object != 0 && g_delete_count < 32);
g_deleted[g_delete_count++] = object;
return 1;
}
void PostQuitMessage16(INT16 exit_code)
{
assert(exit_code == 0x12);
}
void object_windows_window_destruct(
Win16FarPtr window, uint16_t vmt)
{
assert(window == g_window && vmt == 0);
}
BOOL16 InvalidateRect16(
HWND16 window, Win16FarPtr rectangle, BOOL16 erase)
{
assert(window == 0x3333 && rectangle == 0 && erase == 1);
return 1;
}
void borland_finish_object_destructor(
BorlandObjectCallFrame *frame, uint16_t vmt_field_offset)
{
assert(frame->object == g_window && frame->vmt_argument == 0x9999);
assert(vmt_field_offset == 0);
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_window_bytes, sizeof(g_window_bytes), true);
win16_set_dgroup_selector(0x5000);
g_window = win16_make_far_pointer(0x6000, 0);
g_owned = win16_make_far_pointer(0x6000, 0x0800);
win16_write_u16(g_window, 4, 0x3333);
win16_write_u16(g_window, 0x52, 1);
win16_write_u32(g_window, 0x49, g_owned);
static const uint16_t global_offsets[] = {
0x0859, 0x0863, 0x0865, 0x085f, 0x085b, 0x0861, 0x085d,
};
for (uint16_t index = 0; index < 7; index++) {
win16_write_u16(dgroup_at(global_offsets[index]), 0, (uint16_t)(index + 2));
}
for (uint16_t index = 0; index < 8; index++) {
win16_write_u16(dgroup_at(0x430b), (uint16_t)(index * 2), (uint16_t)(10 + index));
}
for (uint16_t index = 1; index <= 4; index++) {
win16_write_u16(dgroup_at(0x4319), (uint16_t)(index * 2), (uint16_t)(19 + index));
}
for (uint16_t index = 1; index <= 9; index++) {
win16_write_u16(dgroup_at(0x4321), (uint16_t)(index * 2), (uint16_t)(29 + index));
}
tdkpin_asset_window_destruct(g_window, 0x9999);
assert(g_delete_count == 29);
for (uint16_t index = 0; index < 8; index++) {
assert(g_deleted[index] == index + 1);
}
for (uint16_t index = 0; index < 8; index++) {
assert(g_deleted[8 + index] == 10 + index);
}
for (uint16_t index = 0; index < 4; index++) {
assert(g_deleted[16 + index] == 20 + index);
}
for (uint16_t index = 0; index < 9; index++) {
assert(g_deleted[20 + index] == 30 + index);
}
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bd8)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bd9)) == 1);
return 0;
}
@@ -0,0 +1,343 @@
#include "../tdkpin_game_assets.h"
#include <assert.h>
#include <string.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_window_bytes[0x0c00];
HINSTANCE16 g_win16_instance;
static Win16FarPtr g_window;
static uint16_t g_dat_ids[40];
static unsigned g_dat_count;
static uint16_t g_bitmap_ids[8];
static unsigned g_bitmap_count;
static uint8_t g_stages[40];
static unsigned g_stage_count;
static int32_t g_drawn_scores[8];
static unsigned g_draw_score_count;
static unsigned g_select_palette_calls;
static unsigned g_unrealize_calls;
static unsigned g_realize_calls;
static unsigned g_drain_calls;
static unsigned g_sound_calls;
static unsigned g_create_dc_calls;
static unsigned g_create_bitmap_calls;
static unsigned g_select_object_calls;
static unsigned g_blit_calls;
static unsigned g_delete_object_calls;
static unsigned g_delete_dc_calls;
static unsigned g_client_rect_calls;
static unsigned g_get_dc_calls;
static unsigned g_release_dc_calls;
static unsigned g_cursor_calls;
static unsigned g_status_calls;
static uint8_t g_status_player;
static Win16FarPtr dgroup_at(uint16_t offset)
{
return win16_make_far_pointer(0x5000, offset);
}
static void reset_calls(void)
{
g_dat_count = 0;
g_bitmap_count = 0;
g_stage_count = 0;
g_draw_score_count = 0;
g_select_palette_calls = 0;
g_unrealize_calls = 0;
g_realize_calls = 0;
g_drain_calls = 0;
g_sound_calls = 0;
g_create_dc_calls = 0;
g_create_bitmap_calls = 0;
g_select_object_calls = 0;
g_blit_calls = 0;
g_delete_object_calls = 0;
g_delete_dc_calls = 0;
g_client_rect_calls = 0;
g_get_dc_calls = 0;
g_release_dc_calls = 0;
g_cursor_calls = 0;
g_status_calls = 0;
g_status_player = 0;
}
HPALETTE16 SelectPalette16(
HDC16 dc, HPALETTE16 palette, BOOL16 force_background)
{
assert(dc == 0x1111 && force_background == 0);
g_select_palette_calls++;
if (g_select_palette_calls == 1) {
assert(palette == 0x2222);
return 0x3333;
}
assert(palette == 0x3333);
return 0x2222;
}
BOOL16 UnrealizeObject16(HGDIOBJ16 object)
{
assert(object == 0x2222);
g_unrealize_calls++;
return 1;
}
UINT16 RealizePalette16(HDC16 dc)
{
assert(dc == 0x1111);
g_realize_calls++;
return 256;
}
void tdkpin_drain_messages_except_input(void)
{
g_drain_calls++;
}
HBITMAP16 tdkpin_load_bitmap_resource(
Win16FarPtr resource_name, HINSTANCE16 instance)
{
assert(instance == g_win16_instance);
assert(win16_far_selector(resource_name) == 0);
uint16_t id = win16_far_offset(resource_name);
g_dat_ids[g_dat_count++] = id;
return (HBITMAP16)(0x1000 + g_dat_count);
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x1111);
g_create_dc_calls++;
return (HDC16)(0x4000 + g_create_dc_calls);
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
(void)dc;
assert(object != 0);
g_select_object_calls++;
return (HGDIOBJ16)(0x5000 + g_select_object_calls);
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
assert(destination != 0 && source != 0);
(void)destination_x;
(void)destination_y;
(void)width;
(void)height;
assert(source_x == 0 && source_y == 0);
assert(raster_operation == 0x00cc0020 ||
raster_operation == 0x00c60088 ||
raster_operation == 0x00ee0086);
if (raster_operation != 0x00cc0020) {
assert(destination == 0x1111);
assert(destination_x == 10 && destination_y == 20);
assert(width == 29 && height == 39);
}
g_blit_calls++;
return 1;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(object != 0);
g_delete_object_calls++;
return 1;
}
void tdkpin_draw_loading_stage(Win16FarPtr caller_bp, uint8_t stage)
{
assert(win16_read_u16(win16_far_add_offset(caller_bp, 0x0e)) == 0x1111);
if (stage >= 2) {
assert(win16_read_u16(win16_far_add_offset(caller_bp, 0xfffe)) != 0);
}
g_stages[g_stage_count++] = stage;
}
void tdkpin_initialize_sounds(void)
{
g_sound_calls++;
}
HBITMAP16 LoadBitmap16(HINSTANCE16 instance, Win16FarPtr resource)
{
assert(instance == g_win16_instance && win16_far_selector(resource) == 0);
uint16_t id = win16_far_offset(resource);
g_bitmap_ids[g_bitmap_count++] = id;
return (HBITMAP16)(0x2000 + id);
}
HCURSOR16 LoadCursor16(HINSTANCE16 instance, Win16FarPtr resource)
{
assert(instance == 0 && resource == win16_make_far_pointer(0, 0x7f00));
g_cursor_calls++;
return 0x7777;
}
HCURSOR16 SetCursor16(HCURSOR16 cursor)
{
assert(cursor == 0x7777);
return 0;
}
BOOL16 GetClientRect16(HWND16 window, Win16FarPtr rectangle)
{
assert(window == 0x8888);
win16_write_u16(rectangle, 0, 0);
win16_write_u16(rectangle, 2, 0);
win16_write_u16(rectangle, 4, 640);
win16_write_u16(rectangle, 6, 460);
g_client_rect_calls++;
return 1;
}
HBITMAP16 CreateCompatibleBitmap16(HDC16 dc, INT16 width, INT16 height)
{
assert(dc == 0x1111 && width == 641 && height == 461);
g_create_bitmap_calls++;
return 0x6666;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(dc != 0);
g_delete_dc_calls++;
return 1;
}
HDC16 GetDC16(HWND16 window)
{
assert(window == 0x8888);
g_get_dc_calls++;
return 0x9999;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == 0x8888 && dc == 0x9999);
g_release_dc_calls++;
return 1;
}
int32_t borland_multiply_i32(int32_t left, int32_t right)
{
return left * right;
}
void tdkpin_draw_score(HDC16 destination, int32_t value_or_player)
{
assert(destination == 0x9999);
g_drawn_scores[g_draw_score_count++] = value_or_player;
}
void tdkpin_update_status_display(uint8_t player, HDC16 destination)
{
assert(destination == 0x9999);
g_status_player = player;
g_status_calls++;
}
static void assert_dat_ids(void)
{
static const uint16_t expected[] = {
995, 994, 998, 998,
400, 401, 402, 403, 404, 405, 406, 407,
701, 702, 703, 704,
801, 802, 803, 804, 805, 806, 807, 808, 809,
900, 997, 901, 600,
};
assert(g_dat_count == sizeof(expected) / sizeof(expected[0]));
assert(memcmp(g_dat_ids, expected, sizeof(expected)) == 0);
}
static void test_asset_loading(void)
{
reset_calls();
win16_write_u8(dgroup_at(0x07cc), 1);
win16_write_u8(win16_far_add_offset(g_window, 0x0bd8), 1);
tdkpin_initialize_assets_and_redraw(g_window, 0xbeef, 0xcafe, 0x1111);
assert_dat_ids();
assert(g_bitmap_count == 3);
assert(g_bitmap_ids[0] == 500 && g_bitmap_ids[1] == 102 && g_bitmap_ids[2] == 101);
assert(g_stage_count == 34);
for (unsigned index = 0; index < 34; index++) {
assert(g_stages[index] == index + 2);
}
assert(g_sound_calls == 1 && g_drain_calls == 1);
assert(g_select_palette_calls == 2 && g_cursor_calls == 1);
assert(win16_read_u8(dgroup_at(0x07cc)) == 0);
assert(win16_read_u16(win16_far_add_offset(g_window, 0x54)) == 0x2066);
assert(win16_read_u16(win16_far_add_offset(g_window, 0x52)) == 0x2065);
assert(g_get_dc_calls == 0);
}
static void test_redraw(void)
{
reset_calls();
win16_write_u8(dgroup_at(0x07cc), 0);
win16_write_u8(win16_far_add_offset(g_window, 0x0bd8), 0);
win16_write_u8(win16_far_add_offset(g_window, 0x0bde), 0);
win16_write_u32(g_window, 0x0ba2, 10);
win16_write_u32(g_window, 0x0ba6, 20);
win16_write_u16(g_window, 0x54, 0xaaaa);
win16_write_u16(g_window, 0x52, 0xbbbb);
win16_write_u8(dgroup_at(0x07d1), 2);
win16_write_u8(dgroup_at(0x09a3), 3);
win16_write_u32(dgroup_at(0x098e), 8, 10);
win16_write_u32(dgroup_at(0x097e), 4, 0);
win16_write_u32(dgroup_at(0x097e), 8, 42);
win16_write_u32(dgroup_at(0x097e), 12, (uint32_t)-1);
win16_write_u32(dgroup_at(0x097e), 16, 5);
tdkpin_initialize_assets_and_redraw(g_window, 1, 2, 0x1111);
assert(g_dat_count == 0 && g_bitmap_count == 0 && g_stage_count == 0);
assert(g_client_rect_calls == 2 && g_get_dc_calls == 1 && g_release_dc_calls == 1);
assert(g_draw_score_count == 3);
assert(g_drawn_scores[0] == 30 && g_drawn_scores[1] == 2 && g_drawn_scores[2] == 4);
assert(g_status_calls == 1 && g_status_player == 2);
assert(g_blit_calls == 5 && g_select_object_calls == 7);
assert(win16_read_u8(dgroup_at(0x07cb)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bd9)) == 0);
assert(g_select_palette_calls == 2 && g_cursor_calls == 1);
}
static void test_already_redrawn_palette_behavior(void)
{
reset_calls();
win16_write_u8(dgroup_at(0x07cc), 0);
win16_write_u8(win16_far_add_offset(g_window, 0x0bd8), 1);
tdkpin_initialize_assets_and_redraw(g_window, 0, 0, 0x1111);
assert(g_select_palette_calls == 1);
assert(g_unrealize_calls == 1 && g_realize_calls == 1 && g_drain_calls == 1);
assert(g_cursor_calls == 0 && g_get_dc_calls == 0);
}
int main(void)
{
uint8_t stack_marker;
uintptr_t stack_base = (uintptr_t)&stack_marker - 0x8000;
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_window_bytes, sizeof(g_window_bytes), true);
win16_bind_segment(0x7000, (void *)stack_base, 0x10000, true);
win16_set_dgroup_selector(0x5000);
g_window = win16_make_far_pointer(0x6000, 0);
g_win16_instance = 0x4444;
win16_write_u16(dgroup_at(0x444e), 0, 0x2222);
win16_write_u16(dgroup_at(0x085b), 0, 0xaaaa);
win16_write_u16(g_window, 4, 0x8888);
test_asset_loading();
test_redraw();
test_already_redrawn_palette_behavior();
return 0;
}
@@ -0,0 +1,301 @@
#include "../tdkpin_gameplay_helpers.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
int32_t g_ball_x_milli;
int32_t g_ball_y_milli;
uint8_t g_borland_cpu_level = 2;
_Noreturn void borland_runtime_error(uint16_t code)
{
(void)code;
__builtin_trap();
}
_Noreturn void win16_integer_divide_fault(void)
{
__builtin_trap();
}
static uint8_t g_stack[0x400];
static uint8_t g_object[0x0c00];
static uint8_t g_dgroup[0x5000];
static Win16FarPtr g_receiver;
static unsigned g_client_rect_calls;
static unsigned g_get_dc_calls;
static unsigned g_create_dc_calls;
static unsigned g_create_bitmap_calls;
static unsigned g_select_calls;
static unsigned g_blit_calls;
static unsigned g_delete_object_calls;
static unsigned g_delete_dc_calls;
static unsigned g_release_dc_calls;
static INT16 g_expected_x;
static INT16 g_expected_y;
static INT16 g_expected_height;
BOOL16 GetClientRect16(HWND16 window, Win16FarPtr rectangle)
{
assert(window == 0x2222);
assert(rectangle == win16_make_far_pointer(0x7000, 0x00e6));
win16_write_u16(rectangle, 0, 0);
win16_write_u16(rectangle, 2, 0);
win16_write_u16(rectangle, 4, 640);
win16_write_u16(rectangle, 6, 460);
g_client_rect_calls++;
return 1;
}
HDC16 GetDC16(HWND16 window)
{
assert(window == 0x2222);
g_get_dc_calls++;
return 0x3333;
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x3333 && g_create_dc_calls < 3);
return (HDC16)(0x4001 + g_create_dc_calls++);
}
HBITMAP16 CreateCompatibleBitmap16(HDC16 dc, INT16 width, INT16 height)
{
assert(dc == 0x3333 && width == 100 && height == 100);
g_create_bitmap_calls++;
return 0x5000;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
switch (g_select_calls++) {
case 0:
assert(dc == 0x4001 && object == 0x6000);
return 0x7001;
case 1:
assert(dc == 0x4002 && object == 0x5000);
return 0x7002;
case 2:
assert(dc == 0x4001 && object == 0x6100);
return 0x6000;
case 3:
assert(dc == 0x4001 && object == 0x6200);
return 0x6100;
case 4:
assert(dc == 0x4001 && object == 0x7001);
return 0x6200;
case 5:
assert(dc == 0x4002 && object == 0x7002);
return 0x5000;
default:
assert(g_select_calls == 7);
assert(dc == 0x4003 && object == 0x7003);
return 0;
}
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
switch (g_blit_calls++) {
case 0:
assert(destination == 0x4002);
assert(destination_x == 0 && destination_y == 0);
assert(width == 19 && height == 19 && source == 0x4001);
assert(source_x == g_expected_x && source_y == g_expected_y);
assert(raster_operation == 0x00cc0020u);
break;
case 1:
assert(destination == 0x4002 && destination_x == 1);
assert(destination_y == 1 && width == 19 && height == 19);
assert(source == 0x4001 && source_x == 0 && source_y == 0);
assert(raster_operation == 0x008800c6u);
break;
case 2:
assert(destination == 0x4002 && destination_x == 1);
assert(destination_y == 1 && width == 19 && height == 19);
assert(source == 0x4001 && source_x == 0 && source_y == 0);
assert(raster_operation == 0x00ee0086u);
break;
default:
assert(g_blit_calls == 4);
assert(destination == 0x3333);
assert(destination_x == g_expected_x && destination_y == g_expected_y);
assert(width == 19 && height == g_expected_height);
assert(source == 0x4002 && source_x == 0 && source_y == 0);
assert(raster_operation == 0x00cc0020u);
break;
}
return 1;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(object == 0x5000);
g_delete_object_calls++;
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(dc == (HDC16)(0x4001 + g_delete_dc_calls));
g_delete_dc_calls++;
return 1;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == 0x2222 && dc == 0x3333);
g_release_dc_calls++;
return 1;
}
static void reset_counters(void)
{
g_client_rect_calls = 0;
g_get_dc_calls = 0;
g_create_dc_calls = 0;
g_create_bitmap_calls = 0;
g_select_calls = 0;
g_blit_calls = 0;
g_delete_object_calls = 0;
g_delete_dc_calls = 0;
g_release_dc_calls = 0;
}
static void prepare_fixture(void)
{
memset(g_stack, 0, sizeof(g_stack));
memset(g_object, 0, sizeof(g_object));
memset(g_dgroup, 0, sizeof(g_dgroup));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_set_stack_state(0x7000, 0);
g_receiver = win16_make_far_pointer(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
win16_write_stack_u16(0x00fa, 0x7003);
win16_write_u16(g_receiver, 4, 0x2222);
win16_write_u16(g_receiver, 0x52, 0x6200);
win16_write_u16(g_receiver, 0x54, 0x6100);
win16_write_u16(win16_dgroup_pointer(0x085b), 0, 0x6000);
reset_counters();
}
static void assert_complete_gdi_lifecycle(void)
{
assert(g_client_rect_calls == 1 && g_get_dc_calls == 1);
assert(g_create_dc_calls == 3 && g_create_bitmap_calls == 1);
assert(g_select_calls == 7 && g_blit_calls == 4);
assert(g_delete_object_calls == 1 && g_delete_dc_calls == 3);
assert(g_release_dc_calls == 1);
assert(win16_read_stack_u16(0x00f4) == 0x3333);
assert(win16_read_stack_u16(0x00f2) == 0x4001);
assert(win16_read_stack_u16(0x00f0) == 0x4002);
assert(win16_read_stack_u16(0x00ee) == 0x4003);
assert(win16_read_stack_u16(0x00f8) == 0x5000);
assert(win16_read_stack_u16(0x00fe) == 0x7001);
assert(win16_read_stack_u16(0x00fc) == 0x7002);
}
static void test_render_and_bottom_clipping(void)
{
prepare_fixture();
g_ball_x_milli = 100000;
g_ball_y_milli = 440000;
g_expected_x = 91;
g_expected_y = 431;
g_expected_height = 12;
tdkpin_render_ball_from_caller_frame(0x0100);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0ba2)) == 92);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0ba6)) == 432);
assert(win16_read_stack_u16(0x00d6) == 12);
assert_complete_gdi_lifecycle();
prepare_fixture();
g_ball_x_milli = 100000;
g_ball_y_milli = 500000;
g_expected_x = 91;
g_expected_y = 491;
g_expected_height = 0;
tdkpin_render_ball_from_caller_frame(0x0100);
assert(win16_read_stack_u16(0x00d6) == 0);
assert_complete_gdi_lifecycle();
}
static void test_reset_branches_and_state(void)
{
prepare_fixture();
static const uint16_t completion_words[5] = {
0x3371, 0x33c4, 0x3417, 0x346a, 0x34bd};
for (unsigned index = 0; index < 5; index++) {
win16_write_u16(
win16_dgroup_pointer(completion_words[index]), 0, 1);
}
win16_write_u16(win16_dgroup_pointer(0x0851), 0, 0xaaaa);
win16_write_u16(win16_dgroup_pointer(0x0853), 0, 0xbbbb);
win16_write_u8(win16_dgroup_pointer(0x07c8), 0);
win16_write_u8(win16_dgroup_pointer(0x07c6), 1);
win16_write_u8(win16_far_add_offset(g_receiver, 0x0bdd), 1);
g_expected_x = 316;
g_expected_y = 404;
g_expected_height = 19;
tdkpin_reset_ball_state(0x0100);
assert(win16_read_u16(win16_dgroup_pointer(0x0851)) == 0);
assert(win16_read_u16(win16_dgroup_pointer(0x0853)) == 0);
assert(win16_read_u8(win16_dgroup_pointer(0x07c8)) == 1);
assert(win16_read_u8(win16_dgroup_pointer(0x07c6)) == 0);
assert(g_ball_x_milli == 325000 && g_ball_y_milli == 413000);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bba)) ==
325000u);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bbe)) ==
413000u);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bc2)) ==
325000u);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bc6)) ==
413000u);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0baa)) == 0);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bae)) == 0);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bca)) == 0);
assert(win16_read_u16(win16_far_add_offset(g_receiver, 0x0bce)) == 0);
assert(win16_read_u16(win16_far_add_offset(g_receiver, 0x0bd0)) == 0);
assert(win16_read_u16(win16_far_add_offset(g_receiver, 0x0bd2)) == 0);
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x0bdd)) == 0);
assert_complete_gdi_lifecycle();
prepare_fixture();
win16_write_u16(win16_dgroup_pointer(0x3371), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x0851), 0, 0xaaaa);
win16_write_u16(win16_dgroup_pointer(0x0853), 0, 0xbbbb);
win16_write_u8(win16_dgroup_pointer(0x07c8), 7);
g_expected_x = 316;
g_expected_y = 404;
g_expected_height = 19;
tdkpin_reset_ball_state(0x0100);
assert(win16_read_u16(win16_dgroup_pointer(0x0851)) == 0xaaaa);
assert(win16_read_u16(win16_dgroup_pointer(0x0853)) == 0xbbbb);
assert(win16_read_u8(win16_dgroup_pointer(0x07c8)) == 7);
assert_complete_gdi_lifecycle();
}
int main(void)
{
test_render_and_bottom_clipping();
test_reset_branches_and_state();
return 0;
}
@@ -0,0 +1,173 @@
#include "../tdkpin_gameplay_helpers.h"
#include <assert.h>
#include <string.h>
uint8_t g_borland_cpu_level = 2;
_Noreturn void borland_runtime_error(uint16_t code)
{
(void)code;
__builtin_trap();
}
_Noreturn void win16_integer_divide_fault(void)
{
__builtin_trap();
}
static uint8_t g_stack[0x400];
static uint8_t g_object[0x0c00];
static uint8_t g_dgroup[0x5000];
static Win16FarPtr g_receiver;
static unsigned g_create_dc_calls;
static unsigned g_select_calls;
static unsigned g_blit_calls;
static unsigned g_delete_dc_calls;
static unsigned g_release_calls;
static bool g_expect_present;
BOOL16 GetClientRect16(HWND16 window, Win16FarPtr rectangle)
{
assert(window == 0x2222);
assert(rectangle == win16_make_far_pointer(0x7000, 0x00e6));
return 1;
}
HDC16 GetDC16(HWND16 window)
{
assert(window == 0x2222);
return 0x3333;
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x3333 && g_create_dc_calls < 3);
return (HDC16)(0x4001 + g_create_dc_calls++);
}
HBITMAP16 CreateCompatibleBitmap16(HDC16 dc, INT16 width, INT16 height)
{
assert(dc == 0x3333 && width == 100 && height == 100);
return 0x5000;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
switch (g_select_calls++) {
case 0:
assert(dc == 0x4001 && object == 0x6000);
return 0x7001;
case 1:
assert(dc == 0x4002 && object == 0x5000);
return 0x7002;
case 2:
assert(dc == 0x4001 && object == 0x7001);
return 0x6000;
case 3:
assert(dc == 0x4002 && object == 0x7002);
return 0x5000;
default:
assert(g_select_calls == 5);
assert(dc == 0x4003 && object == 0x7003);
return 0;
}
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
assert(raster_operation == 0x00cc0020u);
if (g_blit_calls == 0) {
assert(destination == 0x4002 && destination_x == 0);
assert(destination_y == 0 && width == 19 && height == 19);
assert(source == 0x4001 && source_x == 91 && source_y == 431);
} else {
assert(g_expect_present && g_blit_calls == 1);
assert(destination == 0x3333 && destination_x == 91);
assert(destination_y == 431 && width == 19 && height == 12);
assert(source == 0x4002 && source_x == 0 && source_y == 0);
}
g_blit_calls++;
return 1;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(object == 0x5000);
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(dc == (HDC16)(0x4001 + g_delete_dc_calls));
g_delete_dc_calls++;
return 1;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == 0x2222 && dc == 0x3333);
g_release_calls++;
return 1;
}
static void prepare_fixture(void)
{
memset(g_stack, 0, sizeof(g_stack));
memset(g_object, 0, sizeof(g_object));
memset(g_dgroup, 0, sizeof(g_dgroup));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_set_stack_state(0x7000, 0);
g_receiver = win16_make_far_pointer(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
win16_write_stack_u16(0x00fa, 0x7003);
win16_write_u16(g_receiver, 4, 0x2222);
win16_write_u32(g_receiver, 0x0bc2, 100000);
win16_write_u32(g_receiver, 0x0bc6, 440000);
win16_write_u32(g_receiver, 0x0baa, 0xaaaaaaaau);
win16_write_u32(g_receiver, 0x0bae, 0xbbbbbbbbu);
win16_write_u16(win16_dgroup_pointer(0x085b), 0, 0x6000);
g_create_dc_calls = 0;
g_select_calls = 0;
g_blit_calls = 0;
g_delete_dc_calls = 0;
g_release_calls = 0;
}
static void run_case(uint8_t suppress)
{
prepare_fixture();
g_expect_present = suppress == 0;
tdkpin_restore_previous_ball_region(0x0100, suppress);
assert(g_create_dc_calls == 3 && g_select_calls == 5);
assert(g_blit_calls == (g_expect_present ? 2u : 1u));
assert(g_delete_dc_calls == 3 && g_release_calls == 1);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0ba2)) == 92);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0ba6)) == 432);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0baa)) == 0);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bae)) == 0);
assert(win16_read_stack_u16(0x00d6) == 12);
}
int main(void)
{
run_case(0);
run_case(1);
return 0;
}
@@ -0,0 +1,243 @@
#include "../tdkpin_gameplay_helpers.h"
#include <assert.h>
#include <string.h>
int32_t g_ball_x_milli;
int32_t g_ball_y_milli;
uint8_t g_borland_cpu_level = 2;
_Noreturn void borland_runtime_error(uint16_t code)
{
(void)code;
__builtin_trap();
}
_Noreturn void win16_integer_divide_fault(void)
{
__builtin_trap();
}
static uint8_t g_stack[0x400];
static uint8_t g_object[0x0c00];
static uint8_t g_dgroup[0x5000];
static Win16FarPtr g_receiver;
static unsigned g_create_dc_calls;
static unsigned g_select_calls;
static unsigned g_blit_calls;
static unsigned g_delete_dc_calls;
static INT16 g_dest_x;
static INT16 g_dest_y;
static INT16 g_source_x;
static INT16 g_source_y;
static INT16 g_width;
static INT16 g_capture_height;
static INT16 g_present_height;
BOOL16 GetClientRect16(HWND16 window, Win16FarPtr rectangle)
{
assert(window == 0x2222);
assert(rectangle == win16_make_far_pointer(0x7000, 0x00e6));
return 1;
}
HDC16 GetDC16(HWND16 window)
{
assert(window == 0x2222);
return 0x3333;
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x3333 && g_create_dc_calls < 3);
return (HDC16)(0x4001 + g_create_dc_calls++);
}
HBITMAP16 CreateCompatibleBitmap16(HDC16 dc, INT16 width, INT16 height)
{
assert(dc == 0x3333 && width == 100 && height == 100);
return 0x5000;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
switch (g_select_calls++) {
case 0:
assert(dc == 0x4001 && object == 0x6000);
return 0x7001;
case 1:
assert(dc == 0x4002 && object == 0x5000);
return 0x7002;
case 2:
assert(dc == 0x4001 && object == 0x6100);
return 0x6000;
case 3:
assert(dc == 0x4001 && object == 0x6200);
return 0x6100;
case 4:
assert(dc == 0x4001 && object == 0x7001);
return 0x6200;
case 5:
assert(dc == 0x4002 && object == 0x7002);
return 0x5000;
default:
assert(g_select_calls == 7);
assert(dc == 0x4003 && object == 0x7003);
return 0;
}
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
switch (g_blit_calls++) {
case 0:
assert(destination == 0x4002 && destination_x == 0);
assert(destination_y == 0 && width == g_width);
assert(height == g_capture_height && source == 0x4001);
assert(source_x == g_dest_x && source_y == g_dest_y);
assert(raster_operation == 0x00cc0020u);
break;
case 1:
assert(destination == 0x4002 && destination_x == g_source_x);
assert(destination_y == g_source_y && width == 19 && height == 19);
assert(source == 0x4001 && source_x == 0 && source_y == 0);
assert(raster_operation == 0x008800c6u);
break;
case 2:
assert(destination == 0x4002 && destination_x == g_source_x);
assert(destination_y == g_source_y && width == 19 && height == 19);
assert(source == 0x4001 && source_x == 0 && source_y == 0);
assert(raster_operation == 0x00ee0086u);
break;
default:
assert(g_blit_calls == 4);
assert(destination == 0x3333 && destination_x == g_dest_x);
assert(destination_y == g_dest_y && width == g_width);
assert(height == g_present_height && source == 0x4002);
assert(source_x == 0 && source_y == 0);
assert(raster_operation == 0x00cc0020u);
break;
}
return 1;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(object == 0x5000);
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(dc == (HDC16)(0x4001 + g_delete_dc_calls));
g_delete_dc_calls++;
return 1;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == 0x2222 && dc == 0x3333);
return 1;
}
static void prepare_fixture(void)
{
memset(g_stack, 0, sizeof(g_stack));
memset(g_object, 0, sizeof(g_object));
memset(g_dgroup, 0, sizeof(g_dgroup));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_set_stack_state(0x7000, 0);
g_receiver = win16_make_far_pointer(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
win16_write_stack_u16(0x00fa, 0x7003);
win16_write_u16(g_receiver, 4, 0x2222);
win16_write_u16(g_receiver, 0x52, 0x6200);
win16_write_u16(g_receiver, 0x54, 0x6100);
win16_write_u16(win16_dgroup_pointer(0x085b), 0, 0x6000);
g_create_dc_calls = 0;
g_select_calls = 0;
g_blit_calls = 0;
g_delete_dc_calls = 0;
}
static void run_case(
int32_t current_x,
int32_t current_y,
int32_t previous_x,
int32_t previous_y,
INT16 expected_dest_x,
INT16 expected_dest_y,
INT16 expected_source_x,
INT16 expected_source_y,
INT16 expected_width,
INT16 expected_capture_height,
INT16 expected_present_height,
int32_t expected_delta_x,
int32_t expected_delta_y)
{
prepare_fixture();
g_ball_x_milli = current_x;
g_ball_y_milli = current_y;
win16_write_u32(g_receiver, 0x0bc2, (uint32_t)previous_x);
win16_write_u32(g_receiver, 0x0bc6, (uint32_t)previous_y);
g_dest_x = expected_dest_x;
g_dest_y = expected_dest_y;
g_source_x = expected_source_x;
g_source_y = expected_source_y;
g_width = expected_width;
g_capture_height = expected_capture_height;
g_present_height = expected_present_height;
tdkpin_render_ball_swept_region(0x0100);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0bb2)) == expected_delta_x);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0bb6)) == expected_delta_y);
assert(g_create_dc_calls == 3 && g_select_calls == 7);
assert(g_blit_calls == 4 && g_delete_dc_calls == 3);
}
int main(void)
{
run_case(
120000, 430000, 100000, 440000,
91, 421, 21, 1, 39, 29, 22, 20, -10);
run_case(
80000, 400000, 100000, 380000,
71, 371, 1, 21, 39, 39, 39, -20, 20);
prepare_fixture();
g_ball_x_milli = 100000;
g_ball_y_milli = 400000;
g_dest_x = 91;
g_dest_y = 381;
g_source_x = 21;
g_source_y = 1;
g_width = 39;
g_capture_height = 29;
g_present_height = 29;
tdkpin_move_ball_and_render(0x0100, 20000, -10000);
assert(g_ball_x_milli == 120000 && g_ball_y_milli == 390000);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bc2)) ==
100000u);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bc6)) ==
400000u);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0bb2)) == 20);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0bb6)) == -10);
return 0;
}
@@ -0,0 +1,174 @@
#include "../tdkpin_palette.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_resource[2048];
static uint8_t g_bitmap_info[sizeof(BITMAPINFO256)];
static unsigned g_copy_calls;
static unsigned g_step;
static unsigned g_allocations;
static Win16FarPtr g_stack_header;
static Win16FarPtr dgroup_at(uint16_t offset)
{
return win16_make_far_pointer(0x5000, offset);
}
void borland_far_copy(
Win16FarPtr destination, Win16FarPtr source, uint16_t count)
{
assert(count == sizeof(BITMAPINFOHEADER16));
for (uint16_t index = 0; index < count; index++) {
win16_write_u8(
win16_far_add_offset(destination, index),
win16_read_u8(win16_far_add_offset(source, index)));
}
g_copy_calls++;
}
Win16FarPtr borland_get_mem(uint16_t bytes)
{
assert(bytes == sizeof(BITMAPINFO256));
g_allocations++;
return win16_make_far_pointer(0x7000, 0);
}
HRSRC16 FindResource16(
HINSTANCE16 instance, Win16FarPtr name, Win16FarPtr type)
{
assert(++g_step == 1 && instance == 0x4444);
assert(name == win16_make_far_pointer(0, 900));
assert(type == dgroup_at(0x045e));
return 0x1111;
}
HGLOBAL16 LoadResource16(HINSTANCE16 instance, HRSRC16 resource)
{
assert(++g_step == 2 && instance == 0x4444 && resource == 0x1111);
return 0x2222;
}
Win16FarPtr LockResource16(HGLOBAL16 resource)
{
assert(++g_step == 3 && resource == 0x2222);
return win16_make_far_pointer(0x6000, 0);
}
HDC16 CreateDC16(
Win16FarPtr driver,
Win16FarPtr device,
Win16FarPtr output,
Win16FarPtr initialization_data)
{
assert(++g_step == 4);
assert(driver == dgroup_at(0x0462));
assert(device == 0 && output == 0 && initialization_data == 0);
return 0x3333;
}
HPALETTE16 SelectPalette16(
HDC16 dc, HPALETTE16 palette, BOOL16 force_background)
{
assert(dc == 0x3333 && force_background == 0);
if (++g_step == 5) {
assert(palette == 0x4444);
return 0x5555;
}
assert(g_step == 9 && palette == 0x5555);
return 0x4444;
}
BOOL16 UnrealizeObject16(HGDIOBJ16 object)
{
assert(++g_step == 6 && object == 0x4444);
return 1;
}
UINT16 RealizePalette16(HDC16 dc)
{
assert(++g_step == 7 && dc == 0x3333);
return 256;
}
HBITMAP16 CreateDIBitmap16(
HDC16 dc,
Win16FarPtr header,
uint32_t initialization,
Win16FarPtr bits,
Win16FarPtr bitmap_info,
UINT16 color_usage)
{
assert(++g_step == 8 && dc == 0x3333);
assert(initialization == 4 && color_usage == 0);
assert(bits == win16_make_far_pointer(0x6000, 40));
assert(bitmap_info == win16_make_far_pointer(0x7000, 0));
assert(win16_read_u16(
win16_far_add_offset(header, offsetof(BITMAPINFOHEADER16, bit_count))) == 8);
g_stack_header = header;
for (uint16_t index = 0; index < 40; index++) {
assert(win16_read_u8(win16_far_add_offset(bitmap_info, index)) ==
g_resource[index]);
}
for (uint16_t index = 0; index < 256; index++) {
uint16_t color = (uint16_t)(40 + index * 4);
assert(win16_read_u8(win16_far_add_offset(bitmap_info, color)) ==
g_dgroup[0x4452 + index * 3]);
assert(win16_read_u8(win16_far_add_offset(bitmap_info, (uint16_t)(color + 1))) ==
g_dgroup[0x4451 + index * 3]);
assert(win16_read_u8(win16_far_add_offset(bitmap_info, (uint16_t)(color + 2))) ==
g_dgroup[0x4450 + index * 3]);
assert(win16_read_u8(win16_far_add_offset(bitmap_info, (uint16_t)(color + 3))) == 0);
}
return 0x6666;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(++g_step == 10 && dc == 0x3333);
return 1;
}
BOOL16 GlobalUnlock16(HGLOBAL16 resource)
{
assert(++g_step == 11 && resource == 0x2222);
return 1;
}
BOOL16 FreeResource16(HGLOBAL16 resource)
{
assert(++g_step == 12 && resource == 0x2222);
return 1;
}
int main(void)
{
uint8_t stack_marker;
uintptr_t stack_base = (uintptr_t)&stack_marker - 0x8000;
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_resource, sizeof(g_resource), true);
win16_bind_segment(0x7000, g_bitmap_info, sizeof(g_bitmap_info), true);
win16_bind_segment(0x8000, (void *)stack_base, 0x10000, true);
win16_set_dgroup_selector(0x5000);
memcpy(&g_dgroup[0x045e], "DAT", 4);
memcpy(&g_dgroup[0x0462], "Display", 8);
win16_write_u16(dgroup_at(0x444e), 0, 0x4444);
for (uint16_t index = 0; index < 768; index++) {
g_dgroup[0x4450 + index] = (uint8_t)(index * 13 + 9);
}
memset(g_resource, 0, sizeof(g_resource));
g_resource[0] = 40;
g_resource[14] = 8;
for (uint16_t index = 40; index < sizeof(g_resource); index++) {
g_resource[index] = (uint8_t)(index * 7 + 3);
}
HBITMAP16 bitmap = tdkpin_load_bitmap_resource(
win16_make_far_pointer(0, 900), 0x4444);
assert(bitmap == 0x6666 && g_step == 12);
assert(g_copy_calls == 2 && g_allocations == 1 && g_stack_header != 0);
return 0;
}
@@ -0,0 +1,140 @@
#include "../tdkpin_game_assets.h"
#include <assert.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_window_bytes[32];
HINSTANCE16 g_win16_instance;
static Win16FarPtr g_window;
static unsigned g_setup_step;
static unsigned g_draw_step;
void object_windows_setup_window(Win16FarPtr window)
{
assert(++g_setup_step == 1 && window == g_window);
}
HCURSOR16 LoadCursor16(HINSTANCE16 instance, Win16FarPtr resource)
{
assert(instance == 0);
if (win16_far_offset(resource) == 0x7f02) {
assert(++g_setup_step == 2);
return 0x1111;
}
assert(++g_draw_step == 10);
assert(resource == win16_make_far_pointer(0, 0x7f00));
return 0x2222;
}
HCURSOR16 SetCursor16(HCURSOR16 cursor)
{
if (cursor == 0x1111) {
assert(++g_setup_step == 3);
} else {
assert(++g_draw_step == 11 && cursor == 0x2222);
}
return 0;
}
HBITMAP16 tdkpin_load_bitmap_resource(
Win16FarPtr resource_name, HINSTANCE16 instance)
{
assert(++g_setup_step == 4);
assert(resource_name == win16_make_far_pointer(0, 1005));
assert(instance == g_win16_instance);
return 0x3333;
}
HWND16 SetFocus16(HWND16 window)
{
assert(++g_setup_step == 5 && window == 0x7777);
return 0;
}
HPALETTE16 SelectPalette16(
HDC16 dc, HPALETTE16 palette, BOOL16 force_background)
{
assert(dc == 0x8888 && force_background == 0);
if (++g_draw_step == 1) {
assert(palette == 0x4444);
return 0x5555;
}
assert(g_draw_step == 9 && palette == 0x5555);
return 0x4444;
}
BOOL16 UnrealizeObject16(HGDIOBJ16 object)
{
assert(++g_draw_step == 2 && object == 0x4444);
return 1;
}
UINT16 RealizePalette16(HDC16 dc)
{
assert(++g_draw_step == 3 && dc == 0x8888);
return 256;
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(++g_draw_step == 4 && dc == 0x8888);
return 0x9999;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
assert(dc == 0x9999);
if (++g_draw_step == 5) {
assert(object == 0x3333);
return 0xaaaa;
}
assert(g_draw_step == 7 && object == 0xaaaa);
return 0x3333;
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
assert(++g_draw_step == 6);
assert(destination == 0x8888 && source == 0x9999);
assert(destination_x == 0 && destination_y == 0);
assert(width == 640 && height == 460);
assert(source_x == 0 && source_y == 0);
assert(raster_operation == 0x00cc0020);
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(++g_draw_step == 8 && dc == 0x9999);
return 1;
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_window_bytes, sizeof(g_window_bytes), true);
win16_set_dgroup_selector(0x5000);
g_window = win16_make_far_pointer(0x6000, 0);
g_win16_instance = 0x6666;
win16_write_u16(g_window, 4, 0x7777);
win16_write_u16(win16_make_far_pointer(0x5000, 0x07cd), 0, 5);
win16_write_u16(win16_make_far_pointer(0x5000, 0x444e), 0, 0x4444);
tdkpin_setup_board_window(g_window);
assert(g_setup_step == 5);
assert(win16_read_u16(win16_make_far_pointer(0x5000, 0x0867)) == 0x3333);
tdkpin_draw_board_overlay(g_window, 0xbeef, 0xcafe, 0x8888);
assert(g_draw_step == 11);
return 0;
}
@@ -0,0 +1,539 @@
#include "../tdkpin_borland_files.h"
#include <assert.h>
#include <stdatomic.h>
_Atomic uint16_t g_borland_io_result;
uint8_t g_borland_file_mode = 2;
static Win16FarPtr g_open_proc;
static Win16FarPtr g_flush_proc;
static Win16FarPtr g_close_proc;
static uint16_t g_open_result;
static uint16_t g_flush_result;
static uint16_t g_close_result;
static Win16FarPtr g_call_order[8];
static unsigned g_call_count;
static Win16DosResult g_dos_read_result;
static Win16DosResult g_dos_write_result;
static Win16DosResult g_dos_close_result;
static uint16_t g_dos_handle;
static Win16FarPtr g_dos_buffer;
static uint16_t g_dos_count;
static unsigned g_dos_calls;
static Win16DosResult g_dos_seek_result;
static uint32_t g_seek_offset;
static uint8_t g_seek_origin;
static Win16DosResult g_dos_open_result;
static Win16DosResult g_dos_create_result;
static Win16DosExtendedResult g_dos_extended_seek_results[4];
static unsigned g_dos_extended_seek_count;
static unsigned g_dos_extended_seek_index;
static Win16DosExtendedResult g_dos_device_info_result;
static Win16FarPtr g_dos_name;
static uint16_t g_dos_open_argument;
static uint32_t g_extended_seek_offsets[4];
static uint8_t g_extended_seek_origins[4];
static Win16FarPtr g_last_write_buffer;
enum DosOperation {
DOS_OPERATION_READ,
DOS_OPERATION_WRITE,
DOS_OPERATION_CLOSE,
DOS_OPERATION_SEEK,
DOS_OPERATION_OPEN,
DOS_OPERATION_CREATE,
DOS_OPERATION_SEEK_EXTENDED,
DOS_OPERATION_DEVICE_INFO,
};
static enum DosOperation g_dos_operations[16];
static void record_dos_operation(enum DosOperation operation)
{
assert(g_dos_calls < sizeof(g_dos_operations) / sizeof(*g_dos_operations));
g_dos_operations[g_dos_calls++] = operation;
}
uint16_t win16_call_file_procedure(
Win16FarPtr procedure, Win16FarPtr record)
{
assert(record == win16_make_far_pointer(0x7000, 2));
g_call_order[g_call_count++] = procedure;
if (procedure == g_open_proc) return g_open_result;
if (procedure == g_flush_proc) return g_flush_result;
assert(procedure == g_close_proc);
return g_close_result;
}
static void write_far(
Win16FarPtr record, uint16_t offset, Win16FarPtr procedure)
{
win16_write_u16(record, offset, win16_far_offset(procedure));
win16_write_u16(record, (uint16_t)(offset + 2),
win16_far_selector(procedure));
}
static void reset_calls(void)
{
g_call_count = 0;
g_open_result = 0;
g_flush_result = 0;
g_close_result = 0;
atomic_store(&g_borland_io_result, 0);
g_dos_calls = 0;
g_dos_extended_seek_count = 0;
g_dos_extended_seek_index = 0;
}
Win16DosResult win16_dos_read(
uint16_t handle, Win16FarPtr buffer, uint16_t count)
{
g_dos_handle = handle;
g_dos_buffer = buffer;
g_dos_count = count;
record_dos_operation(DOS_OPERATION_READ);
return g_dos_read_result;
}
Win16DosResult win16_dos_write(
uint16_t handle, Win16FarPtr buffer, uint16_t count)
{
g_dos_handle = handle;
g_dos_buffer = buffer;
g_last_write_buffer = buffer;
g_dos_count = count;
record_dos_operation(DOS_OPERATION_WRITE);
return g_dos_write_result;
}
Win16DosResult win16_dos_close(uint16_t handle)
{
g_dos_handle = handle;
record_dos_operation(DOS_OPERATION_CLOSE);
return g_dos_close_result;
}
Win16DosResult win16_dos_seek(
uint16_t handle, uint32_t offset, uint8_t origin)
{
g_dos_handle = handle;
g_seek_offset = offset;
g_seek_origin = origin;
record_dos_operation(DOS_OPERATION_SEEK);
return g_dos_seek_result;
}
_Noreturn void win16_integer_divide_fault(void)
{
assert(false);
__builtin_unreachable();
}
Win16DosResult win16_dos_open(Win16FarPtr name, uint8_t access_mode)
{
g_dos_name = name;
g_dos_open_argument = access_mode;
record_dos_operation(DOS_OPERATION_OPEN);
return g_dos_open_result;
}
Win16DosResult win16_dos_create(Win16FarPtr name, uint16_t attributes)
{
g_dos_name = name;
g_dos_open_argument = attributes;
record_dos_operation(DOS_OPERATION_CREATE);
return g_dos_create_result;
}
Win16DosExtendedResult win16_dos_seek_extended(
uint16_t handle, uint32_t offset, uint8_t origin)
{
assert(g_dos_extended_seek_index < g_dos_extended_seek_count);
unsigned index = g_dos_extended_seek_index++;
g_dos_handle = handle;
g_extended_seek_offsets[index] = offset;
g_extended_seek_origins[index] = origin;
record_dos_operation(DOS_OPERATION_SEEK_EXTENDED);
return g_dos_extended_seek_results[index];
}
Win16DosExtendedResult win16_dos_get_device_info(uint16_t handle)
{
g_dos_handle = handle;
record_dos_operation(DOS_OPERATION_DEVICE_INFO);
return g_dos_device_info_result;
}
int main(void)
{
static uint8_t record_bytes[512];
static uint8_t code_bytes[0x700];
win16_reset_segment_bindings();
win16_bind_segment(0x7000, record_bytes, sizeof(record_bytes), true);
win16_bind_ne_segment(
5, 0x6000, code_bytes, sizeof(code_bytes), false);
Win16FarPtr record = win16_make_far_pointer(0x7000, 2);
g_open_proc = win16_make_far_pointer(0x6000, 0x1000);
g_flush_proc = win16_make_far_pointer(0x6000, 0x2000);
g_close_proc = win16_make_far_pointer(0x6000, 0x3000);
write_far(record, 0x10, g_open_proc);
write_far(record, 0x14, g_flush_proc);
write_far(record, 0x1c, g_close_proc);
reset_calls();
win16_write_u16(record, 2, BORLAND_FILE_CLOSED);
win16_write_u16(record, 8, 9);
win16_write_u16(record, 10, 10);
borland_reset_file(record);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_INPUT);
assert(win16_read_u16(win16_far_add_offset(record, 8)) == 0);
assert(win16_read_u16(win16_far_add_offset(record, 10)) == 0);
assert(g_call_count == 1 && g_call_order[0] == g_open_proc);
reset_calls();
g_open_result = 5;
win16_write_u16(record, 2, BORLAND_FILE_CLOSED);
borland_rewrite_file(record);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_CLOSED);
assert(atomic_load(&g_borland_io_result) == 5);
reset_calls();
win16_write_u16(record, 2, BORLAND_FILE_OUTPUT);
g_flush_result = 6;
borland_close_file(record);
assert(g_call_count == 2);
assert(g_call_order[0] == g_flush_proc && g_call_order[1] == g_close_proc);
assert(atomic_load(&g_borland_io_result) == 6);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_CLOSED);
reset_calls();
win16_write_u16(record, 2, BORLAND_FILE_INPUT);
borland_close_file(record);
assert(g_call_count == 1 && g_call_order[0] == g_close_proc);
reset_calls();
win16_write_u16(record, 2, 0x1111);
borland_close_file(record);
assert(g_call_count == 0 && atomic_load(&g_borland_io_result) == 0x67);
atomic_store(&g_borland_io_result, 0);
borland_reset_file(record);
assert(g_call_count == 0 && atomic_load(&g_borland_io_result) == 0x66);
reset_calls();
win16_write_u16(record, 2, BORLAND_FILE_OUTPUT);
borland_reset_file(record);
assert(g_call_count == 3);
assert(g_call_order[0] == g_flush_proc);
assert(g_call_order[1] == g_close_proc);
assert(g_call_order[2] == g_open_proc);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_INPUT);
Win16FarPtr buffer = win16_make_far_pointer(0x7100, 0x0200);
win16_write_u16(record, 0, 9);
win16_write_u16(record, 4, 100);
write_far(record, 12, buffer);
win16_write_u16(record, 8, 7);
g_dos_read_result = (Win16DosResult){60, false};
reset_calls();
assert(borland_file_read_buffer(record) == 0);
assert(g_dos_handle == 9 && g_dos_buffer == buffer && g_dos_count == 100);
assert(win16_read_u16(win16_far_add_offset(record, 8)) == 0);
assert(win16_read_u16(win16_far_add_offset(record, 10)) == 60);
g_dos_read_result = (Win16DosResult){5, true};
assert(borland_file_read_buffer(record) == 5);
assert(win16_read_u16(win16_far_add_offset(record, 10)) == 0);
win16_write_u16(record, 8, 20);
g_dos_write_result = (Win16DosResult){20, false};
reset_calls();
assert(borland_file_flush_buffer(record) == 0);
assert(g_dos_count == 20);
assert(win16_read_u16(win16_far_add_offset(record, 8)) == 0);
win16_write_u16(record, 8, 20);
g_dos_write_result = (Win16DosResult){19, false};
assert(borland_file_flush_buffer(record) == 0x65);
win16_write_u16(record, 8, 20);
g_dos_write_result = (Win16DosResult){6, true};
assert(borland_file_flush_buffer(record) == 6);
win16_write_u16(record, 8, 20);
g_dos_write_result = (Win16DosResult){19, false};
assert(borland_file_write_buffer(record) == 0);
win16_write_u16(record, 8, 20);
g_dos_write_result = (Win16DosResult){7, true};
assert(borland_file_write_buffer(record) == 7);
win16_write_u16(record, 0, 4);
reset_calls();
assert(borland_file_close_handle(record) == 0 && g_dos_calls == 0);
win16_write_u16(record, 0, 9);
g_dos_close_result = (Win16DosResult){0, false};
assert(borland_file_close_handle(record) == 0 && g_dos_calls == 1);
g_dos_close_result = (Win16DosResult){8, true};
assert(borland_file_close_handle(record) == 8);
atomic_store(&g_borland_io_result, 0);
win16_write_u16(record, 2, BORLAND_FILE_CLOSED);
assert(!borland_validate_open_file(record));
assert(atomic_load(&g_borland_io_result) == 0x67);
unsigned dos_calls = g_dos_calls;
borland_truncate_file(record);
assert(g_dos_calls == dos_calls);
atomic_store(&g_borland_io_result, 0);
win16_set_indeterminate_u16(0xbeef);
win16_write_u16(record, 0, 9);
win16_write_u16(record, 2, 0xd7b3);
g_dos_write_result = (Win16DosResult){0, false};
borland_truncate_file(record);
assert(g_dos_count == 0);
assert(g_dos_buffer == win16_make_far_pointer(0xbeef, 0xbeef));
g_dos_write_result = (Win16DosResult){9, true};
borland_truncate_file(record);
assert(atomic_load(&g_borland_io_result) == 9);
atomic_store(&g_borland_io_result, 0);
g_dos_close_result = (Win16DosResult){10, true};
borland_close_generic_file(record);
assert(atomic_load(&g_borland_io_result) == 10);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_CLOSED);
win16_write_u16(record, 0, 4);
win16_write_u16(record, 2, 0xd7b3);
dos_calls = g_dos_calls;
borland_close_generic_file(record);
assert(g_dos_calls == dos_calls);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_CLOSED);
Win16FarPtr transfer = win16_make_far_pointer(0x7100, 0x0200);
static uint8_t transfer_bytes[0x400];
win16_bind_segment(
0x7100, transfer_bytes, sizeof(transfer_bytes), true);
win16_write_u16(record, 0, 9);
win16_write_u16(record, 2, 0xd7b3);
win16_write_u16(record, 4, 4);
atomic_store(&g_borland_io_result, 0);
g_dos_read_result = (Win16DosResult){4, false};
borland_file_read_record(transfer, record);
assert(atomic_load(&g_borland_io_result) == 0);
g_dos_read_result = (Win16DosResult){3, false};
borland_file_read_record(transfer, record);
assert(atomic_load(&g_borland_io_result) == 0x64);
atomic_store(&g_borland_io_result, 0);
g_dos_write_result = (Win16DosResult){3, false};
borland_file_write_record(transfer, record);
assert(atomic_load(&g_borland_io_result) == 0x65);
atomic_store(&g_borland_io_result, 0);
g_dos_write_result = (Win16DosResult){11, true};
borland_file_write_record(transfer, record);
assert(atomic_load(&g_borland_io_result) == 11);
Win16FarPtr result_count = win16_make_far_pointer(0x7100, 0);
atomic_store(&g_borland_io_result, 0);
g_dos_read_result = (Win16DosResult){8, false};
borland_file_block_read(result_count, 3, transfer, record);
assert(win16_read_u16(result_count) == 2);
assert(atomic_load(&g_borland_io_result) == 0);
borland_file_block_read(0, 3, transfer, record);
assert(atomic_load(&g_borland_io_result) == 0x64);
atomic_store(&g_borland_io_result, 0);
g_dos_write_result = (Win16DosResult){8, false};
borland_file_block_write(0, 3, transfer, record);
assert(atomic_load(&g_borland_io_result) == 0x65);
borland_file_block_write(result_count, 3, transfer, record);
assert(win16_read_u16(result_count) == 2);
g_dos_seek_result = (Win16DosResult){0, false};
borland_file_seek_record(0x00010002u, record);
assert(g_seek_offset == 0x00040008u && g_seek_origin == 0);
g_dos_seek_result = (Win16DosResult){12, true};
borland_file_seek_record(3, record);
assert(atomic_load(&g_borland_io_result) == 12);
Win16FarPtr name = win16_far_add_offset(record, 48);
win16_write_u8(name, 'F');
win16_write_u8(win16_far_add_offset(name, 1), 0);
atomic_store(&g_borland_io_result, 0);
win16_write_u16(record, 2, BORLAND_FILE_CLOSED);
g_dos_open_result = (Win16DosResult){15, false};
reset_calls();
borland_open_generic_file(4, record);
assert(g_dos_name == name && g_dos_open_argument == 2);
assert(win16_read_u16(record) == 15);
assert(win16_read_u16(win16_far_add_offset(record, 2)) == 0xd7b3);
assert(win16_read_u16(win16_far_add_offset(record, 4)) == 4);
win16_write_u16(record, 2, BORLAND_FILE_CLOSED);
g_dos_create_result = (Win16DosResult){16, false};
borland_create_generic_file(8, record);
assert(g_dos_open_argument == 0 && win16_read_u16(record) == 16);
win16_write_u8(name, 0);
win16_write_u16(record, 2, BORLAND_FILE_CLOSED);
reset_calls();
borland_open_generic_file(4, record);
assert(g_dos_calls == 0 && win16_read_u16(record) == 0);
win16_write_u16(record, 2, BORLAND_FILE_CLOSED);
borland_create_generic_file(4, record);
assert(g_dos_calls == 0 && win16_read_u16(record) == 1);
win16_write_u8(name, 'F');
win16_write_u16(record, 2, BORLAND_FILE_CLOSED);
atomic_store(&g_borland_io_result, 0);
g_dos_open_result = (Win16DosResult){13, true};
borland_open_generic_file(4, record);
assert(atomic_load(&g_borland_io_result) == 13);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_CLOSED);
atomic_store(&g_borland_io_result, 0);
win16_write_u16(record, 2, 0x1111);
borland_open_generic_file(4, record);
assert(atomic_load(&g_borland_io_result) == 0x66);
win16_write_u16(record, 2, 0xd7b3);
win16_write_u16(record, 0, 9);
g_dos_close_result = (Win16DosResult){0, false};
g_dos_open_result = (Win16DosResult){17, false};
reset_calls();
borland_open_generic_file(4, record);
assert(g_dos_calls == 2 && win16_read_u16(record) == 17);
assert(tdkpin_file_close_noop(record) == 0);
Win16FarPtr text_name = win16_far_add_offset(record, 0x30);
Win16FarPtr read_buffer_proc =
win16_make_far_pointer(0x6000, 0x05d8);
Win16FarPtr flush_buffer_proc =
win16_make_far_pointer(0x6000, 0x0608);
Win16FarPtr write_buffer_proc =
win16_make_far_pointer(0x6000, 0x062d);
Win16FarPtr close_handle_proc =
win16_make_far_pointer(0x6000, 0x064d);
reset_calls();
win16_write_u8(text_name, 'I');
win16_write_u8(win16_far_add_offset(text_name, 1), 0);
win16_write_u16(record, 2, BORLAND_FILE_INPUT);
g_dos_open_result = (Win16DosResult){7, false};
assert(borland_open_text_record(record) == 0);
assert(g_dos_calls == 1 && g_dos_operations[0] == DOS_OPERATION_OPEN);
assert(g_dos_open_argument == 0 && win16_read_u16(record) == 7);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_INPUT);
assert(win16_read_far_pointer(record, 0x14) == read_buffer_proc);
assert(win16_read_far_pointer(record, 0x18) == 0);
assert(win16_read_far_pointer(record, 0x1c) == close_handle_proc);
reset_calls();
win16_write_u8(text_name, 'O');
win16_write_u16(record, 2, BORLAND_FILE_OUTPUT);
g_dos_create_result = (Win16DosResult){8, false};
g_dos_device_info_result =
(Win16DosExtendedResult){0, 0, false};
assert(borland_open_text_record(record) == 0);
assert(g_dos_calls == 2);
assert(g_dos_operations[0] == DOS_OPERATION_CREATE);
assert(g_dos_operations[1] == DOS_OPERATION_DEVICE_INFO);
assert(g_dos_open_argument == 0 && win16_read_u16(record) == 8);
assert(win16_read_far_pointer(record, 0x14) == flush_buffer_proc);
assert(win16_read_far_pointer(record, 0x18) == 0);
assert(win16_read_far_pointer(record, 0x1c) == close_handle_proc);
reset_calls();
win16_write_u8(text_name, 'A');
win16_write_u16(record, 2, BORLAND_TEXT_APPEND);
g_dos_open_result = (Win16DosResult){9, false};
g_dos_device_info_result =
(Win16DosExtendedResult){5, 0x0080, true};
assert(borland_open_text_record(record) == 0);
assert(g_dos_calls == 2);
assert(g_dos_operations[0] == DOS_OPERATION_OPEN);
assert(g_dos_operations[1] == DOS_OPERATION_DEVICE_INFO);
assert(g_dos_open_argument == 2 && win16_read_u16(record) == 9);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_OUTPUT);
assert(win16_read_far_pointer(record, 0x14) == write_buffer_proc);
assert(win16_read_far_pointer(record, 0x18) == write_buffer_proc);
reset_calls();
win16_write_u8(text_name, 'A');
win16_write_u16(record, 2, BORLAND_TEXT_APPEND);
g_dos_open_result = (Win16DosResult){10, false};
g_dos_device_info_result =
(Win16DosExtendedResult){0, 0, false};
g_dos_extended_seek_count = 3;
g_dos_extended_seek_results[0] =
(Win16DosExtendedResult){0x0090, 0x0001, false};
g_dos_extended_seek_results[1] =
(Win16DosExtendedResult){0, 0, false};
g_dos_extended_seek_results[2] =
(Win16DosExtendedResult){0x000c, 0x1234, false};
g_dos_read_result = (Win16DosResult){6, false};
Win16FarPtr text_buffer = win16_far_add_offset(record, 0x80);
for (uint16_t index = 0; index < 6; index++) {
win16_write_u8(win16_far_add_offset(text_buffer, index), 'x');
}
win16_write_u8(win16_far_add_offset(text_buffer, 2), 0x1a);
g_dos_write_result = (Win16DosResult){0, false};
assert(borland_open_text_record(record) == 0);
assert(g_dos_calls == 7 && g_dos_extended_seek_index == 3);
assert(g_dos_operations[0] == DOS_OPERATION_OPEN);
assert(g_dos_operations[1] == DOS_OPERATION_DEVICE_INFO);
assert(g_dos_operations[2] == DOS_OPERATION_SEEK_EXTENDED);
assert(g_dos_operations[3] == DOS_OPERATION_SEEK_EXTENDED);
assert(g_dos_operations[4] == DOS_OPERATION_READ);
assert(g_dos_operations[5] == DOS_OPERATION_SEEK_EXTENDED);
assert(g_dos_operations[6] == DOS_OPERATION_WRITE);
assert(g_extended_seek_offsets[0] == 0 &&
g_extended_seek_origins[0] == 2);
assert(g_extended_seek_offsets[1] == 0x00010010u &&
g_extended_seek_origins[1] == 0);
assert(g_extended_seek_offsets[2] == 0xfffffffcu &&
g_extended_seek_origins[2] == 2);
assert(g_dos_count == 0);
assert(g_last_write_buffer ==
win16_make_far_pointer(0x7000, 0x1234));
assert(win16_read_far_pointer(record, 0x14) == flush_buffer_proc);
assert(win16_read_far_pointer(record, 0x18) == 0);
reset_calls();
win16_write_u8(text_name, 0);
win16_write_u16(record, 2, BORLAND_FILE_INPUT);
assert(borland_open_text_record(record) == 0);
assert(g_dos_calls == 0 && win16_read_u16(record) == 0);
win16_write_u16(record, 2, BORLAND_FILE_OUTPUT);
g_dos_device_info_result =
(Win16DosExtendedResult){0, 0x0080, false};
assert(borland_open_text_record(record) == 0);
assert(g_dos_calls == 1 && win16_read_u16(record) == 1);
reset_calls();
win16_write_u8(text_name, 'E');
win16_write_u16(record, 2, BORLAND_FILE_INPUT);
write_far(record, 0x14, win16_make_far_pointer(0x1111, 0x2222));
g_dos_open_result = (Win16DosResult){13, true};
assert(borland_open_text_record(record) == 13);
assert(g_dos_calls == 1 && win16_read_u16(record) == 0);
assert(win16_read_far_pointer(record, 0x14) ==
win16_make_far_pointer(0x1111, 0x2222));
reset_calls();
win16_write_u16(record, 0, 11);
g_dos_extended_seek_count = 2;
g_dos_extended_seek_results[0] =
(Win16DosExtendedResult){4, 0, true};
g_dos_extended_seek_results[1] =
(Win16DosExtendedResult){0, 0, true};
g_dos_read_result = (Win16DosResult){5, true};
borland_text_truncate_at_ctrl_z(record);
assert(g_dos_calls == 3 && g_dos_extended_seek_index == 2);
assert(g_extended_seek_offsets[1] == 0);
assert(g_dos_operations[2] == DOS_OPERATION_READ);
return 0;
}
@@ -0,0 +1,147 @@
#include "../tdkpin_borland_objects.h"
#include <assert.h>
#include <setjmp.h>
#include <string.h>
static BorlandAllocationAttempt g_allocation;
static Win16FarPtr g_freed_object;
static uint16_t g_freed_size;
static bool g_free_failed;
static jmp_buf g_runtime_error_jump;
static uint16_t g_runtime_error_code;
static Win16FarPtr g_called_destructor;
static Win16FarPtr g_destructor_object;
static uint16_t g_dispose_mode;
BorlandAllocationAttempt borland_try_get_mem(uint16_t bytes)
{
assert(bytes == 16);
return g_allocation;
}
bool borland_try_free_mem(Win16FarPtr object, uint16_t bytes)
{
g_freed_object = object;
g_freed_size = bytes;
return g_free_failed;
}
_Noreturn void borland_runtime_error(uint16_t code)
{
g_runtime_error_code = code;
longjmp(g_runtime_error_jump, 1);
}
void win16_call_object_destructor(
Win16FarPtr procedure, Win16FarPtr object, uint16_t dispose_mode)
{
g_called_destructor = procedure;
g_destructor_object = object;
g_dispose_mode = dispose_mode;
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t existing_bytes[64];
static uint8_t allocated_bytes[64];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(
0x7000, existing_bytes, sizeof(existing_bytes), true);
win16_bind_segment(
0x7100, allocated_bytes, sizeof(allocated_bytes), true);
win16_write_u16(win16_make_far_pointer(0x5000, 0x0200), 0, 16);
BorlandObjectCallFrame existing = {
win16_make_far_pointer(0x7000, 4), 0x0200};
borland_enter_object_constructor(&existing, 2);
assert(existing.object == win16_make_far_pointer(0x7000, 4));
assert(existing.vmt_argument == 0);
assert(win16_read_u16(win16_make_far_pointer(0x7000, 6)) == 0x0200);
g_allocation = (BorlandAllocationAttempt){
win16_make_far_pointer(0x7100, 8), false};
BorlandObjectCallFrame allocated = {0, 0x0200};
borland_enter_object_constructor(&allocated, 2);
assert(allocated.object == win16_make_far_pointer(0x7100, 8));
assert(allocated.vmt_argument == 0x0200);
assert(win16_read_u16(win16_make_far_pointer(0x7100, 10)) == 0x0200);
BorlandObjectCallFrame inactive = {
win16_make_far_pointer(0x7000, 20), 0};
borland_enter_object_constructor(&inactive, 0);
assert(inactive.object == win16_make_far_pointer(0x7000, 20));
g_allocation = (BorlandAllocationAttempt){0, false};
BorlandObjectCallFrame suppressed = {0, 0x0200};
borland_enter_object_constructor(&suppressed, 0);
assert(suppressed.object == 0 && suppressed.vmt_argument == 0x0200);
g_allocation = (BorlandAllocationAttempt){0, true};
if (setjmp(g_runtime_error_jump) == 0) {
borland_enter_object_constructor(&suppressed, 0);
assert(false);
}
assert(g_runtime_error_code == 203);
win16_write_u16(win16_make_far_pointer(0x7000, 4), 0, 0x0200);
BorlandObjectCallFrame destroyed = {
win16_make_far_pointer(0x7000, 4), 1};
g_free_failed = false;
borland_finish_object_destructor(&destroyed, 0);
assert(destroyed.object == 0);
assert(g_freed_object == win16_make_far_pointer(0x7000, 4));
assert(g_freed_size == 16);
BorlandObjectCallFrame borrowed = {
win16_make_far_pointer(0x7000, 4), 0};
g_freed_object = 0;
borland_finish_object_destructor(&borrowed, 0);
assert(borrowed.object == win16_make_far_pointer(0x7000, 4));
assert(g_freed_object == 0);
g_free_failed = true;
destroyed.object = win16_make_far_pointer(0x7000, 4);
if (setjmp(g_runtime_error_jump) == 0) {
borland_finish_object_destructor(&destroyed, 0);
assert(false);
}
assert(g_runtime_error_code == 204);
g_allocation = (BorlandAllocationAttempt){
win16_make_far_pointer(0x7100, 8), false};
assert(borland_default_object_constructor(0, 0x0200) ==
win16_make_far_pointer(0x7100, 8));
g_free_failed = false;
g_freed_object = 0;
borland_default_object_destructor(
win16_make_far_pointer(0x7000, 4), 1);
assert(g_freed_object == win16_make_far_pointer(0x7000, 4));
assert(g_freed_size == 16);
Win16FarPtr destructor = win16_make_far_pointer(0x6000, 0x1234);
win16_write_u16(win16_make_far_pointer(0x5000, 0x0208), 0,
win16_far_offset(destructor));
win16_write_u16(win16_make_far_pointer(0x5000, 0x020a), 0,
win16_far_selector(destructor));
borland_dispose_object(win16_make_far_pointer(0x7000, 4));
assert(g_called_destructor == destructor);
assert(g_destructor_object == win16_make_far_pointer(0x7000, 4));
assert(g_dispose_mode == 1);
if (setjmp(g_runtime_error_jump) == 0) {
borland_abstract_method_error();
assert(false);
}
assert(g_runtime_error_code == 211);
if (setjmp(g_runtime_error_jump) == 0) {
object_windows_abstract_method();
assert(false);
}
assert(g_runtime_error_code == 211);
return 0;
}
@@ -0,0 +1,103 @@
#include "../tdkpin_borland_records.h"
#include <assert.h>
#include <setjmp.h>
#include <string.h>
static jmp_buf g_error_jump;
static uint16_t g_error_code;
static unsigned g_ansi_calls;
_Noreturn void borland_runtime_error(uint16_t code)
{
g_error_code = code;
longjmp(g_error_jump, 1);
}
INT16 AnsiToOem16(Win16FarPtr source, Win16FarPtr destination)
{
assert(source == destination);
assert(win16_far_offset(source) == 0x230);
g_ansi_calls++;
return 1;
}
static void expect_vmt_error(Win16FarPtr vmt)
{
g_error_code = 0;
if (setjmp(g_error_jump) == 0) {
borland_validate_vmt_descriptor(vmt);
assert(false);
}
assert(g_error_code == 210);
}
int main(void)
{
static uint8_t data[0x400];
static uint8_t code[0x1000];
win16_reset_segment_bindings();
win16_bind_segment(0x7000, data, sizeof(data), true);
win16_bind_ne_segment(5, 0x6000, code, sizeof(code), false);
Win16FarPtr vmt = win16_make_far_pointer(0x7000, 0);
win16_write_u16(vmt, 0, 18);
win16_write_u16(vmt, 2, (uint16_t)-18);
borland_validate_vmt_descriptor(vmt);
win16_write_u16(vmt, 0, 0);
expect_vmt_error(vmt);
win16_write_u16(vmt, 0, 18);
win16_write_u16(vmt, 2, (uint16_t)-17);
expect_vmt_error(vmt);
Win16FarPtr source = win16_make_far_pointer(0x7000, 0x100);
Win16FarPtr record = win16_make_far_pointer(0x7000, 0x200);
memcpy(&data[0x100], "name.txt", 9);
g_ansi_calls = 0;
borland_assign_text_c(source, record);
assert(win16_read_u16(record) == 0);
assert(win16_read_u16(win16_far_add_offset(record, 2)) == 0xd7b0);
assert(win16_read_u16(win16_far_add_offset(record, 4)) == 128);
assert(win16_read_far_pointer(record, 12) ==
win16_make_far_pointer(0x7000, 0x280));
assert(win16_read_far_pointer(record, 16) ==
win16_make_far_pointer(0x6000, 0x0be9));
assert(memcmp(&data[0x230], "name.txt", 9) == 0);
assert(g_ansi_calls == 1);
memset(&data[0x200], 0xcc, 128);
data[0x100] = 3;
memcpy(&data[0x101], "abc", 3);
borland_assign_text_short(source, record);
assert(memcmp(&data[0x230], "abc", 4) == 0);
for (uint16_t offset = 20; offset < 48; offset++) {
assert(data[0x200 + offset] == 0);
}
memset(&data[0x200], 0xcc, 32);
borland_initialize_file_record(1, 2, 3, record);
assert(win16_read_u16(win16_far_add_offset(record, 4)) == 1);
assert(win16_read_u16(win16_far_add_offset(record, 8)) == 0);
assert(win16_read_u16(win16_far_add_offset(record, 10)) == 0);
assert(win16_read_u16(win16_far_add_offset(record, 12)) == 2);
assert(win16_read_u16(win16_far_add_offset(record, 14)) == 3);
memset(&data[0x200], 0xcc, 128);
data[0x100] = 4;
memcpy(&data[0x101], "file", 4);
borland_assign_file_short(source, record);
assert(win16_read_u16(record) == 0);
assert(win16_read_u16(win16_far_add_offset(record, 2)) == 0xd7b0);
for (uint16_t offset = 4; offset < 48; offset++) {
assert(data[0x200 + offset] == 0);
}
assert(memcmp(&data[0x230], "file", 5) == 0);
memset(&data[0x101], 'x', 100);
data[0x100] = 100;
borland_assign_file_short(source, record);
for (uint16_t index = 0; index < 79; index++) {
assert(data[0x230 + index] == 'x');
}
assert(data[0x230 + 79] == 0);
return 0;
}
@@ -0,0 +1,102 @@
#include "../tdkpin_borland_startup.h"
#include <assert.h>
#include <setjmp.h>
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
uint16_t g_win16_show_command;
Win16FarPtr g_borland_command_line;
uint16_t g_borland_windows_mode;
uint16_t g_borland_huge_pointer_increment;
uint8_t g_borland_cpu_level;
static jmp_buf g_termination_jump;
static uint8_t g_termination_status;
static unsigned g_call_count;
static uint16_t g_init_app_result;
static uint32_t g_win_flags;
static BorlandWin16StartupRegisters g_expected;
void WaitEvent16(uint16_t event)
{
assert(g_call_count++ == 0);
assert(event == 0);
assert(g_win16_previous_instance == g_expected.previous_instance);
assert(g_win16_instance == g_expected.instance);
assert(g_win16_show_command == g_expected.show_command);
assert(g_borland_command_line == g_expected.command_line);
assert(g_borland_windows_mode ==
win16_far_selector(g_expected.command_line));
}
uint16_t InitApp16(HINSTANCE16 instance)
{
assert(g_call_count++ == 1);
assert(instance == g_expected.instance);
return g_init_app_result;
}
uint32_t GetWinFlags16(void)
{
assert(g_call_count++ == 2);
return g_win_flags;
}
_Noreturn void win16_dos_terminate(uint8_t status)
{
g_termination_status = status;
longjmp(g_termination_jump, 1);
}
static void run_success(uint32_t flags, uint8_t expected_cpu_level)
{
g_call_count = 0;
g_init_app_result = 1;
g_win_flags = flags;
g_borland_huge_pointer_increment = 0;
borland_initialize_win16_runtime(&g_expected);
assert(g_call_count == 3);
assert(g_borland_cpu_level == expected_cpu_level);
assert(g_borland_huge_pointer_increment == 8);
}
int main(void)
{
g_expected = (BorlandWin16StartupRegisters){
.loader_ax = 1,
.command_line = win16_make_far_pointer(0x3456, 0x789a),
.show_command = 7,
.previous_instance = 0x1111,
.instance = 0x2222,
};
run_success(0x40, 0); /* WF_CPU086 */
run_success(0x02, 1); /* WF_CPU286 */
run_success(0x04, 2); /* WF_CPU386 */
run_success(0x0104, 2); /* only AL participates */
g_expected.loader_ax = 0;
g_call_count = 0;
g_win16_instance = 0xaaaa;
if (setjmp(g_termination_jump) == 0) {
borland_initialize_win16_runtime(&g_expected);
assert(false);
}
assert(g_termination_status == 0xff);
assert(g_call_count == 0 && g_win16_instance == 0xaaaa);
g_expected.loader_ax = 1;
g_call_count = 0;
g_init_app_result = 0;
g_borland_huge_pointer_increment = 0xaaaa;
if (setjmp(g_termination_jump) == 0) {
borland_initialize_win16_runtime(&g_expected);
assert(false);
}
assert(g_termination_status == 0xff);
assert(g_call_count == 2);
assert(g_win16_instance == g_expected.instance);
assert(g_borland_huge_pointer_increment == 0xaaaa);
return 0;
}
@@ -0,0 +1,97 @@
#include "../tdkpin_object_windows.h"
#include <assert.h>
#include <string.h>
uint16_t g_object_windows_thunk_code_selector;
Win16FarPtr g_object_windows_free_thunk;
Win16FarPtr g_object_windows_dispatch_proc;
static uint8_t g_pool[0x100];
static HGLOBAL16 g_allocation = 0x1234;
static unsigned g_allocations;
static unsigned g_alias_allocations;
static unsigned g_alias_frees;
static unsigned g_presto_calls;
HGLOBAL16 GlobalAlloc16(UINT16 flags, uint32_t bytes)
{
assert(flags == 0 && bytes == sizeof(g_pool));
g_allocations++;
return g_allocation;
}
Win16FarPtr GlobalLock16(HGLOBAL16 allocation)
{
assert(allocation == g_allocation);
return win16_make_far_pointer(0x8000, 0);
}
uint16_t AllocCStoDSAlias16(uint16_t code_selector)
{
assert(code_selector == 0x8000);
g_alias_allocations++;
return 0x8100;
}
uint16_t FreeSelector16(uint16_t selector)
{
assert(selector == 0x8100);
g_alias_frees++;
return 0;
}
uint16_t PrestoChangoSelector16(
uint16_t source_selector, uint16_t destination_selector)
{
assert(source_selector == 0x8000 && destination_selector == 0x8000);
g_presto_calls++;
return 0x8000;
}
int main(void)
{
static uint8_t dgroup[0x1000];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(0x8000, g_pool, sizeof(g_pool), true);
win16_bind_segment(0x8100, g_pool, sizeof(g_pool), true);
const uint8_t template_bytes[5] = {0x5b, 0x2e, 0xc4, 0x1f, 0xea};
memcpy(&dgroup[0x0686], template_bytes, sizeof(template_bytes));
g_object_windows_dispatch_proc =
win16_make_far_pointer(0x6000, 0x1234);
Win16FarPtr first_object = win16_make_far_pointer(0x7000, 0x0020);
Win16FarPtr first = object_windows_allocate_bound_thunk(first_object);
assert(first == win16_make_far_pointer(0x8000, 0x00f9));
assert(g_allocations == 1 && g_presto_calls == 1);
assert(win16_read_u16(win16_make_far_pointer(0x8000, 0)) == 0);
for (uint16_t index = 0; index < sizeof(template_bytes); index++) {
assert(win16_read_u8(
win16_make_far_pointer(0x8000, (uint16_t)(2 + index))) ==
template_bytes[index]);
}
assert(win16_read_far_pointer(
win16_make_far_pointer(0x8000, 7), 0) ==
g_object_windows_dispatch_proc);
assert(win16_read_u8(first) == 0xe8);
assert(win16_read_u16(win16_far_add_offset(first, 1)) == 0xff06);
assert(win16_read_far_pointer(first, 3) == first_object);
assert(g_object_windows_free_thunk ==
win16_make_far_pointer(0x8000, 0x00f2));
Win16FarPtr second_object = win16_make_far_pointer(0x7000, 0x0040);
Win16FarPtr second = object_windows_allocate_bound_thunk(second_object);
assert(second == win16_make_far_pointer(0x8000, 0x00f2));
assert(win16_read_far_pointer(second, 3) == second_object);
assert(g_allocations == 1);
object_windows_release_bound_thunk(first);
assert(g_object_windows_free_thunk == first);
assert(win16_read_far_pointer(first, 3) ==
win16_make_far_pointer(0x8000, 0x00eb));
assert(object_windows_allocate_bound_thunk(first_object) == first);
assert(g_alias_allocations == 4 && g_alias_frees == 4);
return 0;
}
@@ -0,0 +1,182 @@
#include "../tdkpin_object_list.h"
#include <assert.h>
#include <string.h>
static Win16FarPtr g_false_child;
static HWND16 g_iconic_window;
static unsigned g_get_text_calls;
static unsigned g_set_text_calls;
static Win16FarPtr g_prepare_proc;
static uint16_t g_prepare_argument;
static Win16FarPtr g_transfer_children[4];
static Win16FarPtr g_transfer_cursors[4];
static unsigned g_transfer_calls;
bool win16_call_object_bool_method(
Win16FarPtr procedure, Win16FarPtr object)
{
assert(procedure == win16_make_far_pointer(0x6100, 0x0200));
return object != g_false_child;
}
BOOL16 IsIconic16(HWND16 window)
{
return window == g_iconic_window;
}
INT16 GetWindowText16(HWND16 window, char *text, INT16 maximum)
{
assert(window == g_iconic_window && maximum == 81);
strcpy(text, "title");
g_get_text_calls++;
return 5;
}
BOOL16 SetWindowText16(HWND16 window, const char *text)
{
assert(window == g_iconic_window && strcmp(text, "title") == 0);
g_set_text_calls++;
return 1;
}
void borland_dispose_object(Win16FarPtr object)
{
(void)object;
}
bool win16_call_object_predicate(
Win16FarPtr procedure, Win16FarPtr child)
{
(void)procedure;
(void)child;
return false;
}
void win16_call_object_action(
Win16FarPtr procedure, Win16FarPtr child)
{
(void)procedure;
(void)child;
}
void win16_call_object_u16_method(
Win16FarPtr procedure, Win16FarPtr object, uint16_t argument)
{
(void)object;
g_prepare_proc = procedure;
g_prepare_argument = argument;
}
uint16_t win16_call_object_transfer_method(
Win16FarPtr procedure,
Win16FarPtr object,
uint16_t direction,
Win16FarPtr cursor)
{
assert(procedure == win16_make_far_pointer(0x6100, 0x0400));
assert(direction == 2);
g_transfer_children[g_transfer_calls] = object;
g_transfer_cursors[g_transfer_calls] = cursor;
return g_transfer_calls++ == 0 ? 3 : 5;
}
static void configure_child(
Win16FarPtr child, uint16_t group, uint8_t flags, HWND16 window)
{
win16_write_u16(child, 0, 0x0100);
win16_write_u16(child, 4, window);
win16_write_u8(win16_far_add_offset(child, 0x16), flags);
win16_write_u16(child, 0x17, group);
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t container_bytes[64];
static uint8_t a_bytes[64];
static uint8_t b_bytes[64];
static uint8_t c_bytes[64];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(0x7000, container_bytes, sizeof(container_bytes), true);
win16_bind_segment(0x7100, a_bytes, sizeof(a_bytes), true);
win16_bind_segment(0x7200, b_bytes, sizeof(b_bytes), true);
win16_bind_segment(0x7300, c_bytes, sizeof(c_bytes), true);
win16_write_u16(win16_dgroup_pointer(0x0120), 0, 0x0200);
win16_write_u16(win16_dgroup_pointer(0x0122), 0, 0x6100);
Win16FarPtr prepare_proc = win16_make_far_pointer(0x6100, 0x0300);
win16_write_u16(win16_dgroup_pointer(0x0144), 0,
win16_far_offset(prepare_proc));
win16_write_u16(win16_dgroup_pointer(0x0146), 0,
win16_far_selector(prepare_proc));
win16_write_u16(win16_dgroup_pointer(0x0140), 0, 0x0400);
win16_write_u16(win16_dgroup_pointer(0x0142), 0, 0x6100);
Win16FarPtr container = win16_make_far_pointer(0x7000, 2);
Win16FarPtr a = win16_make_far_pointer(0x7100, 2);
Win16FarPtr b = win16_make_far_pointer(0x7200, 2);
Win16FarPtr c = win16_make_far_pointer(0x7300, 2);
configure_child(a, 1, 4, 0x11);
configure_child(b, 2, 4, 0x22);
configure_child(c, 0, 4, 0x33);
object_windows_append_child(container, a);
object_windows_append_child(container, b);
object_windows_append_child(container, c);
assert(object_windows_child_group_matches(1, a));
assert(!object_windows_child_group_matches(-1, a));
assert(!object_windows_child_group_matches(2, a));
g_false_child = a;
assert(object_windows_try_child_activation(0, a));
g_false_child = 0;
g_iconic_window = 0x22;
g_get_text_calls = 0;
g_set_text_calls = 0;
assert(!object_windows_try_child_activation(0, b));
assert(g_get_text_calls == 1 && g_set_text_calls == 1);
assert(!object_windows_unindexed_child_matches(0, b));
g_false_child = a;
assert(object_windows_validate_child_groups(container));
g_false_child = c;
assert(!object_windows_validate_child_groups(container));
g_false_child = 0;
assert(object_windows_validate_child_groups(container));
static uint8_t empty_bytes[64];
win16_bind_segment(0x7400, empty_bytes, sizeof(empty_bytes), true);
assert(object_windows_validate_child_groups(
win16_make_far_pointer(0x7400, 2)));
Win16FarPtr empty = win16_make_far_pointer(0x7400, 2);
win16_write_u16(empty, 0, 0x0100);
g_prepare_proc = 0;
object_windows_prepare_window(empty);
assert(g_prepare_proc == prepare_proc && g_prepare_argument == 2);
g_false_child = c;
win16_write_u16(container, 2, 0);
g_prepare_proc = 0;
object_windows_prepare_window(container);
assert(win16_read_u16(win16_far_add_offset(container, 2)) == 0xfffc);
assert(g_prepare_proc == 0);
win16_write_u8(win16_far_add_offset(a, 0x16), 0x10);
win16_write_u8(win16_far_add_offset(b, 0x16), 0);
win16_write_u8(win16_far_add_offset(c, 0x16), 0x10);
win16_write_u16(container, 0x0e, 0x0100);
win16_write_u16(container, 0x10, 0x8000);
g_transfer_calls = 0;
object_windows_transfer_child_data(container, 2);
assert(g_transfer_calls == 2);
assert(g_transfer_children[0] == a && g_transfer_children[1] == c);
assert(g_transfer_cursors[0] == win16_make_far_pointer(0x8000, 0x0100));
assert(g_transfer_cursors[1] == win16_make_far_pointer(0x8000, 0x0103));
ObjectWindowsTransferContext context = {
win16_make_far_pointer(0x8000, 0xfffe), 2};
g_transfer_calls = 0;
object_windows_transfer_child_callback(&context, a);
assert(context.cursor == win16_make_far_pointer(0x8000, 1));
return 0;
}
@@ -0,0 +1,136 @@
#include "../tdkpin_window_messages.h"
#include <assert.h>
#include <string.h>
Win16FarPtr g_object_windows_application;
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
uint16_t g_win16_show_command;
Win16FarPtr g_win16_message_box;
Win16FarPtr g_object_windows_dispatch_proc;
uint16_t g_object_windows_thunk_code_selector;
Win16FarPtr g_object_windows_free_thunk;
Win16FarPtr g_object_windows_message_hook;
Win16FarPtr g_object_windows_creating_object;
static Win16FarPtr g_class_name_proc;
static Win16FarPtr g_builder_proc;
static Win16FarPtr g_class_name;
static BOOL16 g_class_exists;
static uint16_t g_register_result;
static unsigned g_builder_calls;
static unsigned g_register_calls;
static unsigned g_load_icon_calls;
static unsigned g_load_cursor_calls;
Win16FarPtr win16_call_object_far_method(
Win16FarPtr procedure, Win16FarPtr object)
{
(void)object;
assert(procedure == g_class_name_proc);
return g_class_name;
}
void win16_call_object_wndclass_method(
Win16FarPtr procedure, Win16FarPtr object, WNDCLASS16 *window_class)
{
(void)object;
assert(procedure == g_builder_proc);
memset(window_class, 0, sizeof(*window_class));
window_class->style = 3;
window_class->class_name = g_class_name;
g_builder_calls++;
}
BOOL16 GetClassInfo16(
HINSTANCE16 instance,
Win16FarPtr class_name,
WNDCLASS16 *window_class)
{
assert(instance == g_win16_instance && class_name == g_class_name);
(void)window_class;
return g_class_exists;
}
uint16_t RegisterClass16(const WNDCLASS16 *window_class)
{
assert(window_class->style == 3);
assert(window_class->class_name == g_class_name);
g_register_calls++;
return g_register_result;
}
HICON16 LoadIcon16(HINSTANCE16 instance, Win16FarPtr resource)
{
assert(instance == 0 && resource == win16_make_far_pointer(0, 0x7f00));
g_load_icon_calls++;
return 0x1111;
}
HCURSOR16 LoadCursor16(HINSTANCE16 instance, Win16FarPtr resource)
{
assert(instance == 0 && resource == win16_make_far_pointer(0, 0x7f00));
g_load_cursor_calls++;
return 0x2222;
}
static void write_far(uint16_t offset, Win16FarPtr pointer)
{
win16_write_u16(
win16_dgroup_pointer(offset), 0, win16_far_offset(pointer));
win16_write_u16(
win16_dgroup_pointer((uint16_t)(offset + 2)),
0,
win16_far_selector(pointer));
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t window_bytes[64];
static uint8_t code[0x200];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(0x7000, window_bytes, sizeof(window_bytes), true);
win16_bind_ne_segment(4, 0x6000, code, sizeof(code), false);
Win16FarPtr window = win16_make_far_pointer(0x7000, 2);
uint16_t vmt = 0x0100;
win16_write_u16(window, 0, vmt);
g_class_name_proc = win16_make_far_pointer(0x6000, 0x1000);
g_builder_proc = win16_make_far_pointer(0x6000, 0x2000);
g_class_name = win16_make_far_pointer(0x5000, 0x068c);
g_win16_instance = 0x1234;
write_far(vmt + 0x2c, g_class_name_proc);
write_far(vmt + 0x34, g_builder_proc);
g_class_exists = 1;
g_builder_calls = 0;
g_register_calls = 0;
assert(object_windows_register_class(window));
assert(g_builder_calls == 0 && g_register_calls == 0);
g_class_exists = 0;
g_register_result = 7;
assert(object_windows_register_class(window));
assert(g_builder_calls == 1 && g_register_calls == 1);
g_register_result = 0;
assert(!object_windows_register_class(window));
assert(g_builder_calls == 2 && g_register_calls == 2);
WNDCLASS16 descriptor;
g_load_icon_calls = 0;
g_load_cursor_calls = 0;
object_windows_build_window_class(window, &descriptor);
assert(descriptor.style == 3);
assert(descriptor.window_proc == win16_make_far_pointer(0x6000, 0x018b));
assert(descriptor.class_extra_bytes == 0 && descriptor.window_extra_bytes == 0);
assert(descriptor.instance == g_win16_instance);
assert(descriptor.icon == 0x1111 && descriptor.cursor == 0x2222);
assert(descriptor.background_brush == 6);
assert(descriptor.menu_name == 0 && descriptor.class_name == g_class_name);
assert(g_load_icon_calls == 1 && g_load_cursor_calls == 1);
return 0;
}
@@ -0,0 +1,111 @@
#include "../tdkpin_render.h"
#include <assert.h>
static uint8_t g_dgroup[0x1000];
static unsigned g_restore_calls;
static unsigned g_present_calls;
static unsigned g_create_calls;
static unsigned g_select_calls;
static unsigned g_blit_calls;
static unsigned g_delete_calls;
static INT16 g_expected_source_x;
static INT16 g_expected_source_y;
void tdkpin_restore_background_region_b(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height == 74 && width == 100 && y == 47 && x == 238 && dc == 0x1111);
g_restore_calls++;
}
void tdkpin_present_background_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height == 74 && width == 100 && y == 47 && x == 238 && dc == 0x1111);
g_present_calls++;
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x1111);
return (HDC16)(0x2001 + g_create_calls++);
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
g_select_calls++;
if (g_select_calls == 1) {
assert(dc == 0x2001 && object == 0x3001);
return 0x4001;
}
if (g_select_calls == 2) {
assert(dc == 0x2002 && object == 0x3002);
return 0x4002;
}
if (g_select_calls == 3) {
assert(dc == 0x2001 && object == 0x4001);
} else {
assert(g_select_calls == 4 && dc == 0x2002 && object == 0x4002);
}
return 0;
}
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_blit_calls++;
assert(destination == (g_blit_calls == 1 ? 0x2001 : 0x1111));
assert(x == 238 && y == 47 && width == 100 && height == 74);
assert(source == 0x2002);
assert(source_x == g_expected_source_x && source_y == g_expected_source_y);
assert(raster_operation == 0x00cc0020);
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
g_delete_calls++;
assert(dc == (g_delete_calls == 1 ? 0x2002 : 0x2001));
return 1;
}
static void reset_active(INT16 source_x, INT16 source_y)
{
g_create_calls = 0;
g_select_calls = 0;
g_blit_calls = 0;
g_delete_calls = 0;
g_expected_source_x = source_x;
g_expected_source_y = source_y;
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_write_u16(win16_make_far_pointer(0x5000, 0x085b), 0, 0x3001);
win16_write_u16(win16_make_far_pointer(0x5000, 0x0863), 0, 0x3002);
tdkpin_draw_claw_frame(0, 0, 0x1111);
assert(g_restore_calls == 1 && g_present_calls == 1);
reset_active(0, 148);
tdkpin_draw_claw_frame(0, 1, 0x1111);
assert(g_blit_calls == 2 && g_select_calls == 4 && g_delete_calls == 2);
reset_active(0, 74);
tdkpin_draw_claw_frame(1, 10, 0x1111);
assert(g_blit_calls == 2 && g_select_calls == 4 && g_delete_calls == 2);
reset_active(800, 222);
tdkpin_draw_claw_frame(0, 18, 0x1111);
assert(g_blit_calls == 2);
return 0;
}
@@ -0,0 +1,319 @@
#include "../tdkpin_collision_events.h"
#include <assert.h>
#include <string.h>
uint8_t g_current_player;
static uint8_t g_stack[0x400];
static uint8_t g_object[0x0c00];
static uint8_t g_dgroup[0x5000];
static Win16FarPtr g_receiver;
static uint16_t g_restore_ids[64];
static uint16_t g_refresh_ids[64];
static uint16_t g_sounds[32];
static int32_t g_scores[32];
static unsigned g_restore_calls;
static unsigned g_refresh_calls;
static unsigned g_sound_calls;
static unsigned g_score_calls;
static unsigned g_draw_score_calls;
static unsigned g_composite_calls;
static unsigned g_present_calls;
static unsigned g_restore_background_calls;
static unsigned g_get_dc_calls;
static unsigned g_release_dc_calls;
static unsigned g_random_calls;
static uint16_t g_random_values[4];
static uint16_t g_last_sprite;
static int32_t g_last_drawn_score;
static Win16FarPtr record(uint16_t id)
{
return win16_dgroup_pointer((uint16_t)(0x095b + id * 0x53u));
}
void tdkpin_restore_record_or_item_a(uint16_t caller_bp, uint16_t id)
{
assert(caller_bp == 0x0100 && g_restore_calls < 64);
g_restore_ids[g_restore_calls++] = id;
}
void tdkpin_refresh_collision_record(uint16_t caller_bp, uint16_t id)
{
assert(caller_bp == 0x0100 && g_refresh_calls < 64);
g_refresh_ids[g_refresh_calls++] = id;
}
void tdkpin_play_sound_if_not_tilted(uint16_t sound_number)
{
assert(g_sound_calls < 32);
g_sounds[g_sound_calls++] = sound_number;
}
void tdkpin_add_score_from_caller_frame(uint16_t caller_bp, int32_t delta)
{
assert(caller_bp == 0x0100 && g_score_calls < 32);
g_scores[g_score_calls++] = delta;
}
void tdkpin_draw_score_from_caller_frame(
uint16_t caller_bp, uint16_t low, uint16_t high)
{
assert(caller_bp == 0x0100);
g_last_drawn_score =
(int32_t)((uint32_t)low | ((uint32_t)high << 16));
g_draw_score_calls++;
}
uint16_t borland_random_below(uint16_t upper_bound)
{
assert(upper_bound == 6 && g_random_calls < 4);
return g_random_values[g_random_calls++];
}
int32_t borland_multiply_i32(int32_t left, int32_t right)
{
return (int32_t)((uint32_t)left * (uint32_t)right);
}
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;
}
void tdkpin_composite_player_sprite(uint16_t sprite_index, HDC16 dc)
{
assert(dc == 0x3333);
g_last_sprite = sprite_index;
g_composite_calls++;
}
void tdkpin_restore_background_region_a(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height > 0 && width > 0 && dc == 0x3333);
(void)y;
(void)x;
g_restore_background_calls++;
}
void tdkpin_present_background_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height > 0 && width > 0 && dc == 0x3333);
(void)y;
(void)x;
g_present_calls++;
}
static void reset_counters(void)
{
memset(g_restore_ids, 0, sizeof(g_restore_ids));
memset(g_refresh_ids, 0, sizeof(g_refresh_ids));
memset(g_sounds, 0, sizeof(g_sounds));
memset(g_scores, 0, sizeof(g_scores));
g_restore_calls = 0;
g_refresh_calls = 0;
g_sound_calls = 0;
g_score_calls = 0;
g_draw_score_calls = 0;
g_composite_calls = 0;
g_present_calls = 0;
g_restore_background_calls = 0;
g_get_dc_calls = 0;
g_release_dc_calls = 0;
g_random_calls = 0;
memset(g_random_values, 0, sizeof(g_random_values));
g_last_sprite = 0;
g_last_drawn_score = 0;
}
static void prepare_fixture(void)
{
memset(g_stack, 0, sizeof(g_stack));
memset(g_object, 0, sizeof(g_object));
memset(g_dgroup, 0, sizeof(g_dgroup));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_set_stack_state(0x7000, 0);
g_receiver = win16_make_far_pointer(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
win16_write_u16(g_receiver, 4, 0x2222);
g_current_player = 2;
reset_counters();
}
static void seed_bounds(uint16_t id, INT16 left, INT16 top)
{
win16_write_u16(record(id), 0x4b, (uint16_t)left);
win16_write_u16(record(id), 0x4d, (uint16_t)top);
win16_write_u16(record(id), 0x4f, (uint16_t)(left + 2));
win16_write_u16(record(id), 0x51, (uint16_t)(top + 3));
}
static void test_tilt_and_combined_low_events(void)
{
prepare_fixture();
win16_write_u8(win16_dgroup_pointer(0x07c6), 1);
tdkpin_handle_collision_events(0x0100, 0xffff, 129);
assert(g_restore_calls == 0 && g_sound_calls == 0);
prepare_fixture();
win16_write_u16(record(129), 0x43, 20);
win16_write_u8(win16_far_add_offset(record(20), 0x34), 1);
win16_write_u8(win16_far_add_offset(record(21), 0x34), 1);
win16_write_u8(win16_far_add_offset(record(22), 0x34), 1);
tdkpin_handle_collision_events(
0x0100, 0x0001u | 0x0002u | 0x2000u | 0x0400u, 129);
assert(win16_read_u8(win16_far_add_offset(
g_receiver, (uint16_t)(0x0467 + 2u * 20u + 15u))) == 1);
assert(g_restore_calls == 1 && g_restore_ids[0] == 1015);
assert(g_refresh_calls == 1 && g_refresh_ids[0] == 20);
assert(win16_read_u8(win16_far_add_offset(record(20), 0x34)) == 0);
assert(win16_read_u8(win16_far_add_offset(record(21), 0x34)) == 0);
assert(win16_read_u8(win16_far_add_offset(record(22), 0x34)) == 0);
assert(win16_read_u32(win16_dgroup_pointer(0x0855)) == 0);
assert(win16_read_u8(win16_dgroup_pointer(0x07c7)) == 1);
}
static void test_sprite_award(void)
{
prepare_fixture();
win16_write_u16(win16_dgroup_pointer(0x09a8), 0, 2);
g_random_values[0] = 1;
g_random_values[1] = 2;
tdkpin_handle_collision_events(0x0100, 0x0004, 0);
assert(g_sound_calls == 1 && g_sounds[0] == 12);
assert(win16_read_u8(win16_far_add_offset(record(149), 0x34)) == 1);
assert(g_restore_ids[0] == 149 && g_random_calls == 2);
assert(win16_read_u16(win16_dgroup_pointer(0x09a8)) == 3);
assert(g_composite_calls == 1 && g_last_sprite == 3);
assert(g_present_calls == 1 && g_get_dc_calls == 1);
prepare_fixture();
win16_write_u16(win16_dgroup_pointer(0x399a), 0, 1);
tdkpin_handle_collision_events(0x0100, 0x0004, 0);
assert(win16_read_u16(win16_dgroup_pointer(0x09a8)) == 7);
assert(g_random_calls == 0 && g_last_sprite == 7);
}
static void set_group_heads(uint16_t first, uint16_t groups, uint8_t active)
{
for (uint16_t group = 0; group < groups; group++) {
win16_write_u8(
win16_far_add_offset(record((uint16_t)(first + group * 3u)), 0x34),
active);
}
}
static void test_ready_bank(void)
{
prepare_fixture();
set_group_heads(90, 5, 0);
win16_write_u8(win16_dgroup_pointer(0x0818), 6);
tdkpin_handle_collision_events(0x0100, 0x0008, 0);
assert(g_sound_calls == 2 && g_sounds[0] == 12 && g_sounds[1] == 17);
assert(g_score_calls == 1 && g_scores[0] == 50000);
assert(g_refresh_calls == 5);
prepare_fixture();
set_group_heads(90, 5, 0);
win16_write_u8(win16_dgroup_pointer(0x0818), 2);
tdkpin_handle_collision_events(0x0100, 0x0008, 0);
assert(win16_read_u8(win16_dgroup_pointer(0x0818)) == 3);
assert(g_restore_calls == 1 && g_restore_ids[0] == 1002);
assert(g_score_calls == 0 && g_refresh_calls == 5);
}
static void set_item_bank_count(uint16_t count)
{
for (uint16_t item = 1; item <= count; item++) {
win16_write_u8(win16_far_add_offset(
g_receiver, (uint16_t)(0x046c + 2u * 20u + item)), 1);
}
}
static void test_item_bank_branches(void)
{
prepare_fixture();
set_group_heads(109, 4, 0);
tdkpin_handle_collision_events(0x0100, 0x0010, 0);
assert(g_restore_ids[0] == 1006 && g_scores[0] == 10000);
assert(g_refresh_calls == 4);
prepare_fixture();
set_group_heads(109, 4, 0);
set_item_bank_count(8);
seed_bounds(153, 1, 2);
seed_bounds(6, 3, 4);
seed_bounds(154, 5, 6);
tdkpin_handle_collision_events(0x0100, 0x0010, 0);
assert(g_restore_ids[0] == 1014 && g_scores[0] == 24464);
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x0bdc)) == 1);
assert(g_restore_background_calls == 3 && g_present_calls == 3);
assert(g_get_dc_calls == 1 && g_release_dc_calls == 1);
prepare_fixture();
set_group_heads(109, 4, 0);
set_item_bank_count(9);
win16_write_u8(win16_far_add_offset(g_receiver, 0x0bdc), 1);
win16_write_u32(win16_dgroup_pointer(0x0996), 0, 10);
win16_write_u8(win16_dgroup_pointer(0x09a3), 3);
tdkpin_handle_collision_events(0x0100, 0x0010, 0);
assert(win16_read_u32(win16_dgroup_pointer(0x0996)) == 100010u);
assert(g_draw_score_calls == 1 && g_last_drawn_score == 300030);
assert(g_sounds[1] == 7);
}
static void test_switch_and_timed_records(void)
{
static const uint16_t event_ids[3] = {150, 151, 152};
static const uint16_t related[3] = {153, 6, 154};
for (uint16_t index = 0; index < 3; index++) {
prepare_fixture();
tdkpin_handle_collision_events(0x0100, 0x1000, event_ids[index]);
assert(win16_read_u8(win16_far_add_offset(
g_receiver, (uint16_t)(0x00f7 + index))) == 10);
assert(g_restore_calls == 2);
assert(g_restore_ids[0] == event_ids[index]);
assert(g_restore_ids[1] == related[index]);
assert(win16_read_u8(win16_far_add_offset(
record(related[index]), 0x34)) == 1);
}
prepare_fixture();
win16_write_u8(win16_dgroup_pointer(0x0818), 3);
tdkpin_handle_collision_events(0x0100, 0x0040, 51);
assert(g_sounds[0] == 6 && g_restore_ids[0] == 51);
assert(g_scores[0] == 2000);
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x94)) == 5);
prepare_fixture();
tdkpin_handle_collision_events(0x0100, 0x0040, 140);
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x00ed)) == 20);
assert(g_restore_ids[0] == 140 && g_sound_calls == 0);
}
int main(void)
{
test_tilt_and_combined_low_events();
test_sprite_award();
test_ready_bank();
test_item_bank_branches();
test_switch_and_timed_records();
return 0;
}
@@ -0,0 +1,204 @@
#include "../tdkpin_collision_records.h"
#include <assert.h>
#include <limits.h>
#include <string.h>
int32_t g_collision_path_x;
int32_t g_collision_path_y;
uint8_t g_borland_cpu_level = 2;
_Noreturn void borland_runtime_error(uint16_t code)
{
(void)code;
assert(false);
__builtin_unreachable();
}
_Noreturn void win16_integer_divide_fault(void)
{
assert(false);
__builtin_unreachable();
}
static TdkpinCollisionRecord read_record(
const uint8_t *dgroup, uint16_t index)
{
TdkpinCollisionRecord record;
memcpy(
&record,
dgroup + 0x095b + index * sizeof(record),
sizeof(record));
return record;
}
static BorlandReal48 tagged_real48(uint8_t tag)
{
return (BorlandReal48){{tag, 1, 2, 3, 4, 5}};
}
static TdkpinCollisionInitArgs base_arguments(uint16_t index)
{
return (TdkpinCollisionInitArgs){
.render_bottom = 44,
.render_right = 33,
.render_top = 22,
.render_left = 11,
.layer_mask = 2,
.score_low = 0x5678,
.score_high = 0x1234,
.contact_state = 9,
.flags = 0x0040,
.response_kick = tagged_real48(0x81),
.response_auxiliary = tagged_real48(0x82),
.response_tangent = tagged_real48(0x83),
.response_normal = tagged_real48(0x84),
.subtype = 7,
.type = 4,
.active = 1,
.index = index,
};
}
static void assert_common_fields(
const TdkpinCollisionRecord *record,
const TdkpinCollisionInitArgs *arguments)
{
assert(record->type == (uint8_t)arguments->type);
assert(record->subtype == (uint8_t)arguments->subtype);
assert(record->active == (uint8_t)arguments->active);
assert(memcmp(
&record->response_normal,
&arguments->response_normal,
6) == 0);
assert(memcmp(
&record->response_tangent,
&arguments->response_tangent,
6) == 0);
assert(memcmp(
&record->response_auxiliary,
&arguments->response_auxiliary,
6) == 0);
assert(memcmp(
&record->response_kick,
&arguments->response_kick,
6) == 0);
assert(record->flags == arguments->flags);
assert(record->contact_state == arguments->contact_state);
assert(record->score_low == arguments->score_low);
assert(record->score_high == arguments->score_high);
assert(record->layer_mask == arguments->layer_mask);
assert(record->render_left == arguments->render_left);
assert(record->render_top == arguments->render_top);
assert(record->render_right == arguments->render_right);
assert(record->render_bottom == arguments->render_bottom);
}
int main(void)
{
static uint8_t dgroup[0x4d3a];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
/* OBJECTS.tsv row 1 geometry: absolute line with the historical Y-20. */
TdkpinCollisionInitArgs line = base_arguments(1);
line.type = 2;
line.subtype = 0;
line.point1_x = 7;
line.point1_y = 417;
line.point2_x = 125;
line.point2_y = 457;
line.radius = 0;
line.coordinate_mode = 0;
tdkpin_initialize_collision_record(&line);
TdkpinCollisionRecord record = read_record(dgroup, 1);
assert(record.bounds_min_x_milli == 2000);
assert(record.bounds_min_y_milli == 392000);
assert(record.bounds_max_x_milli == 130000);
assert(record.bounds_max_y_milli == 442000);
assert(record.point1_x_milli == 7000);
assert(record.point1_y_milli == 397000);
assert(record.point2_x_milli == 125000);
assert(record.point2_y_milli == 437000);
assert(record.radius_milli.bytes[0] == 0);
assert(g_collision_path_x == 125 && g_collision_path_y == 437);
assert_common_fields(&record, &line);
/* OBJECTS.tsv row 4 geometry: circle center (141,426), radius 8. */
TdkpinCollisionInitArgs circle = base_arguments(4);
circle.type = 1;
circle.subtype = 1;
circle.point1_x = 141;
circle.point1_y = 446;
circle.point2_x = 0;
circle.point2_y = 20;
circle.radius = 8;
circle.coordinate_mode = 0;
tdkpin_initialize_collision_record(&circle);
record = read_record(dgroup, 4);
assert(record.bounds_min_x_milli == 128000);
assert(record.bounds_min_y_milli == 413000);
assert(record.bounds_max_x_milli == 154000);
assert(record.bounds_max_y_milli == 439000);
assert(record.point1_x_milli == 141000);
assert(record.point1_y_milli == 426000);
assert(record.point2_x_milli == 0 && record.point2_y_milli == 0);
BorlandReal48 expected_radius = borland_i32_to_real48(8000);
assert(memcmp(&record.radius_milli, &expected_radius, 6) == 0);
assert(g_collision_path_x == 141 && g_collision_path_y == 426);
assert_common_fields(&record, &circle);
/* Relative segment: point1 continues from globals, point2 is a delta. */
TdkpinCollisionInitArgs relative = base_arguments(10);
g_collision_path_x = 100;
g_collision_path_y = 200;
relative.coordinate_mode = 1;
relative.point1_x = 5;
relative.point1_y = 7;
relative.point2_x = 10;
relative.point2_y = -20;
relative.radius = 0;
tdkpin_initialize_collision_record(&relative);
record = read_record(dgroup, 10);
assert(record.bounds_min_x_milli == 100000);
assert(record.bounds_max_x_milli == 120000);
assert(record.bounds_min_y_milli == 182000);
assert(record.bounds_max_y_milli == 212000);
assert(record.point1_x_milli == 105000);
assert(record.point1_y_milli == 207000);
assert(record.point2_x_milli == 115000);
assert(record.point2_y_milli == 187000);
assert(g_collision_path_x == 115 && g_collision_path_y == 187);
/* Negative radius reaches all four fallback-adjust branches exactly. */
TdkpinCollisionInitArgs fallback = base_arguments(11);
fallback.point1_x = 10;
fallback.point1_y = 30;
fallback.point2_x = 0;
fallback.point2_y = 0;
fallback.radius = -1;
fallback.fallback_x_adjust = 2;
fallback.fallback_y_adjust = 3;
tdkpin_initialize_collision_record(&fallback);
record = read_record(dgroup, 11);
assert(record.bounds_min_x_milli == 13000);
assert(record.bounds_max_x_milli == 11000);
assert(record.bounds_min_y_milli == 14000);
assert(record.bounds_max_y_milli == 12000);
expected_radius = borland_i32_to_real48(-1000);
assert(memcmp(&record.radius_milli, &expected_radius, 6) == 0);
/* Index 176 is rejected before either table or continuation state changes. */
memset(dgroup + 0x095b, 0xa5, 0x53);
TdkpinCollisionInitArgs rejected = base_arguments(176);
rejected.point1_x = INT32_MAX;
g_collision_path_x = 123;
g_collision_path_y = 456;
tdkpin_initialize_collision_record(&rejected);
for (uint16_t index = 0; index < 0x53; index++) {
assert(dgroup[0x095b + index] == 0xa5);
}
assert(g_collision_path_x == 123 && g_collision_path_y == 456);
return 0;
}
@@ -0,0 +1,61 @@
#include "../tdkpin_command_line.h"
#include <assert.h>
#include <string.h>
static uint8_t g_command_segment[0x10000];
Win16FarPtr g_borland_command_line;
HINSTANCE16 g_win16_instance;
UINT16 GetModuleFileNameFar16(
HINSTANCE16 module, Win16FarPtr buffer, UINT16 size)
{
assert(module == g_win16_instance && size == 0xff);
static const char path[] = "C:\\TDKPIN.EXE";
for (uint16_t index = 0; index < sizeof(path); index++) {
win16_write_u8(win16_far_add_offset(buffer, index), path[index]);
}
return (UINT16)(sizeof(path) - 1);
}
int main(void)
{
static const char command[] = " one two\tthree";
memcpy(&g_command_segment[0x100], command, sizeof(command));
win16_reset_segment_bindings();
win16_bind_segment(
0x5555, g_command_segment, sizeof(g_command_segment), true);
static uint8_t result_segment[0x10000];
win16_bind_segment(
0x6666, result_segment, sizeof(result_segment), true);
g_borland_command_line = win16_make_far_pointer(0x5555, 0x100);
assert(borland_parameter_count() == 3);
BorlandCommandToken first = borland_scan_command_token(1);
assert(first.token == win16_make_far_pointer(0x5555, 0x102));
assert(first.length == 3);
BorlandCommandToken second = borland_scan_command_token(2);
assert(second.token == win16_make_far_pointer(0x5555, 0x106));
assert(second.length == 3);
BorlandCommandToken missing = borland_scan_command_token(4);
assert(missing.length == 0);
Win16FarPtr result = win16_make_far_pointer(0x6666, 0x100);
borland_parameter_string(1, result);
assert(result_segment[0x100] == 3);
assert(memcmp(&result_segment[0x101], "one", 3) == 0);
borland_parameter_string(4, result);
assert(result_segment[0x100] == 0);
g_win16_instance = 0x1234;
borland_parameter_string(0, result);
assert(result_segment[0x100] == 13);
assert(memcmp(&result_segment[0x101], "C:\\TDKPIN.EXE", 13) == 0);
memset(&g_command_segment[0x200], 'x', 260);
g_command_segment[0x304] = 0;
g_borland_command_line = win16_make_far_pointer(0x5555, 0x200);
borland_parameter_string(1, result);
assert(result_segment[0x100] == 4);
assert(memcmp(&result_segment[0x101], &g_command_segment[0x200], 260) == 0);
return 0;
}
+413
View File
@@ -0,0 +1,413 @@
#include "../tdkpin_dialog.h"
#include <assert.h>
static Win16FarPtr g_dialog;
static Win16FarPtr g_child;
static Win16FarPtr g_close_proc;
static Win16FarPtr g_closed_window;
static HWND16 g_end_window;
static INT16 g_end_result;
static unsigned g_for_each_calls;
static Win16FarPtr g_virtual_proc;
static uint16_t g_virtual_argument;
static Win16FarPtr g_disposed_string;
static unsigned g_destruct_sequence;
static HWND16 g_text_window;
static INT16 g_text_control;
static Win16FarPtr g_text_buffer;
static INT16 g_text_maximum;
static unsigned g_prepare_calls;
static Win16FarPtr g_caption;
static INT16 g_set_ids[2];
static Win16FarPtr g_set_texts[2];
static unsigned g_set_text_calls;
static INT16 g_send_id;
static UINT16 g_send_message;
static WPARAM16 g_send_wparam;
static LPARAM16 g_send_lparam;
Win16FarPtr g_win16_create_dialog_param;
Win16FarPtr g_win16_dialog_box_param;
uint8_t g_dialog_alternate_template;
Win16FarPtr g_object_windows_application;
HINSTANCE16 g_win16_instance;
static bool g_constructor_enters = true;
static unsigned g_constructor_entry_calls;
static Win16FarPtr g_construct_parent;
static unsigned g_clear_flag_calls;
static unsigned g_set_flag_calls;
static Win16FarPtr g_duplicate_result;
static Win16FarPtr g_duplicate_source;
static HWND16 g_create_result;
static INT16 g_modal_result;
static unsigned g_create_calls;
static unsigned g_modal_calls;
static Win16FarPtr g_expected_template;
static HWND16 g_expected_parent_window;
static Win16FarPtr g_expected_dialog_proc;
static LPARAM16 g_expected_init_parameter;
void object_windows_for_each_child(
Win16FarPtr container, Win16FarPtr action)
{
assert(container == g_dialog);
assert(action == win16_make_far_pointer(0x6000, 0x2bb1));
g_for_each_calls++;
object_windows_dialog_mark_child(0xffff, g_child);
}
void object_windows_set_flag_4_if_windowed(
uint16_t ignored, Win16FarPtr object)
{
(void)ignored;
if (win16_read_u16(win16_far_add_offset(object, 4)) != 0) {
Win16FarPtr flags = win16_far_add_offset(object, 0x16);
win16_write_u8(flags, (uint8_t)(win16_read_u8(flags) | 4));
}
}
BOOL16 EndDialog16(HWND16 dialog, INT16 result)
{
g_end_window = dialog;
g_end_result = result;
return 1;
}
void object_windows_close_if_allowed(Win16FarPtr window)
{
g_closed_window = window;
}
void win16_call_object_u16_method(
Win16FarPtr procedure, Win16FarPtr object, uint16_t argument)
{
assert(object == g_dialog);
g_virtual_proc = procedure;
g_virtual_argument = argument;
}
void borland_far_strdispose(Win16FarPtr string)
{
assert(++g_destruct_sequence == 1);
g_disposed_string = string;
}
void object_windows_object_destruct(Win16FarPtr object, uint16_t vmt)
{
unsigned expected = g_disposed_string == 0 ? 1u : 2u;
assert(++g_destruct_sequence == expected);
assert(object == g_dialog && vmt == 0);
}
void borland_finish_object_destructor(
BorlandObjectCallFrame *frame, uint16_t vmt_field_offset)
{
unsigned expected = g_disposed_string == 0 ? 2u : 3u;
assert(++g_destruct_sequence == expected);
assert(frame->object == g_dialog && frame->vmt_argument == 0x0200);
assert(vmt_field_offset == 0);
}
UINT16 GetDlgItemTextFar16(
HWND16 dialog,
INT16 control_id,
Win16FarPtr buffer,
INT16 maximum)
{
g_text_window = dialog;
g_text_control = control_id;
g_text_buffer = buffer;
g_text_maximum = maximum;
return 7;
}
void object_windows_prepare_window(Win16FarPtr window)
{
assert(window == g_dialog);
g_prepare_calls++;
}
BOOL16 SetWindowTextFar16(HWND16 window, Win16FarPtr text)
{
assert(window == 0x77);
g_caption = text;
return 0;
}
BOOL16 SetDlgItemText16(
HWND16 dialog, INT16 control_id, Win16FarPtr text)
{
assert(dialog == 0x77 && g_set_text_calls < 2);
g_set_ids[g_set_text_calls] = control_id;
g_set_texts[g_set_text_calls] = text;
g_set_text_calls++;
return 0;
}
LRESULT16 SendDlgItemMessage16(
HWND16 dialog,
INT16 control_id,
UINT16 message,
WPARAM16 wparam,
LPARAM16 lparam)
{
assert(dialog == 0x77);
g_send_id = control_id;
g_send_message = message;
g_send_wparam = wparam;
g_send_lparam = lparam;
return -1;
}
bool borland_enter_object_constructor(
BorlandObjectCallFrame *frame, uint16_t vmt_field_offset)
{
assert(vmt_field_offset == 0);
g_constructor_entry_calls++;
if (g_constructor_enters && frame->vmt_argument != 0) {
win16_write_u16(frame->object, 0, frame->vmt_argument);
}
return g_constructor_enters;
}
Win16FarPtr object_windows_object_construct(
Win16FarPtr object, uint16_t vmt, Win16FarPtr parent)
{
assert(object == g_dialog && vmt == 0);
g_construct_parent = parent;
return object;
}
void object_windows_clear_flag_4(Win16FarPtr object)
{
assert(object == g_dialog);
g_clear_flag_calls++;
}
void object_windows_set_flag_1(Win16FarPtr object)
{
assert(object == g_dialog);
g_set_flag_calls++;
}
Win16FarPtr borland_far_strdup(Win16FarPtr source)
{
g_duplicate_source = source;
return g_duplicate_result;
}
HWND16 win16_call_create_dialog_param(
Win16FarPtr procedure,
HINSTANCE16 instance,
Win16FarPtr template_name,
HWND16 parent,
Win16FarPtr dialog_proc,
LPARAM16 init_parameter)
{
assert(procedure == g_win16_create_dialog_param);
assert(instance == g_win16_instance);
assert(template_name == g_expected_template);
assert(parent == g_expected_parent_window);
assert(dialog_proc == g_expected_dialog_proc);
assert(init_parameter == g_expected_init_parameter);
g_create_calls++;
return g_create_result;
}
INT16 win16_call_dialog_box_param(
Win16FarPtr procedure,
HINSTANCE16 instance,
Win16FarPtr template_name,
HWND16 parent,
Win16FarPtr dialog_proc,
LPARAM16 init_parameter)
{
assert(procedure == g_win16_dialog_box_param);
assert(instance == g_win16_instance);
assert(template_name == g_expected_template);
assert(parent == g_expected_parent_window);
assert(dialog_proc == g_expected_dialog_proc);
assert(init_parameter == g_expected_init_parameter);
g_modal_calls++;
win16_write_u16(g_object_windows_application, 0x0e, 0xdead);
win16_write_u16(g_object_windows_application, 0x10, 0xbeef);
return g_modal_result;
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t code[0x3000];
static uint8_t dialog_bytes[80];
static uint8_t child_bytes[80];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_ne_segment(2, 0x6000, code, sizeof(code), false);
win16_bind_segment(0x7000, dialog_bytes, sizeof(dialog_bytes), true);
win16_bind_segment(0x7100, child_bytes, sizeof(child_bytes), true);
g_dialog = win16_make_far_pointer(0x7000, 2);
g_child = win16_make_far_pointer(0x7100, 2);
win16_write_u16(g_dialog, 0, 0x0100);
win16_write_u16(g_dialog, 4, 0x77);
win16_write_u16(g_child, 4, 0x88);
g_close_proc = win16_make_far_pointer(0x6200, 0x1234);
win16_write_u16(win16_dgroup_pointer(0x0150), 0,
win16_far_offset(g_close_proc));
win16_write_u16(win16_dgroup_pointer(0x0152), 0,
win16_far_selector(g_close_proc));
object_windows_end_modal_dialog(g_dialog, 5);
assert(g_for_each_calls == 0 && g_end_window == 0);
win16_write_u8(win16_far_add_offset(g_dialog, 0x25), 1);
object_windows_end_modal_dialog(g_dialog, 5);
assert(g_for_each_calls == 1);
assert(g_end_window == 0x77 && g_end_result == 5);
assert((win16_read_u8(win16_far_add_offset(g_child, 0x16)) & 4) != 0);
ObjectWindowsMessage message = {0};
win16_write_u8(win16_far_add_offset(g_dialog, 0x25), 0);
object_windows_dialog_close(g_dialog, &message);
assert(g_closed_window == g_dialog);
win16_write_u8(win16_far_add_offset(g_dialog, 0x25), 1);
object_windows_dialog_close(g_dialog, &message);
assert(g_virtual_proc == g_close_proc && g_virtual_argument == 2);
Win16FarPtr string = win16_make_far_pointer(0x7300, 0x1111);
win16_write_u16(g_dialog, 0x1d, win16_far_offset(string));
win16_write_u16(g_dialog, 0x1f, win16_far_selector(string));
g_destruct_sequence = 0;
g_disposed_string = 0;
object_windows_dialog_destruct(g_dialog, 0x0200);
assert(g_destruct_sequence == 3 && g_disposed_string == string);
win16_write_u16(g_dialog, 0x1f, 0);
g_destruct_sequence = 0;
g_disposed_string = 0;
object_windows_dialog_destruct(g_dialog, 0x0200);
assert(g_destruct_sequence == 2 && g_disposed_string == 0);
Win16FarPtr text_buffer = win16_make_far_pointer(0x7400, 0x2222);
win16_write_u16(g_dialog, 0x2e, win16_far_offset(text_buffer));
win16_write_u16(g_dialog, 0x30, win16_far_selector(text_buffer));
win16_write_u16(g_dialog, 0x32, 0x1234);
assert(object_windows_dialog_read_text(g_dialog));
assert(g_text_window == 0x77 && g_text_control == 0x1234);
assert(g_text_buffer == text_buffer && g_text_maximum == 0x65);
Win16FarPtr caption = win16_make_far_pointer(0x7500, 0x0100);
Win16FarPtr label = win16_make_far_pointer(0x7500, 0x0200);
win16_write_u16(g_dialog, 0x26, win16_far_offset(caption));
win16_write_u16(g_dialog, 0x28, win16_far_selector(caption));
win16_write_u16(g_dialog, 0x2a, win16_far_offset(label));
win16_write_u16(g_dialog, 0x2c, win16_far_selector(label));
win16_write_u16(g_dialog, 0x32, 0);
g_prepare_calls = 0;
g_set_text_calls = 0;
object_windows_dialog_setup_text_fields(g_dialog);
assert(g_prepare_calls == 1 && g_caption == caption);
assert(g_set_text_calls == 2);
assert(g_set_ids[0] == 100 && g_set_texts[0] == label);
assert(g_set_ids[1] == 101 && g_set_texts[1] == text_buffer);
assert(g_send_id == 101 && g_send_message == 0x0415);
assert(g_send_wparam == 0xffff && g_send_lparam == 0);
Win16FarPtr parent = g_child;
Win16FarPtr template_name = win16_make_far_pointer(0x7500, 0x0300);
g_duplicate_result = win16_make_far_pointer(0x7600, 0x0400);
g_constructor_entry_calls = 0;
g_clear_flag_calls = 0;
g_duplicate_source = 0;
assert(object_windows_dialog_construct(
g_dialog, 0x0120, template_name, parent) == g_dialog);
assert(g_constructor_entry_calls == 1 && g_construct_parent == parent);
assert(g_duplicate_source == template_name);
assert(win16_read_far_pointer(g_dialog, 0x1d) == g_duplicate_result);
assert(win16_read_far_pointer(g_dialog, 0x21) == 0);
assert(win16_read_u8(win16_far_add_offset(g_dialog, 0x25)) == 0);
assert(g_clear_flag_calls == 1);
Win16FarPtr integer_template = win16_make_far_pointer(0, 0x7f02);
g_duplicate_source = 0;
(void)object_windows_dialog_construct(
g_dialog, 0, integer_template, 0);
assert(g_duplicate_source == 0);
assert(win16_read_far_pointer(g_dialog, 0x1d) == integer_template);
g_constructor_enters = false;
assert(object_windows_dialog_construct(
g_dialog, 0x0120, template_name, parent) == g_dialog);
g_constructor_enters = true;
g_dialog_alternate_template = 1;
g_constructor_entry_calls = 0;
Win16FarPtr input_text = win16_make_far_pointer(0x7500, 0x0500);
(void)object_windows_text_dialog_construct(
g_dialog, 0x0130, 0x1234, input_text, label, caption, parent);
assert(g_constructor_entry_calls == 2);
assert(win16_read_far_pointer(g_dialog, 0x1d) ==
win16_make_far_pointer(0, 0x7f05));
assert(win16_read_far_pointer(g_dialog, 0x26) == caption);
assert(win16_read_far_pointer(g_dialog, 0x2a) == label);
assert(win16_read_far_pointer(g_dialog, 0x2e) == input_text);
assert(win16_read_u16(win16_far_add_offset(g_dialog, 0x32)) == 0x1234);
g_win16_instance = 0x4444;
g_win16_create_dialog_param =
win16_make_far_pointer(0x6000, 0x1000);
g_win16_dialog_box_param =
win16_make_far_pointer(0x6000, 0x2000);
g_expected_template = win16_make_far_pointer(0, 0x7f05);
g_expected_dialog_proc = win16_make_far_pointer(0x6000, 0x3000);
g_expected_init_parameter = (LPARAM16)win16_make_far_pointer(
0x7000, 0x0040);
win16_write_u16(g_dialog, 0x12, win16_far_offset(g_expected_dialog_proc));
win16_write_u16(g_dialog, 0x14, win16_far_selector(g_expected_dialog_proc));
win16_write_u16(g_dialog, 0x21, (uint16_t)g_expected_init_parameter);
win16_write_u16(
g_dialog, 0x23, (uint16_t)((uint32_t)g_expected_init_parameter >> 16));
win16_write_u16(g_dialog, 6, win16_far_offset(parent));
win16_write_u16(g_dialog, 8, win16_far_selector(parent));
g_expected_parent_window = 0x88;
win16_write_u16(g_dialog, 2, 0);
g_create_result = 0x12fe;
g_create_calls = 0;
assert(object_windows_create_modeless_dialog(g_dialog) == 0x1201);
assert(g_create_calls == 1);
assert(win16_read_u16(win16_far_add_offset(g_dialog, 4)) == 0x12fe);
assert(win16_read_u8(win16_far_add_offset(g_dialog, 0x25)) == 0);
win16_write_u16(g_dialog, 2, 0);
g_create_result = 0;
assert(object_windows_create_modeless_dialog(g_dialog) == 0);
assert(win16_read_u16(win16_far_add_offset(g_dialog, 2)) == 0xffff);
win16_set_indeterminate_u16(0xabcd);
assert(object_windows_create_modeless_dialog(g_dialog) == 0xab00);
static uint8_t application_bytes[32];
win16_bind_segment(
0x7700, application_bytes, sizeof(application_bytes), true);
g_object_windows_application = win16_make_far_pointer(0x7700, 0);
Win16FarPtr previous_active = win16_make_far_pointer(0x7800, 0x1111);
win16_write_u16(
g_object_windows_application, 0x0e, win16_far_offset(previous_active));
win16_write_u16(
g_object_windows_application, 0x10, win16_far_selector(previous_active));
win16_write_u16(g_dialog, 2, 0);
g_modal_result = 5;
g_modal_calls = 0;
assert(object_windows_execute_modal_dialog(g_dialog) == 5);
assert(g_modal_calls == 1);
assert(win16_read_far_pointer(g_object_windows_application, 0x0e) ==
previous_active);
assert(win16_read_u16(win16_far_add_offset(g_dialog, 4)) == 0);
assert(win16_read_u8(win16_far_add_offset(g_dialog, 0x25)) == 1);
win16_write_u16(g_dialog, 2, 0);
g_modal_result = -1;
assert(object_windows_execute_modal_dialog(g_dialog) == -1);
assert(win16_read_u16(win16_far_add_offset(g_dialog, 2)) == 0xffff);
assert(object_windows_execute_modal_dialog(g_dialog) == -1);
return 0;
}
@@ -0,0 +1,117 @@
#include "../tdkpin_dispatch.h"
#include <assert.h>
#include <setjmp.h>
static jmp_buf g_error_jump;
static uint16_t g_error_code;
static Win16FarPtr g_dispatched_procedure;
static Win16FarPtr g_receiver;
static Win16FarPtr g_argument;
_Noreturn void borland_runtime_error(uint16_t code)
{
g_error_code = code;
longjmp(g_error_jump, 1);
}
void win16_call_dynamic_method(Win16FarPtr procedure)
{
g_dispatched_procedure = procedure;
}
void win16_call_dynamic_method_two_far(
Win16FarPtr procedure, Win16FarPtr receiver, Win16FarPtr argument)
{
g_dispatched_procedure = procedure;
g_receiver = receiver;
g_argument = argument;
}
static void write_far(uint16_t offset, Win16FarPtr pointer)
{
win16_write_u16(
win16_dgroup_pointer(offset), 0, win16_far_offset(pointer));
win16_write_u16(
win16_dgroup_pointer((uint16_t)(offset + 2)),
0,
win16_far_selector(pointer));
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t object[32];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(0x6000, object, sizeof(object), true);
/* VMT 0x0100 -> derived dynamic table 0x0200. */
win16_write_u16(win16_dgroup_pointer(0x0104), 0, 0x0200);
win16_write_u16(win16_dgroup_pointer(0x0200), 0, 0x0300);
win16_write_u16(win16_dgroup_pointer(0x0202), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(0x0204), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(0x0206), 0, 2);
win16_write_u16(win16_dgroup_pointer(0x0208), 0, 10);
win16_write_u16(win16_dgroup_pointer(0x020a), 0, 20);
write_far(0x020c, win16_make_far_pointer(0x7000, 0x1111));
write_far(0x0210, win16_make_far_pointer(0x7000, 0x2222));
win16_write_u16(win16_dgroup_pointer(0x0300), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x0302), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(0x0304), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(0x0306), 0, 1);
win16_write_u16(win16_dgroup_pointer(0x0308), 0, 30);
write_far(0x030a, win16_make_far_pointer(0x7100, 0x3333));
Win16FarPtr slot = borland_resolve_dynamic_method_slot(0x0100, 20);
assert(slot == win16_dgroup_pointer(0x0210));
assert(win16_read_u16(win16_dgroup_pointer(0x0202)) == 20);
assert(win16_read_u16(win16_dgroup_pointer(0x0204)) == 0x0210);
/* The cache bypasses the now-corrupted method-count word. */
win16_write_u16(win16_dgroup_pointer(0x0206), 0, 0xffff);
assert(borland_find_dynamic_method(0x0100, 20) == slot);
win16_write_u16(win16_dgroup_pointer(0x0206), 0, 2);
slot = borland_resolve_dynamic_method_slot(0x0100, 30);
assert(slot == win16_dgroup_pointer(0x030a));
assert(win16_read_u16(win16_dgroup_pointer(0x0302)) == 30);
assert(win16_read_u16(win16_dgroup_pointer(0x0304)) == 0x030a);
g_dispatched_procedure = 0;
borland_dispatch_dynamic_method(0x0100, 10);
assert(g_dispatched_procedure ==
win16_make_far_pointer(0x7000, 0x1111));
/* Generated callers use the receiver VMT and preserve both far args. */
win16_write_u16(win16_dgroup_pointer(0x0404), 0, 0x0500);
win16_write_u16(win16_dgroup_pointer(0x0500), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x0502), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(0x0504), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(0x0506), 0, 2);
win16_write_u16(win16_dgroup_pointer(0x0508), 0, 0x8002);
win16_write_u16(win16_dgroup_pointer(0x050a), 0, 0x0006);
write_far(0x050c, win16_make_far_pointer(0x7200, 0x4444));
write_far(0x0510, win16_make_far_pointer(0x7300, 0x5555));
Win16FarPtr receiver = win16_make_far_pointer(0x6000, 4);
Win16FarPtr argument = win16_make_far_pointer(0x6100, 0x1234);
win16_write_u16(receiver, 0, 0x0400);
borland_dispatch_method_8002(receiver, argument);
assert(g_dispatched_procedure ==
win16_make_far_pointer(0x7200, 0x4444));
assert(g_receiver == receiver && g_argument == argument);
borland_dispatch_method_0006(receiver, argument);
assert(g_dispatched_procedure ==
win16_make_far_pointer(0x7300, 0x5555));
assert(g_receiver == receiver && g_argument == argument);
if (setjmp(g_error_jump) == 0) {
(void)borland_resolve_dynamic_method_slot(0x0100, 99);
assert(false);
}
assert(g_error_code == 210);
return 0;
}
@@ -0,0 +1,280 @@
#include "../tdkpin_flippers.h"
#include "../tdkpin_collision_records.h"
#include <assert.h>
#include <stddef.h>
#include <string.h>
enum {
RECORDS_BASE = 0x095b,
LEFT_OUTER = 66,
LEFT_EDGE = 67,
LEFT_INNER = 68,
RIGHT_INNER = 81,
RIGHT_EDGE = 82,
RIGHT_OUTER = 83,
};
static uint8_t g_dgroup[0x5000];
static uint8_t g_objects[0x1000];
static uint8_t g_stack[0x1000];
static Win16FarPtr g_receiver;
static unsigned g_get_dc_calls;
static unsigned g_release_dc_calls;
static unsigned g_restore_a_calls;
static unsigned g_restore_b_calls;
static unsigned g_present_calls;
static bool g_expect_parent_stack_untouched;
static uint16_t g_expected_parent_bp;
int32_t borland_multiply_i32(int32_t left, int32_t right)
{
return (int32_t)((uint32_t)left * (uint32_t)right);
}
HDC16 GetDC16(HWND16 window)
{
assert(window == 0x2222);
if (g_expect_parent_stack_untouched) {
assert(win16_read_stack_u16(
(uint16_t)(g_expected_parent_bp - 8)) == 0xaaaa);
assert(win16_read_stack_u16(
(uint16_t)(g_expected_parent_bp - 6)) == 0xbbbb);
assert(win16_read_stack_u16(
(uint16_t)(g_expected_parent_bp - 4)) == 0xcccc);
assert(win16_read_stack_u16(
(uint16_t)(g_expected_parent_bp - 2)) == 0xdddd);
g_expect_parent_stack_untouched = false;
}
g_get_dc_calls++;
return 0x3333;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == 0x2222 && dc == 0x3333);
g_release_dc_calls++;
return 1;
}
static void assert_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(dc == 0x3333);
if (height == 52) {
assert(width == 47 && y == 373 && x == 93);
} else {
assert(height == 53 && width == 45 && y == 372 && x == 175);
}
}
void tdkpin_restore_background_region_a(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert_region(height, width, y, x, dc);
g_restore_a_calls++;
}
void tdkpin_restore_background_region_b(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert_region(height, width, y, x, dc);
g_restore_b_calls++;
}
void tdkpin_present_background_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert_region(height, width, y, x, dc);
g_present_calls++;
}
static Win16FarPtr record(uint16_t id)
{
return win16_dgroup_pointer((uint16_t)(
RECORDS_BASE + id * sizeof(TdkpinCollisionRecord)));
}
static void put(uint16_t id, size_t offset, int32_t value)
{
win16_write_u32(record(id), (uint16_t)offset, (uint32_t)value);
}
static int32_t get(uint16_t id, size_t offset)
{
return (int32_t)win16_read_u32(
win16_far_add_offset(record(id), (uint16_t)offset));
}
static void put_points(
uint16_t id,
int32_t point1_x,
int32_t point1_y,
int32_t point2_x,
int32_t point2_y)
{
put(id, offsetof(TdkpinCollisionRecord, point1_x_milli), point1_x);
put(id, offsetof(TdkpinCollisionRecord, point1_y_milli), point1_y);
put(id, offsetof(TdkpinCollisionRecord, point2_x_milli), point2_x);
put(id, offsetof(TdkpinCollisionRecord, point2_y_milli), point2_y);
}
static void assert_points(
uint16_t id,
int32_t point1_x,
int32_t point1_y,
int32_t point2_x,
int32_t point2_y)
{
assert(get(id, offsetof(TdkpinCollisionRecord, point1_x_milli)) ==
point1_x);
assert(get(id, offsetof(TdkpinCollisionRecord, point1_y_milli)) ==
point1_y);
assert(get(id, offsetof(TdkpinCollisionRecord, point2_x_milli)) ==
point2_x);
assert(get(id, offsetof(TdkpinCollisionRecord, point2_y_milli)) ==
point2_y);
}
static void put_bounds(
uint16_t id,
int32_t minimum_x,
int32_t minimum_y,
int32_t maximum_x,
int32_t maximum_y)
{
put(id, offsetof(TdkpinCollisionRecord, bounds_min_x_milli), minimum_x);
put(id, offsetof(TdkpinCollisionRecord, bounds_min_y_milli), minimum_y);
put(id, offsetof(TdkpinCollisionRecord, bounds_max_x_milli), maximum_x);
put(id, offsetof(TdkpinCollisionRecord, bounds_max_y_milli), maximum_y);
}
static void assert_bounds(
uint16_t id,
int32_t minimum_x,
int32_t minimum_y,
int32_t maximum_x,
int32_t maximum_y)
{
assert(get(id, offsetof(TdkpinCollisionRecord, bounds_min_x_milli)) ==
minimum_x);
assert(get(id, offsetof(TdkpinCollisionRecord, bounds_min_y_milli)) ==
minimum_y);
assert(get(id, offsetof(TdkpinCollisionRecord, bounds_max_x_milli)) ==
maximum_x);
assert(get(id, offsetof(TdkpinCollisionRecord, bounds_max_y_milli)) ==
maximum_y);
}
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, 0);
g_receiver = win16_make_far_pointer(0x7000, 0);
win16_write_u16(g_receiver, 4, 0x2222);
g_get_dc_calls = 0;
g_release_dc_calls = 0;
g_restore_a_calls = 0;
g_restore_b_calls = 0;
g_present_calls = 0;
g_expect_parent_stack_untouched = false;
g_expected_parent_bp = 0;
}
static void prepare_static_link(uint16_t parent_bp, uint16_t caller_bp)
{
win16_write_stack_u16((uint16_t)(parent_bp + 6), caller_bp);
win16_write_stack_u16(
(uint16_t)(caller_bp + 6), win16_far_offset(g_receiver));
win16_write_stack_u16(
(uint16_t)(caller_bp + 8), win16_far_selector(g_receiver));
win16_write_stack_u16((uint16_t)(parent_bp - 8), 0xaaaa);
win16_write_stack_u16((uint16_t)(parent_bp - 6), 0xbbbb);
win16_write_stack_u16((uint16_t)(parent_bp - 4), 0xcccc);
win16_write_stack_u16((uint16_t)(parent_bp - 2), 0xdddd);
g_expect_parent_stack_untouched = true;
g_expected_parent_bp = parent_bp;
}
static void test_left_flipper_raise(void)
{
const uint16_t parent_bp = 0x0200;
const uint16_t caller_bp = 0x0300;
prepare_fixture();
prepare_static_link(parent_bp, caller_bp);
put_points(LEFT_OUTER, 100000, 200000, 300000, 400000);
put_bounds(LEFT_EDGE, 10000, 20000, 100000, 200000);
put_points(LEFT_EDGE, 30000, 40000, 0, 0);
put_points(LEFT_INNER, 300000, 100000, 100000, 400000);
win16_write_u16(g_receiver, 0x0bd4, 0);
tdkpin_move_flipper_bitmap_geometry(parent_bp, 1, 1);
assert_points(LEFT_OUTER, 114000, 204000, 310000, 444000);
assert_bounds(LEFT_OUTER, 109000, 199000, 315000, 449000);
assert_points(LEFT_EDGE, 31000, 82000, 0, 0);
assert_bounds(LEFT_EDGE, 12000, 63000, 98000, 157000);
assert_points(LEFT_INNER, 293000, 143000, 81000, 406000);
assert_bounds(LEFT_INNER, 76000, 138000, 298000, 411000);
assert(win16_read_stack_u16((uint16_t)(parent_bp - 4)) == 0);
assert(win16_read_stack_u16((uint16_t)(parent_bp - 2)) == 0);
assert(win16_read_stack_u16((uint16_t)(parent_bp - 8)) == 0xa7f8);
assert(win16_read_stack_u16((uint16_t)(parent_bp - 6)) == 0);
assert(g_restore_b_calls == 1 && g_restore_a_calls == 0);
assert(g_present_calls == 1);
assert(g_get_dc_calls == 1 && g_release_dc_calls == 1);
}
static void test_right_flipper_lower(void)
{
const uint16_t parent_bp = 0x0200;
const uint16_t caller_bp = 0x0300;
prepare_fixture();
prepare_static_link(parent_bp, caller_bp);
put_points(RIGHT_OUTER, 100000, 200000, 300000, 400000);
put_bounds(RIGHT_EDGE, 10000, 200000, 100000, 400000);
put_points(RIGHT_EDGE, 30000, 200000, 0, 0);
put_points(RIGHT_INNER, 300000, 400000, 100000, 200000);
win16_write_u16(g_receiver, 0x0bd6, 1);
tdkpin_move_flipper_bitmap_geometry(parent_bp, -1, 2);
assert_points(RIGHT_OUTER, 111000, 155000, 316000, 397000);
assert_bounds(RIGHT_OUTER, 106000, 150000, 321000, 402000);
assert_points(RIGHT_EDGE, 32000, 157000, 0, 0);
assert_bounds(RIGHT_EDGE, 12000, 157000, 98000, 443000);
assert_points(RIGHT_INNER, 281000, 394000, 94000, 151000);
assert_bounds(RIGHT_INNER, 89000, 146000, 286000, 399000);
assert(win16_read_stack_u16((uint16_t)(parent_bp - 8)) == 0x5808);
assert(win16_read_stack_u16((uint16_t)(parent_bp - 6)) == 0xffff);
assert(g_restore_a_calls == 1 && g_restore_b_calls == 0);
assert(g_present_calls == 1);
assert(g_get_dc_calls == 1 && g_release_dc_calls == 1);
}
static void test_unknown_flipper_only_balances_dc(void)
{
prepare_fixture();
win16_write_u32(record(66), 0, 0x12345678);
tdkpin_move_flipper_bitmap_geometry_for_receiver(g_receiver, 7, 3);
assert(win16_read_u32(record(66)) == 0x12345678);
assert(g_restore_a_calls == 0 && g_restore_b_calls == 0);
assert(g_present_calls == 0);
assert(g_get_dc_calls == 1 && g_release_dc_calls == 1);
}
int main(void)
{
test_left_flipper_raise();
test_right_flipper_lower();
test_unknown_flipper_only_balances_dc();
return 0;
}
@@ -0,0 +1,279 @@
#include "../tdkpin_flippers.h"
#include "../tdkpin_borland_runtime.h"
#include "../tdkpin_collision_records.h"
#include "../tdkpin_real48.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_objects[0x1000];
static uint8_t g_stack[0x1000];
static Win16FarPtr g_receiver;
static uint16_t g_sounds[4];
static unsigned g_sound_count;
static unsigned g_move_calls;
static int32_t g_move_x;
static int32_t g_move_y;
static unsigned g_load_calls;
static unsigned g_save_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_play_sound_if_not_tilted(uint16_t sound_number)
{
assert(g_sound_count < sizeof(g_sounds) / sizeof(g_sounds[0]));
g_sounds[g_sound_count++] = sound_number;
}
void tdkpin_move_ball_and_render(
uint16_t caller_bp, int32_t delta_x, int32_t delta_y)
{
assert(caller_bp == 0x0300);
g_move_calls++;
g_move_x = delta_x;
g_move_y = delta_y;
}
void tdkpin_load_ball_slot(uint16_t caller_bp, uint16_t slot)
{
assert(caller_bp == 0x0300);
assert(slot == g_load_calls);
g_load_calls++;
}
void tdkpin_save_ball_slot(uint16_t caller_bp, uint16_t slot)
{
assert(caller_bp == 0x0300);
assert(slot == g_save_calls);
g_save_calls++;
}
static Win16FarPtr record(uint16_t id)
{
return win16_dgroup_pointer((uint16_t)(
0x095b + id * sizeof(TdkpinCollisionRecord)));
}
static void write_i32(uint16_t id, size_t offset, int32_t value)
{
win16_write_u32(record(id), (uint16_t)offset, (uint32_t)value);
}
static void write_real48(
uint16_t id, size_t offset, BorlandReal48 value)
{
Win16FarPtr destination = win16_far_add_offset(
record(id), (uint16_t)offset);
for (uint16_t index = 0; index < sizeof(value.bytes); index++) {
win16_write_u8(
win16_far_add_offset(destination, index), value.bytes[index]);
}
}
static void write_points(
uint16_t id,
int32_t point1_x,
int32_t point1_y,
int32_t point2_x,
int32_t point2_y)
{
write_i32(id, offsetof(TdkpinCollisionRecord, point1_x_milli), point1_x);
write_i32(id, offsetof(TdkpinCollisionRecord, point1_y_milli), point1_y);
write_i32(id, offsetof(TdkpinCollisionRecord, point2_x_milli), point2_x);
write_i32(id, offsetof(TdkpinCollisionRecord, point2_y_milli), point2_y);
}
static void prepare_fixture(void)
{
static const BorlandReal48 one_half =
{{0x80, 0, 0, 0, 0, 0}};
static const BorlandReal48 one_quarter =
{{0x7f, 0, 0, 0, 0, 0}};
static const BorlandReal48 three_quarters =
{{0x80, 0, 0, 0, 0, 0x40}};
static const BorlandReal48 zero =
{{0, 0, 0, 0, 0, 0}};
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, 0);
g_receiver = win16_make_far_pointer(0x7000, 0);
win16_write_stack_u16(0x0306, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0308, win16_far_selector(g_receiver));
win16_write_u8(win16_far_add_offset(g_receiver, 0x61), 1);
win16_write_u32(g_receiver, 0x56, 1000);
write_points(65, 103000, 397000, 0, 0);
write_real48(
65,
offsetof(TdkpinCollisionRecord, radius_milli),
borland_i32_to_real48(15000));
write_points(66, 112000, 386000, 141000, 413000);
write_real48(
66, offsetof(TdkpinCollisionRecord, response_normal), one_half);
write_points(68, 130000, 427000, 97000, 411000);
write_real48(
68, offsetof(TdkpinCollisionRecord, response_normal), one_quarter);
write_points(80, 210000, 397000, 0, 0);
write_real48(
80,
offsetof(TdkpinCollisionRecord, radius_milli),
borland_i32_to_real48(15000));
write_points(81, 216000, 411000, 177000, 427000);
write_real48(
81, offsetof(TdkpinCollisionRecord, response_normal), three_quarters);
write_points(83, 172000, 413000, 201000, 386000);
write_real48(
83, offsetof(TdkpinCollisionRecord, response_normal), zero);
g_sound_count = 0;
g_move_calls = 0;
g_move_x = 0;
g_move_y = 0;
g_load_calls = 0;
g_save_calls = 0;
}
static void set_ball(int32_t x, int32_t y, int32_t vx, int32_t vy)
{
win16_write_u32(win16_dgroup_pointer(0x07d3), 0, (uint32_t)x);
win16_write_u32(win16_dgroup_pointer(0x07d7), 0, (uint32_t)y);
win16_write_u32(g_receiver, 0x0baa, (uint32_t)vx);
win16_write_u32(g_receiver, 0x0bae, (uint32_t)vy);
}
static void test_left_negative_contact(void)
{
prepare_fixture();
set_ball(104000, 384000, 1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, -1, 1, 0);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0baa)) == 1266);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0bae)) == 1511);
assert(g_move_x == -6703 && g_move_y == -8000);
assert(g_sound_count == 1 && g_sounds[0] == 21);
assert(g_move_calls == 1);
}
static void test_right_positive_contact(void)
{
prepare_fixture();
set_ball(209000, 419000, -1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, 1, 2, 0);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0baa)) == -737);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0bae)) == 2263);
assert(g_move_x == -8549 && g_move_y == 26250);
assert(g_sound_count == 1 && g_sounds[0] == 21);
assert(g_move_calls == 1);
}
static void test_left_positive_contact(void)
{
prepare_fixture();
set_ball(104000, 421000, 1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, 1, 1, 0);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0baa)) == 622);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0bae)) == 2320);
assert(g_move_x == 5414 && g_move_y == 20193);
assert(g_move_calls == 1);
}
static void test_right_negative_contact(void)
{
prepare_fixture();
set_ball(209000, 385000, -1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, -1, 2, 0);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0baa)) == -1280);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0bae)) == 1560);
assert(g_move_x == 7385 && g_move_y == -9000);
assert(g_move_calls == 1);
}
static void test_vertical_edge_fallbacks(void)
{
prepare_fixture();
write_i32(
66,
offsetof(TdkpinCollisionRecord, point2_x_milli),
73000);
set_ball(94000, 371000, 1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, -1, 1, 0);
assert(g_move_calls == 1 && g_move_y == -1000);
prepare_fixture();
write_i32(
81,
offsetof(TdkpinCollisionRecord, point1_x_milli),
240000);
set_ball(197000, 455000, -1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, 1, 2, 0);
assert(g_move_calls == 1 && g_move_y == 1000);
}
static void test_multiball_slot_loop_and_invalid_flipper(void)
{
prepare_fixture();
win16_write_u8(win16_far_add_offset(g_receiver, 0x61), 2);
set_ball(300000, 300000, 1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, -1, 1, 0);
assert(g_load_calls == 2 && g_save_calls == 2);
assert(g_move_calls == 0);
prepare_fixture();
win16_write_u8(win16_far_add_offset(g_receiver, 0x61), 0);
tdkpin_move_flipper_collision_geometry(0x0300, 7, 3, 0);
assert(g_sound_count == 1 && g_sounds[0] == 21);
assert(g_move_calls == 0 && g_load_calls == 0 && g_save_calls == 0);
}
static void test_gates(void)
{
prepare_fixture();
set_ball(300000, 300000, 1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, -1, 1, 0);
assert(g_move_calls == 0);
prepare_fixture();
set_ball(104000, 384000, 1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, -1, 1, 1);
assert(g_move_calls == 0);
}
int main(void)
{
test_left_negative_contact();
test_right_positive_contact();
test_left_positive_contact();
test_right_negative_contact();
test_vertical_edge_fallbacks();
test_multiball_slot_loop_and_invalid_flipper();
test_gates();
return 0;
}
@@ -0,0 +1,81 @@
#include "../tdkpin_flippers.h"
#include <assert.h>
#include <string.h>
static uint8_t g_stack[0x400];
static uint8_t g_object[0x0c00];
static Win16FarPtr g_receiver;
static unsigned g_collision_calls;
static unsigned g_bitmap_calls;
static int32_t g_deltas[4];
static uint16_t g_flippers[4];
void tdkpin_move_flipper_collision_geometry(
uint16_t caller_bp, int32_t delta, uint16_t flipper, uint16_t mode)
{
assert(caller_bp == 0x0100 && mode == 0);
g_deltas[g_collision_calls] = delta;
g_flippers[g_collision_calls] = flipper;
g_collision_calls++;
}
void tdkpin_move_flipper_bitmap_geometry_for_receiver(
Win16FarPtr receiver, int32_t delta, uint16_t flipper)
{
assert(receiver == g_receiver);
assert(g_bitmap_calls < g_collision_calls);
assert(delta == g_deltas[g_bitmap_calls]);
assert(flipper == g_flippers[g_bitmap_calls]);
g_bitmap_calls++;
}
static void prepare_fixture(void)
{
memset(g_stack, 0, sizeof(g_stack));
memset(g_object, 0, sizeof(g_object));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
win16_set_stack_state(0x7000, 0);
g_receiver = win16_make_far_pointer(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
g_collision_calls = 0;
g_bitmap_calls = 0;
memset(g_deltas, 0, sizeof(g_deltas));
memset(g_flippers, 0, sizeof(g_flippers));
}
int main(void)
{
prepare_fixture();
win16_write_u8(win16_far_add_offset(g_receiver, 0x0bda), 1);
win16_write_u8(win16_far_add_offset(g_receiver, 0x0bdb), 1);
tdkpin_update_flippers(0x0100);
assert(win16_read_u16(win16_far_add_offset(g_receiver, 0x0bd4)) == 1);
assert(win16_read_u16(win16_far_add_offset(g_receiver, 0x0bd6)) == 1);
assert(g_collision_calls == 2 && g_bitmap_calls == 2);
assert(g_deltas[0] == -1 && g_flippers[0] == 1);
assert(g_deltas[1] == -1 && g_flippers[1] == 2);
g_collision_calls = 0;
g_bitmap_calls = 0;
tdkpin_update_flippers(0x0100);
assert(g_collision_calls == 0 && g_bitmap_calls == 0);
win16_write_u8(win16_far_add_offset(g_receiver, 0x0bda), 0);
win16_write_u8(win16_far_add_offset(g_receiver, 0x0bdb), 0);
tdkpin_update_flippers(0x0100);
assert(win16_read_u16(win16_far_add_offset(g_receiver, 0x0bd4)) == 0);
assert(win16_read_u16(win16_far_add_offset(g_receiver, 0x0bd6)) == 0);
assert(g_collision_calls == 2 && g_bitmap_calls == 2);
assert(g_deltas[0] == 1 && g_flippers[0] == 1);
assert(g_deltas[1] == 1 && g_flippers[1] == 2);
g_collision_calls = 0;
g_bitmap_calls = 0;
tdkpin_update_flippers(0x0100);
assert(g_collision_calls == 0 && g_bitmap_calls == 0);
return 0;
}
@@ -0,0 +1,150 @@
#include "../tdkpin_game_application.h"
#include <assert.h>
#include <setjmp.h>
static unsigned g_sequence;
static Win16FarPtr g_application;
static uint8_t g_multimedia_args[3];
static uint16_t g_module_offset;
static uint16_t g_module_selector;
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
Win16FarPtr g_win16_message_box;
static Win16FarPtr g_constructed_main;
static Win16FarPtr g_constructor_title;
static INT16 g_message_box_result;
static uint16_t g_exit_status;
static jmp_buf g_exit_jump;
static unsigned g_base_builder_calls;
void object_windows_application_initialize(Win16FarPtr application)
{
assert(++g_sequence == 1);
assert(application == g_application);
}
uint8_t multimedia_initialize_export(
uint8_t argument_1,
uint8_t argument_2,
uint8_t argument_3,
uint16_t argument_4,
uint16_t argument_5)
{
assert(++g_sequence == 2);
g_multimedia_args[0] = argument_1;
g_multimedia_args[1] = argument_2;
g_multimedia_args[2] = argument_3;
g_module_offset = argument_4;
g_module_selector = argument_5;
return 0;
}
Win16FarPtr tdkpin_main_window_construct(
Win16FarPtr object,
uint16_t vmt,
Win16FarPtr title,
Win16FarPtr parent)
{
assert(object == 0 && vmt == 0x009e && parent == 0);
g_constructor_title = title;
return g_constructed_main;
}
INT16 win16_call_message_box_far(
Win16FarPtr procedure,
HWND16 owner,
Win16FarPtr text,
Win16FarPtr caption,
UINT16 type)
{
assert(procedure == g_win16_message_box && owner == 0);
assert(text == win16_make_far_pointer(0x5000, 0x0245));
assert(caption == win16_make_far_pointer(0x5000, 0x022c));
assert(type == 0x2010);
return g_message_box_result;
}
_Noreturn void borland_runtime_exit(uint16_t status)
{
g_exit_status = status;
longjmp(g_exit_jump, 1);
}
void object_windows_build_window_class(
Win16FarPtr window, WNDCLASS16 *window_class)
{
assert(window == g_application);
*window_class = (WNDCLASS16){.style = 3, .background_brush = 6};
g_base_builder_calls++;
}
HICON16 LoadIcon16(HINSTANCE16 instance, Win16FarPtr resource)
{
assert(instance == g_win16_instance);
assert(resource == win16_make_far_pointer(0, 0x03e4));
return 0x1111;
}
HCURSOR16 LoadCursor16(HINSTANCE16 instance, Win16FarPtr resource)
{
assert(instance == 0 && resource == win16_make_far_pointer(0, 0x7f00));
return 0x2222;
}
HGDIOBJ16 GetStockObject16(INT16 object)
{
assert(object == 0 || object == 5);
return (HGDIOBJ16)(0x3000 + object);
}
int main(void)
{
static uint8_t dgroup[0x1000];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
g_application = win16_make_far_pointer(0x7000, 2);
g_sequence = 0;
tdkpin_application_init_instance(g_application);
assert(g_sequence == 2);
assert(g_multimedia_args[0] == 1 && g_multimedia_args[1] == 1);
assert(g_multimedia_args[2] == 1);
assert(g_module_offset == 0x0222 && g_module_selector == 0x5000);
static uint8_t application_bytes[32];
win16_bind_segment(
0x7000, application_bytes, sizeof(application_bytes), true);
g_application = win16_make_far_pointer(0x7000, 2);
g_win16_previous_instance = 0;
g_constructed_main = win16_make_far_pointer(0x7100, 0x1234);
tdkpin_application_init_application(g_application);
assert(g_constructor_title == win16_make_far_pointer(0x5000, 0x022c));
assert(win16_read_far_pointer(g_application, 8) == g_constructed_main);
g_win16_previous_instance = 1;
g_win16_message_box = win16_make_far_pointer(0x6000, 0x1111);
g_message_box_result = 1;
g_exit_status = 0xffff;
if (setjmp(g_exit_jump) == 0) {
tdkpin_application_init_application(g_application);
assert(false);
}
assert(g_exit_status == 0);
WNDCLASS16 descriptor;
g_win16_instance = 0x4444;
g_base_builder_calls = 0;
tdkpin_build_main_window_class(g_application, &descriptor);
assert(descriptor.style == 0x3000);
assert(descriptor.icon == 0x1111 && descriptor.cursor == 0x2222);
assert(descriptor.background_brush == 0x3000);
assert(descriptor.instance == 0x4444);
assert(descriptor.class_extra_bytes == 0 && descriptor.window_extra_bytes == 0);
tdkpin_build_stock_brush_class_a(g_application, &descriptor);
assert(descriptor.style == 0x3803 && descriptor.background_brush == 0x3005);
tdkpin_build_stock_brush_class_b(g_application, &descriptor);
assert(descriptor.style == 0x3803 && descriptor.background_brush == 0x3005);
assert(g_base_builder_calls == 3);
return 0;
}
@@ -0,0 +1,75 @@
#include "../tdkpin_game_assets.h"
#include <assert.h>
#include <string.h>
static uint8_t g_stack[0x400];
static unsigned g_step;
static INT16 g_expected_y;
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(++g_step == 1 && dc == 0x1111);
return 0x2222;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
assert(dc == 0x2222);
if (++g_step == 2) {
assert(object == 0x3333);
return 0x4444;
}
assert(g_step == 4 && object == 0x4444);
return 0x3333;
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
assert(++g_step == 3);
assert(destination == 0x1111 && source == 0x2222);
assert(destination_x == 13 && destination_y == g_expected_y);
assert(width == 328 && height == 202);
assert(source_x == 0 && source_y == 0);
assert(raster_operation == 0x00cc0020);
return 1;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(++g_step == 5 && object == 0x2222);
return 1;
}
static void run_stage(uint8_t stage, INT16 expected_y)
{
memset(g_stack, 0, sizeof(g_stack));
Win16FarPtr frame = win16_make_far_pointer(0x6000, 0x100);
win16_write_u16(frame, 0x0e, 0x1111);
win16_write_u16(frame, 0xfffe, 0x3333);
g_step = 0;
g_expected_y = expected_y;
tdkpin_draw_loading_stage(frame, stage);
assert(g_step == 5);
assert(win16_read_u16(win16_far_add_offset(frame, 0xfff4)) == 0x2222);
assert(win16_read_u16(win16_far_add_offset(frame, 0xfffc)) == 0x4444);
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x6000, g_stack, sizeof(g_stack), true);
run_stage(1, 0);
run_stage(35, 238);
run_stage(0, -7);
return 0;
}
@@ -0,0 +1,370 @@
#include "../tdkpin_game_setup.h"
#include <assert.h>
#include <setjmp.h>
#include <string.h>
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;
}
@@ -0,0 +1,181 @@
#include "../tdkpin_game_windows.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
enum {
WINDOW_HANDLE = 4,
WINDOW_ATTRIBUTES = 0x21,
WINDOW_X = 0x29,
WINDOW_Y = 0x2b,
WINDOW_WIDTH = 0x2d,
WINDOW_HEIGHT = 0x2f,
WINDOW_OWNER = 0x41,
};
static uint8_t g_objects[0x1000];
static bool g_enter;
static uint32_t g_base_style;
static INT16 g_screen_width;
static INT16 g_screen_height;
static unsigned g_base_calls;
static Win16FarPtr g_expected_title;
static Win16FarPtr g_expected_parent;
static unsigned g_focus_calls;
static HWND16 g_focused_window;
static Win16FarPtr object_at(uint16_t offset)
{
return win16_make_far_pointer(0x6000, offset);
}
static void reset_fixture(void)
{
memset(g_objects, 0, sizeof(g_objects));
win16_reset_segment_bindings();
win16_bind_segment(0x6000, g_objects, sizeof(g_objects), true);
g_enter = true;
g_base_style = 0;
g_screen_width = 1024;
g_screen_height = 768;
g_base_calls = 0;
g_expected_title = object_at(0x700);
g_expected_parent = object_at(0x800);
g_focus_calls = 0;
g_focused_window = 0;
}
bool borland_enter_object_constructor(
BorlandObjectCallFrame *frame, uint16_t offset)
{
assert(offset == 0);
if (g_enter && frame->vmt_argument != 0) {
win16_write_u16(frame->object, 0, frame->vmt_argument);
}
return g_enter;
}
Win16FarPtr object_windows_window_construct(
Win16FarPtr window,
uint16_t vmt,
Win16FarPtr title,
Win16FarPtr parent)
{
assert(vmt == 0);
assert(title == g_expected_title);
assert(parent == g_expected_parent);
g_base_calls++;
win16_write_u32(window, WINDOW_ATTRIBUTES, g_base_style);
win16_write_u16(window, WINDOW_HANDLE, 0x4444);
return window;
}
INT16 GetSystemMetrics16(INT16 index)
{
assert(index == 0 || index == 1);
return index == 0 ? g_screen_width : g_screen_height;
}
HWND16 SetFocus16(HWND16 window)
{
g_focus_calls++;
g_focused_window = window;
return 0x9999;
}
static void assert_geometry(
Win16FarPtr window,
INT16 x,
INT16 y,
uint16_t width,
uint16_t height)
{
assert((INT16)win16_read_u16(
win16_far_add_offset(window, WINDOW_X)) == x);
assert((INT16)win16_read_u16(
win16_far_add_offset(window, WINDOW_Y)) == y);
assert(win16_read_u16(
win16_far_add_offset(window, WINDOW_WIDTH)) == width);
assert(win16_read_u16(
win16_far_add_offset(window, WINDOW_HEIGHT)) == height);
}
static void test_main_window(void)
{
reset_fixture();
Win16FarPtr window = object_at(0x100);
assert(tdkpin_main_window_construct(
window, 0x1234, g_expected_title, g_expected_parent) == window);
assert(g_base_calls == 1);
assert(win16_read_u16(window) == 0x1234);
assert_geometry(window, 192, 144, 640, 480);
assert(win16_read_u32(
win16_far_add_offset(window, WINDOW_ATTRIBUTES)) ==
0x86ca0000u);
assert(g_focus_calls == 0);
reset_fixture();
window = object_at(0x100);
g_screen_width = 801;
g_screen_height = 601;
(void)tdkpin_main_window_construct(
window, 0, g_expected_title, g_expected_parent);
assert_geometry(window, 80, 60, 640, 480);
}
static void test_centered_child_window(void)
{
reset_fixture();
Win16FarPtr window = object_at(0x200);
g_base_style = 0x00010000u;
assert(tdkpin_centered_child_window_construct(
window, 0x2345, g_expected_title, g_expected_parent) == window);
assert(g_base_calls == 1);
assert(win16_read_far_pointer(window, WINDOW_OWNER) == g_expected_parent);
assert_geometry(window, 140, 80, 360, 300);
assert(win16_read_u32(
win16_far_add_offset(window, WINDOW_ATTRIBUTES)) ==
0x46810002u);
assert(g_focus_calls == 1 && g_focused_window == 0x4444);
}
static void test_full_child_window(void)
{
reset_fixture();
Win16FarPtr window = object_at(0x300);
g_base_style = 0xffffffffu;
assert(tdkpin_full_child_window_construct(
window, 0x3456, g_expected_title, g_expected_parent) == window);
assert(g_base_calls == 1);
assert(win16_read_far_pointer(window, WINDOW_OWNER) == g_expected_parent);
assert_geometry(window, 0, 0, 640, 460);
assert(win16_read_u32(
win16_far_add_offset(window, WINDOW_ATTRIBUTES)) ==
0x46000002u);
assert(g_focus_calls == 1 && g_focused_window == 0x4444);
}
static void test_constructor_entry_failure(void)
{
reset_fixture();
Win16FarPtr window = object_at(0x400);
win16_write_u32(window, WINDOW_ATTRIBUTES, 0x11223344u);
g_enter = false;
assert(tdkpin_centered_child_window_construct(
window, 0x4567, g_expected_title, g_expected_parent) == window);
assert(g_base_calls == 0 && g_focus_calls == 0);
assert(win16_read_u32(
win16_far_add_offset(window, WINDOW_ATTRIBUTES)) ==
0x11223344u);
}
int main(void)
{
test_main_window();
test_centered_child_window();
test_full_child_window();
test_constructor_entry_failure();
return 0;
}
@@ -0,0 +1,358 @@
#include "../tdkpin_gameplay_helpers.h"
#include <assert.h>
uint8_t g_player_count;
uint8_t g_gameplay_transition_pending;
int32_t g_ball_x_milli;
int32_t g_ball_y_milli;
uint8_t g_current_player;
static INT16 g_item_status;
static unsigned g_calls;
static unsigned g_kind[32];
static INT16 g_record_y[32];
static INT16 g_record_x[32];
static HBITMAP16 g_bitmap;
static INT16 g_height;
static INT16 g_width;
static INT16 g_y;
static INT16 g_x;
static HDC16 g_dc;
static HWND16 g_window;
static int32_t g_score;
static unsigned g_create_dc_calls;
static unsigned g_select_object_calls;
static unsigned g_bitblt_calls;
static unsigned g_delete_dc_calls;
static void record(
unsigned kind,
INT16 height,
INT16 width,
INT16 y,
INT16 x,
HDC16 dc)
{
g_kind[g_calls] = kind;
g_record_y[g_calls] = y;
g_record_x[g_calls] = x;
g_calls++;
g_height = height;
g_width = width;
g_y = y;
g_x = x;
g_dc = dc;
}
void tdkpin_blit_bitmap_to_background(
HBITMAP16 bitmap,
INT16 height,
INT16 width,
INT16 y,
INT16 x,
HDC16 dc)
{
g_bitmap = bitmap;
record(1, height, width, y, x, dc);
}
void tdkpin_restore_background_region_a(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
record(2, height, width, y, x, dc);
}
void tdkpin_restore_background_region_b(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
record(3, height, width, y, x, dc);
}
void tdkpin_present_background_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
record(4, height, width, y, x, dc);
}
HDC16 GetDC16(HWND16 window)
{
g_window = window;
return 0x3333;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == g_window && dc == 0x3333);
return 1;
}
void tdkpin_draw_score(HDC16 destination, int32_t value)
{
assert(destination == 0x3333);
g_score = value;
}
void tdkpin_draw_item_status(INT16 item, HDC16 destination)
{
assert(destination == 0x3333);
g_item_status = item;
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x4444 && g_create_dc_calls < 2);
return (HDC16)(0x5001 + g_create_dc_calls++);
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
switch (g_select_object_calls++) {
case 0:
assert(dc == 0x5001 && object == 0x6001);
return 0x7001;
case 1:
assert(dc == 0x5002 && object == 0x6002);
return 0x7002;
case 2:
assert(dc == 0x5002 && object == 0x7002);
return 0x6002;
default:
assert(g_select_object_calls == 4);
assert(dc == 0x5001 && object == 0x7001);
return 0x6001;
}
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
assert(destination == 0x5001 && destination_x == 0);
assert(destination_y == 21 && width == 21 && height == 18);
assert(source == 0x5002 && source_x == 314 && source_y == 426);
assert(raster_operation == 0x00cc0020u);
g_bitblt_calls++;
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(dc == (g_delete_dc_calls == 0 ? 0x5002 : 0x5001));
g_delete_dc_calls++;
return 1;
}
int main(void)
{
static uint8_t stack[0x400];
static uint8_t object[0x0c00];
static uint8_t dgroup[0x4d3a];
win16_reset_segment_bindings();
win16_bind_segment(0x7000, stack, sizeof(stack), true);
win16_bind_segment(0x7100, object, sizeof(object), true);
win16_bind_segment(0x7200, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_set_stack_state(0x7000, 0);
Win16FarPtr receiver = win16_make_far_pointer(0x7100, 0);
win16_write_u16(win16_make_far_pointer(0x7000, 0x0106), 0,
win16_far_offset(receiver));
win16_write_u16(win16_make_far_pointer(0x7000, 0x0108), 0,
win16_far_selector(receiver));
win16_write_u16(receiver, 4, 0x2222);
win16_write_stack_u16(0x00f4, 0x4444);
win16_write_u16(win16_dgroup_pointer(0x085b), 0, 0x6001);
win16_write_u16(win16_dgroup_pointer(0x0865), 0, 0x6002);
g_create_dc_calls = 0;
g_select_object_calls = 0;
g_bitblt_calls = 0;
g_delete_dc_calls = 0;
g_calls = 0;
tdkpin_draw_board_decoration_row(0x0100, 21);
assert(g_create_dc_calls == 2 && g_select_object_calls == 4);
assert(g_bitblt_calls == 1 && g_delete_dc_calls == 2);
assert(win16_read_stack_u16(0x00f2) == 0x5001);
assert(win16_read_stack_u16(0x00f0) == 0x5002);
assert(win16_read_stack_u16(0x00ee) == 0x7001);
assert(win16_read_stack_u16(0x00ec) == 0x7002);
assert(g_calls == 1 && g_kind[0] == 4);
assert(g_height == 18 && g_width == 21 && g_y == 21 && g_x == 0);
assert(g_dc == 0x4444);
g_current_player = 2;
win16_write_u16(win16_dgroup_pointer(0x0823), 0, 3);
g_calls = 0;
tdkpin_refresh_player_progress_markers(0x0100);
assert(g_calls == 12);
for (unsigned marker = 0; marker < 6; marker++) {
unsigned restore = marker * 2;
assert(g_kind[restore] == (marker < 3 ? 2u : 3u));
assert(g_kind[restore + 1] == 4);
assert(g_record_y[restore] == 342);
assert(g_record_x[restore] == (INT16)(391 + marker * 8));
assert(g_record_y[restore + 1] == 342);
assert(g_record_x[restore + 1] == (INT16)(391 + marker * 8));
}
g_calls = 0;
tdkpin_blit_bitmap_and_present(0x1111, 10, 20, 30, 40, 0x5555);
assert(g_calls == 2 && g_kind[0] == 1 && g_kind[1] == 4);
assert(g_bitmap == 0x1111 && g_height == 10 && g_width == 20);
assert(g_y == 30 && g_x == 40 && g_dc == 0x5555);
g_calls = 0;
tdkpin_restore_bounds_overlay_a(0xbeef, 10, 20, 5, 7, 0x5555);
assert(g_calls == 2 && g_kind[0] == 2 && g_kind[1] == 4);
assert(g_height == 6 && g_width == 14 && g_y == 5 && g_x == 7);
g_calls = 0;
tdkpin_restore_bounds_overlay_b(0xbeef, 10, 20, 5, 7, 0x5555);
assert(g_calls == 2 && g_kind[0] == 3 && g_kind[1] == 4);
tdkpin_draw_score_from_caller_frame(0x0100, 0x5678, 0x89ab);
assert(g_window == 0x2222 && g_score == (int32_t)0x89ab5678u);
g_player_count = 9;
g_gameplay_transition_pending = 0;
win16_write_u8(win16_far_add_offset(receiver, 0x0bde), 0);
tdkpin_set_gameplay_transition_flags(0x0100);
assert(g_player_count == 0);
assert(g_gameplay_transition_pending == 1);
assert(win16_read_u8(win16_far_add_offset(receiver, 0x0bde)) == 1);
win16_write_u32(receiver, 0x0bba, 0x00001234u);
win16_write_u32(receiver, 0x0bbe, 0x000056abu);
assert(tdkpin_receiver_point_in_bounds(
0x0100, 0x6000, 0x2000, 0x5000, 0x1000) == 0x5601);
assert(tdkpin_receiver_point_in_bounds(
0x0100, 0x6000, 0x2000, 0x5000, 0x1300) == 0x1200);
g_ball_x_milli = 0x1234;
g_ball_y_milli = 0x56ab;
assert(tdkpin_ball_in_bounds(
0, 0x6000, 0x2000, 0x5000, 0x1000) == 0x5601);
g_ball_x_milli = 100000;
g_ball_y_milli = 200000;
tdkpin_update_dynamic_ball_record(0, 0);
Win16FarPtr dynamic = win16_dgroup_pointer(
(uint16_t)(0x095b + 173u * 0x53u));
assert((int32_t)win16_read_u32(win16_far_add_offset(dynamic, 0x12)) ==
100000);
assert((int32_t)win16_read_u32(win16_far_add_offset(dynamic, 0x16)) ==
200000);
assert((int32_t)win16_read_u32(win16_far_add_offset(dynamic, 0x02)) ==
79000);
assert((int32_t)win16_read_u32(win16_far_add_offset(dynamic, 0x06)) ==
179000);
assert((int32_t)win16_read_u32(win16_far_add_offset(dynamic, 0x0a)) ==
121000);
assert((int32_t)win16_read_u32(win16_far_add_offset(dynamic, 0x0e)) ==
221000);
Win16FarPtr record_one = win16_dgroup_pointer(
(uint16_t)(0x095b + 0x53u));
win16_write_u16(record_one, 0x4b, 7);
win16_write_u16(record_one, 0x4d, 5);
win16_write_u16(record_one, 0x4f, 20);
win16_write_u16(record_one, 0x51, 10);
g_calls = 0;
tdkpin_restore_record_or_item_a(0x0100, 1);
assert(g_calls == 2 && g_kind[0] == 2 && g_kind[1] == 4);
assert(g_height == 6 && g_width == 14 && g_y == 5 && g_x == 7);
g_calls = 0;
tdkpin_restore_record_b(0x0100, 1);
assert(g_kind[0] == 3 && g_kind[1] == 4);
Win16FarPtr quad_two = win16_dgroup_pointer(0x4273);
win16_write_u16(quad_two, 0, 7);
win16_write_u16(quad_two, 2, 5);
win16_write_u16(quad_two, 4, 20);
win16_write_u16(quad_two, 6, 10);
g_calls = 0;
tdkpin_restore_record_or_item_a(0x0100, 1002);
assert(g_kind[0] == 2 && g_height == 6 && g_width == 14);
g_item_status = 0;
tdkpin_restore_record_or_item_a(0x0100, 1006);
assert(g_item_status == 1);
win16_write_u16(record_one, 0x41, 0x0080);
win16_write_u8(win16_far_add_offset(record_one, 0x34), 1);
g_calls = 0;
tdkpin_refresh_collision_record(0x0100, 1);
assert(g_kind[0] == 3);
win16_write_u8(win16_far_add_offset(record_one, 0x34), 0);
g_calls = 0;
tdkpin_refresh_collision_record(0x0100, 1);
assert(g_kind[0] == 2);
win16_write_u16(record_one, 0x41, 0x0100);
g_calls = 0;
tdkpin_refresh_collision_record(0x0100, 1);
assert(g_kind[0] == 3);
g_current_player = 1;
win16_write_u8(win16_far_add_offset(receiver, 0x047d), 1);
g_calls = 0;
tdkpin_refresh_player_item(0x0100, 2);
assert(g_kind[0] == 2);
win16_write_u8(win16_far_add_offset(receiver, 0x047d), 0);
g_calls = 0;
tdkpin_refresh_player_item(0x0100, 2);
assert(g_kind[0] == 3);
win16_write_u32(win16_dgroup_pointer(0x07e7), 0, 0x11112222u);
win16_write_u32(win16_dgroup_pointer(0x07ef), 0, 0x33334444u);
win16_write_u32(win16_dgroup_pointer(0x07f7), 0, 0x55556666u);
win16_write_u32(win16_dgroup_pointer(0x07ff), 0, 0x77778888u);
win16_write_u32(win16_dgroup_pointer(0x0807), 0, 0x9999aaaau);
for (uint16_t index = 0; index < 6; index++) {
win16_write_u8(
win16_dgroup_pointer((uint16_t)(0x0811 + index)),
(uint8_t)(0xb0 + index));
}
tdkpin_load_ball_slot(0x0100, 1);
assert((uint32_t)g_ball_x_milli == 0x11112222u);
assert((uint32_t)g_ball_y_milli == 0x33334444u);
assert(win16_read_u32(win16_far_add_offset(receiver, 0x0baa)) ==
0x55556666u);
assert(win16_read_u32(win16_far_add_offset(receiver, 0x0bae)) ==
0x77778888u);
assert(win16_read_u32(win16_far_add_offset(receiver, 0x0bca)) ==
0x9999aaaau);
for (uint16_t index = 0; index < 6; index++) {
assert(win16_read_u8(win16_far_add_offset(
receiver, (uint16_t)(0x0bce + index))) ==
(uint8_t)(0xb0 + index));
}
g_ball_x_milli = (int32_t)0xabcdef01u;
g_ball_y_milli = (int32_t)0x12345678u;
win16_write_u32(receiver, 0x0baa, 0x01020304u);
win16_write_u32(receiver, 0x0bae, 0x05060708u);
win16_write_u32(receiver, 0x0bca, 0x090a0b0cu);
for (uint16_t index = 0; index < 6; index++) {
win16_write_u8(
win16_far_add_offset(receiver, (uint16_t)(0x0bce + index)),
(uint8_t)(0xc0 + index));
}
tdkpin_save_ball_slot(0x0100, 2);
assert(win16_read_u32(win16_dgroup_pointer(0x07eb)) == 0xabcdef01u);
assert(win16_read_u32(win16_dgroup_pointer(0x07f3)) == 0x12345678u);
assert(win16_read_u32(win16_dgroup_pointer(0x07fb)) == 0x01020304u);
assert(win16_read_u32(win16_dgroup_pointer(0x0803)) == 0x05060708u);
assert(win16_read_u32(win16_dgroup_pointer(0x080b)) == 0x090a0b0cu);
for (uint16_t index = 0; index < 6; index++) {
assert(win16_read_u8(win16_dgroup_pointer(
(uint16_t)(0x0817 + index))) ==
(uint8_t)(0xc0 + index));
}
return 0;
}
@@ -0,0 +1,59 @@
#include "../tdkpin_global_memory.h"
#include <assert.h>
static UINT16 g_alloc_flags;
static uint32_t g_alloc_bytes;
static HGLOBAL16 g_handle;
static Win16FarPtr g_locked_pointer;
static uint16_t g_handle_selector;
static unsigned g_unlock_sequence;
static unsigned g_free_sequence;
HGLOBAL16 GlobalAlloc16(UINT16 flags, uint32_t bytes)
{
g_alloc_flags = flags;
g_alloc_bytes = bytes;
return g_handle;
}
Win16FarPtr GlobalLock16(HGLOBAL16 allocation)
{
assert(allocation == g_handle);
return g_locked_pointer;
}
HGLOBAL16 GlobalHandle16(uint16_t selector)
{
g_handle_selector = selector;
return g_handle;
}
BOOL16 GlobalUnlock16(HGLOBAL16 allocation)
{
assert(allocation == g_handle);
g_unlock_sequence = 1;
return 0;
}
HGLOBAL16 GlobalFree16(HGLOBAL16 allocation)
{
assert(allocation == g_handle && g_unlock_sequence == 1);
g_free_sequence = 2;
return 0;
}
int main(void)
{
g_handle = 0x1234;
g_locked_pointer = win16_make_far_pointer(0xa100, 0x0040);
Win16FarPtr block = tdkpin_global_allocate_locked(0x0042, 0x12345678u);
assert(block == g_locked_pointer);
assert(g_alloc_flags == 0x0042 && g_alloc_bytes == 0x12345678u);
tdkpin_global_unlock_and_free(
win16_make_far_pointer(0xa100, 0xbeef));
assert(g_handle_selector == 0xa100);
assert(g_unlock_sequence == 1 && g_free_sequence == 2);
return 0;
}
@@ -0,0 +1,101 @@
#include "../tdkpin_heap.h"
#include <assert.h>
#include <string.h>
uint16_t g_borland_heap_segment_head;
uint16_t g_borland_local_heap_limit = 0x0400;
uint16_t g_borland_heap_segment_bytes = 0x2000;
Win16FarPtr g_borland_heap_error_handler;
static uint8_t g_dgroup[0x10000];
static uint8_t g_heap[0x2000];
static bool g_heap_bound;
static HGLOBAL16 g_freed_handle;
static uint32_t g_free_space;
static uint32_t g_compact_result;
uint32_t GetFreeSpace16(UINT16 flags)
{
assert(flags == 0x1000);
return g_free_space;
}
uint32_t GlobalCompact16(uint32_t requested_bytes)
{
assert(requested_bytes == 0);
return g_compact_result;
}
Win16FarPtr borland_global_allocate(uint16_t bytes)
{
assert(bytes == 0x2000);
if (!g_heap_bound) {
memset(g_heap, 0, sizeof(g_heap));
win16_bind_ne_segment(0, 0x9000, g_heap, sizeof(g_heap), true);
g_heap_bound = true;
}
return win16_make_far_pointer(0x9000, 0);
}
HGLOBAL16 GlobalHandle16(uint16_t selector)
{
return selector == 0x9000 ? 0x77 : 0;
}
BOOL16 GlobalUnlock16(HGLOBAL16 allocation)
{
assert(allocation == 0x77);
return 1;
}
HGLOBAL16 GlobalFree16(HGLOBAL16 allocation)
{
assert(allocation == 0x77);
g_freed_handle = allocation;
return 0;
}
uint16_t win16_call_pascal_u16(Win16FarPtr procedure, uint16_t argument)
{
(void)procedure;
(void)argument;
return 0;
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_ne_segment(6, 0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x5000);
g_borland_heap_segment_head = 0;
g_borland_heap_error_handler = 0;
g_heap_bound = false;
g_freed_handle = 0;
BorlandAllocationAttempt zero = borland_try_get_mem(0);
assert(!zero.failed && zero.block == 0);
BorlandAllocationAttempt first = borland_try_get_mem(100);
assert(!first.failed && first.block == win16_make_far_pointer(0x9000, 12));
assert(g_borland_heap_segment_head == 0x9000);
assert(win16_read_u16(win16_make_far_pointer(0x9000, 0)) == 0x5054);
assert(win16_read_u16(win16_make_far_pointer(0x9000, 8)) == 0x1f90);
g_free_space = 0x10000;
assert(borland_heap_free_space() == 0x11f90);
g_compact_result = 100;
assert(borland_heap_largest_block() == 0x1f90);
g_compact_result = 0x10000;
assert(borland_heap_largest_block() == 0x10000);
BorlandAllocationAttempt second = borland_try_get_mem(20);
assert(!second.failed && second.block == win16_make_far_pointer(0x9000, 112));
assert(win16_read_u16(win16_make_far_pointer(0x9000, 8)) == 0x1f7c);
assert(!borland_try_free_mem(first.block, 100));
assert(borland_try_free_mem(first.block, 100));
assert(!borland_try_free_mem(second.block, 20));
assert(g_borland_heap_segment_head == 0);
assert(g_freed_handle == 0x77);
return 0;
}
@@ -0,0 +1,152 @@
#include "../tdkpin_heap.h"
#include <assert.h>
#include <string.h>
uint16_t g_borland_heap_segment_head;
uint16_t g_borland_local_heap_limit;
uint16_t g_borland_heap_segment_bytes;
Win16FarPtr g_borland_heap_error_handler;
static uint8_t g_dgroup[0x10000];
static uint8_t g_segments[4][0x100];
static unsigned g_segment_count;
static bool g_fail_segment;
static bool g_fail_direct;
static Win16FarPtr g_direct_pointer;
static uint16_t g_handler_action;
static unsigned g_handler_calls;
static unsigned g_global_frees;
Win16FarPtr borland_global_allocate(uint16_t bytes)
{
if (bytes == g_borland_heap_segment_bytes) {
if (g_fail_segment) {
return 0;
}
assert(g_segment_count < 4 && bytes <= sizeof(g_segments[0]));
uint16_t selector = (uint16_t)(0x9000 + g_segment_count);
memset(g_segments[g_segment_count], 0, sizeof(g_segments[0]));
win16_bind_segment(
selector, g_segments[g_segment_count], bytes, true);
g_segment_count++;
return win16_make_far_pointer(selector, 0);
}
return g_fail_direct ? 0 : g_direct_pointer;
}
HGLOBAL16 GlobalHandle16(uint16_t selector)
{
return selector >= 0x9000 && selector < 0x9004
? (HGLOBAL16)(selector - 0x8f00)
: 0;
}
BOOL16 GlobalUnlock16(HGLOBAL16 allocation)
{
assert(allocation >= 0x100 && allocation < 0x104);
return 1;
}
HGLOBAL16 GlobalFree16(HGLOBAL16 allocation)
{
assert(allocation >= 0x100 && allocation < 0x104);
g_global_frees++;
return 0;
}
uint16_t win16_call_pascal_u16(Win16FarPtr procedure, uint16_t argument)
{
assert(procedure == g_borland_heap_error_handler && argument != 0);
g_handler_calls++;
if (g_handler_action == 2) {
g_fail_segment = false;
g_fail_direct = false;
}
return g_handler_action;
}
static void reset_environment(void)
{
win16_reset_segment_bindings();
memset(g_dgroup, 0, sizeof(g_dgroup));
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x5000);
g_borland_heap_segment_head = 0;
g_borland_local_heap_limit = 64;
g_borland_heap_segment_bytes = 64;
g_borland_heap_error_handler = 0;
g_segment_count = 0;
g_fail_segment = false;
g_fail_direct = false;
g_direct_pointer = win16_make_far_pointer(0xa000, 0);
g_handler_action = 0;
g_handler_calls = 0;
g_global_frees = 0;
}
static void test_global_and_fallback_paths(void)
{
reset_environment();
BorlandAllocationAttempt direct = borland_try_get_mem(100);
assert(!direct.failed && direct.block == g_direct_pointer);
reset_environment();
g_borland_local_heap_limit = 32;
g_fail_direct = true;
BorlandAllocationAttempt fallback = borland_try_get_mem(40);
assert(!fallback.failed);
assert(fallback.block == win16_make_far_pointer(0x9000, 12));
}
static void test_heap_error_actions(void)
{
reset_environment();
g_fail_segment = true;
g_fail_direct = true;
g_borland_heap_error_handler = win16_make_far_pointer(0x7000, 0x0100);
g_handler_action = 1;
BorlandAllocationAttempt result = borland_try_get_mem(20);
assert(!result.failed && result.block == 0 && g_handler_calls == 1);
g_handler_calls = 0;
g_handler_action = 0;
result = borland_try_get_mem(20);
assert(result.failed && result.block == 0 && g_handler_calls == 1);
g_handler_calls = 0;
g_handler_action = 2;
result = borland_try_get_mem(20);
assert(!result.failed && result.block == win16_make_far_pointer(0x9000, 12));
assert(g_handler_calls == 1);
}
static void test_multi_segment_ring_and_release(void)
{
reset_environment();
BorlandAllocationAttempt first = borland_try_get_mem(20);
BorlandAllocationAttempt second = borland_try_get_mem(20);
BorlandAllocationAttempt third = borland_try_get_mem(20);
assert(first.block == win16_make_far_pointer(0x9000, 12));
assert(second.block == win16_make_far_pointer(0x9000, 32));
assert(third.block == win16_make_far_pointer(0x9001, 12));
assert(g_borland_heap_segment_head == 0x9001);
assert(win16_read_u16(win16_make_far_pointer(0x9000, 10)) == 0x9001);
assert(win16_read_u16(win16_make_far_pointer(0x9001, 10)) == 0x9000);
assert(!borland_try_free_mem(first.block, 20));
assert(!borland_try_free_mem(second.block, 20));
assert(g_borland_heap_segment_head == 0x9001 && g_global_frees == 1);
assert(win16_read_u16(win16_make_far_pointer(0x9001, 10)) == 0x9001);
assert(!borland_try_free_mem(third.block, 20));
assert(g_borland_heap_segment_head == 0 && g_global_frees == 2);
}
int main(void)
{
test_global_and_fallback_paths();
test_heap_error_actions();
test_multi_segment_ring_and_release();
return 0;
}
@@ -0,0 +1,306 @@
#include "../tdkpin_borland_runtime.h"
#include <assert.h>
#include <setjmp.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
uint16_t g_borland_heap_reserve_bytes = 0x2000;
Win16FarPtr g_borland_heap_reserve;
uint8_t g_borland_heap_reserve_disabled;
Win16FarPtr g_borland_heap_error_handler;
Win16FarPtr g_borland_exit_proc;
uint16_t g_borland_exit_code_word;
uint16_t g_borland_error_offset;
uint16_t g_borland_error_segment;
uint16_t g_borland_windows_mode;
_Atomic uint16_t g_borland_io_result;
char g_borland_runtime_error_text[32];
static Win16FarPtr g_next_allocation;
static Win16FarPtr g_freed_block;
static uint16_t g_freed_bytes;
uint16_t g_borland_global_alloc_flags = 2;
static HGLOBAL16 g_global_handle;
static Win16FarPtr g_global_pointer;
static bool g_try_get_failed;
static bool g_try_free_failed;
static jmp_buf g_terminate_jump;
static bool g_expect_terminate;
static uint8_t g_terminated_status;
static Win16FarPtr g_exit_calls[4];
static unsigned g_exit_call_count;
static Win16FarPtr g_exit_successor;
static unsigned g_message_box_calls;
static UINT16 g_message_box_type;
HGLOBAL16 GlobalAlloc16(UINT16 flags, uint32_t bytes)
{
assert(flags == 2 && bytes == 1234);
return g_global_handle;
}
Win16FarPtr GlobalLock16(HGLOBAL16 allocation)
{
assert(allocation == g_global_handle);
return g_global_pointer;
}
INT16 MessageBox16(
HWND16 owner, Win16FarPtr text, Win16FarPtr caption, UINT16 type)
{
(void)owner;
(void)text;
(void)caption;
g_message_box_calls++;
g_message_box_type = type;
return 0;
}
void win16_call_exit_proc(Win16FarPtr procedure)
{
assert(g_borland_exit_proc == 0);
assert(atomic_load(&g_borland_io_result) == 0);
g_exit_calls[g_exit_call_count++] = procedure;
if (g_exit_successor != 0) {
g_borland_exit_proc = g_exit_successor;
g_exit_successor = 0;
}
}
_Noreturn void win16_dos_terminate(uint8_t status)
{
g_terminated_status = status;
if (g_expect_terminate) {
longjmp(g_terminate_jump, 1);
}
abort();
}
BorlandAllocationAttempt borland_try_get_mem(uint16_t bytes)
{
assert(bytes == g_borland_heap_reserve_bytes);
return (BorlandAllocationAttempt){g_next_allocation, g_try_get_failed};
}
bool borland_try_free_mem(Win16FarPtr block, uint16_t bytes)
{
g_freed_block = block;
g_freed_bytes = bytes;
return g_try_free_failed;
}
int main(void)
{
static uint8_t code[0x4000];
static uint8_t dgroup[0x1000];
win16_reset_segment_bindings();
win16_bind_ne_segment(2, 0x6000, code, sizeof(code), false);
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_set_indeterminate_u16(0xbeef);
code[0] = 2;
g_global_handle = 0;
assert(borland_global_allocate(1234) == 0);
g_global_handle = 9;
g_global_pointer = win16_make_far_pointer(0x8123, 0x0040);
assert(borland_global_allocate(1234) == g_global_pointer);
static uint8_t move_source[32];
static uint8_t move_destination[32];
for (uint8_t index = 0; index < sizeof(move_source); index++) {
move_source[index] = index;
}
memset(move_destination, 0, sizeof(move_destination));
win16_bind_segment(0x7100, move_source, sizeof(move_source), true);
win16_bind_segment(
0x7200, move_destination, sizeof(move_destination), true);
borland_move_bytes(
win16_make_far_pointer(0x7200, 2),
win16_make_far_pointer(0x7100, 8),
6);
assert(memcmp(&move_destination[2], &move_source[8], 6) == 0);
/* Source offset below destination selects STD even across selectors. */
borland_move_bytes(
win16_make_far_pointer(0x7200, 12),
win16_make_far_pointer(0x7100, 2),
6);
assert(memcmp(&move_destination[12], &move_source[2], 6) == 0);
memcpy(move_destination, move_source, sizeof(move_destination));
borland_move_bytes(
win16_make_far_pointer(0x7200, 6),
win16_make_far_pointer(0x7200, 2),
12);
assert(memcmp(&move_destination[6], &move_source[2], 12) == 0);
uint8_t before_zero_move[sizeof(move_destination)];
memcpy(before_zero_move, move_destination, sizeof(move_destination));
borland_move_bytes(
win16_make_far_pointer(0x7200, 30),
win16_make_far_pointer(0x7100, 31),
0);
assert(memcmp(
move_destination,
before_zero_move,
sizeof(move_destination)) == 0);
static uint8_t record_source[24];
static uint8_t record_destination[24];
for (uint8_t index = 0; index < sizeof(record_source); index++) {
record_source[index] = (uint8_t)(0xa0u + index);
}
memset(record_destination, 0x55, sizeof(record_destination));
win16_bind_segment(
0x7300, record_source, sizeof(record_source), true);
win16_bind_segment(
0x7400, record_destination, sizeof(record_destination), true);
win16_write_u16(win16_make_far_pointer(0x5000, 0x0300), 0, 10);
win16_write_u16(win16_make_far_pointer(0x7400, 10), 0, 0x0300);
borland_copy_typed_record(
6,
win16_make_far_pointer(0x7400, 4),
win16_make_far_pointer(0x7300, 2));
assert(memcmp(&record_destination[4], &record_source[2], 6) == 0);
assert(record_destination[12] == record_source[10]);
assert(record_destination[13] == record_source[11]);
assert(win16_read_u16(win16_make_far_pointer(0x7400, 10)) == 0x0300);
win16_write_u16(win16_make_far_pointer(0x5000, 0x0302), 0, 0);
win16_write_u16(win16_make_far_pointer(0x7400, 16), 0, 0x0302);
borland_copy_typed_record(
1,
win16_make_far_pointer(0x7400, 15),
win16_make_far_pointer(0x7500, 0xffff));
assert(win16_read_u16(win16_make_far_pointer(0x7400, 16)) == 0x0302);
g_borland_heap_reserve = 0;
g_next_allocation = win16_make_far_pointer(0x7000, 0x0100);
assert(borland_heap_reserve_is_unallocated());
borland_ensure_heap_reserve();
assert(g_borland_heap_reserve == g_next_allocation);
borland_ensure_heap_reserve();
assert(g_borland_heap_reserve == g_next_allocation);
g_borland_heap_reserve_disabled = 1;
assert(borland_release_heap_reserve_on_oom(64) == 1);
assert(g_borland_heap_reserve == g_next_allocation);
g_borland_heap_reserve_disabled = 0;
g_freed_block = 0;
assert(borland_release_heap_reserve_on_oom(64) == 2);
assert(g_freed_block == g_next_allocation && g_freed_bytes == 0x2000);
assert(g_borland_heap_reserve == 0);
assert(borland_release_heap_reserve_on_oom(64) == 0);
assert(borland_release_heap_reserve_on_oom(0) == 0xbeef);
borland_initialize_heap_reserve();
assert(g_borland_heap_reserve == g_next_allocation);
assert(g_borland_heap_error_handler ==
win16_make_far_pointer(0x6000, 0x2e22));
g_try_get_failed = false;
g_next_allocation = win16_make_far_pointer(0x7000, 0x0200);
assert(borland_get_mem(g_borland_heap_reserve_bytes) == g_next_allocation);
g_try_free_failed = false;
borland_free_mem(g_next_allocation, g_borland_heap_reserve_bytes);
g_borland_windows_mode = 0;
g_expect_terminate = true;
g_try_get_failed = true;
win16_set_current_return_address(
win16_make_far_pointer(0xffff, 0x2222));
if (setjmp(g_terminate_jump) == 0) {
(void)borland_get_mem(g_borland_heap_reserve_bytes);
assert(false);
}
assert(g_terminated_status == 203);
assert(g_borland_error_offset == 0x2222);
assert(g_borland_error_segment == 0xffff);
g_try_get_failed = false;
g_try_free_failed = true;
win16_set_current_return_address(
win16_make_far_pointer(0x6000, 0x3333));
if (setjmp(g_terminate_jump) == 0) {
borland_free_mem(g_next_allocation, g_borland_heap_reserve_bytes);
assert(false);
}
assert(g_terminated_status == 204);
assert(g_borland_error_offset == 0x3333);
assert(g_borland_error_segment == 2);
atomic_store(&g_borland_io_result, 0);
borland_require_io_success();
BorlandInt32Range range = {-10, 20};
borland_check_int32_range(-10, &range);
borland_check_int32_range(20, &range);
atomic_store(&g_borland_io_result, 5);
win16_set_current_return_address(
win16_make_far_pointer(0xffff, 0x4444));
if (setjmp(g_terminate_jump) == 0) {
borland_require_io_success();
assert(false);
}
assert(g_terminated_status == 5);
win16_set_current_return_address(
win16_make_far_pointer(0xffff, 0x5555));
if (setjmp(g_terminate_jump) == 0) {
borland_check_int32_range(-11, &range);
assert(false);
}
assert(g_terminated_status == 201);
if (setjmp(g_terminate_jump) == 0) {
borland_check_int32_range(21, &range);
assert(false);
}
assert(g_terminated_status == 201);
if (setjmp(g_terminate_jump) == 0) {
borland_runtime_error_215();
assert(false);
}
assert(g_terminated_status == 215);
g_exit_call_count = 0;
g_borland_exit_proc = win16_make_far_pointer(0x7000, 0x0100);
g_exit_successor = win16_make_far_pointer(0x7000, 0x0200);
atomic_store(&g_borland_io_result, 99);
borland_run_exit_procedures();
assert(g_exit_call_count == 2);
assert(g_exit_calls[0] == win16_make_far_pointer(0x7000, 0x0100));
assert(g_exit_calls[1] == win16_make_far_pointer(0x7000, 0x0200));
assert(g_borland_exit_proc == 0);
assert(atomic_load(&g_borland_io_result) == 0);
g_borland_windows_mode = 0;
if (setjmp(g_terminate_jump) == 0) {
borland_runtime_exit(0x1234);
assert(false);
}
assert(g_borland_exit_code_word == 0x1234);
assert(g_terminated_status == 0x34);
assert(g_borland_error_offset == 0 && g_borland_error_segment == 0);
strcpy(g_borland_runtime_error_text,
"Runtime error 000 at 0000:0000.");
g_borland_windows_mode = 1;
g_message_box_calls = 0;
win16_set_current_return_address(
win16_make_far_pointer(0x6000, 0x3333));
if (setjmp(g_terminate_jump) == 0) {
borland_runtime_error(201);
assert(false);
}
assert(g_terminated_status == 201);
assert(g_message_box_calls == 1 && g_message_box_type == 0x1010);
assert(strcmp(g_borland_runtime_error_text,
"Runtime error 201 at 0002:3333.") == 0);
g_expect_terminate = false;
return 0;
}
@@ -0,0 +1,688 @@
#include "../tdkpin_highscores.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
enum {
FILE_HEADER_BYTES = 16,
SCORE_BYTES = TDKPIN_HIGHSCORE_COUNT * 4,
NAME_BYTES = TDKPIN_HIGHSCORE_COUNT * TDKPIN_HIGHSCORE_NAME_BYTES,
FILE_BYTES = FILE_HEADER_BYTES + SCORE_BYTES + NAME_BYTES,
};
static uint8_t g_dgroup[0x5000];
static uint8_t g_objects[0x1000];
static uint8_t g_call_stack[0x1000];
static uint8_t g_file_bytes[FILE_BYTES];
static uint8_t g_input_bytes[FILE_BYTES];
static bool g_create_fails;
static unsigned g_create_calls;
static HFILE16 g_open_results[2];
static unsigned g_open_calls;
static uint16_t g_input_position;
static uint16_t g_input_size;
static uint16_t g_read_limits[3];
static unsigned g_read_calls;
static unsigned g_write_calls;
static unsigned g_close_calls;
static uint16_t g_file_position;
static unsigned g_dialog_construct_calls;
static unsigned g_dialog_exec_calls;
static unsigned g_child_construct_calls;
static unsigned g_make_window_calls;
static unsigned g_show_calls;
static Win16FarPtr g_dialog;
static Win16FarPtr g_child;
static Win16FarPtr g_made_child;
static Win16FarPtr g_application;
static Win16FarPtr g_receiver;
static Win16FarPtr g_exec_dialog_procedure;
static Win16FarPtr g_make_window_procedure;
static unsigned g_select_palette_calls;
static unsigned g_unrealize_calls;
static unsigned g_realize_calls;
static unsigned g_compatible_dc_calls;
static unsigned g_compatible_bitmap_calls;
static unsigned g_select_object_calls;
static unsigned g_bitblt_calls;
static unsigned g_text_color_calls;
static unsigned g_text_align_calls;
static unsigned g_bk_mode_calls;
static unsigned g_text_out_calls;
static unsigned g_delete_object_calls;
static unsigned g_delete_dc_calls;
static char g_drawn_text[21][64];
static INT16 g_drawn_x[21];
static INT16 g_drawn_y[21];
static Win16FarPtr dgroup_at(uint16_t offset)
{
return win16_make_far_pointer(0x5000, offset);
}
static Win16FarPtr object_at(uint16_t offset)
{
return win16_make_far_pointer(0x6000, offset);
}
Win16FarPtr object_windows_text_dialog_construct(
Win16FarPtr dialog,
uint16_t vmt,
INT16 control_id,
Win16FarPtr text_buffer,
Win16FarPtr label,
Win16FarPtr caption,
Win16FarPtr parent)
{
assert(dialog == 0 && vmt == 0x01ce && control_id == 0x0016);
uint8_t insertion = g_dgroup[0x07d2];
assert(text_buffer == dgroup_at((uint16_t)(0x0890 + insertion * 22u)));
assert(label == dgroup_at(0x0373));
assert(caption == dgroup_at(0x0349));
assert(parent == g_receiver);
g_dialog_construct_calls++;
return g_dialog;
}
Win16FarPtr tdkpin_centered_child_window_construct(
Win16FarPtr window,
uint16_t vmt,
Win16FarPtr title,
Win16FarPtr parent)
{
assert(window == 0 && vmt == 0x010e);
assert(title == dgroup_at(0x038b) && parent == g_receiver);
g_child_construct_calls++;
return g_child;
}
int16_t win16_call_application_dialog_method(
Win16FarPtr procedure,
Win16FarPtr application,
Win16FarPtr dialog)
{
assert(procedure == g_exec_dialog_procedure);
assert(application == g_application && dialog == g_dialog);
g_dialog_exec_calls++;
return 17;
}
Win16FarPtr win16_call_application_window_method(
Win16FarPtr procedure,
Win16FarPtr application,
Win16FarPtr window)
{
assert(procedure == g_make_window_procedure);
assert(application == g_application && window == g_child);
g_make_window_calls++;
return g_made_child;
}
void object_windows_show(Win16FarPtr object, INT16 command)
{
assert(object == g_made_child && command == 1);
g_show_calls++;
}
HPALETTE16 SelectPalette16(
HDC16 dc, HPALETTE16 palette, BOOL16 force_background)
{
assert(dc == 0x1111 && force_background == 0);
if (g_select_palette_calls == 0) {
assert(palette == 0x2222);
g_select_palette_calls++;
return 0x3333;
}
assert(g_select_palette_calls == 1 && palette == 0x3333);
g_select_palette_calls++;
return 0x2222;
}
BOOL16 UnrealizeObject16(HGDIOBJ16 object)
{
assert(object == 0x2222);
g_unrealize_calls++;
return 1;
}
UINT16 RealizePalette16(HDC16 dc)
{
assert(dc == 0x1111);
g_realize_calls++;
return 5;
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x1111 && g_compatible_dc_calls < 2);
return (HDC16)(0x4001 + g_compatible_dc_calls++);
}
HBITMAP16 CreateCompatibleBitmap16(HDC16 dc, INT16 width, INT16 height)
{
assert(dc == 0x1111 && width == 360 && height == 300);
g_compatible_bitmap_calls++;
return 0x5002;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
switch (g_select_object_calls++) {
case 0:
assert(dc == 0x4001 && object == 0x5001);
return 0x6001;
case 1:
assert(dc == 0x4002 && object == 0x5002);
return 0x6002;
case 2:
assert(dc == 0x4002 && object == 0x6002);
return 0x5002;
default:
assert(g_select_object_calls == 4);
assert(dc == 0x4001 && object == 0x6001);
return 0x5001;
}
}
COLORREF16 GetTextColor16(HDC16 dc)
{
assert(dc == 0x4002);
return 0x00112233u;
}
COLORREF16 SetTextColor16(HDC16 dc, COLORREF16 color)
{
assert(dc == 0x4002 && g_text_color_calls < 2);
assert(color == (g_text_color_calls == 0 ? 0 : 0x00112233u));
g_text_color_calls++;
return color == 0 ? 0x00112233u : 0;
}
UINT16 SetTextAlign16(HDC16 dc, UINT16 alignment)
{
assert(dc == 0x4002 && g_text_align_calls < 22);
if (g_text_align_calls == 0) {
assert(alignment == 6);
} else if (g_text_align_calls == 21) {
assert(alignment == 7);
} else {
assert(alignment == (g_text_align_calls & 1u ? 0 : 2));
}
g_text_align_calls++;
return g_text_align_calls == 1 ? 7 : 0;
}
INT16 SetBkMode16(HDC16 dc, INT16 mode)
{
assert(dc == 0x4002 && g_bk_mode_calls < 2);
assert(mode == (g_bk_mode_calls == 0 ? 1 : 2));
g_bk_mode_calls++;
return mode == 1 ? 2 : 1;
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
assert(destination_x == 0 && destination_y == 0);
assert(width == 360 && height == 300);
assert(raster_operation == 0x00cc0020u);
if (g_bitblt_calls == 0) {
assert(destination == 0x4002 && source == 0x4001);
assert(source_x == 32 && source_y == 204);
} else {
assert(g_bitblt_calls == 1);
assert(destination == 0x1111 && source == 0x4002);
assert(source_x == 0 && source_y == 0);
}
g_bitblt_calls++;
return 1;
}
BOOL16 TextOut16(
HDC16 dc,
INT16 x,
INT16 y,
Win16FarPtr text,
INT16 character_count)
{
assert(dc == 0x4002 && g_text_out_calls < 21);
assert(character_count >= 0 && character_count < 64);
for (INT16 index = 0; index < character_count; index++) {
g_drawn_text[g_text_out_calls][index] = (char)win16_read_u8(
win16_far_add_offset(text, (uint16_t)index));
}
g_drawn_text[g_text_out_calls][character_count] = '\0';
g_drawn_x[g_text_out_calls] = x;
g_drawn_y[g_text_out_calls] = y;
g_text_out_calls++;
return 1;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(object == 0x5002);
g_delete_object_calls++;
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(dc == (g_delete_dc_calls == 0 ? 0x4002 : 0x4001));
g_delete_dc_calls++;
return 1;
}
HFILE16 LCreate16(Win16FarPtr file_name, INT16 attributes)
{
static const char expected_name[] = "HiScores.Dat";
assert(file_name == dgroup_at(0x0312));
assert(attributes == 0);
for (size_t index = 0; index < sizeof(expected_name); index++) {
assert(win16_read_u8(win16_far_add_offset(file_name, index)) ==
(uint8_t)expected_name[index]);
}
g_create_calls++;
return g_create_fails ? (HFILE16)-1 : (HFILE16)7;
}
HFILE16 LOpen16(Win16FarPtr file_name, INT16 mode)
{
assert(file_name == dgroup_at(0x0320));
assert(g_open_calls < 2);
assert(mode == (g_open_calls == 0 ? 0 : 0x40));
return g_open_results[g_open_calls++];
}
UINT16 LRead16(HFILE16 file, Win16FarPtr bytes, UINT16 byte_count)
{
assert(file == 9 && g_read_calls < 3);
static const uint16_t expected_counts[3] = {
FILE_HEADER_BYTES, SCORE_BYTES, NAME_BYTES};
assert(byte_count == expected_counts[g_read_calls]);
uint16_t copied = byte_count;
if (copied > g_read_limits[g_read_calls]) {
copied = g_read_limits[g_read_calls];
}
uint16_t available = (uint16_t)(g_input_size - g_input_position);
if (copied > available) {
copied = available;
}
for (uint16_t index = 0; index < copied; index++) {
win16_write_u8(
win16_far_add_offset(bytes, index),
g_input_bytes[g_input_position + index]);
}
g_input_position = (uint16_t)(g_input_position + copied);
g_read_calls++;
return copied;
}
UINT16 LWrite16(HFILE16 file, Win16FarPtr bytes, UINT16 byte_count)
{
assert(file == 7);
if (g_write_calls == 0) {
assert(bytes == dgroup_at(0x0014));
assert(byte_count == FILE_HEADER_BYTES);
} else if (g_write_calls == 1) {
assert(byte_count == SCORE_BYTES);
} else {
assert(g_write_calls == 2 && byte_count == NAME_BYTES);
}
for (uint16_t index = 0; index < byte_count; index++) {
g_file_bytes[g_file_position + index] =
win16_read_u8(win16_far_add_offset(bytes, index));
}
g_file_position = (uint16_t)(g_file_position + byte_count);
g_write_calls++;
return byte_count;
}
HFILE16 LClose16(HFILE16 file)
{
assert(file == 7 || file == 9);
g_close_calls++;
return 0;
}
static void prepare_fixture(void)
{
memset(g_dgroup, 0, sizeof(g_dgroup));
memset(g_objects, 0, sizeof(g_objects));
memset(g_call_stack, 0, sizeof(g_call_stack));
memset(g_file_bytes, 0xcc, sizeof(g_file_bytes));
memset(g_input_bytes, 0, sizeof(g_input_bytes));
memcpy(&g_dgroup[0x0010], "IWIKTDK Highscores\0\0", 20);
memcpy(&g_dgroup[0x0312], "HiScores.Dat", 13);
memcpy(&g_dgroup[0x0320], "HiScores.Dat", 13);
memcpy(&g_dgroup[0x032d], "No Name", 8);
memcpy(&g_dgroup[0x0336], "TDK Pinball Player", 19);
memcpy(
&g_dgroup[0x0349],
"Congratulations! This is a Top Ten Score!",
42);
memcpy(&g_dgroup[0x0373], "Please input your name:", 24);
memcpy(&g_dgroup[0x038b], "HighScore", 10);
memcpy(&g_dgroup[0x02f4], "HIGH SCORES", 12);
memcpy(&g_dgroup[0x0300], " ", 3);
memcpy(&g_dgroup[0x0303], ". ", 3);
for (uint16_t index = 1; index <= TDKPIN_HIGHSCORE_COUNT; index++) {
uint32_t score = 0x10203040u + (uint32_t)index * 0x01010101u;
g_dgroup[0x087a + index * 4u] = (uint8_t)score;
g_dgroup[0x087b + index * 4u] = (uint8_t)(score >> 8);
g_dgroup[0x087c + index * 4u] = (uint8_t)(score >> 16);
g_dgroup[0x087d + index * 4u] = (uint8_t)(score >> 24);
char name[TDKPIN_HIGHSCORE_NAME_BYTES];
memset(name, 0, sizeof(name));
name[0] = 'P';
name[1] = (char)('0' + index / 10u);
name[2] = (char)('0' + index % 10u);
memcpy(
&g_dgroup[0x0890 + index * TDKPIN_HIGHSCORE_NAME_BYTES],
name,
sizeof(name));
}
g_create_fails = false;
g_create_calls = 0;
g_open_results[0] = (HFILE16)-1;
g_open_results[1] = (HFILE16)-1;
g_open_calls = 0;
g_input_position = 0;
g_input_size = FILE_BYTES;
for (unsigned index = 0; index < 3; index++) {
g_read_limits[index] = UINT16_MAX;
}
g_read_calls = 0;
g_write_calls = 0;
g_close_calls = 0;
g_file_position = 0;
g_dialog_construct_calls = 0;
g_dialog_exec_calls = 0;
g_child_construct_calls = 0;
g_make_window_calls = 0;
g_show_calls = 0;
g_dialog = object_at(0x200);
g_child = object_at(0x300);
g_made_child = object_at(0x350);
g_application = object_at(0x400);
g_receiver = object_at(0x100);
g_exec_dialog_procedure = win16_make_far_pointer(0x8000, 0x0038);
g_make_window_procedure = win16_make_far_pointer(0x8000, 0x0034);
g_select_palette_calls = 0;
g_unrealize_calls = 0;
g_realize_calls = 0;
g_compatible_dc_calls = 0;
g_compatible_bitmap_calls = 0;
g_select_object_calls = 0;
g_bitblt_calls = 0;
g_text_color_calls = 0;
g_text_align_calls = 0;
g_bk_mode_calls = 0;
g_text_out_calls = 0;
g_delete_object_calls = 0;
g_delete_dc_calls = 0;
memset(g_drawn_text, 0, sizeof(g_drawn_text));
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_objects, sizeof(g_objects), true);
win16_bind_segment(0x7100, g_call_stack, sizeof(g_call_stack), true);
win16_set_dgroup_selector(0x5000);
uint8_t stack_anchor = 0;
uintptr_t stack_base = (uintptr_t)&stack_anchor - 0x8000u;
win16_bind_segment(0x7000, (void *)stack_base, 0x10000, true);
win16_set_stack_state(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
win16_write_u16(g_application, 0, 0x0200);
win16_write_u32(dgroup_at(0x0234), 0, g_make_window_procedure);
win16_write_u32(dgroup_at(0x0238), 0, g_exec_dialog_procedure);
win16_write_u32(
dgroup_at(0x064e), 0, g_application);
win16_write_u16(dgroup_at(0x444e), 0, 0x2222);
win16_write_u16(dgroup_at(0x0867), 0, 0x5001);
}
static uint32_t read_dgroup_u32(uint16_t offset)
{
return (uint32_t)g_dgroup[offset] |
((uint32_t)g_dgroup[offset + 1] << 8) |
((uint32_t)g_dgroup[offset + 2] << 16) |
((uint32_t)g_dgroup[offset + 3] << 24);
}
static void prepare_valid_input_file(void)
{
memcpy(g_input_bytes, &g_dgroup[0x0014], FILE_HEADER_BYTES);
const uint32_t key = 0x4b495749u;
for (uint16_t index = 1; index <= TDKPIN_HIGHSCORE_COUNT; index++) {
uint32_t score = 0x01020304u * index;
uint32_t encrypted = score ^ key;
uint16_t score_offset = (uint16_t)(
FILE_HEADER_BYTES + (index - 1u) * 4u);
g_input_bytes[score_offset] = (uint8_t)encrypted;
g_input_bytes[score_offset + 1] = (uint8_t)(encrypted >> 8);
g_input_bytes[score_offset + 2] = (uint8_t)(encrypted >> 16);
g_input_bytes[score_offset + 3] = (uint8_t)(encrypted >> 24);
uint16_t name_offset = (uint16_t)(
FILE_HEADER_BYTES + SCORE_BYTES +
(index - 1u) * TDKPIN_HIGHSCORE_NAME_BYTES);
g_input_bytes[name_offset] = 'L';
g_input_bytes[name_offset + 1] = (uint8_t)('0' + index / 10u);
g_input_bytes[name_offset + 2] = (uint8_t)('0' + index % 10u);
}
}
static uint32_t file_u32(uint16_t offset)
{
return (uint32_t)g_file_bytes[offset] |
((uint32_t)g_file_bytes[offset + 1] << 8) |
((uint32_t)g_file_bytes[offset + 2] << 16) |
((uint32_t)g_file_bytes[offset + 3] << 24);
}
static void test_complete_file_layout(void)
{
prepare_fixture();
tdkpin_save_highscores(0xbeef);
assert(g_create_calls == 1 && g_write_calls == 3 && g_close_calls == 1);
assert(g_file_position == FILE_BYTES);
assert(memcmp(g_file_bytes, "TDK Highscores\0\0", FILE_HEADER_BYTES) == 0);
const uint32_t key = 0x4b495749u;
for (uint16_t index = 1; index <= TDKPIN_HIGHSCORE_COUNT; index++) {
uint32_t score = 0x10203040u + (uint32_t)index * 0x01010101u;
assert(file_u32((uint16_t)(
FILE_HEADER_BYTES + (index - 1u) * 4u)) ==
(score ^ key));
const uint8_t *name = &g_file_bytes[
FILE_HEADER_BYTES + SCORE_BYTES +
(index - 1u) * TDKPIN_HIGHSCORE_NAME_BYTES];
assert(name[0] == 'P');
assert(name[1] == (uint8_t)('0' + index / 10u));
assert(name[2] == (uint8_t)('0' + index % 10u));
for (uint16_t byte = 3; byte < TDKPIN_HIGHSCORE_NAME_BYTES; byte++) {
assert(name[byte] == 0);
}
}
}
static void test_create_failure_is_silent(void)
{
prepare_fixture();
g_create_fails = true;
tdkpin_save_highscores(0x1234);
assert(g_create_calls == 1);
assert(g_write_calls == 0 && g_close_calls == 0 && g_file_position == 0);
}
static void assert_default_table(void)
{
for (uint16_t index = 1; index <= TDKPIN_HIGHSCORE_COUNT; index++) {
assert(read_dgroup_u32((uint16_t)(0x087a + index * 4u)) ==
(uint32_t)(11u - index) * 100000u);
const uint8_t *name =
&g_dgroup[0x0890 + index * TDKPIN_HIGHSCORE_NAME_BYTES];
assert(memcmp(name, "No Name", 8) == 0);
}
}
static void test_valid_load_and_open_retry(void)
{
prepare_fixture();
prepare_valid_input_file();
g_open_results[0] = (HFILE16)-1;
g_open_results[1] = 9;
tdkpin_load_highscores(0xaaaa);
assert(g_open_calls == 2 && g_read_calls == 3 && g_close_calls == 1);
for (uint16_t index = 1; index <= TDKPIN_HIGHSCORE_COUNT; index++) {
assert(read_dgroup_u32((uint16_t)(0x087a + index * 4u)) ==
0x01020304u * index);
const uint8_t *name =
&g_dgroup[0x0890 + index * TDKPIN_HIGHSCORE_NAME_BYTES];
assert(name[0] == 'L');
assert(name[1] == (uint8_t)('0' + index / 10u));
assert(name[2] == (uint8_t)('0' + index % 10u));
}
}
static void test_missing_and_invalid_files_install_defaults(void)
{
prepare_fixture();
tdkpin_load_highscores(0);
assert(g_open_calls == 2 && g_read_calls == 0 && g_close_calls == 0);
assert_default_table();
prepare_fixture();
prepare_valid_input_file();
g_input_bytes[0] ^= 0xffu;
g_open_results[0] = 9;
tdkpin_load_highscores(0);
assert(g_open_calls == 1 && g_read_calls == 1 && g_close_calls == 1);
assert_default_table();
}
static void test_short_score_read_retains_stack_residue(void)
{
prepare_fixture();
prepare_valid_input_file();
g_open_results[0] = 9;
g_read_limits[1] = 4;
win16_set_indeterminate_u16(0x5a5a);
tdkpin_load_highscores(0);
assert(g_read_calls == 3 && g_close_calls == 1);
assert(read_dgroup_u32(0x087e) == 0x01020304u);
for (uint16_t index = 2; index <= TDKPIN_HIGHSCORE_COUNT; index++) {
assert(read_dgroup_u32((uint16_t)(0x087a + index * 4u)) ==
(0x5a5a5a5au ^ 0x4b495749u));
}
}
static void test_highscore_windows(void)
{
prepare_fixture();
g_dgroup[0x07d2] = 0;
tdkpin_show_highscore_windows(0x0100);
assert(g_dialog_construct_calls == 0 && g_dialog_exec_calls == 0);
assert(g_child_construct_calls == 1 && g_make_window_calls == 1);
assert(g_show_calls == 1);
assert(win16_read_far_pointer(g_receiver, 0x41) == g_made_child);
prepare_fixture();
g_dgroup[0x07d2] = 3;
memcpy(&g_dgroup[0x0890 + 3 * 22], "old", 4);
tdkpin_show_highscore_windows(0x0100);
assert(memcmp(&g_dgroup[0x0890 + 3 * 22],
"TDK Pinball Player", 19) == 0);
assert(g_dialog_construct_calls == 1 && g_dialog_exec_calls == 1);
assert(g_child_construct_calls == 1 && g_make_window_calls == 1);
assert(g_show_calls == 1);
}
static void prepare_descending_scores(void)
{
for (uint16_t index = 1; index <= TDKPIN_HIGHSCORE_COUNT; index++) {
uint32_t score = (uint32_t)(11u - index) * 100u;
win16_write_u32(dgroup_at((uint16_t)(0x087a + index * 4u)), 0, score);
uint8_t *name = &g_dgroup[0x0890 + index * 22u];
memset(name, 0, 22);
name[0] = 'P';
name[1] = (uint8_t)('0' + index / 10u);
name[2] = (uint8_t)('0' + index % 10u);
}
g_dgroup[0x07d1] = 1;
}
static void test_score_insertion_and_no_qualification(void)
{
prepare_fixture();
prepare_descending_scores();
win16_write_u32(dgroup_at(0x0982), 0, 750);
tdkpin_insert_current_score(0x0100);
assert(g_dgroup[0x07d2] == 4);
assert(read_dgroup_u32(0x088a) == 750);
assert(read_dgroup_u32(0x088e) == 700);
assert(memcmp(&g_dgroup[0x0890 + 5 * 22], "P04", 4) == 0);
assert(memcmp(&g_dgroup[0x0890 + 4 * 22],
"TDK Pinball Player", 19) == 0);
assert(g_dialog_exec_calls == 1 && g_show_calls == 1);
assert(g_create_calls == 1 && g_write_calls == 3 && g_close_calls == 1);
prepare_fixture();
prepare_descending_scores();
win16_write_u32(dgroup_at(0x0982), 0, 100);
tdkpin_insert_current_score(0x0100);
assert(g_dgroup[0x07d2] == 0);
assert(g_dialog_exec_calls == 0 && g_show_calls == 0);
assert(g_create_calls == 0 && g_write_calls == 0 && g_close_calls == 0);
}
static void test_highscore_paint(void)
{
prepare_fixture();
prepare_descending_scores();
PAINTSTRUCT16 ignored_paint;
memset(&ignored_paint, 0xa5, sizeof(ignored_paint));
tdkpin_paint_highscores(object_at(0x777), &ignored_paint, 0x1111);
assert(g_select_palette_calls == 2);
assert(g_unrealize_calls == 1 && g_realize_calls == 1);
assert(g_compatible_dc_calls == 2 && g_compatible_bitmap_calls == 1);
assert(g_select_object_calls == 4 && g_bitblt_calls == 2);
assert(g_text_color_calls == 2 && g_text_align_calls == 22);
assert(g_bk_mode_calls == 2 && g_text_out_calls == 21);
assert(g_delete_object_calls == 1 && g_delete_dc_calls == 2);
assert(strcmp(g_drawn_text[0], "HIGH SCORES") == 0);
assert(g_drawn_x[0] == 180 && g_drawn_y[0] == 27);
assert(strcmp(g_drawn_text[1], " 1. P01") == 0);
assert(g_drawn_x[1] == 30 && g_drawn_y[1] == 60);
assert(strcmp(g_drawn_text[2], "1000") == 0);
assert(g_drawn_x[2] == 320 && g_drawn_y[2] == 60);
assert(strcmp(g_drawn_text[19], "10. P10") == 0);
assert(g_drawn_x[19] == 30 && g_drawn_y[19] == 240);
assert(strcmp(g_drawn_text[20], "100") == 0);
assert(g_drawn_x[20] == 320 && g_drawn_y[20] == 240);
}
int main(void)
{
test_complete_file_layout();
test_create_failure_is_silent();
test_valid_load_and_open_retry();
test_missing_and_invalid_files_install_defaults();
test_short_score_read_retains_stack_residue();
test_highscore_windows();
test_score_insertion_and_no_qualification();
test_highscore_paint();
return 0;
}
@@ -0,0 +1,9 @@
#include "../tdkpin_initialization.h"
int main(void)
{
tdkpin_initialize_unit_ordinal_91();
tdkpin_initialize_unit_ordinal_86();
tdkpin_initialize_unit_ordinal_72();
return 0;
}
@@ -0,0 +1,167 @@
#include "../tdkpin_key_input.h"
#include <assert.h>
#include <string.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_objects[0x1000];
static Win16FarPtr g_window;
static Win16FarPtr g_event;
static Win16FarPtr g_application;
static Win16FarPtr g_child;
static unsigned g_construct_calls;
static unsigned g_make_calls;
static unsigned g_show_calls;
static unsigned g_get_dc_calls;
static unsigned g_release_dc_calls;
static unsigned g_row_calls;
static INT16 g_last_row;
Win16FarPtr tdkpin_full_child_window_construct(
Win16FarPtr window,
uint16_t vmt,
Win16FarPtr title,
Win16FarPtr parent)
{
assert(window == 0 && vmt == 0x017e);
assert(title == win16_dgroup_pointer(0x0306) && parent == g_window);
g_construct_calls++;
return g_child;
}
Win16FarPtr win16_call_application_window_method(
Win16FarPtr procedure,
Win16FarPtr application,
Win16FarPtr window)
{
assert(procedure == win16_make_far_pointer(0x8000, 0x0034));
assert(application == g_application && window == g_child);
g_make_calls++;
return g_child;
}
void object_windows_show(Win16FarPtr object, INT16 command)
{
assert(object == g_child && command == 1);
g_show_calls++;
}
int32_t borland_multiply_i32(int32_t left, int32_t right)
{
return (int32_t)((uint32_t)left * (uint32_t)right);
}
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;
}
void tdkpin_draw_board_decoration_row_on_dc(INT16 y, HDC16 dc)
{
assert(dc == 0x3333);
g_last_row = y;
g_row_calls++;
}
static void prepare_fixture(uint16_t scan_code)
{
memset(g_dgroup, 0, sizeof(g_dgroup));
memset(g_objects, 0, sizeof(g_objects));
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_set_dgroup_selector(0x7200);
g_window = win16_make_far_pointer(0x7000, 0x0000);
g_event = win16_make_far_pointer(0x7000, 0x0c00);
g_application = win16_make_far_pointer(0x7000, 0x0800);
g_child = win16_make_far_pointer(0x7000, 0x0900);
win16_write_u16(g_window, 4, 0x2222);
win16_write_u16(g_event, 8, scan_code);
win16_write_u16(g_event, 0x0a, 0xaaaa);
win16_write_u16(g_event, 0x0c, 0xbbbb);
win16_write_u16(g_application, 0, 0x0200);
win16_write_u32(
win16_dgroup_pointer(0x0234), 0,
win16_make_far_pointer(0x8000, 0x0034));
win16_write_u32(win16_dgroup_pointer(0x064e), 0, g_application);
g_construct_calls = 0;
g_make_calls = 0;
g_show_calls = 0;
g_get_dc_calls = 0;
g_release_dc_calls = 0;
g_row_calls = 0;
g_last_row = 0;
}
static void test_help_and_auxiliary_gate(void)
{
prepare_fixture(0x3b);
win16_set_indeterminate_u16(0xabcd);
assert(tdkpin_handle_key_press(g_window, g_event) == 0xabcd);
assert(g_construct_calls == 1 && g_make_calls == 1 && g_show_calls == 1);
assert(win16_read_far_pointer(g_window, 0x45) == g_child);
assert(win16_read_u16(win16_far_add_offset(g_event, 0x0a)) == 0xaaaa);
prepare_fixture(0x1d);
win16_write_u16(g_window, 0x41, 1);
assert(tdkpin_handle_key_press(g_window, g_event) == 0);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bda)) == 0);
assert(win16_read_u16(win16_far_add_offset(g_event, 0x0a)) == 0);
assert(win16_read_u16(win16_far_add_offset(g_event, 0x0c)) == 0);
}
static void test_down_key_threshold_and_player_gate(void)
{
prepare_fixture(0x50);
win16_write_u32(g_window, 0x5a, 100);
win16_write_u32(g_window, 0x0bae, (uint32_t)-12000);
win16_write_u32(g_window, 0x0baa, 0x12345678u);
win16_write_u8(win16_dgroup_pointer(0x07d0), 1);
tdkpin_handle_key_press(g_window, g_event);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == -17000);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 0);
assert(g_row_calls == 1 && g_last_row == 84);
assert(g_get_dc_calls == 1 && g_release_dc_calls == 1);
prepare_fixture(0x50);
win16_write_u32(g_window, 0x5a, 100);
win16_write_u32(g_window, 0x0bae, 1000);
tdkpin_handle_key_press(g_window, g_event);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == -4000);
assert(g_row_calls == 0 && g_get_dc_calls == 0);
}
static void test_flipper_keys_and_tilt(void)
{
prepare_fixture(0x1d);
tdkpin_handle_key_press(g_window, g_event);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bda)) == 1);
prepare_fixture(0x1c);
tdkpin_handle_key_press(g_window, g_event);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdb)) == 1);
prepare_fixture(0x1d);
win16_write_u8(win16_dgroup_pointer(0x07c6), 1);
tdkpin_handle_key_press(g_window, g_event);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bda)) == 0);
}
int main(void)
{
test_help_and_auxiliary_gate();
test_down_key_threshold_and_player_gate();
test_flipper_keys_and_tilt();
return 0;
}
@@ -0,0 +1,499 @@
#include "../tdkpin_key_input.h"
#include "../tdkpin_random.h"
#include "../tdkpin_real48.h"
#include <assert.h>
#include <string.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_objects[0x2000];
static Win16FarPtr g_window;
static Win16FarPtr g_event;
static uint16_t g_random_values[16];
static uint16_t g_random_bounds[16];
static size_t g_random_count;
static size_t g_random_index;
static uint16_t g_sounds[8];
static size_t g_sound_count;
static unsigned g_get_dc_calls;
static unsigned g_release_dc_calls;
static unsigned g_blit_calls;
static unsigned g_restore_a_calls;
static unsigned g_restore_b_calls;
static unsigned g_present_calls;
static unsigned g_draw_item_calls;
static unsigned g_draw_score_calls;
static unsigned g_status_calls;
typedef enum {
REAL_MODE_NONE,
REAL_MODE_UPPER_CLAMP,
REAL_MODE_HORIZONTAL_NUDGE,
} RealMode;
static RealMode g_real_mode;
static unsigned g_real_multiply_calls;
static const BorlandReal48 g_real_random =
{{0x80, 1, 2, 3, 4, 5}};
static const BorlandReal48 g_real_product =
{{0x81, 6, 7, 8, 9, 10}};
static const BorlandReal48 g_real_factor =
{{0x82, 11, 12, 13, 14, 15}};
static const BorlandReal48 g_real_integer =
{{0x83, 16, 17, 18, 19, 20}};
static const BorlandReal48 g_real_result =
{{0x84, 21, 22, 23, 24, 25}};
static void assert_real48(BorlandReal48 actual, BorlandReal48 expected)
{
assert(memcmp(actual.bytes, expected.bytes, sizeof(actual.bytes)) == 0);
}
int32_t borland_multiply_i32(int32_t left, int32_t right)
{
return (int32_t)((uint32_t)left * (uint32_t)right);
}
uint16_t borland_random_below(uint16_t upper_bound)
{
assert(g_random_index < g_random_count);
assert(upper_bound == g_random_bounds[g_random_index]);
return g_random_values[g_random_index++];
}
BorlandReal48 borland_random_real48_registers(void)
{
assert(g_real_mode == REAL_MODE_HORIZONTAL_NUDGE);
return g_real_random;
}
BorlandReal48 borland_i32_to_real48(int32_t value)
{
if (g_real_mode == REAL_MODE_UPPER_CLAMP) {
assert(value == 1000);
} else {
assert(g_real_mode == REAL_MODE_HORIZONTAL_NUDGE);
assert(value == 3);
}
return g_real_integer;
}
BorlandReal48 borland_real48_multiply(
BorlandReal48 left, BorlandReal48 right)
{
static const BorlandReal48 three_fifths =
{{0x80, 0x9a, 0x99, 0x99, 0x99, 0x19}};
static const BorlandReal48 nudge_scale =
{{0x85, 0, 0, 0, 0, 0x20}};
if (g_real_mode == REAL_MODE_UPPER_CLAMP) {
assert(g_real_multiply_calls++ == 0);
assert_real48(left, g_real_integer);
assert_real48(right, three_fifths);
return g_real_result;
}
assert(g_real_mode == REAL_MODE_HORIZONTAL_NUDGE);
if (g_real_multiply_calls++ == 0) {
assert_real48(left, g_real_random);
assert_real48(right, nudge_scale);
return g_real_product;
}
assert_real48(left, g_real_integer);
assert_real48(right, g_real_factor);
return g_real_result;
}
BorlandReal48 borland_real48_subtract(
BorlandReal48 left, BorlandReal48 right)
{
static const BorlandReal48 nudge_offset =
{{0x84, 0, 0, 0, 0, 0x20}};
assert(g_real_mode == REAL_MODE_HORIZONTAL_NUDGE);
assert_real48(left, g_real_product);
assert_real48(right, nudge_offset);
return g_real_factor;
}
int32_t borland_real48_round_to_i32(BorlandReal48 value)
{
assert_real48(value, g_real_result);
return g_real_mode == REAL_MODE_UPPER_CLAMP ? 600 : 7;
}
void tdkpin_play_sound_if_not_tilted(uint16_t sound_number)
{
assert(g_sound_count < sizeof(g_sounds) / sizeof(g_sounds[0]));
g_sounds[g_sound_count++] = sound_number;
}
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;
}
void tdkpin_blit_bitmap_and_present(
HBITMAP16 bitmap,
INT16 height,
INT16 width,
INT16 y,
INT16 x,
HDC16 dc)
{
assert(bitmap == 0x4444);
assert(height == 18 && width == 21);
assert(y == 426 && x == 314 && dc == 0x3333);
g_blit_calls++;
}
void tdkpin_restore_background_region_a(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(dc == 0x3333);
if (height == 20) {
assert(width == 148 && y == 243 && x == 84);
} else {
assert(height == 30 && width == 284 && y == 232 && x == 371);
}
g_restore_a_calls++;
}
void tdkpin_restore_background_region_b(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height == 42 && width == 284 && x == 348 && dc == 0x3333);
assert(y == 360 || y == 405);
g_restore_b_calls++;
}
void tdkpin_present_background_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(dc == 0x3333);
if (height == 20) {
assert(width == 148 && y == 243 && x == 84);
} else if (height == 30) {
assert(width == 284 && y == 232 && x == 371);
} else {
assert(height == 42 && width == 284 && x == 348);
assert(y == 360 || y == 405);
}
g_present_calls++;
}
void tdkpin_draw_item_status(int16_t item, HDC16 dc)
{
assert(item == 0 && dc == 0x3333);
g_draw_item_calls++;
}
void tdkpin_draw_score(HDC16 dc, int32_t value_or_player)
{
assert(dc == 0x3333 && value_or_player == 0);
g_draw_score_calls++;
}
void tdkpin_update_status_display(uint8_t player, HDC16 dc)
{
assert(player == 1 && dc == 0x3333);
g_status_calls++;
}
static void set_random_script(
const uint16_t *bounds, const uint16_t *values, size_t count)
{
assert(count <= sizeof(g_random_values) / sizeof(g_random_values[0]));
memcpy(g_random_bounds, bounds, count * sizeof(bounds[0]));
memcpy(g_random_values, values, count * sizeof(values[0]));
g_random_count = count;
g_random_index = 0;
}
static void prepare_fixture(uint16_t scan_code)
{
memset(g_dgroup, 0, sizeof(g_dgroup));
memset(g_objects, 0, sizeof(g_objects));
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_set_dgroup_selector(0x7200);
g_window = win16_make_far_pointer(0x7000, 0);
g_event = win16_make_far_pointer(0x7000, 0x1000);
win16_write_u16(g_window, 4, 0x2222);
win16_write_u16(g_event, 8, scan_code);
win16_write_u16(g_event, 0x0a, 0xaaaa);
win16_write_u16(g_event, 0x0c, 0xbbbb);
win16_write_u16(win16_dgroup_pointer(0x0865), 0, 0x4444);
g_random_count = 0;
g_random_index = 0;
g_sound_count = 0;
g_get_dc_calls = 0;
g_release_dc_calls = 0;
g_blit_calls = 0;
g_restore_a_calls = 0;
g_restore_b_calls = 0;
g_present_calls = 0;
g_draw_item_calls = 0;
g_draw_score_calls = 0;
g_status_calls = 0;
g_real_mode = REAL_MODE_NONE;
g_real_multiply_calls = 0;
}
static void assert_event_consumed(void)
{
assert(win16_read_u16(win16_far_add_offset(g_event, 0x0a)) == 0);
assert(win16_read_u16(win16_far_add_offset(g_event, 0x0c)) == 0);
}
static void test_debug_keys_and_auxiliary_gate(void)
{
prepare_fixture(0x3c);
win16_write_u8(win16_dgroup_pointer(0x07cf), 1);
win16_write_u32(win16_dgroup_pointer(0x0855), 0, 0x12345678);
assert(tdkpin_handle_key_release(g_window, g_event) == 0);
assert(win16_read_u32(win16_dgroup_pointer(0x0855)) == 0);
assert(win16_read_u8(win16_dgroup_pointer(0x07c7)) == 1);
assert_event_consumed();
prepare_fixture(0x41);
win16_write_u8(win16_dgroup_pointer(0x07cf), 1);
win16_write_u32(win16_dgroup_pointer(0x3340), 0, 0x12345678);
win16_write_u32(win16_dgroup_pointer(0x3344), 0, 0x90abcdef);
win16_write_u32(g_window, 0x0baa, 1);
win16_write_u32(g_window, 0x0bae, 2);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u32(win16_dgroup_pointer(0x07d3)) == 0x12345678);
assert(win16_read_u32(win16_dgroup_pointer(0x07d7)) == 0x90abcdef);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 0);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0bae)) == 0);
prepare_fixture(0x3c);
win16_write_u8(win16_dgroup_pointer(0x07cf), 1);
win16_write_u16(g_window, 0x41, 1);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_dgroup_pointer(0x07c7)) == 0);
assert_event_consumed();
}
static void test_release_down_impulse_clamps(void)
{
static const uint16_t lower_bounds[] = {1000};
static const uint16_t lower_values[] = {80};
prepare_fixture(0x50);
win16_write_u8(win16_dgroup_pointer(0x07d0), 1);
win16_write_u32(g_window, 0x56, 1000);
win16_write_u32(g_window, 0x5a, 2);
win16_write_u32(g_window, 0x0baa, 0x12345678);
win16_write_u32(g_window, 0x0bae, (uint32_t)-2000);
set_random_script(lower_bounds, lower_values, 1);
tdkpin_handle_key_release(g_window, g_event);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == -998);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 0);
assert(win16_read_u32(win16_dgroup_pointer(0x07fb)) == (uint32_t)-998);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdd)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bde)) == 0);
assert(g_random_index == 1);
assert(g_sound_count == 1 && g_sounds[0] == 2);
assert(g_blit_calls == 1 && g_get_dc_calls == 1 && g_release_dc_calls == 1);
static const uint16_t upper_bounds[] = {1000};
static const uint16_t upper_values[] = {80};
prepare_fixture(0x50);
win16_write_u8(win16_dgroup_pointer(0x07d0), 1);
win16_write_u32(g_window, 0x56, 1000);
win16_write_u32(g_window, 0x5a, 2);
win16_write_u32(g_window, 0x0bae, (uint32_t)-399);
g_real_mode = REAL_MODE_UPPER_CLAMP;
set_random_script(upper_bounds, upper_values, 1);
tdkpin_handle_key_release(g_window, g_event);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == -601);
assert(g_real_multiply_calls == 1 && g_random_index == 1);
}
static void test_flipper_key_release(void)
{
prepare_fixture(0x1d);
win16_write_u8(win16_far_add_offset(g_window, 0x0bda), 1);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bda)) == 0);
prepare_fixture(0x1c);
win16_write_u8(win16_far_add_offset(g_window, 0x0bdb), 1);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdb)) == 0);
}
static void test_directional_and_multiball_nudges(void)
{
static const uint16_t left_bounds[] = {20, 10};
static const uint16_t left_values[] = {10, 9};
prepare_fixture(0x2a);
win16_write_u32(g_window, 0x5a, 3);
win16_write_u32(g_window, 0x0baa, 1000);
set_random_script(left_bounds, left_values, 2);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 880);
assert(win16_read_u16(win16_far_add_offset(g_window, 0x5e)) == 25);
assert(g_sound_count == 1 && g_sounds[0] == 19);
static const uint16_t right_bounds[] = {20, 10};
static const uint16_t right_values[] = {10, 9};
prepare_fixture(0x51);
win16_write_u32(g_window, 0x5a, 3);
win16_write_u8(win16_far_add_offset(g_window, 0x61), 2);
win16_write_u32(win16_dgroup_pointer(0x07f3), 0, 1000);
win16_write_u32(win16_dgroup_pointer(0x07f7), 0, 2000);
set_random_script(right_bounds, right_values, 2);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 120);
assert(win16_read_u32(win16_dgroup_pointer(0x07f3)) == 1120);
assert(win16_read_u32(win16_dgroup_pointer(0x07f7)) == 2120);
static const uint16_t space_bounds[] = {20, 21, 100, 10};
static const uint16_t space_values[] = {10, 12, 20, 9};
prepare_fixture(0x39);
win16_write_u32(g_window, 0x5a, 3);
win16_write_u8(win16_far_add_offset(g_window, 0x61), 2);
win16_write_u32(g_window, 0x0baa, 100);
win16_write_u32(g_window, 0x0bae, 200);
win16_write_u32(win16_dgroup_pointer(0x07f3), 0, 1000);
win16_write_u32(win16_dgroup_pointer(0x07f7), 0, 2000);
win16_write_u32(win16_dgroup_pointer(0x07fb), 0, 3000);
win16_write_u32(win16_dgroup_pointer(0x07ff), 0, 4000);
g_real_mode = REAL_MODE_HORIZONTAL_NUDGE;
set_random_script(space_bounds, space_values, 4);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0baa)) == 107);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == 50);
assert(win16_read_u32(win16_dgroup_pointer(0x07f3)) == 1006);
assert(win16_read_u32(win16_dgroup_pointer(0x07f7)) == 2006);
assert(win16_read_u32(win16_dgroup_pointer(0x07fb)) == 2790);
assert(win16_read_u32(win16_dgroup_pointer(0x07ff)) == 3790);
assert(g_real_multiply_calls == 2 && g_random_index == 4);
}
static void test_tilt_threshold(void)
{
static const uint16_t bounds[] = {20, 10};
static const uint16_t values[] = {10, 0};
prepare_fixture(0x2a);
win16_write_u32(g_window, 0x5a, 1);
win16_write_u16(g_window, 0x5e, 14);
win16_write_u8(win16_far_add_offset(g_window, 0x0bda), 1);
win16_write_u8(win16_far_add_offset(g_window, 0x0bdb), 1);
set_random_script(bounds, values, 2);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u16(win16_far_add_offset(g_window, 0x5e)) == 39);
assert(win16_read_u8(win16_dgroup_pointer(0x07c6)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bda)) == 0);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdb)) == 0);
assert(g_sound_count == 2 && g_sounds[0] == 19 && g_sounds[1] == 20);
assert(g_restore_a_calls == 1 && g_present_calls == 1);
assert(g_get_dc_calls == 1 && g_release_dc_calls == 1);
}
static void test_sound_toggle(void)
{
prepare_fixture(0x58);
win16_write_u16(win16_dgroup_pointer(0x43ee), 0, 1);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_dgroup_pointer(0x43f2)) == 1);
prepare_fixture(0x58);
win16_write_u8(win16_dgroup_pointer(0x43f2), 1);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_dgroup_pointer(0x43f2)) == 0);
prepare_fixture(0x58);
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_dgroup_pointer(0x43f2)) == 0);
}
static void test_add_player_copies_all_state(void)
{
prepare_fixture(0x4e);
win16_write_u8(win16_far_add_offset(g_window, 0x0bde), 1);
win16_write_u8(win16_dgroup_pointer(0x07d0), 1);
win16_write_u32(win16_dgroup_pointer(0x0982), 0, 0x11111111);
win16_write_u32(win16_dgroup_pointer(0x0986), 0, 0x22222222);
win16_write_u32(win16_dgroup_pointer(0x098a), 0, 0x33333333);
win16_write_u32(win16_dgroup_pointer(0x098e), 0, 0x44444444);
for (uint16_t id = 1; id <= 175; id++) {
win16_write_u8(
win16_far_add_offset(g_window, (uint16_t)(0x03cc + id)),
(uint8_t)(id ^ 0x5a));
win16_write_u16(
g_window,
(uint16_t)(0x0a42 + id * 2u),
(uint16_t)(id * 257u));
}
tdkpin_handle_key_release(g_window, g_event);
assert(win16_read_u8(win16_dgroup_pointer(0x07d0)) == 2);
assert(win16_read_u8(win16_dgroup_pointer(0x07c9)) == 0);
assert(win16_read_u8(win16_dgroup_pointer(0x07c5)) == 1);
assert(win16_read_u8(win16_dgroup_pointer(0x0874)) == 0);
assert(win16_read_u8(win16_dgroup_pointer(0x07d1)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x61)) == 1);
assert(g_sound_count == 1 && g_sounds[0] == 1);
for (uint16_t player = 1; player <= 4; player++) {
assert(win16_read_u16(win16_dgroup_pointer(
(uint16_t)(0x081f + player * 2u))) == 0xffff);
for (uint16_t id = 1; id <= 175; id++) {
uint8_t expected_byte = (uint8_t)(id ^ 0x5a);
uint16_t expected_word = (uint16_t)(id * 257u);
assert(win16_read_u8(win16_far_add_offset(
g_window,
(uint16_t)(0x0061 + player * 0x00afu + id))) ==
expected_byte);
assert(win16_read_u16(win16_far_add_offset(
g_window,
(uint16_t)(0x036c + player * 0x015eu + id * 2u))) ==
expected_word);
}
}
for (uint16_t id = 1; id <= 175; id++) {
Win16FarPtr record = win16_dgroup_pointer(
(uint16_t)(0x095b + id * 0x53u));
assert(win16_read_u8(win16_far_add_offset(record, 0x34)) ==
(uint8_t)(id ^ 0x5a));
assert(win16_read_u16(win16_far_add_offset(record, 0x43)) ==
(uint16_t)(id * 257u));
}
assert(win16_read_u32(win16_dgroup_pointer(0x0982)) == 0x11111111);
assert(win16_read_u32(win16_dgroup_pointer(0x0986)) == 0x22222222);
assert(win16_read_u32(win16_dgroup_pointer(0x098a)) == 0);
assert(win16_read_u32(win16_dgroup_pointer(0x098e)) == 0);
assert(g_restore_b_calls == 2 && g_restore_a_calls == 1);
assert(g_present_calls == 3 && g_draw_item_calls == 4);
assert(g_draw_score_calls == 1 && g_status_calls == 1);
assert(g_get_dc_calls == 5 && g_release_dc_calls == 5);
}
int main(void)
{
test_debug_keys_and_auxiliary_gate();
test_release_down_impulse_clamps();
test_flipper_key_release();
test_directional_and_multiball_nudges();
test_tilt_threshold();
test_sound_toggle();
test_add_player_copies_all_state();
return 0;
}
@@ -0,0 +1,221 @@
#include "../tdkpin_mechanism_render.h"
#include <assert.h>
#include <string.h>
uint8_t g_current_player;
static uint8_t g_stack[0x400];
static uint8_t g_object[0x0c00];
static uint8_t g_dgroup[0x5000];
static Win16FarPtr g_receiver;
static unsigned g_select_calls;
static unsigned g_blit_calls;
static unsigned g_refresh_calls;
static unsigned g_sound_calls;
static unsigned g_delete_dc_calls;
static int32_t g_state;
HDC16 GetDC16(HWND16 window)
{
assert(window == 0x2222);
return 0x3333;
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
static unsigned calls;
assert(dc == 0x3333);
HDC16 result = (HDC16)(0x4001 + calls);
calls = (calls + 1u) % 2u;
return result;
}
HBITMAP16 CreateCompatibleBitmap16(HDC16 dc, INT16 width, INT16 height)
{
assert(dc == 0x3333 && width == 91 && height == 90);
return 0x5000;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
unsigned call = g_select_calls++;
if (call == 0) {
assert(dc == 0x4001 && object == 0x6000);
return 0x7001;
}
if (call == 1) {
assert(dc == 0x4002 && object == 0x5000);
return 0x7002;
}
if (g_state >= 6 && call == 2) {
assert(dc == 0x4001 && object == 0x6001);
return 0x6000;
}
if (g_state >= 6 && call == 3) {
assert(dc == 0x4001 && object == 0x6000);
return 0x6001;
}
unsigned cleanup_base = g_state >= 6 ? 4u : 2u;
if (call == cleanup_base) {
assert(dc == 0x4001 && object == 0x7001);
return 0x6000;
}
assert(call == cleanup_base + 1u);
assert(dc == 0x4002 && object == 0x7002);
return 0x5000;
}
static const INT16 state_one_positions[5][2] = {
{9, 41}, {23, 11}, {56, 14}, {63, 47}, {35, 63}};
static const INT16 state_six_positions[5][2] = {
{8, 35}, {26, 8}, {59, 18}, {59, 50}, {27, 61}};
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
unsigned call = g_blit_calls++;
if (call == 0) {
assert(destination == 0x4002 && destination_x == 0);
assert(destination_y == 0 && width == 91 && height == 90);
assert(source == 0x4001);
assert(source_x == (g_state < 6 ? 0 : 79));
assert(source_y == (g_state < 6 ? 0 : 33));
assert(raster_operation == 0x00cc0020u);
return 1;
}
if (call <= 10) {
unsigned target = (call - 1u) / 2u;
const INT16 (*positions)[2] =
g_state == 1 ? state_one_positions : state_six_positions;
assert(destination == 0x4002);
assert(destination_x == positions[target][0]);
assert(destination_y == positions[target][1]);
assert(width == 17 && height == 17 && source == 0x4001);
assert(source_x == 455);
if ((call & 1u) != 0) {
assert(source_y == 17 && raster_operation == 0x008800c6u);
} else {
assert(source_y == 0 && raster_operation == 0x00ee0086u);
}
return 1;
}
assert(call == 11);
assert(destination == 0x3333 && destination_x == 79);
assert(destination_y == 33 && width == 91 && height == 90);
assert(source == 0x4002 && source_x == 0 && source_y == 0);
assert(raster_operation == 0x00cc0020u);
return 1;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(object == 0x5000);
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(dc == (g_delete_dc_calls == 0 ? 0x4002 : 0x4001));
g_delete_dc_calls++;
return 1;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == 0x2222 && dc == 0x3333);
return 1;
}
void tdkpin_refresh_player_item(uint16_t caller_bp, uint16_t item)
{
assert(caller_bp == 0x0100 && item == 15 + g_refresh_calls);
g_refresh_calls++;
}
void tdkpin_play_sound_if_not_tilted(uint16_t sound_number)
{
assert(sound_number == 11);
g_sound_calls++;
}
static void prepare_fixture(int32_t state)
{
memset(g_stack, 0, sizeof(g_stack));
memset(g_object, 0, sizeof(g_object));
memset(g_dgroup, 0, sizeof(g_dgroup));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_set_stack_state(0x7000, 0);
g_receiver = win16_make_far_pointer(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
win16_write_u16(g_receiver, 4, 0x2222);
win16_write_u16(win16_dgroup_pointer(0x0859), 0, 0x6000);
win16_write_u16(win16_dgroup_pointer(0x085d), 0, 0x6001);
win16_write_u32(win16_dgroup_pointer(0x0855), 0, (uint32_t)state);
static const uint16_t words[5] = {
0x3371, 0x33c4, 0x3417, 0x346a, 0x34bd};
for (uint16_t index = 0; index < 5; index++) {
win16_write_u16(
win16_dgroup_pointer(words[index]), 0, (uint16_t)(index + 1));
}
g_current_player = 2;
for (uint16_t index = 0; index < 5; index++) {
win16_write_u8(
win16_far_add_offset(
g_receiver, (uint16_t)(0x0476 + 2u * 0x14u + index)),
(uint8_t)(0xa0 + index));
}
g_state = state;
g_select_calls = 0;
g_blit_calls = 0;
g_refresh_calls = 0;
g_sound_calls = 0;
g_delete_dc_calls = 0;
}
static void test_state_one(void)
{
prepare_fixture(1);
tdkpin_rotate_and_draw_targets(0x0100);
assert(g_blit_calls == 12 && g_select_calls == 4);
assert(g_sound_calls == 1 && g_refresh_calls == 0);
}
static void test_state_six_rotation(void)
{
prepare_fixture(6);
tdkpin_rotate_and_draw_targets(0x0100);
assert(g_blit_calls == 12 && g_select_calls == 6);
assert(g_sound_calls == 0 && g_refresh_calls == 5);
static const uint16_t words[5] = {
0x3371, 0x33c4, 0x3417, 0x346a, 0x34bd};
static const uint16_t expected_words[5] = {2, 3, 4, 5, 1};
for (uint16_t index = 0; index < 5; index++) {
assert(win16_read_u16(win16_dgroup_pointer(words[index])) ==
expected_words[index]);
assert(win16_read_u8(win16_far_add_offset(
g_receiver,
(uint16_t)(0x0476 + 2u * 0x14u + index))) ==
(uint8_t)(0xa0 + (index + 1u) % 5u));
}
}
int main(void)
{
test_state_one();
test_state_six_rotation();
return 0;
}
@@ -0,0 +1,169 @@
#include "../tdkpin_object_windows.h"
#include <assert.h>
Win16FarPtr g_object_windows_creating_object;
uint16_t g_object_windows_thunk_code_selector;
Win16FarPtr g_object_windows_free_thunk;
Win16FarPtr g_object_windows_dispatch_proc;
Win16FarPtr g_object_windows_message_hook;
static Win16FarPtr g_expected_handler;
static Win16FarPtr g_expected_object;
static unsigned g_handler_calls;
static unsigned g_hook_calls;
static ObjectWindowsMessage g_hook_message;
static Win16FarPtr g_event_pointer;
void win16_call_object_message_handler(
Win16FarPtr procedure,
Win16FarPtr object,
ObjectWindowsMessage *message)
{
assert(procedure == g_expected_handler);
assert(object == g_expected_object);
assert(message->window == 0x77);
assert(message->wparam == 0x3344);
assert(message->lparam == (LPARAM16)0x89abcdefu);
message->result = (LRESULT16)0x12345678u;
g_handler_calls++;
}
void win16_call_object_message_hook(
Win16FarPtr procedure,
Win16FarPtr object,
Win16FarPtr handler,
HWND16 window,
UINT16 message,
WPARAM16 wparam,
LPARAM16 lparam)
{
assert(procedure == g_object_windows_message_hook);
assert(object == g_expected_object && handler == g_expected_handler);
g_hook_message = (ObjectWindowsMessage){
window, message, wparam, lparam, 0};
g_hook_calls++;
}
void win16_call_object_event_handler(
Win16FarPtr procedure, Win16FarPtr object, Win16FarPtr event)
{
assert(procedure == g_expected_handler);
assert(object == g_expected_object);
g_event_pointer = event;
}
static void write_far(uint16_t offset, Win16FarPtr pointer)
{
win16_write_u16(
win16_dgroup_pointer(offset), 0, win16_far_offset(pointer));
win16_write_u16(
win16_dgroup_pointer((uint16_t)(offset + 2)),
0,
win16_far_selector(pointer));
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t object_bytes[64];
static uint8_t event_bytes[32];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(
0x7000, object_bytes, sizeof(object_bytes), true);
win16_bind_segment(
0x7100, event_bytes, sizeof(event_bytes), true);
const uint16_t vmt = 0x0100;
const uint16_t derived_table = 0x0200;
const uint16_t parent_table = 0x0300;
Win16FarPtr fallback = win16_make_far_pointer(0x6100, 0x1111);
Win16FarPtr derived = win16_make_far_pointer(0x6200, 0x2222);
Win16FarPtr parent = win16_make_far_pointer(0x6300, 0x3333);
write_far((uint16_t)(vmt + 0x0c), fallback);
win16_write_u16(
win16_dgroup_pointer((uint16_t)(vmt + 4)), 0, derived_table);
win16_write_u16(win16_dgroup_pointer(derived_table), 0, parent_table);
win16_write_u16(win16_dgroup_pointer(derived_table + 2), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(derived_table + 4), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(derived_table + 6), 0, 1);
win16_write_u16(win16_dgroup_pointer(derived_table + 8), 0, 0x0100);
write_far((uint16_t)(derived_table + 10), derived);
win16_write_u16(win16_dgroup_pointer(parent_table), 0, 0);
win16_write_u16(win16_dgroup_pointer(parent_table + 2), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(parent_table + 4), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(parent_table + 6), 0, 1);
win16_write_u16(win16_dgroup_pointer(parent_table + 8), 0, 0x0200);
write_far((uint16_t)(parent_table + 10), parent);
assert(object_windows_resolve_message_handler(vmt, 0x0100, 0x0c) ==
win16_dgroup_pointer(derived_table + 10));
assert(object_windows_resolve_message_handler(vmt, 0x0200, 0x0c) ==
win16_dgroup_pointer(parent_table + 10));
assert(win16_read_u16(win16_dgroup_pointer(derived_table + 2)) ==
0x0200);
assert(win16_read_u16(win16_dgroup_pointer(derived_table + 4)) ==
parent_table + 10);
/* Cache lookup still works after the table contents become unusable. */
win16_write_u16(win16_dgroup_pointer(derived_table + 6), 0, 0xffff);
assert(object_windows_resolve_message_handler(vmt, 0x0200, 0x0c) ==
win16_dgroup_pointer(parent_table + 10));
win16_write_u16(win16_dgroup_pointer(derived_table + 6), 0, 1);
assert(object_windows_resolve_message_handler(vmt, 0x0999, 0x0c) ==
win16_dgroup_pointer(vmt + 0x0c));
Win16FarPtr object = win16_make_far_pointer(0x7000, 4);
win16_write_u16(object, 0, vmt);
g_expected_object = object;
g_object_windows_message_hook =
win16_make_far_pointer(0x6400, 0x4444);
g_expected_handler = parent;
g_handler_calls = 0;
g_hook_calls = 0;
LRESULT16 result = object_dispatch_window_or_dialog_proc(
object,
0x77,
0x0200,
0x3344,
(LPARAM16)0x89abcdefu);
assert(result == (LRESULT16)0x12345678u);
assert(win16_read_u16(win16_far_add_offset(object, 4)) == 0x77);
assert(g_handler_calls == 1 && g_hook_calls == 1);
assert(g_hook_message.message == 0x0200);
assert(g_hook_message.lparam == (LPARAM16)0x89abcdefu);
g_object_windows_message_hook = win16_make_far_pointer(0, 0x9999);
g_expected_handler = fallback;
g_hook_calls = 0;
(void)object_dispatch_window_or_dialog_proc(
object,
0x77,
0xffff,
0x3344,
(LPARAM16)0x89abcdefu);
assert(g_hook_calls == 0);
Win16FarPtr event = win16_make_far_pointer(0x7100, 2);
win16_write_u16(event, 0, 0x77);
win16_write_u16(event, 2, 0x0100);
win16_write_u16(event, 4, 0x3344);
win16_write_u16(event, 6, 0xcdef);
win16_write_u16(event, 8, 0x89ab);
g_object_windows_message_hook =
win16_make_far_pointer(0x6400, 0x4444);
g_expected_handler = derived;
g_hook_calls = 0;
g_event_pointer = 0;
object_windows_dispatch_event(0x0c, 0x0100, event, object);
assert(g_event_pointer == event);
assert(g_hook_calls == 1);
assert(g_hook_message.window == 0x77);
assert(g_hook_message.message == 0x0100);
assert(g_hook_message.wparam == 0x3344);
assert(g_hook_message.lparam == (LPARAM16)0x89abcdefu);
return 0;
}
@@ -0,0 +1,94 @@
#include "../tdkpin_message_pump.h"
#include <assert.h>
static MSG16 g_queue[8];
static unsigned g_queue_count;
static unsigned g_queue_index;
static UINT16 g_peek_maximum;
static unsigned g_translate_calls;
static unsigned g_dispatch_calls;
static Win16FarPtr g_closed_window;
BOOL16 PeekMessage16(
MSG16 *message,
HWND16 window,
UINT16 minimum_message,
UINT16 maximum_message,
UINT16 remove_message)
{
assert(window == 0 && minimum_message == 0 && remove_message == 1);
g_peek_maximum = maximum_message;
if (g_queue_index == g_queue_count) {
return 0;
}
*message = g_queue[g_queue_index++];
return 1;
}
BOOL16 TranslateMessage16(const MSG16 *message)
{
assert(message->message == 0x0020);
g_translate_calls++;
return 1;
}
LRESULT16 DispatchMessage16(const MSG16 *message)
{
assert(message->message == 0x0020);
g_dispatch_calls++;
return 0;
}
void object_windows_close_if_allowed(Win16FarPtr window)
{
g_closed_window = window;
}
static void reset_queue(void)
{
g_queue_count = 4;
g_queue_index = 0;
g_queue[0].message = 0x0580;
g_queue[1].message = 0x0100;
g_queue[2].message = 0x0101;
g_queue[3].message = 0x0020;
g_translate_calls = 0;
g_dispatch_calls = 0;
g_closed_window = 0;
}
int main(void)
{
win16_set_indeterminate_u16(0xbeef);
Win16FarPtr window = win16_make_far_pointer(0x7000, 2);
reset_queue();
tdkpin_drain_messages_except_input();
assert(g_queue_index == 4 && g_peek_maximum == 0x32);
assert(g_translate_calls == 1 && g_dispatch_calls == 1);
reset_queue();
tdkpin_close_variant_a(window);
assert(g_closed_window == window);
reset_queue();
tdkpin_close_variant_b(window);
assert(g_closed_window == window);
ObjectWindowsMessage message = {0};
reset_queue();
message.lparam = (LPARAM16)0x001c0000u;
assert(tdkpin_conditional_close_variant_a(window, &message) == 0xbeef);
assert(g_closed_window == 0 && g_queue_index == 0);
message.lparam = (LPARAM16)0x001d0000u;
assert(tdkpin_conditional_close_variant_a(window, &message) == 0xbeef);
assert(g_closed_window == window);
reset_queue();
message.lparam = (LPARAM16)0x003b0000u;
assert(tdkpin_conditional_close_variant_b(window, &message) == 0xbeef);
assert(g_closed_window == 0 && g_queue_index == 0);
message.lparam = (LPARAM16)0x003a0000u;
assert(tdkpin_conditional_close_variant_b(window, &message) == 0xbeef);
assert(g_closed_window == window);
return 0;
}
@@ -0,0 +1,373 @@
#include "../tdkpin_multimedia.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
Win16FarPtr g_multimedia_procedures[11];
HINSTANCE16 g_multimedia_module;
uint8_t g_multimedia_initialized;
uint8_t g_multimedia_pending;
uint16_t g_multimedia_version;
uint8_t g_multimedia_auto_subclassed;
uint8_t g_multimedia_argument_2;
uint8_t g_multimedia_argument_1;
uint8_t g_multimedia_legacy_windows;
char g_multimedia_library_path[21] = "CTL3D.DLL";
Win16FarPtr g_object_windows_application;
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
uint16_t g_win16_show_command;
Win16FarPtr g_win16_message_box;
Win16FarPtr g_object_windows_dispatch_proc;
uint16_t g_object_windows_thunk_code_selector;
Win16FarPtr g_object_windows_free_thunk;
Win16FarPtr g_object_windows_message_hook;
static HINSTANCE16 g_freed_module;
static Win16FarPtr g_called_procedure;
static uint16_t g_called_argument;
static unsigned g_sequence;
static bool g_check_sequence;
static Win16FarPtr g_destroyed_application;
static HINSTANCE16 g_load_result;
static uint16_t g_register_result;
static uint16_t g_version_result;
static uint16_t g_enabled_result;
static uint16_t g_auto_result;
static int g_search_success_attempt;
static unsigned g_search_calls;
static char g_search_paths[5][256];
static unsigned g_load_calls;
static unsigned g_proc_calls;
static UINT16 g_error_modes[4];
static unsigned g_error_mode_calls;
static uint8_t g_dgroup[0x5000];
static void write_far_string(Win16FarPtr destination, const char *source)
{
size_t length = strlen(source);
for (size_t index = 0; index <= length; index++) {
win16_write_u8(
win16_far_add_offset(destination, (uint16_t)index),
(uint8_t)source[index]);
}
}
static void read_far_string(
Win16FarPtr source, char *destination, size_t capacity)
{
size_t index = 0;
while (index + 1 < capacity) {
uint8_t byte = win16_read_u8(
win16_far_add_offset(source, (uint16_t)index));
destination[index++] = (char)byte;
if (byte == 0) {
return;
}
}
destination[capacity - 1] = 0;
}
BOOL16 FreeLibrary16(HINSTANCE16 module)
{
if (g_check_sequence) {
assert(++g_sequence == 2);
}
g_freed_module = module;
return 1;
}
Win16Handle GetCurrentTask16(void)
{
return 0x1234;
}
uint16_t win16_call_pascal_u16(Win16FarPtr procedure, uint16_t argument)
{
if (g_check_sequence) {
assert(++g_sequence == 1);
}
g_called_procedure = procedure;
g_called_argument = argument;
if (procedure == g_multimedia_procedures[6]) {
return g_register_result;
}
if (procedure == g_multimedia_procedures[0]) {
assert(argument == g_win16_instance);
return g_auto_result;
}
return 0xabcd;
}
uint16_t win16_call_pascal_u16_no_args(Win16FarPtr procedure)
{
if (procedure == g_multimedia_procedures[5]) {
return g_version_result;
}
assert(procedure == g_multimedia_procedures[3]);
return g_enabled_result;
}
void object_windows_application_destruct(
Win16FarPtr object, uint16_t vmt)
{
assert(++g_sequence == 3);
assert(vmt == 0);
g_destroyed_application = object;
}
void borland_finish_object_destructor(
BorlandObjectCallFrame *frame, uint16_t vmt_field_offset)
{
assert(++g_sequence == 4);
assert(frame->object == g_destroyed_application);
assert(frame->vmt_argument == 0x0200 && vmt_field_offset == 0);
}
UINT16 SetErrorMode16(UINT16 mode)
{
g_error_modes[g_error_mode_calls++] = mode;
return 0;
}
HINSTANCE16 LoadLibrary16(const char *library_name)
{
assert(strcmp(library_name, g_multimedia_library_path) == 0);
g_load_calls++;
return g_load_result;
}
Win16FarPtr GetProcAddress16(
HINSTANCE16 module, const char *procedure_name)
{
static const char *const names[11] = {
"CTL3DAUTOSUBCLASS", "CTL3DCOLORCHANGE", "CTL3DCTLCOLOREX",
"CTL3DENABLED", "CTL3DDLGFRAMEPAINT", "CTL3DGETVER",
"CTL3DREGISTER", "CTL3DSUBCLASSDLG", "CTL3DSUBCLASSDLGEX",
"CTL3DSUBCLASSCTL", "CTL3DUNREGISTER",
};
assert(module == g_load_result && g_proc_calls < 11);
unsigned index = 0;
while (index < 11 && strcmp(procedure_name, names[index]) != 0) {
index++;
}
assert(index < 11);
g_proc_calls++;
return win16_make_far_pointer(
0x9000, (uint16_t)(0x100 + index));
}
Win16FarPtr multimedia_search_file(
Win16FarPtr search_path, Win16FarPtr file_name, Win16FarPtr result)
{
char file_name_text[32];
read_far_string(file_name, file_name_text, sizeof(file_name_text));
assert(strcmp(file_name_text, g_multimedia_library_path) == 0);
assert(g_search_calls < 5);
read_far_string(
search_path, g_search_paths[g_search_calls],
sizeof(g_search_paths[g_search_calls]));
bool found = (int)g_search_calls == g_search_success_attempt;
g_search_calls++;
win16_write_u8(result, found ? 'X' : 0);
win16_write_u8(win16_far_add_offset(result, 1), 0);
return result;
}
UINT16 GetWindowsDirectoryFar16(Win16FarPtr buffer, UINT16 size)
{
assert(size == 0xff);
write_far_string(buffer, "WIN");
return 3;
}
UINT16 GetSystemDirectoryFar16(Win16FarPtr buffer, UINT16 size)
{
assert(size == 0xff);
write_far_string(buffer, "SYS");
return 3;
}
HINSTANCE16 GetModuleHandle16(Win16FarPtr module_name)
{
assert(module_name == win16_make_far_pointer(0x7000, 0x1234));
return 0x5555;
}
UINT16 GetModuleFileNameFar16(
HINSTANCE16 module, Win16FarPtr buffer, UINT16 size)
{
assert(module == 0x5555 && size == 0x100);
write_far_string(buffer, "C:\\APP\\GAME.EXE");
return 15;
}
uint16_t multimedia_split_path(
Win16FarPtr extension,
Win16FarPtr file_name,
Win16FarPtr directory,
Win16FarPtr input)
{
char input_text[32];
read_far_string(input, input_text, sizeof(input_text));
assert(strcmp(input_text, "C:\\APP\\GAME.EXE") == 0);
assert(extension != 0 && file_name != 0 && directory == input);
write_far_string(directory, "MOD");
return 7;
}
Win16FarPtr multimedia_environment_value(Win16FarPtr name)
{
char name_text[8];
read_far_string(name, name_text, sizeof(name_text));
assert(strcmp(name_text, "PATH") == 0);
return win16_make_far_pointer(0x7000, 0x4700);
}
static void fill_procedures(void)
{
for (unsigned index = 0; index < 11; index++) {
g_multimedia_procedures[index] =
win16_make_far_pointer(0x8000, (uint16_t)(0x100 + index));
}
}
static void reset_loader(void)
{
for (unsigned index = 0; index < 11; index++) {
g_multimedia_procedures[index] = 0;
}
g_multimedia_initialized = 0;
g_multimedia_module = 0;
g_multimedia_version = 0;
g_multimedia_auto_subclassed = 0;
g_multimedia_argument_1 = 0;
g_multimedia_argument_2 = 0;
g_multimedia_legacy_windows = 0;
g_load_result = 64;
g_register_result = 1;
g_version_result = 9;
g_enabled_result = 1;
g_auto_result = 1;
g_search_success_attempt = 0;
g_search_calls = 0;
g_load_calls = 0;
g_proc_calls = 0;
g_error_mode_calls = 0;
g_freed_module = 0;
write_far_string(
win16_make_far_pointer(0x7000, 0x478d),
g_multimedia_library_path);
}
int main(void)
{
uint8_t stack_anchor = 0;
uintptr_t stack_base = (uintptr_t)&stack_anchor - 0x8000;
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7000);
win16_bind_segment(0x7200, (void *)stack_base, 0x10000, true);
write_far_string(win16_make_far_pointer(0x7000, 0x046a), "PATH");
write_far_string(win16_make_far_pointer(0x7000, 0x4700), "ENV");
write_far_string(
win16_make_far_pointer(0x7000, 0x478d),
g_multimedia_library_path);
fill_procedures();
g_multimedia_module = 31;
g_multimedia_initialized = 1;
g_freed_module = 0;
multimedia_unload();
assert(g_freed_module == 0 && g_multimedia_module == 31);
assert(!g_multimedia_initialized);
for (unsigned index = 0; index < 11; index++) {
assert(g_multimedia_procedures[index] == 0);
}
fill_procedures();
g_multimedia_module = 64;
g_multimedia_initialized = 1;
Win16FarPtr terminator = g_multimedia_procedures[10];
g_called_procedure = 0;
multimedia_finalize_current_task();
assert(g_called_procedure == terminator && g_called_argument == 0x1234);
assert(g_freed_module == 64 && !g_multimedia_initialized);
fill_procedures();
g_multimedia_initialized = 0;
g_called_procedure = 0;
multimedia_finalize_current_task();
assert(g_called_procedure == 0 && g_multimedia_procedures[0] != 0);
fill_procedures();
g_multimedia_module = 64;
g_multimedia_initialized = 1;
g_sequence = 0;
g_check_sequence = true;
Win16FarPtr application = win16_make_far_pointer(0x7000, 2);
multimedia_application_destruct(application, 0x0200);
g_check_sequence = false;
assert(g_sequence == 4 && g_destroyed_application == application);
reset_loader();
g_win16_instance = 0x4444;
g_multimedia_pending = 0x7f;
assert(multimedia_initialize_export(1, 2, 1, 0x1234, 0x7000) == 1);
assert(g_multimedia_pending == 0);
assert(g_multimedia_argument_1 == 1 && g_multimedia_argument_2 == 2);
assert(g_multimedia_auto_subclassed == 1);
assert(g_proc_calls == 11 && g_error_mode_calls == 2);
assert(g_error_modes[0] == 0x8000 && g_error_modes[1] == 0);
assert(multimedia_initialize(9, 8, 0, 0x1234, 0x7000) == 1);
assert(g_load_calls == 1); /* Already initialized: no second load. */
reset_loader();
g_load_result = 31;
assert(multimedia_initialize(1, 2, 1, 0x1234, 0x7000) == 0);
assert(g_proc_calls == 0 && g_multimedia_module == 31);
reset_loader();
g_register_result = 0;
assert(multimedia_initialize(1, 2, 1, 0x1234, 0x7000) == 0);
assert(g_freed_module == 64 && g_multimedia_procedures[0] == 0);
reset_loader();
g_version_result = 8;
assert(multimedia_initialize(1, 2, 1, 0x1234, 0x7000) == 0);
assert(g_freed_module == 64);
reset_loader();
g_multimedia_legacy_windows = 1;
g_search_success_attempt = -1;
assert(multimedia_initialize(1, 2, 1, 0x1234, 0x7000) == 0);
assert(g_load_calls == 0 && g_multimedia_module == 0);
reset_loader();
g_multimedia_legacy_windows = 1;
assert(multimedia_initialize(3, 4, 1, 0x1234, 0x7000) == 1);
assert(g_proc_calls == 10 && g_multimedia_procedures[8] == 0);
assert(g_multimedia_auto_subclassed == 0);
assert(g_error_mode_calls == 0);
for (int attempt = 0; attempt < 5; attempt++) {
g_search_success_attempt = attempt;
g_search_calls = 0;
assert(multimedia_locate_library(
win16_make_far_pointer(0x7000, 0x1234),
win16_make_far_pointer(0x7000, 0x478d)));
assert(g_search_calls == (unsigned)attempt + 1);
}
assert(strcmp(g_search_paths[0], "") == 0);
assert(strcmp(g_search_paths[1], "WIN") == 0);
assert(strcmp(g_search_paths[2], "SYS") == 0);
assert(strcmp(g_search_paths[3], "MOD") == 0);
assert(strcmp(g_search_paths[4], "ENV") == 0);
g_search_success_attempt = -1;
g_search_calls = 0;
assert(!multimedia_locate_library(
win16_make_far_pointer(0x7000, 0x1234),
win16_make_far_pointer(0x7000, 0x478d)));
assert(g_search_calls == 5);
return 0;
}
@@ -0,0 +1,178 @@
#include "../tdkpin_multimedia.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
static Win16DosFileAttributesResult g_attribute_results[16];
static unsigned g_attribute_result_count;
static unsigned g_attribute_result_index;
static char g_attribute_names[16][80];
static unsigned g_oem_calls;
static Win16FarPtr g_environment;
static void write_far_string(Win16FarPtr destination, const char *source)
{
size_t length = strlen(source);
for (size_t index = 0; index <= length; index++) {
win16_write_u8(
win16_far_add_offset(destination, (uint16_t)index),
(uint8_t)source[index]);
}
}
static void read_far_string(
Win16FarPtr source, char *destination, size_t capacity)
{
size_t index = 0;
while (index + 1 < capacity) {
uint8_t byte = win16_read_u8(
win16_far_add_offset(source, (uint16_t)index));
destination[index++] = (char)byte;
if (byte == 0) {
return;
}
}
destination[capacity - 1] = 0;
}
INT16 AnsiToOem16(Win16FarPtr source, Win16FarPtr destination)
{
assert(source == destination);
g_oem_calls++;
return 1;
}
Win16DosFileAttributesResult win16_dos_get_file_attributes(
Win16FarPtr oem_name)
{
assert(g_attribute_result_index < g_attribute_result_count);
read_far_string(
oem_name,
g_attribute_names[g_attribute_result_index],
sizeof(g_attribute_names[g_attribute_result_index]));
return g_attribute_results[g_attribute_result_index++];
}
Win16FarPtr GetDOSEnvironment16(void)
{
return g_environment;
}
static void reset_attributes(void)
{
g_attribute_result_count = 0;
g_attribute_result_index = 0;
g_oem_calls = 0;
}
static void assert_far_string(Win16FarPtr string, const char *expected)
{
char actual[256];
read_far_string(string, actual, sizeof(actual));
assert(strcmp(actual, expected) == 0);
}
int main(void)
{
static uint8_t data[0x1000];
uint8_t stack_anchor = 0;
uintptr_t stack_base = (uintptr_t)&stack_anchor - 0x8000;
win16_reset_segment_bindings();
win16_bind_segment(0x7000, data, sizeof(data), true);
win16_bind_segment(0x7200, (void *)stack_base, 0x10000, true);
Win16FarPtr name = win16_make_far_pointer(0x7000, 0x0100);
Win16FarPtr search = win16_make_far_pointer(0x7000, 0x0200);
Win16FarPtr result = win16_make_far_pointer(0x7000, 0x0300);
char long_name[96];
memset(long_name, 'A', sizeof(long_name) - 1);
long_name[sizeof(long_name) - 1] = 0;
write_far_string(name, long_name);
reset_attributes();
g_attribute_result_count = 1;
g_attribute_results[0] =
(Win16DosFileAttributesResult){0x20, false};
Win16DosFileAttributesResult attributes =
borland_dos_get_file_attributes_ansi(name);
assert(!attributes.carry && attributes.attributes == 0x20);
assert(strlen(g_attribute_names[0]) == 79 && g_oem_calls == 1);
write_far_string(name, "FILE.DLL");
write_far_string(search, "C:\\ONE;D:;E:\\TWO");
reset_attributes();
g_attribute_result_count = 4;
g_attribute_results[0] =
(Win16DosFileAttributesResult){0, true};
g_attribute_results[1] =
(Win16DosFileAttributesResult){0x10, false}; /* directory */
g_attribute_results[2] =
(Win16DosFileAttributesResult){0, true};
g_attribute_results[3] =
(Win16DosFileAttributesResult){0x20, false};
assert(multimedia_search_file(search, name, result) == result);
assert(strcmp(g_attribute_names[0], "FILE.DLL") == 0);
assert(strcmp(g_attribute_names[1], "C:\\ONE\\FILE.DLL") == 0);
assert(strcmp(g_attribute_names[2], "D:FILE.DLL") == 0);
assert(strcmp(g_attribute_names[3], "E:\\TWO\\FILE.DLL") == 0);
assert_far_string(result, "E:\\TWO\\FILE.DLL");
write_far_string(search, "");
reset_attributes();
g_attribute_result_count = 1;
g_attribute_results[0] =
(Win16DosFileAttributesResult){0, true};
(void)multimedia_search_file(search, name, result);
assert_far_string(result, "");
write_far_string(search, ";X");
reset_attributes();
g_attribute_result_count = 2;
g_attribute_results[0] =
(Win16DosFileAttributesResult){0, true};
g_attribute_results[1] =
(Win16DosFileAttributesResult){0, false};
(void)multimedia_search_file(search, name, result);
assert(strcmp(g_attribute_names[1], "\\FILE.DLL") == 0);
Win16FarPtr input = win16_make_far_pointer(0x7000, 0x0400);
Win16FarPtr directory = win16_make_far_pointer(0x7000, 0x0500);
Win16FarPtr file = win16_make_far_pointer(0x7000, 0x0600);
Win16FarPtr extension = win16_make_far_pointer(0x7000, 0x0700);
write_far_string(input, "C:\\LONGDIR\\NAME.EXTMORE");
assert(multimedia_split_path(extension, file, directory, input) == 7);
assert_far_string(directory, "C:\\LONGDIR\\");
assert_far_string(file, "NAME");
assert_far_string(extension, ".EXT");
write_far_string(input, "DIR\\A?.*");
assert(multimedia_split_path(extension, file, directory, input) == 15);
assert_far_string(directory, "DIR\\");
assert_far_string(file, "A?");
assert_far_string(extension, ".*");
write_far_string(input, "GAME");
assert(multimedia_split_path(extension, file, directory, input) == 2);
assert_far_string(directory, "");
assert_far_string(file, "GAME");
assert_far_string(extension, "");
g_environment = win16_make_far_pointer(0x7000, 0x0800);
const char environment[] =
"FOO=one\0Path=two;three\0EMPTY=\0\0";
for (size_t index = 0; index < sizeof(environment); index++) {
win16_write_u8(
win16_far_add_offset(g_environment, (uint16_t)index),
(uint8_t)environment[index]);
}
write_far_string(name, "PATH");
Win16FarPtr value = multimedia_environment_value(name);
assert_far_string(value, "two;three");
write_far_string(name, "empty");
value = multimedia_environment_value(name);
assert(value != 0 && win16_read_u8(value) == 0);
write_far_string(name, "missing");
assert(multimedia_environment_value(name) == 0);
return 0;
}
@@ -0,0 +1,242 @@
#include "../tdkpin_multimedia.h"
#include "../tdkpin_borland_files.h"
#include "../tdkpin_borland_records.h"
#include <assert.h>
#include <stdatomic.h>
#include <stdint.h>
#include <string.h>
Win16FarPtr g_multimedia_procedures[11];
HINSTANCE16 g_multimedia_module;
uint8_t g_multimedia_initialized;
uint8_t g_multimedia_pending;
uint16_t g_multimedia_version;
uint8_t g_multimedia_auto_subclassed;
uint8_t g_multimedia_argument_2;
uint8_t g_multimedia_argument_1;
uint8_t g_multimedia_legacy_windows;
char g_multimedia_library_path[21];
uint16_t g_multimedia_system_directory_length;
uint16_t g_multimedia_windows_version;
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
uint16_t g_win16_show_command;
_Atomic uint16_t g_borland_io_result;
uint8_t g_borland_file_mode;
static const char *g_system_directory = "SYS";
static uint16_t g_get_version_result;
static uint16_t g_open_results[4];
static unsigned g_open_result_count;
static unsigned g_open_result_index;
static unsigned g_assign_calls;
static unsigned g_close_calls;
static unsigned g_require_calls;
static uint8_t g_assigned_length;
static uint8_t g_assigned_last;
UINT16 GetSystemDirectoryFar16(Win16FarPtr buffer, UINT16 size)
{
assert(size == 200);
size_t length = strlen(g_system_directory);
for (size_t index = 0; index <= length; index++) {
win16_write_u8(
win16_far_add_offset(buffer, (uint16_t)index),
(uint8_t)g_system_directory[index]);
}
return (UINT16)length;
}
uint32_t GetVersion16(void)
{
return 0xbeef0000u | g_get_version_result;
}
void borland_assign_file_short(Win16FarPtr name, Win16FarPtr record)
{
(void)record;
g_assign_calls++;
g_assigned_length = win16_read_u8(name);
g_assigned_last = win16_read_u8(
win16_far_add_offset(name, g_assigned_length));
}
void borland_open_generic_file(uint16_t record_size, Win16FarPtr record)
{
(void)record;
assert(record_size == 1);
assert(g_open_result_index < g_open_result_count);
atomic_store(
&g_borland_io_result,
g_open_results[g_open_result_index++]);
}
uint16_t borland_take_io_result(void)
{
return atomic_exchange(&g_borland_io_result, 0);
}
void borland_close_generic_file(Win16FarPtr record)
{
(void)record;
g_close_calls++;
}
void borland_require_io_success(void)
{
assert(atomic_load(&g_borland_io_result) == 0);
g_require_calls++;
}
static void reset_probe_results(void)
{
g_open_result_count = 0;
g_open_result_index = 0;
g_assign_calls = 0;
g_close_calls = 0;
g_require_calls = 0;
atomic_store(&g_borland_io_result, 0);
}
static void expect_short(
Win16FarPtr string, const char *expected)
{
size_t length = strlen(expected);
assert(win16_read_u8(string) == length);
for (size_t index = 0; index < length; index++) {
assert(win16_read_u8(win16_far_add_offset(
string, (uint16_t)(index + 1))) ==
(uint8_t)expected[index]);
}
}
static void expect_c_string(
Win16FarPtr string, const char *expected)
{
size_t length = strlen(expected);
for (size_t index = 0; index <= length; index++) {
assert(win16_read_u8(win16_far_add_offset(
string, (uint16_t)index)) ==
(uint8_t)expected[index]);
}
}
static void reset_unit_state(uint8_t *dgroup)
{
memset(dgroup + 0x4750, 0xcc, 0x3d);
memcpy(dgroup + 0x0522, "CTL3D.DLL", 10);
memcpy(dgroup + 0x052c, "CTL3DV2.DLL", 12);
for (unsigned index = 0; index < 11; index++) {
g_multimedia_procedures[index] =
win16_make_far_pointer(0x8888, (uint16_t)index);
}
g_multimedia_module = 0x7777;
g_multimedia_initialized = 1;
g_multimedia_pending = 1;
g_multimedia_auto_subclassed = 1;
g_multimedia_argument_1 = 9;
g_multimedia_argument_2 = 8;
}
int main(void)
{
static uint8_t dgroup[0x6000];
static uint8_t code[0x600];
uint8_t stack_anchor = 0;
uintptr_t stack_base = (uintptr_t)&stack_anchor - 0x8000;
win16_reset_segment_bindings();
win16_bind_segment(0x7000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x7000);
win16_bind_ne_segment(3, 0x6000, code, sizeof(code), false);
win16_bind_segment(0x7200, (void *)stack_base, 0x10000, true);
code[0x0470] = 1;
code[0x0471] = '\\';
Win16FarPtr short_name = win16_make_far_pointer(0x7000, 0x600);
win16_write_u8(short_name, 3);
win16_write_u8(win16_far_add_offset(short_name, 1), 'A');
win16_write_u8(win16_far_add_offset(short_name, 2), 'B');
win16_write_u8(win16_far_add_offset(short_name, 3), 'C');
reset_probe_results();
g_open_result_count = 1;
g_open_results[0] = 2;
assert(!multimedia_short_file_exists(short_name));
assert(g_assign_calls == 1 && g_assigned_length == 3 &&
g_assigned_last == 'C');
assert(g_close_calls == 0 && g_require_calls == 0);
reset_probe_results();
g_open_result_count = 1;
g_open_results[0] = 0;
assert(multimedia_short_file_exists(short_name));
assert(g_close_calls == 1 && g_require_calls == 1);
reset_probe_results();
reset_unit_state(dgroup);
g_system_directory = "SYS";
g_open_result_count = 1;
g_open_results[0] = 0; /* CTL3DV2 exists. */
g_get_version_result = 0x0003;
multimedia_initialize_runtime_unit();
assert(g_open_result_index == 1);
expect_short(
win16_make_far_pointer(0x7000, 0x486d),
"SYS\\CTL3D.DLL");
expect_short(
win16_make_far_pointer(0x7000, 0x496d),
"SYS\\CTL3DV2.DLL");
expect_c_string(
win16_make_far_pointer(0x7000, 0x478d),
"CTL3DV2.DLL");
assert(g_multimedia_system_directory_length == 3);
assert(g_multimedia_windows_version == 0x0003);
assert(g_multimedia_legacy_windows == 1);
for (unsigned index = 0; index < 11; index++) {
assert(g_multimedia_procedures[index] == 0);
}
assert(g_multimedia_auto_subclassed == 0);
assert(g_multimedia_argument_2 == 0 && g_multimedia_argument_1 == 1);
assert(g_multimedia_module == 0 && g_multimedia_pending == 0 &&
g_multimedia_initialized == 0);
reset_probe_results();
reset_unit_state(dgroup);
g_open_result_count = 2;
g_open_results[0] = 2; /* V2 absent. */
g_open_results[1] = 0; /* Classic present. */
g_get_version_result = 0x0a03;
multimedia_initialize_runtime_unit();
assert(g_open_result_index == 2);
expect_c_string(
win16_make_far_pointer(0x7000, 0x478d),
"CTL3D.DLL");
assert(g_multimedia_legacy_windows == 0);
reset_probe_results();
reset_unit_state(dgroup);
g_open_result_count = 2;
g_open_results[0] = 2;
g_open_results[1] = 3;
g_get_version_result = 0x0004;
multimedia_initialize_runtime_unit();
expect_c_string(
win16_make_far_pointer(0x7000, 0x478d),
"CTL3DV2.DLL");
assert(g_multimedia_legacy_windows == 0);
reset_probe_results();
reset_unit_state(dgroup);
memcpy(dgroup + 0x478d, "KEEP", 5);
g_system_directory = "";
g_get_version_result = 0x0004;
multimedia_initialize_runtime_unit();
assert(g_open_result_index == 0);
expect_c_string(
win16_make_far_pointer(0x7000, 0x478d), "KEEP");
return 0;
}
@@ -0,0 +1,77 @@
#include "../tdkpin_numeric.h"
#include <assert.h>
#include <string.h>
static uint8_t g_memory[0x10000];
static uintptr_t g_stack_base;
static Win16FarPtr at(uint16_t offset)
{
return win16_make_far_pointer(0x7777, offset);
}
static void bind_memory(void)
{
memset(g_memory, 0, sizeof(g_memory));
win16_reset_segment_bindings();
win16_bind_segment(0x7777, g_memory, sizeof(g_memory), true);
win16_bind_segment(0x8888, (void *)g_stack_base, 0x10000, true);
}
static void set_span(uint16_t offset, const char *text)
{
memcpy(&g_memory[offset], text, strlen(text));
}
static void set_short(uint16_t offset, const char *text)
{
size_t length = strlen(text);
g_memory[offset] = (uint8_t)length;
memcpy(&g_memory[offset + 1], text, length);
}
int main(void)
{
uint8_t stack_marker;
g_stack_base = (uintptr_t)&stack_marker - 0x8000;
bind_memory();
BorlandFormattedSlice formatted =
borland_format_i32_decimal_backward(at(100), -12345);
assert(formatted.start == at(94) && formatted.length == 6);
assert(memcmp(&g_memory[94], "-12345", 6) == 0);
formatted = borland_format_u32_backward(at(120), 0x2af, 16);
assert(formatted.start == at(117) && formatted.length == 3);
assert(memcmp(&g_memory[117], "2AF", 3) == 0);
borland_format_i32_short_string(255, at(140), 0, -12345);
assert(g_memory[140] == 6);
assert(memcmp(&g_memory[141], "-12345", 6) == 0);
borland_format_i32_short_string(255, at(150), 9, 42);
assert(g_memory[150] == 9);
assert(memcmp(&g_memory[151], " 42", 9) == 0);
borland_format_i32_short_string(5, at(170), 9, -12345);
assert(g_memory[170] == 5);
assert(memcmp(&g_memory[171], "-1234", 5) == 0);
set_span(200, "-2147483648");
BorlandParseI32Result parsed = borland_parse_i32_span(at(200), 11);
assert(!parsed.failed && parsed.remaining == 0 && parsed.value == INT32_MIN);
set_span(220, "$FFFFFFFF");
parsed = borland_parse_i32_span(at(220), 9);
assert(!parsed.failed && parsed.value == -1);
set_span(240, "2147483648");
parsed = borland_parse_i32_span(at(240), 10);
assert(parsed.failed);
set_span(260, "12x");
parsed = borland_parse_i32_span(at(260), 3);
assert(!parsed.failed && parsed.value == 12 && parsed.remaining == 1);
set_short(300, " -42");
BorlandValI32Result value = borland_val_i32_short_string(at(300));
assert(value.value == -42 && value.error_position == 0);
set_short(320, "12x");
value = borland_val_i32_short_string(at(320));
assert(value.value == 0 && value.error_position == 3);
return 0;
}
@@ -0,0 +1,161 @@
#include "../tdkpin_object_lifecycle.h"
#include <assert.h>
static unsigned g_sequence;
static Win16FarPtr g_object;
static Win16FarPtr g_parent;
static Win16FarPtr g_thunk;
static Win16FarPtr g_teardown;
static bool g_enter_result;
static unsigned g_construct_sequence;
void win16_call_object_method(
Win16FarPtr procedure, Win16FarPtr object)
{
assert(++g_sequence == 1);
assert(procedure == g_teardown && object == g_object);
}
void object_windows_for_each_child(
Win16FarPtr container, Win16FarPtr action)
{
assert(++g_sequence == 2);
assert(container == g_object);
assert(action == win16_make_far_pointer(0x6000, 0x03e9));
}
void object_windows_remove_child(
Win16FarPtr container, Win16FarPtr child)
{
assert(++g_sequence == 3);
assert(container == g_parent && child == g_object);
}
void object_windows_release_bound_thunk(Win16FarPtr thunk)
{
assert(++g_sequence == (g_parent == 0 ? 3u : 4u));
assert(thunk == g_thunk);
}
void borland_default_object_destructor(
Win16FarPtr object, uint16_t vmt)
{
assert(++g_sequence == (g_parent == 0 ? 4u : 5u));
assert(object == g_object && vmt == 0);
}
void borland_finish_object_destructor(
BorlandObjectCallFrame *frame, uint16_t vmt_field_offset)
{
assert(++g_sequence == (g_parent == 0 ? 5u : 6u));
assert(frame->object == g_object && frame->vmt_argument == 0x0200);
assert(vmt_field_offset == 0);
}
bool borland_enter_object_constructor(
BorlandObjectCallFrame *frame, uint16_t vmt_field_offset)
{
assert(++g_construct_sequence == 1);
assert(frame->object == g_object && frame->vmt_argument == 0x0200);
assert(vmt_field_offset == 0);
return g_enter_result;
}
Win16FarPtr borland_default_object_constructor(
Win16FarPtr object, uint16_t vmt)
{
assert(++g_construct_sequence == 2);
assert(object == g_object && vmt == 0);
return object;
}
void object_windows_append_child(
Win16FarPtr container, Win16FarPtr child)
{
assert(++g_construct_sequence == 3);
assert(container == g_parent && child == g_object);
}
Win16FarPtr object_windows_allocate_bound_thunk(Win16FarPtr object)
{
unsigned expected = g_parent == 0 ? 3u : 4u;
assert(++g_construct_sequence == expected);
assert(object == g_object);
return g_thunk;
}
void object_windows_set_flag_4(Win16FarPtr object)
{
unsigned expected = g_parent == 0 ? 4u : 5u;
assert(++g_construct_sequence == expected);
assert(object == g_object);
Win16FarPtr address = win16_far_add_offset(object, 0x16);
win16_write_u8(address, (uint8_t)(win16_read_u8(address) | 4));
}
static void write_far(Win16FarPtr object, uint16_t offset, Win16FarPtr value)
{
win16_write_u16(object, offset, win16_far_offset(value));
win16_write_u16(
object, (uint16_t)(offset + 2), win16_far_selector(value));
}
static void run_case(bool with_parent)
{
g_sequence = 0;
g_parent = with_parent ? win16_make_far_pointer(0x7100, 2) : 0;
write_far(g_object, 6, g_parent);
object_windows_object_destruct(g_object, 0x0200);
assert(g_sequence == (with_parent ? 6u : 5u));
}
static void run_construct_case(bool with_parent)
{
g_construct_sequence = 0;
g_enter_result = true;
g_parent = with_parent ? win16_make_far_pointer(0x7100, 2) : 0;
assert(object_windows_object_construct(g_object, 0x0200, g_parent) ==
g_object);
assert(g_construct_sequence == (with_parent ? 5u : 4u));
assert(win16_read_u16(win16_far_add_offset(g_object, 2)) == 0);
assert(win16_read_u16(win16_far_add_offset(g_object, 4)) == 0);
assert(win16_read_far_pointer(g_object, 6) == g_parent);
assert(win16_read_far_pointer(g_object, 0x0a) == 0);
assert(win16_read_far_pointer(g_object, 0x0e) == 0);
assert(win16_read_far_pointer(g_object, 0x12) == g_thunk);
assert(win16_read_u8(win16_far_add_offset(g_object, 0x16)) == 4);
if (!with_parent) {
assert(win16_read_far_pointer(g_object, 0x19) == 0);
}
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t code[0x1000];
static uint8_t object_bytes[64];
static uint8_t parent_bytes[64];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_ne_segment(4, 0x6000, code, sizeof(code), false);
win16_bind_segment(0x7000, object_bytes, sizeof(object_bytes), true);
win16_bind_segment(0x7100, parent_bytes, sizeof(parent_bytes), true);
g_object = win16_make_far_pointer(0x7000, 2);
g_thunk = win16_make_far_pointer(0x6200, 0x1234);
g_teardown = win16_make_far_pointer(0x6100, 0x4321);
win16_write_u16(g_object, 0, 0x0100);
write_far(win16_dgroup_pointer(0x0124), 0, g_teardown);
write_far(g_object, 0x12, g_thunk);
run_case(true);
run_case(false);
run_construct_case(true);
run_construct_case(false);
g_construct_sequence = 0;
g_enter_result = false;
assert(object_windows_object_construct(g_object, 0x0200, 0) == g_object);
assert(g_construct_sequence == 1);
return 0;
}
@@ -0,0 +1,169 @@
#include "../tdkpin_object_list.h"
#include <assert.h>
static Win16FarPtr g_match;
static Win16FarPtr g_order[8];
static unsigned g_order_count;
static Win16FarPtr g_blocking_child;
static Win16FarPtr g_disposed_child;
bool win16_call_object_predicate(
Win16FarPtr procedure, Win16FarPtr child)
{
if (procedure == win16_make_far_pointer(0x6000, 0x0ec0)) {
return object_windows_child_blocks_close(0, child);
}
assert(procedure == win16_make_far_pointer(0x6000, 0x0100));
g_order[g_order_count++] = child;
return child == g_match;
}
bool win16_call_object_bool_method(
Win16FarPtr procedure, Win16FarPtr object)
{
assert(procedure == win16_make_far_pointer(0x6100, 0x0300));
return object != g_blocking_child;
}
void borland_dispose_object(Win16FarPtr object)
{
g_disposed_child = object;
}
BOOL16 IsIconic16(HWND16 window)
{
(void)window;
return 0;
}
INT16 GetWindowText16(HWND16 window, char *text, INT16 maximum)
{
(void)window;
(void)text;
(void)maximum;
return 0;
}
BOOL16 SetWindowText16(HWND16 window, const char *text)
{
(void)window;
(void)text;
return 0;
}
void win16_call_object_u16_method(
Win16FarPtr procedure, Win16FarPtr object, uint16_t argument)
{
(void)procedure;
(void)object;
(void)argument;
}
uint16_t win16_call_object_transfer_method(
Win16FarPtr procedure,
Win16FarPtr object,
uint16_t direction,
Win16FarPtr cursor)
{
(void)procedure;
(void)object;
(void)direction;
(void)cursor;
return 0;
}
void win16_call_object_action(
Win16FarPtr procedure, Win16FarPtr child)
{
assert(procedure == win16_make_far_pointer(0x6000, 0x0200));
g_order[g_order_count++] = child;
}
static Win16FarPtr next_of(Win16FarPtr child)
{
return win16_read_far_pointer(child, 0x19);
}
int main(void)
{
static uint8_t container_bytes[64];
static uint8_t a_bytes[64];
static uint8_t b_bytes[64];
static uint8_t c_bytes[64];
static uint8_t dgroup[0x1000];
static uint8_t code[0x1000];
win16_reset_segment_bindings();
win16_bind_segment(
0x7000, container_bytes, sizeof(container_bytes), true);
win16_bind_segment(0x7100, a_bytes, sizeof(a_bytes), true);
win16_bind_segment(0x7200, b_bytes, sizeof(b_bytes), true);
win16_bind_segment(0x7300, c_bytes, sizeof(c_bytes), true);
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_ne_segment(4, 0x6000, code, sizeof(code), false);
Win16FarPtr container = win16_make_far_pointer(0x7000, 2);
Win16FarPtr a = win16_make_far_pointer(0x7100, 2);
Win16FarPtr b = win16_make_far_pointer(0x7200, 2);
Win16FarPtr c = win16_make_far_pointer(0x7300, 2);
object_windows_append_child(container, 0);
assert(win16_read_far_pointer(container, 0x0a) == 0);
object_windows_append_child(container, a);
object_windows_append_child(container, b);
object_windows_append_child(container, c);
assert(win16_read_far_pointer(container, 0x0a) == c);
assert(next_of(a) == b && next_of(b) == c && next_of(c) == a);
win16_write_u16(a, 0, 0x0100);
win16_write_u16(b, 0, 0x0100);
win16_write_u16(c, 0, 0x0100);
win16_write_u16(a, 4, 1);
win16_write_u16(b, 4, 1);
win16_write_u16(c, 4, 1);
win16_write_u16(win16_dgroup_pointer(0x013c), 0, 0x0300);
win16_write_u16(win16_dgroup_pointer(0x013e), 0, 0x6100);
Win16FarPtr predicate = win16_make_far_pointer(0x6000, 0x0100);
g_match = b;
g_order_count = 0;
assert(object_windows_find_child(container, predicate) == b);
assert(g_order_count == 2 && g_order[0] == a && g_order[1] == b);
g_match = 0;
g_order_count = 0;
assert(object_windows_find_child(container, predicate) == 0);
assert(g_order_count == 3 && g_order[2] == c);
g_blocking_child = b;
assert(!object_windows_all_children_can_close(container));
g_blocking_child = 0;
assert(object_windows_all_children_can_close(container));
win16_write_u16(a, 4, 0);
g_blocking_child = a;
assert(object_windows_all_children_can_close(container));
win16_write_u16(a, 4, 1);
g_order_count = 0;
object_windows_for_each_child(
container, win16_make_far_pointer(0x6000, 0x0200));
assert(g_order_count == 3);
assert(g_order[0] == a && g_order[1] == b && g_order[2] == c);
object_windows_remove_child(container, b);
assert(next_of(a) == c && next_of(c) == a);
assert(win16_read_far_pointer(container, 0x0a) == c);
object_windows_remove_child(container, c);
assert(win16_read_far_pointer(container, 0x0a) == a);
assert(next_of(a) == a);
object_windows_remove_child(container, c);
assert(win16_read_far_pointer(container, 0x0a) == a);
object_windows_remove_child(container, a);
assert(win16_read_far_pointer(container, 0x0a) == 0);
assert(object_windows_find_child(container, predicate) == 0);
object_windows_for_each_child(
container, win16_make_far_pointer(0x6000, 0x0200));
assert(object_windows_minus_one_default(container) == 0xffff);
g_disposed_child = 0;
object_windows_dispose_child_callback(0xbeef, b);
assert(g_disposed_child == b);
return 0;
}
@@ -0,0 +1,174 @@
#include "../tdkpin_object_windows.h"
#include <assert.h>
#include <string.h>
static uint8_t g_dgroup[0x10000];
static uint8_t g_code[0x10000];
static uint8_t g_object[0x100];
static BOOL16 g_is_window;
static Win16FarPtr g_window_proc;
static Win16FarPtr g_property_object;
static unsigned g_set_count;
static unsigned g_remove_count;
static uint16_t g_property_offsets[2];
static uint16_t g_property_values[2];
static unsigned g_set_window_long_calls;
static Win16FarPtr g_installed_proc;
static Win16FarPtr g_called_proc;
static HWND16 g_called_window;
static UINT16 g_called_message;
static WPARAM16 g_called_wparam;
static LPARAM16 g_called_lparam;
Win16FarPtr g_object_windows_creating_object;
LRESULT16 SetWindowLong16(HWND16 window, INT16 index, int32_t value)
{
assert(window == 0x77 && index == -4);
g_set_window_long_calls++;
g_installed_proc = (Win16FarPtr)(uint32_t)value;
return 0;
}
LRESULT16 win16_call_window_proc(
Win16FarPtr procedure,
HWND16 window,
UINT16 message,
WPARAM16 wparam,
LPARAM16 lparam)
{
g_called_proc = procedure;
g_called_window = window;
g_called_message = message;
g_called_wparam = wparam;
g_called_lparam = lparam;
return (LRESULT16)0x12345678u;
}
BOOL16 SetProp16(HWND16 window, Win16FarPtr name, Win16Handle value)
{
assert(window == 0x77);
assert(g_set_count < 2);
g_property_offsets[g_set_count] = win16_far_offset(name);
g_property_values[g_set_count] = value;
g_set_count++;
return 1;
}
Win16Handle RemoveProp16(HWND16 window, Win16FarPtr name)
{
assert(window == 0x77);
assert(g_remove_count < 2);
g_property_offsets[g_remove_count++] = win16_far_offset(name);
return 0;
}
Win16Handle GetProp16(HWND16 window, Win16FarPtr name)
{
assert(window == 0x77);
return win16_far_offset(name) == 0x067a
? win16_far_selector(g_property_object)
: win16_far_offset(g_property_object);
}
BOOL16 IsWindow16(HWND16 window)
{
assert(window == 0x77);
return g_is_window;
}
int32_t GetWindowLong16(HWND16 window, INT16 index)
{
assert(window == 0x77 && index == -4);
return (int32_t)g_window_proc;
}
static void reset_state(void)
{
memset(g_dgroup, 0, sizeof(g_dgroup));
memset(g_code, 0, sizeof(g_code));
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_code, sizeof(g_code), true);
win16_bind_segment(0x7000, g_object, sizeof(g_object), true);
win16_set_dgroup_selector(0x5000);
g_is_window = 1;
g_set_count = 0;
g_remove_count = 0;
g_set_window_long_calls = 0;
}
static void test_bootstrap_transition(void)
{
reset_state();
Win16FarPtr object = win16_make_far_pointer(0x7000, 0x0010);
Win16FarPtr bound_proc = win16_make_far_pointer(0x6000, 0x0300);
g_object_windows_creating_object = object;
win16_write_u16(object, 0x12, win16_far_offset(bound_proc));
win16_write_u16(object, 0x14, win16_far_selector(bound_proc));
LRESULT16 result = object_window_bootstrap_proc(
0x77,
0x0123,
0x4567,
(LPARAM16)0x89abcdefu);
assert(result == (LRESULT16)0x12345678u);
assert(g_set_window_long_calls == 1 && g_installed_proc == bound_proc);
assert(g_set_count == 2);
assert(g_property_values[0] == win16_far_selector(object));
assert(g_property_values[1] == win16_far_offset(object));
assert(g_called_proc == bound_proc && g_called_window == 0x77);
assert(g_called_message == 0x0123 && g_called_wparam == 0x4567);
assert(g_called_lparam == (LPARAM16)0x89abcdefu);
}
static void test_property_binding(void)
{
reset_state();
Win16FarPtr object = win16_make_far_pointer(0x3456, 0x789a);
object_windows_bind_window(0x77, object);
assert(g_set_count == 2);
assert(g_property_offsets[0] == 0x067a && g_property_values[0] == 0x3456);
assert(g_property_offsets[1] == 0x067e && g_property_values[1] == 0x789a);
object_windows_unbind_window(0x77);
assert(g_remove_count == 2);
assert(g_property_offsets[0] == 0x067a && g_property_offsets[1] == 0x067e);
}
static void test_generated_thunk_lookup(void)
{
reset_state();
g_window_proc = win16_make_far_pointer(0x6000, 0x0100);
Win16FarPtr object = win16_make_far_pointer(0x3456, 0x789a);
g_code[2] = 0x5b;
g_code[3] = 0x2e;
g_code[0x0100] = 0xe8;
g_code[0x0101] = 0xff;
g_code[0x0102] = 0xfe;
g_code[0x0103] = (uint8_t)object;
g_code[0x0104] = (uint8_t)(object >> 8);
g_code[0x0105] = (uint8_t)(object >> 16);
g_code[0x0106] = (uint8_t)(object >> 24);
assert(object_windows_object_from_window(0x77) == object);
}
static void test_property_fallback_and_invalid_window(void)
{
reset_state();
g_window_proc = win16_make_far_pointer(0x6000, 0x0200);
g_property_object = win16_make_far_pointer(0x1111, 0x2222);
assert(object_windows_object_from_window(0x77) == g_property_object);
g_is_window = 0;
assert(object_windows_object_from_window(0x77) == 0);
}
int main(void)
{
test_property_binding();
test_generated_thunk_lookup();
test_property_fallback_and_invalid_window();
test_bootstrap_transition();
return 0;
}
@@ -0,0 +1,169 @@
#include "../tdkpin_dialog.h"
#include <assert.h>
Win16FarPtr g_object_windows_application;
uint8_t g_object_windows_focus_event_marker;
static bool g_bool_result;
static Win16FarPtr g_expected_bool_procedure;
static Win16FarPtr g_expected_bool_receiver;
static unsigned g_bool_calls;
static HWND16 g_set_focus_window;
static Win16FarPtr g_window_from_handle;
static Win16FarPtr g_called_method;
static Win16FarPtr g_called_receiver;
static unsigned g_method_calls;
static unsigned g_dispose_calls;
static Win16FarPtr g_u16_procedures[2];
static uint16_t g_u16_arguments[2];
static unsigned g_u16_calls;
bool win16_call_object_bool_method(
Win16FarPtr procedure, Win16FarPtr object)
{
assert(procedure == g_expected_bool_procedure);
assert(object == g_expected_bool_receiver);
g_bool_calls++;
return g_bool_result;
}
HWND16 SetFocus16(HWND16 window)
{
g_set_focus_window = window;
return 0x9999;
}
Win16FarPtr object_windows_object_from_window(HWND16 window)
{
assert(window == g_set_focus_window);
return g_window_from_handle;
}
void win16_call_object_method(
Win16FarPtr procedure, Win16FarPtr object)
{
g_called_method = procedure;
g_called_receiver = object;
g_method_calls++;
}
void borland_dispose_object(Win16FarPtr object)
{
g_called_receiver = object;
g_dispose_calls++;
}
void win16_call_object_u16_method(
Win16FarPtr procedure, Win16FarPtr object, uint16_t argument)
{
assert(g_u16_calls < 2);
g_u16_procedures[g_u16_calls] = procedure;
g_u16_arguments[g_u16_calls] = argument;
g_called_receiver = object;
g_u16_calls++;
}
static void write_far(
Win16FarPtr base, uint16_t offset, Win16FarPtr value)
{
win16_write_u16(base, offset, win16_far_offset(value));
win16_write_u16(
base, (uint16_t)(offset + 2), win16_far_selector(value));
}
int main(void)
{
static uint8_t dgroup[0x1000];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
Win16FarPtr application = win16_make_far_pointer(0x5000, 0x0200);
Win16FarPtr main_window = win16_make_far_pointer(0x5000, 0x0300);
Win16FarPtr other_window = win16_make_far_pointer(0x5000, 0x0400);
Win16FarPtr attached = win16_make_far_pointer(0x5000, 0x0600);
g_object_windows_application = application;
win16_write_u16(application, 0, 0x0700);
write_far(application, 8, main_window);
win16_write_u16(main_window, 0, 0x0780);
win16_write_u16(other_window, 0, 0x0800);
win16_write_u16(attached, 0, 0x0880);
Win16FarPtr app_can_close =
win16_make_far_pointer(0x6000, 0x1111);
Win16FarPtr window_can_close =
win16_make_far_pointer(0x6000, 0x2222);
write_far(win16_dgroup_pointer(0x0700), 0x44, app_can_close);
write_far(win16_dgroup_pointer(0x0800), 0x3c, window_can_close);
ObjectWindowsMessage message = {0};
g_expected_bool_procedure = app_can_close;
g_expected_bool_receiver = application;
g_bool_result = true;
g_bool_calls = 0;
object_windows_query_end_session_result(main_window, &message);
assert(g_bool_calls == 1 && message.result == 0);
g_expected_bool_procedure = window_can_close;
g_expected_bool_receiver = other_window;
g_bool_result = false;
object_windows_query_end_session_result(other_window, &message);
assert(message.result == 1);
message.wparam = 0x1234;
g_window_from_handle = other_window;
write_far(other_window, 0x43, attached);
Win16FarPtr attached_method =
win16_make_far_pointer(0x6000, 0x3333);
write_far(win16_dgroup_pointer(0x0880), 0x0c, attached_method);
g_method_calls = 0;
g_object_windows_focus_event_marker = 0;
object_windows_focus_message(main_window, &message);
assert(g_set_focus_window == 0x1234);
assert(g_method_calls == 1 && g_called_method == attached_method &&
g_called_receiver == attached);
assert(g_object_windows_focus_event_marker == 1);
g_window_from_handle = 0;
g_method_calls = 0;
g_object_windows_focus_event_marker = 0;
object_windows_focus_message(0, &message);
assert(g_method_calls == 0 && g_object_windows_focus_event_marker == 1);
Win16FarPtr dialog = win16_make_far_pointer(0x5000, 0x0500);
win16_write_u16(dialog, 0, 0x0900);
Win16FarPtr dialog_can_close =
win16_make_far_pointer(0x6000, 0x4444);
Win16FarPtr dialog_transfer =
win16_make_far_pointer(0x6000, 0x5555);
Win16FarPtr dialog_close =
win16_make_far_pointer(0x6000, 0x6666);
write_far(win16_dgroup_pointer(0x0900), 0x3c, dialog_can_close);
write_far(win16_dgroup_pointer(0x0900), 0x44, dialog_transfer);
write_far(win16_dgroup_pointer(0x0900), 0x50, dialog_close);
win16_write_u8(win16_far_add_offset(dialog, 0x25), 0);
g_expected_bool_procedure = dialog_can_close;
g_expected_bool_receiver = dialog;
g_bool_result = true;
g_dispose_calls = 0;
object_windows_dialog_validate_and_close(dialog, &message);
assert(g_dispose_calls == 1 && g_called_receiver == dialog);
win16_write_u8(win16_far_add_offset(dialog, 0x25), 1);
g_expected_bool_procedure = dialog_can_close;
g_expected_bool_receiver = dialog;
g_bool_result = false;
g_u16_calls = 0;
object_windows_dialog_validate_and_close(dialog, &message);
assert(g_u16_calls == 0);
g_bool_result = true;
object_windows_dialog_validate_and_close(dialog, &message);
assert(g_u16_calls == 2 && g_called_receiver == dialog);
assert(g_u16_procedures[0] == dialog_transfer &&
g_u16_arguments[0] == 1);
assert(g_u16_procedures[1] == dialog_close &&
g_u16_arguments[1] == 1);
return 0;
}
@@ -0,0 +1,44 @@
#include "../tdkpin_game_assets.h"
#include <assert.h>
static uint8_t g_dgroup[0x1000];
static uint8_t g_window_bytes[32];
HINSTANCE16 g_win16_instance;
static Win16FarPtr g_window;
static unsigned g_step;
void object_windows_setup_window(Win16FarPtr window)
{
assert(++g_step == 1 && window == g_window);
}
HBITMAP16 tdkpin_load_bitmap_resource(
Win16FarPtr resource_name, HINSTANCE16 instance)
{
assert(++g_step == 2);
assert(resource_name == win16_make_far_pointer(0, 993));
assert(instance == g_win16_instance);
return 0x7777;
}
HWND16 SetFocus16(HWND16 window)
{
assert(++g_step == 3 && window == 0x2222);
return 0x1111;
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_window_bytes, sizeof(g_window_bytes), true);
win16_set_dgroup_selector(0x5000);
g_win16_instance = 0x4444;
g_window = win16_make_far_pointer(0x6000, 0);
win16_write_u16(g_window, 4, 0x2222);
tdkpin_setup_overlay_window(g_window);
assert(g_step == 3);
assert(win16_read_u16(win16_make_far_pointer(0x5000, 0x0867)) == 0x7777);
return 0;
}
+140
View File
@@ -0,0 +1,140 @@
#include "../tdkpin_palette.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_allocation[sizeof(LOGPALETTE256)];
static uint8_t g_resource_bytes[768];
static uint8_t g_bitmap_info[sizeof(BITMAPINFO256)];
static unsigned g_range_checks;
static unsigned g_get_mem_calls;
static unsigned g_free_mem_calls;
static unsigned g_create_palette_calls;
static unsigned g_find_calls;
static unsigned g_load_calls;
static unsigned g_lock_calls;
static unsigned g_unlock_calls;
static Win16FarPtr dgroup_at(uint16_t offset)
{
return win16_make_far_pointer(0x5000, offset);
}
Win16FarPtr borland_get_mem(uint16_t bytes)
{
assert(bytes == sizeof(LOGPALETTE256));
g_get_mem_calls++;
return win16_make_far_pointer(0x6000, 0);
}
void borland_free_mem(Win16FarPtr block, uint16_t bytes)
{
assert(block == win16_make_far_pointer(0x6000, 0));
assert(bytes == sizeof(LOGPALETTE256));
g_free_mem_calls++;
}
void borland_check_int32_range(
int32_t value, const BorlandInt32Range *range)
{
assert(value >= range->minimum && value <= range->maximum);
if (range->maximum == 0xffff) {
assert(value == sizeof(LOGPALETTE256));
} else {
assert(range->minimum == 0 && range->maximum == 0x02ff);
}
g_range_checks++;
}
HPALETTE16 CreatePalette16(Win16FarPtr logical_palette)
{
assert(logical_palette == win16_make_far_pointer(0x6000, 0));
assert(win16_read_u16(logical_palette) == 0x0300);
assert(win16_read_u16(win16_far_add_offset(logical_palette, 2)) == 256);
for (uint16_t index = 0; index < 256; index++) {
Win16FarPtr entry = win16_far_add_offset(
logical_palette, (uint16_t)(4 + index * 4));
assert(win16_read_u8(entry) == g_dgroup[0x4450 + index * 3]);
assert(win16_read_u8(win16_far_add_offset(entry, 1)) ==
g_dgroup[0x4451 + index * 3]);
assert(win16_read_u8(win16_far_add_offset(entry, 2)) ==
g_dgroup[0x4452 + index * 3]);
assert(win16_read_u8(win16_far_add_offset(entry, 3)) == 0);
}
g_create_palette_calls++;
return 0x2468;
}
HRSRC16 FindResource16(
HINSTANCE16 instance, Win16FarPtr name, Win16FarPtr type)
{
assert(instance == 0x4444);
assert(name == win16_make_far_pointer(0, 999));
assert(type == dgroup_at(0x045a));
g_find_calls++;
return 0x1111;
}
HGLOBAL16 LoadResource16(HINSTANCE16 instance, HRSRC16 resource)
{
assert(instance == 0x4444 && resource == 0x1111);
g_load_calls++;
return 0x2222;
}
Win16FarPtr LockResource16(HGLOBAL16 resource)
{
assert(resource == 0x2222);
g_lock_calls++;
return win16_make_far_pointer(0x7000, 0);
}
BOOL16 GlobalUnlock16(HGLOBAL16 resource)
{
assert(resource == 0x2222);
g_unlock_calls++;
return 1;
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_allocation, sizeof(g_allocation), true);
win16_bind_segment(0x7000, g_resource_bytes, sizeof(g_resource_bytes), false);
win16_bind_segment(0x8000, g_bitmap_info, sizeof(g_bitmap_info), true);
win16_set_dgroup_selector(0x5000);
memcpy(&g_dgroup[0x045a], "pal", 4);
for (uint16_t index = 0; index < 768; index++) {
g_dgroup[0x4450 + index] = (uint8_t)(index * 37 + 11);
g_resource_bytes[index] = (uint8_t)(index * 19 + 7);
}
tdkpin_create_palette_from_rgb_table();
assert(g_get_mem_calls == 1 && g_create_palette_calls == 1);
assert(g_range_checks == 1 && g_free_mem_calls == 1);
assert(win16_read_u16(dgroup_at(0x444e)) == 0x2468);
g_range_checks = 0;
tdkpin_load_palette_resource(
dgroup_at(0x4450), win16_make_far_pointer(0, 999), 0x4444);
assert(g_find_calls == 1 && g_load_calls == 1 && g_lock_calls == 1);
assert(g_unlock_calls == 1 && g_range_checks == 1536);
assert(memcmp(&g_dgroup[0x4450], g_resource_bytes, 768) == 0);
Win16FarPtr bitmap_info = win16_make_far_pointer(0x8000, 0);
win16_write_u16(
bitmap_info, offsetof(BITMAPINFOHEADER16, bit_count), 8);
tdkpin_fill_bitmap_info_palette(bitmap_info);
for (uint16_t index = 0; index < 256; index++) {
size_t output = offsetof(BITMAPINFO256, colors) + index * 4;
assert(g_bitmap_info[output] == g_dgroup[0x4452 + index * 3]);
assert(g_bitmap_info[output + 1] == g_dgroup[0x4451 + index * 3]);
assert(g_bitmap_info[output + 2] == g_dgroup[0x4450 + index * 3]);
assert(g_bitmap_info[output + 3] == 0);
}
return 0;
}
@@ -0,0 +1,95 @@
#include "../tdkpin_palette.h"
#include <assert.h>
#include <stdbool.h>
#include <string.h>
static uint8_t g_palette_bytes[sizeof(LOGPALETTE256)];
static bool g_fail_allocation;
static unsigned g_step;
static unsigned g_get_mem_calls;
static unsigned g_free_mem_calls;
Win16FarPtr borland_get_mem(uint16_t bytes)
{
assert(bytes == sizeof(LOGPALETTE256));
g_get_mem_calls++;
return g_fail_allocation ? 0 : win16_make_far_pointer(0x6000, 0);
}
void borland_free_mem(Win16FarPtr block, uint16_t bytes)
{
assert(++g_step == 8);
assert(block == win16_make_far_pointer(0x6000, 0));
assert(bytes == sizeof(LOGPALETTE256));
g_free_mem_calls++;
}
HDC16 GetDC16(HWND16 window)
{
assert(++g_step == 1 && window == 0x1234);
return 0x2222;
}
HPALETTE16 CreatePalette16(Win16FarPtr logical_palette)
{
assert(++g_step == 2);
assert(win16_read_u16(logical_palette) == 0x0300);
assert(win16_read_u16(win16_far_add_offset(logical_palette, 2)) == 256);
for (uint16_t index = 0; index < 256; index++) {
Win16FarPtr entry = win16_far_add_offset(
logical_palette, (uint16_t)(4 + index * 4));
assert(win16_read_u8(entry) == 0);
assert(win16_read_u8(win16_far_add_offset(entry, 1)) == 0);
assert(win16_read_u8(win16_far_add_offset(entry, 2)) == 0);
assert(win16_read_u8(win16_far_add_offset(entry, 3)) == 4);
}
return 0x3333;
}
HPALETTE16 SelectPalette16(
HDC16 dc, HPALETTE16 palette, BOOL16 force_background)
{
assert(dc == 0x2222);
if (++g_step == 3) {
assert(palette == 0x3333 && force_background == 0);
return 0x4444;
}
assert(g_step == 5);
assert(palette == 0x4444 && force_background == 1);
return 0x3333;
}
UINT16 RealizePalette16(HDC16 dc)
{
assert(++g_step == 4 && dc == 0x2222);
return 256;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(++g_step == 6 && object == 0x3333);
return 1;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(++g_step == 7 && window == 0x1234 && dc == 0x2222);
return 1;
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(
0x6000, g_palette_bytes, sizeof(g_palette_bytes), true);
g_fail_allocation = true;
tdkpin_realize_nocollapse_palette(0x1234);
assert(g_get_mem_calls == 1 && g_step == 0 && g_free_mem_calls == 0);
g_fail_allocation = false;
memset(g_palette_bytes, 0xff, sizeof(g_palette_bytes));
tdkpin_realize_nocollapse_palette(0x1234);
assert(g_get_mem_calls == 2 && g_step == 8 && g_free_mem_calls == 1);
return 0;
}
@@ -0,0 +1,89 @@
#include "../tdkpin_game_assets.h"
#include <assert.h>
#include <string.h>
static uint8_t g_dgroup[0x1000];
static uint8_t g_objects[0x100];
static Win16FarPtr g_window;
static Win16FarPtr g_message;
static BOOL16 g_iconic;
static unsigned g_iconic_calls;
static unsigned g_invalidate_calls;
static unsigned g_update_calls;
BOOL16 IsIconic16(HWND16 window)
{
assert(window == 0x2222);
g_iconic_calls++;
return g_iconic;
}
BOOL16 InvalidateRect16(
HWND16 window, Win16FarPtr rectangle, BOOL16 erase)
{
assert(window == 0x2222 && rectangle == 0 && erase == 0);
g_invalidate_calls++;
return 1;
}
BOOL16 UpdateWindow16(HWND16 window)
{
assert(window == 0x2222);
g_update_calls++;
return 1;
}
static void reset_fixture(void)
{
memset(g_dgroup, 0, sizeof(g_dgroup));
memset(g_objects, 0, sizeof(g_objects));
win16_write_u16(g_window, 4, 0x2222);
win16_write_u32(g_message, 10, 0x12345678);
g_iconic = 0;
g_iconic_calls = 0;
g_invalidate_calls = 0;
g_update_calls = 0;
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_objects, sizeof(g_objects), true);
win16_set_dgroup_selector(0x5000);
g_window = win16_make_far_pointer(0x6000, 0);
g_message = win16_make_far_pointer(0x6000, 0x40);
win16_set_indeterminate_u16(0xbeef);
reset_fixture();
win16_write_u16(g_message, 4, 0);
win16_write_u8(win16_make_far_pointer(0x5000, 0x07c4), 1);
assert(tdkpin_palette_change_variant_a(g_window, g_message) == 0);
assert(win16_read_u8(win16_make_far_pointer(0x5000, 0x07c4)) == 0);
assert(g_iconic_calls == 0 && g_invalidate_calls == 0);
reset_fixture();
win16_write_u16(g_message, 4, 1);
g_iconic = 1;
assert(tdkpin_palette_change_variant_a(g_window, g_message) == 0xbeef);
assert(g_iconic_calls == 1 && g_invalidate_calls == 0);
reset_fixture();
win16_write_u16(g_message, 4, 1);
assert(tdkpin_palette_change_variant_a(g_window, g_message) == 0);
assert(g_iconic_calls == 1 && g_invalidate_calls == 1 && g_update_calls == 1);
assert(win16_read_u32(win16_far_add_offset(g_message, 10)) == 0);
assert(win16_read_u8(win16_make_far_pointer(0x5000, 0x07c4)) == 1);
assert(tdkpin_palette_change_variant_a(g_window, g_message) == 0xbeef);
reset_fixture();
win16_write_u16(g_message, 4, 1);
tdkpin_palette_change_variant_b(g_window, g_message);
assert(g_invalidate_calls == 0);
win16_write_u16(g_message, 4, 0);
tdkpin_palette_change_variant_b(g_window, g_message);
assert(g_invalidate_calls == 1 && g_update_calls == 1);
assert(win16_read_u32(win16_far_add_offset(g_message, 10)) == 0);
return 0;
}
@@ -0,0 +1,141 @@
#include "../tdkpin_game_assets.h"
#include <assert.h>
#include <string.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_objects[0x100];
static Win16FarPtr g_window;
static Win16FarPtr g_message;
static HWND16 g_parent_result;
static int16_t g_realize_result;
static unsigned g_step;
static unsigned g_invalidate_calls;
HWND16 GetParent16(HWND16 window)
{
assert(++g_step == 1 && window == 0x3333);
return g_parent_result;
}
HDC16 GetDC16(HWND16 window)
{
assert(window == 0x2222);
g_step++;
return 0x4444;
}
HPALETTE16 SelectPalette16(
HDC16 dc, HPALETTE16 palette, BOOL16 force_background)
{
assert(dc == 0x4444);
g_step++;
if (palette == 0x5555) {
assert(force_background == 0 || force_background == 1);
return 0x6666;
}
assert(palette == 0x6666 && force_background == 0);
return 0x5555;
}
UINT16 RealizePalette16(HDC16 dc)
{
assert(dc == 0x4444);
g_step++;
return (uint16_t)g_realize_result;
}
BOOL16 UpdateColors16(HDC16 dc)
{
assert(dc == 0x4444);
g_step++;
return 1;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == 0x2222 && dc == 0x4444);
g_step++;
return 1;
}
BOOL16 InvalidateRect16(
HWND16 window, Win16FarPtr rectangle, BOOL16 erase)
{
assert(window == 0x2222 && rectangle == 0 && erase == 0);
g_invalidate_calls++;
return 1;
}
static void reset_message(HWND16 changed_window)
{
memset(g_objects, 0, sizeof(g_objects));
win16_write_u16(g_window, 4, 0x2222);
win16_write_u16(g_message, 4, changed_window);
win16_write_u32(g_message, 10, 0x12345678);
g_step = 0;
g_invalidate_calls = 0;
g_parent_result = 0;
g_realize_result = 0;
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_objects, sizeof(g_objects), true);
win16_set_dgroup_selector(0x5000);
g_window = win16_make_far_pointer(0x6000, 0);
g_message = win16_make_far_pointer(0x6000, 0x40);
win16_write_u16(win16_make_far_pointer(0x5000, 0x444e), 0, 0x5555);
reset_message(0x2222);
assert(tdkpin_wm_palette_changed(g_window, g_message) == 1);
assert(g_step == 0);
reset_message(0x3333);
g_parent_result = 0x2222;
assert(tdkpin_wm_palette_changed(g_window, g_message) == 1);
assert(g_step == 1);
reset_message(0x3333);
assert(tdkpin_wm_palette_changed(g_window, g_message) == 0);
assert(g_step == 7);
assert(win16_read_u32(win16_far_add_offset(g_message, 10)) == 0);
reset_message(0);
g_realize_result = 0;
assert(tdkpin_wm_query_new_palette(g_window, g_message) == 0);
assert(g_step == 5 && g_invalidate_calls == 0);
assert(win16_read_u32(win16_far_add_offset(g_message, 10)) == 0);
reset_message(0);
g_realize_result = 3;
assert(tdkpin_wm_query_new_palette(g_window, g_message) == 3);
assert(g_step == 5 && g_invalidate_calls == 1);
assert(win16_read_u32(win16_far_add_offset(g_message, 10)) == 3);
uint16_t (*changed_variants[])(Win16FarPtr, Win16FarPtr) = {
tdkpin_wm_palette_changed_variant_b,
tdkpin_wm_palette_changed_variant_c,
};
for (unsigned index = 0; index < 2; index++) {
reset_message(0x3333);
assert(changed_variants[index](g_window, g_message) == 0);
assert(g_step == 7);
assert(win16_read_u32(win16_far_add_offset(g_message, 10)) == 0);
}
int16_t (*query_variants[])(Win16FarPtr, Win16FarPtr) = {
tdkpin_wm_query_new_palette_variant_b,
tdkpin_wm_query_new_palette_variant_c,
};
for (unsigned index = 0; index < 2; index++) {
reset_message(0);
g_realize_result = 2;
assert(query_variants[index](g_window, g_message) == 2);
assert(g_step == 5 && g_invalidate_calls == 1);
assert(win16_read_u32(win16_far_add_offset(g_message, 10)) == 2);
}
return 0;
}
@@ -0,0 +1,248 @@
#include "../tdkpin_panel_animation.h"
#include <assert.h>
#include <string.h>
typedef struct {
HDC16 destination;
HGDIOBJ16 destination_object;
INT16 destination_x;
INT16 destination_y;
INT16 width;
INT16 height;
HDC16 source;
HGDIOBJ16 source_object;
INT16 source_x;
INT16 source_y;
uint32_t rop;
} RecordedBlit;
static uint8_t g_stack[0x400];
static uint8_t g_object[0x0c00];
static uint8_t g_dgroup[0x5000];
static Win16FarPtr g_receiver;
static unsigned g_create_dc_calls;
static unsigned g_select_calls;
static unsigned g_blit_calls;
static unsigned g_delete_dc_calls;
static unsigned g_play_calls;
static unsigned g_stop_calls;
static uint16_t g_played_sound;
static HGDIOBJ16 g_source_selected;
static HGDIOBJ16 g_panel_selected;
static RecordedBlit g_blits[16];
HDC16 GetDC16(HWND16 window)
{
assert(window == 0x2222);
return 0x3333;
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x3333 && g_create_dc_calls < 2);
return (HDC16)(0x4001 + g_create_dc_calls++);
}
HBITMAP16 CreateCompatibleBitmap16(HDC16 dc, INT16 width, INT16 height)
{
assert(dc == 0x3333 && width == 91 && height == 123);
return 0x5000;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
g_select_calls++;
if (dc == 0x4001) {
HGDIOBJ16 old = g_source_selected;
g_source_selected = object;
return old;
}
assert(dc == 0x4002);
HGDIOBJ16 old = g_panel_selected;
g_panel_selected = object;
return old;
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
assert(g_blit_calls < 16);
RecordedBlit *blit = &g_blits[g_blit_calls++];
*blit = (RecordedBlit){
destination,
destination == 0x4001 ? g_source_selected
: destination == 0x4002 ? g_panel_selected : 0,
destination_x,
destination_y,
width,
height,
source,
source == 0x4001 ? g_source_selected
: source == 0x4002 ? g_panel_selected : 0,
source_x,
source_y,
raster_operation};
return 1;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(object == 0x5000);
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(dc == (g_delete_dc_calls == 0 ? 0x4002 : 0x4001));
g_delete_dc_calls++;
return 1;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == 0x2222 && dc == 0x3333);
return 1;
}
void tdkpin_play_sound_if_not_tilted(uint16_t sound_number)
{
g_played_sound = sound_number;
g_play_calls++;
}
uint16_t tdkpin_stop_sound_if_active(void)
{
g_stop_calls++;
return 0xabcd;
}
static void prepare_fixture(int32_t frame)
{
memset(g_stack, 0, sizeof(g_stack));
memset(g_object, 0, sizeof(g_object));
memset(g_dgroup, 0, sizeof(g_dgroup));
memset(g_blits, 0, sizeof(g_blits));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_set_stack_state(0x7000, 0);
g_receiver = win16_make_far_pointer(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
win16_write_u16(g_receiver, 4, 0x2222);
win16_write_u16(win16_dgroup_pointer(0x085f), 0, 0x6000);
win16_write_u16(win16_dgroup_pointer(0x0859), 0, 0x6001);
win16_write_u16(win16_dgroup_pointer(0x085b), 0, 0x6002);
win16_write_u16(win16_dgroup_pointer(0x085d), 0, 0x6003);
win16_write_u32(win16_dgroup_pointer(0x0851), 0, (uint32_t)frame);
g_create_dc_calls = 0;
g_select_calls = 0;
g_blit_calls = 0;
g_delete_dc_calls = 0;
g_play_calls = 0;
g_stop_calls = 0;
g_played_sound = 0;
g_source_selected = 0x7001;
g_panel_selected = 0x7002;
}
static void assert_common_edges(void)
{
assert(g_create_dc_calls == 2 && g_delete_dc_calls == 2);
assert(g_blits[0].destination == 0x4002);
assert(g_blits[0].width == 91 && g_blits[0].height == 123);
assert(g_blits[0].source_object == 0x6000);
assert(g_blits[0].source_x == 79 && g_blits[0].source_y == 0);
RecordedBlit *last = &g_blits[g_blit_calls - 1];
assert(last->destination == 0x3333);
assert(last->destination_x == 79 && last->destination_y == 0);
assert(last->width == 91 && last->height == 123);
assert(last->source == 0x4002 && last->source_x == 0);
assert(last->source_y == 0 && last->rop == 0x00cc0020u);
}
static void run_count_case(
int32_t frame,
unsigned expected_blits,
uint16_t expected_sound,
unsigned expected_stops)
{
prepare_fixture(frame);
tdkpin_draw_panel_animation(0x0100);
assert(g_blit_calls == expected_blits);
assert(g_stop_calls == expected_stops);
assert(g_play_calls == (expected_sound != 0 ? 1u : 0u));
assert(g_played_sound == expected_sound);
assert_common_edges();
}
static void test_phase_boundaries(void)
{
run_count_case(45, 4, 0, 1);
run_count_case(58, 4, 12, 0);
run_count_case(66, 7, 13, 0);
run_count_case(111, 5, 0, 0);
run_count_case(129, 3, 0, 1);
run_count_case(159, 5, 13, 0);
run_count_case(177, 7, 0, 0);
run_count_case(204, 7, 0, 0);
run_count_case(221, 7, 0, 0);
run_count_case(222, 6, 0, 1);
run_count_case(230, 6, 12, 0);
run_count_case(238, 6, 13, 0);
}
static void test_first_frame_and_formulas(void)
{
prepare_fixture(1);
tdkpin_draw_panel_animation(0x0100);
assert(g_blit_calls == 5 && g_played_sound == 13);
assert(g_blits[1].destination_object == 0x6002);
assert(g_blits[1].destination_x == 79 && g_blits[1].width == 91);
assert(g_blits[1].height == 90 && g_blits[1].source_x == 271);
assert(g_blits[2].destination_x == 20 && g_blits[2].height == 2);
assert(g_blits[2].source_x == 45 && g_blits[2].source_y == 177);
assert(g_blits[2].rop == 0x008800c6u);
assert(g_blits[3].source_x == 0 && g_blits[3].source_y == 177);
assert_common_edges();
prepare_fixture(83);
tdkpin_draw_panel_animation(0x0100);
assert(g_blits[2].destination_y == 0 && g_blits[2].height == 90);
assert(g_blits[2].source_y == 92);
assert(g_blits[4].height == 54 && g_blits[4].source_y == 124);
}
static void test_final_frame_background_commit(void)
{
prepare_fixture(281);
tdkpin_draw_panel_animation(0x0100);
assert(g_blit_calls == 7);
RecordedBlit *commit = &g_blits[5];
assert(commit->destination == 0x4002);
assert(commit->destination_object == 0x6002);
assert(commit->destination_x == 79 && commit->destination_y == 0);
assert(commit->width == 91 && commit->height == 90);
assert(commit->source_object == 0x6003);
assert(commit->source_x == 79 && commit->source_y == 0);
assert_common_edges();
}
int main(void)
{
test_phase_boundaries();
test_first_frame_and_formulas();
test_final_frame_background_commit();
return 0;
}
+453
View File
@@ -0,0 +1,453 @@
#include "../tdkpin_physics.h"
#include "../tdkpin_borland_runtime.h"
#include "../tdkpin_collision_records.h"
#include "../tdkpin_real48.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
static uint8_t g_dgroup[0x5000];
static uint8_t g_objects[0x1000];
static uint8_t g_stack[0x1000];
static Win16FarPtr g_window;
static unsigned g_finish_calls;
static unsigned g_render_calls;
static unsigned g_update_calls;
static int32_t g_last_update;
static unsigned g_restore_b_calls;
static uint16_t g_last_restore_b;
static unsigned g_event_calls;
static uint16_t g_last_event_id;
static uint16_t g_last_event_flags;
static unsigned g_sound_calls;
static uint16_t g_last_sound;
static int32_t g_score_total;
static unsigned g_load_calls;
static unsigned g_save_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_finish_ball_or_advance_player(uint16_t caller_bp)
{
assert(caller_bp == 0x0300);
g_finish_calls++;
}
void tdkpin_render_ball_swept_region(uint16_t caller_bp)
{
assert(caller_bp == 0x0300);
g_render_calls++;
}
void tdkpin_update_dynamic_ball_record(
uint16_t caller_bp, int32_t displacement)
{
assert(caller_bp == 0x0300);
g_update_calls++;
g_last_update = displacement;
}
void tdkpin_restore_record_b(uint16_t caller_bp, uint16_t id)
{
assert(caller_bp == 0x0300);
g_restore_b_calls++;
g_last_restore_b = id;
}
void tdkpin_restore_record_or_item_a(uint16_t caller_bp, uint16_t id)
{
(void)caller_bp;
(void)id;
}
void tdkpin_handle_collision_events(
uint16_t caller_bp, uint16_t event_flags, uint16_t record_id)
{
assert(caller_bp == 0x0300);
g_event_calls++;
g_last_event_flags = event_flags;
g_last_event_id = record_id;
}
void tdkpin_play_sound_if_not_tilted(uint16_t sound_number)
{
g_sound_calls++;
g_last_sound = sound_number;
}
void tdkpin_add_score_from_caller_frame(uint16_t caller_bp, int32_t delta)
{
assert(caller_bp == 0x0300);
g_score_total += delta;
}
void tdkpin_draw_score_from_caller_frame(
uint16_t caller_bp, uint16_t value_low, uint16_t value_high)
{
(void)caller_bp;
(void)value_low;
(void)value_high;
}
void tdkpin_refresh_player_progress_markers(uint16_t caller_bp)
{
(void)caller_bp;
}
void tdkpin_apply_player_sprite_effect(
uint16_t caller_bp, int16_t argument)
{
(void)caller_bp;
(void)argument;
}
void tdkpin_composite_player_sprite(uint16_t sprite_index, HDC16 reference_dc)
{
(void)sprite_index;
(void)reference_dc;
}
void tdkpin_present_background_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
(void)height;
(void)width;
(void)y;
(void)x;
(void)dc;
}
HDC16 GetDC16(HWND16 window)
{
(void)window;
return 0x3333;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
(void)window;
(void)dc;
return 1;
}
void tdkpin_load_ball_slot(uint16_t caller_bp, uint16_t slot)
{
(void)caller_bp;
(void)slot;
g_load_calls++;
}
void tdkpin_save_ball_slot(uint16_t caller_bp, uint16_t slot)
{
(void)caller_bp;
(void)slot;
g_save_calls++;
}
uint16_t borland_random_below(uint16_t upper_bound)
{
assert(upper_bound == 4);
return 0;
}
BorlandReal48 borland_random_real48_registers(void)
{
return (BorlandReal48){{0x80,0,0,0,0,0}};
}
static bool in_bounds(
int32_t x,
int32_t y,
int32_t maximum_y,
int32_t maximum_x,
int32_t minimum_y,
int32_t minimum_x)
{
return x >= minimum_x && x <= maximum_x &&
y >= minimum_y && y <= maximum_y;
}
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 in_bounds(
(int32_t)win16_read_u32(win16_dgroup_pointer(0x07db)),
(int32_t)win16_read_u32(win16_dgroup_pointer(0x07df)),
maximum_y, maximum_x, minimum_y, minimum_x);
}
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)
{
assert(caller_bp == 0x0300);
return in_bounds(
(int32_t)win16_read_u32(win16_far_add_offset(g_window, 0x0bba)),
(int32_t)win16_read_u32(win16_far_add_offset(g_window, 0x0bbe)),
maximum_y, maximum_x, minimum_y, minimum_x);
}
static Win16FarPtr record_pointer(uint16_t id)
{
return win16_dgroup_pointer((uint16_t)(
0x095b + id * sizeof(TdkpinCollisionRecord)));
}
static void write_record(
uint16_t id, const TdkpinCollisionRecord *record)
{
const uint8_t *bytes = (const uint8_t *)record;
for (uint16_t index = 0; index < sizeof(*record); index++) {
win16_write_u8(
win16_far_add_offset(record_pointer(id), index), bytes[index]);
}
}
static TdkpinCollisionRecord base_record(uint8_t type)
{
TdkpinCollisionRecord record = {0};
record.type = type;
record.subtype = 1;
record.active = 1;
record.bounds_min_x_milli = 0;
record.bounds_min_y_milli = 0;
record.bounds_max_x_milli = 200000;
record.bounds_max_y_milli = 200000;
record.layer_mask = 1;
return record;
}
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, 0);
g_window = win16_make_far_pointer(0x7000, 0);
win16_write_stack_u16(0x0306, win16_far_offset(g_window));
win16_write_stack_u16(0x0308, win16_far_selector(g_window));
win16_write_u16(g_window, 4, 0x2222);
win16_write_u8(win16_far_add_offset(g_window, 0x61), 1);
win16_write_u8(win16_far_add_offset(g_window, 0x0bdd), 1);
win16_write_u16(win16_dgroup_pointer(0x0871), 0, 1);
win16_write_u32(g_window, 0x56, 10000);
g_finish_calls = g_render_calls = g_update_calls = 0;
g_restore_b_calls = g_event_calls = g_sound_calls = 0;
g_score_total = 0;
g_load_calls = g_save_calls = 0;
}
static void set_ball(
int32_t x, int32_t y, int32_t velocity_x, int32_t velocity_y)
{
win16_write_u32(win16_dgroup_pointer(0x07d3), 0, (uint32_t)x);
win16_write_u32(win16_dgroup_pointer(0x07d7), 0, (uint32_t)y);
win16_write_u32(g_window, 0x0baa, (uint32_t)velocity_x);
win16_write_u32(g_window, 0x0bae, (uint32_t)velocity_y);
}
static void test_free_integration_and_speed_clamp(void)
{
prepare_fixture();
set_ball(100000, 100000, 1000, 2000);
win16_write_u32(g_window, 0x5a, 100);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert((int32_t)win16_read_u32(win16_dgroup_pointer(0x07d3)) == 101000);
assert((int32_t)win16_read_u32(win16_dgroup_pointer(0x07d7)) == 102100);
assert(g_update_calls == 1 && g_last_update == 1);
assert(g_render_calls == 1);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x4d)) == 1);
prepare_fixture();
set_ball(100000, 100000, 3000, 4000);
win16_write_u32(g_window, 0x56, 1000);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0baa)) == 600);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == 800);
}
static void test_circle_and_segment_collisions(void)
{
prepare_fixture();
set_ball(100000, 100000, 1000, 0);
TdkpinCollisionRecord circle = base_record(1);
circle.point1_x_milli = 103000;
circle.point1_y_milli = 100000;
circle.radius_milli = borland_i32_to_real48(4000);
write_record(1, &circle);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0baa)) == 0);
assert((int32_t)win16_read_u32(win16_dgroup_pointer(0x07d3)) == 100000);
prepare_fixture();
set_ball(100000, 100000, 0, 1000);
TdkpinCollisionRecord segment = base_record(2);
segment.point1_x_milli = 50000;
segment.point1_y_milli = 101000;
segment.point2_x_milli = 150000;
segment.point2_y_milli = 101000;
write_record(1, &segment);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == 0);
assert((int32_t)win16_read_u32(win16_dgroup_pointer(0x07d7)) == 100000);
prepare_fixture();
set_ball(100000, 100000, 1000, 0);
circle = base_record(1);
circle.point1_x_milli = 103000;
circle.point1_y_milli = 100000;
circle.radius_milli = borland_i32_to_real48(4000);
circle.response_normal = borland_i32_to_real48(1);
circle.flags = 0x0020;
circle.score_low = 100;
write_record(1, &circle);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0baa)) == -1000);
assert(g_event_calls == 1 && g_last_event_flags == 0x0020);
assert(g_score_total == 100);
}
static void test_capture_and_trigger_records(void)
{
prepare_fixture();
set_ball(100000, 100000, 0, 0);
TdkpinCollisionRecord capture = base_record(3);
capture.point1_x_milli = 100000;
capture.point1_y_milli = 100000;
capture.radius_milli = borland_i32_to_real48(20000);
write_record(50, &capture);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert(win16_read_u16(win16_far_add_offset(record_pointer(50), 0x43)) == 1);
assert(win16_read_u32(win16_far_add_offset(g_window, 0x0bca)) == 5);
assert(g_sound_calls == 1 && g_last_sound == 15);
prepare_fixture();
set_ball(100000, 100000, 1000, 0);
TdkpinCollisionRecord trigger = base_record(4);
trigger.point1_x_milli = 0;
trigger.point1_y_milli = 0;
trigger.point2_x_milli = 200000;
trigger.point2_y_milli = 200000;
write_record(6, &trigger);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0baa)) == 900);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0bae)) == -8500);
assert(g_restore_b_calls == 1 && g_last_restore_b == 6);
prepare_fixture();
set_ball(100000, 100000, 1000, 0);
win16_write_u8(win16_dgroup_pointer(0x07d1), 1);
win16_write_u8(win16_dgroup_pointer(0x081b), 2);
trigger = base_record(4);
trigger.point1_x_milli = 101000;
trigger.point1_y_milli = 100000;
trigger.radius_milli = borland_i32_to_real48(5000);
trigger.flags = 0x0040;
trigger.score_low = 100;
write_record(10, &trigger);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert((win16_read_u16(
win16_far_add_offset(record_pointer(10), 0x41)) & 0x4000) != 0);
assert(g_sound_calls == 1 && g_last_sound == 4);
assert(g_event_calls == 1 && g_last_event_id == 10 &&
g_last_event_flags == 0x4040);
assert(g_score_total == 200);
}
static void test_capture_boundary_and_claw(void)
{
prepare_fixture();
set_ball(100000, 100000, 1000, 0);
TdkpinCollisionRecord capture = base_record(3);
capture.point1_x_milli = 103000;
capture.point1_y_milli = 102000;
capture.radius_milli = borland_i32_to_real48(3800);
capture.contact_state = 99;
capture.flags = 0x0010;
write_record(50, &capture);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert(g_event_calls == 1 && g_last_event_id == 50);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_window, 0x0baa)) != 1000);
prepare_fixture();
set_ball(100000, 100000, 0, 0);
capture = base_record(3);
capture.point1_x_milli = 100000;
capture.point1_y_milli = 100000;
capture.radius_milli = borland_i32_to_real48(20000);
capture.contact_state = 1;
write_record(89, &capture);
win16_write_u32(g_window, 0x0bca, 300);
for (uint16_t id = 12; id <= 20; id++) {
TdkpinCollisionRecord gate = base_record(4);
write_record(id, &gate);
}
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert(win16_read_u8(win16_dgroup_pointer(0x07ca)) == 1);
assert(win16_read_u16(win16_dgroup_pointer(0x086f)) == 1);
assert(win16_read_u8(win16_dgroup_pointer(0x0873)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_window, 0x0bdf)) == 1);
for (uint16_t id = 12; id <= 20; id++) {
assert(win16_read_u8(
win16_far_add_offset(record_pointer(id), 0x34)) == 0);
}
}
static void test_out_of_bounds_and_zero_substeps_multiball(void)
{
prepare_fixture();
set_ball(340000, 100000, 1001, 0);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert(g_finish_calls == 1);
prepare_fixture();
win16_write_u8(win16_far_add_offset(g_window, 0x61), 2);
win16_write_u16(win16_dgroup_pointer(0x0871), 0, 0);
tdkpin_simulate_ball_and_dispatch_collision(0x0300);
assert(g_load_calls == 2 && g_save_calls == 2);
assert(g_render_calls == 2 && g_update_calls == 0);
}
int main(void)
{
test_free_integration_and_speed_clamp();
test_circle_and_segment_collisions();
test_capture_and_trigger_records();
test_capture_boundary_and_claw();
test_out_of_bounds_and_zero_substeps_multiball();
return 0;
}
@@ -0,0 +1,112 @@
#include "../tdkpin_gameplay_helpers.h"
#include <assert.h>
#include <string.h>
static uint8_t g_dgroup[0x5000];
static unsigned g_create_dc_calls;
static unsigned g_create_bitmap_calls;
static unsigned g_select_calls;
static unsigned g_blit_calls;
static unsigned g_delete_object_calls;
static unsigned g_delete_dc_calls;
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x3333 && g_create_dc_calls < 3);
return (HDC16)(0x4001 + g_create_dc_calls++);
}
HBITMAP16 CreateCompatibleBitmap16(HDC16 dc, INT16 width, INT16 height)
{
assert(dc == 0x3333 && width == 14 && height == 14);
g_create_bitmap_calls++;
return 0x5000;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
switch (g_select_calls++) {
case 0:
assert(dc == 0x4001 && object == 0x6000);
return 0x7001;
case 1:
assert(dc == 0x4002 && object == 0x6003);
return 0x7002;
case 2:
assert(dc == 0x4003 && object == 0x5000);
return 0x7003;
case 3:
assert(dc == 0x4003 && object == 0x7003);
return 0x5000;
case 4:
assert(dc == 0x4002 && object == 0x7002);
return 0x6003;
default:
assert(g_select_calls == 6);
assert(dc == 0x4001 && object == 0x7001);
return 0x6000;
}
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
assert(raster_operation == 0x00cc0020u);
switch (g_blit_calls++) {
case 0:
assert(destination == 0x4003 && destination_x == 0);
assert(destination_y == 0 && width == 14 && height == 14);
assert(source == 0x4001 && source_x == 84 && source_y == 77);
break;
case 1:
assert(destination == 0x4001 && destination_x == 55);
assert(destination_y == 79 && width == 42 && height == 123);
assert(source == 0x4002 && source_x == 0 && source_y == 0);
break;
default:
assert(g_blit_calls == 3);
assert(destination == 0x4001 && destination_x == 84);
assert(destination_y == 77 && width == 14 && height == 14);
assert(source == 0x4003 && source_x == 0 && source_y == 0);
break;
}
return 1;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(object == 0x5000);
g_delete_object_calls++;
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
assert(dc == (HDC16)(0x4003 - g_delete_dc_calls));
g_delete_dc_calls++;
return 1;
}
int main(void)
{
memset(g_dgroup, 0, sizeof(g_dgroup));
win16_reset_segment_bindings();
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_write_u16(win16_dgroup_pointer(0x085b), 0, 0x6000);
win16_write_u16(win16_dgroup_pointer(0x4311), 0, 0x6003);
tdkpin_composite_player_sprite(3, 0x3333);
assert(g_create_dc_calls == 3 && g_create_bitmap_calls == 1);
assert(g_select_calls == 6 && g_blit_calls == 3);
assert(g_delete_object_calls == 1 && g_delete_dc_calls == 3);
return 0;
}
@@ -0,0 +1,305 @@
#include "../tdkpin_player_state.h"
#include <assert.h>
#include <string.h>
uint8_t g_player_count;
uint8_t g_current_player;
static uint8_t g_stack[0x400];
static uint8_t g_object[0x0c00];
static uint8_t g_dgroup[0x5000];
static Win16FarPtr g_receiver;
static unsigned g_collision_calls;
static unsigned g_restore_record_calls;
static unsigned g_zero_score_calls;
static unsigned g_add_score_calls;
static unsigned g_marker_calls;
static unsigned g_item_calls;
static unsigned g_restore_a_calls;
static unsigned g_restore_b_calls;
static unsigned g_present_calls;
static unsigned g_sprite_calls;
static unsigned g_get_dc_calls;
static unsigned g_release_dc_calls;
static uint16_t g_last_sprite;
static int32_t g_last_shown_score;
static int32_t g_added_delta[4];
static uint8_t g_add_player[4];
void tdkpin_refresh_collision_record(uint16_t caller_bp, uint16_t id)
{
assert(caller_bp == 0x0100);
assert(id == g_collision_calls + 1);
g_collision_calls++;
}
void tdkpin_restore_record_b(uint16_t caller_bp, uint16_t id)
{
assert(caller_bp == 0x0100);
assert(id == 1001 + g_restore_record_calls);
g_restore_record_calls++;
}
void tdkpin_draw_score_from_caller_frame(
uint16_t caller_bp, uint16_t low, uint16_t high)
{
assert(caller_bp == 0x0100);
int32_t value = (int32_t)((uint32_t)low | ((uint32_t)high << 16));
if (value == 0 && g_zero_score_calls == 0) {
g_zero_score_calls++;
} else {
g_last_shown_score = value;
}
}
void tdkpin_add_score_from_caller_frame(uint16_t caller_bp, int32_t delta)
{
assert(caller_bp == 0x0100 && g_add_score_calls < 4);
g_added_delta[g_add_score_calls] = delta;
g_add_player[g_add_score_calls] = g_current_player;
g_add_score_calls++;
}
void tdkpin_refresh_player_progress_markers(uint16_t caller_bp)
{
assert(caller_bp == 0x0100);
assert(g_current_player == g_marker_calls + 1);
g_marker_calls++;
}
void tdkpin_refresh_player_item(uint16_t caller_bp, uint16_t item)
{
assert(caller_bp == 0x0100 && g_current_player == 1);
assert(item == g_item_calls + 1);
g_item_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;
}
static void check_geometry(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(dc == 0x3333);
bool panel = height == 31 && width == 139 && x == 472;
bool marker = height == 11 && width == 11 && x == 372;
bool score_area = height == 123 && width == 42 && y == 79 && x == 55;
assert(panel || marker || score_area);
if (panel || marker) {
uint16_t base = panel ? 276 : 280;
assert(y >= (INT16)base && ((uint16_t)(y - base) % 44u) == 0);
}
}
void tdkpin_restore_background_region_a(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
check_geometry(height, width, y, x, dc);
assert(height != 123);
g_restore_a_calls++;
}
void tdkpin_restore_background_region_b(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
check_geometry(height, width, y, x, dc);
assert(height == 11);
g_restore_b_calls++;
}
void tdkpin_present_background_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
check_geometry(height, width, y, x, dc);
g_present_calls++;
}
void tdkpin_composite_player_sprite(uint16_t sprite_index, HDC16 dc)
{
assert(dc == 0x3333);
g_last_sprite = sprite_index;
g_sprite_calls++;
}
int32_t borland_multiply_i32(int32_t left, int32_t right)
{
return (int32_t)((uint32_t)left * (uint32_t)right);
}
static void prepare_fixture(void)
{
memset(g_stack, 0, sizeof(g_stack));
memset(g_object, 0xa5, sizeof(g_object));
memset(g_dgroup, 0xcc, sizeof(g_dgroup));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_set_stack_state(0x7000, 0);
g_receiver = win16_make_far_pointer(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
win16_write_u16(g_receiver, 4, 0x2222);
g_collision_calls = 0;
g_restore_record_calls = 0;
g_zero_score_calls = 0;
g_add_score_calls = 0;
g_marker_calls = 0;
g_item_calls = 0;
g_restore_a_calls = 0;
g_restore_b_calls = 0;
g_present_calls = 0;
g_sprite_calls = 0;
g_get_dc_calls = 0;
g_release_dc_calls = 0;
g_last_sprite = 0;
g_last_shown_score = 0;
memset(g_added_delta, 0, sizeof(g_added_delta));
memset(g_add_player, 0, sizeof(g_add_player));
}
static void test_initialize_player_state(void)
{
prepare_fixture();
g_player_count = 2;
g_current_player = 4;
for (uint16_t id = 1; id <= 175; id++) {
win16_write_u16(
win16_dgroup_pointer((uint16_t)(
0x095b + id * 0x53u + 0x43u)),
0,
(uint16_t)(0x1000 + id));
}
tdkpin_initialize_player_state(0x0100);
assert(g_collision_calls == 175 && g_restore_record_calls == 5);
assert(g_zero_score_calls == 1);
for (uint16_t player = 1; player <= 4; player++) {
for (uint16_t id = 1; id <= 175; id++) {
assert(win16_read_u16(win16_far_add_offset(
g_receiver,
(uint16_t)(0x036c + player * 0x015eu + id * 2u))) ==
(uint16_t)(0x1000 + id));
}
}
assert(g_add_score_calls == 2 && g_marker_calls == 2);
assert(g_add_player[0] == 1 && g_add_player[1] == 2);
assert(g_added_delta[0] == 0 && g_added_delta[1] == 0);
for (uint16_t player = 1; player <= 2; player++) {
assert(win16_read_u32(win16_dgroup_pointer(
(uint16_t)(0x097e + player * 4u))) == 0);
assert(win16_read_u32(win16_dgroup_pointer(
(uint16_t)(0x098e + player * 4u))) == 0);
assert(win16_read_u8(win16_dgroup_pointer(
(uint16_t)(0x09a1 + player))) == 1);
assert(win16_read_u16(win16_dgroup_pointer(
(uint16_t)(0x09a4 + player * 2u))) == 0);
assert(win16_read_u8(win16_dgroup_pointer(
(uint16_t)(0x0816 + player))) == 1);
assert(win16_read_u16(win16_dgroup_pointer(
(uint16_t)(0x081f + player * 2u))) ==
(player == 1 ? 2 : 3));
assert(win16_read_u32(win16_dgroup_pointer(
(uint16_t)(0x0835 + player * 4u))) == 1);
assert(win16_read_u8(win16_dgroup_pointer(
(uint16_t)(0x081a + player))) == 1);
for (uint16_t item = 1; item <= 20; item++) {
assert(win16_read_u8(win16_far_add_offset(
g_receiver,
(uint16_t)(0x0467 + player * 0x14u + item))) == 0);
}
}
assert(g_current_player == 1 && g_item_calls == 20);
assert(win16_read_u8(win16_dgroup_pointer(0x07d2)) == 0);
assert(win16_read_u8(win16_dgroup_pointer(0x0874)) == 0);
}
static void test_redraw_all_players(void)
{
prepare_fixture();
g_player_count = 3;
g_current_player = 2;
win16_write_u32(win16_dgroup_pointer(0x0996), 0, 10);
win16_write_u8(win16_dgroup_pointer(0x09a3), 3);
win16_write_u16(win16_dgroup_pointer(0x09a8), 0, 5);
tdkpin_redraw_all_players(0x0100);
assert(g_add_score_calls == 3);
assert(g_add_player[0] == 1 && g_added_delta[0] == -1);
assert(g_add_player[1] == 2 && g_added_delta[1] == 0);
assert(g_add_player[2] == 3 && g_added_delta[2] == -1);
assert(g_last_shown_score == 30);
assert(g_sprite_calls == 1 && g_last_sprite == 5);
assert(g_restore_a_calls == 4 && g_restore_b_calls == 2);
assert(g_present_calls == 7);
assert(g_current_player == 2);
assert(g_get_dc_calls == 1 && g_release_dc_calls == 1);
}
static void set_effect(uint16_t effect)
{
win16_write_u16(
win16_dgroup_pointer((uint16_t)(0x09a4 + g_current_player * 2u)),
0,
effect);
}
static void test_player_sprite_effects(void)
{
static const uint32_t additions[5] = {
10000u, 20000u, 50000u, 100000u, 200000u};
for (uint16_t effect = 1; effect <= 5; effect++) {
prepare_fixture();
g_current_player = 2;
set_effect(effect);
win16_write_u32(win16_dgroup_pointer(0x0996), 0, 0xfffffff0u);
win16_write_u8(win16_dgroup_pointer(0x09a3), 3);
tdkpin_apply_player_sprite_effect(0x0100, 0);
uint32_t expected = 0xfffffff0u + additions[effect - 1u];
assert(win16_read_u32(win16_dgroup_pointer(0x0996)) == expected);
assert((uint32_t)g_last_shown_score == expected * 3u);
assert(g_add_score_calls == 0);
}
prepare_fixture();
g_current_player = 2;
set_effect(6);
win16_write_u32(win16_dgroup_pointer(0x0996), 0, 12345);
win16_write_u8(win16_dgroup_pointer(0x09a3), 4);
tdkpin_apply_player_sprite_effect(0x0100, 0);
assert(g_add_score_calls == 1 && g_added_delta[0] == 49380);
assert(win16_read_u32(win16_dgroup_pointer(0x0996)) == 0);
assert(g_last_shown_score == 0);
prepare_fixture();
g_current_player = 2;
set_effect(7);
win16_write_u16(win16_dgroup_pointer(0x399a), 0, 55);
win16_write_u8(win16_dgroup_pointer(0x0874), 0);
win16_write_u8(win16_far_add_offset(g_receiver, 0x0be0), 0);
tdkpin_apply_player_sprite_effect(0x0100, 55);
assert(win16_read_u8(win16_dgroup_pointer(0x0874)) == 0);
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x0be0)) == 0);
tdkpin_apply_player_sprite_effect(0x0100, -1);
assert(win16_read_u8(win16_dgroup_pointer(0x0874)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x0be0)) == 1);
}
int main(void)
{
test_initialize_player_state();
test_redraw_all_players();
test_player_sprite_effects();
return 0;
}
@@ -0,0 +1,217 @@
#include "../tdkpin_program_entry.h"
#include "../tdkpin_application.h"
#include <assert.h>
#include <setjmp.h>
uint32_t g_tdkpin_windows_version;
Win16FarPtr g_win16_message_box;
static jmp_buf g_exit_jump;
static uint16_t g_exit_status;
static uint32_t g_version_result;
static unsigned g_order[16];
static unsigned g_order_count;
static unsigned g_message_calls;
static unsigned g_construct_calls;
static unsigned g_run_calls;
static unsigned g_destruct_calls;
enum CallOrder {
CALL_INIT_TASK = 1,
CALL_BORLAND_STARTUP,
CALL_STACK,
CALL_TPWINCRT,
CALL_MULTIMEDIA,
CALL_UNIT_91,
CALL_UNIT_86,
CALL_UNIT_72,
CALL_GET_VERSION,
CALL_MESSAGE_BOX,
CALL_CONSTRUCT,
CALL_RUN,
CALL_DESTRUCT,
CALL_EXIT,
};
static void record_call(unsigned call)
{
assert(g_order_count < sizeof(g_order) / sizeof(*g_order));
g_order[g_order_count++] = call;
}
BorlandWin16StartupRegisters InitTask16(void)
{
record_call(CALL_INIT_TASK);
return (BorlandWin16StartupRegisters){
.loader_ax = 1,
.command_line = win16_make_far_pointer(0x4444, 0x0100),
.show_command = 7,
.previous_instance = 0,
.instance = 0x2222,
};
}
void borland_initialize_win16_runtime(
const BorlandWin16StartupRegisters *registers)
{
assert(registers->loader_ax == 1 && registers->instance == 0x2222);
assert(registers->command_line ==
win16_make_far_pointer(0x4444, 0x0100));
record_call(CALL_BORLAND_STARTUP);
}
void tdkpin_initialize_stack_guard(void)
{
record_call(CALL_STACK);
}
void tpwincrt_initialize_runtime_unit(void)
{
record_call(CALL_TPWINCRT);
}
void multimedia_initialize_runtime_unit(void)
{
record_call(CALL_MULTIMEDIA);
}
void tdkpin_initialize_unit_ordinal_91(void)
{
record_call(CALL_UNIT_91);
}
void tdkpin_initialize_unit_ordinal_86(void)
{
record_call(CALL_UNIT_86);
}
void tdkpin_initialize_unit_ordinal_72(void)
{
record_call(CALL_UNIT_72);
}
uint32_t GetVersion16(void)
{
record_call(CALL_GET_VERSION);
return g_version_result;
}
INT16 win16_call_message_box_far(
Win16FarPtr procedure,
HWND16 owner,
Win16FarPtr text,
Win16FarPtr caption,
UINT16 type)
{
assert(procedure == g_win16_message_box);
assert(owner == 0);
assert(text == win16_dgroup_pointer(0x0396));
assert(caption == win16_dgroup_pointer(0x03c8));
assert(type == 0x10);
g_message_calls++;
record_call(CALL_MESSAGE_BOX);
return 1;
}
Win16FarPtr object_windows_application_construct(
Win16FarPtr object, uint16_t vmt, Win16FarPtr name)
{
assert(object == win16_dgroup_pointer(0x0748));
assert(vmt == 0x0024);
assert(name == win16_dgroup_pointer(0x03e1));
win16_write_u16(object, 0, vmt);
g_construct_calls++;
record_call(CALL_CONSTRUCT);
return object;
}
void win16_call_object_method(
Win16FarPtr procedure, Win16FarPtr object)
{
assert(procedure == win16_make_far_pointer(0x6000, 0x1111));
assert(object == win16_dgroup_pointer(0x0748));
g_run_calls++;
record_call(CALL_RUN);
}
void win16_call_object_destructor(
Win16FarPtr procedure, Win16FarPtr object, uint16_t dispose_mode)
{
assert(procedure == win16_make_far_pointer(0x6000, 0x2222));
assert(object == win16_dgroup_pointer(0x0748));
assert(dispose_mode == 0);
g_destruct_calls++;
record_call(CALL_DESTRUCT);
}
_Noreturn void borland_runtime_exit(uint16_t status)
{
g_exit_status = status;
record_call(CALL_EXIT);
longjmp(g_exit_jump, 1);
}
static void write_far(
Win16FarPtr base, uint16_t offset, Win16FarPtr value)
{
win16_write_u16(base, offset, win16_far_offset(value));
win16_write_u16(
base, (uint16_t)(offset + 2), win16_far_selector(value));
}
static void run_case(uint32_t version, bool supported)
{
g_version_result = version;
g_order_count = 0;
g_message_calls = 0;
g_construct_calls = 0;
g_run_calls = 0;
g_destruct_calls = 0;
if (setjmp(g_exit_jump) == 0) {
tdkpin_ne_program_entry();
assert(false);
}
assert(g_exit_status == 0);
assert(g_tdkpin_windows_version == version);
for (unsigned index = 0; index < 9; index++) {
assert(g_order[index] == index + 1);
}
if (supported) {
assert(g_message_calls == 0);
assert(g_construct_calls == 1 && g_run_calls == 1 &&
g_destruct_calls == 1);
assert(g_order_count == 13);
assert(g_order[9] == CALL_CONSTRUCT);
assert(g_order[10] == CALL_RUN);
assert(g_order[11] == CALL_DESTRUCT);
assert(g_order[12] == CALL_EXIT);
} else {
assert(g_message_calls == 1);
assert(g_construct_calls == 0 && g_run_calls == 0 &&
g_destruct_calls == 0);
assert(g_order_count == 11);
assert(g_order[9] == CALL_MESSAGE_BOX);
assert(g_order[10] == CALL_EXIT);
}
}
int main(void)
{
static uint8_t dgroup[0x800];
win16_reset_segment_bindings();
win16_bind_segment(0x7000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x7000);
g_win16_message_box = win16_make_far_pointer(0x5000, 0x0100);
Win16FarPtr vmt = win16_dgroup_pointer(0x0024);
write_far(vmt, 0x1c, win16_make_far_pointer(0x6000, 0x1111));
write_far(vmt, 0x08, win16_make_far_pointer(0x6000, 0x2222));
run_case(0xabcd6302u, false); /* major 2 */
run_case(0xabcd0903u, false); /* Windows 3.9 */
run_case(0xabcd0a03u, true); /* Windows 3.10 */
run_case(0xabcd0004u, true); /* Windows 4.x */
return 0;
}
@@ -0,0 +1,49 @@
#include "../tdkpin_random.h"
#include <assert.h>
uint32_t g_borland_random_seed;
static uint32_t expected_next(uint32_t seed)
{
return seed * 0x08088405u + 1u;
}
int main(void)
{
g_borland_random_seed = 0x12345678;
uint32_t first = expected_next(g_borland_random_seed);
assert(borland_next_random() == first);
uint32_t second = expected_next(first);
uint16_t expected_below = (uint16_t)(((uint64_t)second * 3800) >> 32);
assert(borland_random_below(3800) == expected_below);
uint32_t third = expected_next(second);
int8_t expected_exponent = 0;
if (third != 0) {
expected_exponent = -128;
while ((third & 0x80000000u) == 0) {
third <<= 1;
expected_exponent--;
}
}
assert(borland_random_exponent() == expected_exponent);
g_borland_random_seed = 7;
(void)borland_random_below(0);
assert(g_borland_random_seed == expected_next(7));
g_borland_random_seed = 0xfedcba98;
uint32_t unit_seed = expected_next(g_borland_random_seed);
assert(borland_random_unit_interval() ==
(double)unit_seed / 4294967296.0);
assert(borland_random_unit_interval() >= 0.0 &&
borland_random_unit_interval() < 1.0);
win16_reset_segment_bindings();
win16_set_dos_time_words(0x33441122);
borland_seed_random_from_dos_time();
assert(g_borland_random_seed == 0x33441122);
return 0;
}
+387
View File
@@ -0,0 +1,387 @@
#include "../tdkpin_real48.h"
#include <assert.h>
#include <limits.h>
#include <setjmp.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
static jmp_buf g_runtime_error_jump;
static uint16_t g_runtime_error_code;
_Noreturn void borland_runtime_error(uint16_t code)
{
g_runtime_error_code = code;
longjmp(g_runtime_error_jump, 1);
}
static BorlandReal48 real48(
uint8_t exponent,
uint8_t byte_1,
uint8_t byte_2,
uint8_t byte_3,
uint8_t byte_4,
uint8_t byte_5)
{
return (BorlandReal48){{
exponent, byte_1, byte_2, byte_3, byte_4, byte_5}};
}
static void assert_encoding(
BorlandReal48 actual, BorlandReal48 expected)
{
assert(memcmp(actual.bytes, expected.bytes, sizeof(actual.bytes)) == 0);
}
static void test_integer_encoding(void)
{
assert_encoding(
borland_i32_to_real48_registers(0),
real48(0, 0, 0, 0, 0, 0));
assert_encoding(
borland_i32_to_real48_registers(1),
real48(0x81, 0, 0, 0, 0, 0));
assert_encoding(
borland_i32_to_real48_registers(-1),
real48(0x81, 0, 0, 0, 0, 0x80));
assert_encoding(
borland_i32_to_real48_registers(2),
real48(0x82, 0, 0, 0, 0, 0));
assert_encoding(
borland_i32_to_real48_registers(INT32_MAX),
real48(0x9f, 0x00, 0xfe, 0xff, 0xff, 0x7f));
assert_encoding(
borland_i32_to_real48_registers(INT32_MIN),
real48(0xa0, 0, 0, 0, 0, 0x80));
assert_encoding(
borland_i32_to_real48_registers(123456789),
real48(0x9b, 0x00, 0xa0, 0xa2, 0x79, 0x6b));
assert_encoding(
borland_i32_to_real48_registers(-123456789),
real48(0x9b, 0x00, 0xa0, 0xa2, 0x79, 0xeb));
static const int32_t values[] = {
INT32_MIN, -123456789, -65536, -32768, -1,
0, 1, 32767, 65535, 123456789, INT32_MAX,
};
for (size_t index = 0; index < sizeof(values) / sizeof(values[0]); index++) {
BorlandReal48 encoded = borland_i32_to_real48(values[index]);
BorlandReal48IntegerResult decoded =
borland_real48_to_i32_registers(encoded, false);
assert(!decoded.overflow && decoded.value == values[index]);
}
}
static void test_round_and_truncate(void)
{
BorlandReal48 positive_half = real48(0x80, 0, 0, 0, 0, 0);
BorlandReal48 negative_half = real48(0x80, 0, 0, 0, 0, 0x80);
BorlandReal48 below_half =
real48(0x7f, 0xff, 0xff, 0xff, 0xff, 0x7f);
BorlandReal48 positive_one_and_half =
real48(0x81, 0, 0, 0, 0, 0x40);
BorlandReal48 below_one_and_half =
real48(0x81, 0xff, 0xff, 0xff, 0xff, 0x3f);
BorlandReal48 positive_one_and_three_quarters =
real48(0x81, 0, 0, 0, 0, 0x60);
BorlandReal48 negative_one_and_three_quarters =
real48(0x81, 0, 0, 0, 0, 0xe0);
BorlandReal48IntegerResult converted =
borland_real48_to_i32_registers(positive_half, false);
assert(!converted.overflow && converted.value == 0);
converted = borland_real48_to_i32_registers(positive_half, true);
assert(!converted.overflow && converted.value == 1);
converted = borland_real48_to_i32_registers(negative_half, true);
assert(!converted.overflow && converted.value == -1);
converted = borland_real48_to_i32_registers(below_half, true);
assert(!converted.overflow && converted.value == 0);
converted =
borland_real48_to_i32_registers(positive_one_and_half, true);
assert(!converted.overflow && converted.value == 2);
converted = borland_real48_to_i32_registers(below_one_and_half, true);
assert(!converted.overflow && converted.value == 1);
converted = borland_real48_to_i32_registers(
positive_one_and_three_quarters, false);
assert(!converted.overflow && converted.value == 1);
converted = borland_real48_to_i32_registers(
negative_one_and_three_quarters, false);
assert(!converted.overflow && converted.value == -1);
assert(borland_real48_truncate_to_i32(
positive_one_and_three_quarters) == 1);
assert(borland_real48_truncate_to_i32(
negative_one_and_three_quarters) == -1);
assert(borland_real48_round_to_i32(
positive_one_and_three_quarters) == 2);
}
static void test_integer_overflow(void)
{
BorlandReal48 positive_two_to_31 =
real48(0xa0, 0, 0, 0, 0, 0);
BorlandReal48 negative_two_to_31 =
real48(0xa0, 0, 0, 0, 0, 0x80);
BorlandReal48 below_negative_two_to_31 =
real48(0xa0, 0, 1, 0, 0, 0x80);
BorlandReal48 huge = real48(0xff, 0, 0, 0, 0, 0);
BorlandReal48IntegerResult converted =
borland_real48_to_i32_registers(positive_two_to_31, false);
assert(converted.overflow && converted.value == INT32_MIN);
converted =
borland_real48_to_i32_registers(negative_two_to_31, false);
assert(!converted.overflow && converted.value == INT32_MIN);
converted = borland_real48_to_i32_registers(
below_negative_two_to_31, false);
assert(converted.overflow && converted.value == INT32_MAX);
converted = borland_real48_to_i32_registers(huge, true);
assert(converted.overflow);
g_runtime_error_code = 0;
if (setjmp(g_runtime_error_jump) == 0) {
(void)borland_real48_round_to_i32(positive_two_to_31);
assert(false);
}
assert(g_runtime_error_code == 207);
if (setjmp(g_runtime_error_jump) == 0) {
(void)borland_real48_truncate_to_i32(positive_two_to_31);
assert(false);
}
assert(g_runtime_error_code == 207);
}
static void test_comparison(void)
{
BorlandReal48 zero = real48(0, 0, 0, 0, 0, 0);
BorlandReal48 noncanonical_zero = real48(0, 1, 2, 3, 4, 5);
BorlandReal48 one = real48(0x81, 0, 0, 0, 0, 0);
BorlandReal48 one_and_half = real48(0x81, 0, 0, 0, 0, 0x40);
BorlandReal48 two = real48(0x82, 0, 0, 0, 0, 0);
BorlandReal48 minus_one = real48(0x81, 0, 0, 0, 0, 0x80);
BorlandReal48 minus_two = real48(0x82, 0, 0, 0, 0, 0x80);
assert(borland_real48_compare_magnitude_registers(
zero, noncanonical_zero) == 0);
assert(borland_real48_compare_magnitude_registers(one, one) == 0);
assert(borland_real48_compare_magnitude_registers(one, one_and_half) < 0);
assert(borland_real48_compare_magnitude_registers(two, one_and_half) > 0);
assert(borland_real48_compare(zero, one) < 0);
assert(borland_real48_compare(one_and_half, one) > 0);
assert(borland_real48_compare(minus_one, one) < 0);
assert(borland_real48_compare(minus_two, minus_one) < 0);
assert(borland_real48_compare(minus_one, minus_two) > 0);
}
static void test_arithmetic(void)
{
BorlandReal48 zero = real48(0, 0, 0, 0, 0, 0);
BorlandReal48 half = real48(0x80, 0, 0, 0, 0, 0);
BorlandReal48 one = real48(0x81, 0, 0, 0, 0, 0);
BorlandReal48 minus_one = real48(0x81, 0, 0, 0, 0, 0x80);
BorlandReal48 one_and_half = real48(0x81, 0, 0, 0, 0, 0x40);
BorlandReal48 one_and_three_quarters =
real48(0x81, 0, 0, 0, 0, 0x60);
BorlandReal48 three_quarters = real48(0x80, 0, 0, 0, 0, 0x40);
BorlandReal48 minus_half = real48(0x80, 0, 0, 0, 0, 0x80);
BorlandReal48 two = real48(0x82, 0, 0, 0, 0, 0);
BorlandReal48 two_and_quarter = real48(0x82, 0, 0, 0, 0, 0x10);
BorlandReal48 three = real48(0x82, 0, 0, 0, 0, 0x40);
BorlandReal48 smallest = real48(1, 0, 0, 0, 0, 0);
BorlandReal48 largest =
real48(0xff, 0xff, 0xff, 0xff, 0xff, 0x7f);
assert_encoding(borland_real48_add(one, one), two);
assert_encoding(borland_real48_add(one, minus_one), zero);
assert_encoding(borland_real48_add(one_and_half, half), two);
assert_encoding(borland_real48_subtract(one_and_half, half), one);
assert_encoding(
borland_real48_multiply(one_and_half, half), three_quarters);
assert_encoding(borland_real48_multiply(one_and_half, two), three);
assert_encoding(borland_real48_square(one_and_half), two_and_quarter);
assert_encoding(borland_real48_divide(one_and_half, half), three);
assert_encoding(
borland_real48_add_preserving_right(one_and_half, half).value, two);
assert_encoding(
borland_real48_subtract_preserving_right(one_and_half, half).value,
one);
assert_encoding(
borland_real48_multiply_preserving_right(one_and_half, half).value,
three_quarters);
assert_encoding(
borland_real48_divide_preserving_right(one_and_half, half).value,
three);
assert_encoding(borland_real48_multiply(smallest, half), zero);
assert_encoding(borland_real48_add(smallest, half), half);
assert_encoding(borland_real48_subtract(smallest, half), minus_half);
assert_encoding(
borland_real48_divide(smallest, half),
real48(2, 0, 0, 0, 0, 0));
assert_encoding(borland_real48_integer_part(half), zero);
assert_encoding(borland_real48_fractional_part(half), half);
assert_encoding(borland_real48_integer_part(one_and_three_quarters), one);
assert_encoding(
borland_real48_fractional_part(one_and_three_quarters),
three_quarters);
assert_encoding(borland_real48_sqrt(zero), zero);
assert_encoding(borland_real48_sqrt(one), one);
assert_encoding(borland_real48_sqrt(two),
real48(0x81, 0xfa, 0x33, 0xf3, 0x04, 0x35));
assert_encoding(
borland_real48_sqrt(real48(0x83, 0, 0, 0, 0, 0)), two);
assert_encoding(
borland_real48_sqrt(one_and_half),
real48(0x81, 0x49, 0xa0, 0x70, 0xc4, 0x1c));
assert_encoding(
borland_real48_arctan_series(zero), zero);
assert_encoding(
borland_real48_arctan_series(
real48(0x7f, 0, 0, 0, 0, 0)),
real48(0x7e, 0xb6, 0xb6, 0xaf, 0xdb, 0x7a));
assert_encoding(
borland_real48_arctan_series(half),
real48(0x7f, 0x0b, 0x0c, 0x35, 0x62, 0x6d));
assert_encoding(
borland_real48_arctan_series(
real48(0x7f, 0xe7, 0xcf, 0xcc, 0x13, 0x54)),
real48(0x7f, 0x49, 0xe2, 0xc2, 0x0f, 0x49));
assert_encoding(borland_real48_arctan(zero), zero);
assert_encoding(
borland_real48_arctan(real48(0x7e, 0, 0, 0, 0, 0)),
real48(0x7d, 0x60, 0xd5, 0xd4, 0xad, 0x7e));
assert_encoding(
borland_real48_arctan(half),
real48(0x7f, 0x0a, 0x2b, 0x38, 0x63, 0x6d));
assert_encoding(
borland_real48_arctan(one),
real48(0x80, 0x21, 0xa2, 0xda, 0x0f, 0x49));
assert_encoding(
borland_real48_arctan(two),
real48(0x81, 0x5f, 0x97, 0x0c, 0xb7, 0x0d));
assert_encoding(
borland_real48_arctan(minus_one),
real48(0x80, 0x21, 0xa2, 0xda, 0x0f, 0xc9));
assert_encoding(
borland_real48_ln(half),
real48(0x80, 0xd0, 0xf7, 0x17, 0x72, 0xb1));
assert_encoding(borland_real48_ln(one), zero);
assert_encoding(
borland_real48_ln(two),
real48(0x80, 0xd5, 0xf7, 0x17, 0x72, 0x31));
assert_encoding(
borland_real48_ln(one_and_half),
real48(0x7f, 0xff, 0x65, 0x1f, 0x99, 0x4f));
assert_encoding(
borland_real48_ln(real48(0x84, 0, 0, 0, 0, 0x20)),
real48(0x82, 0xac, 0xdd, 0x8d, 0x5d, 0x13));
assert_encoding(
borland_real48_exp(real48(0x82, 0, 0, 0, 0, 0x80)),
real48(0x7e, 0xfc, 0x1d, 0x55, 0x95, 0x0a));
assert_encoding(
borland_real48_exp(minus_one),
real48(0x7f, 0x67, 0xb1, 0xb1, 0x5a, 0x3c));
assert_encoding(
borland_real48_exp(real48(0x80, 0, 0, 0, 0, 0x80)),
real48(0x80, 0x7b, 0xe3, 0x97, 0x45, 0x1b));
assert_encoding(borland_real48_exp(zero), one);
assert_encoding(
borland_real48_exp(half),
real48(0x81, 0xf2, 0x70, 0x4c, 0x09, 0x53));
assert_encoding(
borland_real48_exp(one),
real48(0x82, 0xa3, 0x58, 0x54, 0xf8, 0x2d));
assert_encoding(
borland_real48_exp(two),
real48(0x83, 0xa7, 0xc6, 0x25, 0x73, 0x6c));
assert_encoding(
borland_real48_exp(real48(0x84, 0, 0, 0, 0, 0x20)),
real48(0x8f, 0xa8, 0x7c, 0xee, 0x14, 0x2c));
BorlandReal48 pi_over_6 = real48(0x80, 0x6a, 0xc1, 0x91, 0x0a, 0x06);
BorlandReal48 pi_over_2 = real48(0x81, 0x21, 0xa2, 0xda, 0x0f, 0x49);
BorlandReal48 pi = real48(0x82, 0x21, 0xa2, 0xda, 0x0f, 0x49);
BorlandReal48 two_pi = real48(0x83, 0x21, 0xa2, 0xda, 0x0f, 0x49);
assert_encoding(borland_real48_sin(zero), zero);
assert_encoding(
borland_real48_sin(pi_over_6),
real48(0x7f, 0xfe, 0xff, 0xff, 0xff, 0x7f));
assert_encoding(
borland_real48_sin(pi_over_2),
real48(0x80, 0xfa, 0xff, 0xff, 0xff, 0x7f));
assert_encoding(borland_real48_sin(pi), zero);
assert_encoding(borland_real48_sin(two_pi), zero);
pi_over_2.bytes[5] |= 0x80;
assert_encoding(
borland_real48_sin(pi_over_2),
real48(0x80, 0xf9, 0xff, 0xff, 0xff, 0xff));
assert_encoding(
borland_real48_sin(one),
real48(0x80, 0x48, 0x78, 0xa4, 0x6a, 0x57));
assert_encoding(
borland_real48_cos(zero),
real48(0x80, 0xfa, 0xff, 0xff, 0xff, 0x7f));
assert_encoding(
borland_real48_cos(pi_over_6),
real48(0x80, 0xc2, 0x42, 0xd7, 0xb3, 0x5d));
pi_over_2.bytes[5] &= 0x7f;
assert_encoding(borland_real48_cos(pi_over_2), zero);
assert_encoding(
borland_real48_cos(pi),
real48(0x80, 0xf9, 0xff, 0xff, 0xff, 0xff));
assert_encoding(
borland_real48_cos(two_pi),
real48(0x80, 0xf9, 0xff, 0xff, 0xff, 0x7f));
pi_over_2.bytes[5] |= 0x80;
assert_encoding(borland_real48_cos(pi_over_2), zero);
assert_encoding(
borland_real48_cos(one),
real48(0x80, 0xa8, 0x7d, 0x40, 0x51, 0x0a));
one_and_three_quarters.bytes[5] |= 0x80;
assert_encoding(
borland_real48_integer_part(one_and_three_quarters), minus_one);
three_quarters.bytes[5] |= 0x80;
assert_encoding(
borland_real48_fractional_part(one_and_three_quarters),
three_quarters);
BorlandReal48Result overflow =
borland_real48_add_registers(largest, largest);
assert(overflow.overflow && !overflow.divide_by_zero);
overflow = borland_real48_multiply_registers(largest, largest);
assert(overflow.overflow && !overflow.divide_by_zero);
BorlandReal48Result division =
borland_real48_divide_registers(one, zero);
assert(division.divide_by_zero && !division.overflow);
g_runtime_error_code = 0;
if (setjmp(g_runtime_error_jump) == 0) {
(void)borland_real48_add(largest, largest);
assert(false);
}
assert(g_runtime_error_code == 205);
if (setjmp(g_runtime_error_jump) == 0) {
(void)borland_real48_divide(one, zero);
assert(false);
}
assert(g_runtime_error_code == 200);
if (setjmp(g_runtime_error_jump) == 0) {
(void)borland_real48_sqrt(minus_one);
assert(false);
}
assert(g_runtime_error_code == 207);
if (setjmp(g_runtime_error_jump) == 0) {
(void)borland_real48_ln(minus_one);
assert(false);
}
assert(g_runtime_error_code == 207);
}
int main(void)
{
test_integer_encoding();
test_round_and_truncate();
test_integer_overflow();
test_comparison();
test_arithmetic();
return 0;
}
+119
View File
@@ -0,0 +1,119 @@
#include "../tdkpin_render.h"
#include <assert.h>
static uint8_t g_dgroup[0x1000];
static unsigned g_step;
static unsigned g_mode;
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x1111);
g_step++;
return (HDC16)(0x2000 + g_step);
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
g_step++;
if (g_mode == 4) {
if (g_step == 2) {
assert(dc == 0x2001 && object == 0x3000);
return 0x4000;
}
assert(g_step == 4 && dc == 0x2001 && object == 0x4000);
return 0x3000;
}
if (g_step == 3) {
assert(dc == 0x2001 && object == 0x3000);
return 0x4000;
}
if (g_step == 4) {
HGDIOBJ16 expected = g_mode == 1 ? 0x3333 : (g_mode == 2 ? 0x3002 : 0x3001);
assert(dc == 0x2002 && object == expected);
return 0x4001;
}
if (g_step == 6) {
assert(dc == 0x2002 && object == 0x4001);
return 0;
}
assert(g_step == 7 && dc == 0x2001 && object == 0x4000);
return 0;
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
g_step++;
assert(raster_operation == 0x00cc0020);
assert(width == 30 && height == 40);
if (g_mode == 4) {
assert(g_step == 3 && destination == 0x1111 && source == 0x2001);
assert(destination_x == 10 && destination_y == 20);
assert(source_x == 10 && source_y == 20);
} else {
assert(g_step == 5 && destination == 0x2001 && source == 0x2002);
assert(destination_x == 10 && destination_y == 20);
if (g_mode == 1) {
assert(source_x == 0 && source_y == 0);
} else {
assert(source_x == 10 && source_y == 20);
}
}
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
g_step++;
if (g_mode == 4) {
assert(g_step == 5 && dc == 0x2001);
} else if (g_step == 8) {
assert(dc == 0x2002);
} else {
assert(g_step == 9 && dc == 0x2001);
}
return 1;
}
static void run_mode(unsigned mode)
{
g_mode = mode;
g_step = 0;
if (mode == 1) {
tdkpin_blit_bitmap_to_background(0x3333, 40, 30, 20, 10, 0x1111);
assert(g_step == 9);
} else if (mode == 2) {
tdkpin_restore_background_region_a(40, 30, 20, 10, 0x1111);
assert(g_step == 9);
} else if (mode == 3) {
tdkpin_restore_background_region_b(40, 30, 20, 10, 0x1111);
assert(g_step == 9);
} else {
tdkpin_present_background_region(40, 30, 20, 10, 0x1111);
assert(g_step == 5);
}
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_write_u16(win16_make_far_pointer(0x5000, 0x085b), 0, 0x3000);
win16_write_u16(win16_make_far_pointer(0x5000, 0x085d), 0, 0x3001);
win16_write_u16(win16_make_far_pointer(0x5000, 0x085f), 0, 0x3002);
run_mode(1);
run_mode(2);
run_mode(3);
run_mode(4);
return 0;
}
@@ -0,0 +1,66 @@
#include "../tdkpin_resources.h"
#include <assert.h>
#include <string.h>
static void hash_u16(uint64_t *hash, uint16_t value)
{
for (unsigned byte = 0; byte < 2; byte++) {
*hash ^= (uint8_t)(value >> (byte * 8));
*hash *= UINT64_C(1099511628211);
}
}
static void hash_u32(uint64_t *hash, uint32_t value)
{
for (unsigned byte = 0; byte < 4; byte++) {
*hash ^= (uint8_t)(value >> (byte * 8));
*hash *= UINT64_C(1099511628211);
}
}
int main(void)
{
size_t direct = 0;
size_t system_resolved = 0;
size_t linked_unused = 0;
uint64_t manifest_fields_hash = UINT64_C(14695981039346656037);
for (size_t index = 0; index < TDKPIN_RESOURCE_COUNT; index++) {
const TdkpinResourceDescriptor *entry = &tdkpin_resource_catalog[index];
assert(entry->meaningful_size > 0);
assert(entry->meaningful_size <= entry->allocated_size);
assert(entry->role != NULL && entry->role[0] != '\0');
for (size_t earlier = 0; earlier < index; earlier++) {
assert(entry->type != tdkpin_resource_catalog[earlier].type ||
entry->id != tdkpin_resource_catalog[earlier].id);
}
if (entry->disposition == TDKPIN_RESOURCE_DIRECT) {
direct++;
} else if (entry->disposition == TDKPIN_RESOURCE_SYSTEM_RESOLVED) {
system_resolved++;
} else {
assert(entry->disposition == TDKPIN_RESOURCE_LINKED_UNUSED);
linked_unused++;
}
hash_u16(&manifest_fields_hash, (uint16_t)entry->type);
hash_u16(&manifest_fields_hash, entry->id);
hash_u32(&manifest_fields_hash, entry->file_offset);
hash_u32(&manifest_fields_hash, entry->allocated_size);
hash_u32(&manifest_fields_hash, entry->meaningful_size);
hash_u16(&manifest_fields_hash, entry->flags);
hash_u16(&manifest_fields_hash, entry->width);
hash_u16(&manifest_fields_hash, entry->height);
hash_u32(&manifest_fields_hash, entry->sample_rate);
}
assert(direct == 57);
assert(system_resolved == 2);
assert(linked_unused == 4);
assert(manifest_fields_hash == UINT64_C(0x29d77e86100b5e75));
assert(tdkpin_resource_catalog[0].type == TDKPIN_RESOURCE_DAT);
assert(tdkpin_resource_catalog[0].id == 400);
assert(tdkpin_resource_catalog[62].type == TDKPIN_RESOURCE_DIALOG);
assert(tdkpin_resource_catalog[62].id == 32517);
assert(strcmp(tdkpin_resource_catalog[54].role,
"image selected by GROUP_ICON 996") == 0);
return 0;
}
@@ -0,0 +1,155 @@
#include "../tdkpin_runtime_ui.h"
#include <assert.h>
#include <setjmp.h>
#include <stdbool.h>
#include <string.h>
uint8_t g_tpwincrt_has_focus;
uint8_t g_tpwincrt_caret_enabled;
uint8_t g_tpwincrt_break_check_enabled;
uint16_t g_tpwincrt_input_count;
uint8_t g_tpwincrt_input_buffer[64];
uint16_t g_tpwincrt_view_column;
uint16_t g_tpwincrt_view_row;
uint16_t g_tpwincrt_max_view_column;
uint16_t g_tpwincrt_max_view_row;
uint16_t g_tpwincrt_visible_columns;
uint16_t g_tpwincrt_visible_rows;
uint16_t g_tpwincrt_character_width;
uint16_t g_tpwincrt_character_height;
uint8_t g_tpwincrt_window_created;
HWND16 g_tpwincrt_window;
static unsigned g_destroy_caret_calls;
static uint16_t g_exit_status;
static jmp_buf g_exit_jump;
static INT16 g_key_state;
static unsigned g_scroll_window_calls;
BOOL16 DestroyCaret16(void)
{
g_destroy_caret_calls++;
return 1;
}
_Noreturn void borland_runtime_exit(uint16_t status)
{
g_exit_status = status;
longjmp(g_exit_jump, 1);
}
INT16 GetKeyState16(INT16 virtual_key)
{
assert(virtual_key == 0x11);
return g_key_state;
}
INT16 SetScrollPos16(
HWND16 window, INT16 bar, INT16 position, BOOL16 redraw)
{
assert(window == g_tpwincrt_window && redraw == 1);
assert(bar == 0 || bar == 1);
return position;
}
BOOL16 ScrollWindow16(
HWND16 window,
INT16 delta_x,
INT16 delta_y,
Win16FarPtr scroll,
Win16FarPtr clip)
{
assert(window == g_tpwincrt_window && scroll == 0 && clip == 0);
(void)delta_x;
(void)delta_y;
g_scroll_window_calls++;
return 1;
}
BOOL16 UpdateWindow16(HWND16 window)
{
assert(window == g_tpwincrt_window);
return 1;
}
static void expect_abort(bool focused, bool caret)
{
g_tpwincrt_has_focus = focused;
g_tpwincrt_caret_enabled = caret;
g_destroy_caret_calls = 0;
g_exit_status = 0;
if (setjmp(g_exit_jump) == 0) {
tpwincrt_abort_on_ctrl_c();
assert(false);
}
assert(g_exit_status == 0xff);
assert(g_destroy_caret_calls == (focused && caret ? 1u : 0u));
}
int main(void)
{
expect_abort(true, true);
expect_abort(true, false);
expect_abort(false, true);
memset(g_tpwincrt_input_buffer, 0, sizeof(g_tpwincrt_input_buffer));
g_tpwincrt_break_check_enabled = 0;
g_tpwincrt_input_count = 0;
tpwincrt_queue_character('A');
assert(g_tpwincrt_input_count == 1 && g_tpwincrt_input_buffer[0] == 'A');
tpwincrt_queue_character(3);
assert(g_tpwincrt_input_count == 2 && g_tpwincrt_input_buffer[1] == 3);
g_tpwincrt_input_count = 64;
tpwincrt_queue_character('B');
assert(g_tpwincrt_input_count == 64);
g_tpwincrt_break_check_enabled = 1;
g_tpwincrt_input_count = 0;
g_tpwincrt_has_focus = 1;
g_tpwincrt_caret_enabled = 1;
g_destroy_caret_calls = 0;
if (setjmp(g_exit_jump) == 0) {
tpwincrt_queue_character(3);
assert(false);
}
assert(g_exit_status == 0xff && g_destroy_caret_calls == 1);
assert(g_tpwincrt_input_count == 0);
g_tpwincrt_break_check_enabled = 0;
g_tpwincrt_window_created = 1;
g_tpwincrt_window = 0x77;
g_tpwincrt_max_view_column = 10;
g_tpwincrt_max_view_row = 10;
g_tpwincrt_visible_columns = 4;
g_tpwincrt_visible_rows = 4;
g_tpwincrt_character_width = 8;
g_tpwincrt_character_height = 16;
g_tpwincrt_view_column = 5;
g_tpwincrt_view_row = 5;
g_key_state = 0;
g_scroll_window_calls = 0;
tpwincrt_handle_keydown(0x25);
assert(g_tpwincrt_view_column == 4 && g_tpwincrt_view_row == 5);
g_tpwincrt_view_column = 5;
g_key_state = -1;
tpwincrt_handle_keydown(0x25);
assert(g_tpwincrt_view_column == 3);
g_key_state = 0;
tpwincrt_handle_keydown(0x26);
assert(g_tpwincrt_view_row == 4);
uint16_t column = g_tpwincrt_view_column;
uint16_t row = g_tpwincrt_view_row;
tpwincrt_handle_keydown(0x30);
assert(g_tpwincrt_view_column == column && g_tpwincrt_view_row == row);
assert(g_scroll_window_calls == 3);
g_tpwincrt_break_check_enabled = 1;
if (setjmp(g_exit_jump) == 0) {
tpwincrt_handle_keydown(3);
assert(false);
}
assert(g_exit_status == 0xff);
return 0;
}
@@ -0,0 +1,113 @@
#include "../tdkpin_runtime_ui.h"
#include <assert.h>
uint16_t g_tpwincrt_columns;
uint16_t g_tpwincrt_rows;
uint8_t g_tpwincrt_window_created;
Win16FarPtr g_tpwincrt_screen_buffer;
uint8_t g_tpwincrt_break_check_enabled;
HWND16 g_tpwincrt_window;
uint16_t g_tpwincrt_cursor_column;
uint16_t g_tpwincrt_cursor_row;
uint16_t g_tpwincrt_view_column;
uint16_t g_tpwincrt_view_row;
static uint16_t g_allocated_bytes;
static Win16FarPtr g_allocation;
static Win16FarPtr g_filled_block;
static uint16_t g_filled_bytes;
static uint8_t g_fill_value;
static unsigned g_menu_calls;
static HMENU16 g_enabled_menu;
static Win16FarPtr g_freed_block;
static uint16_t g_freed_bytes;
static INT16 g_quit_code;
Win16FarPtr borland_get_mem(uint16_t bytes)
{
g_allocated_bytes = bytes;
return g_allocation;
}
void borland_fill_bytes(
Win16FarPtr destination, uint16_t count, uint8_t value)
{
g_filled_block = destination;
g_filled_bytes = count;
g_fill_value = value;
}
HMENU16 GetSystemMenu16(HWND16 window, BOOL16 revert)
{
assert(window == g_tpwincrt_window && revert == 0);
g_menu_calls++;
return 0x1234;
}
BOOL16 EnableMenuItem16(HMENU16 menu, UINT16 item, UINT16 flags)
{
assert(item == 0xf060 && flags == 3);
g_enabled_menu = menu;
return 1;
}
void borland_free_mem(Win16FarPtr block, uint16_t bytes)
{
g_freed_block = block;
g_freed_bytes = bytes;
}
void PostQuitMessage16(INT16 exit_code)
{
g_quit_code = exit_code;
}
static void reset_create(void)
{
g_tpwincrt_window_created = 0;
g_tpwincrt_screen_buffer = 0;
g_allocated_bytes = 0;
g_filled_block = 0;
g_filled_bytes = 0;
g_fill_value = 0;
g_menu_calls = 0;
g_enabled_menu = 0;
}
int main(void)
{
g_tpwincrt_columns = 400;
g_tpwincrt_rows = 200;
uint16_t wrapped_bytes = (uint16_t)(400u * 200u);
g_allocation = win16_make_far_pointer(0x7000, 0x0100);
g_tpwincrt_window = 0x77;
reset_create();
g_tpwincrt_break_check_enabled = 1;
tpwincrt_on_create();
assert(g_tpwincrt_window_created == 1);
assert(g_allocated_bytes == wrapped_bytes);
assert(g_tpwincrt_screen_buffer == g_allocation);
assert(g_filled_block == g_allocation && g_filled_bytes == wrapped_bytes);
assert(g_fill_value == ' ' && g_menu_calls == 0);
reset_create();
g_tpwincrt_break_check_enabled = 0;
tpwincrt_on_create();
assert(g_menu_calls == 1 && g_enabled_menu == 0x1234);
g_tpwincrt_cursor_column = 4;
g_tpwincrt_cursor_row = 5;
g_tpwincrt_view_column = 6;
g_tpwincrt_view_row = 7;
g_tpwincrt_window_created = 1;
g_freed_block = 0;
g_quit_code = -1;
tpwincrt_on_destroy();
assert(g_freed_block == g_allocation && g_freed_bytes == wrapped_bytes);
assert(g_tpwincrt_cursor_column == 0 && g_tpwincrt_cursor_row == 0);
assert(g_tpwincrt_view_column == 0 && g_tpwincrt_view_row == 0);
assert(g_quit_code == 0 && g_tpwincrt_window_created == 0);
return 0;
}
@@ -0,0 +1,145 @@
#include "../tdkpin_runtime_ui.h"
#include "../tdkpin_segmented.h"
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
HWND16 g_tpwincrt_window;
uint8_t g_tpwincrt_paint_message_active;
HDC16 g_tpwincrt_paint_dc;
PAINTSTRUCT16 g_tpwincrt_paint;
HGDIOBJ16 g_tpwincrt_previous_font;
uint16_t g_tpwincrt_character_width;
uint16_t g_tpwincrt_character_height;
uint16_t g_tpwincrt_caret_y_offset;
uint16_t g_tpwincrt_columns;
uint16_t g_tpwincrt_rows;
static unsigned g_release_calls;
static unsigned g_select_calls;
HDC16 GetDC16(HWND16 window)
{
assert(window == 0x77);
return 0x100;
}
HDC16 BeginPaint16(HWND16 window, Win16FarPtr paint)
{
(void)window;
(void)paint;
assert(false);
return 0;
}
BOOL16 EndPaint16(HWND16 window, Win16FarPtr paint)
{
(void)window;
(void)paint;
assert(false);
return 0;
}
INT16 ReleaseDC16(HWND16 window, HDC16 dc)
{
assert(window == 0x77 && dc == 0x100);
g_release_calls++;
return 1;
}
HGDIOBJ16 GetStockObject16(INT16 object)
{
assert(object == 0x10);
return 0x200;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
assert(dc == 0x100);
g_select_calls++;
if (g_select_calls == 1) {
assert(object == 0x200);
return 0x300;
}
assert(object == 0x300);
return 0x200;
}
COLORREF16 GetSysColor16(INT16 index)
{
return (COLORREF16)(0x1000 + index);
}
COLORREF16 SetTextColor16(HDC16 dc, COLORREF16 color)
{
assert(dc == 0x100 && color == 0x1008);
return 0;
}
COLORREF16 SetBkColor16(HDC16 dc, COLORREF16 color)
{
assert(dc == 0x100 && color == 0x1005);
return 0;
}
BOOL16 GetTextMetrics16(HDC16 dc, Win16FarPtr metrics)
{
assert(dc == 0x100);
TEXTMETRIC16 value;
memset(&value, 0, sizeof(value));
value.height = 12;
value.external_leading = 2;
value.ascent = 9;
value.maximum_character_width = 8;
const uint8_t *bytes = (const uint8_t *)&value;
for (uint16_t index = 0; index < sizeof(value); index++) {
win16_write_u8(win16_far_add_offset(metrics, index), bytes[index]);
}
return 1;
}
INT16 GetSystemMetrics16(INT16 index)
{
switch (index) {
case 0: return 640;
case 1: return 480;
case 2: return 16;
case 3: return 16;
case 4: return 20;
case 32: return 4;
case 33: return 5;
default: assert(false); return 0;
}
}
int main(void)
{
uint8_t stack_marker;
uintptr_t stack_base = (uintptr_t)&stack_marker - 0x8000;
static uint8_t output[32];
win16_reset_segment_bindings();
win16_bind_segment(0x9000, (void *)stack_base, 0x10000, true);
win16_bind_segment(0x8000, output, sizeof(output), true);
g_tpwincrt_window = 0x77;
g_tpwincrt_paint_message_active = 0;
g_tpwincrt_columns = 80;
g_tpwincrt_rows = 25;
g_release_calls = 0;
g_select_calls = 0;
Win16FarPtr target = win16_make_far_pointer(0x8000, 0);
tpwincrt_fill_minmax_info(target);
assert(g_tpwincrt_character_width == 8);
assert(g_tpwincrt_character_height == 14);
assert(g_tpwincrt_caret_y_offset == 9);
assert(win16_read_u16(win16_far_add_offset(target, 4)) == 648);
assert(win16_read_u16(win16_far_add_offset(target, 6)) == 396);
assert(win16_read_u16(win16_far_add_offset(target, 12)) == 152);
assert(win16_read_u16(win16_far_add_offset(target, 14)) == 102);
assert(win16_read_u16(win16_far_add_offset(target, 16)) == 648);
assert(win16_read_u16(win16_far_add_offset(target, 18)) == 396);
assert(g_select_calls == 2 && g_release_calls == 1);
return 0;
}
@@ -0,0 +1,76 @@
#include "../tdkpin_runtime_ui.h"
#include <assert.h>
#include <limits.h>
uint16_t g_tpwincrt_columns;
uint16_t g_tpwincrt_rows;
uint16_t g_tpwincrt_ring_row_origin;
Win16FarPtr g_tpwincrt_screen_buffer;
static void test_signed_bounds(void)
{
assert(tpwincrt_min_i16(-7, 3) == -7);
assert(tpwincrt_min_i16(INT16_MAX, INT16_MIN) == INT16_MIN);
assert(tpwincrt_max_i16(-7, 3) == 3);
assert(tpwincrt_max_i16(INT16_MIN, INT16_MAX) == INT16_MAX);
}
static void test_scroll_commands(void)
{
TpWinCrtScrollMessage message = {.thumb_position = 7};
message.command = TPWINCRT_SB_LINEUP;
assert(tpwincrt_apply_scroll_command(&message, 20, 4, 10) == 9);
message.command = TPWINCRT_SB_LINEDOWN;
assert(tpwincrt_apply_scroll_command(&message, 20, 4, 10) == 11);
message.command = TPWINCRT_SB_PAGEUP;
assert(tpwincrt_apply_scroll_command(&message, 20, 4, 10) == 6);
message.command = TPWINCRT_SB_PAGEDOWN;
assert(tpwincrt_apply_scroll_command(&message, 20, 4, 10) == 14);
message.command = TPWINCRT_SB_THUMBPOSITION;
assert(tpwincrt_apply_scroll_command(&message, 20, 4, 10) == 7);
message.command = TPWINCRT_SB_TOP;
assert(tpwincrt_apply_scroll_command(&message, 20, 4, 10) == 0);
message.command = TPWINCRT_SB_BOTTOM;
assert(tpwincrt_apply_scroll_command(&message, 20, 4, 10) == 20);
message.command = 5;
assert(tpwincrt_apply_scroll_command(&message, 20, 4, 10) == 10);
message.command = TPWINCRT_SB_LINEDOWN;
assert(tpwincrt_apply_scroll_command(&message, 0, 0, INT16_MAX) == INT16_MIN);
}
static void test_circular_cell_pointer(void)
{
g_tpwincrt_columns = 80;
g_tpwincrt_rows = 25;
g_tpwincrt_ring_row_origin = 24;
g_tpwincrt_screen_buffer = win16_make_far_pointer(0x1234, 0xfff0);
Win16FarPtr result = tpwincrt_cell_pointer(2, 5);
assert(win16_far_selector(result) == 0x1234);
assert(win16_far_offset(result) == 0x0045);
}
static void test_key_table_bytes(void)
{
static const uint8_t expected[48] = {
0x25, 0, 0, 0, 0x27, 0, 0, 1, 0x25, 1, 0, 2, 0x27, 1, 0, 3,
0x24, 0, 0, 6, 0x23, 0, 0, 7, 0x26, 0, 1, 0, 0x28, 0, 1, 1,
0x21, 0, 1, 2, 0x22, 0, 1, 3, 0x24, 1, 1, 6, 0x23, 1, 1, 7,
};
const uint8_t *actual = (const uint8_t *)g_tpwincrt_key_bindings;
for (unsigned index = 0; index < sizeof(expected); index++) {
assert(actual[index] == expected[index]);
}
}
int main(void)
{
test_signed_bounds();
test_scroll_commands();
test_circular_cell_pointer();
test_key_table_bytes();
return 0;
}
@@ -0,0 +1,121 @@
#include "../tdkpin_render.h"
#include "../tdkpin_arithmetic.h"
#include <assert.h>
static uint8_t g_dgroup[0x1000];
static unsigned g_create_dc_calls;
static unsigned g_select_calls;
static unsigned g_blit_calls;
static unsigned g_delete_dc_calls;
static unsigned g_divide_calls;
BorlandDivI32Result borland_divide_i32(int32_t dividend, int32_t divisor)
{
assert(divisor == 10);
g_divide_calls++;
return (BorlandDivI32Result){dividend / divisor, dividend % divisor};
}
HDC16 CreateCompatibleDC16(HDC16 dc)
{
assert(dc == 0x1111);
g_create_dc_calls++;
return (HDC16)(0x2000 + g_create_dc_calls);
}
HBITMAP16 CreateCompatibleBitmap16(HDC16 dc, INT16 width, INT16 height)
{
assert(dc == 0x1111 && width == 40 && height == 150);
return 0x3333;
}
HGDIOBJ16 SelectObject16(HDC16 dc, HGDIOBJ16 object)
{
g_select_calls++;
switch (g_select_calls) {
case 1:
assert(dc == 0x2001 && object == 0x3000);
return 0x4000;
case 2:
assert(dc == 0x2002 && object == 0x3333);
return 0x4001;
case 3:
case 4:
assert(dc == 0x2001 && object == 0x3001);
return 0;
case 5:
assert(dc == 0x2001 && object == 0x4000);
return 0;
default:
assert(g_select_calls == 6 && dc == 0x2002 && object == 0x4001);
return 0;
}
}
BOOL16 BitBlt16(
HDC16 destination,
INT16 destination_x,
INT16 destination_y,
INT16 width,
INT16 height,
HDC16 source,
INT16 source_x,
INT16 source_y,
uint32_t raster_operation)
{
g_blit_calls++;
if (g_blit_calls == 1) {
assert(destination == 0x2002 && source == 0x2001);
assert(destination_x == 0 && destination_y == 0);
assert(width == 40 && height == 130);
assert(source_x == 321 && source_y == 478);
assert(raster_operation == 0x00cc0020);
} else if (g_blit_calls == 2 || g_blit_calls == 3) {
assert(destination == 0x2002 && source == 0x2001);
assert(destination_x == 0 && width == 28 && height == 16);
if (g_blit_calls == 2) {
assert(destination_y == 112 && source_x == 32);
} else {
assert(destination_y == 96 && source_x == 64);
}
assert(source_y == 0 && raster_operation == 0x008800c6);
} else {
assert(g_blit_calls == 4);
assert(destination == 0x1111 && source == 0x2002);
assert(destination_x == 321 && destination_y == 478);
assert(width == 40 && height == 130);
assert(source_x == 0 && source_y == 0);
assert(raster_operation == 0x00cc0020);
}
return 1;
}
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(object == 0x3333);
return 1;
}
BOOL16 DeleteDC16(HDC16 dc)
{
g_delete_dc_calls++;
assert(dc == (g_delete_dc_calls == 1 ? 0x2001 : 0x2002));
return 1;
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_write_u16(win16_make_far_pointer(0x5000, 0x085b), 0, 0x3000);
win16_write_u16(win16_make_far_pointer(0x5000, 0x0861), 0, 0x3001);
win16_write_u32(win16_make_far_pointer(0x5000, 0x0982), 4, 42);
tdkpin_draw_score(0x1111, 2);
assert(g_create_dc_calls == 2 && g_select_calls == 6);
assert(g_blit_calls == 4 && g_delete_dc_calls == 2);
assert(g_divide_calls == 16);
return 0;
}
+165
View File
@@ -0,0 +1,165 @@
#include "../tdkpin_gameplay_helpers.h"
#include <assert.h>
#include <string.h>
uint8_t g_current_player;
static uint8_t g_stack[0x400];
static uint8_t g_object[0x0c00];
static uint8_t g_dgroup[0x5000];
static Win16FarPtr g_receiver;
static unsigned g_get_dc_calls;
static unsigned g_release_dc_calls;
static unsigned g_sound_calls;
static unsigned g_status_calls;
static unsigned g_score_calls;
static unsigned g_restore_a_calls;
static unsigned g_restore_b_calls;
static unsigned g_present_calls;
static uint8_t g_status_player;
static int32_t g_draw_score_argument;
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;
}
void tdkpin_play_sound_if_not_tilted(uint16_t sound_number)
{
assert(sound_number == 7);
g_sound_calls++;
}
void tdkpin_update_status_display(uint8_t player, HDC16 dc)
{
assert(dc == 0x3333);
g_status_player = player;
g_status_calls++;
}
void tdkpin_draw_score(HDC16 dc, int32_t value_or_player)
{
assert(dc == 0x3333);
g_draw_score_argument = value_or_player;
g_score_calls++;
}
void tdkpin_restore_background_region_a(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height == 5 && width == 5 && dc == 0x3333);
assert(y == 342 && x >= 391 && x <= 431);
g_restore_a_calls++;
}
void tdkpin_restore_background_region_b(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height == 5 && width == 5 && dc == 0x3333);
assert(y == 342 && x >= 391 && x <= 431);
g_restore_b_calls++;
}
void tdkpin_present_background_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height == 5 && width == 5 && dc == 0x3333);
assert(y == 342 && x >= 391 && x <= 431);
g_present_calls++;
}
static void prepare_fixture(void)
{
memset(g_stack, 0, sizeof(g_stack));
memset(g_object, 0, sizeof(g_object));
memset(g_dgroup, 0, sizeof(g_dgroup));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_set_stack_state(0x7000, 0);
g_receiver = win16_make_far_pointer(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
win16_write_u16(g_receiver, 4, 0x2222);
g_current_player = 2;
g_get_dc_calls = 0;
g_release_dc_calls = 0;
g_sound_calls = 0;
g_status_calls = 0;
g_score_calls = 0;
g_restore_a_calls = 0;
g_restore_b_calls = 0;
g_present_calls = 0;
g_status_player = 0;
g_draw_score_argument = 0;
}
static uint32_t player_score(void)
{
return win16_read_u32(win16_dgroup_pointer(0x0986));
}
static void test_multipliers_and_redraw_only_gates(void)
{
prepare_fixture();
win16_write_u32(win16_dgroup_pointer(0x0986), 0, 100);
win16_write_u32(win16_dgroup_pointer(0x083d), 0, 5);
win16_write_u8(win16_dgroup_pointer(0x0874), 1);
win16_write_u8(win16_far_add_offset(g_receiver, 0x0bdc), 1);
tdkpin_add_score_from_caller_frame(0x0100, 10);
assert(player_score() == 140);
assert(g_status_calls == 1 && g_status_player == 2);
assert(g_score_calls == 1 && g_draw_score_argument == 2);
assert(g_sound_calls == 0 && g_get_dc_calls == 1);
assert(g_release_dc_calls == 1);
prepare_fixture();
win16_write_u32(win16_dgroup_pointer(0x0986), 0, 100);
tdkpin_add_score_from_caller_frame(0x0100, -1);
assert(player_score() == 100 && g_status_calls == 0);
assert(g_score_calls == 1 && g_release_dc_calls == 1);
prepare_fixture();
win16_write_u32(win16_dgroup_pointer(0x0986), 0, 100);
win16_write_u8(win16_dgroup_pointer(0x07c6), 1);
tdkpin_add_score_from_caller_frame(0x0100, 10);
assert(player_score() == 100 && g_status_calls == 0);
assert(g_score_calls == 1 && g_release_dc_calls == 1);
}
static void test_threshold_award_and_marker_refresh(void)
{
prepare_fixture();
win16_write_u32(win16_dgroup_pointer(0x0986), 0, 90);
win16_write_u32(win16_dgroup_pointer(0x083d), 0, 0);
win16_write_u32(win16_dgroup_pointer(0x0825), 0, 100);
win16_write_u16(win16_dgroup_pointer(0x0823), 0, 3);
tdkpin_add_score_from_caller_frame(0x0100, 10);
assert(player_score() == 100);
assert(win16_read_u32(win16_dgroup_pointer(0x083d)) == 1);
assert(win16_read_u16(win16_dgroup_pointer(0x0823)) == 4);
assert(g_sound_calls == 1 && g_status_calls == 1);
assert(g_restore_a_calls == 4 && g_restore_b_calls == 2);
assert(g_present_calls == 6);
assert(g_get_dc_calls == 2 && g_release_dc_calls == 2);
assert(g_score_calls == 1 && g_draw_score_argument == 2);
}
int main(void)
{
test_multipliers_and_redraw_only_gates();
test_threshold_award_and_marker_refresh();
return 0;
}
@@ -0,0 +1,118 @@
#include "../tdkpin_shortstrings.h"
#include <assert.h>
#include <string.h>
static uint8_t g_left[0x10000];
static uint8_t g_right[0x10000];
static Win16FarPtr left_at(uint16_t offset)
{
return win16_make_far_pointer(0x3333, offset);
}
static Win16FarPtr right_at(uint16_t offset)
{
return win16_make_far_pointer(0x4444, offset);
}
static void reset_memory(void)
{
memset(g_left, 0, sizeof(g_left));
memset(g_right, 0, sizeof(g_right));
win16_reset_segment_bindings();
win16_bind_segment(0x3333, g_left, sizeof(g_left), true);
win16_bind_segment(0x4444, g_right, sizeof(g_right), true);
}
static void set_short(uint8_t *bytes, uint16_t offset, const char *text)
{
size_t length = strlen(text);
assert(length <= 255);
bytes[offset] = (uint8_t)length;
memcpy(&bytes[offset + 1], text, length);
}
static void test_shifts(void)
{
assert(borland_shift_right_u32(0x87654321, 4) == 0x08765432);
assert(borland_shift_left_u32(0x87654321, 4) == 0x76543210);
assert(borland_shift_right_u32(0x87654321, 36) == 0x08765432);
assert(borland_shift_left_u32(0x87654321, 32) == 0x87654321);
}
static void test_assignment_copy_and_concat(void)
{
reset_memory();
set_short(g_left, 10, "abcdef");
borland_short_assign(right_at(10), left_at(10));
assert(memcmp(&g_right[10], &g_left[10], 7) == 0);
borland_short_assign_truncated(right_at(30), left_at(10), 3);
assert(g_right[30] == 3 && memcmp(&g_right[31], "abc", 3) == 0);
borland_short_substring(right_at(50), left_at(10), 2, 3);
assert(g_right[50] == 3 && memcmp(&g_right[51], "bcd", 3) == 0);
borland_short_substring(right_at(60), left_at(10), -2, -1);
assert(g_right[60] == 0);
set_short(g_right, 100, "one");
set_short(g_left, 100, "two");
borland_short_concat(right_at(100), left_at(100));
assert(g_right[100] == 6 && memcmp(&g_right[101], "onetwo", 6) == 0);
g_right[200] = 250;
memset(&g_right[201], 'x', 250);
g_left[200] = 10;
memset(&g_left[201], 'y', 10);
borland_short_concat(right_at(200), left_at(200));
assert(g_right[200] == 255);
assert(memcmp(&g_right[451], "yyyyy", 5) == 0);
}
static void test_search_compare_and_conversion(void)
{
reset_memory();
set_short(g_left, 10, "cab");
set_short(g_right, 10, "abcabc");
assert(borland_short_position(left_at(10), right_at(10)) == 3);
g_left[10] = 0;
assert(borland_short_position(left_at(10), right_at(10)) == 0);
set_short(g_left, 30, "abc");
set_short(g_right, 30, "abd");
assert(borland_short_compare(left_at(30), right_at(30)) < 0);
set_short(g_right, 30, "abcx");
assert(borland_short_compare(left_at(30), right_at(30)) < 0);
borland_character_to_short(right_at(60), 'Q');
assert(g_right[60] == 1 && g_right[61] == 'Q');
memcpy(&g_left[80], "bytes", 5);
borland_bytes_to_short(right_at(80), left_at(80), 5);
assert(g_right[80] == 5 && memcmp(&g_right[81], "bytes", 5) == 0);
}
static void test_insert_and_delete(void)
{
reset_memory();
set_short(g_right, 100, "abcd");
set_short(g_left, 100, "XY");
borland_short_insert(right_at(100), left_at(100), 3, 255);
assert(g_right[100] == 6 && memcmp(&g_right[101], "abXYcd", 6) == 0);
set_short(g_right, 120, "abcd");
borland_short_insert(right_at(120), left_at(100), 99, 5);
assert(g_right[120] == 5 && memcmp(&g_right[121], "abcdX", 5) == 0);
borland_short_delete(right_at(100), 2, 3);
assert(g_right[100] == 3 && memcmp(&g_right[101], "acd", 3) == 0);
borland_short_delete(right_at(100), 0, 2);
assert(g_right[100] == 3 && memcmp(&g_right[101], "acd", 3) == 0);
}
int main(void)
{
test_shifts();
test_assignment_copy_and_concat();
test_search_compare_and_conversion();
test_insert_and_delete();
return 0;
}
+451
View File
@@ -0,0 +1,451 @@
#include "../tdkpin_sound.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
enum {
DGROUP_SOUND_LENGTHS = 0x03ea,
DGROUP_INI_SUFFIX = 0x0442,
DGROUP_INI_SECTION = 0x0446,
DGROUP_INI_KEY = 0x044f,
DGROUP_WAVE_TYPE = 0x0455,
DGROUP_GAME_TILTED = 0x07c6,
DGROUP_WAVE_CAPS = 0x43be,
DGROUP_DEVICE_COUNT = 0x43ee,
DGROUP_DEVICE_ID = 0x43f0,
DGROUP_SOUND_BASE = 0x43f2,
DGROUP_SOUND_ENABLED = 0x43f2,
DGROUP_SOUND_PLAYING = 0x43f3,
DGROUP_VOLUME_STEP = 0x43f4,
CODE2_DOT = 0x0f2c,
SOUND_COUNT = 22,
};
static uint8_t g_dgroup[0x5000];
static uint8_t g_code2[0x1000];
HINSTANCE16 g_win16_instance;
Win16FarPtr g_borland_command_line;
static UINT16 g_profile_value;
static unsigned g_profile_calls;
static UINT16 g_wave_device_count;
static UINT16 g_wave_caps_result;
static uint32_t g_wave_support;
static uint32_t g_wave_volume;
static unsigned g_wave_count_calls;
static unsigned g_wave_caps_calls;
static unsigned g_wave_volume_calls;
static uint16_t g_current_sound;
static unsigned g_resource_stage;
static uint16_t g_loaded_sounds[SOUND_COUNT];
static unsigned g_loaded_count;
static unsigned g_stop_calls;
static Win16FarPtr g_played_sound;
static UINT16 g_play_flags;
static BOOL16 g_play_result;
static Win16FarPtr g_freed_sounds[SOUND_COUNT];
static unsigned g_free_count;
static Win16FarPtr dgroup_at(uint16_t offset)
{
return win16_make_far_pointer(0x5000, offset);
}
static bool far_string_equals(Win16FarPtr string, const char *expected)
{
size_t index = 0;
do {
if (win16_read_u8(win16_far_add_offset(string, (uint16_t)index)) !=
(uint8_t)expected[index]) {
return false;
}
} while (expected[index++] != '\0');
return true;
}
static uint16_t sound_index_offset(uint16_t sound_number)
{
return (uint16_t)(sound_number << 2);
}
static Win16FarPtr sound_buffer(uint16_t sound_number)
{
return win16_read_far_pointer(
dgroup_at(DGROUP_SOUND_BASE), sound_index_offset(sound_number));
}
static void set_sound_buffer(uint16_t sound_number, Win16FarPtr value)
{
win16_write_u32(
dgroup_at(DGROUP_SOUND_BASE), sound_index_offset(sound_number), value);
}
static void initialize_image_data(void)
{
memset(g_dgroup, 0, sizeof(g_dgroup));
memset(g_code2, 0, sizeof(g_code2));
memcpy(&g_dgroup[DGROUP_INI_SUFFIX], "INI", 4);
memcpy(&g_dgroup[DGROUP_INI_SECTION], "Settings", 9);
memcpy(&g_dgroup[DGROUP_INI_KEY], "Sound", 6);
memcpy(&g_dgroup[DGROUP_WAVE_TYPE], "WAV", 4);
g_code2[CODE2_DOT] = 1;
g_code2[CODE2_DOT + 1] = '.';
/* 03e6 belongs to the preceding object but is read for sound number zero. */
win16_write_u32(dgroup_at(0x03e6), 0, 0x00007070);
for (uint16_t index = 0; index < SOUND_COUNT; index++) {
win16_write_u32(
dgroup_at(DGROUP_SOUND_LENGTHS),
(uint16_t)(index * 4),
g_sound_resource_byte_counts[index]);
}
}
static void reset_calls(void)
{
g_profile_calls = 0;
g_wave_count_calls = 0;
g_wave_caps_calls = 0;
g_wave_volume_calls = 0;
g_current_sound = 0;
g_resource_stage = 0;
g_loaded_count = 0;
g_stop_calls = 0;
g_played_sound = 0;
g_play_flags = 0;
g_free_count = 0;
}
static void reset_sound_state(void)
{
memset(&g_dgroup[DGROUP_WAVE_CAPS], 0, 0x90);
reset_calls();
}
UINT16 GetModuleFileNameFar16(
HINSTANCE16 module, Win16FarPtr buffer, UINT16 size)
{
static const char module_name[] = "C:\\GAMES\\TDKPIN.EXE";
assert(module == g_win16_instance && size == 0xff);
size_t length = strlen(module_name);
for (size_t index = 0; index < length; index++) {
win16_write_u8(
win16_far_add_offset(buffer, (uint16_t)index),
(uint8_t)module_name[index]);
}
win16_write_u8(win16_far_add_offset(buffer, (uint16_t)length), 0);
return (UINT16)length;
}
UINT16 GetPrivateProfileInt16(
Win16FarPtr section,
Win16FarPtr key,
INT16 default_value,
Win16FarPtr file_name)
{
assert(section == dgroup_at(DGROUP_INI_SECTION));
assert(key == dgroup_at(DGROUP_INI_KEY));
assert(default_value == 1);
assert(far_string_equals(file_name, "C:\\GAMES\\TDKPIN.INI"));
g_profile_calls++;
return g_profile_value;
}
UINT16 WaveOutGetNumDevs16(void)
{
g_wave_count_calls++;
return g_wave_device_count;
}
UINT16 WaveOutGetDevCaps16(
UINT16 device_id, Win16FarPtr capabilities, UINT16 byte_count)
{
assert(device_id == win16_read_u16(dgroup_at(DGROUP_DEVICE_ID)));
assert(capabilities == dgroup_at(DGROUP_WAVE_CAPS));
assert(byte_count == 48);
win16_write_u32(capabilities, 44, g_wave_support);
g_wave_caps_calls++;
return g_wave_caps_result;
}
UINT16 WaveOutGetVolume16(UINT16 device_id, Win16FarPtr volume)
{
assert(device_id == win16_read_u16(dgroup_at(DGROUP_DEVICE_ID)));
win16_write_u32(volume, 0, g_wave_volume);
g_wave_volume_calls++;
return 0;
}
HRSRC16 FindResource16(
HINSTANCE16 instance, Win16FarPtr name, Win16FarPtr type)
{
assert(g_resource_stage == 0 && instance == g_win16_instance);
assert(win16_far_selector(name) == 0);
assert(type == dgroup_at(DGROUP_WAVE_TYPE));
g_current_sound = (uint16_t)(win16_far_offset(name) - 2000);
assert(g_current_sound >= 1 && g_current_sound <= SOUND_COUNT);
assert(g_sound_resource_byte_counts[g_current_sound - 1] != 0);
g_resource_stage = 1;
return (HRSRC16)(0x1000 + g_current_sound);
}
HGLOBAL16 LoadResource16(HINSTANCE16 instance, HRSRC16 resource)
{
assert(g_resource_stage == 1 && instance == g_win16_instance);
assert(resource == (HRSRC16)(0x1000 + g_current_sound));
g_resource_stage = 2;
return (HGLOBAL16)(0x2000 + g_current_sound);
}
Win16FarPtr tdkpin_global_allocate_locked(UINT16 flags, uint32_t bytes)
{
assert(g_resource_stage == 2 && flags == 0x42);
assert(bytes == g_sound_resource_byte_counts[g_current_sound - 1]);
g_resource_stage = 3;
return win16_make_far_pointer(
(uint16_t)(0x7000 + g_current_sound), 0x0100);
}
Win16FarPtr LockResource16(HGLOBAL16 resource)
{
assert(g_resource_stage == 3);
assert(resource == (HGLOBAL16)(0x2000 + g_current_sound));
g_resource_stage = 4;
return win16_make_far_pointer(
(uint16_t)(0x6000 + g_current_sound), 0x0200);
}
void borland_move_bytes(
Win16FarPtr destination, Win16FarPtr source, uint16_t count)
{
assert(g_resource_stage == 4);
assert(destination == sound_buffer(g_current_sound));
assert(destination == win16_make_far_pointer(
(uint16_t)(0x7000 + g_current_sound), 0x0100));
assert(source == win16_make_far_pointer(
(uint16_t)(0x6000 + g_current_sound), 0x0200));
assert(count == g_sound_resource_byte_counts[g_current_sound - 1]);
g_resource_stage = 5;
}
BOOL16 GlobalUnlock16(HGLOBAL16 resource)
{
assert(g_resource_stage == 5);
assert(resource == (HGLOBAL16)(0x2000 + g_current_sound));
g_resource_stage = 6;
return 1;
}
BOOL16 FreeResource16(HGLOBAL16 resource)
{
assert(g_resource_stage == 6);
assert(resource == (HGLOBAL16)(0x2000 + g_current_sound));
g_loaded_sounds[g_loaded_count++] = g_current_sound;
g_resource_stage = 0;
return 1;
}
BOOL16 SndPlaySound16(Win16FarPtr sound, UINT16 flags)
{
if (sound == 0) {
assert(flags == 0);
g_stop_calls++;
return 1;
}
g_played_sound = sound;
g_play_flags = flags;
return g_play_result;
}
void tdkpin_global_unlock_and_free(Win16FarPtr block)
{
assert(g_free_count < SOUND_COUNT);
g_freed_sounds[g_free_count++] = block;
}
static void test_initializer_disabled_and_probe_failures(void)
{
reset_sound_state();
win16_write_u8(dgroup_at(DGROUP_SOUND_ENABLED), 0xaa);
win16_write_u8(dgroup_at(DGROUP_SOUND_PLAYING), 0xbb);
win16_write_u8(dgroup_at(DGROUP_VOLUME_STEP), 0x55);
win16_write_u16(dgroup_at(DGROUP_DEVICE_COUNT), 0, 0xcccc);
g_profile_value = 0;
tdkpin_initialize_sounds();
assert(g_profile_calls == 1 && g_wave_count_calls == 0);
assert(win16_read_u8(dgroup_at(DGROUP_SOUND_ENABLED)) == 0);
assert(win16_read_u8(dgroup_at(DGROUP_SOUND_PLAYING)) == 0);
assert(win16_read_u16(dgroup_at(DGROUP_DEVICE_COUNT)) == 0);
assert(win16_read_u8(dgroup_at(DGROUP_VOLUME_STEP)) == 0x55);
reset_sound_state();
g_profile_value = 1;
g_wave_device_count = 0;
tdkpin_initialize_sounds();
assert(g_wave_count_calls == 1 && g_wave_caps_calls == 0);
assert(win16_read_u16(dgroup_at(DGROUP_DEVICE_COUNT)) == 0);
reset_sound_state();
g_wave_device_count = 2;
g_wave_caps_result = 1;
tdkpin_initialize_sounds();
assert(g_wave_caps_calls == 1 && g_loaded_count == 0);
assert(win16_read_u16(dgroup_at(DGROUP_DEVICE_COUNT)) == 0);
}
static void prime_sound_buffers(void)
{
for (uint16_t sound = 1; sound <= SOUND_COUNT; sound++) {
set_sound_buffer(
sound, win16_make_far_pointer(0x5555, sound));
}
}
static void assert_loaded_resource_set(void)
{
unsigned loaded = 0;
for (uint16_t sound = 1; sound <= SOUND_COUNT; sound++) {
uint32_t bytes = g_sound_resource_byte_counts[sound - 1];
if (bytes == 0) {
assert(sound_buffer(sound) ==
win16_make_far_pointer(0x5555, sound));
continue;
}
assert(g_loaded_sounds[loaded++] == sound);
assert(sound_buffer(sound) == win16_make_far_pointer(
(uint16_t)(0x7000 + sound), 0x0100));
}
assert(loaded == 16 && g_loaded_count == loaded && g_resource_stage == 0);
}
static void test_initializer_success(void)
{
reset_sound_state();
prime_sound_buffers();
g_profile_value = 1;
g_wave_device_count = 2;
g_wave_caps_result = 0;
g_wave_support = 0;
tdkpin_initialize_sounds();
assert(win16_read_u16(dgroup_at(DGROUP_DEVICE_COUNT)) == 2);
assert(win16_read_u8(dgroup_at(DGROUP_SOUND_ENABLED)) == 1);
assert(win16_read_u8(dgroup_at(DGROUP_SOUND_PLAYING)) == 0);
assert(win16_read_u8(dgroup_at(DGROUP_VOLUME_STEP)) == 0xff);
assert(g_wave_volume_calls == 0);
assert_loaded_resource_set();
reset_sound_state();
prime_sound_buffers();
g_wave_support = 4;
g_wave_volume = 0x1234abcd;
tdkpin_initialize_sounds();
assert(g_wave_volume_calls == 1);
assert(win16_read_u8(dgroup_at(DGROUP_VOLUME_STEP)) == 170);
assert_loaded_resource_set();
}
static void test_stop_unload_and_play(void)
{
win16_set_indeterminate_u16(0xbeef);
reset_calls();
win16_write_u16(dgroup_at(DGROUP_DEVICE_COUNT), 0, 0);
win16_write_u8(dgroup_at(DGROUP_SOUND_PLAYING), 1);
assert(tdkpin_stop_sound_if_active() == 0xbeef);
assert(g_stop_calls == 0);
win16_write_u16(dgroup_at(DGROUP_DEVICE_COUNT), 0, 1);
win16_write_u8(dgroup_at(DGROUP_SOUND_PLAYING), 0);
assert(tdkpin_stop_sound_if_active() == 0xbeef);
assert(g_stop_calls == 0);
win16_write_u8(dgroup_at(DGROUP_SOUND_PLAYING), 1);
assert(tdkpin_stop_sound_if_active() == 0xbeef);
assert(g_stop_calls == 1);
assert(win16_read_u8(dgroup_at(DGROUP_SOUND_PLAYING)) == 1);
reset_calls();
for (uint16_t sound = 1; sound <= SOUND_COUNT; sound++) {
set_sound_buffer(sound, win16_make_far_pointer(0x7100, sound));
}
tdkpin_unload_sounds();
assert(g_stop_calls == 1 && g_free_count == SOUND_COUNT);
for (uint16_t index = 0; index < SOUND_COUNT; index++) {
assert(g_freed_sounds[index] ==
win16_make_far_pointer(0x7100, (uint16_t)(index + 1)));
}
assert(win16_read_u8(dgroup_at(DGROUP_SOUND_PLAYING)) == 0);
reset_calls();
win16_write_u16(dgroup_at(DGROUP_DEVICE_COUNT), 0, 0);
tdkpin_unload_sounds();
assert(g_free_count == 0);
reset_calls();
win16_write_u16(dgroup_at(DGROUP_DEVICE_COUNT), 0, 1);
win16_write_u8(dgroup_at(DGROUP_SOUND_ENABLED), 1);
win16_write_u8(dgroup_at(DGROUP_SOUND_PLAYING), 1);
set_sound_buffer(4, win16_make_far_pointer(0x7200, 0x4444));
g_play_result = 1;
assert(tdkpin_play_sound(4) == 0xbeef);
assert(g_stop_calls == 1);
assert(g_played_sound == win16_make_far_pointer(0x7200, 0x4444));
assert(g_play_flags == 5);
assert(win16_read_u8(dgroup_at(DGROUP_SOUND_PLAYING)) == 1);
g_play_result = 0;
assert(tdkpin_play_sound(4) == 0xbeef);
assert(win16_read_u8(dgroup_at(DGROUP_SOUND_PLAYING)) == 0);
g_played_sound = 0;
(void)tdkpin_play_sound(3); /* Absent resource: exact size-table gate. */
assert(g_played_sound == 0);
/* The binary does not range-check: number zero reads the preceding dword. */
win16_write_u8(dgroup_at(DGROUP_SOUND_ENABLED), 1);
win16_write_u8(dgroup_at(DGROUP_SOUND_PLAYING), 0);
win16_write_u8(dgroup_at(DGROUP_VOLUME_STEP), 0xaa);
g_play_result = 1;
(void)tdkpin_play_sound(0);
assert(g_played_sound == win16_make_far_pointer(0x00aa, 0x0001));
}
static void test_gameplay_sound_dispatcher(void)
{
reset_calls();
win16_write_u16(dgroup_at(DGROUP_DEVICE_COUNT), 0, 1);
win16_write_u8(dgroup_at(DGROUP_SOUND_ENABLED), 1);
win16_write_u8(dgroup_at(DGROUP_SOUND_PLAYING), 0);
win16_write_u8(dgroup_at(DGROUP_GAME_TILTED), 1);
set_sound_buffer(4, win16_make_far_pointer(0x7300, 0x4444));
g_play_result = 1;
tdkpin_play_sound_if_not_tilted(4);
assert(g_played_sound == 0);
win16_write_u8(dgroup_at(DGROUP_GAME_TILTED), 0);
win16_write_u8(dgroup_at(DGROUP_SOUND_ENABLED), 0);
tdkpin_play_sound_if_not_tilted(4);
assert(g_played_sound == 0);
win16_write_u8(dgroup_at(DGROUP_SOUND_ENABLED), 1);
tdkpin_play_sound_if_not_tilted(4);
assert(g_played_sound == win16_make_far_pointer(0x7300, 0x4444));
assert(g_play_flags == 5);
}
int main(void)
{
uint8_t stack_marker;
uintptr_t stack_base = (uintptr_t)&stack_marker - 0x8000;
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_ne_segment(2, 0x5100, g_code2, sizeof(g_code2), false);
win16_bind_segment(0x9000, (void *)stack_base, 0x10000, true);
win16_set_dgroup_selector(0x5000);
g_win16_instance = 0x4444;
initialize_image_data();
test_initializer_disabled_and_probe_failures();
test_initializer_success();
test_stop_unload_and_play();
test_gameplay_sound_dispatcher();
return 0;
}
+48
View File
@@ -0,0 +1,48 @@
#include "../tdkpin_stack.h"
#include <assert.h>
#include <setjmp.h>
static jmp_buf g_error_jump;
static uint16_t g_error_code;
_Noreturn void borland_runtime_error(uint16_t code)
{
g_error_code = code;
longjmp(g_error_jump, 1);
}
static void expect_stack_error(uint16_t requested_bytes)
{
g_error_code = 0;
if (setjmp(g_error_jump) == 0) {
borland_check_stack(requested_bytes);
assert(false);
}
assert(g_error_code == 202);
}
int main(void)
{
static uint8_t stack[0x10000];
win16_reset_segment_bindings();
win16_bind_segment(0x7000, stack, sizeof(stack), true);
win16_set_stack_state(0x7000, 0x8000);
win16_write_stack_u16(0x000a, 0x1000);
win16_write_stack_u16(0x000e, 0x7000);
tdkpin_initialize_stack_guard();
assert(win16_read_stack_u16(0x000e) == 0x7000);
borland_check_stack(0x2000);
assert(win16_read_stack_u16(0x000e) == 0x5c00);
win16_write_stack_u16(0x000e, 0x7000);
borland_check_stack(0x6c00);
assert(win16_read_stack_u16(0x000e) == 0x1000);
expect_stack_error(0xfc00); /* AX + 0x400 carry. */
expect_stack_error(0x7c00); /* Required bytes collide with SP. */
expect_stack_error(0x6c01); /* Remaining byte falls below SS:000a. */
return 0;
}
@@ -0,0 +1,139 @@
#include "../tdkpin_render.h"
#include <assert.h>
static uint8_t g_dgroup[0x5000];
static unsigned g_blit_calls;
static unsigned g_restore_a_calls;
static unsigned g_restore_b_calls;
static unsigned g_present_calls;
static void assert_region(
INT16 height, INT16 width, INT16 y, INT16 x,
INT16 expected_height, INT16 expected_width,
INT16 expected_y, INT16 expected_x)
{
assert(height == expected_height && width == expected_width);
assert(y == expected_y && x == expected_x);
}
void tdkpin_blit_bitmap_to_background(
HBITMAP16 bitmap,
INT16 height,
INT16 width,
INT16 y,
INT16 x,
HDC16 reference_dc)
{
assert(reference_dc == 0x1111);
if (height == 61) {
assert(bitmap == 0x4003);
assert_region(height, width, y, x, 61, 68, 300, 123);
} else {
assert(bitmap == 0x3002);
assert_region(height, width, y, x, 142, 146, 69, 380);
}
g_blit_calls++;
}
void tdkpin_restore_background_region_a(
INT16 height,
INT16 width,
INT16 y,
INT16 x,
HDC16 reference_dc)
{
assert(reference_dc == 0x1111);
if (height == 11) {
assert_region(height, width, y, x, 11, 11, 544, 123);
} else {
assert_region(height, width, y, x, 142, 146, 69, 380);
}
g_restore_a_calls++;
}
void tdkpin_restore_background_region_b(
INT16 height,
INT16 width,
INT16 y,
INT16 x,
HDC16 reference_dc)
{
assert(reference_dc == 0x1111);
if (height == 11) {
assert(width == 11 && y == 544);
assert(x == 169 || x == 146 || x == 100 || x == 77 || x == 123);
} else if (height == 61) {
assert_region(height, width, y, x, 61, 68, 300, 123);
} else {
assert_region(height, width, y, x, 142, 146, 69, 380);
}
g_restore_b_calls++;
}
void tdkpin_present_background_region(
INT16 height,
INT16 width,
INT16 y,
INT16 x,
HDC16 destination)
{
assert(destination == 0x1111);
if (height == 11) {
assert(width == 11 && y == 544);
assert(x == 169 || x == 146 || x == 123 || x == 100 || x == 77);
} else if (height == 61) {
assert_region(height, width, y, x, 61, 68, 300, 123);
} else {
assert_region(height, width, y, x, 142, 146, 69, 380);
}
g_present_calls++;
}
static void reset_calls(void)
{
g_blit_calls = 0;
g_restore_a_calls = 0;
g_restore_b_calls = 0;
g_present_calls = 0;
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_write_u8(win16_make_far_pointer(0x5000, 0x07d1), 2);
win16_write_u16(win16_make_far_pointer(0x5000, 0x086b), 0, 99);
win16_write_u32(win16_make_far_pointer(0x5000, 0x0982), 4, 140001);
win16_write_u16(win16_make_far_pointer(0x5000, 0x4319), 4, 0x3002);
tdkpin_update_status_display(2, 0x1111);
assert(g_blit_calls == 1 && g_restore_a_calls == 1);
assert(g_restore_b_calls == 4 && g_present_calls == 6);
assert(win16_read_u16(win16_make_far_pointer(0x5000, 0x086b)) == 2);
reset_calls();
tdkpin_update_status_display(2, 0x1111);
assert(g_blit_calls == 0 && g_restore_a_calls == 0);
assert(g_restore_b_calls == 0 && g_present_calls == 0);
reset_calls();
tdkpin_update_status_display(0, 0x1111);
assert(g_blit_calls == 0 && g_restore_a_calls == 0);
assert(g_restore_b_calls == 6 && g_present_calls == 6);
assert(win16_read_u16(win16_make_far_pointer(0x5000, 0x086b)) == 99);
reset_calls();
tdkpin_update_status_display(5, 0x1111);
assert(g_restore_b_calls == 0 && g_present_calls == 0);
win16_write_u16(win16_make_far_pointer(0x5000, 0x4321), 6, 0x4003);
reset_calls();
tdkpin_draw_item_status(0, 0x1111);
assert(g_restore_b_calls == 1 && g_present_calls == 1);
reset_calls();
tdkpin_draw_item_status(3, 0x1111);
assert(g_blit_calls == 1 && g_present_calls == 1);
return 0;
}
+164
View File
@@ -0,0 +1,164 @@
#include "../tdkpin_strings.h"
#include <assert.h>
#include <string.h>
static uint8_t g_source[0x10000];
static uint8_t g_destination[0x10000];
static Win16FarPtr g_disposed_string;
static uint16_t g_disposed_bytes;
static Win16FarPtr g_allocated_string;
static uint16_t g_allocated_bytes;
void borland_free_mem(Win16FarPtr block, uint16_t bytes)
{
g_disposed_string = block;
g_disposed_bytes = bytes;
}
Win16FarPtr borland_get_mem(uint16_t bytes)
{
g_allocated_bytes = bytes;
return g_allocated_string;
}
static Win16FarPtr source_at(uint16_t offset)
{
return win16_make_far_pointer(0x1111, offset);
}
static Win16FarPtr destination_at(uint16_t offset)
{
return win16_make_far_pointer(0x2222, offset);
}
static void reset_memory(void)
{
memset(g_source, 0xcc, sizeof(g_source));
memset(g_destination, 0xdd, sizeof(g_destination));
win16_reset_segment_bindings();
win16_bind_segment(0x1111, g_source, sizeof(g_source), true);
win16_bind_segment(0x2222, g_destination, sizeof(g_destination), true);
}
static void test_length_end_and_copy(void)
{
reset_memory();
memcpy(&g_source[10], "Abc", 4);
assert(borland_far_strlen(source_at(10)) == 3);
assert(borland_far_strend(source_at(10)) == source_at(13));
assert(borland_far_strcpy(destination_at(20), source_at(10)) == destination_at(20));
assert(memcmp(&g_destination[20], "Abc", 4) == 0);
g_destination[30] = 0xaa;
(void)borland_far_strncpy(destination_at(30), source_at(10), 2);
assert(g_destination[30] == 'A' && g_destination[31] == 'b');
assert(g_destination[32] == 0);
memset(&g_destination[40], 0xaa, 8);
(void)borland_far_strncpy(destination_at(40), source_at(10), 4);
assert(memcmp(&g_destination[40], "Abc\0\0", 5) == 0);
reset_memory();
assert(borland_far_strlen(source_at(0)) == 0xfffe);
assert(borland_far_strend(source_at(0)) == source_at(0xfffe));
}
static void test_memmove_and_concat(void)
{
reset_memory();
win16_fill_bytes(destination_at(20), 5, 0x5a);
assert(memcmp(&g_destination[20], "ZZZZZ", 5) == 0);
memcpy(&g_source[40], "copy", 4);
borland_far_copy(destination_at(40), source_at(40), 4);
assert(memcmp(&g_destination[40], "copy", 4) == 0);
memcpy(&g_source[100], "abcdef", 6);
(void)borland_far_memmove(source_at(102), source_at(100), 4);
assert(memcmp(&g_source[100], "ababcd", 6) == 0);
memcpy(&g_source[300], "cross", 5);
(void)borland_far_memmove(destination_at(500), source_at(300), 5);
assert(memcmp(&g_destination[500], "cross", 5) == 0);
memcpy(&g_destination[100], "one", 4);
memcpy(&g_source[200], "two", 4);
assert(borland_far_strcat(destination_at(100), source_at(200)) == destination_at(100));
assert(memcmp(&g_destination[100], "onetwo", 7) == 0);
}
static void test_short_strings(void)
{
reset_memory();
g_source[20] = 3;
memcpy(&g_source[21], "xyz", 3);
(void)borland_short_to_c_string(destination_at(20), source_at(20));
assert(memcmp(&g_destination[20], "xyz", 4) == 0);
memcpy(&g_source[40], "hello", 6);
borland_c_to_short_string(destination_at(40), source_at(40));
assert(g_destination[40] == 5);
assert(memcmp(&g_destination[41], "hello", 5) == 0);
memset(&g_source[100], 'q', 260);
g_source[360] = 0;
borland_c_to_short_string(destination_at(100), source_at(100));
assert(g_destination[100] == 4);
assert(memcmp(&g_destination[101], &g_source[100], 260) == 0);
}
static void test_compare_and_search(void)
{
reset_memory();
memcpy(&g_source[100], "aBcD", 5);
memcpy(&g_destination[100], "Abce", 5);
assert(borland_far_strnicmp(source_at(100), destination_at(100), 3) == 0);
assert(borland_far_strnicmp(source_at(100), destination_at(100), 4) == -1);
assert(borland_far_strchr(source_at(100), 'B') == source_at(101));
assert(borland_far_strchr(source_at(100), 0) == source_at(104));
memcpy(&g_source[200], "abca", 5);
assert(borland_far_strrchr(source_at(200), 'a') == source_at(203));
assert(borland_far_strrchr(source_at(200), 0) == source_at(204));
assert(borland_far_strrchr(source_at(200), 'z') == 0);
}
static void test_dispose(void)
{
reset_memory();
g_disposed_string = 0;
g_disposed_bytes = 0;
borland_far_strdispose(0);
assert(g_disposed_string == 0 && g_disposed_bytes == 0);
memcpy(&g_source[100], "abc", 4);
borland_far_strdispose(source_at(100));
assert(g_disposed_string == source_at(100));
assert(g_disposed_bytes == 4);
}
static void test_duplicate(void)
{
reset_memory();
g_allocated_string = destination_at(1000);
g_allocated_bytes = 0;
assert(borland_far_strdup(0) == 0);
g_source[50] = 0;
assert(borland_far_strdup(source_at(50)) == 0);
memcpy(&g_source[100], "copy", 5);
assert(borland_far_strdup(source_at(100)) == g_allocated_string);
assert(g_allocated_bytes == 5);
assert(memcmp(&g_destination[1000], "copy", 5) == 0);
g_allocated_string = 0;
assert(borland_far_strdup(source_at(100)) == 0);
}
int main(void)
{
test_length_end_and_copy();
test_memmove_and_concat();
test_short_strings();
test_compare_and_search();
test_dispose();
test_duplicate();
return 0;
}
@@ -0,0 +1,114 @@
#include "../tdkpin_system_timer.h"
#include <assert.h>
static BorlandAllocationAttempt g_allocation;
static Win16FarPtr g_freed_object;
static uint16_t g_freed_size;
static uint16_t g_make_result;
static HWND16 g_make_recipient;
static uint16_t g_make_delay;
static uint16_t g_make_resolution;
static uint16_t g_make_one_shot;
static uint16_t g_killed_timer;
BorlandAllocationAttempt borland_try_get_mem(uint16_t bytes)
{
assert(bytes == 6);
return g_allocation;
}
bool borland_try_free_mem(Win16FarPtr object, uint16_t bytes)
{
g_freed_object = object;
g_freed_size = bytes;
return false;
}
_Noreturn void borland_runtime_error(uint16_t code)
{
(void)code;
assert(false);
__builtin_unreachable();
}
uint16_t SystemTimerMake16(
HWND16 recipient,
uint16_t delay_ms,
uint16_t resolution_ms,
uint16_t one_shot)
{
g_make_recipient = recipient;
g_make_delay = delay_ms;
g_make_resolution = resolution_ms;
g_make_one_shot = one_shot;
return g_make_result;
}
uint16_t TimeKillEvent16(uint16_t timer_id)
{
g_killed_timer = timer_id;
return 0;
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t stack[0x10000];
static uint8_t caller_object[32];
static uint8_t allocated_object[32];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(0x6000, stack, sizeof(stack), true);
win16_set_stack_state(0x6000, 0x8000);
win16_write_stack_u16(0x000a, 0x1000);
win16_write_stack_u16(0x000e, 0x7fff);
win16_bind_segment(
0x7000, caller_object, sizeof(caller_object), true);
win16_bind_segment(
0x7100, allocated_object, sizeof(allocated_object), true);
win16_write_u16(win16_make_far_pointer(0x5000, 0x0200), 0, 6);
Win16FarPtr caller = win16_make_far_pointer(0x7000, 4);
g_make_result = 0x4321;
Win16FarPtr result = tdkpin_system_timer_construct(
caller, 0x0200, 1, 7, 250, 0x1234);
assert(result == caller);
assert(win16_read_u16(caller) == 0x0200);
assert(win16_read_u16(win16_far_add_offset(caller, 2)) == 0x4321);
assert(win16_read_u16(win16_far_add_offset(caller, 4)) == 0x1234);
assert(g_make_recipient == 0x1234);
assert(g_make_delay == 250 && g_make_resolution == 7);
assert(g_make_one_shot == 0x7b01);
g_killed_timer = 0;
tdkpin_system_timer_stop(caller);
assert(g_killed_timer == 0x4321);
assert(win16_read_u16(win16_far_add_offset(caller, 2)) == 0);
tdkpin_system_timer_stop(caller);
assert(g_killed_timer == 0x4321);
Win16FarPtr allocated = win16_make_far_pointer(0x7100, 8);
g_allocation = (BorlandAllocationAttempt){allocated, false};
g_make_result = 0;
g_freed_object = 0;
result = tdkpin_system_timer_construct(
0, 0x0200, 0, 1, 20, 0x2222);
assert(result == 0);
assert(g_freed_object == allocated && g_freed_size == 6);
g_make_result = 0;
g_freed_object = 0;
result = tdkpin_system_timer_construct(
caller, 0x0200, 0, 1, 20, 0x3333);
assert(result == caller && g_freed_object == 0);
win16_write_u16(caller, 0, 0x0200);
win16_write_u16(caller, 2, 0x1111);
g_freed_object = 0;
tdkpin_system_timer_destruct(caller, 1);
assert(g_killed_timer == 0x1111);
assert(g_freed_object == caller && g_freed_size == 6);
return 0;
}
@@ -0,0 +1,24 @@
#include "../tdkpin_tables.h"
#include <assert.h>
int main(void)
{
static uint8_t dgroup[0x10000];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
tdkpin_set_word_quad(0x1111, 0x2222, 0x3333, 0x4444, 1);
Win16FarPtr first = win16_make_far_pointer(0x5000, 0x426b);
assert(win16_read_u16(first) == 0x4444);
assert(win16_read_u16(win16_far_add_offset(first, 2)) == 0x3333);
assert(win16_read_u16(win16_far_add_offset(first, 4)) == 0x2222);
assert(win16_read_u16(win16_far_add_offset(first, 6)) == 0x1111);
tdkpin_set_word_quad(0xaaaa, 0xbbbb, 0xcccc, 0xdddd, 20);
Win16FarPtr last = win16_make_far_pointer(0x5000, 0x4303);
assert(win16_read_u16(last) == 0xdddd);
assert(win16_read_u16(win16_far_add_offset(last, 6)) == 0xaaaa);
return 0;
}
@@ -0,0 +1,538 @@
#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 == 0);
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 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;
}
@@ -0,0 +1,327 @@
#include "../tdkpin_tpwincrt_lifecycle.h"
#include "../tdkpin_borland_files.h"
#include <assert.h>
#include <stdatomic.h>
#include <string.h>
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
uint16_t g_win16_show_command;
Win16FarPtr g_borland_command_line;
uint16_t g_borland_windows_mode;
uint16_t g_borland_huge_pointer_increment;
uint8_t g_borland_cpu_level;
HWND16 g_tpwincrt_window;
uint8_t g_tpwincrt_window_created;
uint8_t g_tpwincrt_break_check_enabled;
WNDCLASS16 g_tpwincrt_window_class;
int16_t g_tpwincrt_initial_x;
int16_t g_tpwincrt_initial_y;
int16_t g_tpwincrt_initial_width;
int16_t g_tpwincrt_initial_height;
const char g_tpwincrt_inactive_format[14] = "(Inactive %s)";
char g_tpwincrt_window_title[80];
Win16FarPtr g_tpwincrt_saved_exit_proc;
Win16FarPtr g_borland_exit_proc;
uint16_t g_borland_error_offset;
uint16_t g_borland_error_segment;
_Atomic uint16_t g_borland_io_result;
uint8_t g_borland_file_mode;
static unsigned g_create_calls;
static Win16FarPtr g_create_class;
static Win16FarPtr g_create_title;
static uint32_t g_create_style;
static INT16 g_create_x;
static INT16 g_create_y;
static INT16 g_create_width;
static INT16 g_create_height;
static HINSTANCE16 g_create_instance;
static unsigned g_show_calls;
static unsigned g_update_calls;
static unsigned g_icon_calls;
static unsigned g_cursor_calls;
static unsigned g_register_calls;
static unsigned g_text_init_calls;
static Win16FarPtr g_text_records[2];
static unsigned g_reset_calls;
static unsigned g_rewrite_calls;
static unsigned g_require_calls;
static unsigned g_module_name_calls;
static unsigned g_oem_calls;
static unsigned g_set_title_calls;
static unsigned g_get_menu_calls;
static unsigned g_enable_calls;
static unsigned g_get_message_calls;
static unsigned g_translate_calls;
static unsigned g_dispatch_calls;
HWND16 CreateWindow16(
Win16FarPtr class_name,
Win16FarPtr window_name,
uint32_t style,
INT16 x,
INT16 y,
INT16 width,
INT16 height,
HWND16 parent,
HMENU16 menu,
HINSTANCE16 instance,
Win16FarPtr parameter)
{
assert(parent == 0 && menu == 0 && parameter == 0);
g_create_calls++;
g_create_class = class_name;
g_create_title = window_name;
g_create_style = style;
g_create_x = x;
g_create_y = y;
g_create_width = width;
g_create_height = height;
g_create_instance = instance;
return 0x4321;
}
BOOL16 ShowWindow16(HWND16 window, INT16 command)
{
assert(window == 0x4321 && command == (INT16)g_win16_show_command);
g_show_calls++;
return 1;
}
BOOL16 UpdateWindow16(HWND16 window)
{
assert(window == g_tpwincrt_window);
g_update_calls++;
return 1;
}
HICON16 LoadIcon16(HINSTANCE16 instance, Win16FarPtr resource)
{
assert(instance == 0 && resource == win16_make_far_pointer(0, 0x7f00));
g_icon_calls++;
return 0x1111;
}
HCURSOR16 LoadCursor16(HINSTANCE16 instance, Win16FarPtr resource)
{
assert(instance == 0 && resource == win16_make_far_pointer(0, 0x7f00));
g_cursor_calls++;
return 0x2222;
}
uint16_t RegisterClass16(const WNDCLASS16 *window_class)
{
assert(window_class == &g_tpwincrt_window_class);
assert(window_class->instance == g_win16_instance);
assert(window_class->icon == 0x1111 && window_class->cursor == 0x2222);
assert(window_class->background_brush == 6);
g_register_calls++;
return 1;
}
void tpwincrt_initialize_text_record(Win16FarPtr record)
{
assert(g_text_init_calls < 2);
g_text_records[g_text_init_calls++] = record;
}
void borland_reset_file(Win16FarPtr record)
{
assert(record == win16_dgroup_pointer(0x4b38));
g_reset_calls++;
}
void borland_rewrite_file(Win16FarPtr record)
{
assert(record == win16_dgroup_pointer(0x4c38));
g_rewrite_calls++;
}
void borland_require_io_success(void)
{
g_require_calls++;
}
UINT16 GetModuleFileNameFar16(
HINSTANCE16 module, Win16FarPtr buffer, UINT16 size)
{
assert(module == g_win16_instance);
assert(buffer == win16_dgroup_pointer(0x4a6e) && size == 0x50);
g_module_name_calls++;
return 7;
}
INT16 OemToAnsi16(Win16FarPtr source, Win16FarPtr destination)
{
assert(source == win16_dgroup_pointer(0x4a6e));
assert(destination == source);
g_oem_calls++;
return 1;
}
INT16 wvsprintf16(
char *output, const char *format, const uint16_t *arguments)
{
assert(format == g_tpwincrt_inactive_format);
Win16FarPtr title = win16_dgroup_pointer(0x4a6e);
assert(arguments[0] == win16_far_offset(title));
assert(arguments[1] == win16_far_selector(title));
memcpy(output, "inactive", 9);
return 8;
}
BOOL16 SetWindowText16(HWND16 window, const char *text)
{
assert(window == g_tpwincrt_window && strcmp(text, "inactive") == 0);
g_set_title_calls++;
return 1;
}
HMENU16 GetSystemMenu16(HWND16 window, BOOL16 revert)
{
assert(window == g_tpwincrt_window && revert == 0);
g_get_menu_calls++;
return 0x7777;
}
BOOL16 EnableMenuItem16(HMENU16 menu, UINT16 item, UINT16 flags)
{
assert(menu == 0x7777 && item == 0xf060 && flags == 0);
g_enable_calls++;
return 1;
}
BOOL16 GetMessage16(
MSG16 *message,
HWND16 window,
UINT16 minimum_message,
UINT16 maximum_message)
{
assert(window == 0 && minimum_message == 0 && maximum_message == 0);
g_get_message_calls++;
message->message = WIN16_WM_PAINT;
return g_get_message_calls < 3;
}
BOOL16 TranslateMessage16(const MSG16 *message)
{
assert(message->message == WIN16_WM_PAINT);
g_translate_calls++;
return 1;
}
LRESULT16 DispatchMessage16(const MSG16 *message)
{
assert(message->message == WIN16_WM_PAINT);
g_dispatch_calls++;
return 0;
}
static void reset_counts(void)
{
g_create_calls = 0;
g_show_calls = 0;
g_update_calls = 0;
g_icon_calls = 0;
g_cursor_calls = 0;
g_register_calls = 0;
g_text_init_calls = 0;
g_reset_calls = 0;
g_rewrite_calls = 0;
g_require_calls = 0;
g_module_name_calls = 0;
g_oem_calls = 0;
g_set_title_calls = 0;
g_get_menu_calls = 0;
g_enable_calls = 0;
g_get_message_calls = 0;
g_translate_calls = 0;
g_dispatch_calls = 0;
}
int main(void)
{
static uint8_t dgroup[0x6000];
static uint8_t code[0x3000];
win16_reset_segment_bindings();
win16_bind_segment(0x7000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x7000);
win16_bind_ne_segment(2, 0x6000, code, sizeof(code), false);
g_tpwincrt_window_class.class_name =
win16_make_far_pointer(0x7000, 0x057c);
g_tpwincrt_initial_x = (int16_t)0x8000;
g_tpwincrt_initial_y = (int16_t)0x8000;
g_tpwincrt_initial_width = (int16_t)0x8000;
g_tpwincrt_initial_height = (int16_t)0x8000;
g_win16_instance = 0x1234;
g_win16_show_command = 7;
reset_counts();
g_tpwincrt_window_created = 0;
tpwincrt_create_window();
assert(g_create_calls == 1 && g_show_calls == 1 && g_update_calls == 1);
assert(g_tpwincrt_window == 0x4321);
assert(g_create_class == g_tpwincrt_window_class.class_name);
assert(g_create_title == win16_dgroup_pointer(0x4a6e));
assert(g_create_style == 0x00ff0000u);
assert(g_create_x == (int16_t)0x8000 &&
g_create_y == (int16_t)0x8000);
assert(g_create_width == (int16_t)0x8000 &&
g_create_height == (int16_t)0x8000);
assert(g_create_instance == g_win16_instance);
g_tpwincrt_window_created = 1;
tpwincrt_create_window();
assert(g_create_calls == 1);
reset_counts();
g_win16_previous_instance = 0;
g_borland_exit_proc = win16_make_far_pointer(0x5555, 0x4444);
tpwincrt_initialize_runtime_unit();
assert(g_icon_calls == 1 && g_cursor_calls == 1 && g_register_calls == 1);
assert(g_text_init_calls == 2);
assert(g_text_records[0] == win16_dgroup_pointer(0x4b38));
assert(g_text_records[1] == win16_dgroup_pointer(0x4c38));
assert(g_reset_calls == 1 && g_rewrite_calls == 1 && g_require_calls == 2);
assert(g_module_name_calls == 1 && g_oem_calls == 1);
assert(g_tpwincrt_saved_exit_proc ==
win16_make_far_pointer(0x5555, 0x4444));
assert(g_borland_exit_proc ==
win16_make_far_pointer(0x6000, 0x24a5));
reset_counts();
g_win16_previous_instance = 1;
g_borland_exit_proc = 0;
tpwincrt_initialize_runtime_unit();
assert(g_icon_calls == 0 && g_cursor_calls == 0 && g_register_calls == 0);
assert(g_text_init_calls == 2 && g_reset_calls == 1 && g_rewrite_calls == 1);
reset_counts();
g_tpwincrt_saved_exit_proc = win16_make_far_pointer(0xaaaa, 0xbbbb);
g_borland_exit_proc = 0;
g_tpwincrt_window_created = 1;
g_tpwincrt_window = 0x4321;
g_borland_error_offset = 0;
g_borland_error_segment = 0;
g_tpwincrt_break_check_enabled = 1;
tpwincrt_shutdown_exit_proc();
assert(g_borland_exit_proc == g_tpwincrt_saved_exit_proc);
assert(g_set_title_calls == 1 && g_get_menu_calls == 1 &&
g_enable_calls == 1);
assert(g_tpwincrt_break_check_enabled == 0);
assert(g_get_message_calls == 3);
assert(g_translate_calls == 2 && g_dispatch_calls == 2);
reset_counts();
g_borland_exit_proc = 0;
g_borland_error_offset = 1;
tpwincrt_shutdown_exit_proc();
assert(g_borland_exit_proc == g_tpwincrt_saved_exit_proc);
assert(g_set_title_calls == 0 && g_get_message_calls == 0);
return 0;
}
@@ -0,0 +1,356 @@
#include "../tdkpin_tpwincrt_text.h"
#include "../tdkpin_borland_files.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
HWND16 g_tpwincrt_window;
uint16_t g_tpwincrt_columns;
uint16_t g_tpwincrt_rows;
uint16_t g_tpwincrt_cursor_column;
uint16_t g_tpwincrt_cursor_row;
uint16_t g_tpwincrt_view_column;
uint16_t g_tpwincrt_view_row;
uint8_t g_tpwincrt_break_check_enabled;
uint8_t g_tpwincrt_auto_scroll;
uint8_t g_tpwincrt_ctrl_z_is_eof;
uint16_t g_tpwincrt_input_count;
uint16_t g_tpwincrt_ring_row_origin;
uint8_t g_tpwincrt_window_created;
uint8_t g_tpwincrt_has_focus;
uint8_t g_tpwincrt_caret_enabled;
uint8_t g_tpwincrt_paint_message_active;
Win16FarPtr g_tpwincrt_screen_buffer;
uint16_t g_tpwincrt_character_width;
uint16_t g_tpwincrt_character_height;
uint16_t g_tpwincrt_caret_y_offset;
HDC16 g_tpwincrt_paint_dc;
PAINTSTRUCT16 g_tpwincrt_paint;
HGDIOBJ16 g_tpwincrt_previous_font;
uint16_t g_tpwincrt_visible_columns;
uint16_t g_tpwincrt_visible_rows;
uint16_t g_tpwincrt_max_view_column;
uint16_t g_tpwincrt_max_view_row;
uint8_t g_tpwincrt_input_buffer[64];
_Atomic uint16_t g_borland_io_result;
uint8_t g_borland_file_mode;
static unsigned g_begin_calls;
static unsigned g_end_calls;
static unsigned g_draw_calls;
static INT16 g_last_x;
static INT16 g_last_y;
static INT16 g_last_draw_count;
static unsigned g_scroll_cursor_calls;
static unsigned g_create_window_calls;
static unsigned g_beep_calls;
static unsigned g_scroll_window_calls;
static INT16 g_scroll_delta_y;
static unsigned g_update_calls;
static unsigned g_create_caret_calls;
static unsigned g_destroy_caret_calls;
static unsigned g_wait_calls;
static MSG16 g_messages[4];
static unsigned g_message_count;
static unsigned g_message_index;
static unsigned g_translate_calls;
static unsigned g_dispatch_calls;
Win16FarPtr tpwincrt_cell_pointer(int16_t row, int16_t column)
{
uint16_t physical = (uint16_t)(row + g_tpwincrt_ring_row_origin);
if ((int16_t)physical >= (int16_t)g_tpwincrt_rows) {
physical = (uint16_t)(physical - g_tpwincrt_rows);
}
uint16_t offset = (uint16_t)(
win16_far_offset(g_tpwincrt_screen_buffer) +
physical * g_tpwincrt_columns + (uint16_t)column);
return win16_make_far_pointer(
win16_far_selector(g_tpwincrt_screen_buffer), offset);
}
void tpwincrt_begin_drawing(void)
{
g_begin_calls++;
g_tpwincrt_paint_dc = 0x3333;
}
void tpwincrt_end_drawing(void)
{
g_end_calls++;
}
void tpwincrt_scroll_cursor_into_view(void)
{
g_scroll_cursor_calls++;
}
void tpwincrt_create_window(void)
{
g_create_window_calls++;
}
void tpwincrt_create_caret(void)
{
g_create_caret_calls++;
}
void tpwincrt_destroy_caret(void)
{
g_destroy_caret_calls++;
}
_Noreturn void tpwincrt_abort_on_ctrl_c(void)
{
assert(false);
__builtin_unreachable();
}
BOOL16 TextOut16(
HDC16 dc, INT16 x, INT16 y, Win16FarPtr text, INT16 count)
{
assert(dc == 0x3333);
(void)text;
g_draw_calls++;
g_last_x = x;
g_last_y = y;
g_last_draw_count = count;
return 1;
}
BOOL16 ScrollWindow16(
HWND16 window,
INT16 delta_x,
INT16 delta_y,
Win16FarPtr scroll_rect,
Win16FarPtr clip_rect)
{
assert(window == g_tpwincrt_window && delta_x == 0);
assert(scroll_rect == 0 && clip_rect == 0);
g_scroll_window_calls++;
g_scroll_delta_y = delta_y;
return 1;
}
BOOL16 UpdateWindow16(HWND16 window)
{
assert(window == g_tpwincrt_window);
g_update_calls++;
return 1;
}
BOOL16 MessageBeep16(UINT16 type)
{
assert(type == 0);
g_beep_calls++;
return 1;
}
BOOL16 PeekMessage16(
MSG16 *message,
HWND16 window,
UINT16 minimum_message,
UINT16 maximum_message,
UINT16 remove_message)
{
assert(window == 0 && minimum_message == 0 && maximum_message == 0);
assert(remove_message == 1);
if (g_message_index == g_message_count) {
return 0;
}
*message = g_messages[g_message_index++];
return 1;
}
BOOL16 TranslateMessage16(const MSG16 *message)
{
assert(message->message == g_messages[g_translate_calls].message);
g_translate_calls++;
return 1;
}
LRESULT16 DispatchMessage16(const MSG16 *message)
{
assert(message->message == g_messages[g_dispatch_calls].message);
g_dispatch_calls++;
return 0;
}
BOOL16 WaitMessage16(void)
{
g_wait_calls++;
g_tpwincrt_input_buffer[0] = 'Z';
g_tpwincrt_input_count = 1;
return 1;
}
void borland_fill_bytes(
Win16FarPtr destination, uint16_t count, uint8_t value)
{
for (uint16_t index = 0; index != count; index++) {
win16_write_u8(win16_far_add_offset(destination, index), value);
}
}
void borland_move_bytes(
Win16FarPtr destination, Win16FarPtr source, uint16_t count)
{
assert(win16_far_offset(destination) == 0x4af8);
assert(win16_far_offset(source) == 0x4af9);
memmove(g_tpwincrt_input_buffer, g_tpwincrt_input_buffer + 1, count);
}
static void reset_observations(void)
{
g_begin_calls = 0;
g_end_calls = 0;
g_draw_calls = 0;
g_scroll_cursor_calls = 0;
g_create_window_calls = 0;
g_beep_calls = 0;
g_scroll_window_calls = 0;
g_update_calls = 0;
g_create_caret_calls = 0;
g_destroy_caret_calls = 0;
g_wait_calls = 0;
g_message_count = 0;
g_message_index = 0;
g_translate_calls = 0;
g_dispatch_calls = 0;
}
int main(void)
{
static uint8_t dgroup[0x6000];
static uint8_t screen[0x100];
static uint8_t code[0x3000];
uint8_t stack_anchor = 0;
uintptr_t stack_base = (uintptr_t)&stack_anchor - 0x8000;
win16_reset_segment_bindings();
win16_bind_segment(0x7000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x7000);
win16_bind_segment(0x7100, screen, sizeof(screen), true);
win16_bind_ne_segment(2, 0x6000, code, sizeof(code), false);
win16_bind_segment(0x7200, (void *)stack_base, 0x10000, true);
g_tpwincrt_window = 0x2222;
g_tpwincrt_columns = 5;
g_tpwincrt_rows = 3;
g_tpwincrt_character_width = 8;
g_tpwincrt_character_height = 12;
g_tpwincrt_screen_buffer = win16_make_far_pointer(0x7100, 0);
memset(screen, ' ', sizeof(screen));
Win16FarPtr record = win16_make_far_pointer(0x7000, 0x100);
memset(dgroup + 0x100, 0xa5, 0x100);
tpwincrt_initialize_text_record(record);
assert(win16_read_u16(record) == 0xffff);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_CLOSED);
assert(win16_read_u16(win16_far_add_offset(record, 4)) == 0x80);
assert(win16_read_u16(win16_far_add_offset(record, 6)) == 0xa5a5);
assert(win16_read_far_pointer(record, 0x0c) ==
win16_far_add_offset(record, 0x80));
assert(win16_read_far_pointer(record, 0x10) ==
win16_make_far_pointer(0x6000, 0x238b));
assert(win16_read_u8(win16_far_add_offset(record, 0x30)) == 0);
win16_write_u16(record, 2, BORLAND_FILE_INPUT);
assert(tpwincrt_open_text_record(record) == 0);
assert(win16_read_far_pointer(record, 0x14) ==
win16_make_far_pointer(0x6000, 0x233c));
assert(win16_read_far_pointer(record, 0x18) == 0);
assert(win16_read_far_pointer(record, 0x1c) ==
win16_make_far_pointer(0x6000, 0x2374));
win16_write_u16(record, 2, BORLAND_TEXT_APPEND);
assert(tpwincrt_open_text_record(record) == 0);
assert(win16_read_u16(win16_far_add_offset(record, 2)) ==
BORLAND_FILE_OUTPUT);
assert(win16_read_far_pointer(record, 0x14) ==
win16_make_far_pointer(0x6000, 0x22fc));
assert(win16_read_far_pointer(record, 0x18) ==
win16_make_far_pointer(0x6000, 0x22fc));
reset_observations();
g_tpwincrt_auto_scroll = 1;
uint8_t output[] = {'A', 'B', '\b', 'C', '\r', 'D', '\a'};
Win16FarPtr output_far = win16_make_far_pointer(0x7000, 0x300);
memcpy(dgroup + 0x300, output, sizeof(output));
tpwincrt_write_bytes(sizeof(output), output_far);
assert(memcmp(screen, "AC D", 6) == 0);
assert(g_tpwincrt_cursor_column == 1 && g_tpwincrt_cursor_row == 1);
assert(g_beep_calls == 1 && g_create_window_calls == 1);
assert(g_scroll_cursor_calls == 1 && g_draw_calls >= 2);
reset_observations();
g_tpwincrt_cursor_column = 0;
g_tpwincrt_cursor_row = 2;
g_tpwincrt_ring_row_origin = 2;
int16_t changed_start = 0;
int16_t changed_end = 0;
memset(screen, 'Q', 15);
tpwincrt_advance_line(&changed_start, &changed_end);
assert(g_tpwincrt_cursor_row == 2 && g_tpwincrt_ring_row_origin == 0);
assert(memcmp(screen + 10, " ", 5) == 0);
assert(g_scroll_window_calls == 1 && g_scroll_delta_y == -12);
assert(g_update_calls == 1);
reset_observations();
g_messages[0].message = WIN16_WM_PAINT;
g_messages[1].message = WIN16_WM_SIZE;
g_message_count = 2;
g_tpwincrt_input_count = 1;
assert(tpwincrt_poll_input());
assert(g_translate_calls == 2 && g_dispatch_calls == 2);
reset_observations();
memcpy(g_tpwincrt_input_buffer, "XY", 2);
g_tpwincrt_input_count = 2;
assert(tpwincrt_read_character() == 'X');
assert(g_tpwincrt_input_count == 1 &&
g_tpwincrt_input_buffer[0] == 'Y');
assert(g_wait_calls == 0);
reset_observations();
g_tpwincrt_input_count = 0;
g_tpwincrt_has_focus = 1;
assert(tpwincrt_read_character() == 'Z');
assert(g_wait_calls == 1);
assert(g_create_caret_calls == 1 && g_destroy_caret_calls == 1);
assert(g_tpwincrt_caret_enabled == 0);
reset_observations();
g_tpwincrt_cursor_column = 0;
g_tpwincrt_cursor_row = 0;
g_tpwincrt_ring_row_origin = 0;
g_tpwincrt_has_focus = 0;
g_tpwincrt_ctrl_z_is_eof = 0;
const uint8_t line_input[] = {'A', 'B', '\b', 'C', '\r'};
memcpy(g_tpwincrt_input_buffer, line_input, sizeof(line_input));
g_tpwincrt_input_count = sizeof(line_input);
Win16FarPtr line = win16_make_far_pointer(0x7000, 0x400);
assert(tpwincrt_read_line(10, line) == 4);
assert(memcmp(dgroup + 0x400, "AC\r\n", 4) == 0);
reset_observations();
const uint8_t eof_input[] = {'X', 0x1a};
memcpy(g_tpwincrt_input_buffer, eof_input, sizeof(eof_input));
g_tpwincrt_input_count = sizeof(eof_input);
g_tpwincrt_ctrl_z_is_eof = 1;
assert(tpwincrt_read_line(10, line) == 2);
assert(dgroup[0x400] == 'X' && dgroup[0x401] == 0x1a);
reset_observations();
Win16FarPtr record_buffer = win16_make_far_pointer(0x7000, 0x500);
memcpy(dgroup + 0x500, "OK", 2);
win16_write_u16(record, 8, 2);
win16_write_u16(record, 0x0c, win16_far_offset(record_buffer));
win16_write_u16(record, 0x0e, win16_far_selector(record_buffer));
assert(tpwincrt_text_write_buffer(record) == 0);
assert(win16_read_u16(win16_far_add_offset(record, 8)) == 0);
return 0;
}
@@ -0,0 +1,363 @@
#include "../tdkpin_turn_state.h"
#include <assert.h>
#include <string.h>
uint8_t g_player_count;
uint8_t g_current_player;
uint8_t g_gameplay_transition_pending;
int32_t g_ball_x_milli;
int32_t g_ball_y_milli;
static uint8_t g_stack[0x400];
static uint8_t g_object[0x0c00];
static uint8_t g_dgroup[0x5000];
static Win16FarPtr g_receiver;
static unsigned g_restore_previous_calls;
static unsigned g_restore_record_b_calls;
static unsigned g_dynamic_record_calls;
static unsigned g_sound_calls;
static unsigned g_load_highscores_calls;
static unsigned g_insert_score_calls;
static unsigned g_transition_calls;
static unsigned g_show_highscore_calls;
static unsigned g_collision_refresh_calls;
static unsigned g_item_refresh_calls;
static unsigned g_restore_item_calls;
static unsigned g_draw_item_calls;
static unsigned g_status_calls;
static unsigned g_redraw_calls;
static unsigned g_reset_ball_calls;
static unsigned g_restore_b_calls;
static unsigned g_present_calls;
static unsigned g_marker_refresh_calls;
static unsigned g_get_dc_calls;
static unsigned g_release_dc_calls;
static uint16_t g_last_item_count;
static uint8_t g_last_status_player;
void tdkpin_restore_previous_ball_region(
uint16_t caller_bp, uint8_t suppress_present)
{
assert(caller_bp == 0x0100 && suppress_present == 0);
g_restore_previous_calls++;
}
void tdkpin_restore_record_b(uint16_t caller_bp, uint16_t id)
{
assert(caller_bp == 0x0100 && id == 1020);
g_restore_record_b_calls++;
}
void tdkpin_update_dynamic_ball_record(
uint16_t ignored_static_link, int32_t displacement)
{
assert(ignored_static_link == 0x0100 && displacement == 1);
g_dynamic_record_calls++;
}
BorlandReal48 borland_i32_to_real48(int32_t value)
{
assert(value == 1000);
return (BorlandReal48){{0x81, 1, 2, 3, 4, 5}};
}
BorlandReal48 borland_real48_multiply(
BorlandReal48 left, BorlandReal48 right)
{
static const uint8_t expected_left[6] = {0x81, 1, 2, 3, 4, 5};
static const uint8_t expected_right[6] = {
0x80, 0xcd, 0xcc, 0xcc, 0xcc, 0x4c};
assert(memcmp(left.bytes, expected_left, 6) == 0);
assert(memcmp(right.bytes, expected_right, 6) == 0);
return (BorlandReal48){{0x82, 6, 7, 8, 9, 10}};
}
int32_t borland_real48_round_to_i32(BorlandReal48 value)
{
static const uint8_t expected[6] = {0x82, 6, 7, 8, 9, 10};
assert(memcmp(value.bytes, expected, 6) == 0);
return 800;
}
void tdkpin_play_sound_if_not_tilted(uint16_t sound_number)
{
assert(sound_number == 8);
g_sound_calls++;
}
void tdkpin_load_highscores(uint16_t ignored_static_link)
{
assert(ignored_static_link == 0x0100);
g_load_highscores_calls++;
}
void tdkpin_insert_current_score(uint16_t caller_bp)
{
assert(caller_bp == 0x0100);
g_insert_score_calls++;
}
void tdkpin_set_gameplay_transition_flags(uint16_t caller_bp)
{
assert(caller_bp == 0x0100);
g_transition_calls++;
}
void tdkpin_show_highscore_windows(uint16_t caller_bp)
{
assert(caller_bp == 0x0100);
g_show_highscore_calls++;
}
void tdkpin_refresh_collision_record(uint16_t caller_bp, uint16_t id)
{
assert(caller_bp == 0x0100);
assert(id == g_collision_refresh_calls + 1);
g_collision_refresh_calls++;
}
void tdkpin_refresh_player_item(uint16_t caller_bp, uint16_t item)
{
assert(caller_bp == 0x0100);
assert(item == g_item_refresh_calls + 1);
g_item_refresh_calls++;
}
void tdkpin_restore_record_or_item_a(uint16_t caller_bp, uint16_t id)
{
assert(caller_bp == 0x0100);
assert(id == 1001 + g_restore_item_calls);
g_restore_item_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;
}
void tdkpin_draw_item_status(int16_t item, HDC16 dc)
{
assert(dc == 0x3333);
g_last_item_count = (uint16_t)item;
g_draw_item_calls++;
}
void tdkpin_update_status_display(uint8_t player, HDC16 dc)
{
assert(dc == 0x3333);
g_last_status_player = player;
g_status_calls++;
}
void tdkpin_redraw_all_players(uint16_t caller_bp)
{
assert(caller_bp == 0x0100);
g_redraw_calls++;
}
void tdkpin_reset_ball_state(uint16_t caller_bp)
{
assert(caller_bp == 0x0100);
g_reset_ball_calls++;
}
void tdkpin_restore_background_region_b(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height == 20 && width == 148 && y == 243 && x == 84);
assert(dc == 0x3333);
g_restore_b_calls++;
}
void tdkpin_present_background_region(
INT16 height, INT16 width, INT16 y, INT16 x, HDC16 dc)
{
assert(height == 20 && width == 148 && y == 243 && x == 84);
assert(dc == 0x3333);
g_present_calls++;
}
void tdkpin_refresh_player_progress_markers(uint16_t caller_bp)
{
assert(caller_bp == 0x0100);
g_marker_refresh_calls++;
}
_Noreturn void win16_integer_divide_fault(void)
{
__builtin_trap();
}
static void reset_counters(void)
{
g_restore_previous_calls = 0;
g_restore_record_b_calls = 0;
g_dynamic_record_calls = 0;
g_sound_calls = 0;
g_load_highscores_calls = 0;
g_insert_score_calls = 0;
g_transition_calls = 0;
g_show_highscore_calls = 0;
g_collision_refresh_calls = 0;
g_item_refresh_calls = 0;
g_restore_item_calls = 0;
g_draw_item_calls = 0;
g_status_calls = 0;
g_redraw_calls = 0;
g_reset_ball_calls = 0;
g_restore_b_calls = 0;
g_present_calls = 0;
g_marker_refresh_calls = 0;
g_get_dc_calls = 0;
g_release_dc_calls = 0;
g_last_item_count = 0;
g_last_status_player = 0;
}
static void prepare_fixture(void)
{
memset(g_stack, 0, sizeof(g_stack));
memset(g_object, 0, sizeof(g_object));
memset(g_dgroup, 0, sizeof(g_dgroup));
win16_reset_segment_bindings();
win16_bind_segment(0x7000, g_stack, sizeof(g_stack), true);
win16_bind_segment(0x7100, g_object, sizeof(g_object), true);
win16_bind_segment(0x7200, g_dgroup, sizeof(g_dgroup), true);
win16_set_dgroup_selector(0x7200);
win16_set_stack_state(0x7000, 0);
g_receiver = win16_make_far_pointer(0x7100, 0);
win16_write_stack_u16(0x0106, win16_far_offset(g_receiver));
win16_write_stack_u16(0x0108, win16_far_selector(g_receiver));
win16_write_u16(g_receiver, 4, 0x2222);
g_player_count = 3;
g_current_player = 1;
g_ball_x_milli = 0;
g_ball_y_milli = 0;
reset_counters();
}
static Win16FarPtr record(uint16_t id)
{
return win16_dgroup_pointer((uint16_t)(0x095b + id * 0x53u));
}
static void test_special_respawn(void)
{
prepare_fixture();
g_current_player = 2;
win16_write_u16(win16_dgroup_pointer(0x399a), 0, 7);
win16_write_u32(win16_dgroup_pointer(0x3969), 0, 0x11112222u);
win16_write_u32(win16_dgroup_pointer(0x396d), 0, 0x33334444u);
win16_write_u8(win16_far_add_offset(g_receiver, 0x04a3), 1);
win16_write_u32(g_receiver, 0x56, 1000);
win16_write_u32(g_receiver, 0x0bca, 0xaaaaaaaau);
tdkpin_finish_ball_or_advance_player(0x0100);
assert(g_restore_previous_calls == 1);
assert(g_restore_record_b_calls == 1 && g_dynamic_record_calls == 1);
assert((uint32_t)g_ball_x_milli == 0x11112222u);
assert((uint32_t)g_ball_y_milli == 0x33334444u);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bc2)) ==
0x11112222u);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bc6)) ==
0x33334444u);
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x04a3)) == 0);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0baa)) == 0);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bae)) == 800);
assert(win16_read_u32(win16_far_add_offset(g_receiver, 0x0bca)) == 0);
assert(win16_read_u16(win16_dgroup_pointer(0x399a)) == 0);
assert(win16_read_u8(win16_dgroup_pointer(0x0875)) == 1);
assert(win16_read_u8(win16_dgroup_pointer(0x41f9)) == 1);
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x61)) == 1);
assert(g_sound_calls == 0 && g_redraw_calls == 0);
}
static void seed_collision_and_player_three_mirrors(void)
{
for (uint16_t id = 1; id <= 175; id++) {
win16_write_u8(
win16_far_add_offset(record(id), 0x34), (uint8_t)(id & 1u));
win16_write_u16(record(id), 0x43, (uint16_t)(0x1000 + id));
win16_write_u8(
win16_far_add_offset(g_receiver, (uint16_t)(
0x0061 + 3u * 0x00afu + id)),
(uint8_t)(0x80u | (id & 1u)));
win16_write_u16(
g_receiver,
(uint16_t)(0x036c + 3u * 0x015eu + id * 2u),
(uint16_t)(0x2000 + id));
}
}
static void test_advance_to_live_player(void)
{
prepare_fixture();
win16_write_u8(win16_dgroup_pointer(0x0874), 1);
win16_write_u16(win16_dgroup_pointer(0x0821), 0, 2);
win16_write_u16(win16_dgroup_pointer(0x0823), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x0825), 0, 3);
win16_write_u8(win16_dgroup_pointer(0x0819), 4);
seed_collision_and_player_three_mirrors();
for (uint16_t item = 1; item <= 3; item++) {
win16_write_u8(win16_far_add_offset(
g_receiver, (uint16_t)(0x046c + 3u * 0x14u + item)), 1);
}
tdkpin_finish_ball_or_advance_player(0x0100);
assert(g_sound_calls == 1 && g_restore_previous_calls == 1);
assert(g_current_player == 3);
for (uint16_t id = 1; id <= 175; id++) {
assert(win16_read_u8(win16_far_add_offset(
g_receiver, (uint16_t)(0x0061 + 0x00afu + id))) ==
(uint8_t)(id & 1u));
assert(win16_read_u16(win16_far_add_offset(
g_receiver,
(uint16_t)(0x036c + 0x015eu + id * 2u))) ==
(uint16_t)(0x1000 + id));
assert(win16_read_u8(win16_far_add_offset(record(id), 0x34)) ==
(uint8_t)(0x80u | (id & 1u)));
assert(win16_read_u16(win16_far_add_offset(record(id), 0x43)) ==
(uint16_t)(0x2000 + id));
}
assert(g_collision_refresh_calls == 175 && g_item_refresh_calls == 20);
assert(g_restore_item_calls == 3);
assert(g_draw_item_calls == 1 && g_last_item_count == 3);
assert(g_status_calls == 1 && g_last_status_player == 3);
assert(g_redraw_calls == 1 && g_reset_ball_calls == 1);
assert(win16_read_u16(win16_dgroup_pointer(0x0825)) == 2);
assert(g_restore_b_calls == 1 && g_present_calls == 1);
assert(g_marker_refresh_calls == 1);
assert(g_get_dc_calls == 2 && g_release_dc_calls == 2);
}
static void test_game_over_path(void)
{
prepare_fixture();
win16_write_u8(win16_dgroup_pointer(0x0874), 1);
win16_write_u16(win16_dgroup_pointer(0x0821), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x0823), 0, 0);
g_player_count = 2;
tdkpin_finish_ball_or_advance_player(0x0100);
assert(g_load_highscores_calls == 1 && g_insert_score_calls == 1);
assert(g_current_player == 0 && g_transition_calls == 1);
assert(g_show_highscore_calls == 1);
assert(g_collision_refresh_calls == 0 && g_redraw_calls == 0);
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x0bda)) == 0);
assert(win16_read_u8(win16_far_add_offset(g_receiver, 0x0bdb)) == 0);
}
int main(void)
{
test_special_respawn();
test_advance_to_live_player();
test_game_over_path();
return 0;
}
@@ -0,0 +1,315 @@
#include "../tdkpin_window_messages.h"
#include <assert.h>
#include <stdint.h>
Win16FarPtr g_object_windows_application;
uint8_t g_object_windows_focus_event_marker;
static int32_t g_window_style;
static unsigned g_sequence[32];
static unsigned g_sequence_count;
static Win16FarPtr g_last_procedure;
static Win16FarPtr g_last_receiver;
static ObjectWindowsMessage *g_last_message;
static uint16_t g_scroll_position;
static uint16_t g_scroll_command;
static HDC16 g_paint_dc;
static RECT16 g_window_rectangle;
static MSG16 g_queued_messages[4];
static unsigned g_queued_count;
static unsigned g_queued_index;
static HWND16 g_capture_window;
static unsigned g_release_calls;
static unsigned g_translate_calls;
static unsigned g_dispatch_calls;
static unsigned g_mouse_calls;
enum Sequence {
SEQ_MESSAGE = 1,
SEQ_SCROLL,
SEQ_BEGIN_PAINT,
SEQ_PAINT,
SEQ_METHOD,
SEQ_END_PAINT,
SEQ_GET_RECT,
SEQ_CAPTURE,
SEQ_TRANSLATE,
SEQ_DISPATCH,
SEQ_MOUSE,
SEQ_RELEASE,
};
static void record(unsigned value)
{
assert(g_sequence_count < sizeof(g_sequence) / sizeof(*g_sequence));
g_sequence[g_sequence_count++] = value;
}
int32_t GetWindowLong16(HWND16 window, INT16 index)
{
assert(window == 0x1234 && index == -16);
return g_window_style;
}
void win16_call_object_message_handler(
Win16FarPtr procedure,
Win16FarPtr object,
ObjectWindowsMessage *message)
{
g_last_procedure = procedure;
g_last_receiver = object;
g_last_message = message;
record(SEQ_MESSAGE);
}
void win16_call_object_scroll_method(
Win16FarPtr procedure,
Win16FarPtr object,
uint16_t position,
uint16_t command)
{
g_last_procedure = procedure;
g_last_receiver = object;
g_scroll_position = position;
g_scroll_command = command;
record(SEQ_SCROLL);
}
HDC16 BeginPaint16(HWND16 window, Win16FarPtr paint)
{
assert(window == 0x1234);
win16_write_u16(paint, 0, g_paint_dc);
record(SEQ_BEGIN_PAINT);
return g_paint_dc;
}
BOOL16 EndPaint16(HWND16 window, Win16FarPtr paint)
{
assert(window == 0x1234 && win16_read_u16(paint) == g_paint_dc);
record(SEQ_END_PAINT);
return 1;
}
void win16_call_object_paint_method(
Win16FarPtr procedure,
Win16FarPtr object,
PAINTSTRUCT16 *paint,
HDC16 dc)
{
assert(paint->dc == g_paint_dc && dc == g_paint_dc);
g_last_procedure = procedure;
g_last_receiver = object;
record(SEQ_PAINT);
}
void win16_call_object_method(
Win16FarPtr procedure, Win16FarPtr object)
{
g_last_procedure = procedure;
g_last_receiver = object;
record(SEQ_METHOD);
}
BOOL16 GetWindowRect16(HWND16 window, Win16FarPtr rectangle)
{
assert(window == 0x1234);
win16_write_u16(rectangle, 0, (uint16_t)g_window_rectangle.left);
win16_write_u16(rectangle, 2, (uint16_t)g_window_rectangle.top);
win16_write_u16(rectangle, 4, (uint16_t)g_window_rectangle.right);
win16_write_u16(rectangle, 6, (uint16_t)g_window_rectangle.bottom);
record(SEQ_GET_RECT);
return 1;
}
HWND16 SetCapture16(HWND16 window)
{
g_capture_window = window;
record(SEQ_CAPTURE);
return 0xaaaa;
}
BOOL16 ReleaseCapture16(void)
{
g_release_calls++;
record(SEQ_RELEASE);
return 1;
}
BOOL16 PeekMessage16(
MSG16 *message,
HWND16 window,
UINT16 minimum_message,
UINT16 maximum_message,
UINT16 remove_message)
{
assert(window == 0 && minimum_message == 0 && maximum_message == 0);
assert(remove_message == 1);
if (g_queued_index == g_queued_count) {
return 0;
}
*message = g_queued_messages[g_queued_index++];
return 1;
}
BOOL16 TranslateMessage16(const MSG16 *message)
{
(void)message;
g_translate_calls++;
record(SEQ_TRANSLATE);
return 1;
}
LRESULT16 DispatchMessage16(const MSG16 *message)
{
(void)message;
g_dispatch_calls++;
record(SEQ_DISPATCH);
return 0;
}
void win16_call_object_u16_method(
Win16FarPtr procedure, Win16FarPtr object, uint16_t argument)
{
assert(argument == 0x1234);
g_last_procedure = procedure;
g_last_receiver = object;
g_mouse_calls++;
record(SEQ_MOUSE);
}
static void write_far(
Win16FarPtr base, uint16_t offset, Win16FarPtr value)
{
win16_write_u16(base, offset, win16_far_offset(value));
win16_write_u16(
base, (uint16_t)(offset + 2), win16_far_selector(value));
}
static void reset_observations(void)
{
g_sequence_count = 0;
g_last_procedure = 0;
g_last_receiver = 0;
g_last_message = 0;
g_queued_count = 0;
g_queued_index = 0;
g_release_calls = 0;
g_translate_calls = 0;
g_dispatch_calls = 0;
g_mouse_calls = 0;
}
int main(void)
{
static uint8_t dgroup[0x1000];
uint8_t stack_anchor = 0;
uintptr_t stack_base = (uintptr_t)&stack_anchor - 0x8000;
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(0x7000, (void *)stack_base, 0x10000, true);
Win16FarPtr window = win16_make_far_pointer(0x5000, 0x0200);
Win16FarPtr attached = win16_make_far_pointer(0x5000, 0x0300);
win16_write_u16(window, 0, 0x0400);
win16_write_u16(window, 4, 0x1234);
win16_write_u16(attached, 0, 0x0500);
write_far(window, 0x3b, attached);
Win16FarPtr default_proc = win16_make_far_pointer(0x6000, 0x1000);
Win16FarPtr fallback_scroll = win16_make_far_pointer(0x6000, 0x1001);
Win16FarPtr window_paint = win16_make_far_pointer(0x6000, 0x1002);
Win16FarPtr horizontal = win16_make_far_pointer(0x6000, 0x2000);
Win16FarPtr vertical = win16_make_far_pointer(0x6000, 0x2001);
Win16FarPtr before_paint = win16_make_far_pointer(0x6000, 0x2002);
Win16FarPtr after_paint = win16_make_far_pointer(0x6000, 0x2003);
Win16FarPtr resize = win16_make_far_pointer(0x6000, 0x2004);
Win16FarPtr mouse = win16_make_far_pointer(0x6000, 0x2005);
write_far(win16_dgroup_pointer(0x0400), 0x0c, default_proc);
write_far(win16_dgroup_pointer(0x0400), 0x48, fallback_scroll);
write_far(win16_dgroup_pointer(0x0400), 0x4c, window_paint);
write_far(win16_dgroup_pointer(0x0500), 0x20, horizontal);
write_far(win16_dgroup_pointer(0x0500), 0x1c, vertical);
write_far(win16_dgroup_pointer(0x0500), 0x14, before_paint);
write_far(win16_dgroup_pointer(0x0500), 0x18, after_paint);
write_far(win16_dgroup_pointer(0x0500), 0x0c, resize);
write_far(win16_dgroup_pointer(0x0500), 0x24, mouse);
ObjectWindowsMessage message = {
.wparam = 7,
.lparam = 0x12345678,
};
reset_observations();
g_window_style = 0;
object_windows_wm_hscroll_attached(window, &message);
assert(g_last_procedure == fallback_scroll &&
g_last_receiver == window && g_last_message == &message);
reset_observations();
g_window_style = 0x00100000;
object_windows_wm_hscroll_attached(window, &message);
assert(g_last_procedure == horizontal && g_last_receiver == attached);
assert(g_scroll_position == 0x5678 && g_scroll_command == 7);
reset_observations();
write_far(window, 0x3b, 0);
object_windows_wm_hscroll_attached(window, &message);
assert(g_last_procedure == default_proc && g_last_receiver == window);
write_far(window, 0x3b, attached);
reset_observations();
g_window_style = 0x00200000;
object_windows_wm_vscroll_attached(window, &message);
assert(g_last_procedure == vertical && g_last_receiver == attached);
reset_observations();
g_paint_dc = 0x4444;
object_windows_wm_paint_attached(window, &message);
assert(g_sequence_count == 5);
assert(g_sequence[0] == SEQ_BEGIN_PAINT);
assert(g_sequence[1] == SEQ_PAINT);
assert(g_sequence[2] == SEQ_PAINT);
assert(g_sequence[3] == SEQ_METHOD);
assert(g_sequence[4] == SEQ_END_PAINT);
assert(g_last_procedure == after_paint && g_last_receiver == attached);
reset_observations();
g_window_rectangle = (RECT16){10, 20, 70, 100};
message.wparam = 0;
object_windows_wm_size_attached(window, &message);
assert(g_sequence[0] == SEQ_METHOD);
assert(g_sequence[1] == SEQ_GET_RECT);
assert(g_sequence[2] == SEQ_MESSAGE);
assert(win16_read_u16(win16_far_add_offset(window, 0x2d)) == 60);
assert(win16_read_u16(win16_far_add_offset(window, 0x2f)) == 80);
reset_observations();
message.wparam = 1;
object_windows_wm_size_attached(window, &message);
assert(g_sequence_count == 1 && g_sequence[0] == SEQ_MESSAGE);
reset_observations();
win16_write_u8(win16_far_add_offset(attached, 0x22), 1);
g_queued_messages[0].message = 0x0200;
g_queued_messages[1].message = 0x0202;
g_queued_count = 2;
object_windows_track_mouse_until_button_up(window, &message);
assert(g_capture_window == 0x1234 && g_release_calls == 1);
assert(g_translate_calls == 2 && g_dispatch_calls == 2);
assert(g_mouse_calls == 2);
assert(g_sequence[g_sequence_count - 1] == SEQ_MESSAGE);
reset_observations();
win16_set_indeterminate_u16(0x0202);
object_windows_track_mouse_until_button_up(window, &message);
assert(g_mouse_calls == 1 && g_release_calls == 1);
reset_observations();
win16_write_u8(win16_far_add_offset(attached, 0x22), 0);
object_windows_track_mouse_until_button_up(window, &message);
assert(g_capture_window == 0x1234);
assert(g_mouse_calls == 0 && g_release_calls == 0);
assert(g_sequence_count == 1 && g_sequence[0] == SEQ_MESSAGE);
return 0;
}
@@ -0,0 +1,316 @@
#include "../tdkpin_window_messages.h"
#include <assert.h>
Win16FarPtr g_object_windows_application;
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
uint16_t g_win16_show_command;
Win16FarPtr g_win16_message_box;
Win16FarPtr g_object_windows_dispatch_proc;
uint16_t g_object_windows_thunk_code_selector;
Win16FarPtr g_object_windows_free_thunk;
Win16FarPtr g_object_windows_message_hook;
Win16FarPtr g_object_windows_creating_object;
static HWND16 g_dialog_item;
static uint16_t g_dlg_code;
static HWND16 g_focus;
static HWND16 g_parent;
static unsigned g_parent_calls;
static HWND16 g_lookup_window;
static Win16FarPtr g_window_object;
static Win16FarPtr g_child_object;
static Win16FarPtr g_called_proc;
static Win16FarPtr g_called_object;
static ObjectWindowsMessage *g_called_message;
static uint16_t g_window_word;
HWND16 GetDlgItem16(HWND16 dialog, INT16 control_id)
{
assert(dialog == 0x77);
(void)control_id;
return g_dialog_item;
}
LRESULT16 SendMessage16(
HWND16 window, UINT16 message, WPARAM16 wparam, LPARAM16 lparam)
{
assert(window == g_dialog_item && message == 0x87);
assert(wparam == 0 && lparam == 0);
return g_dlg_code;
}
HWND16 GetFocus16(void)
{
return g_focus;
}
HWND16 GetParent16(HWND16 window)
{
assert(window == g_focus);
g_parent_calls++;
return g_parent;
}
BOOL16 IsWindow16(HWND16 window)
{
g_lookup_window = window;
return window != 0;
}
int32_t GetWindowLong16(HWND16 window, INT16 index)
{
assert(window != 0 && index == -4);
return (int32_t)win16_make_far_pointer(0x6000, 0x0100);
}
Win16Handle GetProp16(HWND16 window, Win16FarPtr name)
{
Win16FarPtr object = window == 0x55 ? g_child_object : 0;
return win16_far_offset(name) == 0x067a
? win16_far_selector(object)
: win16_far_offset(object);
}
uint16_t GetWindowWord16(HWND16 window, INT16 index)
{
assert(window != 0 && index == -12);
return g_window_word;
}
void win16_call_object_event_handler(
Win16FarPtr procedure, Win16FarPtr object, Win16FarPtr event)
{
g_called_proc = procedure;
g_called_object = object;
g_called_message = (ObjectWindowsMessage *)(uintptr_t)event;
}
void win16_call_object_message_handler(
Win16FarPtr procedure,
Win16FarPtr object,
ObjectWindowsMessage *message)
{
g_called_proc = procedure;
g_called_object = object;
g_called_message = message;
}
void win16_call_object_message_hook(
Win16FarPtr procedure,
Win16FarPtr object,
Win16FarPtr handler,
HWND16 window,
UINT16 message,
WPARAM16 wparam,
LPARAM16 lparam)
{
(void)procedure;
(void)object;
(void)handler;
(void)window;
(void)message;
(void)wparam;
(void)lparam;
assert(false);
}
static void write_far(uint16_t offset, Win16FarPtr pointer)
{
win16_write_u16(
win16_dgroup_pointer(offset), 0, win16_far_offset(pointer));
win16_write_u16(
win16_dgroup_pointer((uint16_t)(offset + 2)),
0,
win16_far_selector(pointer));
}
static void write_dispatch_table(
uint16_t vmt,
uint16_t table,
uint16_t id,
Win16FarPtr handler)
{
win16_write_u16(win16_dgroup_pointer((uint16_t)(vmt + 4)), 0, table);
win16_write_u16(win16_dgroup_pointer(table), 0, 0);
win16_write_u16(win16_dgroup_pointer((uint16_t)(table + 2)), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer((uint16_t)(table + 4)), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer((uint16_t)(table + 6)), 0, 1);
win16_write_u16(win16_dgroup_pointer((uint16_t)(table + 8)), 0, id);
write_far((uint16_t)(table + 10), handler);
}
static void reset_call(void)
{
g_called_proc = 0;
g_called_object = 0;
g_called_message = 0;
g_dialog_item = 0;
g_dlg_code = 0;
g_focus = 0;
g_parent = 0;
g_parent_calls = 0;
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t code[0x200];
static uint8_t stack[0x100];
static uint8_t window_bytes[64];
static uint8_t child_bytes[64];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(0x6000, code, sizeof(code), false);
win16_bind_segment(0x7000, window_bytes, sizeof(window_bytes), true);
win16_bind_segment(0x7100, child_bytes, sizeof(child_bytes), true);
win16_bind_segment(0x7200, stack, sizeof(stack), true);
g_window_object = win16_make_far_pointer(0x7000, 2);
g_child_object = win16_make_far_pointer(0x7100, 2);
uint16_t window_vmt = 0x0100;
uint16_t child_vmt = 0x0200;
win16_write_u16(g_window_object, 0, window_vmt);
win16_write_u16(g_child_object, 0, child_vmt);
win16_write_u16(g_window_object, 4, 0x77);
Win16FarPtr menu_handler = win16_make_far_pointer(0x6100, 0x1000);
Win16FarPtr control_handler = win16_make_far_pointer(0x6100, 0x2000);
Win16FarPtr notify_handler = win16_make_far_pointer(0x6100, 0x3000);
Win16FarPtr default_handler = win16_make_far_pointer(0x6100, 0x4000);
Win16FarPtr default_control = win16_make_far_pointer(0x6100, 0x5000);
write_dispatch_table(window_vmt, 0x0300, 0x8005, control_handler);
write_dispatch_table(child_vmt, 0x0340, 0xa010, menu_handler);
/* Parent table supplies the notification id as a second node. */
win16_write_u16(win16_dgroup_pointer(0x0340), 0, 0x0380);
win16_write_u16(win16_dgroup_pointer(0x0380), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x0382), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(0x0384), 0, 0xffff);
win16_write_u16(win16_dgroup_pointer(0x0386), 0, 1);
win16_write_u16(win16_dgroup_pointer(0x0388), 0, 0x9002);
write_far(0x038a, notify_handler);
write_far((uint16_t)(window_vmt + 0x0c), default_handler);
write_far((uint16_t)(window_vmt + 0x14), default_control);
write_far((uint16_t)(window_vmt + 0x10), default_handler);
write_far((uint16_t)(child_vmt + 0x10), default_handler);
write_far((uint16_t)(child_vmt + 0x18), default_handler);
write_far((uint16_t)(child_vmt + 0x14), default_control);
ObjectWindowsMessage *message = (ObjectWindowsMessage *)&stack[16];
Win16FarPtr message_far = win16_make_far_pointer(0x7200, 16);
(void)message_far;
reset_call();
*message = (ObjectWindowsMessage){.wparam = 0x0010, .lparam = 0};
g_focus = 0x66;
g_parent = 0x55;
object_windows_wm_command(g_window_object, message);
assert(g_parent_calls == 1 && g_lookup_window == 0x55);
assert(g_called_proc == menu_handler && g_called_object == g_child_object);
reset_call();
*message = (ObjectWindowsMessage){.wparam = 0x6000, .lparam = 0};
object_windows_wm_command(g_window_object, message);
assert(g_called_proc == default_handler &&
g_called_object == g_window_object);
reset_call();
*message = (ObjectWindowsMessage){
.wparam = 0x0100,
.lparam = (LPARAM16)0x00020055u,
};
g_dialog_item = 0x55;
object_windows_wm_command(g_window_object, message);
assert(g_called_proc == notify_handler && g_called_object == g_child_object);
reset_call();
*message = (ObjectWindowsMessage){.wparam = 5, .lparam = 0x66};
object_windows_wm_command(g_window_object, message);
assert(g_called_proc == control_handler &&
g_called_object == g_window_object);
reset_call();
*message = (ObjectWindowsMessage){.wparam = 0x1000, .lparam = 0x66};
object_windows_wm_command(g_window_object, message);
assert(g_called_proc == default_control &&
g_called_object == g_window_object);
reset_call();
win16_write_u8(win16_far_add_offset(g_window_object, 0x16), 1);
*message = (ObjectWindowsMessage){.wparam = 0x0100, .lparam = 0};
g_dialog_item = 0x55;
g_dlg_code = 0x30;
object_windows_wm_command(g_window_object, message);
assert((uint16_t)message->lparam == 0x55);
assert(g_called_proc == default_handler);
assert(g_called_object == g_child_object);
reset_call();
*message = (ObjectWindowsMessage){.wparam = 2, .lparam = 0};
object_windows_route_scroll_notification(g_window_object, message);
assert(g_called_proc == default_handler);
reset_call();
*message = (ObjectWindowsMessage){
.wparam = 2,
.lparam = (LPARAM16)0x00550000u,
};
object_windows_route_scroll_notification(g_window_object, message);
assert(g_called_proc == notify_handler && g_called_object == g_child_object);
reset_call();
*message = (ObjectWindowsMessage){
.wparam = 2,
.lparam = (LPARAM16)0x00660000u,
};
g_window_word = 5;
object_windows_route_scroll_notification(g_window_object, message);
assert(g_called_proc == control_handler &&
g_called_object == g_window_object);
reset_call();
*message = (ObjectWindowsMessage){
.wparam = 2,
.lparam = (LPARAM16)0x00660000u,
};
g_window_word = 0x1000;
object_windows_route_scroll_notification(g_window_object, message);
assert(g_called_proc == default_control &&
g_called_object == g_window_object);
reset_call();
*message = (ObjectWindowsMessage){
.window = 0x77,
.wparam = 0x0010,
};
object_windows_route_command_target(g_window_object, message);
assert(g_called_proc == default_handler &&
g_called_object == g_window_object);
win16_write_u16(g_window_object, 6, win16_far_offset(g_child_object));
win16_write_u16(g_window_object, 8, win16_far_selector(g_child_object));
message->window = 0x66;
object_windows_route_command_target(g_window_object, message);
assert(g_called_proc == menu_handler && g_called_object == g_child_object);
win16_write_u16(g_window_object, 6, 0);
win16_write_u16(g_window_object, 8, 0);
message->window = 0x55;
object_windows_route_command_target(g_window_object, message);
assert(g_called_proc == menu_handler && g_called_object == g_child_object);
reset_call();
*message = (ObjectWindowsMessage){.message = 0x0111, .wparam = 5};
object_windows_forward_event_to_parent(g_window_object, message);
assert(g_called_proc == 0);
win16_write_u16(g_window_object, 6, win16_far_offset(g_child_object));
win16_write_u16(g_window_object, 8, win16_far_selector(g_child_object));
object_windows_forward_event_to_parent(g_window_object, message);
assert(g_called_proc == default_control &&
g_called_object == g_child_object);
reset_call();
*message = (ObjectWindowsMessage){.message = 2};
g_window_word = 5;
object_windows_forward_event_to_parent(g_window_object, message);
assert(g_called_proc == default_control &&
g_called_object == g_child_object);
return 0;
}
@@ -0,0 +1,289 @@
#include "../tdkpin_window_placement.h"
#include <assert.h>
#include <stdint.h>
HINSTANCE16 g_win16_instance;
Win16FarPtr g_object_windows_creating_object;
static bool g_enter = true;
static Win16FarPtr g_parent_mdi_client;
static Win16FarPtr g_duplicate_result;
static uint8_t g_flags;
static bool g_register_result;
static Win16FarPtr g_class_name;
static HWND16 g_create_result;
static HWND16 g_dialog_item_result;
static Win16FarPtr g_existing_object;
static int32_t g_previous_wndproc;
static unsigned g_bind_calls;
static unsigned g_created_calls;
static unsigned g_create_ex_calls;
static unsigned g_mdi_calls;
bool borland_enter_object_constructor(
BorlandObjectCallFrame *frame, uint16_t offset)
{
assert(offset == 0);
if (g_enter && frame->vmt_argument != 0) {
win16_write_u16(frame->object, 0, frame->vmt_argument);
}
return g_enter;
}
Win16FarPtr object_windows_object_construct(
Win16FarPtr object, uint16_t vmt, Win16FarPtr parent)
{
assert(vmt == 0);
win16_write_u16(object, 6, win16_far_offset(parent));
win16_write_u16(object, 8, win16_far_selector(parent));
return object;
}
Win16FarPtr borland_far_strdup(Win16FarPtr source)
{
assert(source != 0);
return g_duplicate_result;
}
Win16FarPtr win16_def_window_proc_pointer(void)
{
return win16_make_far_pointer(0x6000, 0x0100);
}
Win16FarPtr win16_def_mdi_child_proc_pointer(void)
{
return win16_make_far_pointer(0x6000, 0x0200);
}
void object_windows_update_flags(
Win16FarPtr object, bool set, uint8_t mask)
{
assert(set && mask == 8);
g_flags |= mask;
win16_write_u8(win16_far_add_offset(object, 0x16), g_flags);
}
Win16FarPtr win16_call_object_far_method(
Win16FarPtr procedure, Win16FarPtr object)
{
(void)procedure;
if (win16_far_offset(object) == 0x0300) {
return g_parent_mdi_client;
}
return g_class_name;
}
void object_windows_clear_flag_4(Win16FarPtr object)
{
g_flags &= (uint8_t)~4u;
win16_write_u8(win16_far_add_offset(object, 0x16), g_flags);
}
bool object_windows_has_flags(Win16FarPtr object, uint8_t mask)
{
(void)object;
return (g_flags & mask) == mask;
}
bool win16_call_object_bool_method(
Win16FarPtr procedure, Win16FarPtr object)
{
(void)procedure;
(void)object;
return g_register_result;
}
HWND16 GetDlgItem16(HWND16 dialog, INT16 control_id)
{
assert(dialog == 0x3333 && control_id == 77);
return g_dialog_item_result;
}
HWND16 CreateWindowEx16(
uint32_t extended_style,
Win16FarPtr class_name,
Win16FarPtr window_name,
uint32_t style,
INT16 x,
INT16 y,
INT16 width,
INT16 height,
HWND16 parent,
HMENU16 menu,
HINSTANCE16 instance,
Win16FarPtr parameter)
{
assert(extended_style == 0x11223344u);
assert(class_name == g_class_name);
assert(window_name == g_duplicate_result);
assert(style == 0x55667788u);
assert(x == 10 && y == 20 && width == 30 && height == 40);
assert(parent == 0x3333 && menu == 77 && instance == g_win16_instance);
assert(parameter == win16_make_far_pointer(0x5000, 0x0550));
g_create_ex_calls++;
return g_create_result;
}
LRESULT16 SendMessage16(
HWND16 window, UINT16 message, WPARAM16 wparam, LPARAM16 lparam)
{
assert(window == 0x4444 && message == 0x0220 && wparam == 0);
Win16FarPtr pointer = (Win16FarPtr)lparam;
assert(win16_read_far_pointer(pointer, 0) == g_class_name);
assert(win16_read_far_pointer(pointer, 4) == g_duplicate_result);
assert(win16_read_u16(win16_far_add_offset(pointer, 8)) == g_win16_instance);
assert((INT16)win16_read_u16(win16_far_add_offset(pointer, 10)) == 10);
assert(win16_read_u32(win16_far_add_offset(pointer, 18)) == 0x55667788u);
assert(win16_read_u32(win16_far_add_offset(pointer, 22)) ==
win16_make_far_pointer(0x5000, 0x0550));
g_mdi_calls++;
return g_create_result;
}
Win16FarPtr object_windows_object_from_window(HWND16 window)
{
assert(window != 0);
return g_existing_object;
}
void object_windows_bind_window(HWND16 window, Win16FarPtr object)
{
assert(window != 0 && object != 0);
g_bind_calls++;
}
int32_t SetWindowLong16(HWND16 window, INT16 index, int32_t value)
{
assert(window != 0 && index == -4);
assert((Win16FarPtr)value == win16_make_far_pointer(0x6000, 0x0300));
return g_previous_wndproc;
}
void win16_call_object_method(Win16FarPtr procedure, Win16FarPtr object)
{
assert(procedure == win16_make_far_pointer(0x6000, 0x0600));
assert(object != 0);
g_created_calls++;
}
static void write_far_value(
Win16FarPtr base, uint16_t offset, Win16FarPtr value)
{
win16_write_u16(base, offset, win16_far_offset(value));
win16_write_u16(
base, (uint16_t)(offset + 2), win16_far_selector(value));
}
static void prepare_window(
Win16FarPtr window, Win16FarPtr parent)
{
win16_write_u16(window, 0, 0x0100);
win16_write_u16(window, 2, 0);
win16_write_u16(window, 4, 0);
write_far_value(window, 6, parent);
write_far_value(window, 0x1d, g_duplicate_result);
win16_write_u32(window, 0x21, 0x55667788u);
win16_write_u32(window, 0x25, 0x11223344u);
win16_write_u16(window, 0x29, 10);
win16_write_u16(window, 0x2b, 20);
win16_write_u16(window, 0x2d, 30);
win16_write_u16(window, 0x2f, 40);
write_far_value(window, 0x31, win16_make_far_pointer(0x5000, 0x0550));
win16_write_u16(window, 0x35, 77);
write_far_value(window, 0x12, win16_make_far_pointer(0x6000, 0x0300));
g_flags = 0;
}
int main(void)
{
static uint8_t dgroup[0x1000];
uint8_t stack_anchor = 0;
uintptr_t stack_base = (uintptr_t)&stack_anchor - 0x8000;
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(0x7000, (void *)stack_base, 0x10000, true);
Win16FarPtr window = win16_make_far_pointer(0x5000, 0x0200);
Win16FarPtr parent = win16_make_far_pointer(0x5000, 0x0300);
Win16FarPtr mdi_client = win16_make_far_pointer(0x5000, 0x0400);
g_duplicate_result = win16_make_far_pointer(0x5000, 0x0500);
g_class_name = win16_make_far_pointer(0x5000, 0x0520);
win16_write_u16(parent, 0, 0x0700);
win16_write_u16(parent, 4, 0x3333);
win16_write_u16(mdi_client, 4, 0x4444);
write_far_value(win16_dgroup_pointer(0x0700), 0x30,
win16_make_far_pointer(0x6000, 0x0700));
write_far_value(win16_dgroup_pointer(0x0100), 0x1c,
win16_make_far_pointer(0x6000, 0x0400));
write_far_value(win16_dgroup_pointer(0x0100), 0x2c,
win16_make_far_pointer(0x6000, 0x0500));
write_far_value(win16_dgroup_pointer(0x0100), 0x38,
win16_make_far_pointer(0x6000, 0x0600));
g_parent_mdi_client = 0;
(void)object_windows_window_construct(
window, 0x0100, win16_make_far_pointer(0x5000, 0x0540), 0);
assert(win16_read_u32(win16_far_add_offset(window, 0x21)) == 0x00cf0000u);
assert(win16_read_far_pointer(window, 0x37) ==
win16_make_far_pointer(0x6000, 0x0100));
assert(win16_read_u16(win16_far_add_offset(window, 0x29)) == 0x8000);
g_parent_mdi_client = 0;
(void)object_windows_window_construct(
window, 0, win16_make_far_pointer(0x5000, 0x0540), parent);
assert(win16_read_u32(win16_far_add_offset(window, 0x21)) == 0x10000000u);
g_parent_mdi_client = mdi_client;
(void)object_windows_window_construct(
window, 0, win16_make_far_pointer(0x5000, 0x0540), parent);
assert(win16_read_u32(win16_far_add_offset(window, 0x21)) == 0x04000000u);
assert(win16_read_far_pointer(window, 0x37) ==
win16_make_far_pointer(0x6000, 0x0200));
assert((g_flags & 8) != 0);
g_win16_instance = 0x7777;
g_register_result = true;
g_existing_object = 0;
g_previous_wndproc = (int32_t)win16_make_far_pointer(0x6000, 0x0800);
prepare_window(window, parent);
g_create_result = 0x8888;
g_create_ex_calls = 0;
g_bind_calls = 0;
g_created_calls = 0;
win16_set_indeterminate_u16(0);
assert(object_windows_create_native_window(window) == 1);
assert(g_create_ex_calls == 1 && g_bind_calls == 1 && g_created_calls == 1);
assert(g_object_windows_creating_object == window);
assert(win16_read_far_pointer(window, 0x37) ==
(Win16FarPtr)g_previous_wndproc);
prepare_window(window, parent);
g_flags = 2;
g_dialog_item_result = 0x9999;
g_existing_object = window;
assert(object_windows_create_native_window(window) == 1);
assert(win16_read_u16(win16_far_add_offset(window, 4)) == 0x9999);
prepare_window(window, parent);
g_flags = 8;
g_parent_mdi_client = mdi_client;
g_create_result = 0xaaaa;
g_existing_object = window;
g_mdi_calls = 0;
assert(object_windows_create_native_window(window) == 1);
assert(g_mdi_calls == 1);
prepare_window(window, parent);
g_create_result = 0;
g_existing_object = 0;
assert(object_windows_create_native_window(window) == 0);
assert(win16_read_u16(win16_far_add_offset(window, 2)) == 0xffff);
prepare_window(window, parent);
g_register_result = false;
g_existing_object = window;
win16_set_indeterminate_u16(0xabcd);
assert(object_windows_create_native_window(window) == 0xab01);
return 0;
}
@@ -0,0 +1,132 @@
#include "../tdkpin_window_messages.h"
#include "../tdkpin_object_list.h"
#include <assert.h>
Win16FarPtr g_object_windows_application;
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
uint16_t g_win16_show_command;
Win16FarPtr g_win16_message_box;
Win16FarPtr g_object_windows_dispatch_proc;
uint16_t g_object_windows_thunk_code_selector;
Win16FarPtr g_object_windows_free_thunk;
Win16FarPtr g_object_windows_message_hook;
Win16FarPtr g_object_windows_creating_object;
static Win16FarPtr g_client_result;
static Win16FarPtr g_client_getter;
static unsigned g_getter_calls;
static HWND16 g_destroyed_window;
static HWND16 g_sent_window;
static UINT16 g_sent_message;
static WPARAM16 g_sent_wparam;
static LPARAM16 g_sent_lparam;
void win16_call_object_action(
Win16FarPtr procedure, Win16FarPtr child)
{
assert(procedure == win16_make_far_pointer(0x6000, 0x0cc7));
object_windows_set_flag_4_if_windowed(0xffff, child);
}
Win16FarPtr win16_call_object_far_method(
Win16FarPtr procedure, Win16FarPtr object)
{
(void)object;
assert(procedure == g_client_getter);
g_getter_calls++;
return g_client_result;
}
BOOL16 DestroyWindow16(HWND16 window)
{
g_destroyed_window = window;
return 1;
}
LRESULT16 SendMessage16(
HWND16 window, UINT16 message, WPARAM16 wparam, LPARAM16 lparam)
{
g_sent_window = window;
g_sent_message = message;
g_sent_wparam = wparam;
g_sent_lparam = lparam;
return 0;
}
static void write_far(uint16_t offset, Win16FarPtr pointer)
{
win16_write_u16(
win16_dgroup_pointer(offset), 0, win16_far_offset(pointer));
win16_write_u16(
win16_dgroup_pointer((uint16_t)(offset + 2)),
0,
win16_far_selector(pointer));
}
static void reset_calls(void)
{
g_getter_calls = 0;
g_destroyed_window = 0;
g_sent_window = 0;
g_sent_message = 0;
g_sent_wparam = 0;
g_sent_lparam = 0;
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t code[0x1000];
static uint8_t window_bytes[64];
static uint8_t child_bytes[64];
static uint8_t parent_bytes[64];
static uint8_t client_bytes[64];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_ne_segment(4, 0x6000, code, sizeof(code), false);
win16_bind_segment(0x7000, window_bytes, sizeof(window_bytes), true);
win16_bind_segment(0x7100, child_bytes, sizeof(child_bytes), true);
win16_bind_segment(0x7200, parent_bytes, sizeof(parent_bytes), true);
win16_bind_segment(0x7300, client_bytes, sizeof(client_bytes), true);
Win16FarPtr window = win16_make_far_pointer(0x7000, 2);
Win16FarPtr child = win16_make_far_pointer(0x7100, 2);
Win16FarPtr parent = win16_make_far_pointer(0x7200, 2);
Win16FarPtr client = win16_make_far_pointer(0x7300, 2);
win16_write_u16(window, 4, 0x77);
win16_write_u16(child, 4, 0x88);
win16_write_u16(client, 4, 0x99);
win16_write_u16(window, 6, win16_far_offset(parent));
win16_write_u16(window, 8, win16_far_selector(parent));
object_windows_append_child(window, child);
win16_write_u16(parent, 0, 0x0100);
g_client_getter = win16_make_far_pointer(0x6100, 0x0200);
write_far(0x0130, g_client_getter);
reset_calls();
object_windows_destroy_native_window(window);
assert(g_destroyed_window == 0x77);
assert(object_windows_has_flags(child, 4));
object_windows_update_flags(window, true, 8);
reset_calls();
g_client_result = 0;
object_windows_destroy_native_window(window);
assert(g_getter_calls == 1 && g_destroyed_window == 0x77);
reset_calls();
g_client_result = client;
object_windows_destroy_native_window(window);
assert(g_getter_calls == 2 && g_destroyed_window == 0);
assert(g_sent_window == 0x99 && g_sent_message == 0x221);
assert(g_sent_wparam == 0x77 && g_sent_lparam == 0);
win16_write_u16(window, 4, 0);
reset_calls();
object_windows_destroy_native_window(window);
assert(g_getter_calls == 0 && g_destroyed_window == 0);
return 0;
}
@@ -0,0 +1,89 @@
#include "../tdkpin_window_lifecycle.h"
#include <assert.h>
static uint8_t g_dgroup[0x1000];
static uint8_t g_objects[0x400];
static Win16FarPtr g_window;
static Win16FarPtr g_peer;
static Win16FarPtr g_attached;
static Win16FarPtr g_title;
static unsigned g_step;
static bool g_board_mode;
BOOL16 DeleteObject16(HGDIOBJ16 object)
{
assert(++g_step == 1 && object == 0x7777);
if (g_board_mode) {
assert(win16_read_far_pointer(g_window, 0x41) == 0);
assert(win16_read_far_pointer(g_peer, 0x45) == 0);
} else {
assert(win16_read_far_pointer(g_window, 0x41) == g_peer);
assert(win16_read_far_pointer(g_peer, 0x41) == g_window);
}
return 1;
}
void borland_far_strdispose(Win16FarPtr string)
{
assert(++g_step == 2 && string == g_title);
}
void borland_dispose_object(Win16FarPtr object)
{
assert(++g_step == 3 && object == g_attached);
}
void object_windows_object_destruct(
Win16FarPtr object, uint16_t vmt)
{
assert(++g_step == 4 && object == g_window && vmt == 0);
}
void borland_finish_object_destructor(
BorlandObjectCallFrame *frame, uint16_t vmt_field_offset)
{
assert(frame->object == g_window && vmt_field_offset == 0);
if (++g_step == 5) {
assert(frame->vmt_argument == 0);
} else {
assert(g_step == 6 && frame->vmt_argument == 0x9999);
}
}
int main(void)
{
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_objects, sizeof(g_objects), true);
win16_set_dgroup_selector(0x5000);
g_window = win16_make_far_pointer(0x6000, 0);
g_peer = win16_make_far_pointer(0x6000, 0x100);
g_attached = win16_make_far_pointer(0x6000, 0x200);
g_title = win16_make_far_pointer(0x6000, 0x300);
win16_write_u16(win16_make_far_pointer(0x5000, 0x0867), 0, 0x7777);
win16_write_u32(g_window, 0x1d, g_title);
win16_write_u32(g_window, 0x3b, g_attached);
win16_write_u32(g_window, 0x41, g_peer);
win16_write_u32(g_peer, 0x41, g_window);
g_board_mode = false;
tdkpin_overlay_window_destruct(g_window, 0x9999);
assert(g_step == 6);
assert(win16_read_far_pointer(g_window, 0x3b) == 0);
assert(win16_read_far_pointer(g_window, 0x41) == 0);
assert(win16_read_far_pointer(g_peer, 0x41) == 0);
g_step = 0;
g_board_mode = true;
win16_write_u32(g_window, 0x1d, g_title);
win16_write_u32(g_window, 0x3b, g_attached);
win16_write_u32(g_window, 0x41, g_peer);
win16_write_u32(g_peer, 0x45, g_window);
tdkpin_board_window_destruct(g_window, 0x9999);
assert(g_step == 6);
assert(win16_read_far_pointer(g_window, 0x3b) == 0);
assert(win16_read_far_pointer(g_window, 0x41) == 0);
assert(win16_read_far_pointer(g_peer, 0x45) == 0);
return 0;
}
@@ -0,0 +1,365 @@
#include "../tdkpin_window_messages.h"
#include <assert.h>
Win16FarPtr g_object_windows_application;
HINSTANCE16 g_win16_previous_instance;
HINSTANCE16 g_win16_instance;
uint16_t g_win16_show_command;
Win16FarPtr g_win16_message_box;
Win16FarPtr g_object_windows_dispatch_proc;
uint16_t g_object_windows_thunk_code_selector;
Win16FarPtr g_object_windows_free_thunk;
Win16FarPtr g_object_windows_message_hook;
Win16FarPtr g_object_windows_creating_object;
static bool g_bool_result;
static Win16FarPtr g_bool_proc;
static Win16FarPtr g_disposed;
static Win16FarPtr g_event_proc;
static Win16FarPtr g_event_object;
static unsigned g_event_calls;
static uint16_t g_quit_code;
static uint16_t g_removed_property_offsets[2];
static unsigned g_remove_calls;
static int32_t g_window_style;
static HWND16 g_focus_window;
static HWND16 g_is_child_parent;
static HWND16 g_is_child_child;
static BOOL16 g_is_child_result;
static Win16FarPtr g_void_proc;
static Win16FarPtr g_original_proc;
static HWND16 g_original_window;
static UINT16 g_original_message;
static WPARAM16 g_original_wparam;
static LPARAM16 g_original_lparam;
static BOOL16 g_is_window_result;
static BOOL16 g_is_iconic_result;
static HWND16 g_set_focus_window;
bool win16_call_object_bool_method(
Win16FarPtr procedure, Win16FarPtr object)
{
(void)object;
g_bool_proc = procedure;
return g_bool_result;
}
void borland_dispose_object(Win16FarPtr object)
{
g_disposed = object;
}
void win16_call_object_message_handler(
Win16FarPtr procedure,
Win16FarPtr object,
ObjectWindowsMessage *message)
{
(void)message;
g_event_proc = procedure;
g_event_object = object;
g_event_calls++;
}
void PostQuitMessage16(INT16 exit_code)
{
g_quit_code = (uint16_t)exit_code;
}
Win16Handle RemoveProp16(HWND16 window, Win16FarPtr name)
{
assert(window == 0x77 && g_remove_calls < 2);
g_removed_property_offsets[g_remove_calls++] = win16_far_offset(name);
return 0;
}
int32_t GetWindowLong16(HWND16 window, INT16 index)
{
assert(window == 0x77 && index == -16);
return g_window_style;
}
HWND16 GetFocus16(void)
{
return g_focus_window;
}
BOOL16 IsChild16(HWND16 parent, HWND16 child)
{
g_is_child_parent = parent;
g_is_child_child = child;
return g_is_child_result;
}
BOOL16 IsWindow16(HWND16 window)
{
assert(window != 0);
return g_is_window_result;
}
BOOL16 IsIconic16(HWND16 window)
{
assert(window == 0x77);
return g_is_iconic_result;
}
HWND16 SetFocus16(HWND16 window)
{
g_set_focus_window = window;
return 0;
}
void win16_call_object_method(
Win16FarPtr procedure, Win16FarPtr object)
{
assert(object != 0);
g_void_proc = procedure;
}
LRESULT16 CallWindowProc16(
Win16FarPtr procedure,
HWND16 window,
UINT16 message,
WPARAM16 wparam,
LPARAM16 lparam)
{
g_original_proc = procedure;
g_original_window = window;
g_original_message = message;
g_original_wparam = wparam;
g_original_lparam = lparam;
return (LRESULT16)0x12345678u;
}
static void write_far(uint16_t offset, Win16FarPtr pointer)
{
win16_write_u16(
win16_dgroup_pointer(offset), 0, win16_far_offset(pointer));
win16_write_u16(
win16_dgroup_pointer((uint16_t)(offset + 2)),
0,
win16_far_selector(pointer));
}
int main(void)
{
static uint8_t dgroup[0x1000];
static uint8_t application_bytes[64];
static uint8_t main_bytes[80];
static uint8_t other_bytes[80];
win16_reset_segment_bindings();
win16_bind_segment(0x5000, dgroup, sizeof(dgroup), true);
win16_set_dgroup_selector(0x5000);
win16_bind_segment(
0x7000, application_bytes, sizeof(application_bytes), true);
win16_bind_segment(0x7100, main_bytes, sizeof(main_bytes), true);
win16_bind_segment(0x7200, other_bytes, sizeof(other_bytes), true);
g_object_windows_application = win16_make_far_pointer(0x7000, 2);
Win16FarPtr main_window = win16_make_far_pointer(0x7100, 2);
Win16FarPtr other_window = win16_make_far_pointer(0x7200, 2);
uint16_t app_vmt = 0x0100;
uint16_t window_vmt = 0x0200;
win16_write_u16(g_object_windows_application, 0, app_vmt);
win16_write_u16(main_window, 0, window_vmt);
win16_write_u16(other_window, 0, window_vmt);
win16_write_u16(main_window, 4, 0x77);
win16_write_u16(other_window, 4, 0x77);
win16_write_u16(other_window, 2, 9);
win16_write_u8(win16_far_add_offset(other_window, 0x16), 1);
win16_write_u16(
g_object_windows_application, 8, win16_far_offset(main_window));
win16_write_u16(
g_object_windows_application, 10, win16_far_selector(main_window));
Win16FarPtr app_can_close = win16_make_far_pointer(0x6000, 0x1000);
Win16FarPtr window_can_close = win16_make_far_pointer(0x6000, 0x2000);
Win16FarPtr default_handler = win16_make_far_pointer(0x6100, 0x3000);
Win16FarPtr message_0014 = win16_make_far_pointer(0x6100, 0x4000);
Win16FarPtr scroll_handler = win16_make_far_pointer(0x6100, 0x5000);
Win16FarPtr slot_38 = win16_make_far_pointer(0x6100, 0x6000);
write_far(app_vmt + 0x44, app_can_close);
write_far(window_vmt + 0x3c, window_can_close);
write_far(window_vmt + 0x0c, default_handler);
write_far(window_vmt + 0x10, message_0014);
write_far(window_vmt + 0x48, scroll_handler);
write_far(window_vmt + 0x38, slot_38);
assert(object_windows_has_flags(other_window, 1));
assert(!object_windows_has_flags(other_window, 3));
assert(object_windows_zero_word_default(other_window) == 0);
object_windows_set_flag_4(other_window);
assert(object_windows_has_flags(other_window, 5));
object_windows_clear_flag_4(other_window);
assert(object_windows_has_flags(other_window, 1));
object_windows_set_flag_1(other_window);
assert(object_windows_has_flags(other_window, 1));
object_windows_update_flags(other_window, false, 1);
assert(!object_windows_has_flags(other_window, 1));
object_windows_set_flag_1(other_window);
ObjectWindowsMessage message = {
.window = 0x77,
.message = 0,
.wparam = 1,
.lparam = 0,
.result = 0,
};
g_bool_result = false;
g_disposed = 0;
object_windows_wm_close(main_window, &message);
assert(g_bool_proc == app_can_close && g_disposed == 0);
g_bool_result = true;
object_windows_wm_close(other_window, &message);
assert(g_bool_proc == window_can_close && g_disposed == other_window);
g_event_calls = 0;
g_quit_code = 0;
object_windows_wm_destroy(main_window, &message);
assert(g_quit_code == 0x77);
assert(g_event_calls == 1 && g_event_proc == default_handler);
g_quit_code = 0;
object_windows_wm_destroy(other_window, &message);
assert(g_quit_code == 0);
g_remove_calls = 0;
object_windows_wm_nc_destroy(other_window, &message);
assert(g_remove_calls == 2);
assert(g_removed_property_offsets[0] == 0x067a);
assert(g_removed_property_offsets[1] == 0x067e);
assert(win16_read_u16(win16_far_add_offset(other_window, 4)) == 0);
win16_write_u16(other_window, 4, 0x77);
message.wparam = 1;
object_windows_wm_activate(other_window, &message);
assert(win16_read_far_pointer(g_object_windows_application, 0x0e) ==
other_window);
message.wparam = 0;
object_windows_wm_activate(other_window, &message);
assert(win16_read_far_pointer(g_object_windows_application, 0x0e) ==
other_window);
win16_write_u8(win16_far_add_offset(other_window, 0x16), 0);
message.wparam = 1;
object_windows_wm_activate(other_window, &message);
assert(win16_read_far_pointer(g_object_windows_application, 0x0e) == 0);
g_bool_result = true;
object_windows_wm_query_end_session(main_window, &message);
assert(message.result == 1 && g_bool_proc == app_can_close);
g_bool_result = false;
object_windows_wm_query_end_session(other_window, &message);
assert(message.result == 0 && g_bool_proc == window_can_close);
g_bool_result = false;
g_event_calls = 0;
object_windows_message_0014(main_window, &message);
assert(g_bool_proc == app_can_close && g_event_calls == 0);
object_windows_message_0014(other_window, &message);
assert(g_event_calls == 1 && g_event_proc == message_0014);
assert(g_event_object == other_window);
g_window_style = 0;
object_windows_wm_hscroll(other_window, &message);
assert(g_event_proc == scroll_handler);
object_windows_wm_vscroll(other_window, &message);
assert(g_event_proc == scroll_handler);
g_window_style = (int32_t)0x00100000u;
object_windows_wm_hscroll(other_window, &message);
assert(g_event_proc == default_handler);
g_window_style = (int32_t)0x00200000u;
object_windows_wm_vscroll(other_window, &message);
assert(g_event_proc == default_handler);
assert(object_windows_turbo_window_class_name(other_window) ==
win16_dgroup_pointer(0x068c));
g_event_calls = 0;
object_windows_forward_default(other_window, &message);
assert(g_event_calls == 1 && g_event_proc == default_handler);
win16_write_u8(win16_far_add_offset(other_window, 0x16), 0);
win16_write_u16(other_window, 4, 0);
object_windows_set_flag_4_if_windowed(0xbeef, other_window);
assert(!object_windows_has_flags(other_window, 4));
win16_write_u16(other_window, 4, 0x77);
object_windows_set_flag_4_if_windowed(0xbeef, other_window);
assert(object_windows_has_flags(other_window, 4));
win16_write_u16(other_window, 0x35, 0xbeef);
assert(object_windows_get_field_35(other_window) == 0xbeef);
g_focus_window = 0;
win16_write_u16(other_window, 0x3f, 0);
object_windows_remember_child_focus(other_window);
assert(win16_read_u16(win16_far_add_offset(other_window, 0x3f)) == 0);
g_focus_window = 0x99;
g_is_child_result = 0;
object_windows_remember_child_focus(other_window);
assert(g_is_child_parent == 0x77 && g_is_child_child == 0x99);
assert(win16_read_u16(win16_far_add_offset(other_window, 0x3f)) == 0);
g_is_child_result = 1;
object_windows_remember_child_focus(other_window);
assert(win16_read_u16(win16_far_add_offset(other_window, 0x3f)) == 0x99);
g_void_proc = 0;
g_event_calls = 0;
object_windows_call_slot_38_then_default(other_window, &message);
assert(g_void_proc == slot_38 && g_event_calls == 1);
g_focus_window = 0x88;
g_is_child_result = 1;
g_event_calls = 0;
object_windows_remember_focus_then_default(other_window, &message);
assert(win16_read_u16(win16_far_add_offset(other_window, 0x3f)) == 0x88);
assert(g_event_calls == 1 && g_event_proc == default_handler);
Win16FarPtr original_proc = win16_make_far_pointer(0x6200, 0x7000);
win16_write_u16(other_window, 0x37, win16_far_offset(original_proc));
win16_write_u16(other_window, 0x39, win16_far_selector(original_proc));
message.message = 0x123;
message.wparam = 0x4567;
message.lparam = (LPARAM16)0x89abcdefu;
object_windows_call_original_window_proc(other_window, &message);
assert(g_original_proc == original_proc && g_original_window == 0x77);
assert(g_original_message == 0x123 && g_original_wparam == 0x4567);
assert(g_original_lparam == (LPARAM16)0x89abcdefu);
assert(message.result == (LRESULT16)0x12345678u);
g_void_proc = 0;
object_windows_call_slot_38(other_window, &message);
assert(g_void_proc == slot_38);
message.result = (LRESULT16)0x76543210u;
object_windows_zero_message_result(other_window, &message);
assert(message.result == 0);
win16_write_u16(other_window, 0x3f, 0x99);
g_is_window_result = 0;
g_set_focus_window = 0;
object_windows_restore_child_focus(other_window);
assert(g_set_focus_window == 0);
g_is_window_result = 1;
g_is_iconic_result = 1;
object_windows_restore_child_focus(other_window);
assert(g_set_focus_window == 0);
g_is_iconic_result = 0;
object_windows_restore_child_focus(other_window);
assert(g_set_focus_window == 0x99);
win16_write_u8(win16_far_add_offset(other_window, 0x16), 1);
message.wparam = 0;
g_focus_window = 0x88;
g_is_child_result = 1;
object_windows_wm_activate_with_focus(other_window, &message);
assert(win16_read_u16(win16_far_add_offset(other_window, 0x3f)) == 0x88);
message.wparam = 1;
g_set_focus_window = 0;
object_windows_wm_activate_with_focus(other_window, &message);
assert(g_set_focus_window == 0x88);
message.wparam = 0xf020;
g_focus_window = 0x66;
object_windows_wm_syscommand_focus(other_window, &message);
assert(win16_read_u16(win16_far_add_offset(other_window, 0x3f)) == 0x66);
message.wparam = 0xf120;
g_set_focus_window = 0;
object_windows_wm_syscommand_focus(other_window, &message);
assert(g_set_focus_window == 0x66);
message.wparam = 0x1234;
g_set_focus_window = 0;
object_windows_wm_syscommand_focus(other_window, &message);
assert(g_set_focus_window == 0);
return 0;
}
@@ -0,0 +1,267 @@
#include "../tdkpin_window_placement.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
enum {
WINDOW_HANDLE = 4,
WINDOW_PARENT = 6,
WINDOW_FLAGS = 0x16,
WINDOW_ATTRIBUTES = 0x21,
WINDOW_X = 0x29,
WINDOW_Y = 0x2b,
WINDOW_WIDTH = 0x2d,
WINDOW_HEIGHT = 0x2f,
WINDOW_ATTACHED = 0x3b,
};
static uint8_t g_dgroup[0x1000];
static uint8_t g_objects[0x1000];
static Win16FarPtr g_window;
static Win16FarPtr g_parent;
static Win16FarPtr g_client;
static Win16FarPtr g_attached;
static Win16FarPtr g_parent_getter;
static Win16FarPtr g_attached_setup;
static Win16FarPtr g_client_result;
static bool g_iconic;
static bool g_zoomed;
static RECT16 g_native_rectangle;
static unsigned g_is_iconic_calls;
static unsigned g_is_zoomed_calls;
static unsigned g_get_rect_calls;
static unsigned g_screen_to_client_calls;
static HWND16 g_screen_to_client_window;
static POINT16 g_client_delta;
static unsigned g_prepare_calls;
static unsigned g_flag_calls;
static unsigned g_focus_calls;
static HWND16 g_focused_window;
static unsigned g_parent_getter_calls;
static unsigned g_attached_setup_calls;
static Win16FarPtr object_at(uint16_t offset)
{
return win16_make_far_pointer(0x6000, offset);
}
static Win16FarPtr dgroup_at(uint16_t offset)
{
return win16_make_far_pointer(0x5000, offset);
}
static void write_far(Win16FarPtr base, uint16_t offset, Win16FarPtr value)
{
win16_write_u32(base, offset, value);
}
static void reset_fixture(void)
{
memset(g_objects, 0, sizeof(g_objects));
memset(g_dgroup, 0, sizeof(g_dgroup));
g_window = object_at(0x100);
g_parent = object_at(0x200);
g_client = object_at(0x300);
g_attached = object_at(0x400);
g_parent_getter = win16_make_far_pointer(0x7000, 0x1234);
g_attached_setup = win16_make_far_pointer(0x7000, 0x5678);
win16_write_u16(g_parent, 0, 0x0100);
win16_write_u16(g_attached, 0, 0x0200);
write_far(dgroup_at(0x0100), 0x30, g_parent_getter);
write_far(dgroup_at(0x0200), 0x10, g_attached_setup);
win16_write_u16(g_window, WINDOW_HANDLE, 0x1111);
win16_write_u16(g_parent, WINDOW_HANDLE, 0x2222);
win16_write_u16(g_client, WINDOW_HANDLE, 0x3333);
g_native_rectangle = (RECT16){10, 20, 110, 220};
g_client_delta = (POINT16){3, 5};
g_client_result = 0;
g_iconic = false;
g_zoomed = false;
g_is_iconic_calls = 0;
g_is_zoomed_calls = 0;
g_get_rect_calls = 0;
g_screen_to_client_calls = 0;
g_screen_to_client_window = 0;
g_prepare_calls = 0;
g_flag_calls = 0;
g_focus_calls = 0;
g_focused_window = 0;
g_parent_getter_calls = 0;
g_attached_setup_calls = 0;
}
BOOL16 IsIconic16(HWND16 window)
{
assert(window == 0x1111);
g_is_iconic_calls++;
return g_iconic;
}
BOOL16 IsZoomed16(HWND16 window)
{
assert(window == 0x1111);
g_is_zoomed_calls++;
return g_zoomed;
}
BOOL16 GetWindowRect16(HWND16 window, Win16FarPtr rectangle)
{
assert(window == 0x1111);
win16_write_u16(rectangle, 0, (uint16_t)g_native_rectangle.left);
win16_write_u16(rectangle, 2, (uint16_t)g_native_rectangle.top);
win16_write_u16(rectangle, 4, (uint16_t)g_native_rectangle.right);
win16_write_u16(rectangle, 6, (uint16_t)g_native_rectangle.bottom);
g_get_rect_calls++;
return 1;
}
BOOL16 ScreenToClient16(HWND16 window, Win16FarPtr point)
{
int16_t x = (int16_t)win16_read_u16(point);
int16_t y = (int16_t)win16_read_u16(win16_far_add_offset(point, 2));
win16_write_u16(point, 0, (uint16_t)(x - g_client_delta.x));
win16_write_u16(point, 2, (uint16_t)(y - g_client_delta.y));
g_screen_to_client_window = window;
g_screen_to_client_calls++;
return 1;
}
bool object_windows_has_flags(Win16FarPtr object, uint8_t mask)
{
assert(object == g_window && mask == 8);
g_flag_calls++;
return (win16_read_u8(
win16_far_add_offset(object, WINDOW_FLAGS)) & mask) == mask;
}
Win16FarPtr win16_call_object_far_method(
Win16FarPtr procedure, Win16FarPtr object)
{
assert(procedure == g_parent_getter && object == g_parent);
g_parent_getter_calls++;
return g_client_result;
}
void object_windows_prepare_window(Win16FarPtr window)
{
assert(window == g_window);
g_prepare_calls++;
}
HWND16 SetFocus16(HWND16 window)
{
g_focused_window = window;
g_focus_calls++;
return 0x9999;
}
void win16_call_object_method(
Win16FarPtr procedure, Win16FarPtr object)
{
assert(procedure == g_attached_setup && object == g_attached);
g_attached_setup_calls++;
}
static void assert_placement(
int16_t x, int16_t y, uint16_t width, uint16_t height)
{
assert((int16_t)win16_read_u16(
win16_far_add_offset(g_window, WINDOW_X)) == x);
assert((int16_t)win16_read_u16(
win16_far_add_offset(g_window, WINDOW_Y)) == y);
assert(win16_read_u16(
win16_far_add_offset(g_window, WINDOW_WIDTH)) == width);
assert(win16_read_u16(
win16_far_add_offset(g_window, WINDOW_HEIGHT)) == height);
}
static void test_minimized_and_maximized_skip(void)
{
reset_fixture();
win16_write_u16(g_window, WINDOW_X, 0xaaaa);
win16_write_u16(g_window, WINDOW_Y, 0xbbbb);
win16_write_u16(g_window, WINDOW_WIDTH, 0xcccc);
win16_write_u16(g_window, WINDOW_HEIGHT, 0xdddd);
g_iconic = true;
object_windows_capture_normal_placement(g_window);
assert(g_is_iconic_calls == 1 && g_is_zoomed_calls == 0);
assert(g_get_rect_calls == 0);
assert_placement(
(int16_t)0xaaaa, (int16_t)0xbbbb, 0xcccc, 0xdddd);
g_iconic = false;
g_zoomed = true;
object_windows_capture_normal_placement(g_window);
assert(g_is_iconic_calls == 2 && g_is_zoomed_calls == 1);
assert(g_get_rect_calls == 0);
}
static void test_screen_and_parent_coordinates(void)
{
reset_fixture();
object_windows_capture_normal_placement(g_window);
assert(g_get_rect_calls == 1 && g_screen_to_client_calls == 0);
assert_placement(10, 20, 100, 200);
reset_fixture();
write_far(g_window, WINDOW_PARENT, g_parent);
g_client_result = g_client;
win16_write_u8(win16_far_add_offset(g_window, WINDOW_FLAGS), 8);
object_windows_capture_normal_placement(g_window);
assert(g_parent_getter_calls == 1 && g_flag_calls == 1);
assert(g_screen_to_client_calls == 1);
assert(g_screen_to_client_window == 0x3333);
assert_placement(7, 15, 100, 200);
reset_fixture();
write_far(g_window, WINDOW_PARENT, g_parent);
g_client_result = 0;
win16_write_u32(g_window, WINDOW_ATTRIBUTES, 0x40000000U);
object_windows_capture_normal_placement(g_window);
assert(g_parent_getter_calls == 1 && g_flag_calls == 0);
assert(g_screen_to_client_window == 0x2222);
assert_placement(7, 15, 100, 200);
reset_fixture();
write_far(g_window, WINDOW_PARENT, g_parent);
g_client_result = g_client;
object_windows_capture_normal_placement(g_window);
assert(g_flag_calls == 1 && g_screen_to_client_calls == 0);
assert_placement(10, 20, 100, 200);
}
static void test_setup_sequence(void)
{
reset_fixture();
win16_write_u8(win16_far_add_offset(g_window, WINDOW_FLAGS), 8);
write_far(g_window, WINDOW_ATTACHED, g_attached);
object_windows_setup_window(g_window);
assert(g_prepare_calls == 1 && g_flag_calls == 1);
assert(g_focus_calls == 1 && g_focused_window == 0x1111);
assert(g_attached_setup_calls == 1);
assert(g_get_rect_calls == 1);
assert_placement(10, 20, 100, 200);
reset_fixture();
object_windows_setup_window(g_window);
assert(g_prepare_calls == 1 && g_flag_calls == 1);
assert(g_focus_calls == 0 && g_attached_setup_calls == 0);
assert(g_get_rect_calls == 1);
}
int main(void)
{
uint8_t stack_marker;
uintptr_t stack_base = (uintptr_t)&stack_marker - 0x8000;
win16_reset_segment_bindings();
win16_bind_segment(0x5000, g_dgroup, sizeof(g_dgroup), true);
win16_bind_segment(0x6000, g_objects, sizeof(g_objects), true);
win16_bind_segment(0x9000, (void *)stack_base, 0x10000, true);
win16_set_dgroup_selector(0x5000);
test_minimized_and_maximized_skip();
test_screen_and_parent_coordinates();
test_setup_sequence();
return 0;
}