# What the game reads at boot โ€” `config.ini`, and which GameParts actually exist **Status:** โœ… `CONFIRMED` and **decoded** for the language selection. โ” a bounded **negative** for the boot *order*. ๐ŸŸก for what the registration strings imply about the attract loop. Contributes to [MISSION Q6](../port/MISSION.md), and firms up Q4's ids. ## โœ… `config.ini` โ€” the disc's only config, and it selects the language `config.ini` sits at the **disc root**, is 400 bytes, and is the **only** `.ini`, `.cfg` or `.txt` anywhere on the disc. Its own header comment (Shift-JIS) names it: > `ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณ/ใ‚ฒใƒผใƒ ใƒ‘ใƒผใƒˆๅˆๆœŸ่จญๅฎšใƒ†ใƒผใƒ–ใƒซ` > โ€” *"Application / GamePart initial settings table"* > > `SYSTEM ใ‚ปใ‚ฏใ‚ทใƒงใƒณใซใฏใ€ใ‚ฒใƒผใƒ ๅŠใณๅ„ใ‚ฒใƒผใƒ ใƒ‘ใƒผใƒˆใงๅ…ฑ้€šใซใ‚ขใ‚ฏใ‚ปใ‚นใ™ใ‚‹ๆƒ…ๅ ฑใ‚’่จ˜่ฟฐใ™ใ‚‹` > โ€” *"the SYSTEM section describes information accessed in common by the game and > each game part"* ```ini [SYSTEM] [LANGUAGE] = eng ; default #0x01 = eng ; XC_LANGUAGE_ENGLISH #0x02 = jpn ; XC_LANGUAGE_JAPANESE #0x03 = deu ; XC_LANGUAGE_GERMAN #0x04 = fra ; XC_LANGUAGE_FRENCH #0x05 = esp ; XC_LANGUAGE_SPANISH #0x06 = ita ; XC_LANGUAGE_ITALIAN ``` **This is the mechanism behind the EN/JP screen pairs.** The console's `XC_LANGUAGE_*` setting selects a three-letter code, and that code is what picks `GP_TITLE`'s English or Japanese build ([`ui-title-build-map.md`](ui-title-build-map.md)) and the `.pak` families. Default is `eng`, keyed by the empty line โ€” an unlisted language falls back to English. โœ… decoded. The executable does read the file: the string `config.ini` is at `.rdata 0x82062b44`. ## โ” But the boot ORDER is not in it โ€” and this is the whole search space `[SYSTEM]` โ€” the section the file's own comment says holds what the game and every game part share โ€” is **empty**. So the file the game itself calls the *GamePart initial settings table* says nothing about which part runs first or what follows what. **Reach of the negative:** this is the only config file on the disc (one `find` over the whole extract). The boot order is therefore not in disc-side configuration at all; it is in code, or in a table inside the executable that has not been located. ## ๐ŸŸก `GP_ADVERTISE_DEMO` is never registered โ€” the attract loop is not its own part The executable carries one diagnostic string per GamePart registration site (`silph::GamePartTask::RegisterToFactory::RegisterToFactory is failed!`) โ€” the same evidence the corpus used to pin the id table ([`challenge-mission-gate.md`](challenge-mission-gate.md) ยง3). Extracting all of them gives **24 of the 29 ids bound to a C++ class**; full list in [`data/gamepart-class-ids.txt`](data/gamepart-class-ids.txt). **The five ids with no registration site are `1`, `2`, `16`, `18`, `28`** โ€” in the id table's naming, `GP_ADVERTISE_DEMO`, `GP_SELECT_STORAGE`, `GP_DEMO`, `GP_SELECTOR`, `GP_TEST`. That `GP_ADVERTISE_DEMO` (1) is among them matters for Q6, and it agrees with what was measured: the attract loop is **the title screen replaying `ADV.wmv`** ([`movie-binding.md`](movie-binding.md)), not a transition into a separate advertise part. The id table names a part the shipped build never registers. ๐ŸŸก **not โœ…**, because this is an argument from a *diagnostic string*: no error message for id 1 implies no registration site for id 1. That is how the corpus already reads these strings, and it is sound, but it is not the code. ### Two bonuses for Q4 * **`3` and `4` are the same class** โ€” `GamePart_SaveLoad` is registered twice. The id table's separate `GP_LOAD` / `GP_SAVE` names are two ids on one part. * The ids behind the menu buttons now match the executable's own **class** names, not just a list of table names: `GamePart_Options` = 8, `GamePart_Tutorial` = 25, `GamePart_Extras` = 5, `GamePart_MissionSelect` = 7, `GamePart_MovieTheater` = 6, `GamePart_Title` = 0. Still a **name match** โ€” screen title โ†” class name โ€” but anchored one level closer to the code. ## What is still missing for Q6 The *transitions*. Nothing found so far says "title, then movie, then title" โ€” the manifest gives the boot-side **assets** in play order ([`movie-binding.md`](movie-binding.md)), `config.ini` gives the **language**, and the registry gives **which parts exist**. What decides to advance is in `GamePart_Title`'s own code, and reading it is a static PPC job that has not been started. ## โ” The transitions are code, not data โ€” the reach of that negative Q6's remaining half asked what the game *reads* to decide the boot order. The answer is: **nothing. It is not data-driven.** Four independent places were checked, and the sequence is in none of them. | looked in | result | |---|---| | **disc configuration** | `config.ini` is the only config file on the disc, its `[SYSTEM]` section is empty (above) | | **the movie manifest** | carries the boot-side *assets* in play order, and no transitions โ€” [`movie-binding.md`](movie-binding.md) | | **a persistent part-id field** | already refuted: the requested GamePart id exists **only as a stack argument in flight**, with no literal store anywhere โ€” [`challenge-mission-gate.md`](challenge-mission-gate.md) ยง5 | | **the id table's attract entry** | the string `GP_ADVERTISE_DEMO` at `0x820a1fe8` has **zero xrefs of any kind**; nothing in the code reads it | So a transition is a **call with an id argument**, chosen by code. There is no table to read and nothing to poke. **Where that code is, as far as it was traced.** The `RegisterToFactory<0, class silph::GamePart_Title>` diagnostic string at `0x820a3d60` is referenced from exactly one place, `sub_8280E148` โ€” the registration site โ€” which also takes the address of **`sub_821C7D98`** (`addi`), the position a factory template puts its creator. ๐ŸŸก That identification is by position and convention, not proven; `sub_821C7D98` itself has **0 `.rdata` references**, consistent with a small `new`+ctor thunk rather than the state machine. The substantial function in the same class neighbourhood is `sub_821C6458` (4 460 bytes, has EH, 15 `.rdata` refs), and **reading it has not been attempted**. ### What this means for the port The boot sequence is **authored**, not transcribed โ€” and that is fine, because the sequence itself is measured end to end: ``` developer splash (a RATC screen, not a video) โ†’ ADV.wmv โ†’ title + PRESS โ’ถ โ”€โ”€idle ~8โ€“10 sโ”€โ”€> fade to black โ†’ ADV.wmv in full โ†’ title โ†’ โ’ถ โ†’ main menu ``` with the fade-through-black timings in [`screen-transitions.md`](screen-transitions.md) and โ’ท from the main menu returning to the title. ## ๐ŸŸก `sub_821C6458` read โ€” the title's states are NAMED in the executable The page above left this function as the named next step: "the substantial function in `GamePart_Title`'s neighbourhood, and **reading it has not been attempted**". It has now been looked at โ€” not disassembled line by line, but characterised, which is enough to sharpen Q6. **It is the title part's screen-state function.** 4 460 bytes, has EH, and called from **exactly one** place โ€” `sub_821C7850`, which sits in the same neighbourhood as the creator the registration site points at (`sub_821C7D98`). It makes 33 distinct calls. **What it names.** Its `.rdata` string references are: | string | at | |---|---| | **`TITLE_SCREEN`** | `0x820a3d3c` | | **`TITLE_MENU`** | `0x820a3d30` | | **`LOADING`** | `0x820a214c` | | `BASE_INFO` (ร—2) | `0x820a20ec` | Those are the states measured off the running game, in the game's own words: the title carrying `PRESS โ’ถ BUTTON`, the five-button menu, and a loading state. And `BASE_INFO` is this corpus's own marker for a **screen-config lookup** rather than a table read ([`REFUTED.md`](REFUTED.md), "`BASE_INFO` discriminates screen-config from table-read, 9/9 vs 10/10"). ### The sharper negative So the title part **does ask for configuration keyed by `TITLE_SCREEN` and `TITLE_MENU`** โ€” and [`config.ini`](#-configini--the-discs-only-config-and-it-selects-the-language), the disc's only config file, contains **no such sections**. Its only sections are an empty `[SYSTEM]` and `[LANGUAGE]`. The lookups find nothing, and the values are whatever the code defaults to. That is a better answer than "the order is not in config": the game *asks the question*, the shipped disc *does not answer it*, and the defaults live in code. For the port it means the state **names** are transcribable even though their contents are not. ๐ŸŸก **Not โœ….** This is a characterisation from string references and call counts, not a read of the control flow. Two self-references (`0x821c6498`, `0x821c6b7c`) inside the function look like jump tables โ€” a switch, which is what a state machine compiles to โ€” but that was **not confirmed**, and nothing here shows which state leads to which. ### ๐Ÿ”ด Refuted on the way, because it looked like a find The words at `0x820a3b48`โ€ฆ resolve as neat `{func, func, ptr}` triples and read convincingly as a state/handler table. **They are not.** The bytes immediately before them are the tail of `โ€ฆSaveLoad>::RegisterToFactory is failed!`, and the `0x8210c1xx` targets are zero-filled descriptors โ€” static-initialiser records trailing the registration strings, not a dispatch table. A plausible-looking array of function pointers next to relevant strings is not evidence of anything until its neighbours are read. ## ๐ŸŸก The title's transition is a screen lookup BY NAME โ€” `sub_821CC860` Reading the code around each of the three state names shows the **same four instructions** at all three sites: ``` lwz r29, 20(r30) ; an object off the part lwz r3, 4(...) ; ... bl 0x822F2328 lis r11, 0x820A li r6, 0 lwz r4, 24(r30) addi r5, r11, 15676 ; "TITLE_SCREEN" (15664 = "TITLE_MENU", lwz r3, 88(r30) ; 8524 = "LOADING") bl 0x821CC860 ; <- lookup(this+88, this+24, NAME, 0) mr r4, r3 bl 0x82187B78 ; <- install the result ``` So a title-side transition is **`sub_821CC860(โ€ฆ, "", 0)` followed by `sub_82187B78(result)`** โ€” a *string-keyed* screen lookup, then an install. Not a numeric id, not a table index. That also fits `GP_ADVERTISE_DEMO` having zero xrefs: at this level the screen graph is keyed by **name**, not by GamePart id. ### The candidate name vocabulary `sub_821CC860` is called from **28 distinct functions**. Collecting the upper-case identifier strings those callers reference gives 35 names, and they split into two obvious families: * **screen/state names** โ€” `TITLE_SCREEN`, `TITLE_MENU`, `LOADING`, **`DIFFICULTY`**, `EXTRA_MENU`, `TUTORIAL_MENU`, `STANDARD_MENU`, `DEBRIEFING`, `CHALLENGE`, `MISSIONS`, `LIVE_BOARD`, `LOCAL_BOARD`, `EXTRA_MENU`; * **config keys** โ€” `TEXT_FONT`, `TEXT_HEIGHT`, `LINE_SPACE`, `GAMMA_RGB`, `GAMMA_WB`, `TEXT_SPEED_PER_LETTER`, `EXIT_VALUE`, `INPUT_DIR`, `MENU_ENABLE_SKIP`/`_DISABLE_SKIP`, `PAUSE_SE`, `JINGLE`, โ€ฆ plus `BASE_INFO` in **19 of the 28** callers, which is this corpus's existing marker for a screen-config function. **Three of the screen names are independently corroborated by measurement**: `DIFFICULTY` is exactly the screen `NEW GAME` opens, `EXTRA_MENU` matches the `EXTRAS` submenu and `TUTORIAL_MENU` the lesson list โ€” all three measured off the running game in [`menu-navigation-semantics.md`](menu-navigation-semantics.md) before this function was ever looked at. โš ๏ธ **Why this is ๐ŸŸก and not โœ….** The 35 strings are what those callers *reference*, **not** proven arguments to `sub_821CC860` โ€” the list plainly mixes screen names with config keys, so it is a **candidate vocabulary**, not a decoded one. Confirming it means checking, per call site, which string actually lands in `r5`. That was not done. ### What is still unread Which state leads to which. The three lookups sit in different branches of one function and at least one (`TITLE_MENU`) is guarded by a `cmplwi`/`bne`, but the branch structure was not traced, so the *order* still comes from measurement, not from the code. ## โœ… The argument is now decoded โ€” and it refutes my own corroboration The section above listed a **candidate** vocabulary from "upper-case strings the callers reference", flagged ๐ŸŸก because those are not proven arguments, and named the check: *confirm per call site which string actually lands in `r5`.* That check has been run, and it fired. **Method.** Forward register simulation over the 100 instructions before each of the **48** call sites of `sub_821CC860`, tracking `lis` / `addi` / `subi` / `mr`. Full table in [`data/name-lookup-callsites.txt`](data/name-lookup-callsites.txt). **Gated on a control, and the first version failed it.** A backward scan for `addi r5, โ€ฆ` recovered only 7/48 and **missed both title sites I had read by eye** โ€” because there the name goes into `r27` first and reaches `r5` via a later `mr`. The rewritten forward simulation reproduces all three known sites (`TITLE_SCREEN`, `TITLE_MENU`, `LOADING`) and then recovers **46 of 48**. ### ๐Ÿ”ด `DIFFICULTY` and `EXTRA_MENU` are *not* arguments Last section I wrote that three names were "independently corroborated by measurement": `DIFFICULTY`, `EXTRA_MENU`, `TUTORIAL_MENU`. **Only `TUTORIAL_MENU` survives.** `DIFFICULTY` and `EXTRA_MENU` never appear in `r5` at any of the 48 sites โ€” they are strings that merely live in the same functions. The corroboration I claimed was an artifact of the loose filter, and the ๐ŸŸก I put on it is exactly what caught it. ### The 28 names that ARE passed ``` BASE BASE(x3) BG(x3) BLACK(x2) BUTTON DEBRIEFING DETAIL EXTRA FADE(x3) FILE(x5) GAME GAMMA_RGB GAMMA_TITLE INFO KEY LIVE_BOARD(x2) LOADING(x2) LOCAL_BOARD(x2) MENU(x2) MENU_DISABLE_SKIP MESSAGE NEW_ITEM(x2) PAD SOUND TITLE(x2) TITLE_MENU TITLE_SCREEN(x2) TUTORIAL_MENU WINDOW ``` ### ๐Ÿ”ด So `sub_821CC860` is not a screen factory The previous section called it one. The real argument list says otherwise: `BG`, `BLACK`, `FADE`, `FILE`, `KEY`, `PAD`, `SOUND`, `WINDOW`, `GAMMA_RGB`, `MENU_DISABLE_SKIP` are **not screens**. It is a **generic name-keyed lookup** โ€” a named-entry getter used for config and resources throughout the executable, of which the title part happens to call it with its three state names. That weakens nothing about the title finding itself โ€” `sub_821C6458` really does transition by calling this with `TITLE_SCREEN` / `TITLE_MENU` / `LOADING` โ€” but it removes the inference that the 28 names are a screen vocabulary. They are a **lookup-key vocabulary**, mostly config. ## โœ… The title's state machine โ€” decoded The last open piece of Q6 was *which state leads to which*. It is a plain `switch` and it is now read. ``` 821c6474 lwz r11, 136(r30) ; state = this+0x88 821c6478 cmplwi cr6, r11, 0x9 ; ten states, 0..9 821c647c bgt cr6, 821c6480 lis/addi r12, 0x821C6498 ; jump table 821c648c lwzx r0, r12, r0 821c6494 bctr ``` โš ๏ธ The table at `0x821C6498` disassembles as ten `lwz r16, N(r28)` instructions. **It is data.** That is the "self-reference" this page flagged two sections ago as *looking* like a jump table โ€” it is one, and the disassembler was decoding its words as code. **Three of the ten states install a named screen**: **0 โ†’ `TITLE_SCREEN`**, **2 โ†’ `TITLE_MENU`**, **8 โ†’ `LOADING`**. **Eighteen transitions**, every one a literal `li rX, N ; stw rX, 136(r30)`: ``` 0 โ†’ 1, 2 2 โ†’ 4 4 โ†’ 0, 5, 8ร—4 6 โ†’ 7, 9, 2 8 โ†’ 2 1 โ†’ 2, 2 3 โ†’ 4 5 โ†’ 6 7 โ†’ 9 9 โ†’ (none) ``` Full table with addresses in [`data/title-state-machine.txt`](data/title-state-machine.txt). ### ๐ŸŸก It lines up with what was measured โ€” read as corroboration, not proof The boot reaches the title (state 0) and โ’ถ opens the main menu (state 2); the graph has `0 โ†’ 2` directly and `0 โ†’ 1 โ†’ 2`. โ’ท at the main menu returns to the title, and `4 โ†’ 0` is the **only** edge back to state 0, reached from `2 โ†’ 4`. Entering a submenu goes through `LOADING` and comes back, and `4 โ†’ 8` (four separate sites) then `8 โ†’ 2` is exactly that shape. Those readings are **mine, matching a graph to observed behaviour** โ€” the conditions on the edges are not decoded, so nothing here proves which input picks which branch. ### What is still not decoded * **the condition on each edge** โ€” which input or event selects it; * **states 1, 3, 5, 6, 7, 9** install no named screen, so what they do is unknown; * **state 3 is never a destination** in this function, so something outside sets it. ## โœ… The edge conditions, for the state that has them โ€” an EVENT CODE The previous section left "the condition on each edge" open. For the state that matters it is now read. **`sub_821C6458`'s third argument is an event code.** State 4 โ€” reached from `2 โ†’ 4`, i.e. immediately after the main menu is installed โ€” is the **input-waiting state**, and it dispatches on that argument: ``` 821c6b5c cmplwi cr6, r27, 0x19 ; 26 events, 0..25 821c6b60 bgt cr6, 821c6b68 addi r12, r12, 27516 ; second jump table, at 0x821c6b7c 821c6b78 bctr ``` **Six of the 26 are handled**; the other twenty fall through and change nothing. | event | โ†’ state | | |---|---|---| | **0** | **0** | `TITLE_SCREEN` โ€” back to the title | | 3 | 8 | `LOADING` | | 5 | 8 | `LOADING` | | 8 | 8 | `LOADING` | | 10 | 5 | โ€” | | 25 | 8 | `LOADING` | So last section's edges `4 โ†’ 0, 5, 8ร—4` are each now attributed to a specific event, and the shape of the state graph is complete for the input state. ### ๐ŸŸก A correspondence worth noticing, and not more than that The main menu has five items, โ’ท returns to the title, and this table has **one event to state 0, four to `LOADING`, one elsewhere**. It is tempting to read that as *โ’ท = event 0, four items load an external archive, `EXTRAS` stays inside `GP_TITLE`* โ€” which would match everything measured. **That is a count-match, not a decode.** The event numbers are not named, nothing here shows event 3 is a particular menu row, and state 5 installs no named screen, so the `EXTRAS` half of the story has no support at all. Recorded as an observation so the next person sees the shape; it is **not** a buttonโ†’event map. ### What is still not decoded * **what the event numbers mean** โ€” button id, menu-item index, or message id; * **conditions on edges out of the other states** (`0 โ†’ 1` vs `0 โ†’ 2`, `6 โ†’ 7/9/2`); * states 1, 3, 5, 6, 7, 9, which install no named screen. ## ๐Ÿ”ด Correcting my own framing: `sub_821C6458` is ONE PHASE, not the whole part Chasing where the event code comes from turned up the level above, and it revises what the previous three sections called "the title part's state machine". `sub_821C6458` has exactly one caller, `sub_821C7850`, and that caller is itself a dispatcher โ€” on a **second, outer state field**: ``` 821c786c lwz r11, 16(r30) ; only runs at all when this+16 == 3 821c7874 bne cr6, 821c787c lwz r11, 132(r30) ; phase = this+0x84 821c7880 cmplwi cr6, r11, 0x4 ; FIVE phases, 0..4 821c789c bctr ; table at 0x821C78A0 ``` | phase | handler | | |---|---|---| | 0 | `sub_821C5690` | **the splash** โ€” independently identified as the splash mechanics in the `iterate3E` notes, before any of this | | 1 | inline at `0x821c790c` | | | 2 | `sub_821C5818` | | | 3 | `sub_821C5EC0` | | | **4** | **`sub_821C6458`** | the title/menu machine โ€” `TITLE_SCREEN` / `TITLE_MENU` / `LOADING` | So `GamePart_Title` has **two nested state fields**: a phase at `this+132` (five values) selecting which sub-machine runs, and the state at `this+136` (ten values) *inside phase 4*. Everything the previous sections decoded โ€” the ten states, the eighteen edges, the event dispatch โ€” is **phase 4 only**. Phases 0โ€“3 are untouched, and one of them is the developer splash. That phase 0 lands on the function the corpus had already fingered as the splash, from a completely different direction, is the useful check here. ### And the event code is forwarded, not created All five phase handlers are called as `(this, r29, r28)` with the *same* arguments, and `r28` is `sub_821C7850`'s own third argument, passed through untouched. So the event vocabulary is defined **at least one level above** this function. Finding what `3`, `5`, `8`, `10`, `25` mean means going up again, and that was not done. ## Going up one more level โ€” three of four callers pass a constant event `sub_821C7850` has **four** direct callers. Recovering the `r5` argument at each: | caller | event passed | |---|---| | `sub_821C7CB8` | **0** (โ†’ back to the title) | | `sub_821C7BA0` | **0** | | `sub_821C47A0` | **5** (โ†’ `LOADING`) | | `sub_821C5580` | **not constant** โ€” `lwz r5, 4(r27)`, read out of a structure | So the vocabulary is still not enumerable from here: the interesting caller reads its event from a field. โ” What `3`, `8`, `10` and `25` mean remains open. **`sub_821C5580` is also where the outer gate is set:** ``` 821c5640 li r11, 3 821c5644 stw r11, 16(r28) ; this+16 = 3 โ€” the exact value sub_821C7850 tests 821c5650 lwz r5, 4(r27) ; event from a struct field 821c5658 bl 0x821C7850 ``` That answers the "what does `this+16 == 3` gate on" question from the previous section: this function arms it. ### ๐ŸŽ And it names the title's music The same function, a few instructions earlier: ``` 821c560c li r5, 1103 821c5610 li r4, 4 821c561c lwz r3, 0(r29) 821c5620 bl 0x8217ACF8 ; a sound-play call ``` **1103 is a BGM cue id** โ€” `BGM_103`. That closes an open residual from Q8/Q10 ("which bank is the menu's music is not on the disc"), and it checks out three ways; see [`structures/bgm-two-stems.md`](structures/bgm-two-stems.md). ## The other phases, characterised โ€” and phase 0 confirms the splash twice over Same treatment for the phase handlers that were still unread. Strings each one references, and whether it carries a jump table of its own: | phase | handler | size | strings referenced | own switch | |---|---|---|---|---| | 0 | `sub_821C5690` | 380 | **`LOGO`** | none | | 2 | `sub_821C5818` | 1 576 | `BASE_INFO`, **`BUTTON`**, **`TITLE_SCREEN`** | none | | 3 | `sub_821C5EC0` | 1 220 | *(none)* | one, `bctr` at `0x821c5ef8` | | 4 | `sub_821C6458` | 4 460 | `BASE_INFO`, `LOADING`, `TITLE_MENU`, `TITLE_SCREEN` | two (known) | **Phase 0 references `LOGO`** โ€” a second, independent confirmation that it is the developer splash. The `iterate3E` notes reached `sub_821C5690` from the guest side and named the splash's `LOGO` items; this reaches the same function from the registration site and finds the same string. **Phase 2 draws the title *with* the `PRESS โ’ถ BUTTON` plate** โ€” it references `TITLE_SCREEN` and `BUTTON`, and [`ui-title-build-map.md`](ui-title-build-map.md) established from the archive that the plate is its own build (2/3), composited over the title art. ### ๐ŸŸก A hypothesis for a puzzle this corpus has had open for months **The title is installed from two different places**: phase 2, and phase 4's state 0. Same screen, different code. [`canary-scripted-input-traps.md`](canary-scripted-input-traps.md) has long recorded, and never explained, that *the title which ends the boot accepts โ’ถ while the title the attract loop returns to accepts nothing* โ€” with the giveaway that a draw capture in each state is identical, 13 quads at the same rects. Two code paths installing the same screen is exactly the shape that would produce that. **Candidate:** the boot title is **phase 2**; the attract-returned title is **phase 4, state 0**. Only phase 2 wires up the โ’ถ handling. โš ๏ธ **Tested, and dead.** โ’ท from the main menu is the decoded edge `4 โ†’ 0`, so the โ’ท-returned title *is* phase 4 state 0 โ€” and **โ’ถ works there**, opening the main menu, with โ’ถ on the boot title as the control in the same run. Phase 4 state 0 handles โ’ถ, so the two-code-paths explanation is refuted. See [`canary-scripted-input-traps.md`](canary-scripted-input-traps.md), which the test did narrow usefully: only the **attract**-returned title is inert, not every title after the first. ### โš ๏ธ One query in this section failed its own control Counting `stw rX, 136(r30)` per phase returned **0 for phase 4**, which is known to have 18 โ€” the operand text is `r11, 136(r30)` and the pattern did not allow the space. The per-phase store counts were discarded. The `bctr` half *does* pass its control (phase 4 shows both known tables) and is reported above. ## โœ… The phase transitions โ€” and they invalidate yesterday's test Nine stores to the phase field `this+132` across the `GamePart_Title` neighbourhood, attributed to the handler each lives in (control: the same query shape finds 30 stores to `this+136`, โ‰ฅ the 18 known in phase 4): | store | sets phase | lives in | |---|---|---| | `0x821c4fbc` | 2 | outside the handlers (entry/setup) | | `0x821c4fe0`, `0x821c4fec` | 4 | outside the handlers | | `0x821c5010` | *(not an immediate)* | outside the handlers | | `0x821c592c` | **0** | phase 2 | | `0x821c5b00` | **3** | phase 2 | | `0x821c6370` | **4** | phase 3 | | **`0x821c6e04`** | **2** | **phase 4, in the event-0 block** | | `0x821c7950` | 2 | the dispatcher | ``` entry โ†’ 2 2 โ†’ 0 (splash) 2 โ†’ 3 3 โ†’ 4 4 โ†’ 2 (event 0) ``` ### ๐Ÿ”ด Withdrawing the refutation from the previous iteration The event-0 block sets **both** fields, one instruction apart: ``` 821c6df8 li r11, 2 821c6e00 stw r28, 136(r30) ; state = 0 821c6e04 stw r11, 132(r30) ; phase = 2 ``` So **โ’ท from the menu lands in phase 2**, not phase 4 state 0. The previous section tested "โ’ถ on the โ’ท-returned title" believing it was probing phase 4 state 0 โ€” **it was probing phase 2**, the same phase as the boot title. The test therefore says nothing about phase 4 state 0, and the refutation it produced is **withdrawn**. Worse for me and better for the idea: โ’ถ working there is exactly what the hypothesis predicts, since phase 2 is the phase that references `BUTTON`. **Status of the hypothesis: untested, and now consistent with two observations** (โ’ถ works on the boot title and on the โ’ท-returned title, both phase 2). What it still needs is the **attract**-returned title's phase โ€” the one thing no test so far has read.