230 lines
No EOL
7 KiB
Markdown
230 lines
No EOL
7 KiB
Markdown
# PSX JL-9 In-Level Event Investigation
|
|
|
|
This note isolates one question from the broader JL-9 work on PlayStation `SLUS_002.68`:
|
|
|
|
- what natural in-level event writes `psx_debug_extra_channel_gate` and makes the later hidden `L0SR` + `R1 + Circle` path unlock `JL-9`?
|
|
|
|
## Current status
|
|
|
|
The downstream half is now closed.
|
|
|
|
- user validation proved that forcing `0x8006739d = 0x01`, then entering `L0SR`, then pressing `R1 + Circle` successfully adds `JL-9`
|
|
- therefore the remaining mystery is entirely on the natural gate-arm side
|
|
- `MFM4` is no longer the main mystery; it is only the strongest **natural prime candidate**
|
|
|
|
Current best interpretation:
|
|
|
|
- the more direct thing being bypassed by the manual poke is the in-level event itself
|
|
- natural `MFM4` failing means `MFM4` alone is not sufficient
|
|
- the missing event is most likely an uncommon or optional scripted/control event in a small late-level family
|
|
|
|
## Proven sink-side mechanics
|
|
|
|
### Gate byte
|
|
|
|
- `psx_debug_extra_channel_gate` = `0x8006739d`
|
|
- writer: `0x800232f0`
|
|
- reader: `0x8002fff4`
|
|
- storage is byte-wide (`sb` write, `lbu` read)
|
|
- late check is nonzero-based, not literal-value-based
|
|
|
|
### Natural gate-arm branch
|
|
|
|
Natural arm still converges on `psx_set_debug_extra_channel_gate` at `0x800230e4`.
|
|
|
|
The exact write branch is:
|
|
|
|
- tuple: `(slot=0x0f, arg1=0x0a, arg2=0x04)`
|
|
- hidden must still be off
|
|
- `psx_level_runtime_header_state` must be `3`
|
|
- only then does `0x800232f0` store `1` to `0x8006739d`
|
|
|
|
### Hidden trigger half
|
|
|
|
Later JL-9 unlock still requires:
|
|
|
|
- hidden passcode branch `L0SR` / `?0SR`
|
|
- input code `0x1e`
|
|
- practical mapping `R1 + Circle`
|
|
|
|
That later path reads `0x8006739d` at `0x8002fff4` and performs the extra `0x0d` unlock when nonzero.
|
|
|
|
## Proven dispatcher path
|
|
|
|
### Sink feeder
|
|
|
|
`0x800214ac..0x800215f8` is now promoted as:
|
|
|
|
- `psx_level_gate_slot_dispatch_from_action_record`
|
|
|
|
Recovered structure:
|
|
|
|
- dispatch slot byte comes from pointer at `record + 0x00`
|
|
- `arg1` byte comes from pointer at `record + 0x08`
|
|
- `arg2` byte comes from pointer at `record + 0x0c`
|
|
- handler call is indirect through `psx_level_gate_slot_handler_table[slot]` at `0x800640a0`
|
|
|
|
Important table entries:
|
|
|
|
- `psx_level_gate_slot_handler_table[0x0d] = 0x80022c6c`
|
|
- `psx_level_gate_slot_handler_table[0x0e] = 0x80022ea8`
|
|
- `psx_level_gate_slot_handler_table[0x0f] = 0x800230e4`
|
|
|
|
## Recovered slot-family behavior
|
|
|
|
### Slot `0x0d`
|
|
|
|
`0x80022c6c` is now named:
|
|
|
|
- `psx_control_event_slot0d_handler`
|
|
|
|
Recovered strong branch:
|
|
|
|
- `(0x0a,0x02)` => mission-complete passcode generation/display lane
|
|
|
|
### Slot `0x0e`
|
|
|
|
`0x80022ea8` is now named:
|
|
|
|
- `psx_control_event_slot0e_handler`
|
|
|
|
Recovered branches:
|
|
|
|
- `(0x0a,0x01)` => mission-complete passcode generation/display lane using quad index `0x0f`
|
|
- `(0x0a,0x02..0x04)` => mode/timer setup branches
|
|
- `(0x0a,0x06)` => selector/stage apply lane that force-applies selector `0x0f`
|
|
|
|
### Slot `0x0f`
|
|
|
|
`0x800230e4` remains:
|
|
|
|
- `psx_set_debug_extra_channel_gate`
|
|
|
|
Recovered `0x0a` cases:
|
|
|
|
- `1`
|
|
- `2`
|
|
- `3`
|
|
- `4`
|
|
- `0x2e`
|
|
|
|
The JL-9 natural arm branch is specifically:
|
|
|
|
- `(0x0a,0x04)`
|
|
|
|
## Host-level narrowing
|
|
|
|
The recovered gate family is still:
|
|
|
|
- `{54,55,56,57,58,82}`
|
|
|
|
Best current host ranking:
|
|
|
|
1. `54`
|
|
2. `55`
|
|
3. `56`
|
|
4. `57`
|
|
5. `82`
|
|
6. `58`
|
|
|
|
Why `54` remains the best anchor:
|
|
|
|
- `MFM4` is the only ordinary published code that statically satisfies the prime-side conditions
|
|
- selector `0x0f` maps to map/level `54`
|
|
- `DAT_80063e68[54] = 0x0f`
|
|
|
|
Why this still does not close the event:
|
|
|
|
- user-tested natural `MFM4` did **not** produce `JL-9`
|
|
- so level-family membership alone is not sufficient
|
|
- the missing event now looks optional, late, rare, or route-specific
|
|
|
|
## Upstream producer state
|
|
|
|
### What still exists as topology
|
|
|
|
The older behavior/subop chain still exists in tables:
|
|
|
|
- `psx_behavior_opcode_handler_table[54] = 0x80027ecc`
|
|
- `psx_behavior_subop_handler_table[49] = 0x800214ac`
|
|
|
|
`0x80027ecc` is now named conservatively:
|
|
|
|
- `psx_behavior_subopcode_dispatch`
|
|
|
|
### Why it is weaker now
|
|
|
|
The only currently proven caller into `psx_object_behavior_opcode_dispatch` is still range-limited:
|
|
|
|
- known caller path bounds `(opcode_word - 1) < 0x0a` at `0x80026710`
|
|
- that means the known active path can only reach indices `0..9`
|
|
- so `54 -> 49` is still valid topology, but it is no longer the best **active** explanation
|
|
|
|
Current practical reading:
|
|
|
|
- keep `54 -> 49` alive as structure
|
|
- deprioritize it as the leading runtime explanation until a second active caller/context is recovered
|
|
|
|
## Strongest event hypothesis now
|
|
|
|
The strongest remaining hypothesis is:
|
|
|
|
- the natural gate-arm is a sibling in the same late control-event family as mission-complete / transition handlers around `0x80022c6c..0x80023390`
|
|
- it is probably an uncommon or optional in-level scripted/control outcome
|
|
- it is more likely tied to a rare objective-path or late-event branch than to ordinary mission completion itself
|
|
|
|
Why this is stronger than the older theories:
|
|
|
|
- the sink-side tuple and slot family are concrete
|
|
- mission-complete siblings are already proven in adjacent handlers
|
|
- hard-clear / beat-the-game theory stayed weak
|
|
- direct forced-gate tests show the real unknown is the natural writer event, not the hidden code
|
|
|
|
## Ghidra changes applied in this event pass
|
|
|
|
### Functions / blocks
|
|
|
|
- `0x800204fc` -> `psx_show_mission_complete_congrats_text`
|
|
- `0x80020f7c` -> `psx_control_event_apply_level_channel_preset`
|
|
- `0x800214ac` -> `psx_level_gate_slot_dispatch_from_action_record`
|
|
- `0x80022c6c` -> `psx_control_event_slot0d_handler`
|
|
- `0x80022ea8` -> `psx_control_event_slot0e_handler`
|
|
- `0x80027ecc` -> `psx_behavior_subopcode_dispatch`
|
|
|
|
### Tables / data
|
|
|
|
- `0x80063e54` -> `psx_selector_to_map_id_table`
|
|
- `0x80063e68` -> `psx_map_id_to_gate_slot_table`
|
|
- `0x80063eac` -> `psx_map_progression_table`
|
|
- `0x80063610` -> `psx_behavior_subop_handler_table`
|
|
- `0x800640a0` -> `psx_level_gate_slot_handler_table`
|
|
- `0x800641ac` -> `psx_behavior_opcode_handler_table`
|
|
|
|
### Key comments added
|
|
|
|
- `0x800214bc`
|
|
- `0x800215bc`
|
|
- `0x800215cc`
|
|
- `0x800215dc`
|
|
- `0x800215e0`
|
|
- `0x800230e4`
|
|
- `0x800232f0`
|
|
- `0x80026710`
|
|
- `0x8002685c`
|
|
- `0x80027f0c`
|
|
- `0x80034d60`
|
|
|
|
## Best next code targets
|
|
|
|
1. Fully classify the control-event family around `0x80022c6c..0x80023390` as one switch/tuple cluster and align each sibling branch with concrete player-facing outcomes.
|
|
2. Recover one actual authored producer for `(slot=0x0f, arg1=0x0a, arg2=0x04)` by tracing the action-record inputs feeding `0x800215cc` / `0x800215e0`.
|
|
3. Find a second active caller/context into `psx_object_behavior_opcode_dispatch` or an alternate feeder into `0x800214ac` that can justify a high-index producer path in live gameplay.
|
|
|
|
## Deferred emulator experiments
|
|
|
|
Keep these queued for later:
|
|
|
|
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: ordering test (`event -> L0SR -> trigger` versus `L0SR -> event -> trigger`) |