/* Portable selector-backed implementation of explicit Win16 memory access. */ #include "tdkpin_segmented.h" #include #include enum { MAX_BOUND_SEGMENTS = 32 }; typedef struct { uint16_t ne_segment; uint16_t selector; uint8_t *bytes; size_t size; bool writable; } BoundSegment; static BoundSegment g_segments[MAX_BOUND_SEGMENTS]; static size_t g_segment_count; static uint16_t g_dgroup_selector; static uint16_t g_stack_selector; static uint16_t g_stack_pointer_offset; static Win16FarPtr g_current_return_address; static uint32_t g_dos_time_words; static uint16_t g_indeterminate_u16; static BoundSegment *find_segment(uint16_t selector) { for (size_t index = 0; index < g_segment_count; index++) { if (g_segments[index].selector == selector) { return &g_segments[index]; } } abort(); /* Invalid selectors fault in the original Win16 environment. */ } static uint8_t *resolve(Win16FarPtr address, size_t bytes, bool write) { BoundSegment *segment = find_segment(win16_far_selector(address)); size_t offset = win16_far_offset(address); if (offset + bytes > segment->size || (write && !segment->writable)) { abort(); } return segment->bytes + offset; } void win16_reset_segment_bindings(void) { g_segment_count = 0; g_dgroup_selector = 0; g_stack_selector = 0; g_stack_pointer_offset = 0; g_current_return_address = 0; g_dos_time_words = 0; g_indeterminate_u16 = 0; } void win16_bind_segment( uint16_t selector, void *bytes, size_t size, bool writable) { win16_bind_ne_segment(0, selector, bytes, size, writable); } void win16_bind_ne_segment( uint16_t ne_segment, uint16_t selector, void *bytes, size_t size, bool writable) { if (g_segment_count == MAX_BOUND_SEGMENTS || size > 0x10000) { abort(); } g_segments[g_segment_count++] = (BoundSegment){ne_segment, selector, bytes, size, writable}; } void win16_set_dgroup_selector(uint16_t selector) { (void)find_segment(selector); g_dgroup_selector = selector; } void win16_set_current_return_address(Win16FarPtr address) { g_current_return_address = address; } void win16_set_dos_time_words(uint32_t words) { g_dos_time_words = words; } void win16_set_indeterminate_u16(uint16_t value) { g_indeterminate_u16 = value; } void win16_set_stack_state(uint16_t selector, uint16_t stack_pointer) { (void)find_segment(selector); g_stack_selector = selector; g_stack_pointer_offset = stack_pointer; } Win16FarPtr win16_dgroup_pointer(uint16_t offset) { if (g_dgroup_selector == 0) { abort(); } return win16_make_far_pointer(g_dgroup_selector, offset); } Win16FarPtr win16_relocated_code_pointer(uint16_t ne_segment, uint16_t offset) { for (size_t index = 0; index < g_segment_count; index++) { if (g_segments[index].ne_segment == ne_segment) { return win16_make_far_pointer(g_segments[index].selector, offset); } } abort(); } Win16FarPtr win16_stack_pointer(void *pointer) { uintptr_t value = (uintptr_t)pointer; for (size_t index = 0; index < g_segment_count; index++) { uintptr_t base = (uintptr_t)g_segments[index].bytes; if (value >= base && value < base + g_segments[index].size) { return win16_make_far_pointer( g_segments[index].selector, (uint16_t)(value - base)); } } abort(); } Win16FarPtr win16_current_stack_address(uint16_t offset) { if (g_stack_selector == 0) { abort(); } return win16_make_far_pointer(g_stack_selector, offset); } uint8_t win16_read_u8(Win16FarPtr address) { return *resolve(address, 1, false); } uint16_t win16_read_u16(Win16FarPtr address) { return (uint16_t)( win16_read_u8(address) | ((uint16_t)win16_read_u8(win16_far_add_offset(address, 1)) << 8)); } uint32_t win16_read_u32(Win16FarPtr address) { return (uint32_t)win16_read_u16(address) | ((uint32_t)win16_read_u16(win16_far_add_offset(address, 2)) << 16); } void win16_write_u8(Win16FarPtr address, uint8_t value) { *resolve(address, 1, true) = value; } Win16FarPtr win16_read_far_pointer(Win16FarPtr base, uint16_t byte_offset) { Win16FarPtr address = win16_far_add_offset(base, byte_offset); uint16_t offset = win16_read_u16(address); uint16_t selector = win16_read_u16(win16_far_add_offset(address, 2)); return win16_make_far_pointer(selector, offset); } void win16_write_u16(Win16FarPtr base, uint16_t byte_offset, uint16_t value) { Win16FarPtr address = win16_far_add_offset(base, byte_offset); win16_write_u8(address, (uint8_t)value); win16_write_u8(win16_far_add_offset(address, 1), (uint8_t)(value >> 8)); } void win16_write_u32(Win16FarPtr base, uint16_t byte_offset, uint32_t value) { win16_write_u16(base, byte_offset, (uint16_t)value); win16_write_u16(base, (uint16_t)(byte_offset + 2), (uint16_t)(value >> 16)); } void win16_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); } } uint16_t win16_indeterminate_u16(void) { return g_indeterminate_u16; } uint32_t win16_dos_time_words(void) { return g_dos_time_words; } Win16FarPtr win16_current_return_address(void) { return g_current_return_address; } uint16_t win16_current_stack_pointer(void) { if (g_stack_selector == 0) { abort(); } return g_stack_pointer_offset; } uint16_t win16_read_stack_u16(uint16_t offset) { if (g_stack_selector == 0) { abort(); } return win16_read_u16(win16_make_far_pointer(g_stack_selector, offset)); } void win16_write_stack_u16(uint16_t offset, uint16_t value) { if (g_stack_selector == 0) { abort(); } win16_write_u16( win16_make_far_pointer(g_stack_selector, offset), 0, value); } uint16_t win16_exchange_u16(Win16FarPtr address, uint16_t value) { uint16_t previous = win16_read_u16(address); win16_write_u16(address, 0, value); return previous; }