#include "../tdkpin_borland_runtime.h" #include #include #include #include #include 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; }