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