Skip to content

Match 3 functions in hsd_3A94 - #3231

Merged
MarkMcCaskey merged 52 commits into
doldecomp:masterfrom
MarkMcCaskey:match-hsd-3a94
Aug 28, 2026
Merged

Match 3 functions in hsd_3A94#3231
MarkMcCaskey merged 52 commits into
doldecomp:masterfrom
MarkMcCaskey:match-hsd-3a94

Conversation

@MarkMcCaskey

Copy link
Copy Markdown
Collaborator

Need 3 functions for getting to vs mode. Seeding from #3223 and I’ll keep grinding from there

MarkMcCaskey and others added 3 commits August 27, 2026 09:13
Restructure fn_803AE7F8, fn_803AF3F0, fn_803B0120, and fn_803B0E9C: hoist
the repeated queue-command blocks into helpers, split the verify/write
results into their own locals, and correct the stack padding.

fn_803AE7F8 96.64 -> 98.34, fn_803AF3F0 92.95 -> 97.80,
fn_803B0120 95.55 -> 96.48, fn_803B0E9C 97.28 -> 98.27.

Co-authored-by: ford-bubba-ai <ford.lascari@gmail.com>
The target's loop exit is a bge/b pair rather than a folded conditional
branch, which is how MWCC lowers an inlined early return. 98.27 -> 98.54;
the remainder is register allocation.
Both CARDWrite retry blocks are identical; sharing one inline also drops
the opacity cast the two copies needed. 98.54 -> 99.24.
@MarkMcCaskey MarkMcCaskey added the ai-assisted Utilizes a LLM to do the heavy lifting label Aug 27, 2026
@decomp-dev

decomp-dev Bot commented Aug 27, 2026

Copy link
Copy Markdown

Report for GALE01 (05d0b1d - e796c29)

📈 Matched code: 91.92% (+0.20%, +7620 bytes)

✅ 3 new matches
Unit Item Bytes Before After
main/sysdolphin/baselib/hsd_3A94 fn_803AF3F0 +238 92.95% 100.00%
main/sysdolphin/baselib/hsd_3A94 fn_803AE7F8 +104 96.64% 100.00%
main/sysdolphin/baselib/hsd_3A94 fn_803B0E9C +33 97.28% 100.00%
📈 1 improvement in an unmatched item
Unit Item Bytes Before After
main/sysdolphin/baselib/hsd_3A94 fn_803B0120 +78 95.55% 97.82%

The first CARDRead retry loop is an inline like the write helper, which
colours its locals from the pool band; declaration order inside each
helper then fixes the remaining register rotations. 99.24 -> 99.53.
The three rewind helpers address the entry through a pointer and advance
the cursor before clearing, like cancelQueuedCardCommands. fn_803AE7F8
98.34 -> 99.10, fn_803AF3F0 97.80 -> 98.64, fn_803B0120 96.48 -> 97.27.
The caller keeps its own copy of the size, so the load CSEs and the copy
into the caller's local shows up as the original's addi. 99.10 -> 99.22.
Mirrors fn_803AE7F8: the helper reads the file size itself and the caller
keeps its own copy. 98.64 -> 98.74.
The TU already pairs First/Final block helpers; giving the last write its
own inline recovers the second site's register assignment. 99.66 -> 99.80.
The offset local sits between the two command buffers in the queue-write
helper, which gives the target's uniform 40-byte stride, and the done
command's buffer is a function-scope object above the verify buffers.
99.22 -> 99.22, but the done buffer and block map now land on their
original stack slots.
Every nine-word command buffer in the file is a CardCmd, so the queue
calls read as field assignments and the state and buffer pointers stop
being laundered through s32. Byte-identical everywhere.
Declaring both buffers at helper scope puts them in the original's order
and lands one on its stack slot. 98.82 -> 98.83.
Block map and frame now land on the original's offsets. 98.83 -> 98.84.
Matches the family idiom in fn_803ACF30 and fn_803ACD58, which the window
index picks out as 100%-matched twins of this loop, and drops the cast.
Byte-identical.
calculateBlocksBefore was a byte-identical second copy of
fn_803AC6B8_blocks_before. Byte-identical output; fn_803ADF90 stays
matched.
The target keeps state->x270[i] in one register for both the comparison
and the assignment. 98.84 -> 98.87.
The original names the induction variable: the target colours it below the
scan loop's locals, which a compiler-created IV never is. 99.22 -> 99.42.
…inal

The walker steps before the counter, and the open block reads the offset
before the file descriptor. 99.42 -> 99.46.
The original does not name it there, so the value coalesces into its
register instead of being copied. 99.46 -> 99.66.
Declaring the block offset before the command buffer puts the buffer at
the lower slot, which matches the frame the original emits for both
write-pass queue commands.

Also spell the sequence wrap as an explicit 0xFF mask.
The repair pass builds its two commands in place, like the write pass,
rather than through a shared inline: the original's frame puts all six
command buffers in one contiguous run of 40-byte blocks, which only
happens for block-scope buffers. With the block chunk size hoisted into
the loop condition the whole frame and every branch target now agree.
…us path

