refactor: refactor VU1 (#191)

* feat: implement fix and changes based on dark cloud report
fix: fix GS AFAIL for RGB/alpha/Z, ZMSK
fix: fix VU1 flags mask and pipeline
fix: small VU1 cache fix
feat: __ct__, __sinit_ are not sillent stubs anymore

* feat: fix song JP pulling

* feat: sound update for lotR

* feat: prevent guest execution to be very slow

* fix: small gs size bug

* feat: refactor VU
fix: fix cliping and other issues on gs
fix: fix wrong vu0 register on recompiler

* fix fix ACC scheduler stall
feat: remove unused test
fix: .fix overflow e underflow on FMAC

* feat: small setting  for windows test
This commit is contained in:
Ranieri
2026-08-05 14:50:24 -03:00
committed by GitHub
parent 61300792a0
commit f49ca4edbc
34 changed files with 5506 additions and 1015 deletions
+148 -2
View File
@@ -82,12 +82,38 @@ namespace
0x28u;
}
uint32_t makeVuIaddiu(uint8_t it, uint8_t is, int16_t immediate)
{
return (0x08u << 25) |
(static_cast<uint32_t>(it & 0xFu) << 16) |
(static_cast<uint32_t>(is & 0xFu) << 11) |
(static_cast<uint32_t>(immediate) & 0x7FFu);
}
uint32_t makeVuLowerSpecial(uint8_t specialOp, uint8_t is,
uint8_t it = 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>(specialOp & 0x7Cu) << 4) |
static_cast<uint32_t>(specialOp & 0x3u) |
0x3Cu;
}
void writeVuInstructionPair(uint8_t *code, uint32_t pc, uint32_t lower, uint32_t upper)
{
std::memcpy(code + pc, &lower, sizeof(lower));
std::memcpy(code + pc + sizeof(lower), &upper, sizeof(upper));
}
uint64_t packVuInstructionPair(uint32_t lower, uint32_t upper)
{
return static_cast<uint64_t>(lower) |
(static_cast<uint64_t>(upper) << 32);
}
bool hasSignedRdWrite(const std::string &generated, uint8_t rd)
{
if (rd == 0u)
@@ -182,7 +208,7 @@ namespace
}
bool shouldPreempt = false;
for (int attempt = 0; attempt < 256 &&
for (int attempt = 0; attempt < 2048 &&
!shouldPreempt;
++attempt)
{
@@ -571,6 +597,30 @@ void register_ps2_runtime_expansion_tests()
t.IsFalse(innerPending, "inner scope must stay untouched");
});
tc.Run("guest preemption policy amortizes uncontended back-edge checks", [](TestCase &t)
{
PS2Runtime runtime;
uint32_t firstPreemptionCall = 0u;
// Use a fresh host thread so this assertion starts with a fresh
// thread-local back-edge counter.
std::thread worker([&]()
{
for (uint32_t call = 1u; call <= 32768u; ++call)
{
if (runtime.shouldPreemptGuestExecution())
{
firstPreemptionCall = call;
break;
}
}
});
worker.join();
t.Equals(firstPreemptionCall, 16384u,
"uncontended parser loops should amortize dispatcher handoffs across many back edges");
});
tc.Run("guest preemption policy requests a dispatcher handoff when another guest thread contends", [](TestCase &t)
{
PS2Runtime runtime;
@@ -1693,7 +1743,7 @@ void register_ps2_runtime_expansion_tests()
writeVuInstructionPair(code, 8u, 0u, makeVuAdd(0xFu, 2u, 1u, 1u));
writeVuInstructionPair(code, 16u, makeVuSq(0xFu, 2u, 0u, 1), kVuEndNop);
R5900Context ctx;
R5900Context ctx{};
runtime.executeVU0Microprogram(runtime.memory().getRDRAM(), &ctx, 0u);
float output[4]{};
@@ -1709,6 +1759,102 @@ void register_ps2_runtime_expansion_tests()
t.Equals(static_cast<uint32_t>(ctx.vi[0]), 0u, "VU0 VI0 should remain zero");
});
tc.Run("VU0 microprogram preserves the architectural RNG state", [](TestCase &t)
{
PS2Runtime runtime;
t.IsTrue(runtime.memory().initialize(), "PS2Memory initialize should succeed");
t.IsTrue(runtime.syncCoreSubsystems(), "runtime core subsystems should bind");
uint8_t *const code = runtime.memory().getVU0Code();
std::memset(code, 0, PS2_VU0_CODE_SIZE);
constexpr uint32_t kVuUpperNop = 0x000002FFu;
constexpr uint32_t kVuUpperEndNop = 0x400002FFu;
writeVuInstructionPair(
code, 0u,
makeVuLowerSpecial(0x40u, 0u, 1u, 0x8u),
kVuUpperEndNop); // RNEXT.x vf1
writeVuInstructionPair(code, 8u, 0u, kVuUpperNop);
constexpr uint32_t seed = 0x3FC00000u;
const uint32_t x = (seed >> 4) & 1u;
const uint32_t y = (seed >> 22) & 1u;
const uint32_t expected =
(((seed << 1) ^ x ^ y) & 0x007FFFFFu) | 0x3F800000u;
R5900Context ctx{};
ctx.vu0_r = _mm_castsi128_ps(
_mm_set1_epi32(static_cast<int32_t>(seed)));
runtime.executeVU0Microprogram(runtime.memory().getRDRAM(), &ctx, 0u);
alignas(16) uint32_t rWords[4]{};
_mm_storeu_si128(reinterpret_cast<__m128i *>(rWords),
_mm_castps_si128(ctx.vu0_r));
t.Equals(rWords[0], expected, "VU0 micro RNG should advance the imported R seed");
t.Equals(rWords[1], expected, "VU0 R should remain replicated for macro-mode access");
alignas(16) uint32_t vf1Words[4]{};
_mm_storeu_si128(reinterpret_cast<__m128i *>(vf1Words),
_mm_castps_si128(ctx.vu0_vf[1]));
t.Equals(vf1Words[0], expected, "RNEXT should expose the same R value through VF1.x");
});
tc.Run("VU0 direct MicroMem writes invalidate the fixed decode cache", [](TestCase &t)
{
PS2Runtime runtime;
t.IsTrue(runtime.memory().initialize(), "PS2Memory initialize should succeed");
t.IsTrue(runtime.syncCoreSubsystems(), "runtime core subsystems should bind");
constexpr uint32_t kVuUpperNop = 0x000002FFu;
constexpr uint32_t kVuUpperEndNop = 0x400002FFu;
runtime.memory().write64(
PS2_VU0_CODE_BASE,
packVuInstructionPair(makeVuIaddiu(1u, 0u, 1), kVuUpperEndNop));
runtime.memory().write64(
PS2_VU0_CODE_BASE + 8u,
packVuInstructionPair(0u, kVuUpperNop));
R5900Context first{};
runtime.executeVU0Microprogram(runtime.memory().getRDRAM(), &first, 0u);
t.Equals(static_cast<uint32_t>(first.vi[1]), 1u,
"first cached VU0 microprogram should execute");
runtime.memory().write64(
PS2_VU0_CODE_BASE,
packVuInstructionPair(makeVuIaddiu(1u, 0u, 2), kVuUpperEndNop));
R5900Context second{};
runtime.executeVU0Microprogram(runtime.memory().getRDRAM(), &second, 0u);
t.Equals(static_cast<uint32_t>(second.vi[1]), 2u,
"VU0 cache should rebuild after a direct MicroMem write");
});
tc.Run("VU0 FBRST TE gates a T-bit microprogram stop", [](TestCase &t)
{
PS2Runtime runtime;
t.IsTrue(runtime.memory().initialize(), "PS2Memory initialize should succeed");
t.IsTrue(runtime.syncCoreSubsystems(), "runtime core subsystems should bind");
uint8_t *const code = runtime.memory().getVU0Code();
std::memset(code, 0, PS2_VU0_CODE_SIZE);
constexpr uint32_t kVuUpperNop = 0x000002FFu;
writeVuInstructionPair(
code, 0u, makeVuIaddiu(1u, 0u, 7),
kVuUpperNop | 0x08000000u);
writeVuInstructionPair(
code, 8u, makeVuIaddiu(2u, 0u, 9),
kVuUpperNop);
R5900Context ctx{};
ctx.vu0_fbrst = 1u << 3; // TE0
runtime.executeVU0Microprogram(runtime.memory().getRDRAM(), &ctx, 0u);
t.Equals(static_cast<uint32_t>(ctx.vi[1]), 7u,
"the T-marked instruction should execute");
t.Equals(static_cast<uint32_t>(ctx.vi[2]), 0u,
"TE0 should stop VU0 before the following instruction");
t.IsTrue((ctx.vu0_vpu_stat & (1u << 2)) != 0u,
"VPU-STAT should report a VU0 T-bit stop");
t.Equals(ctx.vu0_tpc, 8u,
"TPC should point at the first instruction not executed");
});
tc.Run("GS sprite draw applies XYOFFSET and fully-outside scissor should not render", [](TestCase &t)
{
std::vector<uint8_t> vram(PS2_GS_VRAM_SIZE, 0u);