Crusader_Decomp/docs/far-call-targets.md

179 lines
17 KiB
Markdown
Raw Normal View History

# 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 120 (73+ callers)
| Rank | Address | Name | Calls | Description |
|------|---------|------|-------|-------------|
| 1 | `000a:44fd` | `seg091_func_00fd` | 331 | Recovered boundary. Shares init flag `0x44a4` with `runtime_init_or_abort`; thunk-heavy non-returning wrapper. |
| 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` | `runtime_init_or_abort` | 108 | Reentrancy-guarded init. Flag at `0x44a4`; flushes via `FUN_000a_4a56`, then calls `crt_exit_wrapper(1)`. Hidden code gap `0x4616-0x4643`. |
| 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 2140 (5673 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 4160 (4256 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 | Recovered as `seg091_func_00fd`; thunk-heavy init wrapper sharing flag `0x44a4`. |
| `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 | Recovered as `seg091_func_014d`; init/context helper using the `0x45a6` cookie/context global. |
**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 6180 (2942 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 81104 (2429 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 |