# PSX JL-9 / Hidden Passcode Investigation This note is the compact fact-first summary for the active PlayStation `SLUS_002.68` investigation around `JL-2`, `JL-9`, hidden passcodes, the extra post-cheat weapon lane, the checked VRAM dump, and the checked 2 MiB main RAM dump. Natural in-level gate-arm event work now has its own companion note: [docs/psx/jl-9-in-level-event.md](k:\ghidra\Crusader_Decomp\docs\psx\jl-9-in-level-event.md). ## Executive summary ## Authored producer reachability correction pass (2026-04-12 live MCP) This pass pushed farther on the authored producer side across `psx_load_type_state_banks -> psx_object_create_simple_record -> psx_run_object_behavior_program_tick -> psx_object_behavior_opcode_dispatch -> psx_behavior_subopcode_dispatch -> psx_level_gate_slot_dispatch_from_action_record`. ### Strongest producer-context update 1. Loader/constructor provenance remains closed: - `psx_load_type_state_banks` (`0x8003917c`) installs `psx_type_simple_component_bank[type]` from level/LSET payload. - `psx_object_create_simple_record` (`0x800249f4`) seeds component `program_base/pc` from that bank. 2. Behavior tick guard was revalidated at instruction level: - `0x80026710` checks `(record_word0 - 1) < 0x0a`, which is arg-count bound. - opcode is loaded separately from `record_word1` (`lw a0,0x4(a3)`), so opcode `54` is not excluded by this guard. 3. Subdispatch linkage is therefore viable as authored-program context (not guard-blocked): - opcode table entry `54` at `0x80064284` points to `psx_behavior_subopcode_dispatch` (`0x80027ecc`), - subop table entry `49` at `0x800636d4` points to `psx_level_gate_slot_dispatch_from_action_record` (`0x800214ac`). ### Best current candidate producer context Strongest current candidate is still the **type-state behavior program record lane**: 1. simple-record object instance in late final-mission family (`map_id_to_gate_slot == 0x0f`) 2. component program emits opcode `54` 3. subop `49` frame resolves to sink `0x800214ac` 4. sink record bytes resolve to tuple `(slot,arg1,arg2)=(0x0f,0x0a,0x04)` This is now stronger than the old "high opcode not proven active" read, because that old read depended on misinterpreting `0x80026710` as opcode clamp. ### Authored-static vs runtime-remapped read (updated) Current strongest classification remains: 1. **authored-static source lane** at the behavior program record level, 2. with **optional runtime remap transport** via arg-mask/index resolution in `psx_object_behavior_opcode_dispatch`. Reason: arg resolver can pass direct pointers to record words (static bytes) or map arg words through slot-index tables (`base + index*4`) when mask bits are set. Exact mask choice of the emitting shipped record remains open. ### Live Ghidra changes applied in this pass Decompiler comments updated: - `0x80026710` (arg-count guard correction; opcode still independent) - `0x8002685c` (opcode table index semantics + entry 54 viability) - `0x80027f0c` (subop dispatch viability depends on authored records, not guard exclusion) No renames were applied in this pass. ## Synthesis-only conservative-stabilization pass (2026-04-12 live MCP) This pass was synthesis-only: no new lane expansion, only decision cleanup on what to stabilize in live naming/comments from the callback/countdown/progression clue set. ### Updated concise natural-event synthesis 1. Natural JL-9 arm remains a timing-sensitive in-level event lane, not a passcode-decoder-only effect. 2. `0x8002745c` and `0x80027548` remain indirect callback-table targets (no recovered direct callers), so they are transition/progression context, not standalone proven tuple emitters. 3. `0x80020794` remains world-frame countdown/control flow with map-54 boundary split (`<=54 -> 0x1a`, `>54 -> 0x1b`) and no recovered direct edge into slot-dispatch sink `0x800214ac`. 4. Best current miss model is unchanged: late transition/progression can consume the active window before slot-`0x0f` tuple `(0x0a,0x04)` reaches the arm write. ### Live Ghidra changes applied in this pass Decompiler comments were normalized to conservative evidence wording: - `0x8002745c` (`psx_control_callback_apply_level_preset_or_resume`): indirect callback-table target + branch split; explicitly marked not a standalone proven direct slot-`0x0f` feeder. - `0x80027548` (`psx_control_callback_apply_progression_target_level`): indirect callback-table target + progression-state timing lane; explicitly marked as non-proof of tuple emission by itself. - `0x80020794` (`psx_control_event_countdown_transition_tick`): world-frame countdown/control lane + explicit no-direct-edge statement to sink `0x800214ac`. No new renames were applied in this pass. ### Names intentionally left conservative 1. No tuple-specific rename for `0x8002745c` (insufficient direct feeder proof). 2. No deterministic gate-arm rename for `0x80027548` (supports timing influence, not guaranteed tuple production). 3. `0x800230e4` remains `psx_control_event_slot0f_handler` (slot-family scope retained; no narrowing to a single branch writer name). ## Progression latch timing pass (2026-04-12 live MCP) This pass revisited `psx_control_callback_apply_progression_target_level` (`0x80027548`) to decide whether map `54 -> 55` progression can preempt natural slot-`0x0f` tuple arm `(0x0a,0x04)`. ### Strongest new timing implication 1. `0x80027548` reads `psx_map_progression_table[current_map_id]` and writes it through `psx_level_session_set_next_map_id` (`0x8002ba84`, renamed this pass). 2. `psx_level_session_set_next_map_id` only stages deferred next-map latch `DAT_800678d0`; it does not immediately change `current_map_id`. 3. `psx_level_session_loop` commits `current_map_id <- DAT_800678d0` at rollover (`0x80031edc`) after inner world-frame loop exit. 4. Practical consequence: this is not a same-tick overwrite race, but it still creates a preemption window if transition logic exits the map-54 loop before slot-`0x0f` tuple branch `(0x0a,0x04)` is emitted. ### 54->55 miss-window read (updated) 1. `54 -> 55` remains the most likely natural miss window. 2. The key mechanism is deferred rollover ordering, not direct tuple clobber. 3. Countdown/control map split at `0x800208f0` (`<=54 -> 0x1a`, `>54 -> 0x1b`) plus staged progression target explains why natural tuple opportunity can disappear at the boundary. ### Live Ghidra artifacts applied in this pass Renames: - `0x8002ba84` -> `psx_level_session_set_next_map_id` Decompiler comments: - `0x80027560` - `0x8002ba84` - `0x80031edc` - `0x800208f0` ## Countdown-vs-slot frame-order closure pass (2026-04-12 live MCP) This pass targeted one question only: whether the map-54 countdown split around `0x800208f0` can plausibly suppress or precede natural slot-`0x0f` arm tuple `(0x0a,0x04)`. ### Strongest causality clue recovered Recovered world-frame order is now explicit in disassembly/decompile: 1. `0x8002b830`: `psx_control_event_countdown_transition_tick` 2. `0x8002b844`: `psx_run_live_object_behavior_callbacks` 3. `0x8002b864`: `psx_update_motion_and_nearby_interactions` 4. `0x8002b86c`: `psx_flush_deferred_control_queue` So countdown runs earlier in frame than the later behavior/deferred-control lanes that can eventually reach slot dispatch and slot-`0x0f` arm sink `0x800232f0`. ### Map-54 split relevance and branch read 1. At `0x800208f0`, countdown terminal handoff selects mode by map boundary: - `current_map_id <= 54` -> `0x1a` - `current_map_id > 54` -> `0x1b` 2. At `0x80020900`, countdown immediately applies `psx_control_set_mode_and_reset_runtime_flags`, writing shared control globals (`DAT_800675e4`, `DAT_80067379`). 3. Slot-`0x0f` arm branch `(0x0a,0x04)` in `psx_control_event_slot0f_handler` also writes the same control-mode family before/with gate write. ### Updated relevance ranking 1. **Boundary timing is most relevant** (strongest): early countdown split+mode write can narrow later slot-event windows in the same/next frame. 2. **Failure-side suppression is plausible but secondary**: no direct countdown->slot call edge was recovered, but shared mode-state writes can still preempt tuple opportunity. 3. **Pure countdown-success branch is weakest**: no recovered success-path edge deterministically forcing tuple `(0x0f,0x0a,0x04)`. ### Live Ghidra artifacts applied in this pass No renames in this pass. Decompiler comments added: - `0x8002b830`: frame-order anchor (countdown before behavior/deferred lanes) - `0x80020900`: boundary handoff writes shared mode/state before later slot lanes - `0x800232f0`: slot-`0x0f` arm sink is later lane than countdown tick ## Slot-0x0f subcase taxonomy pass (2026-04-12 live MCP) This pass classified `psx_control_event_slot0f_handler` (`0x800230e4`) subcases beyond tuple `(0x0a,0x04)` to narrow natural JL-9 semantics without overfitting a single folklore path. ### Compact taxonomy (slot `0x0f` family) 1. `param2=0x0a, param3=0x01/0x02/0x03` - **Transition/objective-state setup** (not direct reward grant). - Evidence: each branch writes shared control mode/timer state (`DAT_80067340/44`), plays a CD-XA cue (`0x1a/0x1b/0x1d` via `0x80049014`), and clears per-event runtime latches. - `param3=0x03` additionally sets `DAT_80067354=1`. 2. `param2=0x0a, param3=0x2e` - **Opcode-stream transition/reset** lane. - Evidence: switches opcode stream index (`psx_control_assign_opcode_stream_by_index(...,1)`) and performs the same latch-clear pattern. 3. `param2=0x01, param3=0x01` - **Countdown/objective-pressure arm** lane. - Evidence: writes `DAT_8006734d/4e/4f`, which are read by `psx_control_event_countdown_transition_tick` and `psx_draw_clock_digits_overlay`. 4. `param2=0x04, param3=0x01` - **Per-level message/objective-notice** lane. - Evidence: loads text from `DAT_8006754c + index`, sets timer/control globals, and drives shared countdown-style object iteration side effects. 5. `param2=0x06, param3=0x42` - **Type-gated control transition** lane. - Evidence: writes only global mode latches (`DAT_800675e4`, `DAT_80067379`, `DAT_80067350`) and reset helpers; no constructor/resource-bind call. 6. `param2=0x0a, param3=0x04` (anchor case) - **Eligibility arm latch** for hidden follow-up, not immediate reward. - Evidence: only this subcase writes `psx_debug_extra_channel_gate` (`0x800232f0`) under `psx_hidden_passcode_flag==0 && psx_level_runtime_header_state==3`. ### Implication for `(0x0a,0x04)` The surrounding slot-`0x0f` family is dominated by transition/countdown/objective-state control lanes and messaging, not direct reward payout handlers. That makes `(0x0a,0x04)` best interpreted as a **late objective-state eligibility arm** inside that control family, rather than a standalone reward/failure terminal event. ### Live Ghidra artifacts applied in this pass Renames: - `0x80049014` -> `psx_audio_cdxa_select_and_play_cue` Decompiler comments added: - `0x80023154` (slot0f `0x0a/1` transition setup) - `0x800231b0` (slot0f `0x0a/2` countdown/objective-state setup) - `0x8002321c` (slot0f `0x0a/3` sibling objective-state branch) - `0x80023334` (slot0f `0x0a/0x2e` opcode-stream reset lane) - `0x80023390` (slot0f `0x01/1` countdown-overlay/objective-pressure lane) - `0x800236d8` (slot0f `0x04/1` level-message objective cue lane) - `0x800237cc` (slot0f `0x06/0x42` type-gated control transition lane) ## Transition callback provenance pass (2026-04-11 live MCP) This pass traced the newly recovered transition callbacks at `0x8002745c` and `0x80027548` to determine provenance, state effects, and impact on the slot-`0x0f` tuple lane. ### Provenance (best current) 1. Neither callback has a recovered direct caller xref (`get_callers` returns none for both entries). 2. Both are data-referenced from a contiguous function-pointer region near `0x800641f0..0x80064220`: - `0x80064200 -> 0x8002745c` - `0x80064210 -> 0x80027548` 3. Current best classification is therefore **indirect callback-table dispatch** from an unresolved transition/control dispatcher, not direct static callsites. ### State effects recovered 1. `0x8002745c` (`psx_control_callback_apply_level_preset_or_resume`): - if `DAT_80078a14 == 0`, it calls `psx_control_event_apply_level_channel_preset` (`0x80020f7c`) then `psx_control_reset_runtime_flag_67780`. - else it takes an alternate resume lane (`FUN_80044074`) and sets `DAT_800673c4 = 1`. 2. `0x80027548` (`psx_control_callback_apply_progression_target_level`): - computes `next = psx_map_progression_table[current_map_id]` and applies it via `FUN_8002ba84`. ### Slot-0x0f tuple carry/suppress read 1. `0x8002745c` can **carry** tuple opportunity when it takes the `DAT_80078a14 == 0` branch because that branch executes `0x80020f7c` (the known level/channel apply helper in this late control lane). 2. The alternate branch in `0x8002745c` can **suppress or defer** tuple opportunity for that tick because it skips `0x80020f7c` and takes resume/state handling instead. 3. `0x80027548` increases **timing sensitivity**: it advances map progression before later control-event handling, so slot-`0x0f` tuple `(0x0a,0x04)` only carries if emitted after/within the progressed state window; otherwise progression can outrun the arm branch. 4. For map `54 -> 55`, this is not a simple family exit (both are still slot-`0x0f` family), but it is a real state-transition step that can change event ordering and reduce deterministic tuple timing. ### Live Ghidra artifacts applied in this pass Comments added: - `0x8002745c`: table-dispatch provenance + carry/suppress branch semantics. - `0x80027548`: progression callback provenance + timing-sensitive tuple implication. - `0x80064200`: callback-table-region note for `0x8002745c` entry. - `0x80064210`: callback-table-region note for `0x80027548` entry. No additional renames were applied in this pass (existing callback names remained conservative and evidence-backed). ## Multi-map last-mission chain check (2026-04-11 live MCP) This pass tested the specific hunch that the last mission is split across multiple maps and that this split explains why the JL-9 gate-arm family is level-grouped (`54/55/56/57/58`) instead of single-map. ### Fresh table evidence (live bytes) Recovered directly from active `SLUS_002.68` memory tables: 1. Selector anchor into the family: - `psx_selector_to_map_id_table[0x0f]` at `0x80063e63` is `0x36` (map `54`). 2. Gate-slot family closure for the target chain: - `psx_map_id_to_gate_slot_table[54..58]` at `0x80063e9e..0x80063ea2` is `0x0f,0x0f,0x0f,0x0f,0x0f`. 3. Progression chain closure: - `psx_map_progression_table[54..58]` at `0x80063ee2..0x80063ee6` is `0x37,0x38,0x39,0x3a,0xff` (`55,56,57,58,terminal`). Net result: this is a concrete contiguous progression chain that stays inside one gate-slot family (`0x0f`) until terminal progression marker `0xff`. ### Control/transition helper evidence for split-flow behavior 1. `psx_control_event_countdown_transition_tick` (`0x80020794`) has an explicit map boundary at `0x800208f0`: - `current_map_id <= 54` uses mode `0x1a` - `current_map_id > 54` uses mode `0x1b` 2. `psx_control_event_slot0e_handler` has a late feeder branch at `0x80023074` that writes selector `0x0f` then calls `psx_control_event_apply_level_channel_preset`. 3. `psx_control_event_apply_level_channel_preset` reads both: - `psx_map_progression_table[current_map_id]` (`0x80020fa4`) - `psx_map_id_to_gate_slot_table[current_map_id]` (`0x80020fbc`) This supports a staged final-mission flow where late transition/control logic can traverse `54..58` while remaining in the same slot-`0x0f` gate family. ### Best reconstruction (current) 1. Normal passcode selector path anchors entry at map `54` (`selector 0x0f -> map 54`). 2. Late control/transition logic advances through progression chain `54 -> 55 -> 56 -> 57 -> 58`. 3. Across that chain, gate dispatch stays in slot family `0x0f`. 4. Terminal progression marker at map `58` (`0xff`) indicates end-of-chain behavior rather than another ordinary progression handoff. ### JL-9 implication update This strengthens, not weakens, the `level 54` natural-host read: 1. `54` remains the only member with direct selector anchor from the recovered normal passcode path. 2. `55..58` are now better interpreted as downstream split-phase transition maps in the same family. 3. The family-wide slot-`0x0f` mapping now looks like deliberate multi-map mission staging, which explains why JL-9 gate logic is host-family scoped while still leaving `54` as the strongest reproducible natural entry. ### Live Ghidra artifacts applied in this pass Disassembly comments added: - `0x80063e63` (selector `0x0f` -> map `54` anchor) - `0x80063e9e` (map `54` slot-`0x0f` family start) - `0x80063ea2` (map `58` slot-`0x0f` terminal-edge note) - `0x80063ee2` (progression `54 -> 55` chain start) - `0x80063ee6` (progression `58 -> 0xff` terminal marker) - `0x800208f0` (map-54 boundary split `0x1a/0x1b`) - `0x80023074` (slot-`0x0e` late feeder back into selector `0x0f` family) - `0x80020fa4` (progression-table read and `54..58` staged flow note) ## Level-54 boundary clue pass (2026-04-11 live MCP) This pass focused on one narrow question: why natural `MFM4` can still miss even when map `54` remains the best host anchor. ### Strongest level-54-specific clue `psx_control_event_countdown_transition_tick` (`0x80020794`) has an explicit map boundary at `0x36` (54): - at terminal countdown handoff, it chooses control mode code `0x1a` when `current_map_id <= 0x36` - and chooses `0x1b` when `current_map_id > 0x36` This is a concrete level-54 split in late control flow, not a generic family-level observation. Supporting progression/control evidence recovered in the same pass: - `psx_control_callback_apply_progression_target_level` (`0x80027548`) applies `psx_map_progression_table[current_map_id]` via `FUN_8002ba84` - for map `54`, that progression target is `55` - `psx_level_gate_slot05_handler` (`0x80021fac`) has tuple branch `(param2=0x0a,param3=0x28)` that calls `psx_control_event_apply_level_channel_preset` and reset helpers, giving an optional late state-advance lane before slot `0x0f` `(0x0a,0x04)` arm may fire ### Practical interpretation for natural MFM4 failures Current best read is now sharper: 1. `MFM4` still correctly primes the strongest known host (`selector 0x0f -> map 54`, `runtime_header_state=3`). 2. Failure is most likely a timing/optional-path miss inside late control-event progression around the map-54 boundary split, not wrong-host decode. 3. The rare miss model is: a transition/control branch (including map-54-specific `0x1a` lane and optional slot05 branching) can advance or reroute state before slot `0x0f` `(0x0a,0x04)` writes `psx_debug_extra_channel_gate`. ### Live Ghidra artifacts applied in this pass Renames: - `0x80020794` -> `psx_control_event_countdown_transition_tick` - `0x800205e8` -> `psx_control_event_apply_countdown_step` - `0x80020d54` -> `psx_control_set_mode_and_reset_runtime_flags` - created `0x8002745c` -> `psx_control_callback_apply_level_preset_or_resume` - created `0x80027548` -> `psx_control_callback_apply_progression_target_level` Comments: - `0x800208f0`: level-54 boundary (`<=54 -> 0x1a`, `>54 -> 0x1b`) and timing implication - `0x80022068`: slot05 `(0x0a,0x28)` optional late-event/preset lane note - `0x80027560`: progression callback note (`next = map_progression_table[current_map_id]`) ## Slot-handler sibling recovery around table 0x800640a0 (2026-04-11 live pass) Focused scope for this pass was the still-raw slot-handler siblings referenced by `psx_level_gate_slot_handler_table` (`0x800640a0`), with emphasis on whether any `0x0a` sibling branch competes with or narrows tuple `(0x0a,0x04)` in slot `0x0f`. ### Recovered sibling handler roles (table entries) Newly created and named from table boundaries (address range split by next table entry): - `0x800215fc` -> `psx_level_gate_slot01_handler` - `0x80021810` -> `psx_level_gate_slot02_handler` - `0x800219e4` -> `psx_level_gate_slot03_handler` - `0x80021fac` -> `psx_level_gate_slot05_handler` - `0x80022214` -> `psx_level_gate_slot06_handler` - `0x800222e8` -> `psx_level_gate_slot07_handler` - `0x800223cc` -> `psx_level_gate_slot08_handler` - `0x800226e0` -> `psx_level_gate_slot09_handler` - `0x800227ac` -> `psx_level_gate_slot0a_handler` - `0x80022b50` -> `psx_level_gate_slot0c_handler` - `0x80023854` -> `psx_level_gate_slot00_handler` - `0x80023af0` -> `psx_level_gate_slot10_return_true` Existing slot siblings kept/updated: - `0x80022c6c` = `psx_control_event_slot0d_handler` - `0x80022ea8` = `psx_control_event_slot0e_handler` - `0x800230e4` = `psx_control_event_slot0f_handler` - `0x80022940` renamed to `psx_level_gate_slot0b_control_pair_handler` - table entry still points to `0x8002293c` (first prologue instruction), now preserved via comment. ### New interpretation of the 0x0a subcase family Current strongest narrowing is now sibling-explicit across slots `0x0a..0x0f`: 1. Slot `0x0a` (`0x800227ac`) has `(0x0a,0x01..0x03)` control/timer branches and does not write `psx_debug_extra_channel_gate`. 2. Slot `0x0b` (`0x80022940`) `(0x0a,0x02)` sets policy bit `DAT_80078a88 |= 0x0400`, but is not a gate-byte writer. 3. Slot `0x0c` (`0x80022b50`) `0x0a` family is control/message gated by `DAT_80078a88 & 0x200`, with no recovered gate-byte write. 4. Slot `0x0d` (`0x80022c6c`) `(0x0a,0x02)` is mission-complete passcode text lane (`quad index 0x0e`). 5. Slot `0x0e` (`0x80022ea8`) `(0x0a,0x01)` is mission-complete passcode text lane (`quad index 0x0f`); `(0x0a,0x06)` is late selector/apply transition. 6. Slot `0x0f` (`0x800230e4`) `(0x0a,0x04)` remains the only recovered sibling in this family that can arm `psx_debug_extra_channel_gate` (write at `0x800232f0`) under non-hidden + header-state-3 predicates. So `(0x0a,0x04)` is now narrower than before: it is not just “one 0x0a case in slot0f”, it is the only recovered 0x0a-family sibling across adjacent slot handlers that actually sets the JL-9 gate byte. ## 0x0a control-family semantic closure (2026-04-11 live pass) This pass was restricted to the late control family around: - `0x80022c6c` (`psx_control_event_slot0d_handler`) - `0x80022ea8` (`psx_control_event_slot0e_handler`) - `0x800230e4` (renamed this pass to `psx_control_event_slot0f_handler`) - `0x80020f7c` (`psx_control_event_apply_level_channel_preset`) ### Best semantic classification for `(0x0a,0x04)` Current best read is **control-event eligibility arm (pre-hidden gate latch)**, not a reward payload by itself. Evidence: 1. `(0x0a,0x04)` is in slot `0x0f` handler and writes `psx_debug_extra_channel_gate` only under: - `psx_hidden_passcode_flag == 0` - `psx_level_runtime_header_state == 3` 2. The write is a single byte latch (`sb`) at `0x800232f0`, then consumed later by the hidden/debug grant lane at `0x8002fff4`. 3. Sibling `0x0a` cases in slot `0x0d`/`0x0e` are mission-complete text/transition/control setup branches, which places `(0x0a,0x04)` inside a broader control progression family rather than a standalone "grant now" event. ### Sibling-case comparison table (`param_2 == 0x0a`) | Handler slot | Address | Subcase (`param_3`) | Strongest semantics | Direct effect | |---|---|---|---|---| | `0x0d` | `0x80022c6c` | `0x02` | mission-complete passcode text branch | generates encoded quad index `0x0e`, writes congratulations text | | `0x0e` | `0x80022ea8` | `0x01` | mission-complete passcode text branch | generates encoded quad index `0x0f`, writes congratulations text | | `0x0e` | `0x80022ea8` | `0x02..0x04` | transition/setup control branches | mode/timer/runtime control setup | | `0x0e` | `0x80022ea8` | `0x06` | late selector/channel transition | sets selector `0x0f`, calls `psx_control_event_apply_level_channel_preset` and `psx_passcode_apply_mission_selector_and_stage` | | `0x0f` | `0x800230e4` | `0x01..0x03` | sibling control/event setup | runtime/control side effects without gate-byte arm | | `0x0f` | `0x800230e4` | `0x04` | **pre-hidden eligibility arm latch** | conditional write `psx_debug_extra_channel_gate = 1` at `0x800232f0` | Net: `(0x0a,0x04)` is best classified as a **control-arm/preset-eligibility latch** for the later hidden completion path, not as direct reward completion. ## Sink-side feeder closure (2026-04-11 live pass) This pass was restricted to the sink-side feeder and argument sourcing around `0x800214ac..0x800215f8`. ### Recovered action-record dispatch structure Recovered function body is now promoted in live Ghidra as: - `0x800214ac` -> `psx_level_gate_slot_dispatch_from_action_record` Observed layout used by this dispatcher: - `record+0x00` -> pointer to slot byte (`slot = *(*(record+0x00))`) - `record+0x08` -> pointer to arg1 byte (`arg1 = *(*(record+0x08))`) - `record+0x0c` -> pointer to arg2 byte (`arg2 = *(*(record+0x0c))`) Dispatch mechanics: - level gate compare uses `psx_map_id_to_gate_slot_table[current_map_id]` (`0x80063e68`) - indirect call uses `psx_level_gate_slot_handler_table[slot]` (`0x800640a0`) - slot `0x0f` entry points to `0x800230e4` (`psx_set_debug_extra_channel_gate`) at table address `0x800640dc` JL-9 gate-arm tuple remains explicit and unchanged: - slot `0x0f` - `arg1 = 0x0a` - `arg2 = 0x04` which reaches the gate-byte write branch at `0x800232f0`. ### Upstream producer status (what is proven) Proven table topology into the sink feeder: - `psx_behavior_opcode_handler_table[54] = 0x80027ecc` (`psx_behavior_subopcode_dispatch`) at `0x80064284` - `psx_behavior_subop_handler_table[49] = 0x800214ac` (`psx_level_gate_slot_dispatch_from_action_record`) at `0x800636d4` Current proof boundary remains the same: - known gameplay caller lane into `psx_object_behavior_opcode_dispatch` still enforces `(opcode_word-1) < 0x0a` at `0x80026710` - therefore `54 -> 49 -> 0x800214ac` is retained as proven table topology, not yet proven active on the currently recovered caller path ## Host-level family closure for natural JL-9 gate-arm (2026-04-11 live pass) Scope of this pass was restricted to the host-side family `{54,55,56,57,58,82}` with direct MCP evidence for: - selector mapping (`psx_selector_to_map_id_table`, `0x80063e54`) - level/map gate-slot mapping (`psx_map_id_to_gate_slot_table`, `0x80063e68`) - progression/completion transitions (`psx_map_progression_table`, `0x80063eac`, plus `FUN_80020f7c` callers) ### Exact mapping closure used for ranking 1. Published normal-code selector closure still anchors on selector `0x0f`: - `psx_selector_to_map_id_table[0x0f] = 0x36` (map id `54`) 2. Slot-family closure for natural gate-arm dispatch: - `psx_map_id_to_gate_slot_table[54] = 0x0f` - `psx_map_id_to_gate_slot_table[55] = 0x0f` - `psx_map_id_to_gate_slot_table[56] = 0x0f` - `psx_map_id_to_gate_slot_table[57] = 0x0f` - `psx_map_id_to_gate_slot_table[58] = 0x0f` - `psx_map_id_to_gate_slot_table[82] = 0x0f` 3. Reciprocal passcode-validation behavior in caller flow around `0x80034d60..0x80034d7c` remains strongest for map `54`: - map id `54` round-trips through `slot 0x0f -> map 54` by reverse lookup - sibling family members (`55..58,82`) share slot `0x0f` but do not have the same direct selector round-trip anchor in recovered normal passcode tables ### Late objective/completion hosting evidence `FUN_80020f7c` remains the strongest late transition hub in this lane. Live callers include: - `0x80022068` - `0x80022e58` - `0x80023080` - `0x8002748c` and the helper itself reads both: - `psx_map_progression_table[current_map_id]` (`0x80020fa4`) - `psx_map_id_to_gate_slot_table[current_map_id]` (`0x80020fbc`) Progression bytes for the host family are now explicit: - map `54 -> 0x37` (`55`) - map `55 -> 0x38` (`56`) - map `56 -> 0x39` (`57`) - map `57 -> 0x3a` (`58`) - map `58 -> 0xff` (terminal marker in this table) - map `82 -> 0x53` (`83`) This keeps a completion-timing explanation live: transition-heavy late events can move execution across the same slot-`0x0f` family, but not all members are equally stable for deterministic player replication. ### Ranked host levels/maps (current best) 1. **Map/level 54** (best host) - only family member directly anchored by recovered normal selector path (`0x0f -> 54`) - satisfies both prime-side and slot-family-side constraints in one static route 2. **Map/level 55** - immediate progression successor of 54 and still in slot-`0x0f` family - plausible if gate-arm event is late-transition-bound rather than early-in-map 3. **Map/level 56** - same slot family, but one transition further away from direct passcode anchor 4. **Map/level 57** - same slot family, lower reproducibility due to additional transition depth 5. **Map/level 82** - valid slot-family host (`0x0f`) but weak for this specific user scenario because no direct published selector anchor was recovered 6. **Map/level 58** - slot-family valid, but progression entry marks terminal (`0xff`) and is therefore most likely to race/exit through completion paths rather than offer stable pre-hidden gate-arm timing ### Best current explanation for failed natural MFM4 After the failed natural `MFM4` trial, the best-supported explanation remains event-host/timing failure, not decode failure: 1. `MFM4` still fits the strongest static prime (`runtime_header_state==3`, selector family leading to map `54`). 2. The missing piece is still the in-level/control dispatch that must reach slot `0x0f` with tuple `(0x0a,0x04)` before hidden-mode trigger. 3. Completion/transition hubs (`FUN_80020f7c` callers) can move state through family members and potentially past the required gate-arm moment. 4. So a natural run can fail even with correct passcode if: - the specific tuple event never fired, - it fired outside the required hidden-flag polarity window, - or progression/completion moved the run into a less stable timing state before hidden-input trigger. Net result for this pass: **map 54 remains the best host candidate**, but `MFM4` alone is not expected to be sufficient without the concrete in-level event timing. ## Late-objective / mission-complete clue pass (2026-04-11, live MCP) Focused this pass only on concrete late-objective and reward-event clues that can naturally arm the JL-9 gate path (`psx_debug_extra_channel_gate`), with emphasis on mission-complete text handlers, transition siblings, and uncommon branch outcomes. ### Hard evidence (high confidence) 1. Mission-complete congratulations strings are anchored at: - `0x80063f10`: congrats template with next-passcode tail. - `0x80063f74`: shorter congrats variant. 2. Two adjacent control-event handlers are concrete mission-complete/passcode writers: - `0x80022c6c` (`DAT_800640a0[0x0d]`): slot-`0x0d` branch generates encoded passcode index `0x0e` and writes congrats text. - `0x80022ea8` (`DAT_800640a0[0x0e]`): slot-`0x0e` branch generates encoded passcode index `0x0f` and writes congrats text. 3. Slot-`0x0e` has a concrete late-transition branch that force-loads selector `0x0f`: - at `0x80023040` (`param2=0x0a,param3=6` branch), code sets `DAT_80078a8c=0x0f` and calls `psx_passcode_apply_mission_selector_and_stage`. 4. Selector/channel mapping remains decisive for natural gate-arm routing: - `selector 0x0f -> level 0x36 (54) -> DAT_80063e68[level]=0x0f`. - in sampled selector range, this is the only selector that maps directly into channel family `0x0f`. 5. Natural gate-arm write remains bounded to slot-`0x0f` case `(param2=0x0a,param3=4)`: - write at `0x800232f0` sets `DAT_8006739d` only if `psx_hidden_passcode_flag==0` and `psx_level_runtime_header_state==3`. ### Text-display siblings and uncommon branch clues 1. Slot-`0x0f` text-display sibling at `0x800236dc` (`param2=4,param3=1`) calls `ui_message_set_active_text(DAT_8006754c + idx)`. - This is resource-table driven, distinct from hardcoded congratulations templates. - It is a strong clue for optional scripted outcomes/messages tied to level-authored text resources. 2. Slot-`0x0e` uncommon outcomes include: - `param3=5`: writes `DAT_80078a10=2` (state-only branch). - `param3=6`: direct selector apply to `0x0f` (late transition behavior, not ordinary text-only flow). ### Strongest natural late-objective candidate events from this pass 1. `slot 0x0e` mission-complete transition branch (`0x80022ea8`, `param2=0x0a,param3=6`) as the clearest upstream feeder into selector/channel `0x0f` family. 2. `slot 0x0f` event branch (`0x800230e4`, `param2=0x0a,param3=4`) as the proven natural JL-9 gate-arm writer under non-hidden + runtime-state-3 predicates. 3. `slot 0x0f` text-resource branch (`0x800236dc`, `param2=4,param3=1`) as the strongest sibling clue for uncommon/optional scripted objective messaging that likely co-occurs with late transition states. ### What is still speculation 1. Exact player-visible authored event sequence inside the level (the concrete map script moment) that emits `slot 0x0f` with `(0x0a,4)` remains unclosed. 2. Any claim that one specific congratulation screen alone implies gate-arm is still speculative; current proof is branch-level and table-level, not full authored mission-script closure. ## 0x0a family case-map closure around natural JL-9 gate arm (2026-04-11 live pass) This pass was restricted to the control-event family around `0x80022c6c..0x80023390` with live MCP decompile/disassembly and conservative in-database naming. ### Recovered sibling handlers and table roles 1. Slot-handler table bytes at `0x800640d4/0x800640d8/0x800640dc` confirm sibling entries: - slot `0x0d` -> `0x80022c6c` - slot `0x0e` -> `0x80022ea8` - slot `0x0f` -> `0x800230e4` (`psx_set_debug_extra_channel_gate`) 2. Sink dispatch block `0x800214ac..0x800215f8` remains the slot-gated tuple sink: - compare slot byte with `DAT_80063e68[current_level]` - if equal, call `DAT_800640a0[slot]` with tuple `(param_2,param_3)` loaded from record-byte pointers. ### Concrete 0x0a family mapping (best current) For these three sibling handlers, `param_2 == 0x0a` is the family selector and `param_3` is the subcase index. 1. Slot `0x0d` handler (`0x80022c6c`): - `(0x0a,0x02)`: generates passcode quad index `0x0e`, writes encoded chars to `0x80063f6e..0x80063f71`, shows congratulations text. 2. Slot `0x0e` handler (`0x80022ea8`): - `(0x0a,0x01)`: same mission-complete passcode lane but quad index `0x0f`. - `(0x0a,0x02..0x04)`: mode/timer setup branches. - `(0x0a,0x06)`: applies mission selector and stage. 3. Slot `0x0f` handler (`0x800230e4`): - `(0x0a,0x01..0x04,0x2e)` switch family. - critical branch `(0x0a,0x04)` at `0x800232f0`: sets `psx_debug_extra_channel_gate=1` only when `psx_hidden_passcode_flag==0` and `psx_level_runtime_header_state==3`. ### Interpretation of tuple (0x0a,0x04) Strongest conservative interpretation is unchanged but now better bounded: this is the natural pre-hidden arm event branch in the slot-`0x0f` family, sibling to mission-complete/control subcases in slots `0x0d/0x0e/0x0f`, and it is the direct writer path for the later hidden debug grant extra-lane check. Practical consequence: 1. `(0x0a,0x04)` is not an isolated special; it belongs to a coherent control-event sibling family. 2. Its placement next to mission-complete style tuple branches strengthens the read that the missing natural trigger is an in-level authored control-event emission, not a separate passcode-screen operation. ## Natural in-level event synthesis and conservative naming sweep (2026-04-11 live pass) This pass was restricted to the natural in-level gate-arm event lane for JL-9, not the broader hidden/debug chain. ### Event-only synthesis (current best conservative read) 1. Natural arm still centers on in-level control/event dispatch into the slot-gated sink block at `0x800214ac..0x800215f8`. 2. Inside that sink, current-level family is checked against the slot index and then dispatches through slot-handler table entry `slot 0x0f -> 0x800230e4` (`psx_set_debug_extra_channel_gate`). 3. Actual gate write remains constrained to tuple branch `(param_2 == 0x0a, param_3 == 0x04)` at `0x800232f0`, plus existing non-hidden/header-state predicates. 4. Upstream behavior-opcode topology (`opcode 54 -> subop 49 -> sink`) is still structurally supported by table links, but active reachability from the only proven gameplay caller lane remains unproven because the recovered guard at `0x80026710` bounds the known lane to `(opcode_word-1) < 0x0a`. ### Live Ghidra conservative rename/comment sweep applied Renamed data/labels (only still-raw central entities): - `0x800640a0`: `PTR_LAB_800640a0` -> `psx_level_gate_slot_handler_table` - `0x800641ac`: `PTR_LAB_800641ac` -> `psx_behavior_opcode_handler_table` - `0x80063610`: `PTR_LAB_80063610` -> `psx_behavior_subop_handler_table` - `0x800214ac`: `LAB_800214ac` -> `psx_level_gate_slot_dispatch_block_800214ac` - `0x80027ecc`: `LAB_80027ecc` -> `psx_behavior_subop_dispatch_block_80027ecc` Added disassembly comments (evidence/uncertainty preserving): - `0x800215dc`: level-family compare + slot-table indirect-call behavior (slot `0x0f` sink relevance). - `0x800232f0`: exact gate-arm tuple and predicate reminder. - `0x8002685c`: opcode-table dispatch note plus proven caller-lane bound reminder. - `0x80027f0c`: sub-op table dispatch note with explicit reachability uncertainty. - `0x800214ac`: event-sink dispatch-role note. ### Unresolved on purpose - Exact player-visible authored in-level event that naturally emits the `(0x0f, 0x0a, 0x04)` sink tuple. - Proof of an additional active caller/context that can reach high behavior-opcode entries beyond the known `< 0x0a` lane. - Any stronger semantic name for `0x80027ecc` than conservative sub-op dispatch block labeling. ## Event-only synthesis continuation (2026-04-11 live pass) Scope of this continuation pass was intentionally narrow: keep sink-side natural-event naming durable without promoting unproven upstream producer semantics. ### Updated concise synthesis (event-only) 1. Natural JL-9 pre-hidden arm remains a slot-family control-event branch, not a passcode-screen branch. 2. Sink path is unchanged and still strongest: `psx_level_gate_slot_dispatch_from_action_record` (`0x800214ac`) validates current-level family, then dispatches through `psx_level_gate_slot_handler_table[slot]`. 3. Slot `0x0f` resolves to `psx_control_event_slot0f_handler` (`0x800230e4`), where tuple `(0x0a,0x04)` at `0x800232f0` conditionally writes `psx_debug_extra_channel_gate` only under `hidden==0 && runtime_header_state==3`. 4. Upstream `opcode 54 -> subop 49 -> sink` remains table-valid topology, but active gameplay reachability is still unproven past the known caller lane bounded by `(opcode_word-1) < 0x0a`. ### Exact live Ghidra artifacts changed in this continuation pass Renamed function: - `0x800230e4`: `psx_set_debug_extra_channel_gate` -> `psx_control_event_slot0f_handler` Renamed table-entry labels: - `0x800640d4` -> `psx_level_gate_slot_handler_slot0d_entry` - `0x800640d8` -> `psx_level_gate_slot_handler_slot0e_entry` - `0x800640dc` -> `psx_level_gate_slot_handler_slot0f_entry` Added/updated comments: - `0x800230e4`: slot-`0x0f` family role comment; gate write classified as one subcase. - `0x800232f0`: exact natural gate-arm tuple + predicate comment. - `0x800640dc`: slot-`0x0f` table-entry comment with tuple anchor. - `0x80064284`: opcode-table entry `54` comment preserving topology-vs-reachability split. - `0x800636d4`: subop-table entry `49` comment preserving topology-vs-reachability split. ### Deliberately unresolved / refused names 1. Refused rename: `psx_behavior_subopcode_dispatch` (`0x80027ecc`) to any stronger event-producer name. - Why refused: no direct recovered caller currently proves active high-index opcode reachability on gameplay path. 2. Refused rename: `psx_level_gate_slot_dispatch_from_action_record` (`0x800214ac`) to mission-specific or JL-9-specific wording. - Why refused: sink dispatch behavior is generic slot-family machinery beyond JL-9. ## Published mission code sweep closure (2026-04-11 live pass) Focused live pass on the user-supplied ordinary PSX mission code table was used to answer one narrow question: does any published non-hidden mission code already satisfy the static JL-9 preconditions, or do they all fail before the hidden/debug stage? ### Exact decode-space closure 1. Ordinary passcode rows are decode indices `i = 0x00..0x0e`. 2. Special passcodes are the next three rows: - `i = 0x0f` => `?RTN` family - `i = 0x10` => `?0SR` family - `i = 0x11` => `?QQQ` family 3. For ordinary rows, selector space is offset by one: - ordinary decoded row `i` returns selector `s = i + 1` 4. First-character difficulty only affects `psx_level_runtime_header_state`; it does **not** choose the selector/current-level lane. ### Strongest published-code candidate `MFM4` is now the strongest ordinary published JL-9 setup candidate. Reasoning: 1. Suffix `FM4` matches ordinary decode row `i = 0x0e`. 2. First char `M` yields accepted delta `3`, so normal decode writes `psx_level_runtime_header_state = 3`. 3. Ordinary row `i = 0x0e` returns selector `s = 0x0f`. 4. Live table bytes then map: - `DAT_80063e54[0x0f] = 0x36` (`54` decimal) - `DAT_80063e68[54] = 0x0f` 5. Therefore `MFM4` is the only currently recovered published mission code that statically satisfies both known preconditions at once: - non-hidden prime with `runtime_header_state == 3` - current-level family that can reach slot-`0x0f` dispatch ### Strong negatives from the same sweep - `LRTN` / `MRTN` / `PRTN` are not ordinary mission primes; they are the special `?RTN` family and clear `psx_level_runtime_header_state` to `0`. - the older contradiction in this investigation came from mixing decode-row space (`i`) with returned selector space (`s`); `MFM4` works because ordinary `i = 0x0e` still returns selector `0x0f`. - no published mission code currently closes the full JL-9 route by itself; even `MFM4` still needs the in-level gate-arm event and then the later hidden/input trigger. - the user-supplied level-11 strings containing `O` should be treated cautiously as written because `O` is not present in the recovered passcode alphabet (`BCDFGHJKLMNPQRSTVWXZ0123456789`). ### Practical consequence Current best actionable static route is now: 1. Enter `MFM4` as the ordinary prime. 2. In the same running session, hit the in-level scripted/control event that emits dispatch tuple `(0x0f,0x0a,0x04)` and arms `psx_debug_extra_channel_gate`. 3. Enter hidden `L0SR` / `?0SR`. 4. Press `R1 + Circle`. This is still not a fully closed player recipe because step 2 remains the missing concrete event, but it is now the strongest evidence-backed published-code path rather than a generic “some normal code with state 3” placeholder. ## Hard-clear theory check and manual gate-poke closure (2026-04-11 live pass) Two follow-up questions were tested after `MFM4` was isolated as the strongest published prime candidate: 1. is the missing in-level trigger actually tied to beating the game, especially on hard? 2. if not, is manual arming of the known gate byte enough to make hidden `L0SR` plus the trigger input unlock `JL-9`? ### Hard-clear theory verdict Current evidence keeps the "beat the game on hard, then enter `L0SR`" theory in the weak-to-medium bucket, not the lead explanation. What was actually recovered: 1. congratulation/completion text paths do exist near unnamed code around `0x800204fc`, `0x80022d20`, and `0x80022f68`. 2. those paths are plausible mission-complete / transition / UI handlers, but no direct write to `psx_debug_extra_channel_gate` (`0x8006739d`) was recovered from them. 3. no better persistent JL-9-specific latch was recovered than the already-known gate byte itself. 4. the strongest executable-backed model still points to an in-level scripted/event dispatch lane, not a post-credits reward lane. So the current read is narrower than folklore but not fully closed: - `MFM4` plausibly matters because it is the only published prime that sets `runtime_header_state = 3` and reaches the slot-`0x0f` gate family. - but beating the final mission on hard is **not yet proven** to be the missing arm event. - the unresolved gap remains the same concrete in-level producer for dispatch tuple `(0x0f,0x0a,0x04)`. ### Manual gate-poke closure The practical emulator test is now strong enough to state directly. 1. `psx_debug_extra_channel_gate` is runtime byte `0x8006739d`. 2. writer at `0x800232f0` uses `sb`, so storage is byte-wide. 3. reader at `0x8002fff4` uses `lbu` then branches on zero, so the gate is checked as **nonzero**, not as one exact magic literal. 4. no second direct writer or clear for `0x8006739d` has been recovered in the inspected session/load/menu paths. ### Practical manual test sequence The strongest current poke test is: 1. set byte `0x8006739d = 0x01` 2. enter hidden `L0SR` / `?0SR` 3. press `R1 + Circle` Expected result if the trigger path executes normally: - hidden input path reaches `psx_debug_grant_weapon_channels_and_ammo` - late read at `0x8002fff4` sees nonzero gate byte - extra unlock path at `0x80030004` runs for channel/index `0x0d` (`JL-9` lane) ### Practical caveats - the poke bypasses the natural writer-side predicates (`hidden==0`, `runtime_header_state==3`), so it does **not** validate the true in-level event by itself. - hidden mode still has to be active when the input trigger is pressed. - if the emulator uses uncached/physical mirrors, the same byte may appear as `0x0006739d` depending on the UI, but the logical KSEG0 runtime address is `0x8006739d`. - if a manual test fails with gate byte set, the next most likely cause is that hidden mode timed out or the input chord did not decode to `0x1e`. ### User-validated downstream closure User emulator verification now confirms the downstream half directly: 1. set main-memory byte `0x8006739d = 0x01` 2. enter `L0SR` 3. start gameplay and press `R1 + Circle` 4. result: `JL-9` appears in inventory next to `JL-2` Practical meaning of that success: - this validates the late hidden/input grant half of the model, not the natural writer path. - the poke directly pre-satisfies the nonzero check at `0x8002fff4`, so the test does **not** prove that `MFM4` or the natural in-level gate-arm event occurred. - among those two upstream elements, the more direct thing bypassed is the in-level trigger itself, because the poke replaces the writer result (`psx_debug_extra_channel_gate = 1`) rather than recreating the natural writer predicates that would normally produce it. - `MFM4` is therefore best read now as the strongest **natural prime candidate**, not as a required part of the forced test. ### User experiment follow-up (2026-04-11) Additional emulator trials now tighten the natural-versus-forced split further. 1. Natural `MFM4` trial: - user entered `MFM4`, allowed the level to load, then later returned to menu and attempted the hidden/input phase. - result: no `JL-9`; only ordinary `JL-2` was present. - practical meaning: current evidence no longer supports `MFM4` by itself as a sufficient natural route. 2. Forced-gate control with non-hard final-level code: - user entered `JFM4` (final map on easy), returned to menu, set `0x8006739d = 0x01`, entered `L0SR`, started a level, and pressed `R1 + Circle`. - result: `JL-9` still appeared. - practical meaning: the forced downstream route does not depend on `MFM4` specifically; manual gate arm is enough even when the prime code is not the hard-difficulty candidate. Current best interpretation after these trials: - `MFM4` remains the strongest **natural** prime candidate because it matches the writer-side predicates statically. - but it is no longer the lead bottleneck for the overall mystery. - the missing natural in-level event is now the dominant unknown. ### Deferred user experiments Keep these queued for later follow-up: 1. Experiment 2: hard-clear / beat-the-game test 2. Experiment 4: compare `MFM4` against another header-state-`3` code under matched in-level actions 3. Experiment 5: hold map/event family constant and vary only the suspected scripted event 4. Experiment 6: strict ordering test (`event -> L0SR -> trigger` versus `L0SR -> event -> trigger`) ## NRTN / ?RTN passcode path closure (2026-04-11 live pass) Focused live MCP pass on active `SLUS_002.68` for: - `psx_passcode_decode_to_mission_selector` (`0x8003ec8c`) - `psx_passcode_apply_mission_selector_and_stage` (`0x80021138`) - caller block around `0x80034c14..0x80034ddc` ### Exact decode and selector behavior 1. The special `?RTN` row is decode index `i=0x0f` in `psx_passcode_decode_to_mission_selector`. 2. At `0x8003ed10`, this branch clears `psx_level_runtime_header_state` (`DAT_80068ab0`) to `0`. 3. Function return is **not** `0` on this branch. - Due to delay-slot flow (`0x8003ecac`, `0x8003ed04`), `v0` remains `0x10` on return. 4. `psx_passcode_screen_eval_current_entry` stores raw decode return `0x10` into `DAT_80068a8c` and maps through `DAT_80063e54[0x10]`. - From live table bytes, `DAT_80063e54[0x10] = 0x3f`. 5. In caller flow (`0x80034d84`), `s0` is this mapped value (`0x3f`), so `beq s0,zero` does not fire. ### Exact apply/load behavior for NRTN / ?RTN lane 1. Because `s0 != 0`, caller reaches `jal 0x80021138` at `0x80034dcc`. 2. `psx_passcode_apply_mission_selector_and_stage` reads raw selector from `DAT_80068a8c`; for this path it is `0x10`. 3. Switch case for raw selector `0x10` is `0x8002142c` (index `0x0f` after `selector-1`). - writes `DAT_800675e4 = 0x1d` - sets mode byte `DAT_80067379 = 2` - calls common apply helper with `a0 = 0x36` 4. This is consistent with the commonly described office-loading lane, but the exact user-facing mission label remains conservatively treated as "developer-office candidate" until runtime label capture in the same session. ### JL-9 gate-arm compatibility answer - NRTN / ?RTN cannot itself satisfy gate-arm precondition `psx_level_runtime_header_state == 3`. - It explicitly forces that state to `0` in decoder (`0x8003ed10`). - Therefore it does **not** coexist with the required non-hidden gate-arm predicate at `0x800232c0..0x800232f0` (`hidden==0 && runtime_header_state==3`) unless another later path re-primes header state to `3`. Confidence: - high (`~0.95`) on decode return/write/apply control flow above (direct disassembly evidence) - medium-high (`~0.78`) on exact player-visible "developer office" naming for `a0=0x36` without paired runtime UI/string capture ## NRTN -> Office Event -> ?0SR/L0SR -> 0x1e hypothesis check (2026-04-11 live pass) Target hypothesis tested against live `SLUS_002.68` structure: 1. enter `NRTN` to load dev office 2. let an in-level office event arm `psx_debug_extra_channel_gate` 3. enter hidden `?0SR`/`L0SR` 4. press input code `0x1e` (`R1 + Circle`) Verdict: **structurally plausible but currently unproven as an exact deterministic player script**. Exact reasons from live code evidence: - `psx_debug_extra_channel_gate` remains one-writer/one-reader in this image: - write at `0x800232f0` in `psx_set_debug_extra_channel_gate` - read at `0x8002fff4` in `psx_debug_grant_weapon_channels_and_ammo` - gate-arm writer requires `hidden_flag==0` and `runtime_header_state==3` at the write site (`0x800232c0..0x800232f0`), so arming is structurally pre-hidden. - hidden path (`?0SR`/`L0SR`, special index `0x10`) sets `psx_hidden_passcode_flag` at `0x8003ed28` and returns selector `0`, with selector-`0` branch skip around `0x80034d84`. - grant trigger path requires hidden mode and input `0x1e` (`0x80013154..0x80013174`), so trigger is structurally post-hidden. - in-level gate-arm routing is real and dispatch-based (`0x800214ac..0x800215f8`): it compares against `DAT_80063e68[current_level]`, then indirect-calls `DAT_800640a0[index]`; slot `0x0f` resolves to `0x800230e4` and uses byte args from the action record. Why this is not yet fully proven: - static evidence still does not name one exact player-visible "office event" that deterministically emits the required writer tuple (`slot 0x0f`, `param_2==0x0a`, `param_3==0x04`) in dev office specifically. - special-passcode sibling behavior (`?RTN` lane clears `runtime_header_state` in decoder) means the exact `NRTN` practical role still needs one runtime trace confirmation in the same session, not another folklore inference. Confidence split for this exact hypothesis: - high (`0.89`) that the two-phase shape (pre-hidden arm, post-hidden trigger) is structurally correct. - medium (`0.67`) that the concrete user recipe starting specifically from `NRTN` is correct as stated. - overall classification for the exact `NRTN -> office event -> ?0SR/L0SR -> 0x1e` claim: **currently unproven, plausible**. Latest live MCP ownership pass on active `SLUS_002.68` now tightens the storage model around your dump split (`0x801456fc..0x80145748` static across dumps, `0x8014574c..0x801457d0` changed): - code-side ownership is channel-state based, not a direct contiguous owned-id array: - enable/query bit is byte `psx_marker_channel_runtime_block[(channel*4)+0x34]` - per-channel step/ammo state is byte `psx_marker_channel_runtime_block[(channel*4)+0x6c]` - active selected channel is byte `psx_marker_channel_runtime_block+0x32` - this lines up with the dump boundary: if runtime block base is near `0x80145718`, then `+0x34` is exactly `0x8014574c` (start of the changed range), and `+0x6c/+0x84/+0x8c` also sit inside the changed range. - commit/resolver path remains table-driven (`channel*10` rows), not contiguous-id-list driven: - `psx_apply_channel_effect_and_commit_selected_item_id` loads `channel_commit_row_table[(channel*10)+9]` at `0x8002f15c` - that committed id then resolves weapon rows via `idx*0x26` from `0x8006466a` - debug bulk grant is now explicitly separated from single-channel unlock helper in the live database: - `0x8002fd90` renamed to `psx_debug_grant_weapon_channels_and_ammo` - function bulk-calls channel unlocks `0x04..0x0c` and conditionally `0x0d` behind `psx_debug_extra_channel_gate` read at `0x8002fff4` Current best read is stable on four points: - `JL-9` is a real executable-backed weapon-definition row in the PSX build, not a stray string. - `JL-9` is now also user-verified in emulator memory editing as a working selectable weapon with unique sprite/behavior, not just a table-resident leftover. - the extra hidden/debug-conditioned late weapon lane maps to `JL-9`, not `JL-2`. - step 2 of the old JL-9 recipe is now much clearer: it is not a second passcode-screen action, but an indirect in-level scripted handler dispatch that reaches `psx_set_debug_extra_channel_gate` through slot `0x0f` with byte args `0x0a,0x04`. - hidden `?0SR` / `L0SR` also no longer looks like a level-loading code; the hidden branch returns selector `0` in the shared passcode-screen caller and skips the normal mission/apply-load path. - the checked `binary/Crusader - No Remorse Memdump Weapons.bin` artifact is a PSX VRAM dump and does not directly expose weapon-slot RAM. - the checked `binary/Crusader - No Remorse Weapons Main Ram.bin` artifact is plausible main RAM, but it still does not by itself distinguish the selected `JL-?` slot as `JL-2` versus `JL-9`. - the current JL-9 debug-path read is now tighter in two separate ways: input code `0x1e` is now closed to pad mask `0x2800` and high-confidence `R1 + Circle`, while the remaining recipe gap is the exact pre-hidden player action that reaches the `param_2==0x0a,param_3==4` gate-arm writer path. ## What step 2 actually means (2026-04-11 clarification) The earlier wording for step 2 was too abstract. Current live code evidence says: 1. Step 2 is **not** "enter something else on the passcode screen before the level loads." 2. Step 2 is **not** the hidden passcode itself. 3. Step 2 is an **in-level scripted/control event** that indirectly dispatches handler slot `0x0f`, which is `psx_set_debug_extra_channel_gate`. 4. The actual gate-arm write only happens when that handler is called with byte args `0x0a` and `0x04`. So the practical model is now: - step 1: use a normal passcode path that leaves `runtime_header_state == 3` - level loads / control flow continues into gameplay - step 2: some scripted in-level event fires and reaches the slot-`0x0f` handler with args `0x0a,0x04`, arming `psx_debug_extra_channel_gate` - step 3: use the hidden passcode path (`?0SR` / `L0SR`) to set hidden mode without taking the ordinary level-load branch - trigger: press `R1 + Circle` (`0x1e`) while hidden is active so debug grant runs and consumes the already-latched extra gate The remaining unknown is therefore not "what button do I press for step 2?" but "what concrete in-level scripted event emits slot `0x0f` with args `0x0a,0x04`?" ## Gate survivability across load/menu transitions (2026-04-11 live pass) Scope of this pass was the exact user question: does `psx_debug_extra_channel_gate` (`0x8006739d`) survive level loads, passcode transitions, main-menu returns, and hidden-passcode activation in one running session. Direct executable evidence: 1. `psx_debug_extra_channel_gate` still has exactly one recovered writer and one recovered reader in this image. - writer: `0x800232f0` in `psx_set_debug_extra_channel_gate` (`0x800230e4`) - reader: `0x8002fff4` in `psx_debug_grant_weapon_channels_and_ammo` (`0x8002fd90`) - a direct neighborhood write scan over `0x80067380..0x800673b0` found many writes to nearby state bytes (`0x80067384`, `0x8006738c`, `0x8006739c`, `0x800673a0`, `0x800673a4`), but no additional write to `0x8006739d`. 2. Level-load/session loop logic does not recover a static clear of `0x8006739d`. - `psx_level_session_loop` (`0x8002b8ec`) loads WDL bundles and resets several per-session flags, and it does clear `psx_hidden_passcode_flag` on timer expiry at `0x8002b9e4`. - no direct store to `0x8006739d` appears in this loop. 3. Main-menu/UI reset helpers seen in this lane do not recover a static clear of `0x8006739d`. - `FUN_800350e4` and `FUN_800352d4` reset menu/passcode state (`DAT_80067384`, selector bytes, control flags, runtime-header staging), but no write to `0x8006739d` is recovered. 4. Hidden passcode activation and normal passcode application diverge exactly where expected. - decoder special index `0x10` sets `psx_hidden_passcode_flag=1` in `psx_passcode_decode_to_mission_selector` at `0x8003ed28` and returns selector `0`. - in the passcode caller block around `0x80034d84`, selector `0` skips the apply/load branches (`psx_passcode_apply_selector_to_mode_0x0d`, `psx_passcode_apply_selector_to_mode_0x15`, `psx_passcode_apply_mission_selector_and_stage`) because of the explicit `beq s0,zero,...` branch. - practical consequence: hidden-code entry does not itself force the same normal apply/load branch used by nonzero mission selectors. 5. Gate-arm and grant-entry still require opposite hidden-flag states. - gate arm at `0x800232f0` requires `psx_hidden_passcode_flag==0` and `psx_level_runtime_header_state==3`. - debug grant entry at `0x80013154..0x80013174` requires `psx_hidden_passcode_flag!=0` and input code `0x1e`. Answer to the two-phase feasibility question: - In one running session, the two-phase recipe remains feasible. - Strongest current ordering is still: arm gate first in non-hidden state, then activate hidden mode and trigger input code `0x1e`. - Based on current static evidence, step 2 (hidden activation + trigger) is **after** the gate-arm event and can happen after a level load in the same process/session; no static clear of `0x8006739d` was recovered across the inspected load/menu transition helpers. - Step 2 **before** gate-arm is not consistent with the recovered predicates, because gate-arm explicitly requires hidden flag off. Confidence: - high (`~0.92`) on one-writer/one-reader gate model in this image - high (`~0.88`) on hidden-vs-nonhidden polarity constraint forcing a two-phase order - medium-high (`~0.76`) on practical persistence through all menu/load routes, because static evidence found no clear but an unresolved pointer-indirect/runtime write path is still theoretically possible ## Passcode screen semantics closure: what step 1 and step 3 mean (2026-04-11 live pass) This closure pass targeted user-facing behavior, not just branch predicates: are normal and hidden passcodes entered on the same screen, do they immediately transition out, and is the current numbered sequence operationally safe. Direct code-backed findings: 1. Normal and hidden passcodes are decoded through the same passcode-screen evaluator. - `psx_passcode_screen_eval_current_entry` (`0x80034e38`) builds the same 4-byte candidate from current screen-entry bytes and always calls `psx_passcode_decode_to_mission_selector`. - Hidden special decode (`index 0x10`, `?0SR/L0SR`) is set in that shared decoder at `0x8003ed28` (`psx_hidden_passcode_flag = 1`), not in a separate hidden-only input UI. 2. The immediate passcode-screen control flow is value-sensitive in the unnamed caller block around `0x80034c14`. - On the first eval site (`0x80034c14`), return `0` follows the reset/early-return branch (`0x80034c24..0x80034cdc`) rather than the acceptance/transition path. - Nonzero eval values proceed through `0x80034d00` (store selected result) and then into transition/setup calls (`0x800380d8`, `0x8003a46c`) before additional per-level setup (`0x80034d2c` onward). 3. User-facing meaning of the sequence steps is now tighter: - Step 1 (normal passcode with delta=>state 3) means: in the same passcode-entry screen, enter a normal code that reaches the non-special first-char delta lane and writes `psx_level_runtime_header_state = 3` at `0x8003ed58`. - Step 3 (hidden passcode) also means: use that same passcode-entry screen/evaluator path to hit special decode `0x10` (`0x8003ed28`), not a separate hidden-code menu. 4. Operational risk in the old sequence wording: - Because hidden decode and normal decode share this screen and the caller path has distinct zero/nonzero branches, a literal read of "do in-level step 2, then return later for step 3" is not yet proven as one deterministic minimal player script. - Current conservative status is therefore: branch predicates are closed, but exact user-visible ordering remains partially unresolved until the `param_2==0x0a,param_3==4` producer is observed in one live trace. Conservative verdict on sequence correctness: - The old sequence is directionally correct about the two-phase logic (`pre-hidden gate arm` + `hidden input trigger`), - but it is operationally under-specified and should not be treated as a guaranteed one-try recipe without the remaining caller-context closure. Confidence: - high (`~0.90`) that hidden and normal passcodes are entered through the same screen/evaluator path - high (`~0.85`) that the caller block has immediate branch-divergent behavior for return `0` vs nonzero - medium (`~0.64`) on exact practical player-order script, pending direct producer closure for the `0x0a/4` gate-arm event ## Gate-arm caller path closure around 0x800230e4 (2026-04-11 live pass) Scope of this pass was the exact writer condition at `0x800232f0` and the concrete upstream state flow that can make it true before hidden+input grant. Direct code facts now pinned: 1. The gate write at `0x800232f0` is in `psx_set_debug_extra_channel_gate` (`0x800230e4`) and is only reachable in the local jump-table branch for `param_2==0x0a` and `param_3==4`. - jump table at `0x80010550` maps entry `4` to block `0x800232a0` - that block performs `FUN_8002ba90()` and `FUN_8002ba78()` then evaluates hidden/state guards and conditionally stores `1` to `DAT_8006739d` 2. The exact store predicate is now explicit and comment-anchored in-session: - hidden must still be off: `DAT_80067454 == 0` (`0x800232c0`, `0x800232d0` branch) - runtime header must be `3`: `DAT_80078ab0 == 3` (`0x800232dc`, `0x800232e4` branch) - only then store: `sb 1, DAT_8006739d` at `0x800232f0` 3. `runtime_header_state==3` comes from normal (non-special) passcode decode math, not the hidden/special branches. - `psx_passcode_decode_to_mission_selector` (`0x8003ec8c`) normal lane writes: - `DAT_80078ab0 = code[0] - (DAT_80064bbc[idx] + 0x1b)` at `0x8003ed58` - accepted only when result `<=3` - special branches do not produce the required prime state: - index `0x10` (`?0SR` / hidden) sets `DAT_80067454=1` - index `0x0f` (`?RTN`) forces `DAT_80078ab0=0` 4. The strongest player-facing prime model is now: valid non-hidden mission/passcode entry in the normal decode lane with first-char delta resolving to `3`. - this yields the required precondition `DAT_80078ab0==3` while hidden is still `0` - once the `param_2==0x0a,param_3==4` event path runs, `DAT_8006739d` can latch to `1` 5. Hidden+input grant remains a later, separate phase with opposite hidden flag polarity. - grant entry in `psx_object_update_runtime_input_modes` requires `DAT_80067454!=0` and input code `0x1e` at `0x80013154..0x80013174` - grant helper reads `DAT_8006739d` at `0x8002fff4` and only then executes extra `unlock(0x0d)` at `0x80030004` Additional caller-path closure from later passes: 6. The gate writer is reached through an indirect level-gated dispatcher, not a direct passcode callback. - dispatcher block at `0x800214ac..0x800215f8` - `0x800215bc` compares opcode index against `DAT_80063e68[current_level]` - `0x800215dc` calls `DAT_800640a0[index]` via `jalr` - slot `0x0f` in `DAT_800640a0` resolves to `0x800230e4` (`psx_set_debug_extra_channel_gate`) - handler byte args are loaded from action-record pointers at `0x800215cc` and `0x800215e0` - current best narrowed trigger tuple is therefore `(dispatch slot 0x0f, param_2 0x0a, param_3 0x04)` in a small level-scripted family, not a manual UI/menu callback Practical sequence now supported by executable evidence: 1. Enter a normal valid passcode whose first-char delta lane sets `runtime_header_state` to `3` (hidden remains off). 2. Let gameplay continue until the in-level scripted/control event fires that reaches slot `0x0f` / `psx_set_debug_extra_channel_gate` with `param_2==0x0a,param_3==4`, so `DAT_8006739d` is armed. 3. Enter hidden passcode (`?0SR` / canonical `L0SR` when selector is `0`); current passcode-screen flow evidence says this shared decoder branch sets hidden flag but skips the normal nonzero mission/apply-load branch. 4. Perform input code `0x1e` (project mapping currently `R1+Circle`) to run debug grant; extra `0x0d` unlock executes because gate is already set. Confidence: - high (`~0.91`) on gate write predicate and branch identity (`0x800232a0` path) - high (`~0.88`) that `runtime_header_state==3` prime comes from normal decode lane, not special hidden/sibling specials - medium (`~0.62`) on one exact user-visible action name for step 2 (`param_2==0x0a,param_3==4` producer), because this pass closed the argument/value lane and surrounding passcode state flow but did not yet recover a single named UI handler with clean function boundaries in the `0x80022e8..0x800230e0` block ## Executable-only JL-9 validation lane (2026-04-11) Scope of this pass was strict: recover an exact user-facing JL-9 enable sequence from executable evidence only across passcode decode, input chord decode, gate write, grant call, and selected-id commit path. Recovered chain (all executable-backed): 1. Hidden passcode decode arm: - `psx_passcode_decode_to_mission_selector` (`0x8003ec8c`) sets `psx_hidden_passcode_flag` at `0x8003ed28` on special decode index `0x10`. - index `0x10` bypasses first-character validation and uses only the `entry[1..3]` transformed triplet check (`-0x1b`) against `0x80064bd0/0x80064be4/0x80064bf8`. 2. Input chord decode to grant trigger: - `psx_object_update_runtime_input_modes` (`0x80012c30`) gates the grant path on `psx_hidden_passcode_flag!=0` and decoded input code `0x1e` (`0x80013154..0x80013174`). - `psx_input_map_install_profile` (`0x80042ec4`) maps code `0x1e` to pad mask `0x2800` in all recovered profile branches, so the practical chord remains `R1 + Circle` under the project pad-bit model. 3. Extra JL-9 gate write: - `psx_set_debug_extra_channel_gate` (`0x800230e4`) writes `psx_debug_extra_channel_gate=1` at `0x800232f0` only when both conditions hold: - `psx_hidden_passcode_flag == 0` (`0x800232c0` / `0x800232d0` branch) - `psx_level_runtime_header_state == 3` (`0x800232dc..0x800232e4`) 4. Grant call and final extra unlock: - `psx_object_update_runtime_input_modes` calls `psx_debug_grant_weapon_channels_and_ammo` at `0x80013174`. - grant helper reads gate at `0x8002fff4`; if nonzero, branch at `0x80030004` unlocks channel `0x0d` (JL-9 lane). 5. Selected-id commit sink used by watched selected-byte lane: - `psx_apply_channel_effect_and_commit_selected_item_id` loads committed row id from `channel_commit_row_selected_item_id[(channel*10)+9]` at `0x8002f15c` and stores to nested runtime `+0x1c` at `0x8002f168`, also mirroring to `committed_selected_item_id` at `0x8002f170`. - this is the executable-backed row-id commit path that underpins the observed selected-byte `00..0d` behavior (`0x0d` for JL-9). Exact-sequence blocker from executable evidence only: - one link remains unclosed for a fully deterministic player-facing recipe: the concrete user-visible action/context that drives the `param_2=='\\n'` / `case 4` path in `psx_set_debug_extra_channel_gate` (`0x800230e4`) while hidden flag is still `0`. - without that mapping, code proves a two-phase ordering requirement but not one exact minimal button/menu script that always arms the gate before hidden grant input. Minimal next probe to close the blocker: - in one live trace, breakpoint/log `0x800232f0` and caller args into `0x800230e4` (especially `param_2` / `param_3`) while performing candidate player actions around passcode entry and mode transitions. - first observed user-facing action that reaches this writer under `hidden=0 && runtime_header_state=3` closes the final exact-step gap. Focused live follow-up on `0x80067944` and the surrounding `0x80067938..0x80067958` block now narrows one unresolved identity question: - `0x80067944` has no recovered static xrefs in this image under word, halfword, or byte probe passes (`get_data_uses` + operand scans), so it is currently not supported as a selected local weapon id or selected row id owner. - `0x80067938` is reaffirmed as `psx_ctor_placement_section_ptr`, installed in `wdl_resource_bundle_load_by_index` and consumed in `psx_apply_deferred_control_command` as constructor/deferred-control section state, not as weapon selection state. - nearby block ownership remains mixed: `0x8006793c/0x80067940/0x80067948` are consumed by `psx_object_update_runtime_input_modes` for input-mode dispatch helpers, while `0x80067954/0x80067958` are draw/disp environment flip/progress state used by present/spec-upload helpers. - one unnamed helper was promoted conservatively from direct behavior evidence: `FUN_800461d0` -> `psx_draw_progress_overlay_and_swap_drawenv`. The newest user-provided emulator verification replaces the earlier `JL-?=11` shorthand with a stronger selected-weapon mapping, while the starter-only compare still retracts one earlier storage claim: - verified selected-weapon byte at `0x8014577e` maps directly as row-id domain: - `00` no weapon / invalid gun - `01` `RP-16` - `02` `RP-22` - `03` `RP-32` - `04` `SG-A1` - `05` `AC-88` - `06` `PA-31` - `07` `EM-4` - `08` `PL-1` - `09` `UV-9` - `0A` `GL-303` - `0B` `AR-7` - `0C` `JL-2` - `0D` `JL-9` - the new starter-only RAM compare shows `0x1456fc..0x145748` is static across dumps and therefore is not the owned-weapon inventory list - the dynamic region is instead `0x14574c..0x1457d0`, with the strongest current field closure at `0x14577e`: - all-weapons dump byte at `0x14577e`: `0c` - starter-only dump byte at `0x14577e`: `02` - current best read: selected weapon row-id byte inside a nested runtime state block, not a contiguous owned-id slot array - the separate watched field at file offset `0x67944` still changes (`0x0000000b` vs `0x00000001`), but live executable passes did not recover direct static xrefs for `0x80067944`, so it remains an unproven watch field rather than a safe patch target New correction from live MCP reconciliation with the user-verified selected-weapon byte mapping: - treat the verified byte mapping at `0x8014577e` as the selected-weapon **row-id domain** (`00..0d`: none/invalid through `JL-9`). - keep the argument domain at `0x8002ef34` separate: callers pass a compact channel/local code, then `0x8002f15c` converts through `channel_commit_row_selected_item_id[(channel*10)+9]` into the committed row id that is written to the nested runtime field at `+0x1c` (`0x8002f168`). - practical correction: prior shorthand that could read as "local id equals committed row id" is too loose; the robust model is now `caller channel/local code -> commit table -> committed row id (00..0d)`, and the user-verified byte at `0x8014577e` belongs to that committed row-id domain. - static xrefs cannot directly prove absolute runtime RAM addresses such as `0x8014577e`, so address closure remains runtime-evidence-backed plus commit-path-backed rather than static-xref-backed. The strongest remaining unknown is no longer whether `JL-9` exists. It is now split into two narrower questions: - what exact runtime conditions make the late `JL-9` unlock visible in normal play timing, - and what `JL-2` actually is as a normal ammo-using weapon, since `JL-2 AMMO` is present while no matching plain `JL-9 AMMO` string has been recovered. ## Gate and hidden-flag lifecycle closure (2026-04-11 live pass) Scope of this pass was the exact lifecycle of `psx_debug_extra_channel_gate` (`0x8006739d`) and `psx_hidden_passcode_flag` (`0x80067454`) across session init, passcode entry, and debug grant. Recovered reference set is now tight: - `psx_debug_extra_channel_gate` (`0x8006739d`): - writer: `0x800232f0` in `psx_set_debug_extra_channel_gate` - reader: `0x8002fff4` in `psx_debug_grant_weapon_channels_and_ammo` - recovered static clears/resets: none - `psx_hidden_passcode_flag` (`0x80067454`): - sets: `0x8003ed28` (`psx_passcode_decode_to_mission_selector`, special index `0x10`), `0x8002bab8` (`psx_hidden_passcode_arm_runtime_state`) - clear/reset: `0x8002b9e4` (`psx_level_session_loop`, timer-expiry branch) - readers: `0x80013154` (`psx_object_update_runtime_input_modes`), `0x800232c0` (`psx_set_debug_extra_channel_gate`) State-machine facts now closed: 1. Extra unlock gate arm condition is strict and opposite to the grant-entry hidden condition. - At `0x800232c0..0x800232f0`, `psx_set_debug_extra_channel_gate` writes `psx_debug_extra_channel_gate=1` only when: - `psx_hidden_passcode_flag == 0` - `psx_level_runtime_header_state == 3` 2. Debug grant entry requires hidden mode to be active. - At `0x80013154..0x80013174`, `psx_object_update_runtime_input_modes` returns early unless `psx_hidden_passcode_flag != 0`. - If active, decoded input code `0x1e` calls `psx_debug_grant_weapon_channels_and_ammo`. 3. Extra JL-9 unlock check is a separate late latch. - At `0x8002fff4`, grant helper reads `psx_debug_extra_channel_gate`. - If nonzero, branch at `0x80030004` unlocks channel `0x0d` (JL-9 lane). 4. Hidden flag has explicit timed clear behavior; extra gate currently does not. - `psx_hidden_passcode_arm_runtime_state` seeds `DAT_800673cc=2000` and sets hidden flag at `0x8002bab8`. - `psx_level_session_loop` clears hidden flag at `0x8002b9e4` when timer reaches zero. - No recovered static clear path writes `0` to `psx_debug_extra_channel_gate` in this image. Practical persistence answer for the requested A -> B -> `R1+Circle` model: - Strongest executable-backed result is yes: the gate can persist long enough for a two-phase flow because we recovered one set and no static clear for `psx_debug_extra_channel_gate`, while hidden mode can be armed later and used within its own timer window. - This supports sequence shape: - step A: satisfy the non-hidden gate-arm condition (`hidden=0`, header-state `3`) so `psx_debug_extra_channel_gate` becomes `1` - step B: enter hidden passcode (`0x10` decode branch, canonical `L0SR` form when selector is `0`) to arm hidden mode - trigger: press input code `0x1e` (practical mapping in this project remains `R1 + Circle`) before hidden timer expiry - effect: debug grant path runs and late extra branch includes `unlock(0x0d)` Confidence: - high (`~0.93`) on writer/reader/clear sets listed above (direct xref and instruction evidence) - high (`~0.90`) on two-phase ordering requirement (opposite hidden-flag conditions between gate arm and grant entry) - medium-high (`~0.78`) on persistence duration beyond hidden timer window, because no static clear is recovered for the extra gate but pointer-indirect or un-recovered dynamic writes are still theoretically possible ## RP-16 status closure (2026-04-11 pass) Scope of this pass was narrowed to user-observed selected-weapon id `0x01` and whether it should be treated as a real usable RP-16 lane, an invalid slot, an earlier variant, or a startup placeholder. Direct executable findings: 1. Weapon-definition row is real and populated. - row `0x01` at `0x80064690` decodes as `RP-16` and carries nonzero row fields (`+0x1c=0x01`, `+0x20=0x03e8`, `+0x24=0x06`), unlike pure null/blank filler. 2. Primary shop acquire lane does not include row `0x01`. - `psx_weapon_shop_try_apply_entry` front path (`param_1 < 10`) uses direct unlock helper `psx_weapon_channel_unlock_and_seed_markers` and shop table bytes `03 04 05 06 07 08 09 0a 0b 0c`. - practical consequence: direct shop unlock progression reaches `JL-2` (`0x0c`) but excludes `RP-16` (`0x01`) and `RP-22` (`0x02`). 3. `0x01` appears in shop lookup, but in secondary ammo branch, not direct unlock branch. - shop table slot `10` byte is `0x01` (`0x80064b9a`), but this path enters the `0x0a..0x0e` branch that calls `0x8002e32c` (ammo top-up helper), not unlock helper `0x8002e5f0`. - this supports "defined id in economy tables" but not "normal explicit acquisition of RP-16 weapon row" from this lane. 4. Hidden/debug lane remains focused on late ids, not `0x01`. - `psx_debug_grant_weapon_channels_and_ammo` still closes around normal progression plus extra `0x0d` gate behavior; no new fixed immediate `0x01`-specific unlock site was recovered in this pass. HUD/name path closure for RP-16 in this pass: - full weapon-name identity remains row-driven: row `0x01` inline bytes at `0x80064690+2` decode to `RP-16`. - HUD short-label rendering uses selected-id-indexed lookup bytes in `FUN_800455d4` from tables around `0x80064e90` and `0x80064e9c` (selected id minus one). - this supports RP-16 as a represented display id, even where normal-lane unlock evidence is weaker than row existence. Current classification for RP-16 (best supported): - **not invalid/empty**: row is concrete and populated. - **not currently proven as normal direct unlock lane**: primary shop/loadout unlock path evidence emphasizes `>=0x03` progression and caps at `0x0c` in the direct shop lane. - **strongest fit right now**: real defined early weapon row that behaves like a legacy/startup/placeholder-capable entry in this image, with table presence and UI representation but no newly recovered dedicated normal acquisition proof in this pass. Confidence: - high (`~0.89`) that row `0x01` is real structured weapon data (`RP-16`), not random padding. - high (`~0.84`) that direct shop unlock progression excludes `0x01` in the primary acquisition branch. - medium (`~0.61`) on final gameplay role label (`legacy/startup/placeholder`) until one concrete non-debug in-mission acquisition or initialization writer is recovered. ## RP-16 startup/default-init closure (2026-04-11 pass) Focused live MCP pass on active `SLUS_002.68` to answer only this question: does RP-16 (`row/id 0x01`) get seeded as startup/default weapon via fresh-game init, difficulty-driven starts, mode transitions, or mission/loadout init? Direct startup/init findings: 1. Post-load reset explicitly clears selected-id state before init dispatch. - `psx_level_post_load_runtime_reset` writes `committed_selected_item_id = 0` at `0x80039f68`. - from the same function, startup path then dispatches mode actions (`8`, optionally `2`, then `4`), not a fixed `0x01` commit. 2. Mission/loadout init does not perform a fixed RP-16 selected-id write. - `psx_weapon_channels_init_mode_loadout` (`0x8002f814`) is mode-table driven from `psx_level_channel_table_80063e68` and applies unlock/ammo helpers via fallthrough; no fixed immediate `0x01` selected-id commit is present. - `psx_weapon_channels_apply_mode_transition_state` (`0x8002f278`) sets active channel state to `2` or `3` in the observed startup branch (`0x8002f468` / `0x8002f49c`), not channel/id `0x01`. 3. The selected-id global has only two recovered writers in this image. - writer A: reset-to-zero at `0x80039f68`. - writer B: table-based commit sink in `psx_apply_channel_effect_and_commit_selected_item_id` at `0x8002f170`. - no dedicated startup writer with a fixed immediate `0x01` was recovered. 4. Fixed-immediate commit callsites found in this pass do not support RP-16-as-default. - recovered immediate dispatches use `a0=0x11`, `a0=0x12`, and one context-specific `a0=0x01` action lane in unnamed gameplay/control handlers (`0x8001ede8`, `0x8001ef08`, `0x8001f068`, `0x80021930`, `0x80022624`), not the named startup/loadout reset path. - this keeps low-id usage structurally possible outside startup, but does not convert RP-16 into a proven default-start weapon. 5. Default/loadout table context remains non-startup evidence. - shop/channel map bytes at `0x80064b90` still include `0x01` (`... 0c 01 05 04 ...`), but this remains economy/action-path evidence rather than a startup seed proof. Current startup/default verdict for RP-16: - **not proven startup/default weapon** in fresh-game init, difficulty/mode-transition apply, or mission/loadout init paths recovered in this pass. ## JL-9 producer-side authored-source closure pass (2026-04-11 live MCP) Scope of this pass was limited to the producer side of the action-record pointer frame consumed by `psx_level_gate_slot_dispatch_from_action_record` (`0x800214ac`), with priority on finding one concrete upstream authored context for tuple `(slot 0x0f, arg1 0x0a, arg2 0x04)`. ### Strongest producer-side clue recovered The strongest new clue is now loader/constructor explicit: 1. `psx_load_type_state_banks` (`0x800391f0`) installs `psx_type_simple_component_bank[type]` from the level bundle type-state blob (`SPEC_A.WDL`/`L*.WDL` path via `wdl_resource_bundle_load_by_index` at `0x8003977c`). 2. `psx_object_create_simple_record` seeds component `program_base` and `pc` from that exact bank at `0x80024c60` and `0x80024c88`. 3. `psx_run_object_behavior_program_tick` then executes words from that component stream and calls `psx_object_behavior_opcode_dispatch` at `0x80026740`. 4. `psx_object_behavior_opcode_dispatch` routes opcode `54` through `psx_behavior_opcode_handler_table` (`0x800641ac`) to `psx_behavior_subopcode_dispatch` (`0x80027ecc`), which then routes subop `49` through `psx_behavior_subop_handler_table` (`0x80063610`) to sink `0x800214ac`. Practical implication: the pointer-frame lane feeding slot/arg bytes into `0x800214ac` is now best modeled as authored type-state behavior program content loaded per level, not sink-local constants. ### Tuple classification update Current best classification for `(0x0f,0x0a,0x04)` is now: 1. **authored-static at source context** (type-state behavior payload in level bundle), 2. then **runtime-index resolved** in dispatcher frame construction when mask bits request `base + index*4` slot-pointer mapping. So this tuple is not strongest as a pure runtime remap invention; runtime remap appears to be the transport mechanism over authored behavior-program operands. ### Exact live Ghidra changes in this pass Decompiler comments added: - `0x80039250`: producer provenance note on `psx_type_simple_component_bank[type]` install from level/LSET type-state blob. - `0x80024c60`: constructor note that component program base/pc come from `psx_type_simple_component_bank[type]` (upstream authored source lane). - `0x80026740`: behavior-tick note that opcode/mask/args are read from component pc stream and feed `psx_object_behavior_opcode_dispatch`. No function renames were applied in this pass. ### Remaining open item (narrowed) Still open is one concrete **type row + map context** instance whose loaded behavior stream emits the exact `54 -> 49` producer record that resolves to `(slot 0x0f, arg1 0x0a, arg2 0x04)` at runtime. The unresolved part is now specific row attribution, not producer subsystem identity. - **still a real executable-backed row** with non-startup lane presence. - **best current label**: placeholder/legacy-capable early row with unresolved normal acquisition role, not a demonstrated startup default. Confidence (startup/default question only): - high (`~0.86`) that current named startup/loadout/mode-transition initializers do not hard-seed selected id `0x01`. - medium (`~0.63`) that no indirect startup-side table commit resolves to `0x01` in unseen/unnamed init stubs, because several nearby callsites still live in undefined function ranges. ## RP-16 startup/default recheck (2026-04-11 live MCP follow-up) This follow-up pass pushed specifically on undefined nearby init stubs, startup selected-id writes, active-channel writes, and difficulty/mode tables to test any indirect RP-16 (`0x01`) seed route. Direct findings: 1. `committed_selected_item_id` still has exactly two recovered writes in this image. - reset write: `0x80039f68` in `psx_level_post_load_runtime_reset` (`=0`) - commit sink write: `0x8002f170` in `psx_apply_channel_effect_and_commit_selected_item_id` - no third startup/default writer was recovered. 2. Startup mode-action dispatch remains bounded and non-committing. - startup callsites dispatch `mode_action=8` then `2`/`4` (`0x80039fa4`, `0x8003a014`, `0x8003a01c`; additional observed stub callsite at `0x8003e6a0` dispatching `8`). - these actions drive loadout/transition/seed tables but do not directly call the selected-id commit sink. 3. Active-channel writes in startup lanes do not imply RP-16 commit. - `psx_weapon_channels_apply_mode_transition_state` sets `psx_marker_channel_runtime_block+0x32` to `2` or `3` in the startup branch. - loadout unlock/ammo helpers only initialize when `+0x32==0`, then write the channel argument used by that helper path; no startup helper immediate observed here seeds channel `0x01` as selected-id commit. 4. Difficulty/mode table path still does not resolve to selected-id `0x01`. - `psx_level_channel_table_80063e68` feeds `DAT_80078a8c` in startup (`0x8002f2a0`, `0x8002f868`, `0x8002f978`, `0x80039fe8`). - table-driven startup channels resolve into commit-row selected bytes at `channel_commit_row_selected_item_id` (`0x80064355`, `+9` in each 10-byte row), and scanned rows `0x00..0x19` contain no `sel=0x01` byte. 5. Undefined nearby commit callsites found in this pass do not establish startup-default RP-16. - recovered no-function callsites into commit helper include `0x8001ede8` and `0x8001ef08` (`a0=0x11`), plus additional gameplay/control lanes from prior passes. - these are outside the named startup reset/loadout transition chain. Conservative live Ghidra artifact updates from this pass: - rename: `0x8002fd80` -> `psx_marker_channel_set_mode6_only` - decompiler comments: - `0x80039f68` (startup reset selected-id clear) - `0x8002f170` (commit sink table-write semantics) - `0x8002f2a0` (mode-table source and non-`0x01` implication) - disassembly comments: - `0x80064355` (selected-id table semantics and no `0x01` in scanned rows) - `0x8003e6a0` (observed startup/transition stub dispatching mode action `8`) Updated verdict after this follow-up: - startup/default RP-16 remains **ruled out in recovered startup/init code paths** for active `SLUS_002.68`. - RP-16 (`0x01`) remains a real row and remains reachable in non-startup contexts, but this pass found no evidence of startup/difficulty/mode-init seeding to selected-id `0x01`. ## Legit Acquisition Closure (2026-04-11 pass) Scope of this pass was narrowed to legitimate `JL-9` acquisition paths versus hidden/debug leftovers, with emulator verification treated as ground truth for selected weapon id domain (`0x8014577e`, `0x0c=JL-2`, `0x0d=JL-9`). Direct executable findings by lane: 1. Normal loadout lane (`psx_weapon_channels_init_mode_loadout`): - fallthrough switch seeds baseline unlock/ammo progression and reaches ordinary channels, but does not perform a fixed immediate unlock of channel `0x0d` - this lane supports normal progression to `JL-2` (`0x0c`) and earlier channels, not a direct hardcoded `JL-9` grant 2. Shop lane (`psx_weapon_shop_try_apply_entry`): - unlock front-path is gated by `param_1 < 10` - `DAT_80064b90[0..9]` is `03 04 05 06 07 08 09 0a 0b 0c` - practical result: direct shop unlock path is capped at `0x0c` (`JL-2`) and does not directly issue `0x0d` (`JL-9`) 3. Scripted packed-action/pickup lane (`psx_section0_dispatch_root_apply_packed_channel_actions`): - action type `3` dispatches `psx_weapon_channel_unlock_and_seed_markers(channel_byte)` from decoded triplet data - triplet table is seeded at runtime by `psx_section0_dispatch_root_seed_marker_channel_table` from section0 marker records - this means scripted non-debug `0x0d` is structurally possible only if authored level marker data actually supplies channel `0x0d`; this pass did not recover a shipped-map proof row that does so in normal play 4. Hidden/debug lane (`psx_object_update_runtime_input_modes` -> `psx_debug_grant_weapon_channels_and_ammo`): - `psx_hidden_passcode_flag` gate at `0x80013154` must be active before input code `0x1e` can call debug grant - debug grant always unlocks through `0x0c`, and conditionally unlocks `0x0d` only when `psx_debug_extra_channel_gate` is nonzero - this remains the only recovered fixed-immediate `unlock(0x0d)` call site (`0x80030004`) in the current executable Current verdict from executable evidence: - strongest supported path to `JL-9` remains hidden/debug-conditioned - strongest supported normal gameplay acquisition lanes close at `<= 0x0c` (`JL-2`) - non-debug scripted `0x0d` remains the only plausible legitimate exception, but is still unproven without a concrete shipped section0 marker/action row Confidence: - high (`~0.87`) that fixed-code normal/shop paths do not directly grant `JL-9` - high (`~0.90`) that hidden/debug path can grant `JL-9` via the `0x0d` conditional unlock - medium (`~0.52`) on whether shipped non-debug section0 data ever drives a legitimate scripted `0x0d` unlock ## Executable-side condition closure (2026-04-11 pass) Focused live MCP pass on active `SLUS_002.68` to close the exact gate chain around your verified selected-weapon byte watch (`0x8014577e`) and the JL-9 late unlock lane. What this pass closes directly: 1. `psx_debug_extra_channel_gate` (`0x8006739d`) is still a one-writer/one-reader gate in this image: - writer: `psx_set_debug_extra_channel_gate` (`0x800232f0`) - reader: `psx_debug_grant_weapon_channels_and_ammo` (`0x8002fd90`) at `0x8002fff4` 2. `psx_hidden_passcode_flag` (`0x80067454`) writer/reader set is now explicit: - writers: `psx_passcode_decode_to_mission_selector` (`0x8003ed28`), `psx_hidden_passcode_arm_runtime_state` (`0x8002ba9c`), and clear/reset points in `psx_level_session_loop` (`0x8002b9e4`) - readers: `psx_set_debug_extra_channel_gate` (`0x800232c0`) and `psx_object_update_runtime_input_modes` (`0x80013154`) 3. Unlock-capable call families for channel `0x0d` are now bounded: - debug bulk grant: `psx_debug_grant_weapon_channels_and_ammo` (always includes `0x0c`, includes `0x0d` only when `psx_debug_extra_channel_gate != 0`) - scripted packed actions: `psx_section0_dispatch_root_apply_packed_channel_actions` can call `psx_weapon_channel_unlock_and_seed_markers(channel)` for data-driven channel bytes - normal loadout/shop paths call the same unlock helper but are mode/slot constrained; shop front path (`param_1 < 10`) maps channels `0x03..0x0c` and does not directly include `0x0d` 4. Commit/read chain for visible selected weapon remains stable: - commit: `psx_apply_channel_effect_and_commit_selected_item_id` -> `channel_commit_row_selected_item_id[(channel*10)+9]` - sinks: nested player runtime field (`... + 0x1c`) and `committed_selected_item_id` (`0x80078a90`) - consumers include spawn/HUD-adjacent lanes (`FUN_80014d04`, `FUN_80014eac`, `psx_spawn_contact_burst_simple_records`) `0x8014577e` closure status in this pass: - direct static xrefs from `get_data_uses(0x8014577e)` are still empty (expected for heap/runtime fields) - executable-side chain strongly supports that this watch sits in the same nested player runtime object family as the committed selected-id lane, but this pass did not recover one direct static instruction with absolute `0x8014577e` Practical classification after this pass: - strongest debug-only lane: hidden-passcode-gated input path that reaches `psx_debug_grant_weapon_channels_and_ammo`, then conditionally unlocks `0x0d` through `psx_debug_extra_channel_gate` - plausible shipped-reachable lane: section0/script packed-action dispatcher can grant channels by data (including potential `0x0d` if authored data uses it), but no new map/script row proving shipped non-debug `0x0d` authoring was recovered in this pass - strongest normal-lane evidence remains `<= 0x0c` via loadout/shop constraints and earlier commit-table/model work ## Final JL-9 enable sequence closure (2026-04-11 pass) Focused live MCP pass on active `SLUS_002.68` against the exact target points: - `psx_hidden_passcode_arm_runtime_state` (`0x8002ba9c`) - `psx_set_debug_extra_channel_gate` write site (`0x800232f0`, inside function entry `0x800230e4`) - `psx_debug_grant_weapon_channels_and_ammo` (`0x8002fd90`) - extra gate read / extra unlock branch (`0x8002fff4` / `0x80030004`) ### Direct gate facts now closed 1. Final extra JL-9 unlock is definitely gated by `psx_debug_extra_channel_gate`: - disassembly at `0x8002fff4` reads `lbu v0,0x9d(gp)` (`DAT_8006739d`) - if nonzero, branch executes `jal 0x8002e5f0` with `a0=0x0d` at `0x80030004` - this is the only extra post-`0x0c` unlock site in this helper and maps to channel/id `0x0d` (`JL-9` lane) 2. The extra gate is written only under a strict precondition: - writer instruction at `0x800232f0` stores `1` to `DAT_8006739d` - immediate guards in the same block require: - `psx_hidden_passcode_flag == 0` (`lw v1,0x7454(v1)` then `bne v1,zero,...`) - `psx_level_runtime_header_state == 3` (`lw v1,-0x7550(v1)` then `bne v1,3,...`) - practical write condition is therefore: - set extra gate only when hidden-passcode flag is currently **off** and runtime-header state is `3` 3. Calling the grant helper requires the opposite hidden-flag condition: - in `psx_object_update_runtime_input_modes` (`0x80013154`), debug path returns early unless `psx_hidden_passcode_flag != 0` - only then can decoded input code `0x1e` reach `psx_debug_grant_weapon_channels_and_ammo` (`call at `0x80013174`) 4. Hidden-passcode flag lifecycle remains bounded and explicit: - set in `psx_passcode_decode_to_mission_selector` (`0x8003ed28`, special index `0x10` path) - armed/set in `psx_hidden_passcode_arm_runtime_state` (`0x8002ba9c`) with timer seed `DAT_800673cc=2000` - cleared/reset in `psx_level_session_loop` (`0x8002b9e4`) when timed armed state expires ### Exact precondition model for JL-9 extra unlock To execute the extra `0x0d` unlock inside `psx_debug_grant_weapon_channels_and_ammo`, executable evidence now requires both: - `psx_debug_extra_channel_gate != 0` at `0x8002fff4` (gate read) - hidden debug input path active (`psx_hidden_passcode_flag != 0` and input code `0x1e`) to enter the helper And the gate itself is only set in a prior state where: - `psx_hidden_passcode_flag == 0` - `psx_level_runtime_header_state == 3` ### One hidden action or two? Current executable-side verdict is now: - strongest model is a **two-phase hidden flow** for guaranteed JL-9 extra unlock, because gate-write and grant-entry require opposite hidden-flag states (`==0` vs `!=0`) - a single hidden action could only be sufficient if one action simultaneously establishes both states across time (for example gate already latched from an earlier phase), which is not directly proven in this pass Safest practical sequence from code evidence alone: 1. Reach the gate-write condition (`hidden flag off`, runtime-header state `3`) so `psx_debug_extra_channel_gate` is latched. 2. Arm/reactivate hidden-passcode state (`psx_hidden_passcode_flag != 0`). 3. Trigger decoded input code `0x1e` to call `psx_debug_grant_weapon_channels_and_ammo`. 4. Helper runs normal grants through `0x0c`, then executes extra `0x0d` unlock because gate is already set. Confidence for this sequence shape: high (`~0.86`) from direct write/read disassembly and guarded call flow; medium (`~0.63`) on exact player-facing button/passcode choreography for each phase. ## Special passcode priming closure (?0SR / ?RTN / ?QQQ) (2026-04-11 live pass) Scope of this pass was to answer one specific question: can one of the recovered special passcodes (`?0SR`, `?RTN`, `?QQQ`) by itself prime `psx_level_runtime_header_state` or `psx_debug_extra_channel_gate` so JL-9 enable is one-code, or does executable evidence still force a two-code flow. Direct decode/write facts (all from `psx_passcode_decode_to_mission_selector`, `0x8003ec8c`): 1. `?RTN` branch (special index `0x0f`) writes `psx_level_runtime_header_state = 0` at `0x8003ed10`. 2. `?0SR` branch (special index `0x10`) writes `psx_hidden_passcode_flag = 1` at `0x8003ed28`. 3. `?QQQ` branch (special index `0x11`) returns sentinel `0x12` and does not write `psx_hidden_passcode_flag` or `psx_level_runtime_header_state` in that branch. 4. All three special branches bypass the normal first-char delta calculation path that writes runtime-header state in the non-special lane (`0x8003ed3c..0x8003ed58`). Direct gate-arm facts (from `psx_set_debug_extra_channel_gate`, `0x800230e4` / store at `0x800232f0`): 1. `psx_debug_extra_channel_gate` is set to `1` only when both are true at that branch: - `psx_hidden_passcode_flag == 0` (`0x800232d0` guard) - `psx_level_runtime_header_state == 3` (`0x800232e4` guard) 2. This gate is read later at `0x8002fff4` in `psx_debug_grant_weapon_channels_and_ammo` for the extra `unlock(0x0d)` branch. Implication for one-code versus two-code: - None of `?0SR` / `?RTN` / `?QQQ` can by itself satisfy the gate-arm condition for JL-9 extra unlock: - `?RTN` forces header state to `0`, not `3`. - `?0SR` forces hidden flag to `1`, but gate arm requires hidden flag `0`. - `?QQQ` does not set header state to `3` and does not arm the gate by itself. - So the strongest executable-backed result remains a two-phase flow where gate arm must come from a separate pre-hidden state, then hidden mode is armed for the input-triggered grant. Strongest exact passcode sequence currently provable: 1. Prime phase: use a non-hidden passcode/state path that leaves `psx_hidden_passcode_flag==0` and reaches `psx_level_runtime_header_state==3` before the `0x800232f0` gate-arm check runs. 2. Hidden phase: enter `?0SR` (canonical `L0SR` when selector is `0`) to set `psx_hidden_passcode_flag=1`. 3. Trigger phase: execute input code `0x1e` to enter `psx_debug_grant_weapon_channels_and_ammo`; with gate latched, helper executes extra `unlock(0x0d)`. Practical status of the sibling specials in this model: - `?RTN`: anti-prime for JL-9 gate arm (explicitly clears runtime-header state). - `?QQQ`: neutral/side-effect-special in this context (no recovered direct gate-prime write). Confidence: - high (`~0.91`) that recovered special passcodes do not directly prime JL-9 gate arm by themselves (direct branch/store evidence) - high (`~0.88`) that the strongest current model is two-phase rather than one-code for guaranteed `0x0d` extra unlock - medium (`~0.66`) on which exact non-special prime code is used in real play, because this pass intentionally focused on the recovered special-code trio and gate predicates ## Core proven facts ### 1. Weapon table identity is direct - weapon-definition table base is `0x8006466a` - row stride is `0x26` - `JL-2` row is `0x80064832` (index `0x0c`) - `JL-9` row is `0x80064858` (index `0x0d`) Recovered visible order remains: 1. `INVALID GUN` 2. `RP-16` 3. `RP-22` 4. `RP-32` 5. `SG-A1` 6. `AC-88` 7. `PA-31` 8. `EM-4` 9. `PL-1` 10. `UV-9` 11. `GL-303` 12. `AR-7` 13. `JL-2` 14. `JL-9` The strongest stable row split is: - shared: `+0x1c = 0x18`, `+0x23 = 0x0e` - diverged: `JL-2 +0x24 = 0x4b`, `JL-9 +0x24 = 0x0f` ### 1b. Compact local id to weapon row id conversion is table-driven The runtime values near selected/inventory state (`0x0001`, `0x0002`, `0x000b`, `0x000c`) now reconcile cleanly with the weapon table as follows: 1. Callers pass a compact channel/local code to `psx_apply_channel_effect_and_commit_selected_item_id` (`0x8002ef34`) (for example at `0x8001d3fc`). 2. `0x8002f15c` then loads the committed weapon id from `channel_commit_row_selected_item_id[(channel*10)+9]`. 3. That committed id is consumed as the weapon-table index in `psx_weapon_def_get_u16_with_mode_gate` (`0x800315d8`), which resolves `idx*0x26` from base `0x8006466a`. This means the caller-side compact channel/local code to final selected weapon row-id step is implemented through channel-table lookup, not a dedicated global `+1` conversion helper. Concrete JL pair from this path: - compact local/channel `0x0b` resolves through table slot `+9` to committed row `0x0c` (`0x80064832`, `JL-2`) - compact local/channel `0x0c` resolves through table slot `+9` to committed row `0x0d` (`0x80064858`, `JL-9`) This matches the stronger user-verified selected-weapon byte mapping where `0x0c = JL-2` and `0x0d = JL-9` at `0x8014577e`. Correction for domain clarity: - the user-verified selected-weapon byte mapping (`00 none`, `01 RP-16`, `02 RP-22`, `03 RP-32`, `04 SG-A1`, `05 AC-88`, `06 PA-31`, `07 EM-4`, `08 PL-1`, `09 UV-9`, `0A GL-303`, `0B AR-7`, `0C JL-2`, `0D JL-9`) is row-id-domain and aligns with weapon table row identities. - this does not contradict the `0x8002ef34` call path: it clarifies that the call argument and the committed/stored selected id can be from different domains linked by the `channel_commit_row_selected_item_id` lookup. ### 2. Extra late unlock maps to JL-9 The strongest current chain is direct and no longer only inferential: 1. `psx_apply_channel_effect_and_commit_selected_item_id` (`0x8002ef34`) commits the selected item id from `channel_commit_row_selected_item_id`. 2. the channel `0x0d` row commits item id `0x0d`. 3. weapon-definition helpers index rows as `0x8006466a + idx*0x26`. 4. row `0x0d` is `0x80064858`, whose visible name bytes are `JL-9`. 5. row `0x0c` is `0x80064832`, whose visible name bytes are `JL-2`. This closes the late extra lane as: - `0x0c` = `JL-2` - `0x0d` = `JL-9` ## Upstream producer structure recovery for 0x800214ac (2026-04-11 live pass) Scope of this pass was restricted to upstream production of the action-record pointer bytes consumed by `psx_level_gate_slot_dispatch_from_action_record` (`0x800214ac`). ### Best recovered producer structure (current strongest model) Recovered chain in live `SLUS_002.68`: 1. `psx_behavior_subopcode_dispatch` (`0x80027ecc`) calls subop handler table entry with: - context argument from `local_60[10]` - action-record pointer frame from `local_60+1` 2. `psx_level_gate_slot_dispatch_from_action_record` (`0x800214ac`) receives that frame as `record` and consumes: - `record+0x00` pointer -> mode byte (`*(*(record+0x00))`) - `record+0x04` pointer -> slot byte (`*(*(record+0x04))`) - `record+0x08` pointer -> `arg1` byte (`*(*(record+0x08))`) - `record+0x0c` pointer -> `arg2` byte (`*(*(record+0x0c))`) 3. Upstream per-argument pointer production occurs in `psx_object_behavior_opcode_dispatch` (`0x8002677c`): - if argument mask bit is clear, argument word is used as direct pointer - if set, argument word is treated as slot index and resolved by `base + index*4` - resolver base is object-local slot table (`*(object_state+0x18)`), except when mask `0x100` forces global base `DAT_800789e0` This makes the producer structure a pointer-vector frame (`local_60`) populated by opcode-dispatch argument resolution, then forwarded to subop handler `49` (`0x800214ac`) through `psx_behavior_subop_handler_table`. ### Concrete authored-data clue (bounded but real) The sink tuple still closes as `(slot,arg1,arg2)=(0x0f,0x0a,0x04)` for the gate arm branch, but upstream formation is now better constrained: 1. These values are not required to be hardcoded literals in executable code. 2. They are produced as dereferenced bytes via pointer fields in the behavior action-record frame. 3. Because each pointer field can be either direct or slot-index-resolved (`base + index*4`), authored behavior script data can feed this tuple either as direct byte pointers or as slot-table indices resolved at runtime. This is the strongest concrete authored-data clue recovered in this pass: the tuple is behavior-script produced pointer data, not a fixed immediate triple in the sink function. ### Exact Ghidra changes applied in this pass 1. Function rename: - `0x800268a4`: `FUN_800268a4` -> `psx_behavior_arg_index_to_slot_ptr` 2. Decompiler comments: - `0x80026814`: argument resolver semantics (`index*4` pointer mapping and base selection) - `0x80027f0c`: producer frame field map (`local_60[0..4]` plus context at `local_60[10]`) - `0x800215cc`: sink-side field mapping (`record+4/+8/+0c` to slot/arg1/arg2) Conservative confidence split: - high (`~0.9`) on pointer-frame field mapping and resolver behavior (direct decompile evidence) - medium (`~0.65`) on exact authored object/mission row currently producing `(0x0f,0x0a,0x04)` in shipped gameplay without runtime trace ### 3. Hidden passcode path reaches the JL-9 lane The strongest recovered control path is: 1. `psx_passcode_decode_to_mission_selector` (`0x8003ec8c`) sets `psx_hidden_passcode_flag` 2. `psx_object_update_runtime_input_modes` checks `psx_hidden_passcode_flag` 3. on gated input code `0x1e`, it calls `psx_debug_grant_weapon_channels_and_ammo` 4. that helper always unlocks through `0x0c` and conditionally unlocks `0x0d` when `psx_debug_extra_channel_gate != 0` This is the strongest current executable-backed explanation for how the extra PSX-only lane reaches `JL-9`. The newest narrow passes now sharpen the trigger story in a more useful way: - code now clearly supports `hidden passcode active -> gated input code 0x1e -> psx_debug_grant_weapon_channels_and_ammo -> extra 0x0d unlock behind psx_debug_extra_channel_gate` - input-map closure now ties `0x1e` to pad mask `0x2800`, which is high-confidence `R1 + Circle` under the active digital-pad bit layout - the remaining JL-9 trigger uncertainty is no longer the chord; it is the exact pre-hidden player-visible action that reaches the gate-arm writer path at `0x800232f0` ### 4. JL-2 is now the stronger normal ammo-lane unknown The current practical split is: - `JL-2` sits in ordinary acquisition space (`0x0c`) - `JL-9` sits in the extra hidden/debug-conditioned late lane (`0x0d`) - the visible string `JL-2 AMMO` exists at `0x800642b6` - no matching plain `JL-9 AMMO` string has been recovered in the same string family That does not fully decode `JL-2`, but it does shift the next unknown toward `JL-2` rather than back toward `JL-9` existence proof. ### 5. The checked dump is VRAM, not slot RAM The user-supplied `binary/Crusader - No Remorse Memdump Weapons.bin` is now best read as a PSX VRAM capture: - file size is exactly `0x100000` (`1,048,576`) bytes, matching PlayStation VRAM size - the dump contains VRAM-like repeated 16-bit pixel patterns rather than compact inventory rows - inspection found HUD and icon-atlas style regions, but not a direct weapon-slot RAM structure Current safest conclusion: - the dump is useful as a presentation artifact - it is not a direct source for weapon-slot storage values - it does not currently distinguish `JL-2` from `JL-9` with confidence ### 6. The checked 2 MiB main RAM dump is plausible RAM, but still inconclusive The user-supplied `binary/Crusader - No Remorse Weapons Main Ram.bin` is not rejected as the wrong broad domain. Current safest read: - file size is `0x200000` (`2,097,152`) bytes, matching PlayStation main RAM size - no plain ASCII `JL-2`, `JL-9`, or `JL-` strings were recovered in the dump - no immediately self-identifying weapon-slot table was recovered from this pass alone So the main-RAM dump currently lands in a narrower state than the VRAM dump: - it is plausible runtime state memory, - but not yet a self-decoding proof of whether the selected `JL-?` weapon with `10` clips and `0` loaded bullets is `JL-2` or `JL-9`, - because the identity still appears to depend on numeric runtime ids that must be cross-referenced back to the executable-side weapon table and HUD/name resolver logic. The newest inventory-oriented pass did at least tighten where to look next inside that dump: - candidate compact 16-byte slot-like records were noticed very early in the file (for example around file offsets `0xA0`, `0xB0`, `0xC0`) - those records are not self-labeled and were not enough on their own to map `RP-32 ... JL-?` - the most useful next RAM step is now to map one confirmed executable-side inventory or HUD pointer onto the dump, instead of continuing a blind whole-image sweep The starter-only compare forces a narrower and more defensible RAM read: - file offset `0x1456fc` does still begin a compact sequence of 8-byte records whose first 16-bit field climbs `0x0002 .. 0x000b` - however, the same sequence is unchanged in the starter-only dump, so it cannot be the live owned-weapon inventory list - the dynamic block starts immediately after that static table, at `0x14574c`, and remains changed through at least `0x1457d0` - the strongest current field closure inside that block is: - byte `0x14577e = 0x0c` in the all-weapons dump - byte `0x14577e = 0x02` in the starter-only dump - executable-side write path now supports this as selected/committed weapon row-id state inside a player/runtime state block - `0x67944` separately changes from `0x0000000b` to `0x00000001`, but this field still lacks direct static ownership in the current image and should not be treated as a closed selected-weapon home yet Current strongest practical consequence: - the earlier `0x145744 -> 0x000c` / `0x67944 -> 0x0000000c` patch recommendation is retracted - current evidence supports a channel-state ownership model plus a dynamic current-weapon row field at `0x14577e`, not a contiguous owned-id inventory list that can be safely patched in place from this evidence alone - the next RAM step should target ownership-state bytes/flags in the dynamic runtime block and the channel-runtime block model, not the static `0x1456fc` table The latest follow-up also narrows one false lead and one stronger lead inside the same dump: - the candidate commit-table field at `DAT_80064355[(channel*10)+9]` does not show plain `0x0c` / `0x0d` values in the sampled rows of this dump, so that exact byte is not acting like a direct final JL row id here - however, denser table-like clusters of `0x0c` / `0x0d` do appear much later in RAM, especially around file offsets `0x133000` and nearby `0x133416` / `0x1335d4`, which are now stronger candidates for real runtime weapon-slot or inventory-state structures than the earlier blind slot guess The newest pass tightened those late clusters one step further: - the `0x133000` neighborhood now looks more like a compact fixed-record runtime table than random data, with the strongest candidate stride landing near `0x40` - values in that area are dominated by small enum-like bytes such as `0x0d` and `0x0f`, which fit a runtime index/flag table better than text or raw pixel data - one additional pass also found an alternative clue region around `0x422c..0x4440` where triplet-like patterns such as `0x0c 0x0a 0x00` could plausibly encode a `JL-2 / 10 clips / 0 loaded` style state, but that interpretation is not yet strong enough to replace the `0x133000` cluster as the main RAM lead - the remaining blocker is now very specific: one executable-side HUD/inventory anchor still has to be tied to one of these RAM regions before the dump can be read as a confirmed runtime weapon list Current best practical consequence: - no ask-user re-dump was forced from this pass, because the file does look like the right broad memory domain, - but a stronger runtime closure still needs either a more targeted live memory capture around the player/inventory struct or a resolved id->name/UI chain inside the executable. ## Evidence detail ### Weapon table and row consumers - `psx_weapon_def_get_u16_with_mode_gate` (`0x800315d8`) computes `idx*0x26` and reads row fields from the `0x8006466a` family - `psx_weapon_def_apply_spawn_profile_by_index` (`0x8003d02c`) fans row fields into live-named globals: - `psx_weapon_spawn_type` - `psx_weapon_spawn_audio_event_id` - `psx_weapon_spawn_state_selector` - current best art read stays narrow: - `JL-2` and `JL-9` share base type/art lane via `+0x1c = 0x18` - they diverge on selector/state lane via `+0x24 = 0x4b` vs `0x0f` ### Channel commit and HUD presentation - `channel_commit_row_table` is the per-channel commit source table - `committed_selected_item_id` mirrors the committed item id after `0x8002f15c` - `psx_hud_draw_selected_item_tile_bar` (`0x800424ac`) is the strongest currently named HUD-side consumer lane - `psx_ui_color_cycle_state`, `psx_hud_selected_item_color`, `psx_hud_selected_tile_color_a`, and `psx_hud_selected_tile_color_b` now cover the nearby UI-tail state that was previously left as raw `DAT_` labels - latest pass also tightened the selected/equipped chain with fresh comments at `0x8002ef34`, `0x8002f15c`, `0x800315d8`, `0x8003d02c`, and `0x800424ac`, plus explicit extra-unlock naming at `0x80030004` -> `psx_weapon_channel_unlock_and_seed_markers` - latest pass also renamed the known writer for the extra late unlock gate: `0x800232f0` -> `psx_set_debug_extra_channel_gate` - the newest input-side pass also renamed `0x8001E37C` -> `psx_handle_special_input_code`, which is now the strongest current upstream candidate for consuming the special `0x1e` debug-trigger code range - the newest ammo-side pass also tightened the marker/runtime helper family with live names such as `psx_marker_channel_mode_is_enabled`, `psx_marker_channel_get_mode_step_value`, `psx_marker_channel_runtime_state_snapshot`, `psx_marker_channel_runtime_state_restore`, and `psx_marker_channel_dispatch_mode_action`, which are now the strongest nearby candidates for the actual ammo/count mutation lane even though the exact `clips` versus `loaded bullets` fields are still not closed - Evidence note: no alternate per-slot source was found — HUD item id is resolved via the channel commit table (DAT_80064355[(channel*10)+9]) -> committed_selected_item_id -> weapon-definition table at `0x8006466a` -> `psx_weapon_def_apply_spawn_profile_by_index` -> HUD draw. This lane helps explain what the HUD is drawing, but it is still weaker than the channel-commit chain for direct `JL-2` vs `JL-9` identity. Confirmed resolver chain (evidence-backed): - commit source: `DAT_80064355[(channel*10)+9]` is loaded at `0x8002f15c` and supplies the committed item id for a channel. - id -> row resolver: `psx_weapon_def_get_u16_with_mode_gate` (`0x800315d8`) computes `idx * 0x26` and indexes the weapon-definition table at `0x8006466a` to select the weapon row. - row -> display: `psx_weapon_def_apply_spawn_profile_by_index` (`0x8003d02c`) fans row fields into spawn/art/selectors consumed by HUD code; `psx_hud_draw_selected_item_tile_bar` (`0x800424ac`) consumes those selectors to draw the on-screen tile/art. Name/label bytes live in the weapon row and are therefore resolved implicitly by the `idx*0x26` table access rather than a separate string-lookup UI helper. - `0x8003ec8c` -> `psx_passcode_decode_to_mission_selector` - `0x80021138` -> `psx_passcode_apply_mission_selector_and_stage` - transformed compare tables: - `0x80064bbc` - `0x80064bd0` - `0x80064be4` - `0x80064bf8` - key hidden decoder behavior: - hidden index `0x0f`: writes `psx_level_runtime_header_state = 0`, returns selector `0x10` - hidden index `0x10`: writes `psx_hidden_passcode_flag = 1`, returns selector `0` - hidden index `0x11`: returns selector `0x12` Current safest read is that the hidden compare path is real and transformed/table-driven, even though the exact folklore string mapping is still not fully closed at runtime. ## Current interpretation ### What is now solid - `JL-9` is real - `JL-9` is the extra late hidden/debug lane - `JL-2` is the neighboring ordinary lane and now has stronger explicit ammo-label evidence - the checked dump is VRAM, not direct slot RAM - the checked 2 MiB main RAM dump is plausible runtime RAM, but still needs executable-side id resolution before it can close `JL-2` vs `JL-9` ### What is still open - exact runtime timing for `psx_hidden_passcode_flag` and `psx_debug_extra_channel_gate` - exact role of the denser `0x0c` / `0x0d` RAM clusters near `0x133000`, and whether one of them is the real live inventory/slot table - exact executable-side anchor that proves whether the main RAM lead is the `0x133000` cluster or the smaller `0x422c..0x4440` candidate region - direct UI label/display resolver that prints `JL-9` from committed runtime state in one instruction chain - exact `JL-2` ammo decrement/storage path - exact `JL-9` sprite/frame identity - exact shipped-map placement for either JL row ## Input code `0x1e` button-chord closure (2026-04-11 pass) Focused live MCP pass on active `SLUS_002.68` to close the exact input-side decode path around: - `psx_object_update_runtime_input_modes` (`0x80012c30`) - `psx_input_map_get_code_and_edge` (`0x8002adbc`, renamed this pass) - `psx_input_map_update_state_for_pad` (`0x8002abe0`, renamed this pass) - `psx_input_map_install_profile` (`0x80042ec4`, renamed this pass) Direct closure: 1. `local_14` in runtime input mode update is read from the input-map state table. - `psx_object_update_runtime_input_modes` calls `psx_input_map_get_code_and_edge(1,&local_18,&local_14)`. - `local_14 == 0x1e` is the exact compare that gates the debug grant helper when `psx_hidden_passcode_flag != 0`. 2. Input code resolution is exact-match against a 0x32-entry mask table. - `psx_input_map_update_state_for_pad` polls raw pad mask from `FUN_80048d04` and scans `0x80090d34 + pad*0xfc` for exact value equality. - matched index is written to current-code state (`0x80090e24 + pad*0xfc`) and returned by `psx_input_map_get_code_and_edge`. 3. Input code `0x1e` is mapped to pad mask `0x2800` in all recovered profile cases. - `psx_input_map_install_profile` writes map entries with `psx_input_map_set_code_to_padmask(1, code, mask)`. - every switch branch in this function writes code `0x1e -> 0x2800`. 4. Using the same PSX digital mask legend implied by nearby known controls (`0x08` start, d-pad nibble patterns, shoulder/face combos), `0x2800` resolves to `R1 (0x0800) + Circle (0x2000)`. Practical trigger statement now supported: - after hidden passcode state is active, the JL-9 debug grant path is triggered by the decoded special input code `0x1e`, which maps to the controller chord `R1 + Circle`. - because map lookup is exact-match, extra simultaneously held buttons can produce a different code and miss the `0x1e` gate. Confidence: - high (`~0.93`) that `0x1e -> 0x2800` is the actual decode mapping in this executable. - high (`~0.88`) that `0x2800` corresponds to `R1 + Circle` under the active digital-pad bit layout. ### Selected-weapon-adjacent pointer reclassification (2026-04-11) The user-reported pointer delta near the selected-weapon watch block is now closed as render-state, not inventory state: - all-weapons dump: pointer-like value `0x80078248` - starter-only dump: pointer-like value `0x800782b8` Live MCP decompile/xref evidence on `SLUS_002.68` shows these are the two double-buffer draw-environment records: - `psx_platform_init` (`0x80042b38`) initializes them with `SetDefDrawEnv((DRAWENV *)&DAT_80078248, ...)` and `SetDefDrawEnv((DRAWENV *)&DAT_800782b8, ...)` - the paired display environments are at `0x800782a4` and `0x80078314` (`+0x5c` from each draw-env base), matching a packed per-buffer draw/disp environment layout - `DAT_80067954` is the active draw-env pointer toggled each frame in: - `psx_present_frame_and_flip` (`0x80044188`) - `render_reset_draw_state` (`0x80042be8`) - `FUN_800461d0` (`0x800461d0`) Practical consequence: - the observed `0x80078248 <-> 0x800782b8` change is a frame-buffer/render flip artifact - this pointer pair does not encode selected weapon id, HUD selected-item row, or inventory ownership state - weapon selection/inventory evidence remains stronger in the channel-commit and weapon-definition lanes (`DAT_80064355`, `committed_selected_item_id`, `0x8006466a + idx*0x26`) ### Runtime block classification around `0x8014574c..0x801457d0` (2026-04-11) Current strongest executable-backed interpretation for the user-targeted RAM block: - this region is most likely a live player-object nested runtime-state block, not a HUD color cache and not the static channel-commit table - the watched byte at file offset `0x14577e` (RAM `0x8014577e`) is most strongly the active selected weapon row-id byte within the nested `+0x1c` runtime field family - this byte behaves like current-weapon metadata, not ownership-bitset storage and not direct ammo-count storage Concrete evidence chain: 1. `psx_apply_channel_effect_and_commit_selected_item_id` writes the committed item id from `channel_commit_row_selected_item_id[(channel*10)]` into two sinks: - global `committed_selected_item_id` (`0x80078a90`) - nested player runtime field at `*( *( *(DAT_800789f8 + 8) + 0x18) + 0x1c )` 2. `psx_set_debug_extra_channel_gate` clears that same nested `+0x1c` field in several mode-reset branches, alongside nearby nested fields (`+0x14`, `+0x16`, low byte of `+0x18`), consistent with runtime weapon-mode/state reset behavior. 3. `psx_object_create_simple_record` initializes the nested block rooted at `*(obj_component + 0x18)` as a large runtime state area and stores back-pointer/object runtime linkage there, matching a heap-resident per-object state block. 4. The dump delta at byte `0x14577e` (`0x0c` all-weapons vs `0x02` starter-only) fits this as a selected weapon row-id change rather than ownership-table cardinality or HUD palette animation. Interpretation confidence: - `0x14577e` as selected weapon row-id byte (`nested +0x1c` field family): high - broader `0x8014574c..0x801457d0` region as player nested runtime-state block: medium-high - region as primary inventory ownership table: low - region as primary ammo counters table: low - region as HUD-only cache: low ### Starter-only compare correction (2026-04-11) The follow-up compare against `binary/Crusader - No Remorse RAM starter weapon only.bin` corrects one earlier overreach in this note. What changed: - `0x1456fc..0x145748` is identical in the all-weapons and starter-only dumps, even though the observed weapon inventory is very different - therefore that attractive `0x0002 .. 0x000b` 8-byte record sequence is a static table or static-adjacent runtime seed, not the live owned-weapon list - the real dynamic lane starts at `0x14574c` and includes: - `0x14577e: 0x000c -> 0x0002` - nearby count/flag-like deltas around `0x14575c..0x14578c` - the separate watched field at `0x67944` also changes cleanly (`0x0000000b -> 0x00000001`), but the current image still has no recovered static xrefs for `0x80067944` Current safest wording after that compare: - no contiguous owned-weapon id list has been recovered yet - the storage model is better explained by channel-state ownership plus one or more dynamic current-weapon fields in the runtime block - the earlier patch-target recommendation on `0x145744` should be considered withdrawn ## Speculation and folklore Keep speculation bounded here rather than mixing it into the evidence chain. Current plausible but not fully closed reads: - `L0SR` remains the strongest current executable-backed cheat-mode candidate because it fits the recovered four-symbol hidden branch better than `L0SER` - `R1 + Circle when L0SR is active` is now the strongest executable-backed button-chord read for the gated input route; the remaining folklore-level uncertainty is about the exact pre-hidden gate-arm action, not the `0x1e` chord mapping itself - `XXXX` still plausibly maps to the hidden-pictures folklore because the decoder has a second hidden-special lane consistent with a transformed secret path - the VRAM dump likely contains the weapon HUD/icon atlas, but without labeled reference icons or a cleaner runtime capture it does not yet let us separate `JL-2` from `JL-9` visually - the main RAM dump likely contains the relevant runtime inventory state, but current evidence is split between a nearby commit-table neighborhood that does not show plain final JL ids and stronger later clusters around `0x133000` that still need one executable-side anchor to decode - the strongest current input-side static closure now turns the special `0x1e` code into pad mask `0x2800` and a high-confidence `R1 + Circle` interpretation, while still leaving the upstream pre-hidden gate-arm producer unresolved None of those points are needed for the current core conclusion that the extra late executable-backed lane is `JL-9`. ## Best next steps 1. Capture a runtime sample after the hidden passcode/debug gate is active and log which weapon channels are actually unlocked, especially `0x0d`. 2. Decode the controller-button mapping that produces the gated input code reaching `psx_debug_grant_weapon_channels_and_ammo`, so the `L0SR` trigger path is either confirmed or corrected. 3. Trace all reads and writes of `psx_debug_extra_channel_gate` to close the late extra-unlock precondition. 4. Trace the direct `JL-2 AMMO` UI path and the underlying ammo decrement/storage lane. 5. Decode the denser `0x0c` / `0x0d` RAM clusters around `0x133000` using one confirmed executable-side inventory/HUD anchor, instead of continuing whole-image sweeps. 6. If that anchor still does not land cleanly, test the smaller `0x422c..0x4440` candidate region against the same runtime meaning before requesting another dump or capture. ## Weapon acquisition systems vs id `0x0d` / `0x01` (2026-04-11 broad pass) Focused live MCP pass on active `SLUS_002.68` to classify which acquisition families can produce the verified selected ids `JL-9=0x0d` and `RP-16?=0x01`. ### Systems analyzed - default loadout/init: `psx_weapon_channels_init_mode_loadout` (`0x8002f814`) - mission transition mode application: `psx_weapon_channels_apply_mode_transition_state` (`0x8002f278`) - pickups/action-coded grants: accepted-code dispatch lane at `0x8001d3fc` - shop path: `psx_weapon_shop_try_apply_entry` (`0x8003de68`) using `DAT_80064B90` - scripted awards: `psx_section0_dispatch_root_apply_packed_channel_actions` (`0x800311f4`) - debug grants: `psx_debug_grant_weapon_channels_and_ammo` (`0x8002fd90`) ### Concrete results 1. id `0x0d` (`JL-9`) remains gated from ordinary loadout/shop paths. - loadout init has fixed unlock/ammo calls through `0x0c` and does not directly unlock `0x0d` - mission transition state helper does not add a `0x0d` direct unlock path - shop front path (`param<10`) maps through `DAT_80064B90[0..9] = 03 04 05 06 07 08 09 0a 0b 0c`, so no direct `0x0d` there 2. id `0x0d` is directly produced by debug grant and is possible in data-driven scripted/pickup lanes. - debug: `0x8002fff4` conditionally calls `psx_weapon_channel_unlock_and_seed_markers(0x0d)` only when `psx_debug_extra_channel_gate != 0` - scripted awards: packed-action decoder (`0x80031250`) calls `unlock(channel)` from seeded `DAT_8008F120` rows (runtime-seeded from section0 markers) - pickup/action-coded lane (`0x8001d3fc`) range-check path still commits accepted codes through `psx_apply_channel_effect_and_commit_selected_item_id(a0)`, including `0x0d` 3. id `0x01` is not excluded and appears in non-debug systems. - shop table byte dump confirms `DAT_80064B90 = 03 04 05 06 07 08 09 0a 0b 0c 01 05 04 03 02 07` - while `0x01` is outside the first ten direct-unlock shop slots, it is still present in shop's alternate branch (`param_1=10`) via the same channel map - pickup/action-coded commit path accepts low ids (including `0x01`) and routes them to commit - scripted packed actions are data-driven and can target `0x01` when authored in section0 records ### Exclusion read after this pass - `0x0d`: strongest current read is "not part of baseline ordinary progression," with explicit debug-gated production and only data-driven/scripted/pickup exceptions. - `0x01`: not excluded; it appears in normal non-debug content paths (shop mapping plus accepted low-id action path). ### Notes on evidence quality - The packed-action LUT base (`0x8008F120`) is runtime-seeded and can be uninitialized in static reads; classification therefore uses the seeding/writer and decoder call behavior, not a static full table dump. - The core `0x0d` gate remains directly evidenced at `0x8002fff4` in live `SLUS_002.68`. ## Appendix: key anchors ### Weapon rows and helpers - `0x8006466a` weapon-definition table base - `0x80064832` `JL-2` row (`0x0c`) - `0x80064858` `JL-9` row (`0x0d`) - `0x800315d8` `psx_weapon_def_get_u16_with_mode_gate` - **Live field map (evidence):** `row+0x20` is read as a 16-bit row field (loaded at `0x8003160c`), `row+0x22` is a 1-byte mode/gate field (loaded at `0x8003163c`), and `row+0x24` contains a per-row state/selector used by HUD/ammo resolver (example: `JL-2` shows `0x4B`, `JL-9` shows `0x0F`). The nearby ASCII `JL-2 AMMO` at `0x800642b6` aligns with code reads from the `0x800642b2/0x800642b4` region, supporting a small adjacent string/table used for ammo text resolution. - `0x8003d02c` `psx_weapon_def_apply_spawn_profile_by_index` ### Channel commit and HUD - `0x8002ef34` `psx_apply_channel_effect_and_commit_selected_item_id` - `0x8002f15c` committed item-id load from channel commit row - `0x800424ac` `psx_hud_draw_selected_item_tile_bar` - `0x800350a8` `psx_render_mode_dispatch` ### Hidden passcode and extra unlock - `0x80034e38` `psx_passcode_screen_eval_current_entry` - `0x8003ec8c` `psx_passcode_decode_to_mission_selector` - `0x80013154` `psx_object_update_runtime_input_modes` - `0x8002fd90` `psx_debug_grant_weapon_channels_and_ammo` - `0x800232f0` `psx_set_debug_extra_channel_gate` - `0x80030004` extra `0x0d` unlock call site ### Runtime dump artifacts - `binary/Crusader - No Remorse Memdump Weapons.bin` -> VRAM-sized HUD/presentation artifact - `binary/Crusader - No Remorse Weapons Main Ram.bin` -> 2 MiB plausible main RAM artifact, currently not self-decoding enough to close `JL-2` vs `JL-9` - stronger current unresolved RAM cluster candidates for live weapon-slot state: around file offsets `0x133000`, `0x133416`, and `0x1335d4` - secondary smaller RAM candidate for `JL-2 / 10 clips / 0 loaded` style state: around file offsets `0x422c..0x4440` ### Live-named globals used in this note - `psx_hidden_passcode_flag` - `psx_debug_extra_channel_gate` - `psx_level_runtime_header_state` - `channel_commit_row_table` - `channel_commit_row_selected_item_id` - `committed_selected_item_id` - `psx_weapon_spawn_type` - `psx_weapon_spawn_audio_event_id` - `psx_weapon_spawn_state_selector` - `psx_ui_color_cycle_state` - `psx_hud_selected_item_color` - `psx_hud_selected_tile_color_a` - `psx_hud_selected_tile_color_b` ## Hidden passcode decode closure (2026-04-11 pass) Focused live MCP pass on active `SLUS_002.68` against: - `psx_passcode_screen_eval_current_entry` (`0x80034e38`) - `psx_passcode_decode_to_mission_selector` (`0x8003ec8c`) - passcode tables at `0x80064bbc`, `0x80064bd0`, `0x80064be4`, `0x80064bf8` - helper `psx_passcode_generate_encoded_quad` (`0x8003ec30`) ### Closed transform logic Decode path (`0x8003ec8c`) compares transformed input, not direct ASCII: 1. `entry[1..3]` are normalized by `-0x1b` and matched against table triplets: - `T1 = [0x80064bd0 + i]` - `T2 = [0x80064be4 + i]` - `T3 = [0x80064bf8 + i]` - valid `i` range is `0..0x11` (`18` entries) 2. If no `i` matches all three, decode fails. 3. For ordinary entries (`i != 0x0f/0x10/0x11`): - `delta = entry[0] - (0x1b + [0x80064bbc + i])` - `psx_level_runtime_header_state = delta` (stored at `0x80068ab0`) - accept only when `delta < 4`; return selector `i+1` Special branches: - `i == 0x0f`: clears `psx_level_runtime_header_state` and returns `0x10` (delay-slot flow preserves `v0=0x10`) - `i == 0x10`: sets `psx_hidden_passcode_flag = 1` (`0x80067454`) and returns `0` - `i == 0x11`: returns sentinel `0x12` Important closure: all three special branches bypass the first-character delta check, so their first character is effectively wildcarded by the decoder. ### Recovered table values Read directly from `0x80064bb0..0x80064c20`: - `T0 (0x80064bbc)` = `01 03 0B 0E 0F 06 07 0A 09 12 01 02 03 03 06 08 08 12` - `T1 (0x80064bd0)` = `10 11 08 13 02 15 16 17 18 19 1A 1B 1C 04 03 0D 14 12` - `T2 (0x80064be4)` = `0B 0C 0D 0A 19 00 01 02 03 04 05 06 07 08 09 0F 0E 12` - `T3 (0x80064bf8)` = `02 0B 0C 03 0E 0F 10 11 12 13 14 15 16 17 18 0A 0D 12` Symbol alphabet (`0x80063ef0`) for passcode glyph indexes is: - `BCDFGHJKLMNPQRSTVWXZ0123456789` ### Concrete recovered hidden/debug strings Generator (`0x8003ec30`) emits: - `out[0] = T0[i] + psx_level_runtime_header_state` - `out[1] = T1[i]` - `out[2] = T2[i]` - `out[3] = T3[i]` Mapped through `0x80063ef0`, special rows are: 1. `i=0x10` (sets `psx_hidden_passcode_flag`): `?0SR` - canonical when selector state is `0`: `L0SR` - decoder confirms first character wildcard for this branch 2. `i=0x0f` (clears selector state): `?RTN` - canonical when selector state is `0`: `LRTN` - first character wildcard for this branch 3. `i=0x11` (sentinel return `0x12`): `?QQQ` - canonical when selector state is `0`: `XQQQ` - first character wildcard for this branch ### Debug JL-9 path tie-in This pass tightens the two-phase gate model using direct branch conditions: 1. Hidden branch (`i=0x10`) sets `psx_hidden_passcode_flag` (`0x80067454`) at `0x8003ed28`. 2. Input-mode handler (`0x80013154`) only calls debug grant (`0x8002fd90`) on input code `0x1e` when hidden flag is nonzero. 3. Extra JL-9 unlock (`0x80030004`, channel `0x0d`) still requires `psx_debug_extra_channel_gate != 0` at `0x8002fff4`. 4. Gate write (`0x800232f0`) still requires hidden flag zero plus `psx_level_runtime_header_state == 3`. Current best practical read remains: extra `JL-9` requires a staged hidden/debug flow; no single direct ordinary-lane unlock for `0x0d` was recovered. ## Shipped section0 slot-0x14 closure (2026-04-11 pass) Focused live MCP + scene-cache pass on active `SLUS_002.68` to answer one narrow question: can shipped non-debug authored section0/script bytes ever drive the `unlock(0x0d)` lane used by `JL-9`. ### Executable closure 1. Packed-action dispatch behavior is explicit at `psx_section0_dispatch_root_apply_packed_channel_actions` (`0x800311c4`): - decoder uses `slot = (action_byte & 0x3f)` into seeded triplets `DAT_8008f120/121/124` - triplet kind `3` path calls `psx_weapon_channel_unlock_and_seed_markers(channel)` 2. Seed mapping is explicit at `psx_section0_dispatch_root_seed_marker_channel_table` (`0x8002f518`): - `psx_section0_dispatch_root_find_marker_record_by_channel(0x14, 3, 0x0d)` - therefore slot `0x14` maps to action-kind `3`, channel `0x0d` 3. Live naming cleanup applied: - `0x8002fd90` renamed to `psx_debug_grant_weapon_channels_and_ammo` to resolve the duplicate-name collision with `0x8002e5f0` ### Shipped authored-data closure Cross-map scan of shipped PSX scene-cache root-dispatch records (`.cache/scene-cache/psx-remorse/map-*/.../scene.json`) parsed packed action dwords from record raw words and checked for bytes where `(byte & 0x3f) == 0x14`. Results: - scanned root-dispatch records: `3298` - slot-`0x14` authored-byte hits: `119` - maps with at least one slot-`0x14` hit: `22` - map ids with hits: `1,2,3,4,5,22,23,26,27,28,29,47,63,64,80,82,88,89,104,106,108,122` Representative shipped records: - map `1`, `item:21:psx-section0_dispatch_roots:left:8`, raw words `0045 1d3b 0d43 0014 0000 0020` (packed dword `0x00140d43`) - map `5`, `item:11:psx-section0_dispatch_roots:right:8`, raw words `004e 1b83 0943 0014 0000 0020` (packed dword `0x00140943`) - map `63`, slot-`0x14` hits present (`2`) - map `89`, slot-`0x14` hits present (`30`) ### Practical verdict for JL-9 legit-path question - **Ruled in**: shipped non-debug section0/script authored data does include packed action bytes that resolve to slot `0x14`, and executable seed/dispatch logic maps slot `0x14` to `unlock(channel 0x0d)`. - **Not fully universal**: this does not prove every campaign route triggers those records during normal play timing, but it does prove the non-debug shipped-data lane exists and is executable-reachable in principle. Confidence: - high (`~0.93`) that slot `0x14` is an `unlock(0x0d)`-capable packed-action slot in this build - high (`~0.91`) that shipped non-debug section0 root records contain authored slot-`0x14` bytes - medium-high (`~0.77`) that this yields legitimate in-play JL-9 acquisition in at least some non-debug scenarios (remaining uncertainty is trigger/reachability timing, not byte-path existence) ## Step-2 caller narrowing for gate-arm event (`0x800230e4`/`0x800232f0`) (2026-04-11 live pass) Goal of this pass was to close the immediate caller/event side for the gate writer branch (`param_2==0x0a`, `param_3==4`) inside `psx_set_debug_extra_channel_gate`. ### Exact executable evidence recovered 1. The gate writer remains exactly `sb v0,0x739d(at)` at `0x800232f0` in `psx_set_debug_extra_channel_gate` (`0x800230e4`) under local branch `param_2==0x0a` and case `param_3==4`. 2. Direct caller xrefs stay empty because this lane is indirect-dispatch based. 3. A concrete indirect dispatcher block was recovered at `0x800214ac..0x800215f8`: - `0x800215bc`: compares dispatch index (`a2`) against per-level table byte `DAT_80063e68[current_level]`. - `0x800215dc`: loads handler pointer from jump table `DAT_800640a0[a2]` and `jalr`s. - `0x800215cc` + `0x800215e0`: handler arguments are loaded as bytes from pointer fields in the action record (`a1=*(*(record+8))`, `a2=*(*(record+0xc))`). 4. Table slot proof: - `DAT_800640a0[0x0f] = 0x800230e4` (the target handler). - therefore the gate-arm branch requires this record-level triplet at dispatch time: - dispatch index byte = `0x0f` (to pick handler `0x800230e4`), - first argument byte = `0x0a`, - second argument byte = `0x04`. 5. Per-level gating proof from `DAT_80063e68`: - indices where table byte is `0x0f`: `54,55,56,57,58,82`. - outside those level indices, this exact `0x0f -> 0x800230e4` lane is not selected by the recovered level gate compare. ### Inferred player-facing meaning (narrowed) - strongest read is now a late/level-scripted control-event lane, not a random global input callback: the branch is reached through a level-gated action dispatcher and a control-handler jump table. - practical narrow set: player-visible triggers tied to scripted/control progression events in level-index set `{54..58,82}` that emit dispatch triple `(0x0f,0x0a,0x04)`. - still not fully singular: static analysis in this pass did not recover one uniquely named UI/menu function boundary that can be asserted as the only producer of that exact triple. ### Confidence update - high (`~0.90`) that call reachability into `0x800230e4` is through `0x800214ac..0x800215f8` + `DAT_800640a0` indirect dispatch. - high (`~0.87`) that branch trigger shape is exactly `(dispatch_index=0x0f, param_2=0x0a, param_3=0x04)`. - medium-high (`~0.76`) that player-facing source is a small late-level scripted/control action family (`54..58,82`), not a single global menu button. ### Live Ghidra artifacts changed - conservative disassembly comments added: - `0x800215bc` (level-gated dispatch compare against `DAT_80063e68[current_level]`) - `0x800215cc` (argument-byte extraction from action record pointers) - `0x800215dc` (jump-table dispatch through `DAT_800640a0`, slot `0x0f -> 0x800230e4`) ## Mission 16 / developer office mapping correction (2026-04-11 live pass) An older interpretation in this note treated the `?RTN` / `NRTN` special branch as if it directly selected `DAT_80063e54[0x0f] = 0x36`, which would have put Mission 16 / dev-office content inside the `DAT_80063e68 == 0x0f` gate family. That interpretation is now superseded. ### Correct runtime flow 1. The `?RTN` special branch is decode index `i = 0x0f`, but it does **not** return `0x0f`. - it clears `psx_level_runtime_header_state` at `0x8003ed10` - due to the decoder's return flow, raw selector returned to caller is `0x10` 2. `psx_passcode_screen_eval_current_entry` then maps through `DAT_80063e54[0x10]`, not `DAT_80063e54[0x0f]`. - live table bytes now anchor this as `DAT_80063e54[0x10] = 0x3f` 3. Caller state at `0x80034d84` therefore uses mapped `s0 = 0x3f`, not `0x36`. 4. Apply path still reaches selector-`0x10` case at `0x8002142c`, which writes `DAT_800675e4 = 0x1d`, sets `DAT_80067379 = 2`, and calls the common helper with `a0 = 0x36`. ### Practical consequence for the dev-office hypothesis - `a0 = 0x36` in the selector-`0x10` apply case may still be related to the user-observed developer-office lane, but that is **not** the same claim as `current_level_map_id == 0x36` or `DAT_80063e68[current_level] == 0x0f`. - so the previously attractive chain `?RTN -> map 54 -> gate family 0x0f` should be treated as withdrawn. - the surviving office hypothesis is weaker and narrower: `?RTN` / `NRTN` may still route into office-like content, but current static evidence does **not** prove that this places the session directly inside the slot-`0x0f` gate-arm family. ### Confidence update - high (`~0.95`) that the correct caller mapping is `?RTN index 0x0f -> raw selector 0x10 -> DAT_80063e54[0x10] = 0x3f` - high (`~0.91`) that the older `DAT_80063e54[0x0f] = 0x36` interpretation was the wrong table index for the runtime `?RTN` path - medium (`~0.66`) that `a0 = 0x36` in the apply case still corresponds to the same player-facing developer-office concept without one runtime label capture ### Live Ghidra artifacts changed - conservative comments added in later follow-up: - `0x8003ed10` - `0x80034d84` - `0x8002142c` ## Step-2 producer-path closure (control-opcode lane) (2026-04-11 live pass) Goal of this pass was to close the unresolved upstream producer for the gate-arm dispatcher block (`0x800214ac..0x800215f8`) by tracing backward from `0x800215cc` / `0x800215e0` into the parent script/control lane. ### Exact executable evidence recovered 1. `0x800215cc` / `0x800215e0` byte loads are inside unnamed control helper `0x800214ac`. - it reads pointer arguments from `record+8` and `record+0xc` (`arg1=*(*(record+8))`, `arg2=*(*(record+0xc))`), then dispatches through `DAT_800640a0[slot]` after level gate compare at `0x800215bc`. 2. `0x800214ac` is now proven as table entry `DAT_80063610[49]` (address `0x800636d4`). 3. Upstream call chain for that entry is now explicit: - `psx_object_behavior_opcode_dispatch` (`0x8002677c`) dispatches through `DAT_800641ac[opcode_index]`. - table entry `DAT_800641ac[54] = 0x80027ecc`. - `0x80027ecc` performs a second dispatch by loading sub-opcode from `*(*(a1+0))` and jumping through `DAT_80063610[subop]`. - when `subop==49`, it reaches `0x800214ac`. 4. Therefore the unresolved writer tuple path is no longer a free-floating callback; it is a **nested behavior/control opcode producer path**: - behavior opcode index `54` -> secondary sub-opcode `49` -> `0x800214ac` -> slot table `DAT_800640a0`. 5. The gate-arm writer tuple remains unchanged at the sink: - slot `0x0f` selects `psx_set_debug_extra_channel_gate` (`0x800230e4`), and branch write requires args `0x0a`, `0x04`. ### What this closes and what remains open - closed: where the action-record bytes come from structurally. - they are produced from the object behavior/control stream argument pack used by opcode-54 handler `0x80027ecc` and its sub-dispatch into `DAT_80063610[49]`. - still open: one singular player-facing mission event label. - static executable evidence now identifies the producer subsystem and dispatch indexes, but does not yet uniquely name one authored script instance as "the" office event. ### Narrowed map/event set in this pass - executable gate compare lane remains constrained by `DAT_80063e68[current_level] == 0x0f` (current narrowed level-id family from prior pass: `{54..58,82}`). - cache-side cross-check in this pass: - available scene-cache member from that family was map `82` only in current catalog snapshot. - map `82` section0 root records showed no direct slot-`0x0f` packed-byte hits, which is consistent with this pass's new upstream finding that the writer can be produced by the **object control-opcode stream lane** (not only section0 root packed-action bytes). ### Practical player-visible status - status is now stronger than "unknown source": - this is a scripted in-level behavior/control opcode event lane (nested opcode `54 -> 49`), i.e., a real gameplay/script progression path rather than a menu-only artifact. - but still not fully singular: - one concrete authored trigger instance (exact mission script/object instance) is still unresolved. ### Confidence update - high (`~0.94`) that producer path is `behavior opcode 54 -> sub-op 49 -> 0x800214ac -> slot 0x0f handler 0x800230e4`. - high (`~0.90`) that action-byte arguments at `0x800215cc/0x800215e0` are sourced from opcode argument-pointer records, not direct UI input. - medium (`~0.74`) that a single map-82-like scripted control event family is the practical player-visible source, pending one final authored-instance bind. ## Step-2 producer-path reachability correction (2026-04-11 live pass) This follow-up rechecked whether the previously claimed `opcode 54 -> sub-op 49 -> 0x800214ac` chain is currently proven reachable in active `SLUS_002.68`. ### Direct executable evidence from live script/ref scans 1. `DAT_800641ac` does contain high-index entries: - `DAT_800641ac[49] = 0x80027d2c` - `DAT_800641ac[54] = 0x80027ecc` 2. `0x80027ecc` still dispatches through `DAT_80063610[subop]`, and `DAT_80063610[49] = 0x800214ac` remains true. 3. But static reachability in this image is currently narrower than that table topology suggests: - only recovered caller to `psx_object_behavior_opcode_dispatch` (`0x8002677c`) is at `0x80026740` - that caller guards with `(opcode_word - 1) < 0x0a` at `0x80026710` - therefore this known path only selects `DAT_800641ac[0..9]` 4. `0x80027ecc` currently has only one recovered incoming reference, and it is data-only (`0x80064284` table slot), not a direct call/jump xref. 5. `0x800214ac` likewise currently has data-only incoming evidence (`0x800636d4` table slot), not a direct call xref. ### Practical correction - The earlier `54 -> 49` statement should now be treated as **table-topology evidence**, not yet as a proven active runtime producer path. - What remains high-confidence is the sink-side gate-writer shape: - `0x800214ac..0x800215f8` is a real level-gated slot dispatcher. - `DAT_800640a0[0x0f] = 0x800230e4` and sink args remain `0x0f,0x0a,0x04`. - The open upstream question is now more precise: - recover a second proven caller path into `0x8002677c` (or alternate path into `0x800214ac`) that can actually feed high dispatch indices. ### Live Ghidra artifacts changed - conservative disassembly comments added: - `0x80026710` (known caller path bounds dispatch index to `< 0x0a`) - `0x8002685c` (documents `DAT_800641ac[a0]` use and known caller-path bound) - `0x80027f0c` (documents secondary `DAT_80063610[subop]` use under `0x80027ecc`) ### Confidence update (corrected) - high (`~0.93`) that sink-side slot dispatch into `0x800230e4` via `DAT_800640a0[0x0f]` is real. - high (`~0.89`) that current static refs do **not yet** prove active reachability of `DAT_800641ac[54] -> 0x80027ecc` from the known caller path. - medium (`~0.71`) that high-index behavior-opcode producers still exist in runtime via currently un-recovered caller/context lanes. ## Natural gate-arm active-caller recheck (2026-04-11 live pass) Scope of this pass was narrow and read-only: verify whether any currently proven gameplay caller path can really feed the sink-side dispatcher at `0x800214ac..0x800215f8` with the known tuple `(slot 0x0f, arg1 0x0a, arg2 0x04)`. ### New direct evidence 1. Sink-side facts remain unchanged and strong: - `0x800214ac` performs level-gated indirect slot dispatch through `DAT_800640a0[a2]`. - slot `0x0f` still resolves to `0x800230e4` (`psx_set_debug_extra_channel_gate`) from table entry address `0x800640dc`. - sink arg bytes are still loaded as `a1=*(*(record+8))` and `a2=*(*(record+0xc))`. 2. Upstream topology is still true but only as topology: - `0x80027ecc` is still table-backed at `DAT_800641ac[54]` (`0x80064284`). - `0x80027d2c` is still table-backed at `DAT_800641ac[49]` (`0x80064270`). - `0x800214ac` is still table-backed at `DAT_80063610[49]` (`0x800636d4`). 3. Proven active gameplay caller lane into behavior-op dispatch remains bounded: - callers into `psx_run_object_behavior_program_tick` (`0x80026690`) include `psx_object_integrate_motion_and_route_visible` callsites (`0x80012668`, `0x800131d0`) plus two additional runtime callsites (`0x80011e78`, `0x80011f78`). - the dispatch handoff at `0x80026740` is still guarded by `(opcode_word - 1) < 0x0a` at `0x80026710`. - therefore this proven gameplay lane can only select `DAT_800641ac[0..9]`, not index `54`. 4. No additional direct caller/xref path into `0x8002677c`, `0x80027ecc`, or `0x800214ac` was recovered in this pass. ### Practical interpretation update - strongest **active** producer candidate is now the currently proven gameplay behavior-tick lane: `psx_object_integrate_motion_and_route_visible -> psx_run_object_behavior_program_tick -> psx_object_behavior_opcode_dispatch`. - but that lane currently cannot feed the `54 -> 49 -> 0x800214ac` chain because of the `< 0x0a` guard. - so the older `54 -> 49` chain should stay in the model only as valid table topology, and be deprioritized as an immediate natural-event explanation until a second real caller/context is recovered. ### New concrete static target Next high-value target is to recover the missing caller/context lane that reaches `psx_object_behavior_opcode_dispatch` with indices above `9`, or an alternate feeder that reaches `DAT_80063610[49]` directly. Most concrete first pivot for that is the mode/transition hub around `FUN_80020f7c` (`0x80020f7c`) and its active callers (`0x80022068`, `0x80022e58`, `0x80023080`, `0x8002748c`), because this is now the strongest live non-table-only control lane that already consumes level-family state (`DAT_80063e68`) and is visibly active in runtime transitions. ### Confidence update - high (`~0.95`) that sink-side tuple shape and slot mapping remain correct. - high (`~0.92`) that the currently proven gameplay behavior-op lane is bounded to `0..9`. - medium-high (`~0.79`) that the natural gate-arm event still exists in an unrecovered alternate caller/context lane rather than in the currently proven `0..9` path. ## Dispatcher caller/context reassessment around `0x80026690` / `0x8002677c` (2026-04-11 live pass) This pass was dedicated to finding an active context that can naturally feed the JL-9 sink dispatcher chain, with explicit focus on behavior dispatch around `0x80026690/0x8002677c` and sibling control-program runners. ### Direct caller closure (behavior lane) 1. `psx_object_behavior_opcode_dispatch` (`0x8002677c`) still has one recovered direct caller: - `0x80026740` inside `psx_run_object_behavior_program_tick` (`0x80026690`). 2. That caller still enforces `(opcode_word - 1) < 0x0a` at `0x80026710` before calling `0x8002677c`. 3. Therefore the only proven active behavior lane currently dispatches indices `0..9`, not high indices such as `54`. ### Secondary-dispatch topology (still valid, still not active-proven) 1. `DAT_800641ac[54] = 0x80027ecc` remains true. 2. `0x80027ecc` dispatches through `DAT_80063610[subop]`; `DAT_80063610[49] = 0x800214ac` remains true. 3. `0x800214ac` remains the level-gated sink block that can route slot `0x0f` into `0x800230e4`. 4. But no additional caller/xref path was recovered that proves runtime entry into `0x80027ecc` or `0x800214ac` from an active lane beyond table topology. ### Strongest active context candidate now The strongest active context remains the object update loop: - `psx_object_integrate_motion_and_route_visible` - calls `psx_run_object_behavior_program_tick` at `0x80012668` and `0x800131d0` - also calls sibling `psx_object_run_control_opcode` at `0x80012ae0` This makes the behavior/control sibling pair the best live context family, but only the behavior half is currently connected to `0x8002677c`, and that half is still bounded to `< 0x0a`. ### 54 -> 49 status update - keep `54 -> 49` as structurally correct table evidence. - retire it as an immediate active-caller explanation for now. - working priority should stay on recovering a second active caller/context into `0x8002677c` (or alternate proven feeder into `0x800214ac`). ### Live Ghidra artifacts changed in this pass 1. Created function: - `0x80027ecc` -> `psx_behavior_subopcode_dispatch` (body `0x80027ecc..0x80027f34`). 2. Renamed function: - `0x80020f7c` -> `psx_control_event_apply_level_channel_preset`. 3. Added decompiler comments: - `0x80026710`: documents `< 0x0a` proven caller bound. - `0x80027f0c`: documents secondary sub-op dispatch and unresolved active reachability of entry `49`. - `0x80020f7c`: documents role as level-channel preset helper used by control-event slot handlers. ## JL-9 natural-event synthesis from new clues (2026-04-11 live pass) Scope of this pass was narrow and evidence-only: integrate the two new clues (timed failure segment and "last mission is multi-map") into a ranked natural-arm model, then apply conservative live Ghidra artifacts only where call/xref evidence is explicit. ### Ranked hypothesis list (current best) 1. **Optional scripted event within countdown/transition chain** (strongest) - slot-`0x0f` arm remains indirect-only (`0x800640dc -> 0x800230e4`) and tuple-specific (`0x0a,0x04`), which fits a conditional scripted branch better than a guaranteed branch. - progression callback lane is table-driven (`0x80064210 -> 0x80027548`) and therefore naturally optional/contextual. 2. **Countdown-failure branch timing miss** - countdown tick is on active world-frame path (`0x8002b9a0 -> 0x8002b738 -> 0x80020794`). - map boundary split at `0x800208f0` (`<=54 -> 0x1a`, `>54 -> 0x1b`) gives a concrete timed bifurcation where a natural run can miss tuple timing. 3. **Map-to-map transition interaction** - multi-map clue is code-backed via `psx_map_progression_table` reads in both preset and callback lanes (`0x80020fa4`, `0x80027560`). - this supports "state advanced before arm tuple" as a real mechanism, but currently weaker than #1 because no single transition branch is proven to always suppress arm. 4. **Countdown-success branch itself** (weakest of the four) - no recovered direct evidence that plain success path alone arms `psx_debug_extra_channel_gate` without the tuple-specific slot `0x0f` branch. - success/failure appears to gate mode flow, while arm remains a separate control-event tuple branch. ### Strongest new reason to test one path first Most decisive next test target is the **optional scripted-event path inside the countdown/transition chain**. Reason: - this is the only ranked path simultaneously supported by all new evidence anchors in one chain: - frame-timed countdown integration (`0x8002b738 -> 0x80020794`), - map-54 bifurcation (`0x800208f0`), - multi-map progression callbacks (`0x80020fa4`, `0x80027548`/`0x80027560`), - and indirect slot-`0x0f` tuple arm (`0x800640dc -> 0x800230e4`, `(0x0a,0x04)`). So the best discriminating runtime probe is now: observe whether tuple `(0x0a,0x04)` is skipped when the countdown branch/preset path takes the map-boundary/progression route before slot-`0x0f` dispatch. ### Live Ghidra artifacts changed in this pass No new function renames were applied in this pass (conservative threshold not met for additional naming). Decompiler comments added: - `0x80020794`: countdown tick call-chain note (`level_session_loop -> world_frame_tick -> countdown_transition_tick`). - `0x800208f0`: explicit map-54 boundary bifurcation note (`<=54 -> 0x1a`, `>54 -> 0x1b`). - `0x80020fa4`: progression-table read note with multi-map timing implication. - `0x80027548`: callback-table-driven progression note (`xref via 0x80064210`). - `0x800230e4`: indirect-only slot-table reachability reminder (`0x800640dc`), preserving tuple-branch specificity.