How to read an entry

A name like FUN_8003E4E8 is Ghidra's (the disassembler's) automatic label for the function whose code starts at RAM address 0x8003E4E8 - the game shipped no symbol names, so the address is the name, and it is stable across every doc and probe in this project. DAT_/PTR_ prefixes mark data and pointer-table addresses the same way; "JT" is a jump table (the array of handler addresses behind a switch).

The one thing that catches people out: functions at 0x801C0000+ are RAM-loaded overlays (code the game pages into RAM per game mode), and several overlays share that address window. One address can therefore name a different function depending on which overlay was resident when the dump was taken. If an entry does not match the code you are reading, check which overlay you have before concluding the entry is wrong - see overlay capture.

By subsystem

Boot + asset loader

  • FUN_8003E4E8 - boot-time TOC loader; reads first 3 sectors of PROT.DAT into 0x801C70F0
  • FUN_8003E8A8 - LBA resolver against the in-RAM TOC
  • FUN_8003E6BC - path-based file opener (data\battle\efect.dat etc.)
  • FUN_8003EBE4 / FUN_8003EC70 - overlay loaders A / B; param + 0x381 → raw TOC index = extraction entry param + 0x37F (the resolver indexes the raw in-RAM PROT.DAT head, 2 above extraction indexing); two overlays resident at once. Loader B pages in the per-summon Seru-magic overlays: a player Seru-magic cast (spell id in 0x81..0x8b) calls FUN_8003EC70(id - 0x79) → extraction PROT 903..913 (Gimard Burning Attack 0x81 → PROT 903). The summon's 3D models live in those overlays + the extraction-0871 etmd.dat model library, not in befect_data.
  • FUN_8001F05C - asset-type dispatcher
  • FUN_80020224 - descriptor-pair walker
  • FUN_8002541C - streaming-asset driver
  • FUN_80021934 - scene-transition streaming actor (5-state SM over actor+0x1A, jump table 0x80010760; real entry 3 insns before the 0x80021940 prologue - the body sat in never-analyzed space, invisible to instruction walks). Spawned on demand by FUN_8001FD44 via FUN_80020DE0 from the spawn descriptor at 0x80070734 (one of the phase-misaligned descriptor family 0x800705FC..0x80070763, NOT a mode-table row). Streams the destination scene's DATA_FIELD\<scene>.LZS bundle (raw scene_base+3) into _DAT_8007B85C - by PROT index in dev (state 2), by built filename in retail (state 4) - then hands off with _DAT_8007B83C = 2 (MAIN INIT). This is the _DAT_8007B85C staging path described in asset loader. See ghidra/scripts/funcs/80021940.txt.

