Merge branch 'master' of https://github.com/dolphin-emu/dolphin into dolphin-emu-master

This commit is contained in:
Nayla Hanegan 2023-12-25 20:08:08 -05:00
commit c18016e795
767 changed files with 87644 additions and 70168 deletions

View file

@ -0,0 +1,178 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/Arm64Emitter.h"
#include "Common/BitSet.h"
#include "Common/BitUtils.h"
#include <gtest/gtest.h>
using namespace Arm64Gen;
namespace
{
u32 ZeroParameterFunction()
{
return 123;
}
u32 OneParameterFunction(u64 a)
{
return a + 23;
}
u32 TwoParameterFunction(u64 a, u64 b)
{
return a * 10 + b + 3;
}
u32 ThreeParameterFunction(u64 a, u64 b, u64 c)
{
return a * 10 + b + c / 10;
}
u32 EightParameterFunction(u64 a, u64 b, u64 c, u64 d, u64 e, u64 f, u64 g, u64 h)
{
return a / 20 + b / 8 + c / 10 + d / 2 + e / 5 - f + g + h / 3;
}
class TestCallFunction : public ARM64CodeBlock
{
public:
TestCallFunction() { AllocCodeSpace(4096); }
template <typename F>
void Emit(F f)
{
ResetCodePtr();
m_code_pointer = GetCodePtr();
{
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
constexpr BitSet32 link_register{DecodeReg(ARM64Reg::X30)};
ABI_PushRegisters(link_register);
f();
ABI_PopRegisters(link_register);
RET();
}
FlushIcacheSection(const_cast<u8*>(m_code_pointer), const_cast<u8*>(GetCodePtr()));
}
void Run()
{
const u64 actual = Common::BitCast<u64 (*)()>(m_code_pointer)();
constexpr u64 expected = 123;
EXPECT_EQ(expected, actual);
}
private:
const u8* m_code_pointer = nullptr;
};
} // namespace
TEST(Arm64Emitter, CallFunction_ZeroParameters)
{
TestCallFunction test;
test.Emit([&] { test.ABI_CallFunction(&ZeroParameterFunction); });
test.Run();
}
TEST(Arm64Emitter, CallFunction_OneConstantParameter)
{
TestCallFunction test;
test.Emit([&] { test.ABI_CallFunction(&OneParameterFunction, 100); });
test.Run();
}
TEST(Arm64Emitter, CallFunction_OneRegisterParameterNoMov)
{
TestCallFunction test;
test.Emit([&] {
test.MOVI2R(ARM64Reg::X0, 100);
test.ABI_CallFunction(&OneParameterFunction, ARM64Reg::X0);
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_OneRegisterParameterMov)
{
TestCallFunction test;
test.Emit([&] {
test.MOVI2R(ARM64Reg::X8, 100);
test.ABI_CallFunction(&OneParameterFunction, ARM64Reg::X8);
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_TwoRegistersMixed)
{
TestCallFunction test;
test.Emit([&] {
test.MOVI2R(ARM64Reg::X0, 20);
test.ABI_CallFunction(&TwoParameterFunction, 10, ARM64Reg::X0);
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_TwoRegistersCycle)
{
TestCallFunction test;
test.Emit([&] {
test.MOVI2R(ARM64Reg::X0, 20);
test.MOVI2R(ARM64Reg::X1, 10);
test.ABI_CallFunction(&TwoParameterFunction, ARM64Reg::X1, ARM64Reg::X0);
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_ThreeRegistersMixed)
{
TestCallFunction test;
test.Emit([&] {
test.MOVI2R(ARM64Reg::X1, 10);
test.MOVI2R(ARM64Reg::X2, 20);
test.ABI_CallFunction(&ThreeParameterFunction, ARM64Reg::X1, ARM64Reg::X2, 30);
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_ThreeRegistersCycle1)
{
TestCallFunction test;
test.Emit([&] {
test.MOVI2R(ARM64Reg::X0, 30);
test.MOVI2R(ARM64Reg::X1, 10);
test.MOVI2R(ARM64Reg::X2, 20);
test.ABI_CallFunction(&ThreeParameterFunction, ARM64Reg::X1, ARM64Reg::X2, ARM64Reg::X0);
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_ThreeRegistersCycle2)
{
TestCallFunction test;
test.Emit([&] {
test.MOVI2R(ARM64Reg::X0, 20);
test.MOVI2R(ARM64Reg::X1, 30);
test.MOVI2R(ARM64Reg::X2, 10);
test.ABI_CallFunction(&ThreeParameterFunction, ARM64Reg::X2, ARM64Reg::X0, ARM64Reg::X1);
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_EightRegistersMixed)
{
TestCallFunction test;
test.Emit([&] {
test.MOVI2R(ARM64Reg::X3, 12);
test.MOVI2R(ARM64Reg::X4, 23);
test.MOVI2R(ARM64Reg::X5, 24);
test.MOVI2R(ARM64Reg::X30, 2000);
test.ABI_CallFunction(&EightParameterFunction, ARM64Reg::X30, 40, ARM64Reg::X4, ARM64Reg::X5,
ARM64Reg::X4, ARM64Reg::X3, 5, ARM64Reg::X4);
});
test.Run();
}

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
add_dolphin_test(AssemblerTest AssemblerTest.cpp)
add_dolphin_test(BitFieldTest BitFieldTest.cpp)
add_dolphin_test(BitSetTest BitSetTest.cpp)
add_dolphin_test(BitUtilsTest BitUtilsTest.cpp)
@ -18,7 +19,9 @@ add_dolphin_test(SPSCQueueTest SPSCQueueTest.cpp)
add_dolphin_test(StringUtilTest StringUtilTest.cpp)
add_dolphin_test(SwapTest SwapTest.cpp)
if (_M_X86)
if (_M_X86_64)
add_dolphin_test(x64EmitterTest x64EmitterTest.cpp)
target_link_libraries(x64EmitterTest PRIVATE bdisasm)
elseif (_M_ARM_64)
add_dolphin_test(Arm64EmitterTest Arm64EmitterTest.cpp)
endif()

View file

@ -1,6 +1,7 @@
// Copyright 2018 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <limits>
#include <random>
@ -9,6 +10,8 @@
#include "Common/BitUtils.h"
#include "Common/FloatUtils.h"
#include "../Core/PowerPC/TestValues.h"
TEST(FloatUtils, IsQNAN)
{
EXPECT_TRUE(Common::IsQNAN(std::numeric_limits<double>::quiet_NaN()));
@ -62,3 +65,35 @@ TEST(FloatUtils, FlushToZero)
EXPECT_EQ(i_tmp, Common::BitCast<u32>(Common::FlushToZero(Common::BitCast<float>(i_tmp))));
}
}
TEST(FloatUtils, ApproximateReciprocalSquareRoot)
{
constexpr std::array<u64, 57> expected_values{
0x7FF0'0000'0000'0000, 0x617F'FE80'0000'0000, 0x60BF'FE80'0000'0000, 0x5FE0'0008'2C00'0000,
0x5FDF'FE80'0000'0000, 0x5FDF'FE80'0000'0000, 0x3FEF'FE80'0000'0000, 0x1FF0'0008'2C00'0000,
0x0000'0000'0000'0000, 0x7FF8'0000'0000'0001, 0x7FFF'FFFF'FFFF'FFFF, 0x7FF8'0000'0000'0000,
0x7FFF'FFFF'FFFF'FFFF, 0xFFF0'0000'0000'0000, 0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000,
0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000,
0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000, 0xFFF8'0000'0000'0001, 0xFFFF'FFFF'FFFF'FFFF,
0xFFF8'0000'0000'0000, 0xFFFF'FFFF'FFFF'FFFF, 0x43E6'9FA0'0000'0000, 0x43DF'FE80'0000'0000,
0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000, 0x43E6'9360'6000'0000, 0x43DF'ED30'7000'0000,
0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000, 0x44A6'9FA0'0000'0000, 0x4496'9FA0'0000'0000,
0x448F'FE80'0000'0000, 0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000,
0x44A6'9360'6000'0000, 0x4496'9360'6000'0000, 0x448F'ED30'7000'0000, 0x7FF8'0000'0000'0000,
0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000, 0x3C06'9FA0'0000'0000, 0x3BFF'FE80'0000'0000,
0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000, 0x43EF'FE80'0000'0000, 0x43F6'9FA0'0000'0000,
0x7FF8'0000'0000'0000, 0x7FF8'0000'0000'0000, 0x3FEA'2040'0000'0000, 0x3FA0'3108'0000'0000,
0x7FF8'0000'0000'0000};
for (size_t i = 0; i < double_test_values.size(); ++i)
{
u64 ivalue = double_test_values[i];
double dvalue = Common::BitCast<double>(ivalue);
u64 expected = expected_values[i];
u64 actual = Common::BitCast<u64>(Common::ApproximateReciprocalSquareRoot(dvalue));
EXPECT_EQ(expected, actual);
}
}

View file

@ -15,7 +15,9 @@ add_dolphin_test(ESFormatsTest IOS/ES/FormatsTest.cpp)
add_dolphin_test(FileSystemTest IOS/FS/FileSystemTest.cpp)
if(_M_X86)
add_dolphin_test(SkylandersTest IOS/USB/SkylandersTest.cpp)
if(_M_X86_64)
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/Jit64Common/ConvertDoubleToSingle.cpp

View file

@ -0,0 +1,186 @@
#include <gtest/gtest.h>
#include <array>
#include <string_view>
#include "Common/BitUtils.h"
#include "Core/IOS/USB/Emulated/Skylanders/SkylanderCrypto.h"
#include "Core/IOS/USB/Emulated/Skylanders/SkylanderFigure.h"
using namespace IOS::HLE::USB::SkylanderCrypto;
// Figure data generated by:
//
// const std::string temp_dir = File::CreateTempDir();
// IOS::HLE::USB::SkylanderFigure figure(temp_dir + "/test.sky");
// figure.Create(0x1D6, 0x3000, std::array<u8, 4>({0x01, 0x23, 0x45, 0x67}));
//
// IOS::HLE::USB::FigureData data = figure.GetData();
// data.skylander_data.money = 5000;
// data.skylander_data.hero_level = 50;
// data.skylander_data.playtime = 1564894;
// const std::u16string nickname = UTF8ToUTF16("Test");
// std::memset(data.skylander_data.nickname.data(), 0, data.skylander_data.nickname.size());
// std::memcpy(data.skylander_data.nickname.data(), nickname.data(), nickname.size() * 2);
// data.skylander_data.last_reset.minute = 5;
// data.skylander_data.last_reset.hour = 7;
// data.skylander_data.last_reset.day = 11;
// data.skylander_data.last_reset.month = 3;
// data.skylander_data.last_reset.year = 2020;
// data.skylander_data.last_placed.minute = 44;
// data.skylander_data.last_placed.hour = 8;
// data.skylander_data.last_placed.day = 14;
// data.skylander_data.last_placed.month = 4;
// data.skylander_data.last_placed.year = 2021;
// figure.SetData(&data);
//
// data.skylander_data.money = 5600;
// data.skylander_data.hero_level = 51;
// data.skylander_data.playtime = 1764894;
// std::memset(data.skylander_data.nickname.data(), 0, data.skylander_data.nickname.size());
// std::memcpy(data.skylander_data.nickname.data(), nickname.data(), nickname.size() * 2);
// data.skylander_data.last_reset.minute = 5;
// data.skylander_data.last_reset.hour = 7;
// data.skylander_data.last_reset.day = 11;
// data.skylander_data.last_reset.month = 3;
// data.skylander_data.last_reset.year = 2020;
// data.skylander_data.last_placed.minute = 59;
// data.skylander_data.last_placed.hour = 9;
// data.skylander_data.last_placed.day = 14;
// data.skylander_data.last_placed.month = 4;
// data.skylander_data.last_placed.year = 2021;
// figure.SetData(&data);
//
// std::array<u8, IOS::HLE::USB::FIGURE_SIZE> decrypted = {};
// figure.DecryptFigure(&decrypted);
// File::IOFile f(temp_dir + "/decrypted.sky", "wb");
// f.WriteBytes(decrypted.data(), decrypted.size());
//
static constexpr std::array<u8, IOS::HLE::USB::FIGURE_SIZE> decrypted_figure = {
0x01, 0x23, 0x45, 0x67, 0x00, 0x81, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xD6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xCB, 0x7D,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x4B, 0x0B, 0x20, 0x10, 0x7C, 0xCB, 0x0F, 0x0F, 0x0F, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3C, 0x98, 0xF9, 0x25, 0xA1, 0x7F, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x88, 0x13, 0xDE, 0xE0, 0x17, 0x00, 0x01, 0x88, 0x3C, 0xC4, 0xE3, 0x76, 0xF9,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1A, 0xF5, 0x2D, 0x76, 0x76, 0xBC, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2C, 0x08, 0x0E, 0x04, 0xE5, 0x07, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x07, 0x0B, 0x03, 0xE4, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x89, 0xC3, 0xC7, 0xDF, 0x9D, 0x5D, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x54, 0x5B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC5, 0x19, 0x6F, 0x78, 0x33, 0xDA, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x56, 0x2F, 0x85, 0xD1, 0xD8, 0x3B, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x70, 0x42, 0x51, 0x82, 0x0F, 0xF8, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE3, 0x74, 0xBB, 0x2B, 0xE4, 0x19, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7B, 0xC0, 0xEA, 0x64, 0xB9, 0x16, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xE0, 0x15, 0x1E, 0xEE, 0x1A, 0x00, 0x02, 0x03, 0xE7, 0xC4, 0xE3, 0x0F, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE8, 0xF6, 0x00, 0xCD, 0x52, 0xF7, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3B, 0x09, 0x0E, 0x04, 0xE5, 0x07, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x07, 0x0B, 0x03, 0xE4, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xCE, 0x9B, 0xD4, 0x9E, 0x85, 0x34, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0xD7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5D, 0xAD, 0x3E, 0x37, 0x6E, 0xD5, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x11, 0x77, 0x96, 0x90, 0xC0, 0x52, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x82, 0x41, 0x7C, 0x39, 0x2B, 0xB3, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xA4, 0x2C, 0xA8, 0x6A, 0xFC, 0x70, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x37, 0x1A, 0x42, 0xC3, 0x17, 0x91, 0x7F, 0x0F, 0x08, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Can be assumed to also mean ComputeCRC48 is correct
TEST(Skylanders, Keygen)
{
struct
{
std::array<u8, 4> nuid;
u8 sector;
u64 expected;
} const inputs[]{{{0x00, 0x00, 0x00, 0x00}, 0, 0x4B0B20107CCB},
{{0x94, 0xB0, 0xEE, 0x2D}, 0, 0x4B0B20107CCB},
{{0x00, 0x00, 0x00, 0x00}, 11, 0xEA168579FF28},
{{0x94, 0xB0, 0xEE, 0x2D}, 1, 0x278e4DA896B5},
{{0xF7, 0xDB, 0xFD, 0x5F}, 2, 0x75B9B1F4B9EB}};
for (auto& test : inputs)
{
auto actual = CalculateKeyA(test.sector, test.nuid);
EXPECT_EQ(test.expected, actual);
}
}
// Can be assumed to also mean ComputeCRC16 is correct
TEST(Skylanders, Checksums)
{
std::array<u8, 2> actual = {};
ComputeChecksumType0(decrypted_figure.data(), actual.data());
EXPECT_EQ(Common::BitCastPtr<u16>(decrypted_figure.data() + 0x1E),
Common::BitCastPtr<u16>(actual.data()));
u16 area_offset = 0x80;
for (u8 i = 0; i < 2; i++)
{
ComputeChecksumType3(decrypted_figure.data() + area_offset + 0x50, actual.data());
EXPECT_EQ(Common::BitCastPtr<u16>(decrypted_figure.data() + area_offset + 0xA),
Common::BitCastPtr<u16>(actual.data()));
ComputeChecksumType2(decrypted_figure.data() + area_offset + 0x10, actual.data());
EXPECT_EQ(Common::BitCastPtr<u16>(decrypted_figure.data() + area_offset + 0xC),
Common::BitCastPtr<u16>(actual.data()));
ComputeChecksumType1(decrypted_figure.data() + area_offset, actual.data());
EXPECT_EQ(Common::BitCastPtr<u16>(decrypted_figure.data() + area_offset + 0xE),
Common::BitCastPtr<u16>(actual.data()));
area_offset += 0x90;
ComputeChecksumType6(decrypted_figure.data() + area_offset, actual.data());
EXPECT_EQ(Common::BitCastPtr<u16>(decrypted_figure.data() + area_offset),
Common::BitCastPtr<u16>(actual.data()));
area_offset += 0x130;
}
}
TEST(Skylanders, ToyCode)
{
const std::array<u8, 11> code_chars = ComputeToyCode(0x14E2CE497CB0B);
const std::string_view code_string(reinterpret_cast<const char*>(code_chars.data()),
code_chars.size());
EXPECT_EQ(code_string, "WCJGC-HHR5Q");
}

View file

@ -84,6 +84,8 @@ TEST(JitArm64, FPRF)
const u32 expected_double = RunUpdateFPRF(
ppc_state, [&] { ppc_state.UpdateFPRFDouble(Common::BitCast<double>(double_input)); });
const u32 actual_double = RunUpdateFPRF(ppc_state, [&] { test.fprf_double(double_input); });
if (expected_double != actual_double)
fmt::print("{:016x} -> {:08x} == {:08x}\n", double_input, actual_double, expected_double);
EXPECT_EQ(expected_double, actual_double);
const u32 single_input = ConvertToSingle(double_input);
@ -91,6 +93,8 @@ TEST(JitArm64, FPRF)
const u32 expected_single = RunUpdateFPRF(
ppc_state, [&] { ppc_state.UpdateFPRFSingle(Common::BitCast<float>(single_input)); });
const u32 actual_single = RunUpdateFPRF(ppc_state, [&] { test.fprf_single(single_input); });
if (expected_single != actual_single)
fmt::print("{:08x} -> {:08x} == {:08x}\n", single_input, actual_single, expected_single);
EXPECT_EQ(expected_single, actual_single);
}
}

View file

@ -98,12 +98,12 @@ TEST(JitArm64, MovI2R_LogImm)
for (unsigned rotation = 0; rotation < size; ++rotation)
{
test.Check64(imm);
EXPECT_EQ(static_cast<bool>(LogicalImm(imm, 64)), true);
EXPECT_EQ(static_cast<bool>(LogicalImm(imm, GPRSize::B64)), true);
if (size < 64)
{
test.Check32(imm);
EXPECT_EQ(static_cast<bool>(LogicalImm(static_cast<u32>(imm), 32)), true);
EXPECT_EQ(static_cast<bool>(LogicalImm(static_cast<u32>(imm), GPRSize::B32)), true);
}
imm = (imm >> 63) | (imm << 1);

View file

@ -67,6 +67,7 @@
<ClCompile Include="Core\DSP\HermesText.cpp" />
<ClCompile Include="Core\IOS\ES\FormatsTest.cpp" />
<ClCompile Include="Core\IOS\FS\FileSystemTest.cpp" />
<ClCompile Include="Core\IOS\USB\SkylandersTest.cpp" />
<ClCompile Include="Core\MMIOTest.cpp" />
<ClCompile Include="Core\PageFaultTest.cpp" />
<ClCompile Include="Core\PowerPC\DivUtilsTest.cpp" />
@ -80,6 +81,7 @@
<ClCompile Include="Core\PowerPC\Jit64Common\Frsqrte.cpp" />
</ItemGroup>
<ItemGroup Condition="'$(Platform)'=='ARM64'">
<ClCompile Include="Common\Arm64EmitterTest.cpp" />
<ClCompile Include="Core\PowerPC\JitArm64\ConvertSingleDouble.cpp" />
<ClCompile Include="Core\PowerPC\JitArm64\FPRF.cpp" />
<ClCompile Include="Core\PowerPC\JitArm64\Fres.cpp" />

View file

@ -759,6 +759,298 @@ TEST_P(VertexLoaderNormalTest, NormalAll)
}
}
class VertexLoaderSkippedColorsTest : public VertexLoaderTest,
public ::testing::WithParamInterface<std::tuple<bool, bool>>
{
};
INSTANTIATE_TEST_SUITE_P(AllCombinations, VertexLoaderSkippedColorsTest,
::testing::Combine(::testing::Values(false, true),
::testing::Values(false, true)));
TEST_P(VertexLoaderSkippedColorsTest, SkippedColors)
{
bool enable_color_0, enable_color_1;
std::tie(enable_color_0, enable_color_1) = GetParam();
size_t input_size = 1;
size_t output_size = 3 * sizeof(float);
size_t color_0_offset = 0;
size_t color_1_offset = 0;
m_vtx_desc.low.Position = VertexComponentFormat::Index8;
if (enable_color_0)
{
m_vtx_desc.low.Color0 = VertexComponentFormat::Index8;
input_size++;
color_0_offset = output_size;
output_size += sizeof(u32);
}
if (enable_color_1)
{
m_vtx_desc.low.Color1 = VertexComponentFormat::Index8;
input_size++;
color_1_offset = output_size;
output_size += sizeof(u32);
}
m_vtx_attr.g0.PosElements = CoordComponentCount::XYZ;
m_vtx_attr.g0.PosFormat = ComponentFormat::Float;
m_vtx_attr.g0.Color0Elements = ColorComponentCount::RGBA;
m_vtx_attr.g0.Color0Comp = ColorFormat::RGBA8888;
m_vtx_attr.g0.Color1Elements = ColorComponentCount::RGBA;
m_vtx_attr.g0.Color1Comp = ColorFormat::RGBA8888;
CreateAndCheckSizes(input_size, output_size);
// Vertex 0
Input<u8>(1);
if (enable_color_0)
Input<u8>(1);
if (enable_color_1)
Input<u8>(1);
// Vertex 1
Input<u8>(0);
if (enable_color_0)
Input<u8>(0);
if (enable_color_1)
Input<u8>(0);
// Position array
VertexLoaderManager::cached_arraybases[CPArray::Position] = m_src.GetPointer();
g_main_cp_state.array_strides[CPArray::Position] =
sizeof(float); // so 1, 2, 3 for index 0; 2, 3, 4 for index 1
Input(1.f);
Input(2.f);
Input(3.f);
Input(4.f);
// Color array 0
VertexLoaderManager::cached_arraybases[CPArray::Color0] = m_src.GetPointer();
g_main_cp_state.array_strides[CPArray::Color0] = sizeof(u32);
Input<u32>(0x00010203u);
Input<u32>(0x04050607u);
// Color array 1
VertexLoaderManager::cached_arraybases[CPArray::Color1] = m_src.GetPointer();
g_main_cp_state.array_strides[CPArray::Color1] = sizeof(u32);
Input<u32>(0x08090a0bu);
Input<u32>(0x0c0d0e0fu);
ASSERT_EQ(m_loader->m_native_vtx_decl.colors[0].enable, enable_color_0);
if (enable_color_0)
{
ASSERT_EQ(m_loader->m_native_vtx_decl.colors[0].offset, color_0_offset);
}
ASSERT_EQ(m_loader->m_native_vtx_decl.colors[1].enable, enable_color_1);
if (enable_color_1)
{
ASSERT_EQ(m_loader->m_native_vtx_decl.colors[1].offset, color_1_offset);
}
RunVertices(2);
// Vertex 0
ExpectOut(2);
ExpectOut(3);
ExpectOut(4);
if (enable_color_0)
{
EXPECT_EQ((m_dst.Read<u32, true>()), 0x04050607u);
}
if (enable_color_1)
{
EXPECT_EQ((m_dst.Read<u32, true>()), 0x0c0d0e0fu);
}
// Vertex 1
ExpectOut(1);
ExpectOut(2);
ExpectOut(3);
if (enable_color_0)
{
EXPECT_EQ((m_dst.Read<u32, true>()), 0x00010203u);
}
if (enable_color_1)
{
EXPECT_EQ((m_dst.Read<u32, true>()), 0x08090a0bu);
}
}
class VertexLoaderSkippedTexCoordsTest : public VertexLoaderTest,
public ::testing::WithParamInterface<u32>
{
public:
static constexpr u32 NUM_COMPONENTS_TO_TEST = 3;
static constexpr u32 NUM_PARAMETERS_PER_COMPONENT = 3;
static constexpr u32 NUM_COMBINATIONS =
1 << (NUM_COMPONENTS_TO_TEST * NUM_PARAMETERS_PER_COMPONENT);
};
INSTANTIATE_TEST_SUITE_P(AllCombinations, VertexLoaderSkippedTexCoordsTest,
::testing::Range(0u, VertexLoaderSkippedTexCoordsTest::NUM_COMBINATIONS));
TEST_P(VertexLoaderSkippedTexCoordsTest, SkippedTextures)
{
std::array<bool, NUM_COMPONENTS_TO_TEST> enable_tex, enable_matrix, use_st;
const u32 param = GetParam();
for (u32 component = 0; component < NUM_COMPONENTS_TO_TEST; component++)
{
const u32 bits = param >> (component * NUM_PARAMETERS_PER_COMPONENT);
enable_tex[component] = (bits & 1);
enable_matrix[component] = (bits & 2);
use_st[component] = (bits & 4);
}
size_t input_size = 1;
size_t output_size = 3 * sizeof(float);
std::array<bool, NUM_COMPONENTS_TO_TEST> component_enabled{};
std::array<size_t, NUM_COMPONENTS_TO_TEST> component_offset{};
m_vtx_desc.low.Position = VertexComponentFormat::Index8;
m_vtx_attr.g0.PosElements = CoordComponentCount::XYZ;
m_vtx_attr.g0.PosFormat = ComponentFormat::Float;
for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++)
{
if (enable_matrix[i] || enable_tex[i])
{
component_enabled[i] = true;
component_offset[i] = output_size;
if (enable_matrix[i])
{
output_size += 3 * sizeof(float);
}
else
{
if (use_st[i])
{
output_size += 2 * sizeof(float);
}
else
{
output_size += sizeof(float);
}
}
}
if (enable_matrix[i])
{
m_vtx_desc.low.TexMatIdx[i] = enable_matrix[i];
input_size++;
}
if (enable_tex[i])
{
m_vtx_desc.high.TexCoord[i] = VertexComponentFormat::Index8;
input_size++;
}
m_vtx_attr.SetTexElements(i, use_st[i] ? TexComponentCount::ST : TexComponentCount::S);
m_vtx_attr.SetTexFormat(i, ComponentFormat::Float);
m_vtx_attr.SetTexFrac(i, 0);
}
CreateAndCheckSizes(input_size, output_size);
// Vertex 0
for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++)
{
if (enable_matrix[i])
Input<u8>(u8(20 + i));
}
Input<u8>(1); // Position
for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++)
{
if (enable_tex[i])
Input<u8>(1);
}
// Vertex 1
for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++)
{
if (enable_matrix[i])
Input<u8>(u8(10 + i));
}
Input<u8>(0); // Position
for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++)
{
if (enable_tex[i])
Input<u8>(0);
}
// Position array
VertexLoaderManager::cached_arraybases[CPArray::Position] = m_src.GetPointer();
g_main_cp_state.array_strides[CPArray::Position] =
sizeof(float); // so 1, 2, 3 for index 0; 2, 3, 4 for index 1
Input(1.f);
Input(2.f);
Input(3.f);
Input(4.f);
// Texture coord arrays
for (u8 i = 0; i < NUM_COMPONENTS_TO_TEST; i++)
{
VertexLoaderManager::cached_arraybases[CPArray::TexCoord0 + i] = m_src.GetPointer();
g_main_cp_state.array_strides[CPArray::TexCoord0 + i] = 2 * sizeof(float);
Input<float>(i * 100 + 11);
Input<float>(i * 100 + 12);
Input<float>(i * 100 + 21);
Input<float>(i * 100 + 22);
}
for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++)
{
ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[i].enable, component_enabled[i]);
if (component_enabled[i])
{
ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[i].offset, component_offset[i]);
}
}
RunVertices(2);
// Vertex 0
ExpectOut(2);
ExpectOut(3);
ExpectOut(4);
for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++)
{
size_t num_read = 0;
if (enable_tex[i])
{
ExpectOut(i * 100 + 21);
num_read++;
if (use_st[i])
{
ExpectOut(i * 100 + 22);
num_read++;
}
}
if (enable_matrix[i])
{
// With a matrix there are always 3 components; otherwise-unused components should be 0
while (num_read++ < 2)
ExpectOut(0);
ExpectOut(20 + i);
}
}
// Vertex 1
ExpectOut(1);
ExpectOut(2);
ExpectOut(3);
for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++)
{
size_t num_read = 0;
if (enable_tex[i])
{
ExpectOut(i * 100 + 11);
num_read++;
if (use_st[i])
{
ExpectOut(i * 100 + 12);
num_read++;
}
}
if (enable_matrix[i])
{
// With a matrix there are always 3 components; otherwise-unused components should be 0
while (num_read++ < 2)
ExpectOut(0);
ExpectOut(10 + i);
}
}
}
// For gtest, which doesn't know about our fmt::formatters by default
static void PrintTo(const VertexComponentFormat& t, std::ostream* os)
{