The queued path's success branch jumps past the x170/x270 updates, so
those two stores belong to the CARDWrite branch only.
The scan loop's index and the inlined sequence comparison hold each
other's registers otherwise.
The file block count comes from fn_803AC634 itself rather than a
duplicate of its body, and the queue entry the completion path fills in
is addressed through a pointer.
The spare-block command is the ordinary write command with a logical
index of 0xFFFF, which is what makes it reject block zero.
Pull the card-open retry and the sequence-number check into helpers, name
the write pass's block size, and address the completion queue entry
through a pointer; every command buffer now lands at its retail slot.
The digest slice offset is the block index scaled by the digest size, not
a second counter; the compiler strength-reduces it, which is why the
original never spends a register on it.
The block list the check only reads is const, and drop the now-unused
command wrapper.
The retail exit from the free-block reclaim loop branches straight to the
write pass; ours re-tested secondary_count first.  Nesting the -257 bail
inside the same guard as the reclaim loop reproduces the branch target.
The verify walker is compiler-created: retail seeds it from the already
materialised block_map base and steps it before the counter, both of
which only the block_map[0][i] index form reproduces.

With that in place the two queue sites need separate inlines, as
elsewhere in this file: the first returns the command result directly,
the final keeps a named result.  That one extra web is what orders the
walker, the counter and the data pointer, and with them file_blocks,
free_count and the sequence-match flag.
Retail keeps the incremented sequence number in a scratch register and
masks it into current_seq; the (u8) cast folds both into one web, the
& 0xFF spelling (as in fn_803AE7F8) keeps them apart.
Same shape as the verify pair: the full-chunk site returns the command
result directly, the trailing-chunk site (and the free-block site that
shares its register web) keeps a named result.  That fixes the write
pass's walker and its remaining counter.
With the two command builders split, the write pass wants the same
counter web as the other loops rather than its own; that recovers the
data pointer, the sequence number and the counter itself.
Only the trailing-chunk site keeps a named block; the full-chunk site
reads the map inline, which is what puts the chunk size in its retail
register.
The block value has to outrank the offset helper's own pooled temps in
the simplify scan; a named local puts it in the low band, where it
survives the first pass and colours before the logical index.
The two block values reach their command builders as inline parameter
temps, which are numbered above the offset helper's own temps and so
colour after them.  A one-field carrier re-ranks each into the loop
temp band, which fixes the free-block command and the full-chunk write
command; PAD_STACK absorbs the two carriers' frame slots.
Block-scope carriers sort below the completion command buffer and push
it eight bytes up; at function scope they sort above it, so the whole
frame comes back exact.
The write loop's sector load and the two block-offset sums were swapped between r3 and r4. The sector web is removed from the interference graph with degree 27 against K=29, just under the threshold, because four earlier pool temps decrement it first; it is therefore coloured after the sums instead of before. Reading remaining at the top of the loop body extends its live range across the loop head, which restores the two edges that defer the sector web to a later simplify pass.
The frame was 0x18 short of the target's 0x548; PAD_STACK(80) recovers it exactly.

The verify loop left through a break and then re-tested i >= file_blocks || remaining <= 0, which emitted the loop's bottom test a second time. The target reaches the close block by fallthrough, so the early exits become a goto past it and the guard goes away. 97.2688 -> 97.7601.
@MarkMcCaskey MarkMcCaskey changed the title Improve hsd_3A94 (WIP) Match 3 fucntions in hsd_3A94 Aug 28, 2026
MarkMcCaskey and others added 3 commits August 28, 2026 11:25
Transplants the matched fn_803AF3F0 shapes: the write helper declares its main CardCmd before the offset temp so the type-1 buffer lands below the type-2 one; the secondary-block site becomes a real queue_write call, whose unreachable type-2 buffer is still allocated and produces the target's 0x50 buffer gap; state->x8 - 0x20 moves into a chunk_size helper whose local bills the 16 bytes missing below the first buffer; and the write preheader takes AF3F0's named-temp form.

Six of the eight command buffers and block_map now sit at the target's offsets. 97.7601 -> 97.8169.
Two structural facts, both read off the frame rather than the score. The write helper declared its type-2 buffer before the type-1 one, so every expansion's pair was allocated in the wrong order. And the redundancy loop hand-expanded queue_write with phys = 0xFFFF, which allocated one named buffer at the top of the frame where the target has it fifth, followed by the unreachable type-2 buffer that produces the 0x50 gap.

Calling the helper also removes the duplicated command block. Score is unchanged at 98.7003; the buffer order and the misplaced buffer are now right, which the score cannot see.
@MarkMcCaskey
MarkMcCaskey marked this pull request as ready for review August 28, 2026 19:32
@MarkMcCaskey MarkMcCaskey changed the title Match 3 fucntions in hsd_3A94 Match 3 functions in hsd_3A94 Aug 28, 2026
@MarkMcCaskey
MarkMcCaskey merged commit 88e3324 into doldecomp:master Aug 28, 2026
12 checks passed
fjooord added a commit to fjooord/melee that referenced this pull request Aug 29, 2026
…mp#3253: upstream-gospel gmresultplayer/grkongo/grgreens/hsd_3A94/tydisplay functions; keep our exact grmaterial (Matching unit) over upstream's near-match
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-assisted Utilizes a LLM to do the heavy lifting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant