Feature vu1 cache (#158)

* feat: explode vu1 in files
feat: added way  more tests for vu1

* feat: added VU1 cache
This commit is contained in:
Ranieri
2026-07-07 21:48:26 -03:00
committed by GitHub
parent 52edf07657
commit ecc86f4b5d
14 changed files with 2145 additions and 1628 deletions
-157
View File
@@ -2,7 +2,6 @@
#include "runtime/ps2_memory.h"
#include "runtime/ps2_gs_gpu.h"
#include "runtime/ps2_gs_psmct32.h"
#include "runtime/ps2_vu1.h"
#include "ps2_runtime.h"
#include "ps2_runtime_macros.h"
#include "Stubs/DMA.h"
@@ -156,17 +155,6 @@ namespace
appendU64(packet, 0u);
}
uint32_t makeVuLowerSpecial(uint8_t specialOp, uint8_t is, uint8_t it = 0u, uint8_t id = 0u, uint8_t dest = 0u)
{
return (0x40u << 25) |
(static_cast<uint32_t>(dest & 0xFu) << 21) |
(static_cast<uint32_t>(it & 0x1Fu) << 16) |
(static_cast<uint32_t>(is & 0x1Fu) << 11) |
(static_cast<uint32_t>(id & 0x1Fu) << 6) |
(static_cast<uint32_t>(specialOp & 0x7Cu) << 4) |
static_cast<uint32_t>(specialOp & 0x3u) |
0x3Cu;
}
}
void register_ps2_memory_tests()
@@ -1732,72 +1720,6 @@ void register_ps2_memory_tests()
t.Equals(mem.readIORegister(kDstadr), 0u, "sceDmaReset should clear D_STADR");
});
tc.Run("VU1 XGKICK wraps packet payload across VU1 memory boundary", [](TestCase &t)
{
PS2Memory mem;
t.IsTrue(mem.initialize(), "PS2Memory initialize should succeed");
std::vector<std::vector<uint8_t>> captured;
mem.setGifPacketCallback([&](const uint8_t *data, uint32_t sizeBytes)
{
captured.emplace_back(data, data + sizeBytes);
});
std::vector<uint8_t> vram(PS2_GS_VRAM_SIZE, 0u);
GS gs;
gs.init(vram.data(), static_cast<uint32_t>(vram.size()), nullptr);
uint8_t *vuCode = mem.getVU1Code();
uint8_t *vuData = mem.getVU1Data();
std::memset(vuCode, 0, PS2_VU1_CODE_SIZE);
std::memset(vuData, 0, PS2_VU1_DATA_SIZE);
constexpr uint32_t kLastQw = (PS2_VU1_DATA_SIZE / 16u) - 1u;
const uint32_t tagOffset = kLastQw * 16u;
const uint64_t imageTag = makeGifTag(1u, GIF_FMT_IMAGE, 0u, true);
std::memcpy(vuData + tagOffset, &imageTag, sizeof(imageTag));
for (uint32_t i = 0; i < 16u; ++i)
{
vuData[i] = static_cast<uint8_t>(0xC0u + i);
}
const uint32_t lower = makeVuLowerSpecial(0x6Cu, 1u);
std::memcpy(vuCode + 0u, &lower, sizeof(lower));
const uint32_t upper = 0u;
std::memcpy(vuCode + 4u, &upper, sizeof(upper));
VU1Interpreter vu1;
vu1.state().vi[1] = static_cast<int32_t>(kLastQw);
vu1.execute(vuCode,
PS2_VU1_CODE_SIZE,
vuData,
PS2_VU1_DATA_SIZE,
gs,
&mem,
0u,
0u,
0u,
1u);
t.Equals(captured.size(), static_cast<size_t>(1u), "XGKICK should emit one wrapped GIF packet");
if (!captured.empty())
{
t.Equals(captured[0].size(), static_cast<size_t>(32u), "wrapped packet should include tag plus one qword payload");
bool payloadOk = true;
for (uint32_t i = 0; i < 16u; ++i)
{
if (captured[0].size() < 32u || captured[0][16u + i] != static_cast<uint8_t>(0xC0u + i))
{
payloadOk = false;
break;
}
}
t.IsTrue(payloadOk, "wrapped payload should be copied from start of VU1 memory");
}
});
tc.Run("VIF1 DMA DIRECT image packet reaches GS through arbiter", [](TestCase &t)
{
PS2Memory mem;
@@ -1919,85 +1841,6 @@ void register_ps2_memory_tests()
t.IsTrue(imageOk, "raw qwords after a DIRECT image tag should continue the PATH2 image upload");
});
tc.Run("VIF MSCAL callback can execute XGKICK and update GS VRAM", [](TestCase &t)
{
PS2Memory mem;
t.IsTrue(mem.initialize(), "PS2Memory initialize should succeed");
GS gs;
gs.init(mem.getGSVRAM(), static_cast<uint32_t>(PS2_GS_VRAM_SIZE), &mem.gs());
GifArbiter arbiter([&](const uint8_t *data, uint32_t sizeBytes)
{
gs.processGIFPacket(data, sizeBytes);
});
mem.setGifArbiter(&arbiter);
const uint64_t bitblt =
(static_cast<uint64_t>(0u) << 0) |
(static_cast<uint64_t>(1u) << 16) |
(static_cast<uint64_t>(0u) << 24) |
(static_cast<uint64_t>(0u) << 32) |
(static_cast<uint64_t>(1u) << 48) |
(static_cast<uint64_t>(0u) << 56);
gs.writeRegister(GS_REG_BITBLTBUF, bitblt);
gs.writeRegister(GS_REG_TRXPOS, 0ull);
gs.writeRegister(GS_REG_TRXREG, (4ull << 0) | (1ull << 32));
gs.writeRegister(GS_REG_TRXDIR, 0ull);
uint8_t *vuCode = mem.getVU1Code();
uint8_t *vuData = mem.getVU1Data();
std::memset(vuCode, 0, PS2_VU1_CODE_SIZE);
std::memset(vuData, 0, PS2_VU1_DATA_SIZE);
const uint32_t lower = makeVuLowerSpecial(0x6Cu, 0u);
std::memcpy(vuCode + 0u, &lower, sizeof(lower));
const uint32_t upper = 0u;
std::memcpy(vuCode + 4u, &upper, sizeof(upper));
const uint64_t gifTag = makeGifTag(1u, GIF_FMT_IMAGE, 0u, true);
std::memcpy(vuData + 0u, &gifTag, sizeof(gifTag));
const uint64_t tagHi = 0u;
std::memcpy(vuData + 8u, &tagHi, sizeof(tagHi));
for (uint32_t i = 0; i < 16u; ++i)
{
vuData[16u + i] = static_cast<uint8_t>(0x90u + i);
}
VU1Interpreter vu1;
mem.setVu1MscalCallback([&](uint32_t startPC, uint32_t top, uint32_t itop)
{
vu1.execute(vuCode,
PS2_VU1_CODE_SIZE,
vuData,
PS2_VU1_DATA_SIZE,
gs,
&mem,
startPC,
top,
itop,
1u);
});
const uint32_t mscalCmd = makeVifCmd(0x14u, 0u, 0u);
mem.processVIF1Data(reinterpret_cast<const uint8_t *>(&mscalCmd), sizeof(mscalCmd));
const uint8_t *vramOut = mem.getGSVRAM();
bool imageOk = true;
for (uint32_t x = 0; x < 4u && imageOk; ++x)
{
const uint32_t off = GSPSMCT32::addrPSMCT32(0u, 1u, x, 0u);
for (uint32_t c = 0; c < 4u; ++c)
{
if (vramOut[off + c] != static_cast<uint8_t>(0x90u + x * 4u + c))
{
imageOk = false;
break;
}
}
}
t.IsTrue(imageOk, "MSCAL-triggered XGKICK should route PATH1 packet into GS VRAM");
});
tc.Run("unaligned accesses throw", [](TestCase &t)
{
PS2Memory mem;