fix(dma): transfer the VIF chain tag half for every tag id - #190
Draft
smmathews wants to merge 1 commit into
Draft
fix(dma): transfer the VIF chain tag half for every tag id#190smmathews wants to merge 1 commit into
smmathews wants to merge 1 commit into
Conversation
The DMA chain walker only appended a chain tag's upper-half vifcodes to the VIF stream for CNT/NEXT/CALL/RET/END tags, skipping REFE/REF/REFS entirely. That gate conflates the tag id, which says where a tag's payload lives, with the tag's own upper quadword, which hardware transfers for every tag id when CHCR.TTE is set. REF/REFS/REFE are exactly the tags most likely to put something meaningful in that half, since their payload lives elsewhere; dropping it desyncs the parser because the referenced payload gets decoded as vifcode instead of being consumed as the data it is. Make the append unconditional per tag on both VIF channels instead of gating it on id, since the latched CHCR does not reliably expose TTE here and gating on it would regress chains that work today. Fixing that alone regresses PATH2 image continuation, because the interpreter used to drain pending continuation data as a raw byte grab at the top of its loop, with no regard for whether those bytes were actually a vifcode. Now that a chain can legitimately contain a real vifcode between an image DIRECT and its continuation, move the drain into the DIRECT/DIRECTHL handler itself: a pending continuation is only ever satisfied by a follow-up DIRECT/DIRECTHL, out of the front of that vifcode's own payload. Also raise the chain-tag walk cap, which was low enough to silently truncate a legitimately long chain, and add a one-time diagnostic for when it is hit so a truncation is visible instead of silent. The cap itself stays, as the only guard against a self-referential NEXT tag hanging the walker. Four tests pin all three pieces directly against processVIF1Data and PS2Memory, each written and confirmed failing against the unmodified tree before the fix, with no game content involved.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The DMA chain walker in
PS2Memory::writeIORegisterappended a chain tag's upperhalf — the 8 bytes at offset
+8within the tag — to the VIF stream only for tag ids 1, 2, 5, 6 and 7(CNT/NEXT/CALL/RET/END). Ids 0, 3 and 4 (REFE/REF/REFS) were skipped.
REF/REFS/REFE are the ids whose half is most likely to carry something, because their
payload lives elsewhere: a builder can put an STCYCL+UNPACK pair or a DIRECT header in
the tag half and point the tag at a separate block of raw data. Dropping it
means the vifcode that would have consumed the referenced block never runs, the
referenced bytes decode as vifcode instead, and the stream desyncs from there.
Fix
ps2_memory.cpp— append the tag half once per tag, after the id switch, for bothVIF channels (
0x10008000and0x10009000). Payload sourcing is unchanged: for ids1/2/5/6/7 the removed special case read from the tag's own
+16offset, which is whatdataAddralready holds for them.ps2_memory.cpp— raise the walk cap from 4096 to 65536 tags and print onediagnostic per process when it is hit. The cap guards against a self-referential NEXT
tag hanging the walker; at 4096 a long but legitimate chain was truncated with no
indication. The cap stays, because removing it would trade truncation for a hang.
ps2_vif1_interpreter.cpp— move the PATH2 image continuation from the top of thevifcode loop into the
DIRECT/DIRECTHLhandler. The old loop drained the next16-byte-aligned bytes as image pixels whenever a continuation was pending, without
checking whether they were a vifcode; once REF/REFS/REFE halves are transferred, a
real vifcode (NOP padding) can sit between the opening DIRECT and the continuing one.
A continuation is now satisfied from the front of a later DIRECT/DIRECTHL payload, the
rest of that payload is that DIRECT's own content, and any other vifcode runs
normally.
Hardware basis
the payload and next-tag addresses, not whether that field exists.
stream as two 32-bit vifcodes.
Testing
New tests in
ps2xTest/src/ps2_memory_tests.cppdrivePS2MemoryandprocessVIF1Datadirectly:expected VU1 offset with the expected bytes.
whose own half is non-empty.
and the continuation's image quadwords come from that second DIRECT's own payload.
One existing test continued a PATH2 image upload with unwrapped quadwords; it now
continues it with a follow-up DIRECT.
Risk / not in scope
it adds bytes that were dropped before and removes none.
scratch in the tag half. Unused padding decodes as a pair of NOPs.
reflect TTE here, so gating on it today would drop the half for chains that currently
render correctly. How this runtime merges CHCR writes is a separate, unstarted
investigation, and unconditional transfer composes with a later TTE-gated narrowing.
in
ps2_memory.cpp, two lines in an IRQ block inps2_vif1_interpreter.cpp. Neitherhunk overlaps a region changed here.
Fail-before signatures and a source-chain census
Each new test was written and run against the unmodified tree first, and failed there
with the signature below.
Background, not part of the automated gate and not reproducible from this branch: a VIF1
source chain captured from a frame was walked and its decoded vifcode stream censused
three ways — current main behaviour, unconditional append, and TTE-gated append. Under
the unconditional rule the stream decoded without unrecognized opcodes and without
out-of-range microprogram kicks; the other two rules each produced some. The tests
above, not this census, are what the suite enforces.