/* Borland local/global Win16 heap core reconstructed from Code5. */ #include "tdkpin_heap.h" static Win16FarPtr heap_address(uint16_t selector, uint16_t offset) { return win16_make_far_pointer(selector, offset); } static uint16_t heap_read(uint16_t selector, uint16_t offset) { return win16_read_u16(heap_address(selector, offset)); } static void heap_write(uint16_t selector, uint16_t offset, uint16_t value) { win16_write_u16(heap_address(selector, 0), offset, value); } static uint16_t align_heap_size(uint16_t bytes) { return (uint16_t)((bytes + 3u) & 0xfffcu); } uint32_t borland_heap_free_space(void) { uint32_t total = GetFreeSpace16(0x1000); uint16_t selector = g_borland_heap_segment_head; if (selector != 0) { do { total += heap_read(selector, 8); selector = heap_read(selector, 10); } while (selector != g_borland_heap_segment_head); } return total; } uint32_t borland_heap_largest_block(void) { uint32_t largest = GlobalCompact16(0); if ((largest >> 16) != 0) { return largest; } uint16_t selector = g_borland_heap_segment_head; if (selector != 0) { do { uint16_t local_free = heap_read(selector, 8); if ((uint16_t)largest < local_free) { largest = local_free; } selector = heap_read(selector, 10); } while (selector != g_borland_heap_segment_head); } return largest; } BorlandLocalAllocation borland_local_allocate_in_segment( uint16_t selector, uint16_t aligned_bytes) { uint16_t previous_link = 4; while (true) { uint16_t block = heap_read(selector, previous_link); if (block == 0) { return (BorlandLocalAllocation){0, true}; } uint16_t block_size = heap_read(selector, (uint16_t)(block + 2)); if (block_size < aligned_bytes) { previous_link = block; continue; } uint16_t remaining = (uint16_t)(block_size - aligned_bytes); uint16_t replacement = heap_read(selector, block); if (remaining != 0) { replacement = (uint16_t)(block + aligned_bytes); heap_write(selector, replacement, heap_read(selector, block)); heap_write(selector, (uint16_t)(replacement + 2), remaining); } heap_write(selector, previous_link, replacement); heap_write( selector, 8, (uint16_t)(heap_read(selector, 8) - aligned_bytes)); return (BorlandLocalAllocation){block, false}; } } void borland_local_merge_next(uint16_t selector, uint16_t block) { uint16_t next = heap_read(selector, block); uint16_t adjacent = (uint16_t)(block + heap_read(selector, (uint16_t)(block + 2))); if (adjacent == next) { heap_write(selector, block, heap_read(selector, next)); heap_write( selector, (uint16_t)(block + 2), (uint16_t)(heap_read(selector, (uint16_t)(block + 2)) + heap_read(selector, (uint16_t)(next + 2)))); } } uint16_t borland_create_local_heap_segment(void) { Win16FarPtr allocation = borland_global_allocate(g_borland_heap_segment_bytes); if (allocation == 0) { return 0; } uint16_t selector = win16_far_selector(allocation); uint16_t free_bytes = (uint16_t)(g_borland_heap_segment_bytes - BORLAND_LOCAL_HEAP_HEADER_BYTES); heap_write(selector, 0, BORLAND_LOCAL_HEAP_MAGIC); heap_write(selector, 2, 0); heap_write(selector, 4, BORLAND_LOCAL_HEAP_HEADER_BYTES); heap_write(selector, 6, 0); heap_write(selector, 8, free_bytes); uint16_t next = selector; if (g_borland_heap_segment_head != 0) { next = heap_read(g_borland_heap_segment_head, 10); heap_write(g_borland_heap_segment_head, 10, selector); } heap_write(selector, 10, next); heap_write(selector, 12, 0); heap_write(selector, 14, free_bytes); return selector; } static BorlandAllocationAttempt borland_try_local_allocation(uint16_t bytes) { uint16_t aligned = align_heap_size(bytes); uint16_t selector = g_borland_heap_segment_head; if (selector != 0) { do { BorlandLocalAllocation local = borland_local_allocate_in_segment(selector, aligned); if (!local.failed) { g_borland_heap_segment_head = selector; return (BorlandAllocationAttempt){ heap_address(selector, local.offset), false}; } selector = heap_read(selector, 10); } while (selector != g_borland_heap_segment_head); } selector = borland_create_local_heap_segment(); if (selector == 0) { return (BorlandAllocationAttempt){0, true}; } BorlandLocalAllocation local = borland_local_allocate_in_segment(selector, aligned); if (local.failed) { return (BorlandAllocationAttempt){0, true}; } g_borland_heap_segment_head = selector; return (BorlandAllocationAttempt){ heap_address(selector, local.offset), false}; } BorlandAllocationAttempt borland_try_get_mem(uint16_t bytes) { if (bytes == 0) { return (BorlandAllocationAttempt){0, false}; } while (true) { BorlandAllocationAttempt result; if (bytes < g_borland_local_heap_limit) { result = borland_try_local_allocation(bytes); if (!result.failed) { return result; } Win16FarPtr global = borland_global_allocate(bytes); if (global != 0) { return (BorlandAllocationAttempt){global, false}; } } else { Win16FarPtr global = borland_global_allocate(bytes); if (global != 0) { return (BorlandAllocationAttempt){global, false}; } if (g_borland_local_heap_limit != 0 && bytes <= (uint16_t)(g_borland_heap_segment_bytes - BORLAND_LOCAL_HEAP_HEADER_BYTES)) { result = borland_try_local_allocation(bytes); if (!result.failed) { return result; } } } uint16_t action = g_borland_heap_error_handler == 0 ? 0 : win16_call_pascal_u16(g_borland_heap_error_handler, bytes); if (action > 1) { continue; } return (BorlandAllocationAttempt){0, action == 0}; } } static bool borland_global_free_selector(uint16_t selector) { if (selector == win16_far_selector(win16_dgroup_pointer(0))) { return true; } HGLOBAL16 handle = GlobalHandle16(selector); if (handle == 0) { return true; } (void)GlobalUnlock16(handle); (void)GlobalFree16(handle); return false; } static bool borland_release_empty_local_segment(uint16_t selector) { uint16_t next = heap_read(selector, 10); uint16_t new_head = 0; if (next != selector) { uint16_t previous = g_borland_heap_segment_head; while (heap_read(previous, 10) != selector) { previous = heap_read(previous, 10); } heap_write(previous, 10, next); new_head = previous; } g_borland_heap_segment_head = new_head; return borland_global_free_selector(selector); } bool borland_try_free_mem(Win16FarPtr block, uint16_t bytes) { if (bytes == 0) { return false; } uint16_t selector = win16_far_selector(block); uint16_t offset = win16_far_offset(block); if (offset == 0) { return borland_global_free_selector(selector); } uint16_t aligned = align_heap_size(bytes); if (heap_read(selector, 0) != BORLAND_LOCAL_HEAP_MAGIC || (offset & 3) != 0) { return true; } uint16_t previous_link = 4; uint16_t current = heap_read(selector, previous_link); while (current != 0 && offset > current) { previous_link = current; current = heap_read(selector, current); } if (offset == current) { return true; } heap_write(selector, offset, current); heap_write(selector, (uint16_t)(offset + 2), aligned); uint16_t free_bytes = (uint16_t)(heap_read(selector, 8) + aligned); heap_write(selector, 8, free_bytes); if ((uint16_t)(free_bytes + BORLAND_LOCAL_HEAP_HEADER_BYTES) == g_borland_heap_segment_bytes) { return borland_release_empty_local_segment(selector); } borland_local_merge_next(selector, offset); heap_write(selector, previous_link, offset); borland_local_merge_next(selector, previous_link); return false; }