# Far-Call Targets: Top-104 Most-Called Functions Content extracted from `crusader_decompilation_notes.md`. Named via systematic analysis of 11,692 NE relocation fixup entries — the functions most frequently called through the `CALLF 0x0000:ffff` thunk mechanism. --- ## Tier 1: Ranks 1–20 (73+ callers) | Rank | Address | Name | Calls | Description | |------|---------|------|-------|-------------| | 1 | `000a:44fd` | `fatal_error_report_fmt_a_and_exit` | 331 | Reentrancy-guarded fatal report helper. Prints the shared banner at `0x44a5`, formats template `0x44cc` with caller words, then exits; earlier `0005:2c68` selector speculation is now rejected. | | 2 | `0003:ac7e` | `mem_alloc` | 272 | Allocation wrapper → seg082:0000 (`0009:a200`) | | 3 | `0008:dbec` | `entity_word_list_destroy` | 238 | Frees entity word-list buffer. | | 4 | `0003:a751` | `mem_free` | 207 | Free wrapper → seg082:007a (`0009:a27a` = `mem_free_checked`) | | 5 | `0008:bb4f` | `mem_alloc_far` | 174 | Thin wrapper → `mem_alloc` | | 6 | `0003:a897` | `far_memcpy` | 165 | REP MOVSW + trailing MOVSB | | 7 | `0005:088f` | `entity_get_type_word` | 130 | Returns type word from table `0x7df9` indexed by slot | | 8 | `000b:358d` | `sprite_tree_accumulate_pos` | 122 | Recursively sums X/Y offsets (`+0x21/+0x23`) through linked child nodes (`+0x19/+0x1b`), copies 8-byte position block via `far_memcpy` | | 9 | `0008:ce3d` | `entity_call_two_vtables` | 118 | Calls vtable[`+4`] at entity+`0x1e` and `+0x28` | | 10 | `0004:26cd` | `nop_void_stub` | 118 | Empty function, returns void | | 11 | `0008:ce00` | `entity_call_two_vtables_base` | 117 | Calls vtable[0] at entity+`0x1e` and `+0x28` | | 12 | `0008:bb8c` | `entity_check_flag_0x4000` | 115 | Short-circuits if flag `0x4000` set at `+0x16` | | 13 | `0008:cda7` | `entity_free_both_word_lists` | 115 | Frees word lists at entity+`0x1e` and `+0x28` if optional pointers at `+0x24/+0x26` and `+0x2e/+0x30` non-null. Both call `entity_word_list_free_existing`. | | 14 | `0004:26d2` | `nop_void_stub_b` | 111 | Empty function, returns void | | 15 | `000a:45fe` | `fatal_error_report_fmt_c_and_exit` | 108 | Sibling fatal report helper. Uses the same `0x44a4` guard and banner string, formats static template `0x4506` with caller words, then exits. | | 16 | `0004:3324` | `nop_return_zero` | 95 | Returns 0 | | 17 | `0009:c563` | `event_queue_push` | 82 | Circular buffer enqueue. Ring index (`+0xe`) masked `0x3f`, slot masked `0xfff8`. Writes event type word + data byte pair. | | 18 | `0005:c448` | `list_remove_and_free` | 74 | Unlinks node from linked list via `FUN_0005_c495`, optionally calls `mem_free` if bit 0 of flags set | | 19 | `000b:2e00` | *(no function in Ghidra)* | 74 | Analysis gap at seg109:0000. Needs manual function creation. | | 20 | `0009:1f12` | `dos_file_lseek` | 73 | DOS LSEEK (INT 21h AH=42h) wrapper with error reporting to `0x867a` | --- ## Tier 2: Ranks 21–40 (56–73 callers) | Rank | Address | Name | Calls | Description | |------|---------|------|-------|-------------| | 21 | `0009:3600` | `rotating_buffer_advance` | 73 | Advances 5-slot circular counter at `0x3eb6`, zeros pointer in table at `0x867c`, dispatches via jump table | | 22 | `0009:943a` | `entity_rect_compare_and_dispatch` | 68 | Compares bounding rectangles of two entities, dispatches based on flag bits `4/2/1` at `+0x16` | | 23 | `0009:1e61` | `dos_file_close` | 65 | DOS file close (INT 21h), error reporting, sets handle to `-1` | | 24 | `0005:e252` | *(unnamed — unclear)* | 65 | Copies 11 words from Phar Lap extender area (`FUN_0000_12c6+5`), then calls thunk. Interrupt/trampoline setup? | | 25 | `0003:dbcc` | `crt_format_string` | 64 | MetaWare High C formatting wrapper. Calls `FUN_0003_bb92` with runtime format dispatch table. | | 26 | `0007:5a00` | *(no function in Ghidra)* | 64 | High-traffic raw target at `seg043:0000`. Earlier `debris_spawn` / seg001 mapping was rejected after checking relocation labels. Still needs manual function creation and direct analysis. | | 27 | `000a:4742` | `assert_buffer_valid` | 63 | Validates handle: asserts `param_2 == cookie` at `0x45a6` and `param_1 < limit` at `0x87e0` | | 28 | `0009:9216` | `entity_conditional_render_dispatch` | 63 | Checks entity flag bits 4 and 1 at `+0x16`, dispatches to vtable[`+0xc`] or thunk | | 29 | `0008:cb2c` | `entity_flag20_clear_and_update_target` | 61 | Clears flag bit `0x20`, writes target `+0x12/+0x14`, calls refresh | | 30 | `0008:cb5c` | `entity_flag20_set_and_init_target` | 61 | Sets flag bit `0x20`, inits target if zero, calls refresh | | 31 | `0007:7306` | `entity_create_stack_object` | 58 | Allocates `0xCC` bytes on stack, inits via `object_init_zero_fields` (`0005:c400`), calls thunk | | 32 | `0007:8709` | `entity_mark_dirty_and_sync_tile_aux` | 58 | Syncs tile aux, sets flag bit `0x04` at `+0x42` | | 33 | `0007:87c5` | `entity_set_flag20_from_field42` | 58 | Reads entity `+0x42/+0x44`, calls `entity_flag20_set_and_init_target` with those values | | 34 | `0007:8508` | `entity_table_lookup_and_dispatch` | 58 | Searches table at `0x2b46`, dispatches via indirect jump | | 35 | `0007:8920` | `entity_call_vtable_slot0c` | 58 | Calls vtable entry at `+0x0c` | | 36 | `000a:b988` | `sprite_node_get_or_traverse` | 57 | If child pointer at `+0x19/+0x1b` non-null, traverses; otherwise returns leaf value | | 37 | `0003:a98b` | `crt_signed_div32` | 56 | Entry: adjusts near→far stack, sets `CX=0` (signed quotient), jumps to `crt_div32_impl` | | 38 | `000a:7b44` | `nop_return_void_a` | 56 | Empty function (default vtable slot?) | | 39 | `000a:7b49` | `nop_return_void_b` | 56 | Empty function (default vtable slot?) | | 40 | `000a:7b53` | `nop_return_void_c` | 56 | Empty function (default vtable slot?) | --- ## Supporting Functions Discovered | Address | Name | Description | |---------|------|-------------| | `000b:3a00` | `sprite_tree_sum_x_offset` | Recursive: sums field `+0x21` through child chain `+0x19/+0x1b` | | `000b:3a35` | `sprite_tree_sum_y_offset` | Recursive: sums field `+0x23` through child chain `+0x19/+0x1b` | | `0003:a845` | `crt_exit_wrapper` | Calls `crt_exit_impl(param,0,0)` | | `0003:a7ee` | `crt_exit_impl` | Full C exit: atexit handlers, stdio flush, MetaWare runtime cleanup | | `0003:a9a8` | `crt_div32_impl` | 32-bit division core. CX flags: bit0=unsigned, bit1=modulo, bit2=negate | | `0005:c400` | `object_init_zero_fields` | Zeros fields `+0x25`, `+0x29`, `+0x31`, `+0x32` of a struct. Returns pointer. | | `000a:4440` | `joystick_read_axes_and_buttons` | Reads PC game port `0x201`. Times axis responses, reads button nibble to `0x44a2` | | `000b:3380` | `sprite_node_is_dirty` | Checks flags at `obj+0x29 & 3 == 1 or 3` → returns bool | | `000b:33a6` | `sprite_node_mark_dirty` | If not dirty, calls `FUN_000b_3965` with `mode=3` to invalidate | --- ## Tier 3: Ranks 41–60 (42–56 callers) | Rank | Address | Name | Calls | Description | |------|---------|------|-------|-------------| | 41 | `000a:7b58` | `nop_return_zero_b` | 56 | Returns 0 (default vtable slot) | | 42 | `000b:3ab2` | `sprite_node_dispatch_event` | 56 | Large event dispatch: checks event type (`2/4/8/0x100`), updates global focus ptr at `[0x4fd0:4fd2]`, dispatches via vtable methods `[+0x14/+0x18/+0x20/+0x24]` by event code. Switch table for 16 event types. | | 43 | `000a:48ff` | `rng_next_modulo` | 55 | Advances seg091 RNG state and returns the result modulo the requested bound; returns 0 when bound is 0. | | 44 | `000b:3362` | `sprite_tree_unwind_check` | 55 | Validates `SS == param_2` (stack segment guard), then decrements global counter at `[0x4fd6]` | | 45 | `000b:40ee` | `sprite_node_update_and_dispatch` | 55 | If `sprite_node_is_dirty` returns false: marks dirty, calcs accumulated bounds via `sprite_tree_get_accumulated_bounds` (`3ed8`), then dispatches via thunk | | 46 | `000a:7b5f` | `vtable_stub_trampoline` | 55 | Calls through fixup thunk (forwarder to another function) | | 47 | `000a:7b78` | `nop_return_void_e` | 55 | Empty function (default vtable slot) | | 48 | `000a:7b7d` | `nop_return_void_f` | 55 | Empty function (default vtable slot) | | 49 | `000a:7b4e` | `nop_return_void_d` | 54 | Empty function (default vtable slot) | | 50 | `000b:330c` | `sprite_tree_dispatch_wrapper` | 52 | Pure thunk wrapper: calls through fixup | | 51 | `0009:2034` | `dos_file_seek` | 51 | INT 21h AH=42h (LSEEK). Takes file object ptr, extracts handle at `obj+4`, seeks to offset param. Error reporting to `[0x867a]`. | | 52 | `0005:0466` | `entity_resolve_slot_ptr` | 50 | *(pre-existing name)* | | 53 | `0003:a880` | *(no function in Ghidra)* | 49 | Analysis gap in CRT segment | | 54 | `0006:170c` | `tile_class_get_byte` | 47 | Looks up class data: indexes into table at `[0x7e1e]` by `(*param_1 * 0x79)`, returns byte at offset `+0xc` | | 55 | `000b:4097` | `sprite_dispatch_with_event` | 45 | Pushes event params + global `[0x49c2:0x49c4]`, calls thunk | | 56 | `0005:02c1` | `entity_is_type_match` | 43 | Compares `*param_1` against global at `[0x27c8]`, returns 1 if equal, 0 otherwise | | 57 | `0003:ad75` | *(no function in Ghidra)* | 43 | Analysis gap in CRT segment | | 58 | `000a:e709` | `render_dispatch_by_flag` | 43 | Dispatches between two thunk paths based on boolean flag at `stack+0x10` | | 59 | `0003:d0ff` | `crt_sprintf_wrapper` | 42 | Calls `FUN_0003_bb92` (format engine) with rearranged params and string constant at `0x67ac` | | 60 | `000b:326e` | `sprite_node_destroy` | 42 | Destructor: sets vtable ptr to `0x501a`, clears global `[0x4fd0:4fd2]` if self, releases child nodes, calls `mem_free` via thunk | --- ## Analysis Gaps | Address | NE Segment | Callers | Notes | |---------|-----------|---------|-------| | `000a:44fd` | seg091:00fd | 331 | Fatal report helper now identified; remaining gap is the exact human-readable template text at `0x44cc`/`0x44a5`, not control flow. | | `000b:2e00` | seg109:0000 | 74 | Start of segment 109. | | `0007:5a00` | seg043:0000 | 64 | Start of segment 43. Earlier seg001 `debris_spawn` port was rejected; still needs manual function creation and direct analysis. | | `000a:48ff` | seg091:04ff | 55 | Recovered as `rng_next_modulo`; bounded wrapper around seg091 RNG state advance. | | `0003:a880` | seg005:0880 | 49 | In CRT segment near `far_memcpy`. | | `0003:ad75` | seg005:0d75 | 43 | In CRT segment near `mem_alloc`. | | `000a:454d` | seg091:014d | 32 | Buffer-normalizing fatal report sibling. Copies/clears context through the `0x45a6` global, formats template `0x44e7`, then exits. | **seg043 reconciliation:** - The earlier standalone seg001 port hypothesis in this subrange was wrong. - Relocation data places raw `0007:5a00` at `seg043:0000`, and the named helper at `0007:5b6f` sits at `seg043:016f`. - Because of that segment placement, standalone seg001 names such as `debris_spawn` (`0x7490`) and `entity_die` (`0x75ff`) should NOT be ported into this raw range. - `0007:5b6f` no longer exists as a function after the PyGhidra repair pass. Its behavior now lines up with the repaired function `0007:5b7a = entity_set_at_target_update_facing`. - The currently repaired functions at `0007:5a90` and `0007:5c1c` should keep their positional names until a later pass resolves the thunk-heavy bodies more clearly. --- ## Tier 4: Ranks 61–80 (29–42 callers) | Rank | Address | Name | Calls | Description | |------|---------|------|-------|-------------| | 61 | `000b:30a5` | `sprite_tree_forward_wrapper` | 42 | Pure thunk forwarder | | 62 | `0008:bc27` | `entity_set_event_type_checked` | 41 | Sets event code at `+0x06` with range/timer checks | | 63 | `0008:d214` | `entity_dispatch_entry_ctor_vtbl_3aa6` | 40 | Constructor: alloc `0x40`, vtbl `3AA6`, flag `0x200` | | 64 | `0005:1565` | `entity_action_by_type_dispatch` | 39 | Checks entity type against whitelist (`0x432,0x5a0,0x1fd,0x1fe,0x8f,0x59f,0x2b3,0x2ca`), dispatches by flags at `[0xc76]` and `[0x85f]` | | 65 | `0008:4bba` | `channel_slot_enable` | 39 | Sets enable byte=1 in 5-slot table at `0x84ca` (slot * `0xd` stride) | | 66 | `0009:6f5a` | `vga_palette_write` | 38 | Writes RGB triplets to VGA DAC (port `0x3C8/0x3C9`). Range `param_2..param_3` from palette data at `*param_1` | | 67 | `0009:8ef6` | `line_draw_dispatch` | 38 | Compares `abs(dx)` vs `abs(dy)` to determine major axis, dispatches to appropriate line draw routine | | 68 | `000a:7b30` | `nop_return_void_g` | 38 | Empty function (default vtable slot) | | 69 | `000a:7b3f` | `nop_return_void_h` | 38 | Empty function (default vtable slot) | | 70 | `0009:6e7f` | `palette_free_if_set` | 35 | Frees existing palette data if ptr non-null, checks alignment | | 71 | `000a:7b35` | `nop_return_void_i` | 35 | Empty function (default vtable slot) | | 72 | `0009:c433` | `event_queue_align_index` | 34 | Returns `param_1 & 0xFFF8` — aligns ring index to 8-byte event slot boundary | | 73 | `0009:2156` | `dos_file_get_size` | 33 | Saves file position, does INT 21h AH=42h AL=02 (seek to end), restores position. Returns file size in DX:AX | | 74 | `000a:2c41` | `list_iterate_next` | 33 | Linked list iterator: if `*out==0` returns first from `obj+2`; else follows next at `ptr+2/+4`. Returns bool (has more) | | 75 | `000a:454d` | `seg091_func_014d` | 32 | Recovered boundary. Shares flag `0x44a4`; checks optional long argument against the `0x45a6` cookie/context global. | | 76 | `000b:2446` | `sprite_clear_redraw_flag` | 31 | Clears flag at `obj+0x17e`, then dispatches via thunk | | 77 | `0005:1238` | `entity_get_class_word` | 30 | Looks up table at `[0x7e01]` indexed by `*param_1 * 2`, returns word. Sister of `entity_get_type_word` (which uses `[0x7df9]`) | | 78 | `000b:1446` | `display_null_check_dispatch` | 30 | Null-checks far ptr params, dispatches to different thunks based on result | | 79 | `000d:85da` | `vga_palette_set_all_black` | 29 | Sets byte at `global_obj[0x6828]+0x40 = 1` if global non-null, then calls thunk. *(Note: earlier name `map_object_set_dirty_flag` was incorrect; corrected in seg137 analysis.)* | | 80 | `0005:1511` | `entity_destroy_trampoline` | 29 | Pure thunk forwarder to entity destruction | --- ## Tier 5: Ranks 81–104 (24–29 callers) | Rank | Address | Name | Calls | Description | |------|---------|------|-------|-------------| | 81 | `0009:1c00` | `dos_file_handle_init` | 29 | Inits 6-byte file handle struct: dword=0, word+4=`0xFFFF` (invalid). Aborts on null ptr | | 82 | `0008:75f3` | `entity_get_ptr` | 29 | Looks up entity far ptr from table at `DS:0x39b0`, indexed by `id*4` | | 83 | `0006:0208` | `entity_class_get_flag4` | 29 | Returns bit 2 of classinfo byte at `[0x7e1e]+*p1*0x79+0x13` → 0 or 1 | | 84 | `000a:30d7` | `list_node_set_if_context` | 29 | Sets node fields `+2/+4` if params match context globals at `0x45a6/0x45a8` | | 85 | `0009:c45f` | `object_init_and_get_next` | 29 | Calls `object_init_zero_fields` then returns `*(result+2)` — init+accessor combo | | 86 | `0004:d7a0` | `object_deref_get_word4` | 28 | Dereferences far ptr chain: returns word at `*(*(param_1)+4)` | | 87 | `000a:5276` | `debug_check_flag_45aa` | 28 | If byte at `DS:0x45aa` non-zero, calls thunk (diagnostic/assert check) | | 88 | `0003:d94f` | `far_memset` | 28 | Wrapper reordering params for CRT memset impl at `0003:d92b` (odd-aligned, word-fill loop) | | 89 | `000a:7b3a` | `nop_return_void_j` | 28 | Empty function (default vtable slot) | | 90 | `0008:ca18` | `entity_pair_sync_b` | 27 | Pairwise sync wrapper direction B | | 91 | `0008:bd20` | `entity_sprite_set_target_pos` | 27 | Sets flag `0x1000`, copies player pos to entity `+0x0a/+0x0c` | | 92 | `0009:3ceb` | `buffer_release_and_dispatch` | 27 | Frees far ptr at `obj+0x3b` if set, nulls it; conditionally dispatches on bit 0 | | 93 | `0005:09b4` | `entity_get_flags_byte` | 27 | Reads byte from `[0x7dfd]+id`, conditionally extends with classinfo byte at `[0x7e1e]+id*0x79+0xf` | | 94 | `0005:0fbb` | `entity_lookup_sprite_word` | 27 | Returns word from `[0x7e05]+*p1*2` — sprite/visual index table | | 95 | `0008:d27e` | `entity_dispatch_trampoline_b` | 26 | Pure forwarder thunk (CALLF thunk only) | | 96 | `0005:0376` | `entity_resolve_base_type` | 26 | Walks entity class hierarchy (bit 8 in `[0x7e01]`) via `[0x7ded]`, returns base type from `[0x7df1]` | | 97 | `000b:2492` | `sprite_redraw_if_needed` | 26 | If redraw flag at `+0x17e` is clear, calls update routine + thunk | | 98 | `0003:e4d3` | `dos_file_open_wrapper` | 26 | Zeros output byte, delegates to file open impl at `0003:bb92` | | 99 | `0005:033e` | `entity_resolve_base_parent` | 25 | Same hierarchy walk as `entity_resolve_base_type` but returns parent from `[0x7ded]` | | 100 | `000a:87fd` | `render_clip_rect_to_viewport` | 25 | Clips 4 rect params to viewport bounds at `[0x4014]`, sets dirty flag at `0x8a16`, increments draw counter at `0x4716` | | 101 | `0005:3cf5` | `entity_class_has_flag2000` | 25 | Returns `(entity_get_class_word(slot) & 0x2000) != 0` | | 102 | `0009:80db` | `bbox_overlap_test` | 25 | Boolean rectangle overlap test for two 4-word bbox structs; unlike `bbox_intersect`, it does not write back an intersection | | 103 | `000d:cc00` | `entity_compute_proximity_or_visibility_bucket` | 25 | Returns `0x40` if entity is null or projected bbox overlaps viewport; otherwise buckets world-distance from current reference entity (`0x7e22`) into `0x32/0x20/0x10/0x08` | | 104 | `000d:d413` | `entity_refresh_recent_proximity_or_visibility_buckets` | 24 | Recomputes bucket values for the last four active entries in `0x69ac` and notifies backing handles via `000a:6343` when a bucket changes |