re: WITHDRAWN -- the phase-1 condition IS polled, at 5 Hz

Tracing Stage 02 phase 1 by control flow (following op 12 jumps and the six
conditional branches) gives the whole chain: timer5 @ 4.0s -> trigger 0x2D30
(which also does timer_set/timer_resume) -> start_coroutine at 0x2FE8 ->
coroutine 0xBB9C, whose body is wait_s(0.2) ; yield -> ... -> 0xF524, the
ADN110/111/112 polls that latch set_flag(8).

Exactly one of phase 1's 79 coroutine entries reaches 0xF524 under a real
control-flow walk, and it is a 0.2s polling loop. So the condition is checked at
5 Hz from 4 seconds into the phase, and my 'the condition coroutine is not
polling' explanation -- which I had promoted to real evidence after the poke
experiments -- is withdrawn.

Two weaker methods gave wrong answers first and are named: linear decode fell
through into unrelated code and implicated 30-odd entries; 'nearest preceding
entry' implicated the right one only by proximity luck, 0x3988 bytes away. And I
matched start_coroutine operands across phases at first, which is meaningless
since the operand is relative to [phase+232] -- the same per-phase base trap that
broke isl.py's jump targets, hit again three iterations later.

The poke results now need a different explanation. The likeliest is that neither
poke produced the tested state: +16 is never read by built-in 69, and +4 = 0
takes the early-exit path an UNDEPLOYED unit also takes. Recorded as a hypothesis
with the experiment that would settle it.
This commit is contained in:
Sylpheed RE agent
2026-08-25 20:50:35 +00:00
parent eb7a065313
commit 795bbf303c

View File

@@ -476,3 +476,45 @@ That leaves the third candidate: **the anomalous run's mission never loaded**,
and its `IN FLIGHT` was a misdetection. Consistent with what became of it — it
was **frozen on a black screen** when checked afterwards. Not proven, but it is
now the only surviving explanation rather than one of three.
## 🔴 WITHDRAWN: "the condition coroutine is not polling"
That explanation — promoted to "real evidence" after the poke experiments — is
**wrong**. Tracing Stage 02 phase 1 by **control flow** (following op 12 jumps
and the six conditional branches, not linear decode) gives the whole chain:
```
timer5 @ 4.0s -> trigger 0x2D30 (timer_set(1200,180); timer_resume; set_flag(0))
-> start_coroutine at 0x2FE8
-> coroutine 0xBB9C : wait_s(0.2) ; builtin103 ; yield ; unit_hp_pct(TCN001,…)
-> … -> 0xF524 : unit_state(ADN110/111/112) -> set_flag(8)
```
**Exactly one** of phase 1's 79 coroutine entries reaches `0xF524` under a real
control-flow walk, and its body is a **`wait_s(0.2)` loop**. So the condition is
polled at **5 Hz**, from 4 seconds into the phase — the opposite of what I
concluded.
⚠️ Two weaker methods gave the wrong answer first, and both are worth naming:
*linear decode* fell through into unrelated code and implicated 30-odd entries;
*"nearest preceding entry"* implicated `0xBB9C` only by luck of proximity
(`0x3988` bytes away). Only following branches settles containment.
⚠️ I also matched `start_coroutine` operands **across phases** at first, which is
meaningless — the operand is relative to `[phase+232]`, which differs per phase.
Same trap that broke `isl.py`'s jump targets, hit again three iterations later.
### 🟡 So why did the pokes do nothing? A different explanation is needed
With the polls running at 5 Hz, "nobody was looking" is out. The likeliest
remaining reason is that **neither poke produced the state the predicate tests**:
* `+16` — built-in 69 never reads it (established at `0x8226ADF0`);
* `+4 = 0` — that takes the handler's **early-exit** path, which is the same
branch an *undeployed* unit takes. The script very likely reads that as "not
here" rather than "destroyed".
**This is a hypothesis, not a finding.** What would settle it: read what built-in
69 returns for a genuinely destroyed squadron versus a zeroed handle — the run
that caught `ADN111` dying naturally at 433 s is the reference, and its record is
the thing to compare against.