diff --git a/docs/COMPILER-NOTES.md b/docs/COMPILER-NOTES.md
index ee575a934..af454398d 100644
--- a/docs/COMPILER-NOTES.md
+++ b/docs/COMPILER-NOTES.md
@@ -44,6 +44,24 @@ source method or adding a compiler mechanism. At revision
This does not justify permuting arguments inside a bank or omitting an argument.
RAC2 already uses this call-shape method in `func_0030E700` and Barlow helpers.
+Meaningful one-pass statement groups are also present in active RAC1 game C at
+[`1d8228cc`](https://github.com/OpenRAC/rac1-decomp/tree/1d8228cc0c81da7bc2bdc3e77479ee050e0ef434):
+[`taxi_and_mine_layer.c`](https://github.com/OpenRAC/rac1-decomp/blob/1d8228cc0c81da7bc2bdc3e77479ee050e0ef434/src/overlays/l03_kerwan/taxi_and_mine_layer.c#L1391)
+groups an actual mission-field read, and
+[`map_menu_filters.c`](https://github.com/OpenRAC/rac1-decomp/blob/1d8228cc0c81da7bc2bdc3e77479ee050e0ef434/src/overlays/shared/map_menu_filters.c#L212)
+groups an actual inner loop. `tools/overlay_asm.py` discovers these overlay
+sources; the public release maps include matched entries from them. Following
+the current RAC1 source-practice rule, a nonempty `do { real_operation; } while (0)`
+can group necessary operations here too. It may preserve compiler loop notes,
+scheduling or label alignment without adding runtime instructions. Empty loops,
+dummy values, false tests, extra accesses and changed call contracts remain
+excluded; a group must preserve the original control flow and side effects.
+The three centered-text draws (`func_002CDA20`, `func_002CF2B8`, `func_002D53F0`)
+group their real formatter call, preserving its 64-bit style argument and the
+text getter's signed 32-bit EE address result. `func_L02_003FB000` groups its
+final 16-bit report-state store. All 84 strict release programs pass, with the
+functions present in each carrying program. These are source forms, not compiler patches.
+
Different C declarations across units need a consistent interface or adapter for
a native ABI; an EE match alone does not establish a working native port.
Qualify the actual RAC2 source/profile and every carrying release, rather than
diff --git a/docs/MATCHING-BLOCKERS.md b/docs/MATCHING-BLOCKERS.md
index 0d7344c1b..3377df261 100644
--- a/docs/MATCHING-BLOCKERS.md
+++ b/docs/MATCHING-BLOCKERS.md
@@ -34,6 +34,22 @@ agreed result here. A persistent personal issue comment is no longer required fo
## Recent verified updates
+### Player initialization and retained allocation constraints
+
+**4 November 2026, strict linked match; pending PR review.**
+`func_L22_002A8B98` is 172 bytes and matches both placements in each release with
+ordinary assignments and the real signed32 return of `func_L22_002C9898`.
+Four parameter/selector grouping trials did not match; no group or additional
+flag is needed. The borrowed player prefix describes the measured field offsets
+without creating an owning allocation.
+
+The same investigation did not resolve `func_002B6658`, `func_L22_003D0840` or
+`func_L13_0041AB30`. True callback/SetState contracts, consumed one-pass groups
+and measured grid views remain short or allocate differently; their markers
+and near misses are retained. The second vector grid ends at the counter at
++0x620 (three rows of four cells), so an owning four-row view is not justified.
+
+
### `func_002B60B0` returns nothing
**4 November 2026, local match (pending PR).** `func_002B60B0` (the motion-parameter
@@ -3410,7 +3426,7 @@ attributed for provenance, not exclusive ownership.
| **Clear through a register address at a nonzero offset:** `func_002B5678` | Open | Retail has `addiu $v0,$s1,0x60 ; sq $zero,0($v0)`, the clear macro's register operand. After #258, C gives `por` + `sq $v0,0x60($s1)`. **Next:** a profile that materializes the address of a displaced const-0 TImode store. |
| **A `por` clear at a zero displacement:** `func_L02_003DB200`, `func_L02_00423520`, `func_L23_00422798`, and with the copy barrier `func_L02_003DBA70`, `func_L23_00429DC8` | Proposed: PR #281 | These functions clear a stack vector with `por $v0,$zero,$zero` + `sq $v0,0($sp)`. Since #258, the fold still makes that `sq $zero,0($sp)`, because the address is register-only.
A finer retail count (one place per catalogue row, boot game code from 0x26EA00, a store counted when every path into it starts from the `por`) finds 183 `por`-fed stores at a zero displacement, in 153 functions. Every one of them is to `0($sp)`; none goes through another register. Against that, 396 `sq $zero,0(reg)` stores in 263 functions, of which 82 (58 functions) are also to `0($sp)`.
PR #281 adds the opt-in `-frac2-zero-ti-por`: with it, the fold leaves a store to `0($sp)` alone, and stores through any other register still fold. It is neutral by default (806 of 806 units). Forced on everywhere, it changes exactly the 4 units whose matched functions clear `0($sp)` with `sq $zero`. The five functions above are exact with it. `func_L23_003C16D8` stays out: three of its clears go through a computed stack address. |
| **A cse block boundary that leaves no instruction:** `func_L02_003D26B0`; the five 632-byte twins `func_L11_00436D08`, `func_L11_00449C70`, `func_L11_0044C850`, `func_L11_004530E0`, `func_L11_004566C0`; `func_L02_003F1BA8`, `func_L02_0040BBD8`; `func_L07_00378898` | Open | Retail keeps a gcse (PRE) copy, for example `daddu $s5,$s4,$zero` of `e + 0x10`, that this cc1 makes only when cse1 and cse2 both see a label at that spot. Adding `\|\| x == 7 \|\| y == 9` to the test reproduces it, with extra code. An empty `do { } while (0)` reproduces it exactly but is rejected (#257, fc4a6c6). For `func_L07_00378898`, `-fno-cse-follow-jumps -fno-cse-skip-blocks` reproduces the block, as a probe only. **Next:** the original's construct at that spot, or a measured cse rule. |
-| **A non-empty `do { ... } while (0)` as a block barrier:** `func_L02_003FB000` (288 bytes), `func_0033A520` (360 bytes, 28 places) and `func_002D53F0` (140 bytes, 28 places) | Question for maintainers | `func_L02_003FB000` is exact with its last store wrapped in `do { ... } while (0);`. The loop notes give the shared `jr $ra` retail's alignment `nop`, which cc1 adds only after a loop. `func_0033A520` needs a block boundary after its rounding of `D_001B23A4 * 300`; the wrapper gets that block right, and six allocation words remain. `func_002D53F0` is exact with its draw call wrapped and 329.0f held in a local; its rounding of `D_001B23A4 * 329.0f + 0.5f` is the same shape as `func_0033A520`'s. `func_L20_004450D8`, listed here before, now matches without a wrapper: written as the group-walk loop of its merged twins (`func_L02_003EAD38` and others), with their repeated `q != 0` test.
Two merged functions use the same wrapper as a documented scheduling barrier: `func_00124818` and `func_L23_0041C8C0` (ours). The repository check rejects only the empty form. Since AGENTS.md rules out artificial barriers, we have not landed more. **Question:** is a non-empty wrapper acceptable as a macro-shaped statement, or should the two precedents be revisited? |
+| **Meaningful nonempty one-pass groups:** `func_L02_003FB000`, `func_002CDA20`, `func_002CF2B8`, `func_002D53F0` | Strict linked matches; pending PR review | **4 November 2026:** current active RAC1 game C supplies the missing source-method precedent; see [source methods](COMPILER-NOTES.md#source-methods-shared-with-rac1). The actual last store gives Maktar 288 bytes, 1/1 places in each USA release (PAL identity absent). Actual formatter-call groups give the three text draws 140/136/140 bytes and 28/28 USA v1.01 places each; their canonical identities are absent in the other releases. The formatter style remains 64 bits; its return and the signed32 address getter follow their real definitions. Old narrower-style trials remain invalid. All 84 strict programs pass with zero C fallbacks, and all 92 placements of the five added functions are present in their carrying programs. This supersedes the earlier blanket hold on nonempty groups, not the ban on empty loops or fake dependencies. `func_0033A520` is still unresolved: its historical exact probe also assumes an unevidenced const global and fails both alternate releases. |
| **Jump tables the build cannot place:** `func_L06_003FDA08`, `func_L13_00422CB8`, `func_L03_003C4578`, `func_L02_003F9AF0`, `func_L02_003F9B70`, `func_L02_003FE1B0` | Solved, PR pending | A computed goto through retail's own table, in the form of `func_00310A40`, matches all six. The three level 2 records' in-order switches were wrong: retail's tables map the values to the bodies in another order. Decode the table before trusting a record's `switch`. |
| **Labels behind hazard nops:** `func_L18_004308F0`, `func_L02_00411530` | Merged, #264 | Opt-in `-frac2-hazard-labels`. 198 retail branch targets after a compare or `mtc1` sit on the nop, and SN's `Ps2EeAs` agrees. Forced on everywhere it is neutral (778 of 779, one `.mdebug`). |
| **gcse critical-edge split, Cygnus block merge:** `func_002CD720`, `func_L02_003E79F8`, `func_L02_003FCCA0`, `func_L18_004283F0` | Merged, #261 | Opt-in `-frac2-gcse-unsplit-edges` and `-frac2-keep-block-order`. `func_L23_0041B9F0` reaches retail's size with the gcse switch and still stops at the copy order. |
diff --git a/nonmatching/README.md b/nonmatching/README.md
index fac93384b..fd4d7a712 100644
--- a/nonmatching/README.md
+++ b/nonmatching/README.md
@@ -209,14 +209,11 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are
| [`func_001185E8`](core/func_001185E8.c) | 1496 | Candidate is 1508 bytes; the SDK vfprintf.obj float-to-string cvt, reconstructed from the assembly - its six-way Inf/-Inf/NaN/Ind dispatch matches instruction for instruction, and the twelve bytes left are register allocation (masked value in `$v1` against retail's `$s2`) plus the exponent tail |
| [`func_002A0488`](game/func_002A0488.c) | 48 | Non-volatile duplicate-arm form (as in func_002A0468) gives every retail word but the `nop` before `jr $ra` (blocker 4): 44/48, 0/28. An inline-asm `nop` probe matches 28/28 but is not acceptable. Neighbours reading the same word look handwritten; see retained source. |
| [`func_002CA070`](game/func_002CA070.c) | 1372 | 26 October 2026 (finisher F1, after big31): 343 of 343 instructions, all equal except five registers in case 8 (local-alloc priority order of the 0x18, init, upd, page and c values). Own unit -O2 -G16 -Wa,-G16. See the header. |
-| [`func_002CDA20`](game/func_002CDA20.c) | 140 | 4 November 2026: US 140/140 bytes, 0/28, first +0x24 after removing the call wrapper used only as a scheduling barrier. US2/PAL canonical identities absent. Recover a real rounding/call block; the controlled historical result cannot qualify this source. Historical index notes (superseded where inconsistent): Retained wide-style near miss. PR #365 u32 trials used a false ABI: target unsigned long is 64 bits. Earlier candidate restored; no new match. |
-| [`func_002CF2B8`](game/func_002CF2B8.c) | 136 | Retained wide-style near miss. PR #365 u32 trials used a false ABI: target unsigned long is 64 bits. Earlier candidate restored; no new match. |
| [`func_002859C8`](game/func_002859C8.c) | 3436 | 3436/3436 bytes, not exact. Restore func_0027FAA8's integer return and the 64-bit colour argument of func_0028F410. The remaining panel-call scheduling still differs. US 0/28, US2 absent, PAL absent. |
| [`func_002900B8`](game/func_002900B8.c) | 1388 | 1372 bytes; it belongs in `0028F948-g8.c` and returns `D_001A8D40`. Retail has seven `mfc1` directly followed by a reader of their target, where this assembler inserts a `nop` (docs/COMPILER-NOTES.md), and cc1 spreads the float calculations where retail keeps the source order |
| [`func_002B44C0`](game/func_002B44C0.c) | 1248 | 1192 bytes: the same folded pointer on the two copies at the top, which changes the saved registers holding `self + 0x10` and `target` and the order of the spills at 0xC8-0xEC |
| [`func_0033A520`](game/func_0033A520.c) | 360 | 28 October 2026 recheck: plain C is 360/360 bytes but 0/28 USA v1.01 placements; 26 words differ in the rounding/allocation block. A non-empty `do/while(0)` barrier reproduces the block boundary, but its admissibility remains pending maintainer review. See source and blocker #23. |
| [`func_L22_003F54B8`](levels/func_L22_003F54B8.c) | 196 | Candidate is 200 bytes; retail uses the annulled conditional store (`beql $v0,$zero,L; swc1` with the negative pi/2 above the branch), this cc1 emits the plain branch and both arms |
-| [`func_L22_002A8B98`](levels/func_L22_002A8B98.c) | 172 | Recorded instruction mismatch at +0x4. |
| [`func_L22_003D0840`](levels/func_L22_003D0840.c) | 60 | 4; matches size and every other word, but the parameter copy is `daddu $v1,$a0,$zero` against retail's `$a3`. |
| [`func_00129410`](core/func_00129410.c) | 64 | 2 November 2026: VU0 sqc2 stores plus unmatched +0xA0 sp release and 12-byte CDCD fill. Corrected R5900 decode at +0x30 to ordinary sq; existing no-incoming-reference evidence, no C candidate. |
| [`func_L00_002EDC90`](levels/func_L00_002EDC90.c) | 40 | G8 candidate is 44 bytes; 0/27 places. Separate multiply/add does not reproduce the retail COP1 accumulation sequence. |
@@ -298,7 +295,6 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are
| [`func_002FF0D8`](game/func_002FF0D8.c) | 428 | 3 November 2026: Real preview vectors belong to an incomplete resident byte owner, without a fake one-slot model bound. 428/428 bytes, 0/28 USA, first +0x90. Voice request/key scheduling remains; no artificial branch wrapper. |
| [`func_002CF5B0`](game/func_002CF5B0.c) | 576 | Near miss: 576/576 B, 10 words, base/status register swap plus item register; see the header. Profile `-O2 -G32 -Wa,-G32 -ffunction-sections`. |
| [`func_002D2188`](game/func_002D2188.c) | 592 | 2 November 2026: Matched0027FAA8 returns signed int. Correcting the ignored text-draw result ties at580/592,0/28. Missing retail unused saved s1/0x30 frame and FP rounding lifetimes remain. Not linked or counted. |
-| [`func_L02_003FB000`](levels/func_L02_003FB000.c) | 288 | 284 bytes in a `-G8` unit: the only missing word is the `nop` that aligns the shared `jr $ra`, which final.c gives only to the first label after a loop start; exact with the last store in a non-empty `do { ... } while (0)`, not landed (a question on #157) |
| [`func_L02_003FF6E8`](levels/func_L02_003FF6E8.c) | 696 | 700 bytes: with `ctx` a pointer to the first buffer, retail's 0xF0 frame and register roles; retail rebuilds the address at each call and still saves an unused `$s2` (21 retail game functions save a register they never use, none in C) |
| [`func_L23_0042A018`](levels/func_L23_0042A018.c) | 1052 | 25 October 2026: 1052/1052 bytes. The 144-byte packet and matrix/axis extents are preserved. See retained source for profile, release coverage and reopening condition. |
| [`func_L23_0041A3B8`](levels/func_L23_0041A3B8.c) | 652 | 2 November 2026: Three canonical current helpers return pointers rather thanvoid. Correct declarations retained; output remains same known sound-tail branch mismatch. No fabricated single-pass scheduling loop added. Not linked or counted. |
@@ -336,7 +332,6 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are
| [`func_L16_00410E40`](levels/func_L16_00410E40.c) | 356 | Near miss: 364 bytes against retail's 356. The table of the handle's index is at 0x447FE7C0 in level 16, the second argument of func_002ACBC8 is the result of func_L16_00417788 and not the table entry, and the first loop is a do-while whose index is incremented in the delay slot of the `bc1fl`. What is left is the register allocation (retail keeps that result in $fp across both loops, this source spills it) and the modulo of the second loop, which retail computes from the index it reloads from the frame |
-| [`func_002D53F0`](game/func_002D53F0.c) | 140 | Retained wide-style near miss. PR #365 u32 trials used a false ABI: target unsigned long is 64 bits. Earlier candidate restored; no new match. |
| `func_0028A180` | 240 | One 228-byte plain-C draft matched 0/28 in US, PAL, and US2. It used guessed fixed data addresses; no safe shared-source data symbols were established, so the draft was discarded. Reopen with verified data-symbol mapping and retail loop/layout evidence. |
diff --git a/nonmatching/functional.tsv b/nonmatching/functional.tsv
index 3b8c62aed..0d353f52a 100644
--- a/nonmatching/functional.tsv
+++ b/nonmatching/functional.tsv
@@ -156,7 +156,6 @@ func_L15_00437B68 nonmatching/functional/func_L15_00437B68.c
func_L16_003AA3F0 nonmatching/functional/func_L16_003AA3F0.c
func_L17_003E8A58 nonmatching/functional/func_L17_003E8A58.c
func_L18_003FAE50 nonmatching/functional/func_L18_003FAE50.c
-func_L22_002A8B98 nonmatching/functional/func_L22_002A8B98.c
func_L22_003A15D0 nonmatching/functional/func_L22_003A15D0.c
func_L22_003D0840 nonmatching/functional/func_L22_003D0840.c
func_L22_003F54B8 nonmatching/functional/func_L22_003F54B8.c
@@ -287,7 +286,6 @@ func_002F93B0 nonmatching/functional/func_002F93B0.c
func_002F97A0 nonmatching/functional/func_002F97A0.c
func_002FB1A0 nonmatching/functional/func_002FB1A0.c
func_00281A08 nonmatching/functional/func_00281A08.c
-func_002D53F0 nonmatching/functional/func_002D53F0.c
func_L00_00341FA8 nonmatching/functional/func_L00_00341FA8.c
func_L01_003801B8 nonmatching/functional/func_L01_003801B8.c
func_L22_003A5018 nonmatching/functional/func_L22_003A5018.c
@@ -304,7 +302,6 @@ func_L00_00316F60 nonmatching/functional/func_L00_00316F60.c
func_L00_002E0200 nonmatching/functional/func_L00_002E0200.c
func_002A3110 nonmatching/functional/func_002A3110.c
func_002CF5B0 nonmatching/functional/func_002CF5B0.c
-func_002CF2B8 nonmatching/functional/func_002CF2B8.c
func_003166C8 nonmatching/functional/func_003166C8.c
func_L00_00423290 nonmatching/functional/func_L00_00423290.c
func_L00_002C40C0 nonmatching/functional/func_L00_002C40C0.c
@@ -316,7 +313,6 @@ func_L02_003C43F0 nonmatching/functional/func_L02_003C43F0.c
func_L00_003B84F0 nonmatching/functional/func_L00_003B84F0.c
func_L00_00395428 nonmatching/functional/func_L00_00395428.c
func_00282D48 nonmatching/functional/func_00282D48.c
-func_002CDA20 nonmatching/functional/func_002CDA20.c
func_002900B8 nonmatching/functional/func_002900B8.c
func_002A3DB8 nonmatching/functional/func_002A3DB8.c
func_L00_00408E58 nonmatching/functional/func_L00_00408E58.c
diff --git a/nonmatching/functional/func_002CDA20.c b/nonmatching/functional/func_002CDA20.c
deleted file mode 100644
index aaa937a90..000000000
--- a/nonmatching/functional/func_002CDA20.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/* functional: high. Same calls with the same arguments in the same order and the same result as retail; the near miss's matching-only do/while(0) wrapper and float local are gone. y is (D_001B23A4 * 293.0f) + 0.5f truncated toward zero (trunc.w.s), as the (int) cast does. The style is retail's 64-bit register holding 0x80F0F0F0 zero-extended (dli/dsll/ori), so it is unsigned long, as the landed func_002CF2B8/func_002D53F0 declare it; the text is a game address as host/font.c takes it. */
-
-#include "common.h"
-
-extern void func_0027C540(int);
-extern void func_0029C4C0(void);
-extern void func_0027C660(void);
-extern int func_00288FB8(int);
-extern int func_0027FAA8(int, int, unsigned long, unsigned int, int);
-
-extern float D_001B23A4 NOT_SDA;
-
-/* Draws a fixed text prompt (text id 0x2D4F) at x 0x1A5, y = 293 * D_001B23A4 + 0.5, colour 0x80F0F0F0, between the
- text-batch begin/end calls (func_0029C4C0 sets up the text state first). Returns 0. */
-int func_002CDA20(void)
-{
- int y;
-
- func_0027C540(0);
- func_0029C4C0();
- y = (int)(D_001B23A4 * 293.0f + 0.5f);
- func_0027FAA8(0x1A5, y, 0x80F0F0F0UL, (unsigned int)func_00288FB8(0x2D4F), -1);
- func_0027C660();
- return 0;
-}
diff --git a/nonmatching/functional/func_002CF2B8.c b/nonmatching/functional/func_002CF2B8.c
deleted file mode 100644
index 4fec5e494..000000000
--- a/nonmatching/functional/func_002CF2B8.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/* functional: high. Same calls with the same arguments in the same order and the same result as retail. Retail computes the value as (D_001B23A4 * 256.0f) + 0.5f truncated toward zero (trunc.w.s), which the (int) cast does. Retail passes the style as a 64-bit register holding 0x80F0F0F0 zero-extended (the callee stores all 64 bits into its packet; an int would sign-extend it, which the gate caught), so the style is unsigned long. The near-miss draft's ref_ name and level-copy callee were replaced with the boot names retail uses here (lui 0x1B / lwc1 9124 = D_001B23A4; jal 0x29C530 = func_0029C530). */
-
-#include "common.h"
-
-extern void func_0027C540(long);
-extern void func_0029C530(void);
-extern void func_0027C660(void);
-extern int func_00288FB8(int);
-extern int func_0027FAA8(int, int, unsigned long, unsigned int, int); /* text as host/font.c takes it (a game address); the style is retail's 64-bit register, as the landed func_002859C8/func_002D53F0 declare it */
-
-extern float D_001B23A4 NOT_SDA;
-
-/* Draws a fixed text prompt (text id 0x2D4F) at x 0x1A5, y = 256 * D_001B23A4 + 0.5, colour 0x80F0F0F0, between the
- text-batch begin/end calls. Returns 0. */
-int func_002CF2B8(void)
-{
- int y;
-
- func_0027C540(0);
- func_0029C530();
- y = (int)(D_001B23A4 * 256.0f + 0.5f);
- func_0027FAA8(0x1A5, y, 0x80F0F0F0UL, (unsigned int)func_00288FB8(0x2D4F), -1);
- func_0027C660();
- return 0;
-}
diff --git a/nonmatching/functional/func_002D53F0.c b/nonmatching/functional/func_002D53F0.c
deleted file mode 100644
index ce54ba51a..000000000
--- a/nonmatching/functional/func_002D53F0.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/* functional: high. Same calls, arguments, reads and writes as retail (the near miss without its matching-only address macro). */
-
-/* Draws a centred text line (string 0x2D4F) at x 422, y = (int)(D_001B23A4 * 329 + 0.5), colour 0x80F0F0F0,
- * between the text begin/end calls, and returns 0. */
-
-extern float D_001B23A4;
-extern void func_0027C540(int);
-extern void func_0029C530(void);
-extern unsigned char *func_00288FB8(int);
-extern void func_0027C660(void);
-extern int func_0027FAA8(int, int, unsigned long, unsigned char *, int);
-
-int func_002D53F0(void)
-{
- int y;
-
- func_0027C540(0);
- func_0029C530();
- y = (int)(D_001B23A4 * 329.0f + 0.5f);
- func_0027FAA8(0x1A6, y, 0x80F0F0F0UL, func_00288FB8(0x2D4F), -1);
- func_0027C660();
- return 0;
-}
diff --git a/nonmatching/functional/func_L22_002A8B98.c b/nonmatching/functional/func_L22_002A8B98.c
deleted file mode 100644
index d21aa2001..000000000
--- a/nonmatching/functional/func_L22_002A8B98.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/* functional: high. Near-miss draft within a few instructions of retail: differences are register allocation, scheduling and instruction shape. */
-/* GPT-6.1 Sol high worker, 29 October 2026, pinned upstream game compiler.
- Default -O2 -G0 -ffunction-sections. 172/172 bytes, 0/2 USA v1.01 places;
- final check first differs at +0x60. Only three stores differ: retail stores
- speed0, speed1, flag; this candidate stores flag, speed0, speed1.
- Reordering the duplicate float assignments fixes all FP register allocation.
- Raw byte-pointer accesses, an unsigned-char flag, and an inline comparison
- preserve the same final store rotation. Moving flag earlier only moves it
- earlier in the generated block. Allocator dumps confirm the rotation already
- exists before reload. Next: independently evidenced initialization grouping
- or a matching RAC1 counterpart, without barriers or selective volatility.
- All required declarations are included. Not linked or counted. */
-
-extern int D_001A79F0[] __attribute__((sda));
-extern int D_001A73A0[] __attribute__((sda));
-extern char D_L22_001B1920[];
-extern void func_L00_002D24B8(void);
-extern void func_L22_002C9898(int, int);
-extern void func_L00_003B09F8(void);
-typedef struct {
- char pad0[0x250]; float speed0, speed1, tuning0, tuning1;
- char pad260[4]; float tuning2;
- char pad268[0xC24-0x268]; float scale;
- char padC28[0x1888-0xC28]; void *counts;
- char pad188C[0x2294-0x188C]; int state;
- char pad2298[0x24F0-0x2298]; unsigned char flag;
-} InitState;
-extern InitState D_00189E20;
-void func_L22_002A8B98(void) {
- int flag = D_001A79F0[0] == 0x1A;
- InitState *p = &D_00189E20;
- p->speed0 = 0.8f;
- p->speed1 = 0.7f;
- p->tuning2 = 0.45f;
- p->tuning0 = 0.8f;
- p->tuning1 = 0.7f;
- p->scale = 1.0f;
- p->flag = flag;
- D_001A73A0[0] = 0;
- p->state = 0;
- func_L00_002D24B8();
- func_L22_002C9898(0, 1);
- func_L00_003B09F8();
- p->counts = D_L22_001B1920;
-}
diff --git a/nonmatching/game/func_002B6658.c b/nonmatching/game/func_002B6658.c
index 5815f5c27..1969b9093 100644
--- a/nonmatching/game/func_002B6658.c
+++ b/nonmatching/game/func_002B6658.c
@@ -1,3 +1,13 @@
+/* 4 November 2026 follow-up: nonmatching, not linked. The actual head-cache
+ * store occurs once in the entry branch slot, using the getter's v0; the
+ * loop backedge enters at the current-node state test. Do not add a cache
+ * store per iteration. The void callback view agrees with current RAC1's
+ * Moby update API. A real one-pass callback group, the same group with -g,
+ * and a stored signed32 callback-address view all remain 144/148 bytes,
+ * 0/28 USA: callback allocation and null-branch scheduling still differ.
+ * Those probes are preserved privately; the pointer-based baseline below
+ * retains its historical measurement. Corrected only its scope name here.
+ */
/* 2 November 2026, remaining-function review: nonmatching; no linked credit.
* Profile: -O2 -G8 -ffunction-sections.
* US check: 144/148 bytes, 0/28 placements.
@@ -21,14 +31,14 @@ struct Node {
extern Node *func_002A08A0(void);
extern void func_002A05A8(Node *);
extern void func_002A0F48(Node *);
-extern Node *ref_L00_002B6658_00 __attribute__((sda));
+extern Node *ref_002B6658_00 __attribute__((sda));
void func_002B6658(void)
{
Node *t;
void (*update)(Node *);
- ref_L00_002B6658_00 = func_002A08A0();
- t = ref_L00_002B6658_00;
+ ref_002B6658_00 = func_002A08A0();
+ t = ref_002B6658_00;
for (; t != 0; t = t->next) {
if (t->flag20 >= 0) {
if ((t->flags & 0x40) == 0)
diff --git a/nonmatching/game/func_002CDA20.c b/nonmatching/game/func_002CDA20.c
deleted file mode 100644
index 7f529084b..000000000
--- a/nonmatching/game/func_002CDA20.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/* 4 November 2026 source audit: removed the call wrapper used only as a scheduling barrier.
- * The previous controlled result below is historical and cannot qualify this
- * revised draft. Production remains assembly; no linked or native credit.
- * Fresh check: 140/140 bytes, US 0/28, first mismatch +0x24. US2/PAL name absent.
- * Profile: -O2 -G0 -ffunction-sections. Reopen on a real rounding/call block witness.
- */
-/* Maintainer ABI correction, 29 October 2026: the qualified EE compiler
- * measures sizeof(long) == sizeof(long long) == 8 and sizeof(void *) == 4.
- * The PR #365 trials narrowing the style argument to u32 used a false
- * interface. Their sizes are not a valid ABI-preserving improvement.
- * Restore the earlier wide-argument candidate; no new match is claimed.
- */
-#include "common.h"
-
-/* func_002CDA20 (140 bytes, boot + 27 levels), tier opus-2, 29 October 2026.
- *
- * The C below is exact in all places: check.py printed
- * "28 of 28 places match" when it replaced the marker in
- * src/game/002CD8C0.c (default -O2 -G0 unit, no -G8 sibling needed).
- * It is kept out of src/ because it depends on the non-empty
- * `do { ... } while (0)` wrapper. MATCHING-BLOCKERS.md has an open question
- * for maintainers about using that wrapper as a block barrier
- * (func_L02_003FB000, func_0033A520). If they accept the wrapper, this C can
- * land as it is.
- *
- * Without the wrapper the best result is 140/140 bytes and 0/28 places, with
- * 7 instructions differing from +0x24:
- * - 293.0f and 0.5f go to $f1/$f2, where retail has $f2/$f1;
- * - after the func_00288FB8 call the arguments are set up as a3, a2, a1, t0,
- * where retail has a1, a3, a2, t0.
- * The argument order cannot be reached in one basic block. The fix result
- * (fcvt, cost 3) feeds `move a1,s0`, while the call waits only one cycle
- * (anti dependence) after the fix. So a1 is always one cycle behind `move a3,v0`,
- * in both sched1 and sched2.
- *
- * The loop notes of the wrapper make the first insn inside it (the li a0 of
- * the call) depend on every earlier set (haifa sched_analyze_insn's loop-note
- * barrier). That delays the call until a1 is ready together with a3, and a1
- * wins (5 vs 5 dependents in sched2, and the lower LUID). The barrier also
- * takes the call off the float load's dependents. The load and the 293.0f
- * then tie, so LUID decides: `k = 293.0f` must come before the expression.
- * The 293.0f is then born first and has the longer life, so local-alloc gives
- * the 0.5f $f1 first and the 293.0f $f2. Without the local (plain
- * `D_001B23A4 * 293.0f`) the arguments are right, but the float registers
- * stay swapped.
- *
- * The same retail shape (and the same wrapper result, checked as a
- * standalone compile only) fits func_002CF2B8 (256.0f, func_0029C530) and
- * probably func_002D53F0 (329.0f, 0x1A6). func_002CD3C0's retail
- * (a3, a2, t0, a1) is the unwrapped form.
- *
- * Declarations: D_001B23A4 as declared in src/game/0033C028-qcopy.c (the
- * per-program float; the sda attribute in a -G0 unit gives retail's
- * `lui $at` + `lwc1` macro load); func_0029C4C0 is the boot name of the
- * second callee (check.py resolves it in every level); the remaining
- * externs match those already in src/game/002CD8C0.c.
- */
-
-extern void func_0027C540(long);
-
-extern void func_0027C660(void);
-
-extern void func_0029C4C0(void);
-
-extern float D_001B23A4 __attribute__((sda));
-
-extern char *func_00288FB8(int);
-
-extern int func_0027FAA8(int, int, unsigned long, char *, int);
-
-int func_002CDA20(void)
-{
- float k;
- int y;
-
- func_0027C540(0);
- func_0029C4C0();
- k = 293.0f;
- y = (int)(D_001B23A4 * k + 0.5f);
- func_0027FAA8(0x1A5, y, 0x80F0F0F0, func_00288FB8(0x2D4F), -1);
- func_0027C660();
- return 0;
-}
diff --git a/nonmatching/game/func_002CF2B8.c b/nonmatching/game/func_002CF2B8.c
deleted file mode 100644
index 074bc3dbd..000000000
--- a/nonmatching/game/func_002CF2B8.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Maintainer ABI correction, 29 October 2026: the qualified EE compiler
- * measures sizeof(long) == sizeof(long long) == 8 and sizeof(void *) == 4.
- * The PR #365 trials narrowing the style argument to u32 used a false
- * interface. Their sizes are not a valid ABI-preserving improvement.
- * Restore the earlier wide-argument candidate; no new match is claimed.
- */
-#include "common.h"
-
-/* Near miss: the candidate is retail's exact 136 bytes and every call, the
- * float computation and the u64 argument are right, but three instructions
- * differ.
- *
- * Retail keeps the 256.0f in `$f2` and the 0.5f in `$f1`
- * (`mtc1 $at,$f2` ; `mtc1 $at,$f1`), this cc1 keeps them the other way
- * round (`$f1` and `$f2`); the order of the two constants in the source does
- * not change it.
- *
- * Retail also sets up the arguments of the `func_0027FAA8` call as `$a1`,
- * `$a3`, `$a2` and `$t0`; this cc1 sets up `$a3`, `$a2` and `$a1`.
- *
- * The float is read from a per-program address (0x001B23A4 in the boot,
- * 0x001B2704 in level 00), so it is a `ref_` symbol; with `-G8` the load is
- * the symbolic `l.s $f0,sym` that retail's `lui $at`+`lwc1` pair comes from,
- * and with `-G0` the address is materialised in `$v0` instead.
- *
- * The unit needs `-G8` for the symbolic load.
- *
- * Opus-1 (Claude Opus 5.5, task v2125, 3 November 2026): recompiled here,
- * this body is still 136/136 bytes, 0/28 places. Both walls come from
- * sched1. The ref load has an anti-dependence on the func_00288FB8 call, which
- * raises its priority, so it issues before the 256.0f and local-alloc gives
- * the 256.0f `$f1`. And the `$a1` copy waits on the cvt latency, so `$a3`/`$a2`
- * are set first. The following form gives all 28 of 28 places, but only with a
- * `do { } while (0)` around the call (a forbidden barrier, so not landable):
- *
- * static __inline__ int probe_y(float value)
- * { return (int)(value * ref_L00_002CF2B8_01 + 0.5f); }
- * ...
- * func_0027C540(0);
- * func_L00_0030D608();
- * {
- * int y = probe_y(256.0f);
- * do {
- * func_0027FAA8(0x1A5, y, 0x80F0F0F0, func_00288FB8(0x2D4F), -1);
- * } while (0);
- * }
- * func_0027C660();
- * return 0;
- *
- * The loop notes split the float computation from the call setup: the 256.0f
- * is issued first (it is the inline parameter, so it has the lower LUID), and
- * `$a1` is copied first in the call block. Writing the y argument inline as
- * a call to a helper with a real `if (D_001B23A0 != 0)` test, whose two arms
- * become identical here, gives retail's float registers and argument order.
- * The only extra code is the `lui`+`lw` of D_001B23A0 (8 bytes). jump2 merges
- * the arms after flow2 has run, so nothing deletes the dead load. Retail has
- * no such load, so the original construct must end the block in the same
- * place without reading memory. Look for a register-only test or a real loop
- * in the sibling text-draw functions (func_002CDA20.c has the same pattern).
- */
-
-extern void func_0027C540(long);
-
-extern void func_0027C660(void);
-
-extern void func_L00_0030D608(void);
-
-extern s32 func_00288FB8(s32);
-
-extern s32 func_0027FAA8(s32, s32, u64, s32, s32);
-
-extern float ref_L00_002CF2B8_01;
-
-int func_002CF2B8(void)
-{
- s32 s0;
-
- func_0027C540(0);
- func_L00_0030D608();
- s0 = (s32)(0.5f + 256.0f * ref_L00_002CF2B8_01);
- func_0027FAA8(0x1A5, s0, 0x80F0F0F0, func_00288FB8(0x2D4F), -1);
- func_0027C660();
- return 0;
-}
diff --git a/nonmatching/game/func_002D53F0.c b/nonmatching/game/func_002D53F0.c
deleted file mode 100644
index bfd7ba52f..000000000
--- a/nonmatching/game/func_002D53F0.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/* 3 November 2026, Opus 5.5 xhigh (opus-1), task v2126, on v2/c04 (upstream + #348):
- * best rule-compliant result unchanged: 140/140 bytes, 0 of 28 places (the plain C below).
- * The wrapper form is still exact here, 28 of 28 (cdiff.py): with the file's own
- * `extern float D_001B23A4[] __attribute__((sda));`, write `float scale;` and assign
- * `scale = 329.0f;` on the line before `y = (int)(D_001B23A4[0] * scale + 0.5f);`
- * (initialising it at the declaration hoists 329.0f above both calls into $f20), then
- * `do { func_0027FAA8(0x1A6, y, 0x80F0F0F0ULL, func_00288FB8(0x2D4F), -1); } while (0);`.
- * Why it works (sched1 dumps, -fsched-verbose-5): the LOOP_BEG note makes `a0 = 0x2D4F` truly
- * depend on the fix_truncsfsi2 (latency 3), so the func_00288FB8 call issues 3 cycles later and
- * the `a1 = y` copy is ready with the other argument moves (rank: lowest reg weight, then LUID
- * gives a1, a3, a2, t0, a0). Without it the call follows the fix at once (only a cost-0 anti
- * dependence) and the a1 copy waits two cycles, giving a3, a2, a1. The FP colours follow:
- * with the note, 329.0f is set first and lives 8 insns against 0.5f's 6, so 0.5f wins $f1.
- * Nothing but a loop note or a real block boundary between the fix and `a0 = 0x2D4F` can do
- * this; the only integer in a register there is y itself (func_0029C530 is void), so a
- * vanishing display-mode test would leave its load. An if/else on y with the call in both
- * arms does not cross-jump (the branch stays). Lands only if the wrapper is ruled acceptable.
- */
-/* 31 October 2026: exact (28 of 28 places) with `float scale = 329.0f;` held in a local AND
- * the func_0027FAA8 call wrapped in a non-empty `do { ... } while (0);`, whose loop notes keep
- * the argument setup after func_00288FB8 in retail's order and put 329.0f in $f2. Either change
- * alone gives 0/28. Not landed: that wrapper is the open maintainer question in
- * docs/MATCHING-BLOCKERS.md ("A non-empty do { ... } while (0) as a block barrier"), the same
- * shape as func_0033A520's rounding of D_001B23A4 * 300. If the wrapper is accepted, this
- * function lands with it.
- */
-/* Maintainer ABI correction, 29 October 2026: the qualified EE compiler
- * measures sizeof(long) == sizeof(long long) == 8 and sizeof(void *) == 4.
- * The PR #365 trials narrowing the style argument to u32 used a false
- * interface. Their sizes are not a valid ABI-preserving improvement.
- * Restore the earlier wide-argument candidate; no new match is claimed.
- */
-/* func_002D53F0 (140 bytes, 28 places), M16: 140/140 under -O2 -G0 in its own unit.
- * D_001B23A4 declared MACRO_ADDR gives retail's one absolute `lwc1 $f0,D_001B23A4` through
- * $at at retail's position (this replaces the old -mno-split-addresses note); the callees
- * take their real prototypes (func_0027FAA8 (int, int, u64, u8 *, int) as defined in
- * 0027F980.c, func_00288FB8 returning the text pointer). Left (9 lines, registers and
- * order only): 329.0f in $f1 / 0.5f in $f2 where retail has $f2 / $f1, and the a1/a3/a2
- * setup order after func_00288FB8. Constant locals, a split product, the text as a local,
- * the sibling units' -fno-expensive-optimizations -fno-builtin profile, -G8 and sda forms
- * leave both. In the permuter queue. */
-#include "common.h"
-
-extern float D_001B23A4 MACRO_ADDR;
-extern void func_0027C540(long);
-extern void func_0029C530(void);
-extern unsigned char *func_00288FB8(int);
-extern void func_0027C660(void);
-extern int func_0027FAA8(int, int, u64, unsigned char *, int);
-
-int func_002D53F0(void)
-{
- int y;
-
- func_0027C540(0);
- func_0029C530();
- y = (int)(D_001B23A4 * 329.0f + 0.5f);
- func_0027FAA8(0x1A6, y, 0x80F0F0F0ULL, func_00288FB8(0x2D4F), -1);
- func_0027C660();
- return 0;
-}
diff --git a/nonmatching/levels/func_L02_003FB000.c b/nonmatching/levels/func_L02_003FB000.c
deleted file mode 100644
index 96e9a53a4..000000000
--- a/nonmatching/levels/func_L02_003FB000.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/* func_L02_003FB000 (288 bytes, 1 place, 02:003FB000) - near miss, measured 21 October 2026.
-
- In a -G8 unit of its own (src/levels/02_maktar_nebula/L02_003FB000-g8.c, no cflags line) this
- C is 284 bytes against retail's 288, 0 of 1 places: every instruction is retail's (both $gp
- loads included), and the only missing word is the `nop` at +0x114 that aligns the shared
- return (`jr $ra` at +0x118, the target of the two early returns) to 8 bytes.
- In src/levels/02_maktar_nebula/L02_003F5430.c (-G0) the same C is 288 bytes but 3 words off:
- no nop, and D_L02_001B1214 comes out `lui` + `lhu %lo` (patch 0020: outside a delay slot,
- gas only uses $gp for a symbol whose .extern size it has seen, and -G0 emits none).
-
- Why the nop is missing: final.c (shorten_branches) gives LOOP_ALIGN to the first CODE_LABEL
- after a NOTE_INSN_LOOP_BEG, and nothing else gives an 8-byte label alignment here. Retail's
- return label must therefore follow loop notes with no label in between, i.e. a loop after
- the last store whose body compiled to nothing.
- - Diagnosis in scratch only: the same C with an empty `do { } while (0);` after the last
- store is retail's 72 words exactly. That spelling is rejected (empty loop).
- - A wrapper around the LAST store only gives retail's bytes: `do { }
- while (0);` or `for (;;) { ; return; }` - each measured in the -G8 unit,
- `tools/check.py`: 1 of 1 places match. Not landed: such a wrapper is there only to steer
- the alignment (the merged precedents are func_00124818's do/while(0) barrier and
- func_00122760's `while` whose body returns); the lead decides whether it is admissible.
- - A wrapper around the three tail statements (`do { ... } while (0)`, or
- `for (;;) { ...; return; }`) also aligns the label, but then gcse's PRE hoist of the %hi
- of D_00189E20 above the inner loop stays (without it cse2 folds the copy back into the
- tail; with it the pseudo is global and gets $t8): 280 bytes.
- - `while (entry != 0) { ...; return; }` around the whole walk: the outer LOOP_BEG's first
- label is the inner loop head, so the return label stays 4-aligned (and the %hi is
- hoisted the same way): 280 bytes.
- What would unblock it: a plain-C construct that leaves loop notes after the tail without
- wrapping it (the original was probably an empty debug/assert macro). */
-
-#include "include_asm.h"
-
-extern char *D_L02_001B359C __attribute__((sda));
-extern unsigned int D_L02_001FC060[];
-extern unsigned short D_L02_001B1214 __attribute__((sda));
-extern char D_00189E20_c[];
-
-void func_L02_003FB000(char *a0) {
- char *state = *(char **)(a0 + 0x68);
- unsigned int index = *(unsigned int *)(state + 0x40);
- unsigned int entry;
- short *p;
- char *tbl;
- char *rec;
- char *g;
-
- *(int *)(state + 0x60) = 0;
- *(float *)(state + 0x64) = 0;
- if (index == 0xFF)
- return;
- entry = D_L02_001FC060[index];
- if (entry == 0)
- return;
- p = (short *)entry;
- tbl = D_L02_001B359C;
- do {
- rec = tbl + ((*(unsigned short *)p & 0x7FFF) << 8);
- if (rec == 0)
- continue;
- if (*(unsigned char *)(rec + 0x20) == 0xFE)
- continue;
- if (*(unsigned char *)(rec + 0x20) == 0xFD)
- continue;
- if (*(unsigned char *)(rec + 0x20) == 1)
- continue;
- {
- short type = *(short *)(rec + 0xAA);
- if (type == 0x9CA || type == 0xB22 || type == 0xC3B || type == 0xC3E || type == 0x9F8 || type == 0xA44) {
- *(int *)(state + 0x60) += 1;
- *(float *)(state + 0x64) += *(float *)(*(char **)(rec + 0x68) + 0x20);
- }
- }
- } while (*p++ >= 0);
- g = D_00189E20_c;
- *(float *)(state + 0x68) = (float)*(int *)(g + 0x24CC);
- *(short *)(state + 0x11C) = 0;
- *(unsigned short *)(state + 0x11E) = D_L02_001B1214;
-}
diff --git a/nonmatching/levels/func_L13_0041AB30.c b/nonmatching/levels/func_L13_0041AB30.c
index ceb3b9f0c..c10925689 100644
--- a/nonmatching/levels/func_L13_0041AB30.c
+++ b/nonmatching/levels/func_L13_0041AB30.c
@@ -1,3 +1,14 @@
+/* 4 November 2026 source-layout review, retained near miss.
+ * Four new grid/address/group forms with -O2 -G8 -ffunction-sections -g
+ * remain 460/460 bytes, 0/2 USA places (first +0x78 or +0x84).
+ * Both borrowed grids have three rows of four 16-byte cells: the second
+ * ends at counter +0x620. A four-row owning view would overlap that counter.
+ * Typed cell pointers and signed/unsigned EE-word row sums do not preserve
+ * the second preheader copy. A real inner-loop or row-assignment one-pass
+ * group does not resolve it either. The older best C below is retained.
+ * Reopen with evidence of the row pseudo's lifetime across loop reduction;
+ * no dummy pointer, false return or register pin was introduced.
+ */
/* Nonmatching C reconstruction (opus-1, v2196, USA v1.01, 2026-11-03).
* Tier: near miss. Profile: -O2 -G8 -ffunction-sections -g in its own unit
* (-g supplies the two nops before each div.s).
diff --git a/nonmatching/levels/func_L22_002A8B98.c b/nonmatching/levels/func_L22_002A8B98.c
deleted file mode 100644
index be41b908b..000000000
--- a/nonmatching/levels/func_L22_002A8B98.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/* GPT-6.1 Sol high worker, 29 October 2026, pinned upstream game compiler.
- Default -O2 -G0 -ffunction-sections. 172/172 bytes, 0/2 USA v1.01 places;
- final check first differs at +0x60. Only three stores differ: retail stores
- speed0, speed1, flag; this candidate stores flag, speed0, speed1.
- Reordering the duplicate float assignments fixes all FP register allocation.
- Raw byte-pointer accesses, an unsigned-char flag, and an inline comparison
- preserve the same final store rotation. Moving flag earlier only moves it
- earlier in the generated block. Allocator dumps confirm the rotation already
- exists before reload. Next: independently evidenced initialization grouping
- or a matching RAC1 counterpart, without barriers or selective volatility.
- All required declarations are included. Not linked or counted. */
-
-extern int D_001A79F0[] __attribute__((sda));
-extern int D_001A73A0[] __attribute__((sda));
-extern char D_L22_001B1920[];
-extern void func_L00_002D24B8(void);
-extern void func_L22_002C9898(int, int);
-extern void func_L00_003B09F8(void);
-typedef struct {
- char pad0[0x250]; float speed0, speed1, tuning0, tuning1;
- char pad260[4]; float tuning2;
- char pad268[0xC24-0x268]; float scale;
- char padC28[0x1888-0xC28]; void *counts;
- char pad188C[0x2294-0x188C]; int state;
- char pad2298[0x24F0-0x2298]; unsigned char flag;
-} InitState;
-extern InitState D_00189E20;
-void func_L22_002A8B98(void) {
- int flag = D_001A79F0[0] == 0x1A;
- InitState *p = &D_00189E20;
- p->speed0 = 0.8f;
- p->speed1 = 0.7f;
- p->tuning2 = 0.45f;
- p->tuning0 = 0.8f;
- p->tuning1 = 0.7f;
- p->scale = 1.0f;
- p->flag = flag;
- D_001A73A0[0] = 0;
- p->state = 0;
- func_L00_002D24B8();
- func_L22_002C9898(0, 1);
- func_L00_003B09F8();
- p->counts = D_L22_001B1920;
-}
diff --git a/nonmatching/levels/func_L22_003D0840.c b/nonmatching/levels/func_L22_003D0840.c
index b8e533e35..d921eb8d5 100644
--- a/nonmatching/levels/func_L22_003D0840.c
+++ b/nonmatching/levels/func_L22_003D0840.c
@@ -1,3 +1,10 @@
+/* 4 November 2026: a real one-pass flag-update/state-change group, as seen
+ * in current RAC1 game C, gives 64/60 bytes and 0/1 USA. The object still
+ * uses v1 rather than a3 and an extra alignment nop remains. No improvement;
+ * the probe is retained privately. Keep the meaningful pointer-based baseline
+ * and canonical three-argument state setter below. Its declaration cleanup
+ * is not a new matching measurement; older results remain historical.
+ */
/* Near miss from src/levels/22_dobbo_orbit/L22_00358778.c — 1 word (4 bytes) differ.
*
* Kept as a starting point; not linked, not counted.
@@ -36,14 +43,13 @@ typedef unsigned short u16;
typedef int s32;
-extern void func_31FBB8();
+extern void func_002A7750(u8 *, int, int);
-void func_L22_003D0840(s32 a0) {
- s32 a3 = a0;
- if (*(u8 *)(a3 + 0x20) == 0) {
- u16 v = *(u16 *)(a3 + 0x34) | 0x41;
- *(u16 *)(a3 + 0x34) = v;
- func_31FBB8(a3, 1, -1);
+void func_L22_003D0840(u8 *object) {
+ if (object[0x20] == 0) {
+ u16 flags = *(u16 *)(object + 0x34) | 0x41;
+ *(u16 *)(object + 0x34) = flags;
+ func_002A7750(object, 1, -1);
}
}
/* Second attempt (wave 24, worker 15): the same one word off, same check line
diff --git a/src/game/002CD8C0.c b/src/game/002CD8C0.c
index fcd348051..3ece1dec1 100644
--- a/src/game/002CD8C0.c
+++ b/src/game/002CD8C0.c
@@ -121,7 +121,7 @@ long long func_002CD8C0(void)
int func_002CD8F0(void *object) { char *root=D_001A8CFC[0]; if (root != 0) { func_00341550(root+0x3BE08,1); *(void **)((char *)object+0x34)=func_00341558(D_001A8CFC[0]+0x3BE08); } return 0; }
-INCLUDE_ASM("asm/game", func_002CDA20);
+/* func_002CDA20 is defined in 002CDA20.c. */
int func_002CDAB0(void)
{
@@ -169,7 +169,7 @@ int func_002CF188(char *object) {
return 0;
}
-INCLUDE_ASM("asm/game", func_002CF2B8);
+/* func_002CF2B8 is defined in 002CF2B8.c. */
/* func_002CF340 is built from 002CF340-g8.c. */
diff --git a/src/game/002CDA20.c b/src/game/002CDA20.c
new file mode 100644
index 000000000..fdec2519a
--- /dev/null
+++ b/src/game/002CDA20.c
@@ -0,0 +1,26 @@
+#include "common.h"
+
+extern void func_0027C540(int);
+extern void func_0029C4C0(void);
+extern float D_001B23A4[] __attribute__((sda));
+extern s32 func_00288FB8(s32);
+extern int func_0027FAA8(int, int, u64, u8 *, int);
+extern void func_0027C660(void);
+
+/* Draw the centered panel text in scaled screen coordinates. */
+int func_002CDA20(void)
+{
+ float position;
+ int y;
+
+ func_0027C540(0);
+ func_0029C4C0();
+ position = 293.0f;
+ y = (int)(D_001B23A4[0] * position + 0.5f);
+ /* One-pass text draw group after coordinate conversion. */
+ do {
+ func_0027FAA8(0x1A5, y, 0x80F0F0F0ULL, (u8 *)func_00288FB8(0x2D4F), -1);
+ } while (0);
+ func_0027C660();
+ return 0;
+}
diff --git a/src/game/002CF2B8.c b/src/game/002CF2B8.c
new file mode 100644
index 000000000..ddda37e43
--- /dev/null
+++ b/src/game/002CF2B8.c
@@ -0,0 +1,25 @@
+#include "common.h"
+
+extern void func_0027C540(int);
+extern void func_0029C530(void);
+extern float D_001B23A4[] __attribute__((sda));
+extern s32 func_00288FB8(s32);
+extern int func_0027FAA8(int, int, u64, u8 *, int);
+extern void func_0027C660(void);
+
+/* Draw centered text in scaled screen coordinates. */
+int func_002CF2B8(void)
+{
+ float position;
+ int y;
+
+ func_0027C540(0);
+ func_0029C530();
+ position = 256.0f;
+ y = (int)(D_001B23A4[0] * position + 0.5f);
+ do {
+ func_0027FAA8(0x1A5, y, 0x80F0F0F0ULL, (u8 *)func_00288FB8(0x2D4F), -1);
+ } while (0);
+ func_0027C660();
+ return 0;
+}
diff --git a/src/game/002D3390.c b/src/game/002D3390.c
index fb0d3a26e..518b4fac5 100644
--- a/src/game/002D3390.c
+++ b/src/game/002D3390.c
@@ -260,7 +260,7 @@ int func_002D5290(char *object) {
/* func_002D52D8 is in the -G8 sibling. */
-INCLUDE_ASM("asm/game", func_002D53F0);
+/* func_002D53F0 is defined in 002D53F0.c. */
extern void func_00287E80(void);
extern void func_00341540(char *, char *, char *, char *);
diff --git a/src/game/002D53F0.c b/src/game/002D53F0.c
new file mode 100644
index 000000000..4ec00ba21
--- /dev/null
+++ b/src/game/002D53F0.c
@@ -0,0 +1,26 @@
+#include "common.h"
+
+extern float D_001B23A4[] __attribute__((sda));
+extern void func_0027C540(int);
+extern void func_0029C530(void);
+extern s32 func_00288FB8(s32);
+extern int func_0027FAA8(int, int, u64, u8 *, int);
+extern void func_0027C660(void);
+
+/* Draw centered text in scaled screen coordinates. */
+int func_002D53F0(void)
+{
+ float position;
+ int y;
+
+ func_0027C540(0);
+ func_0029C530();
+ position = 329.0f;
+ y = (int)(D_001B23A4[0] * position + 0.5f);
+ /* One-pass text draw group after coordinate conversion. */
+ do {
+ func_0027FAA8(0x1A6, y, 0x80F0F0F0ULL, (u8 *)func_00288FB8(0x2D4F), -1);
+ } while (0);
+ func_0027C660();
+ return 0;
+}
diff --git a/src/levels/02_maktar_nebula/L02_003F5430.c b/src/levels/02_maktar_nebula/L02_003F5430.c
index d4feb42d9..ffb838485 100644
--- a/src/levels/02_maktar_nebula/L02_003F5430.c
+++ b/src/levels/02_maktar_nebula/L02_003F5430.c
@@ -255,7 +255,7 @@ void func_L02_003FAF90(char *a0) {
*(u32 *)(s0 + 0xC0) = 0;
}
-INCLUDE_ASM("asm/levels", func_L02_003FB000);
+/* func_L02_003FB000 is defined in L02_003FB000-g8.c. */
/* func_L02_003FB120 is matched in L02_003FB120-g8.c (built with -g). */
diff --git a/src/levels/02_maktar_nebula/L02_003FB000-g8.c b/src/levels/02_maktar_nebula/L02_003FB000-g8.c
new file mode 100644
index 000000000..277b822d2
--- /dev/null
+++ b/src/levels/02_maktar_nebula/L02_003FB000-g8.c
@@ -0,0 +1,51 @@
+#include "common.h"
+
+extern char *D_L02_001B359C __attribute__((sda));
+extern u32 D_L02_001FC060[];
+extern u16 D_L02_001B1214 __attribute__((sda));
+extern char D_00189E20[];
+
+/* Accumulate the active group entries and reset their current report state. */
+void func_L02_003FB000(char *object)
+{
+ char *state = *(char **)(object + 0x68);
+ u32 index = *(u32 *)(state + 0x40);
+ u32 entry;
+ short *cursor;
+ char *table;
+ char *record;
+ char *hero;
+
+ *(int *)(state + 0x60) = 0;
+ *(float *)(state + 0x64) = 0;
+ if (index == 0xFF)
+ return;
+ entry = D_L02_001FC060[index];
+ if (entry == 0)
+ return;
+ cursor = (short *)entry;
+ table = D_L02_001B359C;
+ do {
+ short type;
+ record = table + ((*(u16 *)cursor & 0x7FFF) << 8);
+ if (record == 0)
+ continue;
+ if ((u8)record[0x20] == 0xFE)
+ continue;
+ if ((u8)record[0x20] == 0xFD)
+ continue;
+ if ((u8)record[0x20] == 1)
+ continue;
+ type = *(short *)(record + 0xAA);
+ if (type == 0x9CA || type == 0xB22 || type == 0xC3B || type == 0xC3E || type == 0x9F8 || type == 0xA44) {
+ *(int *)(state + 0x60) += 1;
+ *(float *)(state + 0x64) += *(float *)(*(char **)(record + 0x68) + 0x20);
+ }
+ } while (*cursor++ >= 0);
+ hero = D_00189E20;
+ *(float *)(state + 0x68) = (float)*(int *)(hero + 0x24CC);
+ *(short *)(state + 0x11C) = 0;
+ do {
+ *(u16 *)(state + 0x11E) = D_L02_001B1214;
+ } while (0);
+}
diff --git a/src/levels/shared/L22_002A8B98-init.c b/src/levels/shared/L22_002A8B98-init.c
new file mode 100644
index 000000000..456bdca94
--- /dev/null
+++ b/src/levels/shared/L22_002A8B98-init.c
@@ -0,0 +1,46 @@
+#include "common.h"
+
+/* Borrowed player-state prefix; this view never owns an allocation. */
+typedef struct {
+ u8 reserved00[0x250];
+ float parameter250, parameter254, parameter258, parameter25c;
+ u8 reserved260[4];
+ float parameter264;
+ u8 reserved268[0xC24 - 0x268];
+ float scale;
+ u8 reservedc28[0x1888 - 0xC28];
+ void *entry_table;
+ u8 reserved188c[0x2294 - 0x188C];
+ s32 state;
+ u8 reserved2298[0x24F0 - 0x2298];
+ u8 special_level;
+} LevelPlayerPrefix;
+
+extern LevelPlayerPrefix D_00189E20;
+extern s32 D_001A79F0[] __attribute__((sda));
+extern s32 D_001A73A0[] __attribute__((sda));
+extern char D_L22_001B1920[];
+extern void func_L00_002D24B8(void);
+extern s32 func_L22_002C9898(s32, s32);
+extern void func_L00_003B09F8(void);
+
+/* Install this level's player parameters and start the shared subsystems. */
+void func_L22_002A8B98(void)
+{
+ s32 special_level = D_001A79F0[0] == 0x1A;
+ LevelPlayerPrefix *state = &D_00189E20;
+
+ state->parameter250 = 0.8f;
+ state->parameter254 = 0.7f;
+ state->parameter264 = 0.45f;
+ state->parameter258 = 0.8f;
+ state->parameter25c = 0.7f;
+ state->scale = 1.0f;
+ state->special_level = special_level;
+ D_001A73A0[0] = 0;
+ state->state = 0;
+ func_L00_002D24B8();
+ func_L22_002C9898(0, 1);
+ func_L00_003B09F8();
+ state->entry_table = D_L22_001B1920;
+}
diff --git a/src/levels/shared/L22_002A8B98.c b/src/levels/shared/L22_002A8B98.c
index 492a0ea24..656eeeb1a 100644
--- a/src/levels/shared/L22_002A8B98.c
+++ b/src/levels/shared/L22_002A8B98.c
@@ -21,7 +21,7 @@ extern void func_L00_003B09F8(void);
extern int func_L00_002ADEA0(int index);
/* Loads the level's tuning constants and starts the two subsystems. */
-INCLUDE_ASM("asm/levels", func_L22_002A8B98);
+/* func_L22_002A8B98 is defined in L22_002A8B98-init.c. */
extern void func_L00_002D36E0(void *, int, float, float, float);