Game VMs

  • FUN_801D6628 - actor / sprite VM (13 opcodes, JT at 0x801CED70)
  • FUN_80023070 - move-table VM (71 opcodes, JT at 0x80010778)
  • FUN_801D362C - move-VM extension (61 sub-opcodes via op 0x2F); same dispatcher in town / world-map / dialog / cutscene overlays at the same RAM address
  • FUN_8002519C - per-frame actor-list iterator; called 5x per frame from FUN_80016444 against the actor-list heads at _DAT_8007C34C..0x36C
  • FUN_801DE840 - field / event script VM (43 ops, switch at 0x801E00F4)
  • FUN_801E0C3C - field-VM 0x4C 2nd-stage dispatcher (16-entry JT at 0x801CEE60)
  • FUN_801E3040 - field-VM 0x4C outer-nibble-0xE sub-dispatcher (15-entry JT at 0x801CF008)
  • FUN_801E30E4 - field-VM 0x4C 0xE2 FMV trigger; writes FMV index to _DAT_8007BA78 + game mode 0x1A (StrInit) to _DAT_8007B83C
  • FUN_8003CE9C - field-VM context resolver (sign-extended u16 read)
  • FUN_80038158 - per-actor motion / bytecode VM (the second motion VM; dispatched by FUN_8003BC08 when actor +0x10 & 0x80); op-7 sets / op-8 clears the system story-flag bank DAT_80085758 inline, flag index from its own script bytecode. Carrier = MAN tail-section 1 (installer FUN_8003A9D4, parser legaia_asset::man_motion), swept disc-wide by man-scripts --motion-flag-census: the spine gates 0x142/0x482/0x1BE and the town01 one-shot 549 appear in no motion stream - the first three are field-VM script bytes in the streaming variant MAN carriers, and 549 is a direct code path (the earlier "549 via op-7 bytecode" carrier reading is falsified).
  • FUN_800204F8 - move-buffer setup (Tactical Arts)
  • FUN_80021DF4 - per-frame actor tick
  • FUN_8003A1E4 - field-NPC placement installer (reads [model, anim, tile_x, tile_z] - no facing byte in the record). Its tail (body 0x8003A474..0x8003A4F8) runs a spawn-time field-VM prologue pre-run: if the placement record's script opens with op 0x24/0x25, it executes the leading ops one at a time through FUN_801DE840 until a 0x21 NOP or a sub-0x20 byte - this is where a never-walked NPC's INITIAL FACING comes from (ops 0x4C 0x51 sub-1 and 0x38 write actor+0x26 from the 8-direction heading LUT at 0x80073F04, entry i*0x200). Port man_field_scripts::placement_initial_facing + World::seed_field_npc_facings; see field locomotion. See ghidra/scripts/funcs/8003a1e4.txt.

Cutscene / FMV

  • FUN_801CEA3C - STR/MDEC master dispatch (cutscene_str overlay 0970, modes 26/27): decoder select (Iki default; dev slots 9/10 = STRv2/v3), letterbox clears, play-loop call on 0x801D0A6C + fmv_id * 0x20, and the post-play return-scene hand-off (label table 0x801CE8AC → scene-name global 0x80084548 + spawn word 0x80084540)
  • FUN_801CF098 - STR/MDEC FMV main play loop (1236 B / 309 instr; takes (wide_flag, &dispatch_slot); pad-abort honoured only for fmv_id 0)
  • FUN_8005F024 - STR frame-demux state machine (SCUS St library; ready-callback front FUN_8005ECD4): magic-0x160/stream-number gate, seek-to-start-frame, chunk sequencing into per-frame ring slots, end-frame latch; ring API StSetRing FUN_8005BBF8 / StGetNext FUN_8005EF40 / StFreeRing FUN_8005EE4C, read start FUN_8005EB68 (Setmode 0xE0, no SF)
  • FUN_8003D764 - XA-clip drive sequencer / channel selector (SCUS): CdlSeekL → Setmode 0xC8CdlSetlocCdlSetfilter {file 1, chan}CdlReadSCdlGetlocP poll; armed by FUN_8003D53C/FUN_8003EAE4 off the 34-slot XA1..XA34 clip table at 0x801C6ED8
  • 0x801CAE08 (data) - libcd CdlFILE directory cache ([loc][size][name[16]] per file of the last-searched dir)
  • 0x801D0A6C (data) - FMV dispatch table (32 B × 23 slots; static overlay data, legaia_asset::fmv_dispatch)
  • FUN_8003BDE0 - partition-record → VM-context dispatcher, (x, z, record, gate); named-record header (name + 3 story-flag gates: C1 blocks if ANY set vs bitmap DAT_80085758, C2 requires ALL), script entry 1 + name_len*2 + (1+C0) + (1+C1*2) + (1+C2*2) (opdeene record 18 → 0x10). Two caller families: field-VM op 0x44 SPAWN_RECORD (ra 0x801DF098) and the walk-on tile trigger FUN_801D1EC4FUN_801D5630 (ra 0x801D218C)
  • FUN_80037174 - narration crawl roller actor handler (the [CC F8 80 N] op): spawned as a child context so the parent timeline keeps running and the between-block camera cuts play under the scroll (parent blocks only before a new block and before the terminal SceneChange); owns all N pages, bottom-up crawl, all glyphs at once, capture-pinned per-scene window/speed; engine scrolls it on a 60 fps sub-clock since the sim ticks at 100 Hz
  • FUN_8003C764 - on-screen text-balloon spawner (the 4C E1 op; handler FUN_801DA7F0 kills the predecessor); centers the line, fixes Y=180, seeds the per-page timer to 0x78 (120 frames) - not the opening crawl
  • FUN_8003C83C - field-VM cross-context resolver; 0xF8 → player / camera-anchor actor (_DAT_8007C364), 0xFB → system channel
  • FUN_801DB8EC - camera focus + projection setter (field overlay; disassembled from live overlay RAM in the new_game_cutscene_intro_a save, since the headless Ghidra dump truncates it). Calls FUN_801DAB90(transform_ptr, 0x801F3580) (builds the camera-param struct), sets the GTE H register from _DAT_8007B6F4 via func_0x8003D254 (= setCopControlWord(2,…)), and writes the focus globals _DAT_80089118/_DAT_80089120: when camera-mode byte DAT_8007B607>>4 == 5 the focus is tile-derived -(tile<<7)-0x40, otherwise anchor-follow -(anchor+0x14)/-(anchor+0x18). So this is the focus + FOV setter, not an eye-position build - the eye-back depth lives in the op-0x45 translation trio's slot 5 (_DAT_800840C0), not here (see cutscene op-0x45). Invoked by field-VM op 0x4C 0x39 and op 0x4C 0x3E.
  • FUN_801DBC20 - camera-zone config loader (resident camera cluster). Splits the 18-byte camera-region record's bytes[5..17] into the camera globals 0x8007B607..0x8007B627, keyed on the mode nibble byte[5] >> 4 (3 = look-at-anchor split, 5 = fixed-shot split, 6 = no-op, else the position-sweep split); consumed by the camera-param builder FUN_801DAB90 + follow-ease FUN_801DB510, query miss loads fixed defaults in FUN_801DBE9C. Full byte split: encounter § camera-region table.

