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
+143 -10
View File
@@ -12,6 +12,7 @@
#include <filesystem>
#include <cctype>
#include <condition_variable>
#include <cstring>
#include <exception>
#include <mutex>
#include <queue>
@@ -750,6 +751,7 @@ namespace ps2recomp
m_stubFunctions.clear();
m_stubFunctionStarts.clear();
m_stubHandlerBindingsByStart.clear();
m_correctnessCriticalFunctionStarts.clear();
for (const auto &name : m_config.skipFunctions)
{
@@ -809,6 +811,7 @@ namespace ps2recomp
m_symbols = m_elfParser->extractSymbols();
m_sections = m_elfParser->getSections();
m_relocations = m_elfParser->getRelocations();
collectCorrectnessCriticalFunctionStarts();
if (m_functions.empty())
{
@@ -937,24 +940,81 @@ namespace ps2recomp
size_t processedCount = 0;
size_t failedCount = 0;
size_t correctnessCriticalFailureCount = 0;
for (uint32_t initializerStart : m_correctnessCriticalFunctionStarts)
{
const auto functionIt = std::find_if(
m_functions.begin(), m_functions.end(),
[initializerStart](const Function &function)
{ return function.start == initializerStart; });
if (functionIt == m_functions.end())
{
const auto bindingIt = m_stubHandlerBindingsByStart.find(initializerStart);
if (bindingIt != m_stubHandlerBindingsByStart.end() &&
resolveStubTarget(bindingIt->second) != StubTarget::Unknown)
{
Function manualInitializer{};
manualInitializer.name = "manual_initializer_" + bindingIt->second;
manualInitializer.start = initializerStart;
manualInitializer.end = initializerStart + 4u;
m_functions.push_back(std::move(manualInitializer));
m_reporter.info(
"correctness-critical",
"Synthesized initializer entry for resolved handler '" +
bindingIt->second + "'");
continue;
}
++correctnessCriticalFailureCount;
m_reporter.recordCorrectnessCriticalFailure();
m_reporter.errorAt(
"correctness-critical",
".ctors/.init_array",
initializerStart,
"Initializer table target has no discovered guest function or manual handler");
}
}
for (auto &function : m_functions)
{
m_reporter.recordFunctionProcessed();
const bool correctnessCritical = isCorrectnessCriticalFunction(function);
if (isStubFunction(function))
{
function.isStub = true;
function.isSkipped = false;
m_reporter.recordFunctionStubbed();
continue;
if (!correctnessCritical || hasResolvedStubHandler(function))
{
function.isStub = true;
function.isSkipped = false;
m_reporter.recordFunctionStubbed();
continue;
}
m_reporter.recordCorrectnessCriticalGuestFallback();
m_reporter.warningAt(
"correctness-critical",
function.name,
function.start,
"Unresolved initializer stub ignored; recompiling the original guest function");
}
if (shouldSkipFunction(function))
{
function.isSkipped = true;
function.isStub = false;
m_reporter.recordFunctionSkipped();
continue;
if (!correctnessCritical)
{
function.isSkipped = true;
function.isStub = false;
m_reporter.recordFunctionSkipped();
continue;
}
m_reporter.recordCorrectnessCriticalGuestFallback();
m_reporter.warningAt(
"correctness-critical",
function.name,
function.start,
"Initializer skip ignored; recompiling the original guest function");
}
if (!decodeFunction(function))
@@ -962,11 +1022,26 @@ namespace ps2recomp
++failedCount;
m_reporter.recordDecodeFailure();
m_reporter.recordFunctionSkipped();
m_reporter.warningAt("decode", function.name, function.start, "Skipping function due decode failure");
function.isSkipped = true;
if (correctnessCritical)
{
++correctnessCriticalFailureCount;
m_reporter.recordCorrectnessCriticalFailure();
m_reporter.errorAt(
"correctness-critical",
function.name,
function.start,
"Initializer could not be recompiled and has no resolved manual handler");
}
else
{
m_reporter.warningAt("decode", function.name, function.start, "Skipping function due decode failure");
}
continue;
}
function.isStub = false;
function.isSkipped = false;
function.isRecompiled = true;
m_reporter.recordFunctionRecompiled();
#if _DEBUG
@@ -992,7 +1067,7 @@ namespace ps2recomp
}
m_reporter.progress("recompilation pass completed");
return true;
return correctnessCriticalFailureCount == 0u;
}
catch (const std::exception &e)
{
@@ -1964,6 +2039,64 @@ namespace ps2recomp
return ps2_runtime_calls::isStubName(function.name);
}
bool PS2Recompiler::IsCorrectnessCriticalFunctionName(const std::string &name)
{
static constexpr const char *kPrefixes[] = {
"__ct__",
"__sinit_",
"_GLOBAL__sub_I_",
"GLOBAL__sub_I_",
"__static_initialization_and_destruction_0",
"__do_global_ctors",
};
for (const char *prefix : kPrefixes)
{
if (name.rfind(prefix, 0u) == 0u)
return true;
}
return false;
}
bool PS2Recompiler::isCorrectnessCriticalFunction(const Function &function) const
{
return IsCorrectnessCriticalFunctionName(function.name) ||
m_correctnessCriticalFunctionStarts.contains(function.start);
}
bool PS2Recompiler::hasResolvedStubHandler(const Function &function) const
{
std::string handlerName = function.name;
const auto bindingIt = m_stubHandlerBindingsByStart.find(function.start);
if (bindingIt != m_stubHandlerBindingsByStart.end() && !bindingIt->second.empty())
handlerName = bindingIt->second;
return resolveStubTarget(handlerName) != StubTarget::Unknown;
}
void PS2Recompiler::collectCorrectnessCriticalFunctionStarts()
{
m_correctnessCriticalFunctionStarts.clear();
for (const Section &section : m_sections)
{
if (section.name != ".ctors" &&
section.name != ".init_array" &&
section.name != ".preinit_array")
{
continue;
}
if (section.data == nullptr || section.size < sizeof(uint32_t))
continue;
for (uint32_t offset = 0; offset + sizeof(uint32_t) <= section.size; offset += sizeof(uint32_t))
{
uint32_t target = 0u;
std::memcpy(&target, section.data + offset, sizeof(target));
if (target != 0u && target != 0xFFFFFFFFu)
m_correctnessCriticalFunctionStarts.insert(target);
}
}
}
bool PS2Recompiler::writeToFile(const std::string &path, const std::string &content)
{
std::ofstream file(path);
@@ -113,6 +113,18 @@ namespace ps2recomp
m_counters.generatedFunctions += count;
}
void RecompilerReporter::recordCorrectnessCriticalGuestFallback()
{
std::lock_guard<std::mutex> lock(m_mutex);
++m_counters.correctnessCriticalGuestFallbacks;
}
void RecompilerReporter::recordCorrectnessCriticalFailure()
{
std::lock_guard<std::mutex> lock(m_mutex);
++m_counters.correctnessCriticalFailures;
}
void RecompilerReporter::recordIndirectFallbackPromotion(const std::string &functionName,
const std::vector<uint32_t> &jumpAddresses,
size_t promotedEntryCount)
@@ -183,6 +195,8 @@ namespace ps2recomp
os << "Indirect fallback promotions: " << m_counters.indirectFallbackPromotions
<< " (" << m_counters.indirectFallbackEntries << " fallback entries)" << std::endl;
os << "Unhandled instructions: " << m_counters.unhandledInstructions << std::endl;
os << "Correctness-critical guest fallbacks: " << m_counters.correctnessCriticalGuestFallbacks
<< ", failures: " << m_counters.correctnessCriticalFailures << std::endl;
size_t warnings = 0;
size_t errors = 0;
+18 -22
View File
@@ -209,8 +209,7 @@ namespace ps2recomp
return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); "
"__m128 res = PS2_VADD(ctx->vu0_acc, mul_res); "
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
"ctx->vu0_acc = res; }}",
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
vfs, vft, vft, shuffle_pattern,
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
@@ -230,8 +229,7 @@ namespace ps2recomp
return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); "
"__m128 res = PS2_VSUB(ctx->vu0_acc, mul_res); "
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
"ctx->vu0_acc = res; }}",
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
vfs, vft, vft, shuffle_pattern,
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
@@ -285,8 +283,7 @@ namespace ps2recomp
return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); "
"__m128 res = PS2_VADD(ctx->vu0_acc, mul_res); "
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
"ctx->vu0_acc = res; }}",
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
vfs, vft,
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
@@ -301,8 +298,7 @@ namespace ps2recomp
return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q)); "
"__m128 res = PS2_VADD(ctx->vu0_acc, mul_res); "
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
"ctx->vu0_acc = res; }}",
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
vfs,
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
@@ -317,8 +313,7 @@ namespace ps2recomp
return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i)); "
"__m128 res = PS2_VADD(ctx->vu0_acc, mul_res); "
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
"ctx->vu0_acc = res; }}",
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
vfs,
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
@@ -402,12 +397,13 @@ namespace ps2recomp
uint8_t vfs = inst.rd;
uint8_t vft = inst.rt;
uint8_t dest_mask = inst.vectorInfo.vectorField;
return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); "
return fmt::format("{{ __m128 fs_yzx = _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(3,0,2,1)); "
"__m128 ft_zxy = _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(3,1,0,2)); "
"__m128 mul_res = PS2_VMUL(fs_yzx, ft_zxy); "
"__m128 res = PS2_VSUB(ctx->vu0_acc, mul_res); "
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
"ctx->vu0_acc = res; }}",
vfs, vft,
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
vfs, vfs, vft, vft,
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
vfd, vfd);
@@ -450,8 +446,7 @@ namespace ps2recomp
return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); "
"__m128 res = PS2_VSUB(ctx->vu0_acc, mul_res); "
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
"ctx->vu0_acc = res; }}",
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
vfs, vft,
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
@@ -509,8 +504,7 @@ namespace ps2recomp
return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q)); "
"__m128 res = PS2_VSUB(ctx->vu0_acc, mul_res); "
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
"ctx->vu0_acc = res; }}",
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
vfs,
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
@@ -525,8 +519,7 @@ namespace ps2recomp
return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i)); "
"__m128 res = PS2_VSUB(ctx->vu0_acc, mul_res); "
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
"ctx->vu0_acc = res; }}",
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
vfs,
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
@@ -730,8 +723,11 @@ namespace ps2recomp
uint8_t vfs = inst.rd;
uint8_t vft = inst.rt;
uint8_t dest_mask = inst.vectorInfo.vectorField;
return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); ctx->vu0_acc = _mm_blendv_ps(ctx->vu0_acc, res, {}); }}",
vfs, vft, codegen::vuMaskExpr(dest_mask));
return fmt::format("{{ __m128 fs_yzx = _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(3,0,2,1)); "
"__m128 ft_zxy = _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(3,1,0,2)); "
"__m128 res = PS2_VMUL(fs_yzx, ft_zxy); "
"ctx->vu0_acc = _mm_blendv_ps(ctx->vu0_acc, res, {}); }}",
vfs, vfs, vft, vft, codegen::vuMaskExpr(dest_mask));
}
std::string CodeGenerator::translateVU_VITOF(const Instruction &inst, int shift)
+35 -82
View File
@@ -29,124 +29,77 @@ namespace ps2recomp
return fmt::format("SET_GPR_VEC(ctx, {}, _mm_castps_si128(ctx->vu0_vf[{}]));", rt, rd);
case COP2_CFC2:
{
switch (rd) // Control register number is in rd
// CFC2/CTC2 use the same 5-bit register field for VI0..VI15 and the VU special control registers.
if (rd < 16)
{
return fmt::format("SET_GPR_U32(ctx, {}, static_cast<uint32_t>(ctx->vi[{}]));", rt, rd);
}
switch (rd)
{
case VU0_CR_STATUS:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_status);", rt);
case VU0_CR_MAC:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_mac_flags);", rt);
case VU0_CR_VPU_STAT:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_vpu_stat);", rt);
case VU0_CR_CLIP:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_clip_flags & 0x00FFFFFFu);", rt);
case VU0_CR_R:
return fmt::format("SET_GPR_VEC(ctx, {}, _mm_castps_si128(ctx->vu0_r));", rt);
return fmt::format("SET_GPR_U32(ctx, {}, static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_castps_si128(ctx->vu0_r))));", rt);
case VU0_CR_I:
return fmt::format("{{ uint32_t bits; std::memcpy(&bits, &ctx->vu0_i, sizeof(bits)); SET_GPR_U32(ctx, {}, bits); }}", rt);
case VU0_CR_CLIP:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_clip_flags);", rt);
case VU0_CR_Q:
return fmt::format("{{ uint32_t bits; std::memcpy(&bits, &ctx->vu0_q, sizeof(bits)); SET_GPR_U32(ctx, {}, bits); }}", rt);
case VU0_CR_TPC:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_tpc);", rt);
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_tpc >> 3);", rt);
case VU0_CR_CMSAR0:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_cmsar0);", rt);
case VU0_CR_FBRST:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_fbrst);", rt);
case VU0_CR_VPU_STAT2:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_vpu_stat2);", rt);
case VU0_CR_TPC2:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_tpc2);", rt);
case VU0_CR_VPU_STAT:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_vpu_stat);", rt);
case VU0_CR_CMSAR1:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_cmsar1);", rt);
case VU0_CR_FBRST2:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_fbrst2);", rt);
case VU0_CR_VPU_STAT3:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_vpu_stat3);", rt);
case VU0_CR_CMSAR2:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_cmsar2);", rt);
case VU0_CR_FBRST3:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_fbrst3);", rt);
case VU0_CR_VPU_STAT4:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_vpu_stat4);", rt);
case VU0_CR_CMSAR3:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_cmsar3);", rt);
case VU0_CR_FBRST4:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_fbrst4);", rt);
case VU0_CR_ACC:
return fmt::format("SET_GPR_VEC(ctx, {}, _mm_castps_si128(ctx->vu0_acc));", rt);
case VU0_CR_INFO: // I dd found on offical docs but ok
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_info);", rt);
case VU0_CR_CLIP2:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_clip_flags2);", rt);
case VU0_CR_P:
return fmt::format("{{ uint32_t bits; std::memcpy(&bits, &ctx->vu0_p, sizeof(bits)); SET_GPR_U32(ctx, {}, bits); }}", rt);
case VU0_CR_XITOP: // Maybe this does not exist, maybe we handle to vu0_itop
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_xitop);", rt);
case VU0_CR_ITOP:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_itop);", rt);
case VU0_CR_TOP:
return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_top);", rt);
default:
return fmt::format("// Unimplemented CFC2 VU CReg: {}", rt);
return fmt::format("// Unimplemented CFC2 VU control register: {}", rd);
}
}
case COP2_QMTC2:
return fmt::format("ctx->vu0_vf[{}] = _mm_castsi128_ps(GPR_VEC(ctx, {}));", rd, rt);
case COP2_CTC2:
{
switch (rd) // Control register number is in rd
if (rd < 16)
{
if (rd == 0)
{
return "// CTC2 write to VI0 ignored";
}
return fmt::format("ctx->vi[{}] = static_cast<uint16_t>(GPR_U32(ctx, {}));", rd, rt);
}
switch (rd)
{
case VU0_CR_STATUS:
return fmt::format("ctx->vu0_status = GPR_U32(ctx, {}) & 0xFFFF;", rt);
return fmt::format("ctx->vu0_status = static_cast<uint16_t>(GPR_U32(ctx, {}) & 0xFFFFu);", rt);
case VU0_CR_MAC:
return fmt::format("ctx->vu0_mac_flags = GPR_U32(ctx, {});", rt);
case VU0_CR_TPC:
case VU0_CR_VPU_STAT:
return fmt::format("ctx->vu0_vpu_stat = GPR_U32(ctx, {});", rt);
return fmt::format("// CTC2 write to read-only VU control register {} ignored", rd);
case VU0_CR_CLIP:
return fmt::format("ctx->vu0_clip_flags = GPR_U32(ctx, {});", rt);
return fmt::format("ctx->vu0_clip_flags = GPR_U32(ctx, {}) & 0x00FFFFFFu;", rt);
case VU0_CR_R:
return fmt::format("ctx->vu0_r = _mm_castsi128_ps(GPR_VEC(ctx, {}));", rt);
return fmt::format("ctx->vu0_r = _mm_castsi128_ps(_mm_set1_epi32(static_cast<int32_t>(GPR_U32(ctx, {}))));", rt);
case VU0_CR_I:
return fmt::format("{{ uint32_t tmp = GPR_U32(ctx, {}); std::memcpy(&ctx->vu0_i, &tmp, sizeof(tmp)); }}", rt);
case VU0_CR_TPC:
return fmt::format("ctx->vu0_tpc = GPR_U32(ctx, {});", rt);
case VU0_CR_Q:
return fmt::format("{{ uint32_t tmp = GPR_U32(ctx, {}); std::memcpy(&ctx->vu0_q, &tmp, sizeof(tmp)); }}", rt);
case VU0_CR_CMSAR0:
return fmt::format("ctx->vu0_cmsar0 = GPR_U32(ctx, {});", rt);
case VU0_CR_FBRST:
return fmt::format("ctx->vu0_fbrst = GPR_U32(ctx, {});", rt);
case VU0_CR_VPU_STAT2:
return fmt::format("ctx->vu0_vpu_stat2 = GPR_U32(ctx, {});", rt);
case VU0_CR_TPC2:
return fmt::format("ctx->vu0_tpc2 = GPR_U32(ctx, {});", rt);
return fmt::format("ctx->vu0_fbrst = GPR_U32(ctx, {}) & 0x00000C0Cu;", rt);
case VU0_CR_CMSAR1:
return fmt::format("ctx->vu0_cmsar1 = GPR_U32(ctx, {});", rt);
case VU0_CR_FBRST2:
return fmt::format("ctx->vu0_fbrst2 = GPR_U32(ctx, {});", rt);
case VU0_CR_VPU_STAT3:
return fmt::format("ctx->vu0_vpu_stat3 = GPR_U32(ctx, {});", rt);
case VU0_CR_CMSAR2:
return fmt::format("ctx->vu0_cmsar2 = GPR_U32(ctx, {});", rt);
case VU0_CR_FBRST3:
return fmt::format("ctx->vu0_fbrst3 = GPR_U32(ctx, {});", rt);
case VU0_CR_VPU_STAT4:
return fmt::format("ctx->vu0_vpu_stat4 = GPR_U32(ctx, {});", rt);
case VU0_CR_CMSAR3:
return fmt::format("ctx->vu0_cmsar3 = GPR_U32(ctx, {});", rt);
case VU0_CR_FBRST4:
return fmt::format("ctx->vu0_fbrst4 = GPR_U32(ctx, {});", rt);
case VU0_CR_ACC:
return fmt::format("ctx->vu0_acc = _mm_castsi128_ps(GPR_VEC(ctx, {}));", rt);
case VU0_CR_INFO:
return fmt::format("ctx->vu0_info = GPR_U32(ctx, {});", rt);
case VU0_CR_CLIP2:
return fmt::format("ctx->vu0_clip_flags2 = GPR_U32(ctx, {});", rt);
case VU0_CR_P:
return fmt::format("{{ uint32_t tmp = GPR_U32(ctx, {}); std::memcpy(&ctx->vu0_p, &tmp, sizeof(tmp)); }}", rt);
case VU0_CR_XITOP:
return fmt::format("ctx->vu0_xitop = GPR_U32(ctx, {}) & 0x3FF;", rt);
case VU0_CR_ITOP:
return fmt::format("ctx->vu0_itop = GPR_U32(ctx, {}) & 0x3FF;", rt);
case VU0_CR_TOP:
return fmt::format("ctx->vu0_top = GPR_U32(ctx, {}) & 0x3FF;", rt);
default:
return fmt::format("// Unimplemented CTC2 VU CReg: {}", rd);
return fmt::format("// Unimplemented CTC2 VU control register: {}", rd);
}
}
case COP2_BC: