re: progress word B's writer is sub_8219F2E8 - it sets bit (stage - 24)

Section 5.5 narrowed the search to 21 functions but could not name one, because
the scan assumed the copy destination was an r1-relative stack local. It is not.
Reading the known word-A writer instead of guessing its shape shows a frame
register: bl singleton, addi r3,r31,96 (dest), addi r4,r11,80 (src = obj+80), bl
copier, stw r10,96(r31). That one wrong assumption made the first scan return 0
for all 21, the known-good included - the third filter this week killed by its own
control.

With the real shape - addi r3,rF,K before the copier, then stores at K+d on rF -
word B sits at d = 1956-80 = 1876, and exactly one of the 21 hits: sub_8219F2E8,
copy dest r31+560, storing 2436(r31), and 560+1876 = 2436. Control: the word-A
writer 0x821C1630 stores at relative +0, i.e. word A.

The proof is the bias. Word B's writer computes addi r11,r11,-24 then 1 <<
(index-24) before or-ing into the copy; word A's writer shifts by the raw stage
number with no bias. That reproduces section 5.1's read-side rule - n >= 24 means
bit (n-24) of word B - on the write side, so word B's bits 0-5 are challenge
stages 24-29, the same six that 5.4's MISSION_ID names. The two writers are exact
complements.

It has exactly one real caller, 0x821A0B1C inside 0x821A0AD0, whose strings are
BASE_INFO and DEBRIEFING - so clearing a challenge mission is recorded from the
debriefing screen.

A second bitfield falls out of the same function: it reads the copy at +1880
(2440(r31) = obj+1960, the word after B), re-reads obj+1960 after the setter, xors
old against new, and loops the 32 result bits calling 0x82175C20 with bit + 64. So
obj+1960 is a second 32-bit progress bitfield with ids 64-95, whose newly-set bits
are announced one at a time - an unlock-notification loop.

Against 5.3's own prediction: neither sub_8219F2E8 nor its caller references Time
or Points, 0 for both. 5.3 expected word B's writer to also store the stage's
Time/Points record. It does not - this is the bit-setter only, and the record
store is still unlocated.

All seventeen artefacts byte-identical.
This commit is contained in:
Sylpheed RE agent
2026-08-28 12:50:16 +00:00
parent 5d44b52b07
commit bae2fcf623
2 changed files with 93 additions and 0 deletions

View File

@@ -7090,3 +7090,33 @@ stack copy at `localbase + 1876`, invisible to an offset scan.
no `Time`. **Two `Points` vocabularies.**
🔑 **Next candidate:** `0x8218EFE0` — the only one of the 21 carrying `BASE_EXTRA`.
---
## ✅ 2026-08-28 — progress word B's writer: `sub_8219F2E8`, sets bit (stage 24)
§5.5's scan failed because it assumed the copy destination was an `r1`-relative
stack local. Reading the known word-A writer shows it is a **frame register**:
`addi r3, r31, 96` / `addi r4, r11, 80` / copier / `stw r10, 96(r31)`.
⚠️ **That one wrong assumption returned 0 for all 21, the known-good included —
the third filter this week killed by its own control.**
✅ With the real shape, word B (`d = 195680 = 1876`) hits **exactly one** of the
21: **`sub_8219F2E8`**, copy dest `r31+560`, storing `2436(r31)` (`560+1876`).
Control: `0x821C1630` stores at relative `+0` = word A.
🔑 **The proof is the bias.** Word B's writer computes `addi r11, r11, -24` then
`1 << (index24)`; word A's shifts by the raw stage number with no bias. That
reproduces §5.1's read-side rule (`n >= 24` → bit `n24` of word B) **on the write
side**, so word B's bits 05 are challenge stages 2429 — the same six §5.4 names.
**Called from exactly one place:** `0x821A0B1C` in `0x821A0AD0` (`BASE_INFO` +
`DEBRIEFING`) — clearing a challenge mission is recorded from the debriefing.
**A second bitfield falls out:** the same function diffs `obj+1960` (the word
after B) before/after the setter and loops its 32 bits calling `0x82175C20` with
`bit + 64` — a second progress bitfield with ids **6495**, announced per bit.
⚠️ **Against §5.3's prediction:** neither the writer nor its caller references
`Time` or `Points` (0 both). It is the **bit-setter only**; the Time/Points record
store is still unlocated.