/* Borland Pascal dynamic-method-table resolution and dispatch. */ #include "tdkpin_dispatch.h" enum { VMT_DYNAMIC_TABLE_OFFSET = 4, DYNAMIC_PARENT_OFFSET = 0, DYNAMIC_CACHE_ID_OFFSET = 2, DYNAMIC_CACHE_SLOT_OFFSET = 4, DYNAMIC_COUNT_OFFSET = 6, DYNAMIC_IDS_OFFSET = 8, }; /* * 1020:16e6 — resolve AX in the dynamic table referenced by DS:[DI+4]. * * Each near-DGROUP table node contains a parent pointer, a mutable one-entry * cache, a count, `count` method ids, then `count` far procedure pointers. * Search proceeds from the most-derived node through its parent chain. The * returned far pointer addresses the selected four-byte slot, not its target. */ Win16FarPtr borland_resolve_dynamic_method_slot( uint16_t vmt, uint16_t method_id) { uint16_t table = win16_read_u16( win16_dgroup_pointer((uint16_t)(vmt + VMT_DYNAMIC_TABLE_OFFSET))); while (true) { Win16FarPtr node = win16_dgroup_pointer(table); if (method_id == win16_read_u16(win16_far_add_offset( node, DYNAMIC_CACHE_ID_OFFSET))) { uint16_t cached_slot = win16_read_u16(win16_far_add_offset( node, DYNAMIC_CACHE_SLOT_OFFSET)); return win16_dgroup_pointer(cached_slot); } uint16_t count = win16_read_u16(win16_far_add_offset( node, DYNAMIC_COUNT_OFFSET)); for (uint16_t index = 0; index < count; index++) { uint16_t id = win16_read_u16(win16_far_add_offset( node, (uint16_t)(DYNAMIC_IDS_OFFSET + index * 2u))); if (id != method_id) { continue; } uint16_t slot = (uint16_t)( table + DYNAMIC_IDS_OFFSET + count * 2u + index * 4u); win16_write_u16(node, DYNAMIC_CACHE_ID_OFFSET, method_id); win16_write_u16(node, DYNAMIC_CACHE_SLOT_OFFSET, slot); return win16_dgroup_pointer(slot); } table = win16_read_u16(win16_far_add_offset( node, DYNAMIC_PARENT_OFFSET)); if (table == 0) { borland_runtime_error(210); } } } /* 1020:16dd — public lookup entry; DI receives the selected far slot. */ Win16FarPtr borland_find_dynamic_method(uint16_t vmt, uint16_t method_id) { return borland_resolve_dynamic_method_slot(vmt, method_id); } /* 1020:16e1 — resolve the slot and indirect through its far procedure. */ void borland_dispatch_dynamic_method(uint16_t vmt, uint16_t method_id) { Win16FarPtr slot = borland_resolve_dynamic_method_slot(vmt, method_id); win16_call_dynamic_method(win16_read_far_pointer(slot, 0)); } static void dispatch_two_far_arguments( Win16FarPtr receiver, Win16FarPtr argument, uint16_t method_id) { uint16_t vmt = win16_read_u16(receiver); Win16FarPtr slot = borland_resolve_dynamic_method_slot(vmt, method_id); win16_call_dynamic_method_two_far( win16_read_far_pointer(slot, 0), receiver, argument); } /* 1008:2d66 — generated two-far-argument dispatch for dynamic id 0x8002. */ void borland_dispatch_method_8002( Win16FarPtr receiver, Win16FarPtr argument) { dispatch_two_far_arguments(receiver, argument, 0x8002); } /* 1018:16eb — generated two-far-argument dispatch for dynamic id 0x0006. */ void borland_dispatch_method_0006( Win16FarPtr receiver, Win16FarPtr argument) { dispatch_two_far_arguments(receiver, argument, 0x0006); }