diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 82d2d6748a..58bb11f6bf 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -168,34 +168,35 @@ void PPULLVMRecompiler::VADDSWS(u32 vd, u32 va, u32 vb) { // of any one of the operands. u32 tmp1_v4i32[4] = {0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF}; auto tmp2_v4i32 = m_ir_builder.CreateLShr(va_v4i32, 31); - auto tmp3_v4i32 = m_ir_builder.CreateAdd(tmp2_v4i32, ConstantDataVector::get(m_llvm_context, tmp1_v4i32)); + tmp2_v4i32 = m_ir_builder.CreateAdd(tmp2_v4i32, ConstantDataVector::get(m_llvm_context, tmp1_v4i32)); + auto tmp2_v16i8 = m_ir_builder.CreateBitCast(tmp2_v4i32, VectorType::get(Type::getInt8Ty(m_llvm_context), 16)); // Next, we find if the addition can actually result in an overflow. Since an overflow can only happen if the operands - // have the same sign, we bitwise AND both the operands. If the sign bit of the result is 1 then the operands have the - // same sign and so may cause an overflow. - auto tmp4_v4i32 = m_ir_builder.CreateAnd(va_v4i32, vb_v4i32); + // have the same sign, we bitwise XOR both the operands. If the sign bit of the result is 0 then the operands have the + // same sign and so may cause an overflow. We invert the result so that the sign bit is 1 when the operands have the + // same sign. + auto tmp3_v4i32 = m_ir_builder.CreateXor(va_v4i32, vb_v4i32); + u32 not_mask_v4i32[4] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; + tmp3_v4i32 = m_ir_builder.CreateXor(tmp3_v4i32, ConstantDataVector::get(m_llvm_context, not_mask_v4i32)); // Perform the sum. auto sum_v4i32 = m_ir_builder.CreateAdd(va_v4i32, vb_v4i32); + auto sum_v16i8 = m_ir_builder.CreateBitCast(sum_v4i32, VectorType::get(Type::getInt8Ty(m_llvm_context), 16)); // If an overflow occurs, then the sign of the sum will be different from the sign of the operands. So, we xor the // result with one of the operands. The sign bit of the result will be 1 if the sign bit of the sum and the sign bit of the - // result is different. This result is again ANDed with tmp4 (the sign bit of tmp4 is 1 only if the operands have the same + // result is different. This result is again ANDed with tmp3 (the sign bit of tmp3 is 1 only if the operands have the same // sign and so can cause an overflow). - auto tmp5_v4i32 = m_ir_builder.CreateXor(va_v4i32, sum_v4i32); - auto tmp6_v4i32 = m_ir_builder.CreateAnd(tmp4_v4i32, tmp5_v4i32); - auto tmp7_v4i32 = m_ir_builder.CreateAShr(tmp6_v4i32, 31); + auto tmp4_v4i32 = m_ir_builder.CreateXor(va_v4i32, sum_v4i32); + tmp4_v4i32 = m_ir_builder.CreateAnd(tmp3_v4i32, tmp4_v4i32); + tmp4_v4i32 = m_ir_builder.CreateAShr(tmp4_v4i32, 31); + auto tmp4_v16i8 = m_ir_builder.CreateBitCast(tmp4_v4i32, VectorType::get(Type::getInt8Ty(m_llvm_context), 16)); - // tmp7 is equal to 0xFFFFFFFF if an overflow occured and 0x00000000 otherwise. tmp9 is bitwise inverse of tmp7. - u32 tmp8_v4i32[4] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; - auto tmp9_v4i32 = m_ir_builder.CreateXor(tmp7_v4i32, ConstantDataVector::get(m_llvm_context, tmp8_v4i32)); - auto tmp10_v4i32 = m_ir_builder.CreateAnd(tmp3_v4i32, tmp7_v4i32); - auto tmp11_v4i32 = m_ir_builder.CreateAnd(sum_v4i32, tmp9_v4i32); - auto tmp12_v4i32 = m_ir_builder.CreateOr(tmp10_v4i32, tmp11_v4i32); - SetVr(vd, tmp12_v4i32); + // tmp4 is equal to 0xFFFFFFFF if an overflow occured and 0x00000000 otherwise. + auto res_v16i8 = m_ir_builder.CreateCall3(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse41_pblendvb), sum_v16i8, tmp2_v16i8, tmp4_v16i8); + SetVr(vd, res_v16i8); // TODO: Set SAT - // TODO: Optimize with pblend } void PPULLVMRecompiler::VADDUBM(u32 vd, u32 va, u32 vb) { @@ -210,6 +211,8 @@ void PPULLVMRecompiler::VADDUBS(u32 vd, u32 va, u32 vb) { auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto sum_v16i8 = m_ir_builder.CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_paddus_b), va_v16i8, vb_v16i8); SetVr(vd, sum_v16i8); + + // TODO: Set SAT } void PPULLVMRecompiler::VADDUHM(u32 vd, u32 va, u32 vb) { @@ -224,6 +227,8 @@ void PPULLVMRecompiler::VADDUHS(u32 vd, u32 va, u32 vb) { auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto sum_v8i16 = m_ir_builder.CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_paddus_w), va_v8i16, vb_v8i16); SetVr(vd, sum_v8i16); + + // TODO: Set SAT } void PPULLVMRecompiler::VADDUWM(u32 vd, u32 va, u32 vb) { @@ -246,20 +251,19 @@ void PPULLVMRecompiler::VADDUWS(u32 vd, u32 va, u32 vb) { } void PPULLVMRecompiler::VAND(u32 vd, u32 va, u32 vb) { - auto va_v1i128 = GetVrAsIntVec(va, 128); - auto vb_v1i128 = GetVrAsIntVec(vb, 128); - auto res_v1i128 = m_ir_builder.CreateAnd(va_v1i128, vb_v1i128); - SetVr(vd, res_v1i128); + auto va_v4i32 = GetVrAsIntVec(va, 32); + auto vb_v4i32 = GetVrAsIntVec(vb, 32); + auto res_v4i32 = m_ir_builder.CreateAnd(va_v4i32, vb_v4i32); + SetVr(vd, res_v4i32); } void PPULLVMRecompiler::VANDC(u32 vd, u32 va, u32 vb) { - auto va_v1i128 = GetVrAsIntVec(va, 128); - auto vb_v1i128 = GetVrAsIntVec(vb, 128); - vb_v1i128 = m_ir_builder.CreateXor(vb_v1i128, m_ir_builder.getInt(APInt(128, "-1", 10))); - auto res_v1i128 = m_ir_builder.CreateAnd(va_v1i128, vb_v1i128); - SetVr(vd, res_v1i128); - - // TODO: Check if this generates ANDC + auto va_v4i32 = GetVrAsIntVec(va, 32); + auto vb_v4i32 = GetVrAsIntVec(vb, 32); + u32 not_mask_v4i32[4] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; + vb_v4i32 = m_ir_builder.CreateXor(vb_v4i32, ConstantDataVector::get(m_llvm_context, not_mask_v4i32)); + auto res_v4i32 = m_ir_builder.CreateAnd(va_v4i32, vb_v4i32); + SetVr(vd, res_v4i32); } void PPULLVMRecompiler::VAVGSB(u32 vd, u32 va, u32 vb) { @@ -271,7 +275,7 @@ void PPULLVMRecompiler::VAVGSB(u32 vd, u32 va, u32 vb) { u16 one_v16i16[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; sum_v16i16 = m_ir_builder.CreateAdd(sum_v16i16, ConstantDataVector::get(m_llvm_context, one_v16i16)); auto avg_v16i16 = m_ir_builder.CreateAShr(sum_v16i16, 1); - auto avg_v16i8 = m_ir_builder.CreateBitCast(avg_v16i16, VectorType::get(Type::getInt8Ty(m_llvm_context), 16)); + auto avg_v16i8 = m_ir_builder.CreateTrunc(avg_v16i16, VectorType::get(Type::getInt8Ty(m_llvm_context), 16)); SetVr(vd, avg_v16i8); } @@ -284,7 +288,7 @@ void PPULLVMRecompiler::VAVGSH(u32 vd, u32 va, u32 vb) { u32 one_v8i32[8] = {1, 1, 1, 1, 1, 1, 1, 1}; sum_v8i32 = m_ir_builder.CreateAdd(sum_v8i32, ConstantDataVector::get(m_llvm_context, one_v8i32)); auto avg_v8i32 = m_ir_builder.CreateAShr(sum_v8i32, 1); - auto avg_v8i16 = m_ir_builder.CreateBitCast(avg_v8i32, VectorType::get(Type::getInt16Ty(m_llvm_context), 8)); + auto avg_v8i16 = m_ir_builder.CreateTrunc(avg_v8i32, VectorType::get(Type::getInt16Ty(m_llvm_context), 8)); SetVr(vd, avg_v8i16); } @@ -297,7 +301,7 @@ void PPULLVMRecompiler::VAVGSW(u32 vd, u32 va, u32 vb) { u64 one_v4i64[4] = {1, 1, 1, 1}; sum_v4i64 = m_ir_builder.CreateAdd(sum_v4i64, ConstantDataVector::get(m_llvm_context, one_v4i64)); auto avg_v4i64 = m_ir_builder.CreateAShr(sum_v4i64, 1); - auto avg_v4i32 = m_ir_builder.CreateBitCast(avg_v4i64, VectorType::get(Type::getInt32Ty(m_llvm_context), 4)); + auto avg_v4i32 = m_ir_builder.CreateTrunc(avg_v4i64, VectorType::get(Type::getInt32Ty(m_llvm_context), 4)); SetVr(vd, avg_v4i32); } @@ -324,7 +328,7 @@ void PPULLVMRecompiler::VAVGUW(u32 vd, u32 va, u32 vb) { u64 one_v4i64[4] = {1, 1, 1, 1}; sum_v4i64 = m_ir_builder.CreateAdd(sum_v4i64, ConstantDataVector::get(m_llvm_context, one_v4i64)); auto avg_v4i64 = m_ir_builder.CreateLShr(sum_v4i64, 1); - auto avg_v4i32 = m_ir_builder.CreateBitCast(avg_v4i64, VectorType::get(Type::getInt32Ty(m_llvm_context), 4)); + auto avg_v4i32 = m_ir_builder.CreateTrunc(avg_v4i64, VectorType::get(Type::getInt32Ty(m_llvm_context), 4)); SetVr(vd, avg_v4i32); } diff --git a/rpcs3/Emu/Cell/PPULLVMRecompilerTests.cpp b/rpcs3/Emu/Cell/PPULLVMRecompilerTests.cpp index a1cf4480f5..1ccffa07f0 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompilerTests.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompilerTests.cpp @@ -1,129 +1,441 @@ -#include "stdafx.h" -#include "Utilities/Log.h" -#include "Emu/Cell/PPULLVMRecompiler.h" -#include "llvm/Analysis/Verifier.h" -#include "llvm/CodeGen/MachineCodeInfo.h" -#include "llvm/ExecutionEngine/GenericValue.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; - -void PPULLVMRecompiler::RunTest(const char * name, std::function test_case, std::function input, std::function check_result) { - // Create the unit test function - auto function = cast(m_module->getOrInsertFunction(name, Type::getVoidTy(m_llvm_context), (Type *)nullptr)); - auto block = BasicBlock::Create(m_llvm_context, "start", function); - m_ir_builder.SetInsertPoint(block); - test_case(); - m_ir_builder.CreateRetVoid(); - verifyFunction(*function); - - // Print the IR - std::string ir; - raw_string_ostream ir_ostream(ir); - function->print(ir_ostream); - LOG_NOTICE(PPU, "[UT %s] LLVM IR:%s", name, ir.c_str()); - - // Generate the function - MachineCodeInfo mci; - m_execution_engine->runJITOnFunction(function, &mci); - - // Disassember the generated function - LOG_NOTICE(PPU, "[UT %s] Disassembly:", name); - for (uint64_t pc = 0; pc < mci.size();) { - char str[1024]; - - auto size = LLVMDisasmInstruction(m_disassembler, (uint8_t *)mci.address() + pc, mci.size() - pc, (uint64_t)((uint8_t *)mci.address() + pc), str, sizeof(str)); - LOG_NOTICE(PPU, "[UT %s] %p: %s.", name, (uint8_t *)mci.address() + pc, str); - pc += size; - } - - // Run the test - input(); - std::vector args; - m_execution_engine->runFunction(function, args); - - // Verify results - std::string msg; - bool pass = check_result(msg); - if (pass) { - LOG_NOTICE(PPU, "[UT %s] Test passed. %s.", name, msg.c_str()); - } else { - LOG_ERROR(PPU, "[UT %s] Test failed. %s.", name, msg.c_str()); - } - - m_execution_engine->freeMachineCodeForFunction(function); -} - -void PPULLVMRecompiler::RunAllTests() { - std::function test_case; - std::function input; - std::function check_result; - - LOG_NOTICE(PPU, "Running Unit Tests"); - - /////////////////////////////////////////////////////////////////////////// - test_case = [this]() { - MFVSCR(1); - }; - input = [this]() { - m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x9ABCDEF0; - m_ppu.VSCR.VSCR = 0x12345678; - }; - check_result = [this](std::string & msg) { - msg = fmt::Format("VPR[1]=%s", m_ppu.VPR[1].ToString(true).c_str()); - return m_ppu.VPR[1].Equals((u32)0x12345678, (u32)0, (u32)0, (u32)0); - }; - RunTest("MFVSCR.1", test_case, input, check_result); - - /////////////////////////////////////////////////////////////////////////// - test_case = [this]() { - MTVSCR(1); - }; - input = [this]() { - m_ppu.VPR[1]._u32[0] = 0x9ABCDEF0; - m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x11112222; - m_ppu.VSCR.VSCR = 0x12345678; - }; - check_result = [this](std::string & msg) { - msg = fmt::Format("VPR[1]=0x%s, VSCR=0x%lX", m_ppu.VPR[1].ToString(true).c_str(), m_ppu.VSCR.VSCR); - return m_ppu.VSCR.VSCR == 0x9ABCDEF0; - }; - RunTest("MTVSCR.1", test_case, input, check_result); - - /////////////////////////////////////////////////////////////////////////// - test_case = [this]() { - VADDCUW(0, 1, 2); - }; - input = [this]() { - m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x9ABCDEF0; - m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x99999999; - m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = 0x77777777; - m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 1; - }; - check_result = [this](std::string & msg) { - msg = fmt::Format("VPR[0]=0x%s, VPR[1]=0x%s, VPR[2]=0x%s", - m_ppu.VPR[0].ToString(true).c_str(), - m_ppu.VPR[1].ToString(true).c_str(), - m_ppu.VPR[2].ToString(true).c_str()); - return m_ppu.VPR[0].Equals((u32)1, (u32)1, (u32)0, (u32)0); - }; - RunTest("VADDCUW.1", test_case, input, check_result); - - /////////////////////////////////////////////////////////////////////////// - test_case = [this]() { - VADDFP(0, 1, 2); - }; - input = [this]() { - m_ppu.VPR[0]._f[0] = m_ppu.VPR[0]._f[1] = m_ppu.VPR[0]._f[2] = m_ppu.VPR[0]._f[3] = 100.0f; - m_ppu.VPR[1]._f[0] = m_ppu.VPR[1]._f[1] = m_ppu.VPR[1]._f[2] = m_ppu.VPR[1]._f[3] = 500.0f; - m_ppu.VPR[2]._f[0] = m_ppu.VPR[2]._f[1] = m_ppu.VPR[2]._f[2] = m_ppu.VPR[2]._f[3] = 900.0f; - }; - check_result = [this](std::string & msg) { - msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", - m_ppu.VPR[0].ToString().c_str(), - m_ppu.VPR[1].ToString().c_str(), - m_ppu.VPR[2].ToString().c_str()); - return m_ppu.VPR[0].Equals(1400.0f, 1400.0f, 1400.0f, 1400.0f); - }; - RunTest("VADDFP.1", test_case, input, check_result); -} +#include "stdafx.h" +#include "Utilities/Log.h" +#include "Emu/Cell/PPULLVMRecompiler.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/CodeGen/MachineCodeInfo.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +void PPULLVMRecompiler::RunTest(const char * name, std::function test_case, std::function input, std::function check_result) { + // Create the unit test function + auto function = cast(m_module->getOrInsertFunction(name, Type::getVoidTy(m_llvm_context), (Type *)nullptr)); + auto block = BasicBlock::Create(m_llvm_context, "start", function); + m_ir_builder.SetInsertPoint(block); + test_case(); + m_ir_builder.CreateRetVoid(); + verifyFunction(*function); + + // Print the IR + std::string ir; + raw_string_ostream ir_ostream(ir); + function->print(ir_ostream); + LOG_NOTICE(PPU, "[UT %s] LLVM IR:%s", name, ir.c_str()); + + // Generate the function + MachineCodeInfo mci; + m_execution_engine->runJITOnFunction(function, &mci); + + // Disassember the generated function + LOG_NOTICE(PPU, "[UT %s] Disassembly:", name); + for (uint64_t pc = 0; pc < mci.size();) { + char str[1024]; + + auto size = LLVMDisasmInstruction(m_disassembler, (uint8_t *)mci.address() + pc, mci.size() - pc, (uint64_t)((uint8_t *)mci.address() + pc), str, sizeof(str)); + LOG_NOTICE(PPU, "[UT %s] %p: %s.", name, (uint8_t *)mci.address() + pc, str); + pc += size; + } + + // Run the test + input(); + std::vector args; + m_execution_engine->runFunction(function, args); + + // Verify results + std::string msg; + bool pass = check_result(msg); + if (pass) { + LOG_NOTICE(PPU, "[UT %s] Test passed. %s.", name, msg.c_str()); + } else { + LOG_ERROR(PPU, "[UT %s] Test failed. %s.", name, msg.c_str()); + } + + m_execution_engine->freeMachineCodeForFunction(function); +} + +void PPULLVMRecompiler::RunAllTests() { + std::function test_case; + std::function input; + std::function check_result; + + LOG_NOTICE(PPU, "Running Unit Tests"); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + MFVSCR(1); + }; + input = [this]() { + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x9ABCDEF0; + m_ppu.VSCR.VSCR = 0x12345678; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[1]=%s", m_ppu.VPR[1].ToString(true).c_str()); + return m_ppu.VPR[1].Equals((u32)0x12345678, (u32)0, (u32)0, (u32)0); + }; + RunTest("MFVSCR.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + MTVSCR(1); + }; + input = [this]() { + m_ppu.VPR[1]._u32[0] = 0x9ABCDEF0; + m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x11112222; + m_ppu.VSCR.VSCR = 0x12345678; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[1]=0x%s, VSCR=0x%lX", m_ppu.VPR[1].ToString(true).c_str(), m_ppu.VSCR.VSCR); + return m_ppu.VSCR.VSCR == 0x9ABCDEF0; + }; + RunTest("MTVSCR.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDCUW(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x9ABCDEF0; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x99999999; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = 0x77777777; + m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 1; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=0x%s, VPR[1]=0x%s, VPR[2]=0x%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)1, (u32)1, (u32)0, (u32)0); + }; + RunTest("VADDCUW.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDFP(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._f[0] = m_ppu.VPR[0]._f[1] = m_ppu.VPR[0]._f[2] = m_ppu.VPR[0]._f[3] = 100.0f; + m_ppu.VPR[1]._f[0] = m_ppu.VPR[1]._f[1] = m_ppu.VPR[1]._f[2] = m_ppu.VPR[1]._f[3] = 500.0f; + m_ppu.VPR[2]._f[0] = m_ppu.VPR[2]._f[1] = m_ppu.VPR[2]._f[2] = m_ppu.VPR[2]._f[3] = 900.0f; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString().c_str(), + m_ppu.VPR[1].ToString().c_str(), + m_ppu.VPR[2].ToString().c_str()); + return m_ppu.VPR[0].Equals(1400.0f, 1400.0f, 1400.0f, 1400.0f); + }; + RunTest("VADDFP.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDSBS(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12F06690; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x12F06690; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x24E07F80, (u32)0x24E07F80, (u32)0x24E07F80, (u32)0x24E07F80); + }; + RunTest("VADDSBS.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDSHS(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = 0x12006600; + m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0xFF009000; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x24007FFF, (u32)0x24007FFF, (u32)0xFE008000, (u32)0xFE008000); + }; + RunTest("VADDSHS.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDSWS(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[2]._u32[0] = 0x66000000; + m_ppu.VPR[1]._u32[1] = m_ppu.VPR[2]._u32[1] = 0x90000000; + m_ppu.VPR[1]._u32[2] = m_ppu.VPR[2]._u32[2] = 0x12000000; + m_ppu.VPR[1]._u32[3] = m_ppu.VPR[2]._u32[3] = 0xFF000000; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x7FFFFFFF, (u32)0x80000000, (u32)0x24000000, (u32)0xFE000000); + }; + RunTest("VADDSWS.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDUBM(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12368890; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x12368890; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x246C1020, (u32)0x246C1020, (u32)0x246C1020, (u32)0x246C1020); + }; + RunTest("VADDUBM.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDUBS(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12368890; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x12368890; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x246CFFFF, (u32)0x246CFFFF, (u32)0x246CFFFF, (u32)0x246CFFFF); + }; + RunTest("VADDUBS.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDUHM(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12368890; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x12368890; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x246C1120, (u32)0x246C1120, (u32)0x246C1120, (u32)0x246C1120); + }; + RunTest("VADDUHM.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDUHS(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12368890; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x12368890; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x246CFFFF, (u32)0x246CFFFF, (u32)0x246CFFFF, (u32)0x246CFFFF); + }; + RunTest("VADDUHS.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDUWM(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[2]._u32[0] = 0x12345678; + m_ppu.VPR[1]._u32[1] = m_ppu.VPR[2]._u32[1] = 0x87654321; + m_ppu.VPR[1]._u32[2] = m_ppu.VPR[2]._u32[2] = 0x12345678; + m_ppu.VPR[1]._u32[3] = m_ppu.VPR[2]._u32[3] = 0x87654321; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x2468ACF0, (u32)0x0ECA8642, (u32)0x2468ACF0, (u32)0x0ECA8642); + }; + RunTest("VADDUWM.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VADDUWS(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[2]._u32[0] = 0x12345678; + m_ppu.VPR[1]._u32[1] = m_ppu.VPR[2]._u32[1] = 0x87654321; + m_ppu.VPR[1]._u32[2] = m_ppu.VPR[2]._u32[2] = 0x12345678; + m_ppu.VPR[1]._u32[3] = m_ppu.VPR[2]._u32[3] = 0x87654321; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x2468ACF0, (u32)0xFFFFFFFF, (u32)0x2468ACF0, (u32)0xFFFFFFFF); + }; + RunTest("VADDUWS.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VAND(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0xAAAAAAAA; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0xFFFF0000; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0xAAAA0000, (u32)0xAAAA0000, (u32)0xAAAA0000, (u32)0xAAAA0000); + }; + RunTest("VAND.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VANDC(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0xAAAAAAAA; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0xFFFF0000; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x0000AAAA, (u32)0x0000AAAA, (u32)0x0000AAAA, (u32)0x0000AAAA); + }; + RunTest("VANDC.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VAVGSB(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12345678; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x89ABCDEF; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0xCEF01234, (u32)0xCEF01234, (u32)0xCEF01234, (u32)0xCEF01234); + }; + RunTest("VAVGSB.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VAVGSH(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12345678; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x89ABCDEF; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0xCDF01234, (u32)0xCDF01234, (u32)0xCDF01234, (u32)0xCDF01234); + }; + RunTest("VAVGSH.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VAVGSW(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12345678; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x89ABCDEF; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0xCDF01234, (u32)0xCDF01234, (u32)0xCDF01234, (u32)0xCDF01234); + }; + RunTest("VAVGSW.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VAVGUB(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12345678; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x89ABCDEF; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x4E7092B4, (u32)0x4E7092B4, (u32)0x4E7092B4, (u32)0x4E7092B4); + }; + RunTest("VAVGUB.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VAVGUH(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12345678; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x89ABCDEF; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x4DF09234, (u32)0x4DF09234, (u32)0x4DF09234, (u32)0x4DF09234); + }; + RunTest("VAVGUH.1", test_case, input, check_result); + + /////////////////////////////////////////////////////////////////////////// + test_case = [this]() { + VAVGUW(0, 1, 2); + }; + input = [this]() { + m_ppu.VPR[0]._u32[0] = m_ppu.VPR[0]._u32[1] = m_ppu.VPR[0]._u32[2] = m_ppu.VPR[0]._u32[3] = 0x00000000; + m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[1] = m_ppu.VPR[1]._u32[2] = m_ppu.VPR[1]._u32[3] = 0x12345678; + m_ppu.VPR[2]._u32[0] = m_ppu.VPR[2]._u32[1] = m_ppu.VPR[2]._u32[2] = m_ppu.VPR[2]._u32[3] = 0x89ABCDEF; + }; + check_result = [this](std::string & msg) { + msg = fmt::Format("VPR[0]=%s, VPR[1]=%s, VPR[2]=%s", + m_ppu.VPR[0].ToString(true).c_str(), + m_ppu.VPR[1].ToString(true).c_str(), + m_ppu.VPR[2].ToString(true).c_str()); + return m_ppu.VPR[0].Equals((u32)0x4DF01234, (u32)0x4DF01234, (u32)0x4DF01234, (u32)0x4DF01234); + }; + RunTest("VAVGUW.1", test_case, input, check_result); +}