Renderer + GTE

  • FUN_8002735C - Legaia 60-GTE TMD renderer; the landmark emit leaf reached from FUN_8001ADA4 case-5 for each landmark TMD in a kingdom pack
  • FUN_8001B964 - per-actor animated-mesh renderer: walks the actor's object table against its ANM record (actor[+0x4C]), posing TMD object i with bone i's R·v + T; skips the draw unless the object count equals the record's bone count
  • FUN_8001BE80 - per-(bone, frame) ANM entry decoder (3 nibble-packed signed-12-bit translations + 3 u8 rotations); ported at legaia_asset::player_anm::BoneTransform
  • FUN_80024D78 - actor object-table fill: actor[+0x44] = [count = *(tmd+8)][ptr to tmd+0xC+i*0x1C per object], straight off the pool TMD
  • FUN_80024E80 - screen-fade primitive spawn (fade_template, mode): allocates an actor from pool &DAT_80070674 via FUN_80020DE0, stores mode at actor[+0x18] and loads the fade ramp via FUN_80020B00 (start RGB + frame count << 6, per-frame deltas). The battle-action SM stages the summon backdrop fade (state 0x33) and the successful-escape white-out (state 0x66) through it. Port engine-core::fade::spawn_fade. See ghidra/scripts/funcs/80024e80.txt.
  • FUN_80043390 - Per-prim renderer dispatcher inside FUN_8001ADA4 case-5 (default textured-TMD path). Selects an emit leaf from one of two function-pointer tables (SCUS 0x8007657C vs world-map overlay 0x801F8968) based on _DAT_1F800394 & 1 - the overlay variant routes every prim through fog-enabled leaves at 0x801F7644..0x801F8690. This is the bulk world-map continent emit path.
  • FUN_8001ADA4 - Per-actor RENDER dispatcher (switch on actor[+0x56]); case-5 walks the mesh chain at actor[+0x44] and dispatches each TMD to FUN_80043390 (textured), FUN_80029888 (env-mapped), or FUN_8002735C (landmark); case-0xB is the ocean CLUT-walk emitter (banks acc += DAT_1F800393, fires a 16×1 MoveImage per kingdom-slot-5 table entry on acc >= hold, resets to zero)
  • FUN_8001D140 - Stack-swap wrapper around FUN_8001ADA4; called 6× per frame from FUN_80016444 against _DAT_8007C34C..0x36C (render pass)
  • FUN_8002519C - Per-frame actor-list TICK iterator; called 5× per frame (distinct from the render pass)
  • FUN_8002C69C - HUD / menu / dialog sprite-batch POLY_FT4 emitter (~120 prims/frame for world-map UI; not the continent)
  • FUN_80026B4C - TMD validate + register. Stores tmd_ptr at DAT_8007C018[DAT_8007B774++] then calls FUN_800268DC for the per-group descriptor fixup. Reached from FUN_8001F05C case 2 (TMD-pack) and case 9 (TMD2).
  • FUN_800268DC - TMD pointer fixup; builds the +0xC group descriptor array (0x1C-byte stride) consumed by FUN_80043390.
  • FUN_801F69D8 - World-map top-view tile-visibility dispatcher in overlay_world_map_top_ext.bin (643 instr, 2572 B). Walks per-tile visibility scratchpad and emits TMDs from DAT_8007C018 via FUN_80043390. In world-map warp-transition captures it is the live cluster-A caller (Drake read-breakpoint ra = 0x801F725C).
  • FUN_801D8280 - DAT_8007C018 table walker (overlay-resident). Iterates entries 0..DAT_8007BB38 and for each pointed-to TMD calls FUN_801D5E20 per sub-record.
  • 0x8007C018 - global TMD pointer table; installer-counter at 0x8007B774, walk-counter at 0x8007BB38. See formats / world-map-overlay - DAT_8007C018.
  • 0x80010778 - move-VM jump table
  • DAT_8007326C - per-mode primitive descriptor table
  • 0x801CE868 - move-VM extension JT
  • 0x800EB654 - battle context struct (pointed to by _DAT_8007BD24)

