fix(vu1): maintain MAC/STATUS/CLIP flag registers, correct the flag-reading lower-op table, and saturate FTOI - #189
Draft
smmathews wants to merge 1 commit into
Draft
Conversation
…eading lower-op table, and saturate FTOI The VU1 interpreter declared mac/status/clip fields and the lower-op instructions that read them, but the upper FMAC pipeline never wrote mac, and STATUS was written by exactly one instruction using an unsourced immediate extraction. Every arithmetic FMAC vf-writer and ACC-writer now computes MAC over its DEST lanes and folds it into a STATUS live/sticky pair; MAX/MINI (which the PS2 FMAC pipeline runs without any flag update, matching PCSX2's applyMinMax) and the nine ITOF/FTOI/ABS unary ops and the plain dest-mask lower ops (LQ, MOVE, MR32, LQI, LQD, MFIR, MFP) are explicitly excluded from the flag path. Separately, the flag-reading lower-op table at 0x18-0x1C had two of its four case bodies swapped (0x18/0x1A) and was missing FMOR (0x1B) and FCGET (0x1C, which ran a duplicate FMOR body instead). FSSET's immediate extraction is replaced with the formula sourced from PCSX2's reference VU interpreter, since none of the formulas previously in circulation for this codebase matched it. CLIP's shift-register accumulation is now masked to 24 bits, since without it FCOR permanently loses the ability to compare true once five or more CLIP instructions have run. FTOI0/ 4/12/15 now saturate on overflow instead of relying on undefined behavior, which on x86 silently produced INT_MIN for large positive values too. All of this is sourced against PCSX2 pcsx2/VUops.cpp and pcsx2/VUflags.cpp (see EVIDENCE.md); pipeline latency (FDIV/Q, EFU/P, and any multi-cycle flag commit delay) is out of scope here and left for a follow-up with its own citation.
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
VU1Statecarriesmac,statusandclip, and the FM, FS and FC compare ops that read themhave been present throughout. Nothing in the FMAC pipeline ever wrote
mac. Before this PR:FMAND/FMEQ/FMOR therefore always read zero. Further defects sit in the same path:
0x1B (FMOR), and the FMOR body at 0x1C, which is FCGET.
m_state.clip = (m_state.clip << 6) | flags;accumulates unmasked. After five CLIPs, bitsabove 23 are set permanently and FCOR's
(clip | imm24) == 0xFFFFFFcan never go true again.FCSET masks its own write; the shift-register accumulation is what is unmasked.
int32_t. An out-of-range cast is undefinedbehavior; on x86
cvttss2sireturns0x80000000whatever the sign, so a large positiveinput produced
INT_MIN.Fix
applyDestsplits intowriteDestMasked(the destination-mask copy) andcomputeFmacFlags,so which sites raise flags is visible at the call site (table below).
FMAC, and an OR-only sticky half (bits 11:6) that FSAND/FSOR read.
CLIP's low 12 bits, guarded on a nonzero destination).
imm12 = ((instr >> 21) & 1) << 11 | (instr & 0x7FF)and writes the sticky halfonly, leaving the live half as the last FMAC set it. It previously used
(instr >> 6) & 0xFC0.& 0xFFFFFF.are covered, clamps to
0x7fffffffor0x80000000by sign bit; otherwise the existing cast runs.Reference basis
Sourced against PCSX2:
VUflags.cpp,VU_MAC_UPDATE/VU_STAT_UPDATE— MAC nibble layout, paired U+Z on a flusheddenormal (
0x0101 << shift), per-lane clear outside DEST, live/sticky STATUS fold.VUops.cpp,_LOWER_OPCODE[128]— 0x18 FMEQ, 0x19 unassigned, 0x1A FMAND, 0x1B FMOR, 0x1C FCGET.VUops.cpp,_vuFSSET— the immediate extraction.VUops.cpp,_vuCLIP— the 24-bit mask after each accumulation.VUops.cpp,floatToInt<Offset>— sign-based clamp with no separate NaN case.MAX/MINI are excluded from the flag path: the min/max helper (
applyMinMax) performs no flagupdate.
Testing
New cases in the
PS2VU1suite pin:reading 0.
MAC unchanged.
live half and writing the sourced immediate into the sticky half.
positive NaN to INT_MAX.
Risk and not in scope
order), not execution results, so a cached instruction still recomputes MAC/STATUS/CLIP from
current operands each run.
CLIP commit delay all stay immediate. Each needs its own citation.
computeFmacFlags.MAC layout and flag-call membership
MAC is 16 bits. Within each nibble the lanes run x, y, z, w from the high bit to the low bit.
Z and U can be set together. Lanes outside DEST are not evaluated and read 0 in every field.
writeDestMaskedcomputeFmacFlagsapplyDestAccThe ACC-writer group is uniform, so
applyDestAccbundles both calls; MAC reflects the FMAC resultwhether the destination is a vf register or the accumulator. The vf-writer group is mixed, so the
flag call stays a separate line at each site.