Skip to content

Commit 2bd95c3

Browse files
committed
Correct VIF V3 quadword boundary behavior
1 parent f072ed3 commit 2bd95c3

2 files changed

Lines changed: 11 additions & 9 deletions

File tree

ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -687,9 +687,8 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes)
687687
handledFormat = false;
688688
}
689689

690-
// The VIF expands V2 to XYXY. V3's otherwise-indeterminate W lane
691-
// overlaps the next packed source component; games rely on both
692-
// behaviors, so preserve them when the overlapping bytes are present.
690+
// The VIF expands V2 to XYXY. V3 sources W from the next packed
691+
// component unless XYZ ends on a quadword boundary, where W is zero.
693692
if (handledFormat && components == 2)
694693
{
695694
decompressed[2] = decompressed[0];
@@ -702,7 +701,9 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes)
702701
3u * static_cast<size_t>(bitsPerComponent / 8);
703702
const size_t fourthComponentBytes =
704703
static_cast<size_t>(bitsPerComponent / 8);
705-
if (fourthComponentOffset + fourthComponentBytes <= sizeBytes)
704+
decompressed[3] = 0u;
705+
if ((fourthComponentOffset & 0xFu) != 0u &&
706+
fourthComponentOffset + fourthComponentBytes <= sizeBytes)
706707
{
707708
if (vl == 0u)
708709
{

ps2xTest/src/ps2_memory_tests.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -561,14 +561,15 @@ void register_ps2_memory_tests()
561561
}
562562
});
563563

564-
tc.Run("VIF UNPACK V3 sources W from the next packed component", [](TestCase &t)
564+
tc.Run("VIF UNPACK V3 zeros W at a quadword boundary", [](TestCase &t)
565565
{
566566
PS2Memory mem;
567567
t.IsTrue(mem.initialize(), "PS2Memory initialize should succeed");
568568
std::memset(mem.getVU1Data(), 0, PS2_VU1_DATA_SIZE);
569569

570570
// UNPACK V3-16 (opcode 0x69), NUM=2. The first vector's W overlaps
571-
// the second vector's X; the second overlaps the following VIF word.
571+
// the second vector's X. The second vector's XYZ ends at the source
572+
// quadword boundary, so hardware supplies zero for W.
572573
std::vector<uint8_t> packet;
573574
appendU32(packet, makeVifCmd(0x69u, 2u, 0u));
574575
const uint16_t components[] = {
@@ -581,14 +582,14 @@ void register_ps2_memory_tests()
581582
packet.resize(offset + sizeof(component));
582583
std::memcpy(packet.data() + offset, &component, sizeof(component));
583584
}
584-
appendU32(packet, 0x0000ABCDu); // NOP VIF word and final overlapping source.
585+
appendU32(packet, 0x0000ABCDu); // A following VIF word must not leak into W.
585586

586587
mem.processVIF1Data(packet.data(), static_cast<uint32_t>(packet.size()));
587588

588589
const uint8_t *vu = mem.getVU1Data();
589590
const uint32_t expected[2][4] = {
590591
{0x1111u, 0x2222u, 0x3333u, 0x4444u},
591-
{0x4444u, 0x5555u, 0x6666u, 0xFFFFABCDu},
592+
{0x4444u, 0x5555u, 0x6666u, 0u},
592593
};
593594
for (uint32_t vector = 0u; vector < 2u; ++vector)
594595
{
@@ -597,7 +598,7 @@ void register_ps2_memory_tests()
597598
uint32_t actual = 0u;
598599
std::memcpy(&actual, vu + vector * 16u + lane * 4u, sizeof(actual));
599600
t.Equals(actual, expected[vector][lane],
600-
"V3 W should overlap the next packed source component");
601+
"V3 W should follow source quadword boundary semantics");
601602
}
602603
}
603604
});

0 commit comments

Comments
 (0)