Battle

  • FUN_801E295C - battle action state machine (16 KB)
  • FUN_801D0748 - battle / level-up main tick; sub-states 0x1E/0x32/0x6E/0xFE
  • FUN_801D388C - battle animation dispatcher; switch on animation_type (0..0x31+)
  • FUN_801D5854 - battle per-pose camera/presentation driver (never writes the anim-id fields; the anim system is FUN_80047430FUN_8004AD80FUN_8004998C)
  • FUN_801D8DE8 - HUD element renderer; switches on elem_id (80-entry table) to draw each on-screen element (HP bars, Spirit names, hand-card portraits, victory banner). Shared by the battle HUD and Muscle Dome.
  • FUN_801DA6B4 - battle actor display-state controller; opacity + pose flags for alive actors
  • FUN_801DB81C - next-valid-target scan; actor[+0x14C] != 0 & no death/stone
  • FUN_801ED710 - records / stats screen renderer: nine label-heading rows - battles + escapes (cap 99999), play time (H:MM:SS), six per-character stat categories iterating the record at 0x80084140 + n*0x414 (+0x6B4/+0x6B0, +0x660/+0x664, +0x74D/+0x704), and a final averages row over the 0x801C6460 counters
  • FUN_800520F0 - battle scene loader; sequential state machine that pulls the befect_data cluster (etim texels, etmd effect models registered via FUN_80026B4C, vdf, efect.dat) and the PROT 871 effect-model library into DAT_8007C018[3..32]
  • FUN_80020050 - flame / effect-texture atlas loader (SCUS). Uploads PROT entry 0x366 (870 - the flame TIMs) into VRAM twice via FUN_8001fc00 (→ FUN_8003e8a8, the PROT-index/TOC resolver); the VRAM region is set up by FUN_80017888 / FUN_8001e54c (arg 0xf000). Gated on _DAT_8007b868 == 0. This is the VRAM blit site for PROT 870 - it is not loaded by the battle-bundle path FUN_800520F0 (which pulls 0x367..0x36d).
  • FUN_80026B4C - TMD validate + register (asserts magic 0x80000002); stores into the global TMD table DAT_8007C018
  • FUN_8004E2F0 - battle range / line-of-sight
  • FUN_80054CB0 - monster init
  • FUN_80042558 - per-frame stat aggregator
  • FUN_8003FB10 - action validator
  • FUN_800431D0 - ability bitmask read
  • FUN_8002CDD0 / FUN_8002C2E4 / FUN_8002C488 - battle status-HUD panel / per-member row / icon sprite drawer (the SCUS on-screen-element cluster; FUN_8002C2E4 pins the status-bitfield bit layout at char record +0x12E)
  • PROT 0967 - battle sparring-tutorial overlay, co-resident at 0x801F69D8 during the Tetsu fight (overlapping overlay 0898's rodata tail); drives the how-to-fight tutorial prompts
  • 0x801C9370 - 8-actor pointer table (party 0–2, monsters 3–7); actor HP at +0x14C

Menu overlay

  • FUN_801DD35C - per-frame title-overlay tick / top-level menu dispatcher (12 KB, 3 026 instr). Decrements the attract countdown at 0x801DDCCC (sw v0, -0xe94(a0)) by the per-frame scalar _DAT_1F800393; bgez branches to 0x801DFC3C while still counting, underflow writes _DAT_8007B83C = 0x1A (= STR FMV mode 26) and zeroes _DAT_8007BA78MV1.STR. Same fall-through as the field-VM 0x4C 0xE2 trigger at FUN_801E30E4. Pinned via PCSX-Redux watchpoint on the countdown; dump at ghidra/scripts/funcs/overlay_title_801ddccc.txt.
  • FUN_801D33D8 - per-character status / party panel renderer (Status / Magic / Moves / Skills tabs). Draws one member's page into a caller-supplied window rect (WX=*(i16*)(a0+0xa), WY=*(i16*)(a0+0xc)); content only, frame is caller-drawn. Dispatched as window descriptor 28 of the window table at 0x801E4738 (rect (90,16,218,188)). Submenu id DAT_801e46c0 & 0xfff folded to 0..5; shared string / icon / number primitives; CLUT-staging global DAT_8007b454 read only by the string primitive. Pixel-pinned layout: field menu.
  • window descriptor table 0x801E4738 (data, PROT 0899 file 0x15F20) - 52 x 0x10-byte records [content_id][park_edge][class][x,y,w,h][renderer VA]: the rect + content-renderer dispatch behind every pause-menu window. Renderers by id: 50 = FUN_801CFD68 (top-level command list), 49 = FUN_801D0148 (money/play-time box), 51 = FUN_801D030C (top-level party panel), 26/21 = FUN_801D2094 (party list), 27 = FUN_801D30A4 ("Condition" pager), 30 = FUN_801D31EC (summary), 22 = FUN_801D21C0 (equip main), 48 = FUN_801DCEF0 (options), 0..4 = title tabs. Live windows: 0x5C-stride linked structs, animated rect at +0xa (slide-out parking). Parser legaia_asset::menu_windows; details: field menu.
  • FUN_801E1C1C - shared menu-element renderer
  • FUN_801CF650 - equipment stat aggregator; reads 5 slots at char +0x196, sums into DAT_801EF08C–09C
  • FUN_801DD0C0 - item category / slot validity check

Audio (PsyQ)

  • FUN_8001FA88 - sound init / .dpk loader
  • FUN_8001FC00 - streaming-asset loader (sound\)
  • FUN_8001EBEC - equipment-conditional per-character TMD group patch (OBJECT 10/11 swap)
  • FUN_800243F0 - BGM lookup (PROT-relative)
  • FUN_8003E104 - monster.snd loader
  • 0x80061-0x80067 - libsnd SsAPI sequencer cluster
  • FUN_80068D94 - SsVabOpenHead core: validates the pBAV VAB magic, builds the program-number → packed-tone-page rank map into the ProgAtr +8 reserved words, allocates SPU memory, stashes per-VAG SPU addresses (the earlier "SsSepOpen / 'VAP' loader" reading is corrected from the disassembly - see do not re-walk). Engine port: engine-audio::VabBank::upload
  • 0x80068-0x6D - libspu SPU control cluster
  • FUN_80069B18 - SPU DMA transfer state machine

World map

  • FUN_801E76D4 - world-map controller (9320 B); top-view debug toggle + camera scroll/azimuth/zoom
  • FUN_801EAD98 - world-map developer menu renderer (7280 B); 24-entry scrollable list incl. MAP CHANGE / EVENT FLAG / PLAY POS / GET ITEM (display-only; the tools live in field overlay 0897). Row model + digit formatter ported at legaia_engine_vm::world_map_overlay; draws stay render-side
  • FUN_801EE094 / FUN_801EE328 (0897) - dev-menu warp appliers ("ON RIREMITO," / "ON RULA,"): map-table walk on _DAT_80084628, scene load via func_0x8001fd44, player teleport - the MAP CHANGE half of the debug "jump to beat" behaviour
  • FUN_801DBD04 (0897) - dev-menu EVENT FLAG editor: cursor DAT_801f2aa0 over flag ids 0..0xFFF, SET/CLEAR appliers at 0x801EA4F8/52C - the debug path that can set any story flag incl. the spine gates; also the register-pointer editor that produced the FMV trigger corpus
  • FUN_801EF014 (0897) - flag-window picker widget (descriptor via _DAT_8007B450, op-0x49-installed: +2 = first visible row, +3 = visible row count; confirm on a different row sets base + selection, re-picking the remembered current row or cancelling sets nothing). Disc usage (man-scripts --op49-window-census, 209 sites): the ONLY genuine flag-window family is kor/kor3/kor4 sub-4 base=0x138 count=8 (24 sites) - the Uru Mais warp-pad destination memory; no window contains a spine flag.
  • FUN_801F159C (0897) - system-actor dispatcher over the 52-entry handler table PTR_FUN_801f33b4, indexed by actor+0x50
  • FUN_801D2EBC (0897) - timed-flag scheduler consumer (installer = field-VM op 0x4C 0xD3); retail use: chitei2 escape timer, flag 0x4C7
  • FUN_801DA51C - per-entity tick (260 B); 5-state SM on entity[+0x8A]
  • FUN_801CFC40 - top-view sprite batcher (524 B, top-view only)
  • FUN_80016444 (SCUS, 1352 B) - per-frame render tick reached from FUN_80025EEC (default per-mode handler) or FUN_80025F2C (Mode 13 MAPDSIP); jal 0x801D7EA0 gated by _DAT_8007BC3C == 2
  • FUN_801D7EA0 - 832-byte POLY_FT4 batch emitter; one-shot gated by _DAT_801F351C; 224-iter cos-rotation loop
  • FUN_801D8258 - 40-byte gate setter; _DAT_801F351C = 1 + scale/step/OT-layer params
  • FUN_801D1344 (world_map overlay) + FUN_801C2B2C (0897 field overlay) - 1332-byte gate-arm wrapper (overlay relocation pair). Distinct from the dialog overlay's FUN_801D1344 (different code at the same RAM address).

BIOS + libapi

  • 0x80056678 - EnterCriticalSection
  • 0x80056688 - ExitCriticalSection
  • 0x80056658 - TestEvent
  • 0x8006B844 - WaitEvent
  • 0x80057024 - memmove
  • 0x8005ACAC - memset
  • 0x8005E540 - memcpy
  • 0x80056698+ stride 0x10 - BIOS B-vector thunk cluster

Full reference

Every function above (and ~50 more) is described in detail with provenance citations in the markdown doc:

docs/reference/functions.md →

Per-function dumps (Ghidra disassembly + decompile) live under ghidra/scripts/funcs/<addr>.txt in the repo (gitignored - Sony-derived). Overlay-specific dumps are prefixed overlay_<label>_<addr>.txt.

See also