From 35840c8dc64d7e97763fbc5f17501da5c212c8d4 Mon Sep 17 00:00:00 2001 From: MadLadMikael Date: Mon, 17 Mar 2025 03:11:14 -0500 Subject: [PATCH 1/5] added instructions for potf --- XenonRecomp/CMakeLists.txt | 5 ++ XenonRecomp/recompiler.cpp | 69 ++++++++++++++++++ .../disasm/CMakeFiles/disasm.dir/disasm.c.obj | Bin 0 -> 9275 bytes .../CMakeFiles/disasm.dir/ppc-dis.c.obj | Bin 0 -> 246107 bytes thirdparty/disasm/cmake_install.cmake | 45 ++++++++++++ thirdparty/disasm/libdisasm.a | Bin 0 -> 256456 bytes 6 files changed, 119 insertions(+) create mode 100644 thirdparty/disasm/CMakeFiles/disasm.dir/disasm.c.obj create mode 100644 thirdparty/disasm/CMakeFiles/disasm.dir/ppc-dis.c.obj create mode 100644 thirdparty/disasm/cmake_install.cmake create mode 100644 thirdparty/disasm/libdisasm.a diff --git a/XenonRecomp/CMakeLists.txt b/XenonRecomp/CMakeLists.txt index f5db6d1..895c18c 100644 --- a/XenonRecomp/CMakeLists.txt +++ b/XenonRecomp/CMakeLists.txt @@ -2,6 +2,11 @@ cmake_minimum_required (VERSION 3.8) project("XenonRecomp") +# Find required packages +find_package(fmt REQUIRED) +find_package(tomlplusplus REQUIRED) +find_package(xxHash REQUIRED) + add_executable(XenonRecomp "main.cpp" "recompiler.cpp" diff --git a/XenonRecomp/recompiler.cpp b/XenonRecomp/recompiler.cpp index 5374797..d28ca56 100644 --- a/XenonRecomp/recompiler.cpp +++ b/XenonRecomp/recompiler.cpp @@ -2240,6 +2240,75 @@ bool Recompiler::Recompile( println("\t{}.u64 = {}.u64 ^ {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2] << 16); break; + case PPC_INST_MULHD: + println("\t{}.u64 = ({}.u64 * {}.u64) >> 64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s64, 0);", cr(0), r(insn.operands[0])); + break; + +case PPC_INST_FRSQRTE: + println("\t{}.f64 = 1.0 / sqrt({}.f64);", f(insn.operands[0]), f(insn.operands[1])); + break; + +case PPC_INST_DCBST: + println("\t// Data Cache Block Store - system instruction, no operation in emulator"); + break; + +case PPC_INST_VCFPUXWS128: + println("\t{}.v4sf = vec_convert_to_float({}.v4si);", v(insn.operands[0]), v(insn.operands[1])); + break; + +case PPC_INST_VPKUWUS128: + println("\t{}.v8hi = vec_pack_unsigned({}.v4si, {}.v4si);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + +case PPC_INST_VPKUHUS128: + println("\t{}.v16qi = vec_pack_unsigned({}.v8hi, {}.v8hi);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + +case PPC_INST_EQV: + println("\t{}.u64 = ~({}.u64 ^ {}.u64);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s64, 0);", cr(0), r(insn.operands[0])); + break; + +case PPC_INST_VPKSWSS128: + println("\t{}.v8hi = vec_pack_saturate({}.v4si, {}.v4si);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + +case PPC_INST_MULHDU: + println("\t{}.u64 = ({}.u64 * {}.u64) >> 64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.u64, 0);", cr(0), r(insn.operands[0])); + break; + +case PPC_INST_STFSU: + println("\t*({}.ptr) = {}.f64;", r(insn.operands[1]), f(insn.operands[0])); + println("\t{}.u64 = {}.u64 + {}.s64;", r(insn.operands[1]), r(insn.operands[1]), r(insn.operands[2])); + break; + +case PPC_INST_MULLHWU: + println("\t{}.u32 = ({}.u16 * {}.u16);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.u32, 0);", cr(0), r(insn.operands[0])); + break; + +case PPC_INST_VSEL128: + println("\t{}.v4si = vec_sel({}.v4si, {}.v4si, {}.v4si);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), v(insn.operands[3])); + break; + +case PPC_INST_VNOR128: + println("\t{}.v4si = ~({}.v4si | {}.v4si);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + +case PPC_INST_VANDC: + println("\t{}.v4si = {}.v4si & ~{}.v4si;", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + +case PPC_INST_VPKSWSS: + println("\t{}.v8hi = vec_pack_saturate({}.v4si, {}.v4si);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + default: return false; } diff --git a/thirdparty/disasm/CMakeFiles/disasm.dir/disasm.c.obj b/thirdparty/disasm/CMakeFiles/disasm.dir/disasm.c.obj new file mode 100644 index 0000000000000000000000000000000000000000..a3d0f68a853c5a3479f798ecd456605aea7aaba4 GIT binary patch literal 9275 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

+%>^9)3wO8+O@^C*LBQQ@-FQv&sE>m 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 C`nfd# literal 0 HcmV?d00001 diff --git a/thirdparty/disasm/cmake_install.cmake b/thirdparty/disasm/cmake_install.cmake new file mode 100644 index 0000000..1238808 --- /dev/null +++ b/thirdparty/disasm/cmake_install.cmake @@ -0,0 +1,45 @@ +# Install script for directory: E:/recomp/XenonRecomp/thirdparty/disasm + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "C:/Program Files (x86)/disasm") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "Debug") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "FALSE") +endif() + +# Set path to fallback-tool for dependency-resolution. +if(NOT DEFINED CMAKE_OBJDUMP) + set(CMAKE_OBJDUMP "C:/msys64/clang64/bin/llvm-objdump.exe") +endif() + +string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT + "${CMAKE_INSTALL_MANIFEST_FILES}") +if(CMAKE_INSTALL_LOCAL_ONLY) + file(WRITE "E:/recomp/XenonRecomp/thirdparty/disasm/install_local_manifest.txt" + "${CMAKE_INSTALL_MANIFEST_CONTENT}") +endif() diff --git a/thirdparty/disasm/libdisasm.a b/thirdparty/disasm/libdisasm.a new file mode 100644 index 0000000000000000000000000000000000000000..9e9a18c8e1b94ef50fda7f425454f3d54d6a80fa GIT binary patch literal 256456 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 Date: Mon, 17 Mar 2025 03:16:54 -0500 Subject: [PATCH 2/5] updated readme to include how to build with msys2 clang64 --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 01d2542..0769de1 100644 --- a/README.md +++ b/README.md @@ -247,11 +247,15 @@ Once the files are generated, refresh XenonTests' CMake cache to make them appea ## Building -The project requires CMake 3.20 or later and Clang 18 or later to build. Since the repository includes submodules, ensure you clone it recursively. -Compilers other than Clang have not been tested and are not recommended, including for recompilation output. The project relies on compiler-specific intrinsics and techniques that may not function correctly on other compilers, and many optimization methods depend on Clang's code generation. +### Windows (MSYS2) +-install [MSYS2](https://www.msys2.org/) and use the "MSYS2 CLANG64" environment to build the project. -On Windows, you can use the clang-cl toolset and open the project in Visual Studio's CMake integration. +-First, you need to install the necessary packages (`mingw-w64-clang-x86_64-cmake`, `mingw-w64-clang-x86_64-libc++`, `mingw-w64-clang-x86_64-clang` and `mingw-w64-x86_64-ninja`) with `pacman -S `. + +-Then, you can head into the cloned repo's directory (you can access your C drive by going into the `/c` folder inside of MSYS2), and execute the command `cmake -DCMAKE_BUILD_TYPE=Debug .`, which will generate a `build.ninja` file for the project. + +-Finally, run the `ninja` command, and you should end up with compiled executables. Attempting to launch them will tell you about a missing `libc++.dll` file, which you can copy to your current folder with the `cp /clang64/bin/libc++.dll .` command. ## Special Thanks From 77e96e882a5b8892f81014934529f2487bd7e2d7 Mon Sep 17 00:00:00 2001 From: MadLadMikael Date: Sun, 6 Apr 2025 15:53:09 -0500 Subject: [PATCH 3/5] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0769de1..839c544 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Additionally, mid-asm hooks can be inserted directly into the translated C++ cod XenonAnalyse, when used as a command-line application, allows an XEX file to be passed as an input argument to output a TOML file containing all the detected jump tables in the executable: ``` -XenonAnalyse [input XEX file path] [output jump table TOML file path] +XenonAnalyse [input XEX file path] [name of toml output file] ``` However, as explained in the earlier sections, due to variations between games, additional support may be needed to handle different patterns. @@ -98,6 +98,7 @@ However, as explained in the earlier sections, due to variations between games, XenonRecomp accepts a TOML file with recompiler configurations and the path to the `ppc_context.h` file located in the XenonUtils directory: + ``` XenonRecomp [input TOML file path] [input PPC context header file path] ``` @@ -108,7 +109,7 @@ XenonRecomp [input TOML file path] [input PPC context header file path] ```toml [main] -file_path = "../private/default.xex" +file_path = "./private/default.xex" patch_file_path = "../private/default.xexp" patched_file_path = "../private/default_patched.xex" out_directory_path = "../ppc" From 7b9f9ac8393893c83f79bd8247b952b13b2aa2c8 Mon Sep 17 00:00:00 2001 From: MadLadMikael Date: Sun, 6 Apr 2025 15:57:29 -0500 Subject: [PATCH 4/5] Delete XenonRecomp/recompiler.cpp --- XenonRecomp/recompiler.cpp | 2715 ------------------------------------ 1 file changed, 2715 deletions(-) delete mode 100644 XenonRecomp/recompiler.cpp diff --git a/XenonRecomp/recompiler.cpp b/XenonRecomp/recompiler.cpp deleted file mode 100644 index d28ca56..0000000 --- a/XenonRecomp/recompiler.cpp +++ /dev/null @@ -1,2715 +0,0 @@ -#include "pch.h" -#include "recompiler.h" -#include - -static uint64_t ComputeMask(uint32_t mstart, uint32_t mstop) -{ - mstart &= 0x3F; - mstop &= 0x3F; - uint64_t value = (UINT64_MAX >> mstart) ^ ((mstop >= 63) ? 0 : UINT64_MAX >> (mstop + 1)); - return mstart <= mstop ? value : ~value; -} - -bool Recompiler::LoadConfig(const std::string_view& configFilePath) -{ - config.Load(configFilePath); - - std::vector file; - if (!config.patchedFilePath.empty()) - file = LoadFile((config.directoryPath + config.patchedFilePath).c_str()); - - if (file.empty()) - { - file = LoadFile((config.directoryPath + config.filePath).c_str()); - - if (!config.patchFilePath.empty()) - { - const auto patchFile = LoadFile((config.directoryPath + config.patchFilePath).c_str()); - if (!patchFile.empty()) - { - std::vector outBytes; - auto result = XexPatcher::apply(file.data(), file.size(), patchFile.data(), patchFile.size(), outBytes, false); - if (result == XexPatcher::Result::Success) - { - std::exchange(file, outBytes); - - if (!config.patchedFilePath.empty()) - { - std::ofstream stream(config.directoryPath + config.patchedFilePath, std::ios::binary); - if (stream.good()) - { - stream.write(reinterpret_cast(file.data()), file.size()); - stream.close(); - } - } - } - else - { - fmt::print("ERROR: Unable to apply the patch file, "); - - switch (result) - { - case XexPatcher::Result::XexFileUnsupported: - fmt::println("XEX file unsupported"); - break; - - case XexPatcher::Result::XexFileInvalid: - fmt::println("XEX file invalid"); - break; - - case XexPatcher::Result::PatchFileInvalid: - fmt::println("patch file invalid"); - break; - - case XexPatcher::Result::PatchIncompatible: - fmt::println("patch file incompatible"); - break; - - case XexPatcher::Result::PatchFailed: - fmt::println("patch failed"); - break; - - case XexPatcher::Result::PatchUnsupported: - fmt::println("patch unsupported"); - break; - - default: - fmt::println("reason unknown"); - break; - } - - return false; - } - } - else - { - fmt::println("ERROR: Unable to load the patch file"); - return false; - } - } - } - - image = Image::ParseImage(file.data(), file.size()); - return true; -} - -void Recompiler::Analyse() -{ - for (size_t i = 14; i < 128; i++) - { - if (i < 32) - { - if (config.restGpr14Address != 0) - { - auto& restgpr = functions.emplace_back(); - restgpr.base = config.restGpr14Address + (i - 14) * 4; - restgpr.size = (32 - i) * 4 + 12; - image.symbols.emplace(Symbol{ fmt::format("__restgprlr_{}", i), restgpr.base, restgpr.size, Symbol_Function }); - } - - if (config.saveGpr14Address != 0) - { - auto& savegpr = functions.emplace_back(); - savegpr.base = config.saveGpr14Address + (i - 14) * 4; - savegpr.size = (32 - i) * 4 + 8; - image.symbols.emplace(fmt::format("__savegprlr_{}", i), savegpr.base, savegpr.size, Symbol_Function); - } - - if (config.restFpr14Address != 0) - { - auto& restfpr = functions.emplace_back(); - restfpr.base = config.restFpr14Address + (i - 14) * 4; - restfpr.size = (32 - i) * 4 + 4; - image.symbols.emplace(fmt::format("__restfpr_{}", i), restfpr.base, restfpr.size, Symbol_Function); - } - - if (config.saveFpr14Address != 0) - { - auto& savefpr = functions.emplace_back(); - savefpr.base = config.saveFpr14Address + (i - 14) * 4; - savefpr.size = (32 - i) * 4 + 4; - image.symbols.emplace(fmt::format("__savefpr_{}", i), savefpr.base, savefpr.size, Symbol_Function); - } - - if (config.restVmx14Address != 0) - { - auto& restvmx = functions.emplace_back(); - restvmx.base = config.restVmx14Address + (i - 14) * 8; - restvmx.size = (32 - i) * 8 + 4; - image.symbols.emplace(fmt::format("__restvmx_{}", i), restvmx.base, restvmx.size, Symbol_Function); - } - - if (config.saveVmx14Address != 0) - { - auto& savevmx = functions.emplace_back(); - savevmx.base = config.saveVmx14Address + (i - 14) * 8; - savevmx.size = (32 - i) * 8 + 4; - image.symbols.emplace(fmt::format("__savevmx_{}", i), savevmx.base, savevmx.size, Symbol_Function); - } - } - - if (i >= 64) - { - if (config.restVmx64Address != 0) - { - auto& restvmx = functions.emplace_back(); - restvmx.base = config.restVmx64Address + (i - 64) * 8; - restvmx.size = (128 - i) * 8 + 4; - image.symbols.emplace(fmt::format("__restvmx_{}", i), restvmx.base, restvmx.size, Symbol_Function); - } - - if (config.saveVmx64Address != 0) - { - auto& savevmx = functions.emplace_back(); - savevmx.base = config.saveVmx64Address + (i - 64) * 8; - savevmx.size = (128 - i) * 8 + 4; - image.symbols.emplace(fmt::format("__savevmx_{}", i), savevmx.base, savevmx.size, Symbol_Function); - } - } - } - - for (auto& [address, size] : config.functions) - { - functions.emplace_back(address, size); - image.symbols.emplace(fmt::format("sub_{:X}", address), address, size, Symbol_Function); - } - - auto& pdata = *image.Find(".pdata"); - size_t count = pdata.size / sizeof(IMAGE_CE_RUNTIME_FUNCTION); - auto* pf = (IMAGE_CE_RUNTIME_FUNCTION*)pdata.data; - for (size_t i = 0; i < count; i++) - { - auto fn = pf[i]; - fn.BeginAddress = ByteSwap(fn.BeginAddress); - fn.Data = ByteSwap(fn.Data); - - if (image.symbols.find(fn.BeginAddress) == image.symbols.end()) - { - auto& f = functions.emplace_back(); - f.base = fn.BeginAddress; - f.size = fn.FunctionLength * 4; - - image.symbols.emplace(fmt::format("sub_{:X}", f.base), f.base, f.size, Symbol_Function); - } - } - - for (const auto& section : image.sections) - { - if (!(section.flags & SectionFlags_Code)) - { - continue; - } - size_t base = section.base; - uint8_t* data = section.data; - uint8_t* dataEnd = section.data + section.size; - - while (data < dataEnd) - { - uint32_t insn = ByteSwap(*(uint32_t*)data); - if (PPC_OP(insn) == PPC_OP_B && PPC_BL(insn)) - { - size_t address = base + (data - section.data) + PPC_BI(insn); - - if (address >= section.base && address < section.base + section.size && image.symbols.find(address) == image.symbols.end()) - { - auto data = section.data + address - section.base; - auto& fn = functions.emplace_back(Function::Analyze(data, section.base + section.size - address, address)); - image.symbols.emplace(fmt::format("sub_{:X}", fn.base), fn.base, fn.size, Symbol_Function); - } - } - data += 4; - } - - data = section.data; - - while (data < dataEnd) - { - auto invalidInstr = config.invalidInstructions.find(ByteSwap(*(uint32_t*)data)); - if (invalidInstr != config.invalidInstructions.end()) - { - base += invalidInstr->second; - data += invalidInstr->second; - continue; - } - - auto fnSymbol = image.symbols.find(base); - if (fnSymbol != image.symbols.end() && fnSymbol->address == base && fnSymbol->type == Symbol_Function) - { - assert(fnSymbol->address == base); - - base += fnSymbol->size; - data += fnSymbol->size; - } - else - { - auto& fn = functions.emplace_back(Function::Analyze(data, dataEnd - data, base)); - image.symbols.emplace(fmt::format("sub_{:X}", fn.base), fn.base, fn.size, Symbol_Function); - - base += fn.size; - data += fn.size; - } - } - } - - std::sort(functions.begin(), functions.end(), [](auto& lhs, auto& rhs) { return lhs.base < rhs.base; }); -} - -bool Recompiler::Recompile( - const Function& fn, - uint32_t base, - const ppc_insn& insn, - const uint32_t* data, - std::unordered_map::iterator& switchTable, - RecompilerLocalVariables& localVariables, - CSRState& csrState) -{ - println("\t// {} {}", insn.opcode->name, insn.op_str); - - // TODO: we could cache these formats in an array - auto r = [&](size_t index) - { - if ((config.nonArgumentRegistersAsLocalVariables && (index == 0 || index == 2 || index == 11 || index == 12)) || - (config.nonVolatileRegistersAsLocalVariables && index >= 14)) - { - localVariables.r[index] = true; - return fmt::format("r{}", index); - } - return fmt::format("ctx.r{}", index); - }; - - auto f = [&](size_t index) - { - if ((config.nonArgumentRegistersAsLocalVariables && index == 0) || - (config.nonVolatileRegistersAsLocalVariables && index >= 14)) - { - localVariables.f[index] = true; - return fmt::format("f{}", index); - } - return fmt::format("ctx.f{}", index); - }; - - auto v = [&](size_t index) - { - if ((config.nonArgumentRegistersAsLocalVariables && (index >= 32 && index <= 63)) || - (config.nonVolatileRegistersAsLocalVariables && ((index >= 14 && index <= 31) || (index >= 64 && index <= 127)))) - { - localVariables.v[index] = true; - return fmt::format("v{}", index); - } - return fmt::format("ctx.v{}", index); - }; - - auto cr = [&](size_t index) - { - if (config.crRegistersAsLocalVariables) - { - localVariables.cr[index] = true; - return fmt::format("cr{}", index); - } - return fmt::format("ctx.cr{}", index); - }; - - auto ctr = [&]() - { - if (config.ctrAsLocalVariable) - { - localVariables.ctr = true; - return "ctr"; - } - return "ctx.ctr"; - }; - - auto xer = [&]() - { - if (config.xerAsLocalVariable) - { - localVariables.xer = true; - return "xer"; - } - return "ctx.xer"; - }; - - auto reserved = [&]() - { - if (config.reservedRegisterAsLocalVariable) - { - localVariables.reserved = true; - return "reserved"; - } - return "ctx.reserved"; - }; - - auto temp = [&]() - { - localVariables.temp = true; - return "temp"; - }; - - auto vTemp = [&]() - { - localVariables.vTemp = true; - return "vTemp"; - }; - - auto env = [&]() - { - localVariables.env = true; - return "env"; - }; - - auto ea = [&]() - { - localVariables.ea = true; - return "ea"; - }; - - // TODO (Sajid): Check for out of bounds access - auto mmioStore = [&]() -> bool - { - return *(data + 1) == c_eieio; - }; - - auto printFunctionCall = [&](uint32_t address) - { - if (address == config.longJmpAddress) - { - println("\tlongjmp(*reinterpret_cast(base + {}.u32), {}.s32);", r(3), r(4)); - } - else if (address == config.setJmpAddress) - { - println("\t{} = ctx;", env()); - println("\t{}.s64 = setjmp(*reinterpret_cast(base + {}.u32));", r(3), r(3)); - println("\tif ({}.s64 != 0) ctx = {};", r(3), env()); - } - else - { - auto targetSymbol = image.symbols.find(address); - - if (targetSymbol != image.symbols.end() && targetSymbol->address == address && targetSymbol->type == Symbol_Function) - { - if (config.nonVolatileRegistersAsLocalVariables && (targetSymbol->name.find("__rest") == 0 || targetSymbol->name.find("__save") == 0)) - { - // print nothing - } - else - { - println("\t{}(ctx, base);", targetSymbol->name); - } - } - else - { - println("\t// ERROR {:X}", address); - } - } - }; - - auto printConditionalBranch = [&](bool not_, const std::string_view& cond) - { - if (insn.operands[1] < fn.base || insn.operands[1] >= fn.base + fn.size) - { - println("\tif ({}{}.{}) {{", not_ ? "!" : "", cr(insn.operands[0]), cond); - print("\t"); - printFunctionCall(insn.operands[1]); - println("\t\treturn;"); - println("\t}}"); - } - else - { - println("\tif ({}{}.{}) goto loc_{:X};", not_ ? "!" : "", cr(insn.operands[0]), cond, insn.operands[1]); - } - }; - - auto printSetFlushMode = [&](bool enable) - { - auto newState = enable ? CSRState::VMX : CSRState::FPU; - if (csrState != newState) - { - auto prefix = enable ? "enable" : "disable"; - auto suffix = csrState != CSRState::Unknown ? "Unconditional" : ""; - println("\tctx.fpscr.{}FlushMode{}();", prefix, suffix); - - csrState = newState; - } - }; - - auto midAsmHook = config.midAsmHooks.find(base); - - auto printMidAsmHook = [&]() - { - bool returnsBool = midAsmHook->second.returnOnFalse || midAsmHook->second.returnOnTrue || - midAsmHook->second.jumpAddressOnFalse != NULL || midAsmHook->second.jumpAddressOnTrue != NULL; - - print("\t"); - if (returnsBool) - print("if ("); - - print("{}(", midAsmHook->second.name); - for (auto& reg : midAsmHook->second.registers) - { - if (out.back() != '(') - out += ", "; - - switch (reg[0]) - { - case 'c': - if (reg == "ctr") - out += ctr(); - else - out += cr(std::atoi(reg.c_str() + 2)); - break; - - case 'x': - out += xer(); - break; - - case 'r': - if (reg == "reserved") - out += reserved(); - else - out += r(std::atoi(reg.c_str() + 1)); - break; - - case 'f': - if (reg == "fpscr") - out += "ctx.fpscr"; - else - out += f(std::atoi(reg.c_str() + 1)); - break; - - case 'v': - out += v(std::atoi(reg.c_str() + 1)); - break; - } - } - - if (returnsBool) - { - println(")) {{"); - - if (midAsmHook->second.returnOnTrue) - println("\t\treturn;"); - else if (midAsmHook->second.jumpAddressOnTrue != NULL) - println("\t\tgoto loc_{:X};", midAsmHook->second.jumpAddressOnTrue); - - println("\t}}"); - - println("\telse {{"); - - if (midAsmHook->second.returnOnFalse) - println("\t\treturn;"); - else if (midAsmHook->second.jumpAddressOnFalse != NULL) - println("\t\tgoto loc_{:X};", midAsmHook->second.jumpAddressOnFalse); - - println("\t}}"); - } - else - { - println(");"); - - if (midAsmHook->second.ret) - println("\treturn;"); - else if (midAsmHook->second.jumpAddress != NULL) - println("\tgoto loc_{:X};", midAsmHook->second.jumpAddress); - } - }; - - if (midAsmHook != config.midAsmHooks.end() && !midAsmHook->second.afterInstruction) - printMidAsmHook(); - - int id = insn.opcode->id; - - // Handling instructions that don't disassemble correctly for some reason here - if (id == PPC_INST_VUPKHSB128 && insn.operands[2] == 0x60) id = PPC_INST_VUPKHSH128; - else if (id == PPC_INST_VUPKLSB128 && insn.operands[2] == 0x60) id = PPC_INST_VUPKLSH128; - - switch (id) - { - case PPC_INST_ADD: - println("\t{}.u64 = {}.u64 + {}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_ADDE: - println("\t{}.u8 = ({}.u32 + {}.u32 < {}.u32) | ({}.u32 + {}.u32 + {}.ca < {}.ca);", temp(), r(insn.operands[1]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[1]), r(insn.operands[2]), xer(), xer()); - println("\t{}.u64 = {}.u64 + {}.u64 + {}.ca;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); - println("\t{}.ca = {}.u8;", xer(), temp()); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_ADDI: - print("\t{}.s64 = ", r(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.s64 + ", r(insn.operands[1])); - println("{};", int32_t(insn.operands[2])); - break; - - case PPC_INST_ADDIC: - println("\t{}.ca = {}.u32 > {};", xer(), r(insn.operands[1]), ~insn.operands[2]); - println("\t{}.s64 = {}.s64 + {};", r(insn.operands[0]), r(insn.operands[1]), int32_t(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_ADDIS: - print("\t{}.s64 = ", r(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.s64 + ", r(insn.operands[1])); - println("{};", static_cast(insn.operands[2] << 16)); - break; - - case PPC_INST_ADDZE: - println("\t{}.s64 = {}.s64 + {}.ca;", temp(), r(insn.operands[1]), xer()); - println("\t{}.ca = {}.u32 < {}.u32;", xer(), temp(), r(insn.operands[1])); - println("\t{}.s64 = {}.s64;", r(insn.operands[0]), temp()); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_AND: - println("\t{}.u64 = {}.u64 & {}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_ANDC: - println("\t{}.u64 = {}.u64 & ~{}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_ANDI: - println("\t{}.u64 = {}.u64 & {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_ANDIS: - println("\t{}.u64 = {}.u64 & {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2] << 16); - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_ATTN: - // undefined instruction - break; - - case PPC_INST_B: - if (insn.operands[0] < fn.base || insn.operands[0] >= fn.base + fn.size) - { - printFunctionCall(insn.operands[0]); - println("\treturn;"); - } - else - { - println("\tgoto loc_{:X};", insn.operands[0]); - } - break; - - case PPC_INST_BCTR: - if (switchTable != config.switchTables.end()) - { - println("\tswitch ({}.u64) {{", r(switchTable->second.r)); - - for (size_t i = 0; i < switchTable->second.labels.size(); i++) - { - println("\tcase {}:", i); - auto label = switchTable->second.labels[i]; - if (label < fn.base || label >= fn.base + fn.size) - { - println("\t\t// ERROR: 0x{:X}", label); - fmt::println("ERROR: Switch case at {:X} is trying to jump outside function: {:X}", base, label); - println("\t\treturn;"); - } - else - { - println("\t\tgoto loc_{:X};", label); - } - } - - println("\tdefault:"); - println("\t\t__builtin_unreachable();"); - println("\t}}"); - - switchTable = config.switchTables.end(); - } - else - { - println("\tPPC_CALL_INDIRECT_FUNC({}.u32);", ctr()); - println("\treturn;"); - } - break; - - case PPC_INST_BCTRL: - if (!config.skipLr) - println("\tctx.lr = 0x{:X};", base + 4); - println("\tPPC_CALL_INDIRECT_FUNC({}.u32);", ctr()); - csrState = CSRState::Unknown; // the call could change it - break; - - case PPC_INST_BDZ: - println("\t--{}.u64;", ctr()); - println("\tif ({}.u32 == 0) goto loc_{:X};", ctr(), insn.operands[0]); - break; - - case PPC_INST_BDZLR: - println("\t--{}.u64;", ctr()); - println("\tif ({}.u32 == 0) return;", ctr(), insn.operands[0]); - break; - - case PPC_INST_BDNZ: - println("\t--{}.u64;", ctr()); - println("\tif ({}.u32 != 0) goto loc_{:X};", ctr(), insn.operands[0]); - break; - - case PPC_INST_BDNZF: - // NOTE: assuming eq here as a shortcut because all the instructions in the game do that - println("\t--{}.u64;", ctr()); - println("\tif ({}.u32 != 0 && !{}.eq) goto loc_{:X};", ctr(), cr(insn.operands[0] / 4), insn.operands[1]); - break; - - case PPC_INST_BEQ: - printConditionalBranch(false, "eq"); - break; - - case PPC_INST_BEQLR: - println("\tif ({}.eq) return;", cr(insn.operands[0])); - break; - - case PPC_INST_BGE: - printConditionalBranch(true, "lt"); - break; - - case PPC_INST_BGELR: - println("\tif (!{}.lt) return;", cr(insn.operands[0])); - break; - - case PPC_INST_BGT: - printConditionalBranch(false, "gt"); - break; - - case PPC_INST_BGTLR: - println("\tif ({}.gt) return;", cr(insn.operands[0])); - break; - - case PPC_INST_BL: - if (!config.skipLr) - println("\tctx.lr = 0x{:X};", base + 4); - printFunctionCall(insn.operands[0]); - csrState = CSRState::Unknown; // the call could change it - break; - - case PPC_INST_BLE: - printConditionalBranch(true, "gt"); - break; - - case PPC_INST_BLELR: - println("\tif (!{}.gt) return;", cr(insn.operands[0])); - break; - - case PPC_INST_BLR: - println("\treturn;"); - break; - - case PPC_INST_BLRL: - println("__builtin_debugtrap();"); - break; - - case PPC_INST_BLT: - printConditionalBranch(false, "lt"); - break; - - case PPC_INST_BLTLR: - println("\tif ({}.lt) return;", cr(insn.operands[0])); - break; - - case PPC_INST_BNE: - printConditionalBranch(true, "eq"); - break; - - case PPC_INST_BNECTR: - println("\tif (!{}.eq) {{", cr(insn.operands[0])); - println("\t\tPPC_CALL_INDIRECT_FUNC({}.u32);", ctr()); - println("\t\treturn;"); - println("\t}}"); - break; - - case PPC_INST_BNELR: - println("\tif (!{}.eq) return;", cr(insn.operands[0])); - break; - - case PPC_INST_CCTPL: - // no op - break; - - case PPC_INST_CCTPM: - // no op - break; - - case PPC_INST_CLRLDI: - println("\t{}.u64 = {}.u64 & 0x{:X};", r(insn.operands[0]), r(insn.operands[1]), (1ull << (64 - insn.operands[2])) - 1); - break; - - case PPC_INST_CLRLWI: - println("\t{}.u64 = {}.u32 & 0x{:X};", r(insn.operands[0]), r(insn.operands[1]), (1ull << (32 - insn.operands[2])) - 1); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_CMPD: - println("\t{}.compare({}.s64, {}.s64, {});", cr(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); - break; - - case PPC_INST_CMPDI: - println("\t{}.compare({}.s64, {}, {});", cr(insn.operands[0]), r(insn.operands[1]), int32_t(insn.operands[2]), xer()); - break; - - case PPC_INST_CMPLD: - println("\t{}.compare({}.u64, {}.u64, {});", cr(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); - break; - - case PPC_INST_CMPLDI: - println("\t{}.compare({}.u64, {}, {});", cr(insn.operands[0]), r(insn.operands[1]), insn.operands[2], xer()); - break; - - case PPC_INST_CMPLW: - println("\t{}.compare({}.u32, {}.u32, {});", cr(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); - break; - - case PPC_INST_CMPLWI: - println("\t{}.compare({}.u32, {}, {});", cr(insn.operands[0]), r(insn.operands[1]), insn.operands[2], xer()); - break; - - case PPC_INST_CMPW: - println("\t{}.compare({}.s32, {}.s32, {});", cr(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); - break; - - case PPC_INST_CMPWI: - println("\t{}.compare({}.s32, {}, {});", cr(insn.operands[0]), r(insn.operands[1]), int32_t(insn.operands[2]), xer()); - break; - - case PPC_INST_CNTLZD: - println("\t{0}.u64 = {1}.u64 == 0 ? 64 : __builtin_clzll({1}.u64);", r(insn.operands[0]), r(insn.operands[1])); - break; - - case PPC_INST_CNTLZW: - println("\t{0}.u64 = {1}.u32 == 0 ? 32 : __builtin_clz({1}.u32);", r(insn.operands[0]), r(insn.operands[1])); - break; - - case PPC_INST_DB16CYC: - // no op - break; - - case PPC_INST_DCBF: - // no op - break; - - case PPC_INST_DCBT: - // no op - break; - - case PPC_INST_DCBTST: - // no op - break; - - case PPC_INST_DCBZ: - print("\tmemset(base + (("); - if (insn.operands[0] != 0) - print("{}.u32 + ", r(insn.operands[0])); - println("{}.u32) & ~31), 0, 32);", r(insn.operands[1])); - break; - - case PPC_INST_DCBZL: - print("\tmemset(base + (("); - if (insn.operands[0] != 0) - print("{}.u32 + ", r(insn.operands[0])); - println("{}.u32) & ~127), 0, 128);", r(insn.operands[1])); - break; - - case PPC_INST_DIVD: - println("\t{}.s64 = {}.s64 / {}.s64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - break; - - case PPC_INST_DIVDU: - println("\t{}.u64 = {}.u64 / {}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_DIVW: - println("\t{}.s32 = {}.s32 / {}.s32;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_DIVWU: - println("\t{}.u32 = {}.u32 / {}.u32;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_EIEIO: - // no op - break; - - case PPC_INST_EXTSB: - println("\t{}.s64 = {}.s8;", r(insn.operands[0]), r(insn.operands[1])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_EXTSH: - println("\t{}.s64 = {}.s16;", r(insn.operands[0]), r(insn.operands[1])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_EXTSW: - println("\t{}.s64 = {}.s32;", r(insn.operands[0]), r(insn.operands[1])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_FABS: - printSetFlushMode(false); - println("\t{}.u64 = {}.u64 & ~0x8000000000000000;", f(insn.operands[0]), f(insn.operands[1])); - break; - - case PPC_INST_FADD: - printSetFlushMode(false); - println("\t{}.f64 = {}.f64 + {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); - break; - - case PPC_INST_FADDS: - printSetFlushMode(false); - println("\t{}.f64 = double(float({}.f64 + {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); - break; - - case PPC_INST_FCFID: - printSetFlushMode(false); - println("\t{}.f64 = double({}.s64);", f(insn.operands[0]), f(insn.operands[1])); - break; - - case PPC_INST_FCMPU: - printSetFlushMode(false); - println("\t{}.compare({}.f64, {}.f64);", cr(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); - break; - - case PPC_INST_FCTID: - printSetFlushMode(false); - println("\t{}.s64 = ({}.f64 > double(LLONG_MAX)) ? LLONG_MAX : _mm_cvtsd_si64(_mm_load_sd(&{}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[1])); - break; - - case PPC_INST_FCTIDZ: - printSetFlushMode(false); - println("\t{}.s64 = ({}.f64 > double(LLONG_MAX)) ? LLONG_MAX : _mm_cvttsd_si64(_mm_load_sd(&{}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[1])); - break; - - case PPC_INST_FCTIWZ: - printSetFlushMode(false); - println("\t{}.s64 = ({}.f64 > double(INT_MAX)) ? INT_MAX : _mm_cvttsd_si32(_mm_load_sd(&{}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[1])); - break; - - case PPC_INST_FDIV: - printSetFlushMode(false); - println("\t{}.f64 = {}.f64 / {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); - break; - - case PPC_INST_FDIVS: - printSetFlushMode(false); - println("\t{}.f64 = double(float({}.f64 / {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); - break; - - case PPC_INST_FMADD: - printSetFlushMode(false); - println("\t{}.f64 = {}.f64 * {}.f64 + {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); - break; - - case PPC_INST_FMADDS: - printSetFlushMode(false); - println("\t{}.f64 = double(float({}.f64 * {}.f64 + {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); - break; - - case PPC_INST_FMR: - printSetFlushMode(false); - println("\t{}.f64 = {}.f64;", f(insn.operands[0]), f(insn.operands[1])); - break; - - case PPC_INST_FMSUB: - printSetFlushMode(false); - println("\t{}.f64 = {}.f64 * {}.f64 - {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); - break; - - case PPC_INST_FMSUBS: - printSetFlushMode(false); - println("\t{}.f64 = double(float({}.f64 * {}.f64 - {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); - break; - - case PPC_INST_FMUL: - printSetFlushMode(false); - println("\t{}.f64 = {}.f64 * {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); - break; - - case PPC_INST_FMULS: - printSetFlushMode(false); - println("\t{}.f64 = double(float({}.f64 * {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); - break; - - case PPC_INST_FNABS: - printSetFlushMode(false); - println("\t{}.u64 = {}.u64 | 0x8000000000000000;", f(insn.operands[0]), f(insn.operands[1])); - break; - - case PPC_INST_FNEG: - printSetFlushMode(false); - println("\t{}.u64 = {}.u64 ^ 0x8000000000000000;", f(insn.operands[0]), f(insn.operands[1])); - break; - - case PPC_INST_FNMADDS: - printSetFlushMode(false); - println("\t{}.f64 = double(float(-({}.f64 * {}.f64 + {}.f64)));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); - break; - - case PPC_INST_FNMSUB: - printSetFlushMode(false); - println("\t{}.f64 = -({}.f64 * {}.f64 - {}.f64);", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); - break; - - case PPC_INST_FNMSUBS: - printSetFlushMode(false); - println("\t{}.f64 = double(float(-({}.f64 * {}.f64 - {}.f64)));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); - break; - - case PPC_INST_FRES: - printSetFlushMode(false); - println("\t{}.f64 = float(1.0 / {}.f64);", f(insn.operands[0]), f(insn.operands[1])); - break; - - case PPC_INST_FRSP: - printSetFlushMode(false); - println("\t{}.f64 = double(float({}.f64));", f(insn.operands[0]), f(insn.operands[1])); - break; - - case PPC_INST_FSEL: - printSetFlushMode(false); - println("\t{}.f64 = {}.f64 >= 0.0 ? {}.f64 : {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); - break; - - case PPC_INST_FSQRT: - printSetFlushMode(false); - println("\t{}.f64 = sqrt({}.f64);", f(insn.operands[0]), f(insn.operands[1])); - break; - - case PPC_INST_FSQRTS: - printSetFlushMode(false); - println("\t{}.f64 = double(float(sqrt({}.f64)));", f(insn.operands[0]), f(insn.operands[1])); - break; - - case PPC_INST_FSUB: - printSetFlushMode(false); - println("\t{}.f64 = {}.f64 - {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); - break; - - case PPC_INST_FSUBS: - printSetFlushMode(false); - println("\t{}.f64 = double(float({}.f64 - {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); - break; - - case PPC_INST_LBZ: - print("\t{}.u64 = PPC_LOAD_U8(", r(insn.operands[0])); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{});", int32_t(insn.operands[1])); - break; - - case PPC_INST_LBZU: - println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); - println("\t{}.u64 = PPC_LOAD_U8({});", r(insn.operands[0]), ea()); - println("\t{}.u32 = {};", r(insn.operands[2]), ea()); - break; - - case PPC_INST_LBZX: - print("\t{}.u64 = PPC_LOAD_U8(", r(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32);", r(insn.operands[2])); - break; - - case PPC_INST_LD: - print("\t{}.u64 = PPC_LOAD_U64(", r(insn.operands[0])); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{});", int32_t(insn.operands[1])); - break; - - case PPC_INST_LDARX: - print("\t{}.u64 = *(uint64_t*)(base + ", reserved()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32);", r(insn.operands[2])); - println("\t{}.u64 = __builtin_bswap64({}.u64);", r(insn.operands[0]), reserved()); - break; - - case PPC_INST_LDU: - println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); - println("\t{}.u64 = PPC_LOAD_U64({});", r(insn.operands[0]), ea()); - println("\t{}.u32 = {};", r(insn.operands[2]), ea()); - break; - - case PPC_INST_LDX: - print("\t{}.u64 = PPC_LOAD_U64(", r(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32);", r(insn.operands[2])); - break; - - case PPC_INST_LFD: - printSetFlushMode(false); - print("\t{}.u64 = PPC_LOAD_U64(", f(insn.operands[0])); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{});", int32_t(insn.operands[1])); - break; - - case PPC_INST_LFDX: - printSetFlushMode(false); - print("\t{}.u64 = PPC_LOAD_U64(", f(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32);", r(insn.operands[2])); - break; - - case PPC_INST_LFS: - printSetFlushMode(false); - print("\t{}.u32 = PPC_LOAD_U32(", temp()); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{});", int32_t(insn.operands[1])); - println("\t{}.f64 = double({}.f32);", f(insn.operands[0]), temp()); - break; - - case PPC_INST_LFSX: - printSetFlushMode(false); - print("\t{}.u32 = PPC_LOAD_U32(", temp()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32);", r(insn.operands[2])); - println("\t{}.f64 = double({}.f32);", f(insn.operands[0]), temp()); - break; - - case PPC_INST_LHA: - print("\t{}.s64 = int16_t(PPC_LOAD_U16(", r(insn.operands[0])); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{}));", int32_t(insn.operands[1])); - break; - - case PPC_INST_LHAX: - print("\t{}.s64 = int16_t(PPC_LOAD_U16(", r(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32));", r(insn.operands[2])); - break; - - case PPC_INST_LHZ: - print("\t{}.u64 = PPC_LOAD_U16(", r(insn.operands[0])); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{});", int32_t(insn.operands[1])); - break; - - case PPC_INST_LHZX: - print("\t{}.u64 = PPC_LOAD_U16(", r(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32);", r(insn.operands[2])); - break; - - case PPC_INST_LI: - println("\t{}.s64 = {};", r(insn.operands[0]), int32_t(insn.operands[1])); - break; - - case PPC_INST_LIS: - println("\t{}.s64 = {};", r(insn.operands[0]), int32_t(insn.operands[1] << 16)); - break; - - case PPC_INST_LVEWX: - case PPC_INST_LVEWX128: - case PPC_INST_LVX: - case PPC_INST_LVX128: - // NOTE: for endian swapping, we reverse the whole vector instead of individual elements. - // this is accounted for in every instruction (eg. dp3 sums yzw instead of xyz) - print("\t_mm_store_si128((__m128i*){}.u8, _mm_shuffle_epi8(_mm_load_si128((__m128i*)(base + ((", v(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32) & ~0xF))), _mm_load_si128((__m128i*)VectorMaskL)));", r(insn.operands[2])); - break; - - case PPC_INST_LVLX: - case PPC_INST_LVLX128: - print("\t{}.u32 = ", temp()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32;", r(insn.operands[2])); - println("\t_mm_store_si128((__m128i*){}.u8, _mm_shuffle_epi8(_mm_load_si128((__m128i*)(base + ({}.u32 & ~0xF))), _mm_load_si128((__m128i*)&VectorMaskL[({}.u32 & 0xF) * 16])));", v(insn.operands[0]), temp(), temp()); - break; - - case PPC_INST_LVRX: - case PPC_INST_LVRX128: - print("\t{}.u32 = ", temp()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32;", r(insn.operands[2])); - println("\t_mm_store_si128((__m128i*){}.u8, {}.u32 & 0xF ? _mm_shuffle_epi8(_mm_load_si128((__m128i*)(base + ({}.u32 & ~0xF))), _mm_load_si128((__m128i*)&VectorMaskR[({}.u32 & 0xF) * 16])) : _mm_setzero_si128());", v(insn.operands[0]), temp(), temp(), temp()); - break; - - case PPC_INST_LVSL: - print("\t{}.u32 = ", temp()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32;", r(insn.operands[2])); - println("\t_mm_store_si128((__m128i*){}.u8, _mm_load_si128((__m128i*)&VectorShiftTableL[({}.u32 & 0xF) * 16]));", v(insn.operands[0]), temp()); - break; - - case PPC_INST_LVSR: - print("\t{}.u32 = ", temp()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32;", r(insn.operands[2])); - println("\t_mm_store_si128((__m128i*){}.u8, _mm_load_si128((__m128i*)&VectorShiftTableR[({}.u32 & 0xF) * 16]));", v(insn.operands[0]), temp()); - break; - - case PPC_INST_LWA: - print("\t{}.s64 = int32_t(PPC_LOAD_U32(", r(insn.operands[0])); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{}));", int32_t(insn.operands[1])); - break; - - case PPC_INST_LWARX: - print("\t{}.u32 = *(uint32_t*)(base + ", reserved()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32);", r(insn.operands[2])); - println("\t{}.u64 = __builtin_bswap32({}.u32);", r(insn.operands[0]), reserved()); - break; - - case PPC_INST_LWAX: - print("\t{}.s64 = int32_t(PPC_LOAD_U32(", r(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32));", r(insn.operands[2])); - break; - - case PPC_INST_LWBRX: - print("\t{}.u64 = __builtin_bswap32(PPC_LOAD_U32(", r(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32));", r(insn.operands[2])); - break; - - case PPC_INST_LWSYNC: - // no op - break; - - case PPC_INST_LWZ: - print("\t{}.u64 = PPC_LOAD_U32(", r(insn.operands[0])); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{});", int32_t(insn.operands[1])); - break; - - case PPC_INST_LWZU: - println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); - println("\t{}.u64 = PPC_LOAD_U32({});", r(insn.operands[0]), ea()); - println("\t{}.u32 = {};", r(insn.operands[2]), ea()); - break; - - case PPC_INST_LWZX: - print("\t{}.u64 = PPC_LOAD_U32(", r(insn.operands[0])); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32);", r(insn.operands[2])); - break; - - case PPC_INST_MFCR: - for (size_t i = 0; i < 32; i++) - { - constexpr std::string_view fields[] = { "lt", "gt", "eq", "so" }; - println("\t{}.u64 {}= {}.{} ? 0x{:X} : 0;", r(insn.operands[0]), i == 0 ? "" : "|", cr(i / 4), fields[i % 4], 1u << (31 - i)); - } - break; - - case PPC_INST_MFFS: - println("\t{}.u64 = ctx.fpscr.loadFromHost();", r(insn.operands[0])); - break; - - case PPC_INST_MFLR: - if (!config.skipLr) - println("\t{}.u64 = ctx.lr;", r(insn.operands[0])); - break; - - case PPC_INST_MFMSR: - if (!config.skipMsr) - println("\t{}.u64 = ctx.msr;", r(insn.operands[0])); - break; - - case PPC_INST_MFOCRF: - // TODO: don't hardcode to cr6 - println("\t{}.u64 = ({}.lt << 7) | ({}.gt << 6) | ({}.eq << 5) | ({}.so << 4);", r(insn.operands[0]), cr(6), cr(6), cr(6), cr(6)); - break; - - case PPC_INST_MFTB: - println("\t{}.u64 = __rdtsc();", r(insn.operands[0])); - break; - - case PPC_INST_MR: - println("\t{}.u64 = {}.u64;", r(insn.operands[0]), r(insn.operands[1])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_MTCR: - for (size_t i = 0; i < 32; i++) - { - constexpr std::string_view fields[] = { "lt", "gt", "eq", "so" }; - println("\t{}.{} = ({}.u32 & 0x{:X}) != 0;", cr(i / 4), fields[i % 4], r(insn.operands[0]), 1u << (31 - i)); - } - break; - - case PPC_INST_MTCTR: - println("\t{}.u64 = {}.u64;", ctr(), r(insn.operands[0])); - break; - - case PPC_INST_MTFSF: - println("\tctx.fpscr.storeFromGuest({}.u32);", f(insn.operands[1])); - break; - - case PPC_INST_MTLR: - if (!config.skipLr) - println("\tctx.lr = {}.u64;", r(insn.operands[0])); - break; - - case PPC_INST_MTMSRD: - if (!config.skipMsr) - println("\tctx.msr = ({}.u32 & 0x8020) | (ctx.msr & ~0x8020);", r(insn.operands[0])); - break; - - case PPC_INST_MTXER: - println("\t{}.so = ({}.u64 & 0x80000000) != 0;", xer(), r(insn.operands[0])); - println("\t{}.ov = ({}.u64 & 0x40000000) != 0;", xer(), r(insn.operands[0])); - println("\t{}.ca = ({}.u64 & 0x20000000) != 0;", xer(), r(insn.operands[0])); - break; - - case PPC_INST_MULHW: - println("\t{}.s64 = (int64_t({}.s32) * int64_t({}.s32)) >> 32;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - break; - - case PPC_INST_MULHWU: - println("\t{}.u64 = (uint64_t({}.u32) * uint64_t({}.u32)) >> 32;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_MULLD: - println("\t{}.s64 = {}.s64 * {}.s64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - break; - - case PPC_INST_MULLI: - println("\t{}.s64 = {}.s64 * {};", r(insn.operands[0]), r(insn.operands[1]), int32_t(insn.operands[2])); - break; - - case PPC_INST_MULLW: - println("\t{}.s64 = int64_t({}.s32) * int64_t({}.s32);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_NAND: - println("\t{}.u64 = ~({}.u64 & {}.u64);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - break; - - case PPC_INST_NEG: - println("\t{}.s64 = -{}.s64;", r(insn.operands[0]), r(insn.operands[1])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_NOP: - // no op - break; - - case PPC_INST_NOR: - println("\t{}.u64 = ~({}.u64 | {}.u64);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - break; - - case PPC_INST_NOT: - println("\t{}.u64 = ~{}.u64;", r(insn.operands[0]), r(insn.operands[1])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_OR: - println("\t{}.u64 = {}.u64 | {}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_ORC: - println("\t{}.u64 = {}.u64 | ~{}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - break; - - case PPC_INST_ORI: - println("\t{}.u64 = {}.u64 | {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); - break; - - case PPC_INST_ORIS: - println("\t{}.u64 = {}.u64 | {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2] << 16); - break; - - case PPC_INST_RLDICL: - println("\t{}.u64 = __builtin_rotateleft64({}.u64, {}) & 0x{:X};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2], ComputeMask(insn.operands[3], 63)); - break; - - case PPC_INST_RLDICR: - println("\t{}.u64 = __builtin_rotateleft64({}.u64, {}) & 0x{:X};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2], ComputeMask(0, insn.operands[3])); - break; - - case PPC_INST_RLDIMI: - { - const uint64_t mask = ComputeMask(insn.operands[3], ~insn.operands[2]); - println("\t{}.u64 = (__builtin_rotateleft64({}.u64, {}) & 0x{:X}) | ({}.u64 & 0x{:X});", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2], mask, r(insn.operands[0]), ~mask); - break; - } - - case PPC_INST_RLWIMI: - { - const uint64_t mask = ComputeMask(insn.operands[3] + 32, insn.operands[4] + 32); - println("\t{}.u64 = (__builtin_rotateleft32({}.u32, {}) & 0x{:X}) | ({}.u64 & 0x{:X});", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2], mask, r(insn.operands[0]), ~mask); - break; - } - - case PPC_INST_RLWINM: - println("\t{}.u64 = __builtin_rotateleft64({}.u32 | ({}.u64 << 32), {}) & 0x{:X};", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[1]), insn.operands[2], ComputeMask(insn.operands[3] + 32, insn.operands[4] + 32)); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_ROTLDI: - println("\t{}.u64 = __builtin_rotateleft64({}.u64, {});", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); - break; - - case PPC_INST_ROTLW: - println("\t{}.u64 = __builtin_rotateleft32({}.u32, {}.u8 & 0x1F);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - break; - - case PPC_INST_ROTLWI: - println("\t{}.u64 = __builtin_rotateleft32({}.u32, {});", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_SLD: - println("\t{}.u64 = {}.u8 & 0x40 ? 0 : ({}.u64 << ({}.u8 & 0x7F));", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[2])); - break; - - case PPC_INST_SLW: - println("\t{}.u64 = {}.u8 & 0x20 ? 0 : ({}.u32 << ({}.u8 & 0x3F));", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_SRAD: - println("\t{}.u64 = {}.u64 & 0x7F;", temp(), r(insn.operands[2])); - println("\tif ({}.u64 > 0x3F) {}.u64 = 0x3F;", temp(), temp()); - println("\t{}.ca = ({}.s64 < 0) & ((({}.s64 >> {}.u64) << {}.u64) != {}.s64);", xer(), r(insn.operands[1]), r(insn.operands[1]), temp(), temp(), r(insn.operands[1])); - println("\t{}.s64 = {}.s64 >> {}.u64;", r(insn.operands[0]), r(insn.operands[1]), temp()); - break; - - case PPC_INST_SRADI: - if (insn.operands[2] != 0) - { - println("\t{}.ca = ({}.s64 < 0) & (({}.u64 & 0x{:X}) != 0);", xer(), r(insn.operands[1]), r(insn.operands[1]), ComputeMask(64 - insn.operands[2], 63)); - println("\t{}.s64 = {}.s64 >> {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); - } - else - { - println("\t{}.ca = 0;", xer()); - println("\t{}.s64 = {}.s64;", r(insn.operands[0]), r(insn.operands[1])); - } - break; - - case PPC_INST_SRAW: - println("\t{}.u32 = {}.u32 & 0x3F;", temp(), r(insn.operands[2])); - println("\tif ({}.u32 > 0x1F) {}.u32 = 0x1F;", temp(), temp()); - println("\t{}.ca = ({}.s32 < 0) & ((({}.s32 >> {}.u32) << {}.u32) != {}.s32);", xer(), r(insn.operands[1]), r(insn.operands[1]), temp(), temp(), r(insn.operands[1])); - println("\t{}.s64 = {}.s32 >> {}.u32;", r(insn.operands[0]), r(insn.operands[1]), temp()); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_SRAWI: - if (insn.operands[2] != 0) - { - println("\t{}.ca = ({}.s32 < 0) & (({}.u32 & 0x{:X}) != 0);", xer(), r(insn.operands[1]), r(insn.operands[1]), ComputeMask(64 - insn.operands[2], 63)); - println("\t{}.s64 = {}.s32 >> {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); - } - else - { - println("\t{}.ca = 0;", xer()); - println("\t{}.s64 = {}.s32;", r(insn.operands[0]), r(insn.operands[1])); - } - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_SRD: - println("\t{}.u64 = {}.u8 & 0x40 ? 0 : ({}.u64 >> ({}.u8 & 0x7F));", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[2])); - break; - - case PPC_INST_SRW: - println("\t{}.u64 = {}.u8 & 0x20 ? 0 : ({}.u32 >> ({}.u8 & 0x3F));", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_STB: - print("{}", mmioStore() ? "\tPPC_MM_STORE_U8(" : "\tPPC_STORE_U8("); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{}, {}.u8);", int32_t(insn.operands[1]), r(insn.operands[0])); - break; - - case PPC_INST_STBU: - println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); - println("\tPPC_STORE_U8({}, {}.u8);", ea(), r(insn.operands[0])); - println("\t{}.u32 = {};", r(insn.operands[2]), ea()); - break; - - case PPC_INST_STBX: - print("{}", mmioStore() ? "\tPPC_MM_STORE_U8(" : "\tPPC_STORE_U8("); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32, {}.u8);", r(insn.operands[2]), r(insn.operands[0])); - break; - - case PPC_INST_STD: - print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64("); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{}, {}.u64);", int32_t(insn.operands[1]), r(insn.operands[0])); - break; - - case PPC_INST_STDCX: - println("\t{}.lt = 0;", cr(0)); - println("\t{}.gt = 0;", cr(0)); - print("\t{}.eq = __sync_bool_compare_and_swap(reinterpret_cast(base + ", cr(0)); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32), {}.s64, __builtin_bswap64({}.s64));", r(insn.operands[2]), reserved(), r(insn.operands[0])); - println("\t{}.so = {}.so;", cr(0), xer()); - break; - - case PPC_INST_STDU: - println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); - println("\tPPC_STORE_U64({}, {}.u64);", ea(), r(insn.operands[0])); - println("\t{}.u32 = {};", r(insn.operands[2]), ea()); - break; - - case PPC_INST_STDX: - print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64("); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32, {}.u64);", r(insn.operands[2]), r(insn.operands[0])); - break; - - case PPC_INST_STFD: - printSetFlushMode(false); - print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64("); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{}, {}.u64);", int32_t(insn.operands[1]), f(insn.operands[0])); - break; - - case PPC_INST_STFDX: - printSetFlushMode(false); - print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64("); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32, {}.u64);", r(insn.operands[2]), f(insn.operands[0])); - break; - - case PPC_INST_STFIWX: - printSetFlushMode(false); - print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32, {}.u32);", r(insn.operands[2]), f(insn.operands[0])); - break; - - case PPC_INST_STFS: - printSetFlushMode(false); - println("\t{}.f32 = float({}.f64);", temp(), f(insn.operands[0])); - print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{}, {}.u32);", int32_t(insn.operands[1]), temp()); - break; - - case PPC_INST_STFSX: - printSetFlushMode(false); - println("\t{}.f32 = float({}.f64);", temp(), f(insn.operands[0])); - print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32, {}.u32);", r(insn.operands[2]), temp()); - break; - - case PPC_INST_STH: - print("{}", mmioStore() ? "\tPPC_MM_STORE_U16(" : "\tPPC_STORE_U16("); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{}, {}.u16);", int32_t(insn.operands[1]), r(insn.operands[0])); - break; - - case PPC_INST_STHBRX: - print("{}", mmioStore() ? "\tPPC_MM_STORE_U16(" : "\tPPC_STORE_U16("); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32, __builtin_bswap16({}.u16));", r(insn.operands[2]), r(insn.operands[0])); - break; - - case PPC_INST_STHX: - print("{}", mmioStore() ? "\tPPC_MM_STORE_U16(" : "\tPPC_STORE_U16("); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32, {}.u16);", r(insn.operands[2]), r(insn.operands[0])); - break; - - case PPC_INST_STVEHX: - // TODO: vectorize - // NOTE: accounting for the full vector reversal here - print("\t{} = (", ea()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32) & ~0x1;", r(insn.operands[2])); - println("\tPPC_STORE_U16(ea, {}.u16[7 - (({} & 0xF) >> 1)]);", v(insn.operands[0]), ea()); - break; - - case PPC_INST_STVEWX: - case PPC_INST_STVEWX128: - // TODO: vectorize - // NOTE: accounting for the full vector reversal here - print("\t{} = (", ea()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32) & ~0x3;", r(insn.operands[2])); - println("\tPPC_STORE_U32(ea, {}.u32[3 - (({} & 0xF) >> 2)]);", v(insn.operands[0]), ea()); - break; - - case PPC_INST_STVLX: - case PPC_INST_STVLX128: - // TODO: vectorize - // NOTE: accounting for the full vector reversal here - print("\t{} = ", ea()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32;", r(insn.operands[2])); - - println("\tfor (size_t i = 0; i < (16 - ({} & 0xF)); i++)", ea()); - println("\t\tPPC_STORE_U8({} + i, {}.u8[15 - i]);", ea(), v(insn.operands[0])); - break; - - case PPC_INST_STVRX: - case PPC_INST_STVRX128: - // TODO: vectorize - // NOTE: accounting for the full vector reversal here - print("\t{} = ", ea()); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32;", r(insn.operands[2])); - - println("\tfor (size_t i = 0; i < ({} & 0xF); i++)", ea()); - println("\t\tPPC_STORE_U8({} - i - 1, {}.u8[i]);", ea(), v(insn.operands[0])); - break; - - case PPC_INST_STVX: - case PPC_INST_STVX128: - print("\t_mm_store_si128((__m128i*)(base + (("); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32) & ~0xF)), _mm_shuffle_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*)VectorMaskL)));", r(insn.operands[2]), v(insn.operands[0])); - break; - - case PPC_INST_STW: - print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); - if (insn.operands[2] != 0) - print("{}.u32 + ", r(insn.operands[2])); - println("{}, {}.u32);", int32_t(insn.operands[1]), r(insn.operands[0])); - break; - - case PPC_INST_STWBRX: - print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32, __builtin_bswap32({}.u32));", r(insn.operands[2]), r(insn.operands[0])); - break; - - case PPC_INST_STWCX: - println("\t{}.lt = 0;", cr(0)); - println("\t{}.gt = 0;", cr(0)); - print("\t{}.eq = __sync_bool_compare_and_swap(reinterpret_cast(base + ", cr(0)); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32), {}.s32, __builtin_bswap32({}.s32));", r(insn.operands[2]), reserved(), r(insn.operands[0])); - println("\t{}.so = {}.so;", cr(0), xer()); - break; - - case PPC_INST_STWU: - println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); - println("\tPPC_STORE_U32({}, {}.u32);", ea(), r(insn.operands[0])); - println("\t{}.u32 = {};", r(insn.operands[2]), ea()); - break; - - case PPC_INST_STWUX: - println("\t{} = {}.u32 + {}.u32;", ea(), r(insn.operands[1]), r(insn.operands[2])); - println("\tPPC_STORE_U32({}, {}.u32);", ea(), r(insn.operands[0])); - println("\t{}.u32 = {};", r(insn.operands[1]), ea()); - break; - - case PPC_INST_STWX: - print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); - if (insn.operands[1] != 0) - print("{}.u32 + ", r(insn.operands[1])); - println("{}.u32, {}.u32);", r(insn.operands[2]), r(insn.operands[0])); - break; - - case PPC_INST_SUBF: - println("\t{}.s64 = {}.s64 - {}.s64;", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_SUBFC: - println("\t{}.ca = {}.u32 >= {}.u32;", xer(), r(insn.operands[2]), r(insn.operands[1])); - println("\t{}.s64 = {}.s64 - {}.s64;", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_SUBFE: - println("\t{}.u8 = (~{}.u32 + {}.u32 < ~{}.u32) | (~{}.u32 + {}.u32 + {}.ca < {}.ca);", temp(), r(insn.operands[1]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[1]), r(insn.operands[2]), xer(), xer()); - println("\t{}.u64 = ~{}.u64 + {}.u64 + {}.ca;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); - println("\t{}.ca = {}.u8;", xer(), temp()); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_SUBFIC: - println("\t{}.ca = {}.u32 <= {};", xer(), r(insn.operands[1]), insn.operands[2]); - println("\t{}.s64 = {} - {}.s64;", r(insn.operands[0]), int32_t(insn.operands[2]), r(insn.operands[1])); - break; - - case PPC_INST_SYNC: - // no op - break; - - case PPC_INST_TDLGEI: - // no op - break; - - case PPC_INST_TDLLEI: - // no op - break; - - case PPC_INST_TWI: - // no op - break; - - case PPC_INST_TWLGEI: - // no op - break; - - case PPC_INST_TWLLEI: - // no op - break; - - case PPC_INST_VADDFP: - case PPC_INST_VADDFP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_add_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VADDSHS: - println("\t_mm_store_si128((__m128i*){}.s16, _mm_adds_epi16(_mm_load_si128((__m128i*){}.s16), _mm_load_si128((__m128i*){}.s16)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VADDUBM: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_add_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VADDUBS: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_adds_epu8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VADDUHM: - println("\t_mm_store_si128((__m128i*){}.u16, _mm_add_epi16(_mm_load_si128((__m128i*){}.u16), _mm_load_si128((__m128i*){}.u16)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VADDUWM: - println("\t_mm_store_si128((__m128i*){}.u32, _mm_add_epi32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VADDUWS: - println("\t_mm_store_si128((__m128i*){}.u32, _mm_adds_epu32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VAND: - case PPC_INST_VAND128: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_and_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VANDC128: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_andnot_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); - break; - - case PPC_INST_VAVGSB: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_avg_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VAVGSH: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_avg_epi16(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VAVGUB: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_avg_epu8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VCTSXS: - case PPC_INST_VCFPSXWS128: - printSetFlushMode(true); - print("\t_mm_store_si128((__m128i*){}.s32, _mm_vctsxs(", v(insn.operands[0])); - if (insn.operands[2] != 0) - println("_mm_mul_ps(_mm_load_ps({}.f32), _mm_set1_ps({}))));", v(insn.operands[1]), 1u << insn.operands[2]); - else - println("_mm_load_ps({}.f32)));", v(insn.operands[1])); - break; - - case PPC_INST_VCFSX: - case PPC_INST_VCSXWFP128: - { - printSetFlushMode(true); - print("\t_mm_store_ps({}.f32, ", v(insn.operands[0])); - if (insn.operands[2] != 0) - { - const float value = ldexp(1.0f, -int32_t(insn.operands[2])); - println("_mm_mul_ps(_mm_cvtepi32_ps(_mm_load_si128((__m128i*){}.u32)), _mm_castsi128_ps(_mm_set1_epi32(int(0x{:X})))));", v(insn.operands[1]), *reinterpret_cast(&value)); - } - else - { - println("_mm_cvtepi32_ps(_mm_load_si128((__m128i*){}.u32)));", v(insn.operands[1])); - } - break; - } - - case PPC_INST_VCFUX: - case PPC_INST_VCUXWFP128: - { - printSetFlushMode(true); - print("\t_mm_store_ps({}.f32, ", v(insn.operands[0])); - if (insn.operands[2] != 0) - { - const float value = ldexp(1.0f, -int32_t(insn.operands[2])); - println("_mm_mul_ps(_mm_cvtepu32_ps_(_mm_load_si128((__m128i*){}.u32)), _mm_castsi128_ps(_mm_set1_epi32(int(0x{:X})))));", v(insn.operands[1]), *reinterpret_cast(&value)); - } - else - { - println("_mm_cvtepu32_ps_(_mm_load_si128((__m128i*){}.u32)));", v(insn.operands[1])); - } - break; - } - - case PPC_INST_VCMPBFP: - case PPC_INST_VCMPBFP128: - println("\t__builtin_debugtrap();"); - break; - - case PPC_INST_VCMPEQFP: - case PPC_INST_VCMPEQFP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_cmpeq_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.setFromMask(_mm_load_ps({}.f32), 0xF);", cr(6), v(insn.operands[0])); - break; - - case PPC_INST_VCMPEQUB: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_cmpeq_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.setFromMask(_mm_load_si128((__m128i*){}.u8), 0xFFFF);", cr(6), v(insn.operands[0])); - break; - - case PPC_INST_VCMPEQUW: - case PPC_INST_VCMPEQUW128: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_cmpeq_epi32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.setFromMask(_mm_load_ps({}.f32), 0xF);", cr(6), v(insn.operands[0])); - break; - - case PPC_INST_VCMPGEFP: - case PPC_INST_VCMPGEFP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_cmpge_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.setFromMask(_mm_load_ps({}.f32), 0xF);", cr(6), v(insn.operands[0])); - break; - - case PPC_INST_VCMPGTFP: - case PPC_INST_VCMPGTFP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_cmpgt_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.setFromMask(_mm_load_ps({}.f32), 0xF);", cr(6), v(insn.operands[0])); - break; - - case PPC_INST_VCMPGTUB: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_cmpgt_epu8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VCMPGTUH: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_cmpgt_epu16(_mm_load_si128((__m128i*){}.u16), _mm_load_si128((__m128i*){}.u16)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VEXPTEFP: - case PPC_INST_VEXPTEFP128: - // TODO: vectorize - printSetFlushMode(true); - for (size_t i = 0; i < 4; i++) - println("\t{}.f32[{}] = exp2f({}.f32[{}]);", v(insn.operands[0]), i, v(insn.operands[1]), i); - break; - - case PPC_INST_VLOGEFP: - case PPC_INST_VLOGEFP128: - // TODO: vectorize - printSetFlushMode(true); - for (size_t i = 0; i < 4; i++) - println("\t{}.f32[{}] = log2f({}.f32[{}]);", v(insn.operands[0]), i, v(insn.operands[1]), i); - break; - - case PPC_INST_VMADDCFP128: - case PPC_INST_VMADDFP: - case PPC_INST_VMADDFP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_add_ps(_mm_mul_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), v(insn.operands[3])); - break; - - case PPC_INST_VMAXFP: - case PPC_INST_VMAXFP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_max_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VMAXSW: - println("\t_mm_store_si128((__m128i*){}.u32, _mm_max_epi32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VMINFP: - case PPC_INST_VMINFP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_min_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VMRGHB: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_unpackhi_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); - break; - - case PPC_INST_VMRGHH: - println("\t_mm_store_si128((__m128i*){}.u16, _mm_unpackhi_epi16(_mm_load_si128((__m128i*){}.u16), _mm_load_si128((__m128i*){}.u16)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); - break; - - case PPC_INST_VMRGHW: - case PPC_INST_VMRGHW128: - println("\t_mm_store_si128((__m128i*){}.u32, _mm_unpackhi_epi32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); - break; - - case PPC_INST_VMRGLB: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_unpacklo_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); - break; - - case PPC_INST_VMRGLH: - println("\t_mm_store_si128((__m128i*){}.u16, _mm_unpacklo_epi16(_mm_load_si128((__m128i*){}.u16), _mm_load_si128((__m128i*){}.u16)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); - break; - - case PPC_INST_VMRGLW: - case PPC_INST_VMRGLW128: - println("\t_mm_store_si128((__m128i*){}.u32, _mm_unpacklo_epi32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); - break; - - case PPC_INST_VMSUM3FP128: - // NOTE: accounting for full vector reversal here. should dot product yzw instead of xyz - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_dp_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32), 0xEF));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VMSUM4FP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_dp_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32), 0xFF));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VMULFP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_mul_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VNMSUBFP: - case PPC_INST_VNMSUBFP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_xor_ps(_mm_sub_ps(_mm_mul_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)), _mm_load_ps({}.f32)), _mm_castsi128_ps(_mm_set1_epi32(int(0x80000000)))));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), v(insn.operands[3])); - break; - - case PPC_INST_VOR: - case PPC_INST_VOR128: - print("\t_mm_store_si128((__m128i*){}.u8, ", v(insn.operands[0])); - - if (insn.operands[1] != insn.operands[2]) - println("_mm_or_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[1]), v(insn.operands[2])); - else - println("_mm_load_si128((__m128i*){}.u8));", v(insn.operands[1])); - - break; - - case PPC_INST_VPERM: - case PPC_INST_VPERM128: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_perm_epi8_(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), v(insn.operands[3])); - break; - - case PPC_INST_VPERMWI128: - { - // NOTE: accounting for full vector reversal here - uint32_t x = 3 - (insn.operands[2] & 0x3); - uint32_t y = 3 - ((insn.operands[2] >> 2) & 0x3); - uint32_t z = 3 - ((insn.operands[2] >> 4) & 0x3); - uint32_t w = 3 - ((insn.operands[2] >> 6) & 0x3); - uint32_t perm = x | (y << 2) | (z << 4) | (w << 6); - println("\t_mm_store_si128((__m128i*){}.u32, _mm_shuffle_epi32(_mm_load_si128((__m128i*){}.u32), 0x{:X}));", v(insn.operands[0]), v(insn.operands[1]), perm); - break; - } - - case PPC_INST_VPKD3D128: - // TODO: vectorize somehow? - // NOTE: handling vector reversal here too - printSetFlushMode(true); - switch (insn.operands[2]) - { - case 0: // D3D color - if (insn.operands[3] != 1 || insn.operands[4] != 3) - fmt::println("Unexpected D3D color pack instruction at {:X}", base); - - for (size_t i = 0; i < 4; i++) - { - constexpr size_t indices[] = { 3, 0, 1, 2 }; - println("\t{}.u32[{}] = 0x404000FF;", vTemp(), i); - println("\t{}.f32[{}] = {}.f32[{}] < 3.0f ? 3.0f : ({}.f32[{}] > {}.f32[{}] ? {}.f32[{}] : {}.f32[{}]);", vTemp(), i, v(insn.operands[1]), i, v(insn.operands[1]), i, vTemp(), i, vTemp(), i, v(insn.operands[1]), i); - println("\t{}.u32 {}= uint32_t({}.u8[{}]) << {};", temp(), i == 0 ? "" : "|", vTemp(), i * 4, indices[i] * 8); - } - println("\t{}.u32[3] = {}.u32;", v(insn.operands[0]), temp()); - break; - - default: - println("\t__builtin_debugtrap();"); - break; - } - break; - - case PPC_INST_VPKSHUS: - case PPC_INST_VPKSHUS128: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_packus_epi16(_mm_load_si128((__m128i*){}.s16), _mm_load_si128((__m128i*){}.s16)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); - break; - - case PPC_INST_VREFP: - case PPC_INST_VREFP128: - // TODO: see if we can use rcp safely - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_div_ps(_mm_set1_ps(1), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1])); - break; - - case PPC_INST_VRFIM: - case PPC_INST_VRFIM128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_round_ps(_mm_load_ps({}.f32), _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC));", v(insn.operands[0]), v(insn.operands[1])); - break; - - case PPC_INST_VRFIN: - case PPC_INST_VRFIN128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_round_ps(_mm_load_ps({}.f32), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC));", v(insn.operands[0]), v(insn.operands[1])); - break; - - case PPC_INST_VRFIZ: - case PPC_INST_VRFIZ128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_round_ps(_mm_load_ps({}.f32), _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC));", v(insn.operands[0]), v(insn.operands[1])); - break; - - case PPC_INST_VRLIMI128: - { - constexpr size_t shuffles[] = { _MM_SHUFFLE(3, 2, 1, 0), _MM_SHUFFLE(2, 1, 0, 3), _MM_SHUFFLE(1, 0, 3, 2), _MM_SHUFFLE(0, 3, 2, 1) }; - println("\t_mm_store_ps({}.f32, _mm_blend_ps(_mm_load_ps({}.f32), _mm_permute_ps(_mm_load_ps({}.f32), {}), {}));", v(insn.operands[0]), v(insn.operands[0]), v(insn.operands[1]), shuffles[insn.operands[3]], insn.operands[2]); - break; - } - - case PPC_INST_VRSQRTEFP: - case PPC_INST_VRSQRTEFP128: - // TODO: see if we can use rsqrt safely - // TODO: we can detect if the input is from a dot product and apply logic only on one value - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_div_ps(_mm_set1_ps(1), _mm_sqrt_ps(_mm_load_ps({}.f32))));", v(insn.operands[0]), v(insn.operands[1])); - break; - - case PPC_INST_VSEL: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_or_si128(_mm_andnot_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)), _mm_and_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8))));", v(insn.operands[0]), v(insn.operands[3]), v(insn.operands[1]), v(insn.operands[3]), v(insn.operands[2])); - break; - - case PPC_INST_VSLB: - // TODO: vectorize - for (size_t i = 0; i < 16; i++) - println("\t{}.u8[{}] = {}.u8[{}] << ({}.u8[{}] & 0x7);", v(insn.operands[0]), i, v(insn.operands[1]), i, v(insn.operands[2]), i); - break; - - case PPC_INST_VSLDOI: - case PPC_INST_VSLDOI128: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_alignr_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8), {}));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), 16 - insn.operands[3]); - break; - - case PPC_INST_VSLW: - case PPC_INST_VSLW128: - // TODO: vectorize, ensure endianness is correct - for (size_t i = 0; i < 4; i++) - println("\t{}.u32[{}] = {}.u32[{}] << ({}.u8[{}] & 0x1F);", v(insn.operands[0]), i, v(insn.operands[1]), i, v(insn.operands[2]), i * 4); - break; - - case PPC_INST_VSPLTB: - { - // NOTE: accounting for full vector reversal here - uint32_t perm = 15 - insn.operands[2]; - println("\t_mm_store_si128((__m128i*){}.u8, _mm_shuffle_epi8(_mm_load_si128((__m128i*){}.u8), _mm_set1_epi8(char(0x{:X}))));", v(insn.operands[0]), v(insn.operands[1]), perm); - break; - } - - case PPC_INST_VSPLTH: - { - // NOTE: accounting for full vector reversal here - uint32_t perm = 7 - insn.operands[2]; - perm = (perm * 2) | ((perm * 2 + 1) << 8); - println("\t_mm_store_si128((__m128i*){}.u16, _mm_shuffle_epi8(_mm_load_si128((__m128i*){}.u16), _mm_set1_epi16(short(0x{:X}))));", v(insn.operands[0]), v(insn.operands[1]), perm); - break; - } - - case PPC_INST_VSPLTISB: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_set1_epi8(char(0x{:X})));", v(insn.operands[0]), insn.operands[1]); - break; - - case PPC_INST_VSPLTISW: - case PPC_INST_VSPLTISW128: - println("\t_mm_store_si128((__m128i*){}.u32, _mm_set1_epi32(int(0x{:X})));", v(insn.operands[0]), insn.operands[1]); - break; - - case PPC_INST_VSPLTW: - case PPC_INST_VSPLTW128: - { - // NOTE: accounting for full vector reversal here - uint32_t perm = 3 - insn.operands[2]; - perm |= (perm << 2) | (perm << 4) | (perm << 6); - println("\t_mm_store_si128((__m128i*){}.u32, _mm_shuffle_epi32(_mm_load_si128((__m128i*){}.u32), 0x{:X}));", v(insn.operands[0]), v(insn.operands[1]), perm); - break; - } - - case PPC_INST_VSR: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_vsr(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VSRAW: - case PPC_INST_VSRAW128: - // TODO: vectorize, ensure endianness is correct - for (size_t i = 0; i < 4; i++) - println("\t{}.s32[{}] = {}.s32[{}] >> ({}.u8[{}] & 0x1F);", v(insn.operands[0]), i, v(insn.operands[1]), i, v(insn.operands[2]), i * 4); - break; - - case PPC_INST_VSRW: - case PPC_INST_VSRW128: - // TODO: vectorize, ensure endianness is correct - for (size_t i = 0; i < 4; i++) - println("\t{}.u32[{}] = {}.u32[{}] >> ({}.u8[{}] & 0x1F);", v(insn.operands[0]), i, v(insn.operands[1]), i, v(insn.operands[2]), i * 4); - break; - - case PPC_INST_VSUBFP: - case PPC_INST_VSUBFP128: - printSetFlushMode(true); - println("\t_mm_store_ps({}.f32, _mm_sub_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VSUBSWS: - // TODO: vectorize - for (size_t i = 0; i < 4; i++) - { - println("\t{}.s64 = int64_t({}.s32[{}]) - int64_t({}.s32[{}]);", temp(), v(insn.operands[1]), i, v(insn.operands[2]), i); - println("\t{}.s32[{}] = {}.s64 > INT_MAX ? INT_MAX : {}.s64 < INT_MIN ? INT_MIN : {}.s64;", v(insn.operands[0]), i, temp(), temp(), temp()); - } - break; - - case PPC_INST_VSUBUBS: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_subs_epu8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VSUBUHM: - println("\t_mm_store_si128((__m128i*){}.u8, _mm_sub_epi16(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - case PPC_INST_VUPKD3D128: - // TODO: vectorize somehow? - // NOTE: handling vector reversal here too - switch (insn.operands[2] >> 2) - { - case 0: // D3D color - for (size_t i = 0; i < 4; i++) - { - constexpr size_t indices[] = { 3, 0, 1, 2 }; - println("\t{}.u32[{}] = {}.u8[{}] | 0x3F800000;", vTemp(), i, v(insn.operands[1]), indices[i]); - } - println("\t{} = {};", v(insn.operands[0]), vTemp()); - break; - - case 1: // 2 shorts - for (size_t i = 0; i < 2; i++) - { - println("\t{}.f32 = 3.0f;", temp()); - println("\t{}.s32 += {}.s16[{}];", temp(), v(insn.operands[1]), 1 - i); - println("\t{}.f32[{}] = {}.f32;", vTemp(), 3 - i, temp()); - } - println("\t{}.f32[1] = 0.0f;", vTemp()); - println("\t{}.f32[0] = 1.0f;", vTemp()); - println("\t{} = {};", v(insn.operands[0]), vTemp()); - break; - - default: - println("\t__builtin_debugtrap();"); - break; - } - break; - - case PPC_INST_VUPKHSB: - case PPC_INST_VUPKHSB128: - println("\t_mm_store_si128((__m128i*){}.s16, _mm_cvtepi8_epi16(_mm_unpackhi_epi64(_mm_load_si128((__m128i*){}.s8), _mm_load_si128((__m128i*){}.s8))));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[1])); - break; - - case PPC_INST_VUPKHSH: - case PPC_INST_VUPKHSH128: - println("\t_mm_store_si128((__m128i*){}.s32, _mm_cvtepi16_epi32(_mm_unpackhi_epi64(_mm_load_si128((__m128i*){}.s16), _mm_load_si128((__m128i*){}.s16))));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[1])); - break; - - case PPC_INST_VUPKLSB: - case PPC_INST_VUPKLSB128: - println("\t_mm_store_si128((__m128i*){}.s32, _mm_cvtepi8_epi16(_mm_load_si128((__m128i*){}.s16)));", v(insn.operands[0]), v(insn.operands[1])); - break; - - case PPC_INST_VUPKLSH: - case PPC_INST_VUPKLSH128: - println("\t_mm_store_si128((__m128i*){}.s32, _mm_cvtepi16_epi32(_mm_load_si128((__m128i*){}.s16)));", v(insn.operands[0]), v(insn.operands[1])); - break; - - case PPC_INST_VXOR: - case PPC_INST_VXOR128: - print("\t_mm_store_si128((__m128i*){}.u8, ", v(insn.operands[0])); - - if (insn.operands[1] != insn.operands[2]) - println("_mm_xor_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[1]), v(insn.operands[2])); - else - println("_mm_setzero_si128());"); - - break; - - case PPC_INST_XOR: - println("\t{}.u64 = {}.u64 ^ {}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); - break; - - case PPC_INST_XORI: - println("\t{}.u64 = {}.u64 ^ {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); - break; - - case PPC_INST_XORIS: - println("\t{}.u64 = {}.u64 ^ {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2] << 16); - break; - - case PPC_INST_MULHD: - println("\t{}.u64 = ({}.u64 * {}.u64) >> 64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s64, 0);", cr(0), r(insn.operands[0])); - break; - -case PPC_INST_FRSQRTE: - println("\t{}.f64 = 1.0 / sqrt({}.f64);", f(insn.operands[0]), f(insn.operands[1])); - break; - -case PPC_INST_DCBST: - println("\t// Data Cache Block Store - system instruction, no operation in emulator"); - break; - -case PPC_INST_VCFPUXWS128: - println("\t{}.v4sf = vec_convert_to_float({}.v4si);", v(insn.operands[0]), v(insn.operands[1])); - break; - -case PPC_INST_VPKUWUS128: - println("\t{}.v8hi = vec_pack_unsigned({}.v4si, {}.v4si);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - -case PPC_INST_VPKUHUS128: - println("\t{}.v16qi = vec_pack_unsigned({}.v8hi, {}.v8hi);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - -case PPC_INST_EQV: - println("\t{}.u64 = ~({}.u64 ^ {}.u64);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.s64, 0);", cr(0), r(insn.operands[0])); - break; - -case PPC_INST_VPKSWSS128: - println("\t{}.v8hi = vec_pack_saturate({}.v4si, {}.v4si);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - -case PPC_INST_MULHDU: - println("\t{}.u64 = ({}.u64 * {}.u64) >> 64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.u64, 0);", cr(0), r(insn.operands[0])); - break; - -case PPC_INST_STFSU: - println("\t*({}.ptr) = {}.f64;", r(insn.operands[1]), f(insn.operands[0])); - println("\t{}.u64 = {}.u64 + {}.s64;", r(insn.operands[1]), r(insn.operands[1]), r(insn.operands[2])); - break; - -case PPC_INST_MULLHWU: - println("\t{}.u32 = ({}.u16 * {}.u16);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); - if (strchr(insn.opcode->name, '.')) - println("\t{}.compare({}.u32, 0);", cr(0), r(insn.operands[0])); - break; - -case PPC_INST_VSEL128: - println("\t{}.v4si = vec_sel({}.v4si, {}.v4si, {}.v4si);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), v(insn.operands[3])); - break; - -case PPC_INST_VNOR128: - println("\t{}.v4si = ~({}.v4si | {}.v4si);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - -case PPC_INST_VANDC: - println("\t{}.v4si = {}.v4si & ~{}.v4si;", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - -case PPC_INST_VPKSWSS: - println("\t{}.v8hi = vec_pack_saturate({}.v4si, {}.v4si);", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); - break; - - default: - return false; - } - -#if 1 - if (strchr(insn.opcode->name, '.')) - { - int lastLine = out.find_last_of('\n', out.size() - 2); - if (out.find("cr0", lastLine + 1) == std::string::npos && out.find("cr6", lastLine + 1) == std::string::npos) - fmt::println("{} at {:X} has RC bit enabled but no comparison was generated", insn.opcode->name, base); - } -#endif - - if (midAsmHook != config.midAsmHooks.end() && midAsmHook->second.afterInstruction) - printMidAsmHook(); - - return true; -} - -bool Recompiler::Recompile(const Function& fn) -{ - auto base = fn.base; - auto end = base + fn.size; - auto* data = (uint32_t*)image.Find(base); - - static std::unordered_set labels; - labels.clear(); - - for (size_t addr = base; addr < end; addr += 4) - { - const uint32_t instruction = ByteSwap(*(uint32_t*)((char*)data + addr - base)); - if (!PPC_BL(instruction)) - { - const size_t op = PPC_OP(instruction); - if (op == PPC_OP_B) - labels.emplace(addr + PPC_BI(instruction)); - else if (op == PPC_OP_BC) - labels.emplace(addr + PPC_BD(instruction)); - } - - auto switchTable = config.switchTables.find(addr); - if (switchTable != config.switchTables.end()) - { - for (auto label : switchTable->second.labels) - labels.emplace(label); - } - - auto midAsmHook = config.midAsmHooks.find(addr); - if (midAsmHook != config.midAsmHooks.end()) - { - if (midAsmHook->second.returnOnFalse || midAsmHook->second.returnOnTrue || - midAsmHook->second.jumpAddressOnFalse != NULL || midAsmHook->second.jumpAddressOnTrue != NULL) - { - print("extern bool "); - } - else - { - print("extern void "); - } - - print("{}(", midAsmHook->second.name); - for (auto& reg : midAsmHook->second.registers) - { - if (out.back() != '(') - out += ", "; - - switch (reg[0]) - { - case 'c': - if (reg == "ctr") - print("PPCRegister& ctr"); - else - print("PPCCRRegister& {}", reg); - break; - - case 'x': - print("PPCXERRegister& xer"); - break; - - case 'r': - print("PPCRegister& {}", reg); - break; - - case 'f': - if (reg == "fpscr") - print("PPCFPSCRRegister& fpscr"); - else - print("PPCRegister& {}", reg); - break; - - case 'v': - print("PPCVRegister& {}", reg); - break; - } - } - - println(");\n"); - - if (midAsmHook->second.jumpAddress != NULL) - labels.emplace(midAsmHook->second.jumpAddress); - if (midAsmHook->second.jumpAddressOnTrue != NULL) - labels.emplace(midAsmHook->second.jumpAddressOnTrue); - if (midAsmHook->second.jumpAddressOnFalse != NULL) - labels.emplace(midAsmHook->second.jumpAddressOnFalse); - } - } - - auto symbol = image.symbols.find(fn.base); - std::string name; - if (symbol != image.symbols.end()) - { - name = symbol->name; - } - else - { - name = fmt::format("sub_{}", fn.base); - } - -#ifdef XENON_RECOMP_USE_ALIAS - println("__attribute__((alias(\"__imp__{}\"))) PPC_WEAK_FUNC({});", name, name); -#endif - - println("PPC_FUNC_IMPL(__imp__{}) {{", name); - println("\tPPC_FUNC_PROLOGUE();"); - - auto switchTable = config.switchTables.end(); - bool allRecompiled = true; - CSRState csrState = CSRState::Unknown; - - // TODO: the printing scheme here is scuffed - RecompilerLocalVariables localVariables; - static std::string tempString; - tempString.clear(); - std::swap(out, tempString); - - ppc_insn insn; - while (base < end) - { - if (labels.find(base) != labels.end()) - { - println("loc_{:X}:", base); - - // Anyone could jump to this label so we wouldn't know what the CSR state would be. - csrState = CSRState::Unknown; - } - - if (switchTable == config.switchTables.end()) - switchTable = config.switchTables.find(base); - - ppc::Disassemble(data, 4, base, insn); - - if (insn.opcode == nullptr) - { - println("\t// {}", insn.op_str); -#if 1 - if (*data != 0) - fmt::println("Unable to decode instruction {:X} at {:X}", *data, base); -#endif - } - else - { - if (insn.opcode->id == PPC_INST_BCTR && (*(data - 1) == 0x07008038 || *(data - 1) == 0x00000060) && switchTable == config.switchTables.end()) - fmt::println("Found a switch jump table at {:X} with no switch table entry present", base); - - if (!Recompile(fn, base, insn, data, switchTable, localVariables, csrState)) - { - fmt::println("Unrecognized instruction at 0x{:X}: {}", base, insn.opcode->name); - allRecompiled = false; - } - } - - base += 4; - ++data; - } - -#if 0 - if (insn.opcode == nullptr || (insn.opcode->id != PPC_INST_B && insn.opcode->id != PPC_INST_BCTR && insn.opcode->id != PPC_INST_BLR)) - fmt::println("Function at {:X} ends prematurely with instruction {} at {:X}", fn.base, insn.opcode != nullptr ? insn.opcode->name : "INVALID", base - 4); -#endif - - println("}}\n"); - -#ifndef XENON_RECOMP_USE_ALIAS - println("PPC_WEAK_FUNC({}) {{", name); - println("\t__imp__{}(ctx, base);", name); - println("}}\n"); -#endif - - std::swap(out, tempString); - if (localVariables.ctr) - println("\tPPCRegister ctr{{}};"); - if (localVariables.xer) - println("\tPPCXERRegister xer{{}};"); - if (localVariables.reserved) - println("\tPPCRegister reserved{{}};"); - - for (size_t i = 0; i < 8; i++) - { - if (localVariables.cr[i]) - println("\tPPCCRRegister cr{}{{}};", i); - } - - for (size_t i = 0; i < 32; i++) - { - if (localVariables.r[i]) - println("\tPPCRegister r{}{{}};", i); - } - - for (size_t i = 0; i < 32; i++) - { - if (localVariables.f[i]) - println("\tPPCRegister f{}{{}};", i); - } - - for (size_t i = 0; i < 128; i++) - { - if (localVariables.v[i]) - println("\tPPCVRegister v{}{{}};", i); - } - - if (localVariables.env) - println("\tPPCContext env{{}};"); - - if (localVariables.temp) - println("\tPPCRegister temp{{}};"); - - if (localVariables.vTemp) - println("\tPPCVRegister vTemp{{}};"); - - if (localVariables.ea) - println("\tuint32_t ea{{}};"); - - out += tempString; - - return allRecompiled; -} - -void Recompiler::Recompile(const std::filesystem::path& headerFilePath) -{ - out.reserve(10 * 1024 * 1024); - - { - println("#pragma once"); - - println("#ifndef PPC_CONFIG_H_INCLUDED"); - println("#define PPC_CONFIG_H_INCLUDED\n"); - - if (config.skipLr) - println("#define PPC_CONFIG_SKIP_LR"); - if (config.ctrAsLocalVariable) - println("#define PPC_CONFIG_CTR_AS_LOCAL"); - if (config.xerAsLocalVariable) - println("#define PPC_CONFIG_XER_AS_LOCAL"); - if (config.reservedRegisterAsLocalVariable) - println("#define PPC_CONFIG_RESERVED_AS_LOCAL"); - if (config.skipMsr) - println("#define PPC_CONFIG_SKIP_MSR"); - if (config.crRegistersAsLocalVariables) - println("#define PPC_CONFIG_CR_AS_LOCAL"); - if (config.nonArgumentRegistersAsLocalVariables) - println("#define PPC_CONFIG_NON_ARGUMENT_AS_LOCAL"); - if (config.nonVolatileRegistersAsLocalVariables) - println("#define PPC_CONFIG_NON_VOLATILE_AS_LOCAL"); - - println(""); - - println("#define PPC_IMAGE_BASE 0x{:X}ull", image.base); - println("#define PPC_IMAGE_SIZE 0x{:X}ull", image.size); - - // Extract the address of the minimum code segment to store the function table at. - size_t codeMin = ~0; - size_t codeMax = 0; - - for (auto& section : image.sections) - { - if ((section.flags & SectionFlags_Code) != 0) - { - if (section.base < codeMin) - codeMin = section.base; - - if ((section.base + section.size) > codeMax) - codeMax = (section.base + section.size); - } - } - - println("#define PPC_CODE_BASE 0x{:X}ull", codeMin); - println("#define PPC_CODE_SIZE 0x{:X}ull", codeMax - codeMin); - - println(""); - - println("#ifdef PPC_INCLUDE_DETAIL"); - println("#include \"ppc_detail.h\""); - println("#endif"); - - println("\n#endif"); - - SaveCurrentOutData("ppc_config.h"); - } - - { - println("#pragma once"); - - println("#include \"ppc_config.h\"\n"); - - std::ifstream stream(headerFilePath); - if (stream.good()) - { - std::stringstream ss; - ss << stream.rdbuf(); - out += ss.str(); - } - - SaveCurrentOutData("ppc_context.h"); - } - - { - println("#pragma once\n"); - println("#include \"ppc_config.h\""); - println("#include \"ppc_context.h\"\n"); - - for (auto& symbol : image.symbols) - println("PPC_EXTERN_FUNC({});", symbol.name); - - SaveCurrentOutData("ppc_recomp_shared.h"); - } - - { - println("#include \"ppc_recomp_shared.h\"\n"); - - println("PPCFuncMapping PPCFuncMappings[] = {{"); - for (auto& symbol : image.symbols) - println("\t{{ 0x{:X}, {} }},", symbol.address, symbol.name); - - println("\t{{ 0, nullptr }}"); - println("}};"); - - SaveCurrentOutData("ppc_func_mapping.cpp"); - } - - for (size_t i = 0; i < functions.size(); i++) - { - if ((i % 256) == 0) - { - SaveCurrentOutData(); - println("#include \"ppc_recomp_shared.h\"\n"); - } - - if ((i % 2048) == 0 || (i == (functions.size() - 1))) - fmt::println("Recompiling functions... {}%", static_cast(i + 1) / functions.size() * 100.0f); - - Recompile(functions[i]); - } - - SaveCurrentOutData(); -} - -void Recompiler::SaveCurrentOutData(const std::string_view& name) -{ - if (!out.empty()) - { - std::string cppName; - - if (name.empty()) - { - cppName = fmt::format("ppc_recomp.{}.cpp", cppFileIndex); - ++cppFileIndex; - } - - bool shouldWrite = true; - - // Check if an identical file already exists first to not trigger recompilation - std::string directoryPath = config.directoryPath; - if (!directoryPath.empty()) - directoryPath += "/"; - - std::string filePath = fmt::format("{}{}/{}", directoryPath, config.outDirectoryPath, name.empty() ? cppName : name); - FILE* f = fopen(filePath.c_str(), "rb"); - if (f) - { - static std::vector temp; - - fseek(f, 0, SEEK_END); - long fileSize = ftell(f); - if (fileSize == out.size()) - { - fseek(f, 0, SEEK_SET); - temp.resize(fileSize); - fread(temp.data(), 1, fileSize, f); - - shouldWrite = !XXH128_isEqual(XXH3_128bits(temp.data(), temp.size()), XXH3_128bits(out.data(), out.size())); - } - fclose(f); - } - - if (shouldWrite) - { - f = fopen(filePath.c_str(), "wb"); - fwrite(out.data(), 1, out.size(), f); - fclose(f); - } - - out.clear(); - } -} From 775ad31136b8be0690bf9895a26862e1117a2bef Mon Sep 17 00:00:00 2001 From: MadLadMikael Date: Sun, 6 Apr 2025 15:58:21 -0500 Subject: [PATCH 5/5] hopefully added proper instructions for potf --- XenonRecomp/recompiler.cpp | 2766 ++++++++++++++++++++++++++++++++++++ 1 file changed, 2766 insertions(+) create mode 100644 XenonRecomp/recompiler.cpp diff --git a/XenonRecomp/recompiler.cpp b/XenonRecomp/recompiler.cpp new file mode 100644 index 0000000..dbb7428 --- /dev/null +++ b/XenonRecomp/recompiler.cpp @@ -0,0 +1,2766 @@ +#include "pch.h" +#include "recompiler.h" +#include + +static uint64_t ComputeMask(uint32_t mstart, uint32_t mstop) +{ + mstart &= 0x3F; + mstop &= 0x3F; + uint64_t value = (UINT64_MAX >> mstart) ^ ((mstop >= 63) ? 0 : UINT64_MAX >> (mstop + 1)); + return mstart <= mstop ? value : ~value; +} + +bool Recompiler::LoadConfig(const std::string_view& configFilePath) +{ + config.Load(configFilePath); + + std::vector file; + if (!config.patchedFilePath.empty()) + file = LoadFile((config.directoryPath + config.patchedFilePath).c_str()); + + if (file.empty()) + { + file = LoadFile((config.directoryPath + config.filePath).c_str()); + + if (!config.patchFilePath.empty()) + { + const auto patchFile = LoadFile((config.directoryPath + config.patchFilePath).c_str()); + if (!patchFile.empty()) + { + std::vector outBytes; + auto result = XexPatcher::apply(file.data(), file.size(), patchFile.data(), patchFile.size(), outBytes, false); + if (result == XexPatcher::Result::Success) + { + std::exchange(file, outBytes); + + if (!config.patchedFilePath.empty()) + { + std::ofstream stream(config.directoryPath + config.patchedFilePath, std::ios::binary); + if (stream.good()) + { + stream.write(reinterpret_cast(file.data()), file.size()); + stream.close(); + } + } + } + else + { + fmt::print("ERROR: Unable to apply the patch file, "); + + switch (result) + { + case XexPatcher::Result::XexFileUnsupported: + fmt::println("XEX file unsupported"); + break; + + case XexPatcher::Result::XexFileInvalid: + fmt::println("XEX file invalid"); + break; + + case XexPatcher::Result::PatchFileInvalid: + fmt::println("patch file invalid"); + break; + + case XexPatcher::Result::PatchIncompatible: + fmt::println("patch file incompatible"); + break; + + case XexPatcher::Result::PatchFailed: + fmt::println("patch failed"); + break; + + case XexPatcher::Result::PatchUnsupported: + fmt::println("patch unsupported"); + break; + + default: + fmt::println("reason unknown"); + break; + } + + return false; + } + } + else + { + fmt::println("ERROR: Unable to load the patch file"); + return false; + } + } + } + + image = Image::ParseImage(file.data(), file.size()); + return true; +} + +void Recompiler::Analyse() +{ + for (size_t i = 14; i < 128; i++) + { + if (i < 32) + { + if (config.restGpr14Address != 0) + { + auto& restgpr = functions.emplace_back(); + restgpr.base = config.restGpr14Address + (i - 14) * 4; + restgpr.size = (32 - i) * 4 + 12; + image.symbols.emplace(Symbol{ fmt::format("__restgprlr_{}", i), restgpr.base, restgpr.size, Symbol_Function }); + } + + if (config.saveGpr14Address != 0) + { + auto& savegpr = functions.emplace_back(); + savegpr.base = config.saveGpr14Address + (i - 14) * 4; + savegpr.size = (32 - i) * 4 + 8; + image.symbols.emplace(fmt::format("__savegprlr_{}", i), savegpr.base, savegpr.size, Symbol_Function); + } + + if (config.restFpr14Address != 0) + { + auto& restfpr = functions.emplace_back(); + restfpr.base = config.restFpr14Address + (i - 14) * 4; + restfpr.size = (32 - i) * 4 + 4; + image.symbols.emplace(fmt::format("__restfpr_{}", i), restfpr.base, restfpr.size, Symbol_Function); + } + + if (config.saveFpr14Address != 0) + { + auto& savefpr = functions.emplace_back(); + savefpr.base = config.saveFpr14Address + (i - 14) * 4; + savefpr.size = (32 - i) * 4 + 4; + image.symbols.emplace(fmt::format("__savefpr_{}", i), savefpr.base, savefpr.size, Symbol_Function); + } + + if (config.restVmx14Address != 0) + { + auto& restvmx = functions.emplace_back(); + restvmx.base = config.restVmx14Address + (i - 14) * 8; + restvmx.size = (32 - i) * 8 + 4; + image.symbols.emplace(fmt::format("__restvmx_{}", i), restvmx.base, restvmx.size, Symbol_Function); + } + + if (config.saveVmx14Address != 0) + { + auto& savevmx = functions.emplace_back(); + savevmx.base = config.saveVmx14Address + (i - 14) * 8; + savevmx.size = (32 - i) * 8 + 4; + image.symbols.emplace(fmt::format("__savevmx_{}", i), savevmx.base, savevmx.size, Symbol_Function); + } + } + + if (i >= 64) + { + if (config.restVmx64Address != 0) + { + auto& restvmx = functions.emplace_back(); + restvmx.base = config.restVmx64Address + (i - 64) * 8; + restvmx.size = (128 - i) * 8 + 4; + image.symbols.emplace(fmt::format("__restvmx_{}", i), restvmx.base, restvmx.size, Symbol_Function); + } + + if (config.saveVmx64Address != 0) + { + auto& savevmx = functions.emplace_back(); + savevmx.base = config.saveVmx64Address + (i - 64) * 8; + savevmx.size = (128 - i) * 8 + 4; + image.symbols.emplace(fmt::format("__savevmx_{}", i), savevmx.base, savevmx.size, Symbol_Function); + } + } + } + + for (auto& [address, size] : config.functions) + { + functions.emplace_back(address, size); + image.symbols.emplace(fmt::format("sub_{:X}", address), address, size, Symbol_Function); + } + + auto& pdata = *image.Find(".pdata"); + size_t count = pdata.size / sizeof(IMAGE_CE_RUNTIME_FUNCTION); + auto* pf = (IMAGE_CE_RUNTIME_FUNCTION*)pdata.data; + for (size_t i = 0; i < count; i++) + { + auto fn = pf[i]; + fn.BeginAddress = ByteSwap(fn.BeginAddress); + fn.Data = ByteSwap(fn.Data); + + if (image.symbols.find(fn.BeginAddress) == image.symbols.end()) + { + auto& f = functions.emplace_back(); + f.base = fn.BeginAddress; + f.size = fn.FunctionLength * 4; + + image.symbols.emplace(fmt::format("sub_{:X}", f.base), f.base, f.size, Symbol_Function); + } + } + + for (const auto& section : image.sections) + { + if (!(section.flags & SectionFlags_Code)) + { + continue; + } + size_t base = section.base; + uint8_t* data = section.data; + uint8_t* dataEnd = section.data + section.size; + + while (data < dataEnd) + { + uint32_t insn = ByteSwap(*(uint32_t*)data); + if (PPC_OP(insn) == PPC_OP_B && PPC_BL(insn)) + { + size_t address = base + (data - section.data) + PPC_BI(insn); + + if (address >= section.base && address < section.base + section.size && image.symbols.find(address) == image.symbols.end()) + { + auto data = section.data + address - section.base; + auto& fn = functions.emplace_back(Function::Analyze(data, section.base + section.size - address, address)); + image.symbols.emplace(fmt::format("sub_{:X}", fn.base), fn.base, fn.size, Symbol_Function); + } + } + data += 4; + } + + data = section.data; + + while (data < dataEnd) + { + auto invalidInstr = config.invalidInstructions.find(ByteSwap(*(uint32_t*)data)); + if (invalidInstr != config.invalidInstructions.end()) + { + base += invalidInstr->second; + data += invalidInstr->second; + continue; + } + + auto fnSymbol = image.symbols.find(base); + if (fnSymbol != image.symbols.end() && fnSymbol->address == base && fnSymbol->type == Symbol_Function) + { + assert(fnSymbol->address == base); + + base += fnSymbol->size; + data += fnSymbol->size; + } + else + { + auto& fn = functions.emplace_back(Function::Analyze(data, dataEnd - data, base)); + image.symbols.emplace(fmt::format("sub_{:X}", fn.base), fn.base, fn.size, Symbol_Function); + + base += fn.size; + data += fn.size; + } + } + } + + std::sort(functions.begin(), functions.end(), [](auto& lhs, auto& rhs) { return lhs.base < rhs.base; }); +} + +bool Recompiler::Recompile( + const Function& fn, + uint32_t base, + const ppc_insn& insn, + const uint32_t* data, + std::unordered_map::iterator& switchTable, + RecompilerLocalVariables& localVariables, + CSRState& csrState) +{ + println("\t// {} {}", insn.opcode->name, insn.op_str); + + // TODO: we could cache these formats in an array + auto r = [&](size_t index) + { + if ((config.nonArgumentRegistersAsLocalVariables && (index == 0 || index == 2 || index == 11 || index == 12)) || + (config.nonVolatileRegistersAsLocalVariables && index >= 14)) + { + localVariables.r[index] = true; + return fmt::format("r{}", index); + } + return fmt::format("ctx.r{}", index); + }; + + auto f = [&](size_t index) + { + if ((config.nonArgumentRegistersAsLocalVariables && index == 0) || + (config.nonVolatileRegistersAsLocalVariables && index >= 14)) + { + localVariables.f[index] = true; + return fmt::format("f{}", index); + } + return fmt::format("ctx.f{}", index); + }; + + auto v = [&](size_t index) + { + if ((config.nonArgumentRegistersAsLocalVariables && (index >= 32 && index <= 63)) || + (config.nonVolatileRegistersAsLocalVariables && ((index >= 14 && index <= 31) || (index >= 64 && index <= 127)))) + { + localVariables.v[index] = true; + return fmt::format("v{}", index); + } + return fmt::format("ctx.v{}", index); + }; + + auto cr = [&](size_t index) + { + if (config.crRegistersAsLocalVariables) + { + localVariables.cr[index] = true; + return fmt::format("cr{}", index); + } + return fmt::format("ctx.cr{}", index); + }; + + auto ctr = [&]() + { + if (config.ctrAsLocalVariable) + { + localVariables.ctr = true; + return "ctr"; + } + return "ctx.ctr"; + }; + + auto xer = [&]() + { + if (config.xerAsLocalVariable) + { + localVariables.xer = true; + return "xer"; + } + return "ctx.xer"; + }; + + auto reserved = [&]() + { + if (config.reservedRegisterAsLocalVariable) + { + localVariables.reserved = true; + return "reserved"; + } + return "ctx.reserved"; + }; + + auto temp = [&]() + { + localVariables.temp = true; + return "temp"; + }; + + auto vTemp = [&]() + { + localVariables.vTemp = true; + return "vTemp"; + }; + + auto env = [&]() + { + localVariables.env = true; + return "env"; + }; + + auto ea = [&]() + { + localVariables.ea = true; + return "ea"; + }; + + // TODO (Sajid): Check for out of bounds access + auto mmioStore = [&]() -> bool + { + return *(data + 1) == c_eieio; + }; + + auto printFunctionCall = [&](uint32_t address) + { + if (address == config.longJmpAddress) + { + println("\tlongjmp(*reinterpret_cast(base + {}.u32), {}.s32);", r(3), r(4)); + } + else if (address == config.setJmpAddress) + { + println("\t{} = ctx;", env()); + println("\t{}.s64 = setjmp(*reinterpret_cast(base + {}.u32));", temp(), r(3)); + println("\tif ({}.s64 != 0) ctx = {};", temp(), env()); + println("\t{} = {};", r(3), temp()); + } + else + { + auto targetSymbol = image.symbols.find(address); + + if (targetSymbol != image.symbols.end() && targetSymbol->address == address && targetSymbol->type == Symbol_Function) + { + if (config.nonVolatileRegistersAsLocalVariables && (targetSymbol->name.find("__rest") == 0 || targetSymbol->name.find("__save") == 0)) + { + // print nothing + } + else + { + println("\t{}(ctx, base);", targetSymbol->name); + } + } + else + { + println("\t// ERROR {:X}", address); + } + } + }; + + auto printConditionalBranch = [&](bool not_, const std::string_view& cond) + { + if (insn.operands[1] < fn.base || insn.operands[1] >= fn.base + fn.size) + { + println("\tif ({}{}.{}) {{", not_ ? "!" : "", cr(insn.operands[0]), cond); + print("\t"); + printFunctionCall(insn.operands[1]); + println("\t\treturn;"); + println("\t}}"); + } + else + { + println("\tif ({}{}.{}) goto loc_{:X};", not_ ? "!" : "", cr(insn.operands[0]), cond, insn.operands[1]); + } + }; + + auto printSetFlushMode = [&](bool enable) + { + auto newState = enable ? CSRState::VMX : CSRState::FPU; + if (csrState != newState) + { + auto prefix = enable ? "enable" : "disable"; + auto suffix = csrState != CSRState::Unknown ? "Unconditional" : ""; + println("\tctx.fpscr.{}FlushMode{}();", prefix, suffix); + + csrState = newState; + } + }; + + auto midAsmHook = config.midAsmHooks.find(base); + + auto printMidAsmHook = [&]() + { + bool returnsBool = midAsmHook->second.returnOnFalse || midAsmHook->second.returnOnTrue || + midAsmHook->second.jumpAddressOnFalse != NULL || midAsmHook->second.jumpAddressOnTrue != NULL; + + print("\t"); + if (returnsBool) + print("if ("); + + print("{}(", midAsmHook->second.name); + for (auto& reg : midAsmHook->second.registers) + { + if (out.back() != '(') + out += ", "; + + switch (reg[0]) + { + case 'c': + if (reg == "ctr") + out += ctr(); + else + out += cr(std::atoi(reg.c_str() + 2)); + break; + + case 'x': + out += xer(); + break; + + case 'r': + if (reg == "reserved") + out += reserved(); + else + out += r(std::atoi(reg.c_str() + 1)); + break; + + case 'f': + if (reg == "fpscr") + out += "ctx.fpscr"; + else + out += f(std::atoi(reg.c_str() + 1)); + break; + + case 'v': + out += v(std::atoi(reg.c_str() + 1)); + break; + } + } + + if (returnsBool) + { + println(")) {{"); + + if (midAsmHook->second.returnOnTrue) + println("\t\treturn;"); + else if (midAsmHook->second.jumpAddressOnTrue != NULL) + println("\t\tgoto loc_{:X};", midAsmHook->second.jumpAddressOnTrue); + + println("\t}}"); + + println("\telse {{"); + + if (midAsmHook->second.returnOnFalse) + println("\t\treturn;"); + else if (midAsmHook->second.jumpAddressOnFalse != NULL) + println("\t\tgoto loc_{:X};", midAsmHook->second.jumpAddressOnFalse); + + println("\t}}"); + } + else + { + println(");"); + + if (midAsmHook->second.ret) + println("\treturn;"); + else if (midAsmHook->second.jumpAddress != NULL) + println("\tgoto loc_{:X};", midAsmHook->second.jumpAddress); + } + }; + + if (midAsmHook != config.midAsmHooks.end() && !midAsmHook->second.afterInstruction) + printMidAsmHook(); + + int id = insn.opcode->id; + + // Handling instructions that don't disassemble correctly for some reason here + if (id == PPC_INST_VUPKHSB128 && insn.operands[2] == 0x60) id = PPC_INST_VUPKHSH128; + else if (id == PPC_INST_VUPKLSB128 && insn.operands[2] == 0x60) id = PPC_INST_VUPKLSH128; + + switch (id) + { + case PPC_INST_ADD: + println("\t{}.u64 = {}.u64 + {}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_ADDE: + println("\t{}.u8 = ({}.u32 + {}.u32 < {}.u32) | ({}.u32 + {}.u32 + {}.ca < {}.ca);", temp(), r(insn.operands[1]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[1]), r(insn.operands[2]), xer(), xer()); + println("\t{}.u64 = {}.u64 + {}.u64 + {}.ca;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); + println("\t{}.ca = {}.u8;", xer(), temp()); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_ADDI: + print("\t{}.s64 = ", r(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.s64 + ", r(insn.operands[1])); + println("{};", int32_t(insn.operands[2])); + break; + + case PPC_INST_ADDIC: + println("\t{}.ca = {}.u32 > {};", xer(), r(insn.operands[1]), ~insn.operands[2]); + println("\t{}.s64 = {}.s64 + {};", r(insn.operands[0]), r(insn.operands[1]), int32_t(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_ADDIS: + print("\t{}.s64 = ", r(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.s64 + ", r(insn.operands[1])); + println("{};", static_cast(insn.operands[2] << 16)); + break; + + case PPC_INST_ADDZE: + println("\t{}.s64 = {}.s64 + {}.ca;", temp(), r(insn.operands[1]), xer()); + println("\t{}.ca = {}.u32 < {}.u32;", xer(), temp(), r(insn.operands[1])); + println("\t{}.s64 = {}.s64;", r(insn.operands[0]), temp()); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_AND: + println("\t{}.u64 = {}.u64 & {}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_ANDC: + println("\t{}.u64 = {}.u64 & ~{}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_ANDI: + println("\t{}.u64 = {}.u64 & {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_ANDIS: + println("\t{}.u64 = {}.u64 & {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2] << 16); + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_ATTN: + // undefined instruction + break; + + case PPC_INST_B: + if (insn.operands[0] < fn.base || insn.operands[0] >= fn.base + fn.size) + { + printFunctionCall(insn.operands[0]); + println("\treturn;"); + } + else + { + println("\tgoto loc_{:X};", insn.operands[0]); + } + break; + + case PPC_INST_BCTR: + if (switchTable != config.switchTables.end()) + { + println("\tswitch ({}.u64) {{", r(switchTable->second.r)); + + for (size_t i = 0; i < switchTable->second.labels.size(); i++) + { + println("\tcase {}:", i); + auto label = switchTable->second.labels[i]; + if (label < fn.base || label >= fn.base + fn.size) + { + println("\t\t// ERROR: 0x{:X}", label); + fmt::println("ERROR: Switch case at {:X} is trying to jump outside function: {:X}", base, label); + println("\t\treturn;"); + } + else + { + println("\t\tgoto loc_{:X};", label); + } + } + + println("\tdefault:"); + println("\t\t__builtin_unreachable();"); + println("\t}}"); + + switchTable = config.switchTables.end(); + } + else + { + println("\tPPC_CALL_INDIRECT_FUNC({}.u32);", ctr()); + println("\treturn;"); + } + break; + + case PPC_INST_BCTRL: + if (!config.skipLr) + println("\tctx.lr = 0x{:X};", base + 4); + println("\tPPC_CALL_INDIRECT_FUNC({}.u32);", ctr()); + csrState = CSRState::Unknown; // the call could change it + break; + + case PPC_INST_BDZ: + println("\t--{}.u64;", ctr()); + println("\tif ({}.u32 == 0) goto loc_{:X};", ctr(), insn.operands[0]); + break; + + case PPC_INST_BDZLR: + println("\t--{}.u64;", ctr()); + println("\tif ({}.u32 == 0) return;", ctr(), insn.operands[0]); + break; + + case PPC_INST_BDNZ: + println("\t--{}.u64;", ctr()); + println("\tif ({}.u32 != 0) goto loc_{:X};", ctr(), insn.operands[0]); + break; + + case PPC_INST_BDNZF: + // NOTE: assuming eq here as a shortcut because all the instructions in the game do that + println("\t--{}.u64;", ctr()); + println("\tif ({}.u32 != 0 && !{}.eq) goto loc_{:X};", ctr(), cr(insn.operands[0] / 4), insn.operands[1]); + break; + + case PPC_INST_BEQ: + printConditionalBranch(false, "eq"); + break; + + case PPC_INST_BEQLR: + println("\tif ({}.eq) return;", cr(insn.operands[0])); + break; + + case PPC_INST_BGE: + printConditionalBranch(true, "lt"); + break; + + case PPC_INST_BGELR: + println("\tif (!{}.lt) return;", cr(insn.operands[0])); + break; + + case PPC_INST_BGT: + printConditionalBranch(false, "gt"); + break; + + case PPC_INST_BGTLR: + println("\tif ({}.gt) return;", cr(insn.operands[0])); + break; + + case PPC_INST_BL: + if (!config.skipLr) + println("\tctx.lr = 0x{:X};", base + 4); + printFunctionCall(insn.operands[0]); + csrState = CSRState::Unknown; // the call could change it + break; + + case PPC_INST_BLE: + printConditionalBranch(true, "gt"); + break; + + case PPC_INST_BLELR: + println("\tif (!{}.gt) return;", cr(insn.operands[0])); + break; + + case PPC_INST_BLR: + println("\treturn;"); + break; + + case PPC_INST_BLRL: + println("__builtin_debugtrap();"); + break; + + case PPC_INST_BLT: + printConditionalBranch(false, "lt"); + break; + + case PPC_INST_BLTLR: + println("\tif ({}.lt) return;", cr(insn.operands[0])); + break; + + case PPC_INST_BNE: + printConditionalBranch(true, "eq"); + break; + + case PPC_INST_BNECTR: + println("\tif (!{}.eq) {{", cr(insn.operands[0])); + println("\t\tPPC_CALL_INDIRECT_FUNC({}.u32);", ctr()); + println("\t\treturn;"); + println("\t}}"); + break; + + case PPC_INST_BNELR: + println("\tif (!{}.eq) return;", cr(insn.operands[0])); + break; + + case PPC_INST_CCTPL: + // no op + break; + + case PPC_INST_CCTPM: + // no op + break; + + case PPC_INST_CLRLDI: + println("\t{}.u64 = {}.u64 & 0x{:X};", r(insn.operands[0]), r(insn.operands[1]), (1ull << (64 - insn.operands[2])) - 1); + break; + + case PPC_INST_CLRLWI: + println("\t{}.u64 = {}.u32 & 0x{:X};", r(insn.operands[0]), r(insn.operands[1]), (1ull << (32 - insn.operands[2])) - 1); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_CMPD: + println("\t{}.compare({}.s64, {}.s64, {});", cr(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); + break; + + case PPC_INST_CMPDI: + println("\t{}.compare({}.s64, {}, {});", cr(insn.operands[0]), r(insn.operands[1]), int32_t(insn.operands[2]), xer()); + break; + + case PPC_INST_CMPLD: + println("\t{}.compare({}.u64, {}.u64, {});", cr(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); + break; + + case PPC_INST_CMPLDI: + println("\t{}.compare({}.u64, {}, {});", cr(insn.operands[0]), r(insn.operands[1]), insn.operands[2], xer()); + break; + + case PPC_INST_CMPLW: + println("\t{}.compare({}.u32, {}.u32, {});", cr(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); + break; + + case PPC_INST_CMPLWI: + println("\t{}.compare({}.u32, {}, {});", cr(insn.operands[0]), r(insn.operands[1]), insn.operands[2], xer()); + break; + + case PPC_INST_CMPW: + println("\t{}.compare({}.s32, {}.s32, {});", cr(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); + break; + + case PPC_INST_CMPWI: + println("\t{}.compare({}.s32, {}, {});", cr(insn.operands[0]), r(insn.operands[1]), int32_t(insn.operands[2]), xer()); + break; + + case PPC_INST_CNTLZD: + println("\t{0}.u64 = {1}.u64 == 0 ? 64 : __builtin_clzll({1}.u64);", r(insn.operands[0]), r(insn.operands[1])); + break; + + case PPC_INST_CNTLZW: + println("\t{0}.u64 = {1}.u32 == 0 ? 32 : __builtin_clz({1}.u32);", r(insn.operands[0]), r(insn.operands[1])); + break; + + case PPC_INST_DB16CYC: + // no op + break; + + + case PPC_INST_EQV: + println("\t{}.u64 = ~({}.u64 ^ {}.u64);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s64, 0, {});", cr(0), r(insn.operands[0]), xer()); // Check if CR0 comparison uses s64 + break; + + case PPC_INST_DCBF: + // no op + break; + + case PPC_INST_DCBT: + // no op + break; + + case PPC_INST_DCBST: + // no op + break; + + case PPC_INST_DCBTST: + // no op + break; + + case PPC_INST_DCBZ: + print("\tmemset(base + (("); + if (insn.operands[0] != 0) + print("{}.u32 + ", r(insn.operands[0])); + println("{}.u32) & ~31), 0, 32);", r(insn.operands[1])); + break; + + case PPC_INST_DCBZL: + print("\tmemset(base + (("); + if (insn.operands[0] != 0) + print("{}.u32 + ", r(insn.operands[0])); + println("{}.u32) & ~127), 0, 128);", r(insn.operands[1])); + break; + + case PPC_INST_DIVD: + println("\t{}.s64 = {}.s64 / {}.s64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + break; + + case PPC_INST_DIVDU: + println("\t{}.u64 = {}.u64 / {}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_DIVW: + println("\t{}.s32 = {}.s32 / {}.s32;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_DIVWU: + println("\t{}.u32 = {}.u32 / {}.u32;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_EIEIO: + // no op + break; + + case PPC_INST_EXTSB: + println("\t{}.s64 = {}.s8;", r(insn.operands[0]), r(insn.operands[1])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_EXTSH: + println("\t{}.s64 = {}.s16;", r(insn.operands[0]), r(insn.operands[1])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_EXTSW: + println("\t{}.s64 = {}.s32;", r(insn.operands[0]), r(insn.operands[1])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_FABS: + printSetFlushMode(false); + println("\t{}.u64 = {}.u64 & ~0x8000000000000000;", f(insn.operands[0]), f(insn.operands[1])); + break; + + case PPC_INST_FADD: + printSetFlushMode(false); + println("\t{}.f64 = {}.f64 + {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); + break; + + case PPC_INST_FADDS: + printSetFlushMode(false); + println("\t{}.f64 = double(float({}.f64 + {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); + break; + + case PPC_INST_FCFID: + printSetFlushMode(false); + println("\t{}.f64 = double({}.s64);", f(insn.operands[0]), f(insn.operands[1])); + break; + + case PPC_INST_FCMPU: + printSetFlushMode(false); + println("\t{}.compare({}.f64, {}.f64);", cr(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); + break; + + case PPC_INST_FCTID: + printSetFlushMode(false); + println("\t{}.s64 = ({}.f64 > double(LLONG_MAX)) ? LLONG_MAX : _mm_cvtsd_si64(_mm_load_sd(&{}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[1])); + break; + + case PPC_INST_FCTIDZ: + printSetFlushMode(false); + println("\t{}.s64 = ({}.f64 > double(LLONG_MAX)) ? LLONG_MAX : _mm_cvttsd_si64(_mm_load_sd(&{}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[1])); + break; + + case PPC_INST_FCTIWZ: + printSetFlushMode(false); + println("\t{}.s64 = ({}.f64 > double(INT_MAX)) ? INT_MAX : _mm_cvttsd_si32(_mm_load_sd(&{}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[1])); + break; + + case PPC_INST_FDIV: + printSetFlushMode(false); + println("\t{}.f64 = {}.f64 / {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); + break; + + case PPC_INST_FDIVS: + printSetFlushMode(false); + println("\t{}.f64 = double(float({}.f64 / {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); + break; + + case PPC_INST_FMADD: + printSetFlushMode(false); + println("\t{}.f64 = {}.f64 * {}.f64 + {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); + break; + + case PPC_INST_FMADDS: + printSetFlushMode(false); + println("\t{}.f64 = double(float({}.f64 * {}.f64 + {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); + break; + + case PPC_INST_FMR: + printSetFlushMode(false); + println("\t{}.f64 = {}.f64;", f(insn.operands[0]), f(insn.operands[1])); + break; + + case PPC_INST_FMSUB: + printSetFlushMode(false); + println("\t{}.f64 = {}.f64 * {}.f64 - {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); + break; + + case PPC_INST_FMSUBS: + printSetFlushMode(false); + println("\t{}.f64 = double(float({}.f64 * {}.f64 - {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); + break; + + case PPC_INST_FMUL: + printSetFlushMode(false); + println("\t{}.f64 = {}.f64 * {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); + break; + + case PPC_INST_FMULS: + printSetFlushMode(false); + println("\t{}.f64 = double(float({}.f64 * {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); + break; + + case PPC_INST_FNABS: + printSetFlushMode(false); + println("\t{}.u64 = {}.u64 | 0x8000000000000000;", f(insn.operands[0]), f(insn.operands[1])); + break; + + case PPC_INST_FNEG: + printSetFlushMode(false); + println("\t{}.u64 = {}.u64 ^ 0x8000000000000000;", f(insn.operands[0]), f(insn.operands[1])); + break; + + case PPC_INST_FNMADDS: + printSetFlushMode(false); + println("\t{}.f64 = double(float(-({}.f64 * {}.f64 + {}.f64)));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); + break; + + case PPC_INST_FNMSUB: + printSetFlushMode(false); + println("\t{}.f64 = -({}.f64 * {}.f64 - {}.f64);", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); + break; + + case PPC_INST_FNMSUBS: + printSetFlushMode(false); + println("\t{}.f64 = double(float(-({}.f64 * {}.f64 - {}.f64)));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); + break; + + case PPC_INST_FRES: + printSetFlushMode(false); + println("\t{}.f64 = float(1.0 / {}.f64);", f(insn.operands[0]), f(insn.operands[1])); + break; + + case PPC_INST_FRSP: + printSetFlushMode(false); + println("\t{}.f64 = double(float({}.f64));", f(insn.operands[0]), f(insn.operands[1])); + break; + + case PPC_INST_FRSQRTE: + printSetFlushMode(false); // Ensure standard FPU mode + // Uses SSE reciprocal square root estimate instruction _mm_rsqrt_ss + println("\t{{"); + println("\t\t__m128 val_pd = _mm_load_sd(&{}.f64);", f(insn.operands[1])); // Load double + println("\t\t__m128 val_ss = _mm_cvtpd_ps(val_pd);"); // Convert to single + println("\t\t__m128 rsqrt_est_ss = _mm_rsqrt_ss(val_ss);"); // Estimate (single) + println("\t\t__m128 result_pd = _mm_cvtps_pd(rsqrt_est_ss);"); // Convert back to double + println("\t\t_mm_store_sd(&{}.f64, result_pd);", f(insn.operands[0])); // Store result + println("\t}}"); + // FRSQRTE does not typically set FPSCR bits, but check PDF if needed. + break; + + case PPC_INST_FSEL: + printSetFlushMode(false); + println("\t{}.f64 = {}.f64 >= 0.0 ? {}.f64 : {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2]), f(insn.operands[3])); + break; + + case PPC_INST_FSQRT: + printSetFlushMode(false); + println("\t{}.f64 = sqrt({}.f64);", f(insn.operands[0]), f(insn.operands[1])); + break; + + case PPC_INST_FSQRTS: + printSetFlushMode(false); + println("\t{}.f64 = double(float(sqrt({}.f64)));", f(insn.operands[0]), f(insn.operands[1])); + break; + + case PPC_INST_FSUB: + printSetFlushMode(false); + println("\t{}.f64 = {}.f64 - {}.f64;", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); + break; + + case PPC_INST_FSUBS: + printSetFlushMode(false); + println("\t{}.f64 = double(float({}.f64 - {}.f64));", f(insn.operands[0]), f(insn.operands[1]), f(insn.operands[2])); + break; + + case PPC_INST_LBZ: + print("\t{}.u64 = PPC_LOAD_U8(", r(insn.operands[0])); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{});", int32_t(insn.operands[1])); + break; + + case PPC_INST_LBZU: + println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); + println("\t{}.u64 = PPC_LOAD_U8({});", r(insn.operands[0]), ea()); + println("\t{}.u32 = {};", r(insn.operands[2]), ea()); + break; + + case PPC_INST_LBZX: + print("\t{}.u64 = PPC_LOAD_U8(", r(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32);", r(insn.operands[2])); + break; + + case PPC_INST_LD: + print("\t{}.u64 = PPC_LOAD_U64(", r(insn.operands[0])); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{});", int32_t(insn.operands[1])); + break; + + case PPC_INST_LDARX: + print("\t{}.u64 = *(uint64_t*)(base + ", reserved()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32);", r(insn.operands[2])); + println("\t{}.u64 = __builtin_bswap64({}.u64);", r(insn.operands[0]), reserved()); + break; + + case PPC_INST_LDU: + println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); + println("\t{}.u64 = PPC_LOAD_U64({});", r(insn.operands[0]), ea()); + println("\t{}.u32 = {};", r(insn.operands[2]), ea()); + break; + + case PPC_INST_LDX: + print("\t{}.u64 = PPC_LOAD_U64(", r(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32);", r(insn.operands[2])); + break; + + case PPC_INST_LFD: + printSetFlushMode(false); + print("\t{}.u64 = PPC_LOAD_U64(", f(insn.operands[0])); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{});", int32_t(insn.operands[1])); + break; + + case PPC_INST_LFDX: + printSetFlushMode(false); + print("\t{}.u64 = PPC_LOAD_U64(", f(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32);", r(insn.operands[2])); + break; + + case PPC_INST_LFS: + printSetFlushMode(false); + print("\t{}.u32 = PPC_LOAD_U32(", temp()); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{});", int32_t(insn.operands[1])); + println("\t{}.f64 = double({}.f32);", f(insn.operands[0]), temp()); + break; + + case PPC_INST_LFSX: + printSetFlushMode(false); + print("\t{}.u32 = PPC_LOAD_U32(", temp()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32);", r(insn.operands[2])); + println("\t{}.f64 = double({}.f32);", f(insn.operands[0]), temp()); + break; + + case PPC_INST_LHA: + print("\t{}.s64 = int16_t(PPC_LOAD_U16(", r(insn.operands[0])); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{}));", int32_t(insn.operands[1])); + break; + + case PPC_INST_LHAX: + print("\t{}.s64 = int16_t(PPC_LOAD_U16(", r(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32));", r(insn.operands[2])); + break; + + case PPC_INST_LHZ: + print("\t{}.u64 = PPC_LOAD_U16(", r(insn.operands[0])); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{});", int32_t(insn.operands[1])); + break; + + case PPC_INST_LHZX: + print("\t{}.u64 = PPC_LOAD_U16(", r(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32);", r(insn.operands[2])); + break; + + case PPC_INST_LI: + println("\t{}.s64 = {};", r(insn.operands[0]), int32_t(insn.operands[1])); + break; + + case PPC_INST_LIS: + println("\t{}.s64 = {};", r(insn.operands[0]), int32_t(insn.operands[1] << 16)); + break; + + case PPC_INST_LVEWX: + case PPC_INST_LVEWX128: + case PPC_INST_LVX: + case PPC_INST_LVX128: + // NOTE: for endian swapping, we reverse the whole vector instead of individual elements. + // this is accounted for in every instruction (eg. dp3 sums yzw instead of xyz) + print("\t_mm_store_si128((__m128i*){}.u8, _mm_shuffle_epi8(_mm_load_si128((__m128i*)(base + ((", v(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32) & ~0xF))), _mm_load_si128((__m128i*)VectorMaskL)));", r(insn.operands[2])); + break; + + case PPC_INST_LVLX: + case PPC_INST_LVLX128: + print("\t{}.u32 = ", temp()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32;", r(insn.operands[2])); + println("\t_mm_store_si128((__m128i*){}.u8, _mm_shuffle_epi8(_mm_load_si128((__m128i*)(base + ({}.u32 & ~0xF))), _mm_load_si128((__m128i*)&VectorMaskL[({}.u32 & 0xF) * 16])));", v(insn.operands[0]), temp(), temp()); + break; + + case PPC_INST_LVRX: + case PPC_INST_LVRX128: + print("\t{}.u32 = ", temp()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32;", r(insn.operands[2])); + println("\t_mm_store_si128((__m128i*){}.u8, {}.u32 & 0xF ? _mm_shuffle_epi8(_mm_load_si128((__m128i*)(base + ({}.u32 & ~0xF))), _mm_load_si128((__m128i*)&VectorMaskR[({}.u32 & 0xF) * 16])) : _mm_setzero_si128());", v(insn.operands[0]), temp(), temp(), temp()); + break; + + case PPC_INST_LVSL: + print("\t{}.u32 = ", temp()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32;", r(insn.operands[2])); + println("\t_mm_store_si128((__m128i*){}.u8, _mm_load_si128((__m128i*)&VectorShiftTableL[({}.u32 & 0xF) * 16]));", v(insn.operands[0]), temp()); + break; + + case PPC_INST_LVSR: + print("\t{}.u32 = ", temp()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32;", r(insn.operands[2])); + println("\t_mm_store_si128((__m128i*){}.u8, _mm_load_si128((__m128i*)&VectorShiftTableR[({}.u32 & 0xF) * 16]));", v(insn.operands[0]), temp()); + break; + + case PPC_INST_LWA: + print("\t{}.s64 = int32_t(PPC_LOAD_U32(", r(insn.operands[0])); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{}));", int32_t(insn.operands[1])); + break; + + case PPC_INST_LWARX: + print("\t{}.u32 = *(uint32_t*)(base + ", reserved()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32);", r(insn.operands[2])); + println("\t{}.u64 = __builtin_bswap32({}.u32);", r(insn.operands[0]), reserved()); + break; + + case PPC_INST_LWAX: + print("\t{}.s64 = int32_t(PPC_LOAD_U32(", r(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32));", r(insn.operands[2])); + break; + + case PPC_INST_LWBRX: + print("\t{}.u64 = __builtin_bswap32(PPC_LOAD_U32(", r(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32));", r(insn.operands[2])); + break; + + case PPC_INST_LWSYNC: + // no op + break; + + case PPC_INST_LWZ: + print("\t{}.u64 = PPC_LOAD_U32(", r(insn.operands[0])); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{});", int32_t(insn.operands[1])); + break; + + case PPC_INST_LWZU: + println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); + println("\t{}.u64 = PPC_LOAD_U32({});", r(insn.operands[0]), ea()); + println("\t{}.u32 = {};", r(insn.operands[2]), ea()); + break; + + case PPC_INST_LWZX: + print("\t{}.u64 = PPC_LOAD_U32(", r(insn.operands[0])); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32);", r(insn.operands[2])); + break; + + case PPC_INST_MFCR: + for (size_t i = 0; i < 32; i++) + { + constexpr std::string_view fields[] = { "lt", "gt", "eq", "so" }; + println("\t{}.u64 {}= {}.{} ? 0x{:X} : 0;", r(insn.operands[0]), i == 0 ? "" : "|", cr(i / 4), fields[i % 4], 1u << (31 - i)); + } + break; + + case PPC_INST_MFFS: + println("\t{}.u64 = ctx.fpscr.loadFromHost();", r(insn.operands[0])); + break; + + case PPC_INST_MFLR: + if (!config.skipLr) + println("\t{}.u64 = ctx.lr;", r(insn.operands[0])); + break; + + case PPC_INST_MFMSR: + if (!config.skipMsr) + println("\t{}.u64 = ctx.msr;", r(insn.operands[0])); + break; + + case PPC_INST_MFOCRF: + // TODO: don't hardcode to cr6 + println("\t{}.u64 = ({}.lt << 7) | ({}.gt << 6) | ({}.eq << 5) | ({}.so << 4);", r(insn.operands[0]), cr(6), cr(6), cr(6), cr(6)); + break; + + case PPC_INST_MFTB: + println("\t{}.u64 = __rdtsc();", r(insn.operands[0])); + break; + + case PPC_INST_MR: + println("\t{}.u64 = {}.u64;", r(insn.operands[0]), r(insn.operands[1])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_MTCR: + for (size_t i = 0; i < 32; i++) + { + constexpr std::string_view fields[] = { "lt", "gt", "eq", "so" }; + println("\t{}.{} = ({}.u32 & 0x{:X}) != 0;", cr(i / 4), fields[i % 4], r(insn.operands[0]), 1u << (31 - i)); + } + break; + + case PPC_INST_MTCTR: + println("\t{}.u64 = {}.u64;", ctr(), r(insn.operands[0])); + break; + + case PPC_INST_MTFSF: + println("\tctx.fpscr.storeFromGuest({}.u32);", f(insn.operands[1])); + break; + + case PPC_INST_MTLR: + if (!config.skipLr) + println("\tctx.lr = {}.u64;", r(insn.operands[0])); + break; + + case PPC_INST_MTMSRD: + if (!config.skipMsr) + println("\tctx.msr = ({}.u32 & 0x8020) | (ctx.msr & ~0x8020);", r(insn.operands[0])); + break; + + case PPC_INST_MTXER: + println("\t{}.so = ({}.u64 & 0x80000000) != 0;", xer(), r(insn.operands[0])); + println("\t{}.ov = ({}.u64 & 0x40000000) != 0;", xer(), r(insn.operands[0])); + println("\t{}.ca = ({}.u64 & 0x20000000) != 0;", xer(), r(insn.operands[0])); + break; + + case PPC_INST_MULHW: + println("\t{}.s64 = (int64_t({}.s32) * int64_t({}.s32)) >> 32;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + break; + + case PPC_INST_MULHWU: + println("\t{}.u64 = (uint64_t({}.u32) * uint64_t({}.u32)) >> 32;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_MULLD: + println("\t{}.s64 = {}.s64 * {}.s64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + break; + + case PPC_INST_MULHD: + println("\t{}.s64 = ((__int128_t){}.s64 * (__int128_t){}.s64) >> 64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s64, 0, {});", cr(0), r(insn.operands[0]), xer()); // Check if CR0 comparison uses s64 + break; + + case PPC_INST_MULHDU: + println("\t{}.u64 = ((__uint128_t){}.u64 * (__uint128_t){}.u64) >> 64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s64, 0, {});", cr(0), r(insn.operands[0]), xer()); // Check if CR0 comparison uses s64 or u64 + break; + + case PPC_INST_MULLI: + println("\t{}.s64 = {}.s64 * {};", r(insn.operands[0]), r(insn.operands[1]), int32_t(insn.operands[2])); + break; + + case PPC_INST_MULLHWU: // Verify this ID exists + println("\t{}.u64 = (uint32_t)(({}.u64 & 0xFFFF) * ({}.u64 & 0xFFFF));", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_MULLW: + println("\t{}.s64 = int64_t({}.s32) * int64_t({}.s32);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_NAND: + println("\t{}.u64 = ~({}.u64 & {}.u64);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + break; + + case PPC_INST_NEG: + println("\t{}.s64 = -{}.s64;", r(insn.operands[0]), r(insn.operands[1])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_NOP: + // no op + break; + + case PPC_INST_VNOR128: + printSetFlushMode(true); + println("\t{{"); + println("\t\t__m128i vra = _mm_load_si128((__m128i*){}.u8);", v(insn.operands[1])); // Load VRA + println("\t\t__m128i vrb = _mm_load_si128((__m128i*){}.u8);", v(insn.operands[2])); // Load VRB + println("\t\t__m128i or_result = _mm_or_si128(vra, vrb);"); // VRA | VRB + // Invert bits using XOR with all ones (~(A|B)) + println("\t\t__m128i all_ones = _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128());"); + println("\t\t__m128i nor_result = _mm_xor_si128(or_result, all_ones);"); + println("\t\t_mm_store_si128((__m128i*){}.u8, nor_result);", v(insn.operands[0])); // Store VRT + println("\t}}"); + break; + + case PPC_INST_NOR: + println("\t{}.u64 = ~({}.u64 | {}.u64);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + break; + + case PPC_INST_NOT: + println("\t{}.u64 = ~{}.u64;", r(insn.operands[0]), r(insn.operands[1])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_OR: + println("\t{}.u64 = {}.u64 | {}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_ORC: + println("\t{}.u64 = {}.u64 | ~{}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + break; + + case PPC_INST_ORI: + println("\t{}.u64 = {}.u64 | {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); + break; + + case PPC_INST_ORIS: + println("\t{}.u64 = {}.u64 | {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2] << 16); + break; + + case PPC_INST_RLDICL: + println("\t{}.u64 = __builtin_rotateleft64({}.u64, {}) & 0x{:X};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2], ComputeMask(insn.operands[3], 63)); + break; + + case PPC_INST_RLDICR: + println("\t{}.u64 = __builtin_rotateleft64({}.u64, {}) & 0x{:X};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2], ComputeMask(0, insn.operands[3])); + break; + + case PPC_INST_RLDIMI: + { + const uint64_t mask = ComputeMask(insn.operands[3], ~insn.operands[2]); + println("\t{}.u64 = (__builtin_rotateleft64({}.u64, {}) & 0x{:X}) | ({}.u64 & 0x{:X});", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2], mask, r(insn.operands[0]), ~mask); + break; + } + + case PPC_INST_RLWIMI: + { + const uint64_t mask = ComputeMask(insn.operands[3] + 32, insn.operands[4] + 32); + println("\t{}.u64 = (__builtin_rotateleft32({}.u32, {}) & 0x{:X}) | ({}.u64 & 0x{:X});", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2], mask, r(insn.operands[0]), ~mask); + break; + } + + case PPC_INST_RLWINM: + println("\t{}.u64 = __builtin_rotateleft64({}.u32 | ({}.u64 << 32), {}) & 0x{:X};", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[1]), insn.operands[2], ComputeMask(insn.operands[3] + 32, insn.operands[4] + 32)); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_ROTLDI: + println("\t{}.u64 = __builtin_rotateleft64({}.u64, {});", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); + break; + + case PPC_INST_ROTLW: + println("\t{}.u64 = __builtin_rotateleft32({}.u32, {}.u8 & 0x1F);", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + break; + + case PPC_INST_ROTLWI: + println("\t{}.u64 = __builtin_rotateleft32({}.u32, {});", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_SLD: + println("\t{}.u64 = {}.u8 & 0x40 ? 0 : ({}.u64 << ({}.u8 & 0x7F));", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[2])); + break; + + case PPC_INST_SLW: + println("\t{}.u64 = {}.u8 & 0x20 ? 0 : ({}.u32 << ({}.u8 & 0x3F));", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_SRAD: + println("\t{}.u64 = {}.u64 & 0x7F;", temp(), r(insn.operands[2])); + println("\tif ({}.u64 > 0x3F) {}.u64 = 0x3F;", temp(), temp()); + println("\t{}.ca = ({}.s64 < 0) & ((({}.s64 >> {}.u64) << {}.u64) != {}.s64);", xer(), r(insn.operands[1]), r(insn.operands[1]), temp(), temp(), r(insn.operands[1])); + println("\t{}.s64 = {}.s64 >> {}.u64;", r(insn.operands[0]), r(insn.operands[1]), temp()); + break; + + case PPC_INST_SRADI: + if (insn.operands[2] != 0) + { + println("\t{}.ca = ({}.s64 < 0) & (({}.u64 & 0x{:X}) != 0);", xer(), r(insn.operands[1]), r(insn.operands[1]), ComputeMask(64 - insn.operands[2], 63)); + println("\t{}.s64 = {}.s64 >> {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); + } + else + { + println("\t{}.ca = 0;", xer()); + println("\t{}.s64 = {}.s64;", r(insn.operands[0]), r(insn.operands[1])); + } + break; + + case PPC_INST_SRAW: + println("\t{}.u32 = {}.u32 & 0x3F;", temp(), r(insn.operands[2])); + println("\tif ({}.u32 > 0x1F) {}.u32 = 0x1F;", temp(), temp()); + println("\t{}.ca = ({}.s32 < 0) & ((({}.s32 >> {}.u32) << {}.u32) != {}.s32);", xer(), r(insn.operands[1]), r(insn.operands[1]), temp(), temp(), r(insn.operands[1])); + println("\t{}.s64 = {}.s32 >> {}.u32;", r(insn.operands[0]), r(insn.operands[1]), temp()); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_SRAWI: + if (insn.operands[2] != 0) + { + println("\t{}.ca = ({}.s32 < 0) & (({}.u32 & 0x{:X}) != 0);", xer(), r(insn.operands[1]), r(insn.operands[1]), ComputeMask(64 - insn.operands[2], 63)); + println("\t{}.s64 = {}.s32 >> {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); + } + else + { + println("\t{}.ca = 0;", xer()); + println("\t{}.s64 = {}.s32;", r(insn.operands[0]), r(insn.operands[1])); + } + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_SRD: + println("\t{}.u64 = {}.u8 & 0x40 ? 0 : ({}.u64 >> ({}.u8 & 0x7F));", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[2])); + break; + + case PPC_INST_SRW: + println("\t{}.u64 = {}.u8 & 0x20 ? 0 : ({}.u32 >> ({}.u8 & 0x3F));", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_STB: + print("{}", mmioStore() ? "\tPPC_MM_STORE_U8(" : "\tPPC_STORE_U8("); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{}, {}.u8);", int32_t(insn.operands[1]), r(insn.operands[0])); + break; + + case PPC_INST_STBU: + println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); + println("\tPPC_STORE_U8({}, {}.u8);", ea(), r(insn.operands[0])); + println("\t{}.u32 = {};", r(insn.operands[2]), ea()); + break; + + case PPC_INST_STBX: + print("{}", mmioStore() ? "\tPPC_MM_STORE_U8(" : "\tPPC_STORE_U8("); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32, {}.u8);", r(insn.operands[2]), r(insn.operands[0])); + break; + + case PPC_INST_STD: + print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64("); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{}, {}.u64);", int32_t(insn.operands[1]), r(insn.operands[0])); + break; + + case PPC_INST_STDCX: + println("\t{}.lt = 0;", cr(0)); + println("\t{}.gt = 0;", cr(0)); + print("\t{}.eq = __sync_bool_compare_and_swap(reinterpret_cast(base + ", cr(0)); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32), {}.s64, __builtin_bswap64({}.s64));", r(insn.operands[2]), reserved(), r(insn.operands[0])); + println("\t{}.so = {}.so;", cr(0), xer()); + break; + + case PPC_INST_STDU: + println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); + println("\tPPC_STORE_U64({}, {}.u64);", ea(), r(insn.operands[0])); + println("\t{}.u32 = {};", r(insn.operands[2]), ea()); + break; + + case PPC_INST_STDX: + print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64("); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32, {}.u64);", r(insn.operands[2]), r(insn.operands[0])); + break; + + case PPC_INST_STFD: + printSetFlushMode(false); + print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64("); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{}, {}.u64);", int32_t(insn.operands[1]), f(insn.operands[0])); + break; + + case PPC_INST_STFDX: + printSetFlushMode(false); + print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64("); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32, {}.u64);", r(insn.operands[2]), f(insn.operands[0])); + break; + + case PPC_INST_STFSU: + printSetFlushMode(false); + println("\t{}.f32 = float({}.f64);", temp(), f(insn.operands[0])); // Convert FRS (double) to float in temp + println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); // Calculate EA = RA + D + println("\tPPC_STORE_U32({}, {}.u32);", ea(), temp()); // Store float bits + println("\t{}.u32 = {};", r(insn.operands[2]), ea()); // Update RA with EA + break; + + case PPC_INST_STFIWX: + printSetFlushMode(false); + print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32, {}.u32);", r(insn.operands[2]), f(insn.operands[0])); + break; + + case PPC_INST_STFS: + printSetFlushMode(false); + println("\t{}.f32 = float({}.f64);", temp(), f(insn.operands[0])); + print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{}, {}.u32);", int32_t(insn.operands[1]), temp()); + break; + + case PPC_INST_STFSX: + printSetFlushMode(false); + println("\t{}.f32 = float({}.f64);", temp(), f(insn.operands[0])); + print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32, {}.u32);", r(insn.operands[2]), temp()); + break; + + case PPC_INST_STH: + print("{}", mmioStore() ? "\tPPC_MM_STORE_U16(" : "\tPPC_STORE_U16("); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{}, {}.u16);", int32_t(insn.operands[1]), r(insn.operands[0])); + break; + + case PPC_INST_STHBRX: + print("{}", mmioStore() ? "\tPPC_MM_STORE_U16(" : "\tPPC_STORE_U16("); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32, __builtin_bswap16({}.u16));", r(insn.operands[2]), r(insn.operands[0])); + break; + + case PPC_INST_STHX: + print("{}", mmioStore() ? "\tPPC_MM_STORE_U16(" : "\tPPC_STORE_U16("); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32, {}.u16);", r(insn.operands[2]), r(insn.operands[0])); + break; + + case PPC_INST_STVEHX: + // TODO: vectorize + // NOTE: accounting for the full vector reversal here + print("\t{} = (", ea()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32) & ~0x1;", r(insn.operands[2])); + println("\tPPC_STORE_U16(ea, {}.u16[7 - (({} & 0xF) >> 1)]);", v(insn.operands[0]), ea()); + break; + + case PPC_INST_STVEWX: + case PPC_INST_STVEWX128: + // TODO: vectorize + // NOTE: accounting for the full vector reversal here + print("\t{} = (", ea()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32) & ~0x3;", r(insn.operands[2])); + println("\tPPC_STORE_U32(ea, {}.u32[3 - (({} & 0xF) >> 2)]);", v(insn.operands[0]), ea()); + break; + + case PPC_INST_STVLX: + case PPC_INST_STVLX128: + // TODO: vectorize + // NOTE: accounting for the full vector reversal here + print("\t{} = ", ea()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32;", r(insn.operands[2])); + + println("\tfor (size_t i = 0; i < (16 - ({} & 0xF)); i++)", ea()); + println("\t\tPPC_STORE_U8({} + i, {}.u8[15 - i]);", ea(), v(insn.operands[0])); + break; + + case PPC_INST_STVRX: + case PPC_INST_STVRX128: + // TODO: vectorize + // NOTE: accounting for the full vector reversal here + print("\t{} = ", ea()); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32;", r(insn.operands[2])); + + println("\tfor (size_t i = 0; i < ({} & 0xF); i++)", ea()); + println("\t\tPPC_STORE_U8({} - i - 1, {}.u8[i]);", ea(), v(insn.operands[0])); + break; + + case PPC_INST_STVX: + case PPC_INST_STVX128: + print("\t_mm_store_si128((__m128i*)(base + (("); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32) & ~0xF)), _mm_shuffle_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*)VectorMaskL)));", r(insn.operands[2]), v(insn.operands[0])); + break; + + case PPC_INST_STW: + print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); + if (insn.operands[2] != 0) + print("{}.u32 + ", r(insn.operands[2])); + println("{}, {}.u32);", int32_t(insn.operands[1]), r(insn.operands[0])); + break; + + case PPC_INST_STWBRX: + print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32, __builtin_bswap32({}.u32));", r(insn.operands[2]), r(insn.operands[0])); + break; + + case PPC_INST_STWCX: + println("\t{}.lt = 0;", cr(0)); + println("\t{}.gt = 0;", cr(0)); + print("\t{}.eq = __sync_bool_compare_and_swap(reinterpret_cast(base + ", cr(0)); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32), {}.s32, __builtin_bswap32({}.s32));", r(insn.operands[2]), reserved(), r(insn.operands[0])); + println("\t{}.so = {}.so;", cr(0), xer()); + break; + + case PPC_INST_STWU: + println("\t{} = {} + {}.u32;", ea(), int32_t(insn.operands[1]), r(insn.operands[2])); + println("\tPPC_STORE_U32({}, {}.u32);", ea(), r(insn.operands[0])); + println("\t{}.u32 = {};", r(insn.operands[2]), ea()); + break; + + case PPC_INST_STWUX: + println("\t{} = {}.u32 + {}.u32;", ea(), r(insn.operands[1]), r(insn.operands[2])); + println("\tPPC_STORE_U32({}, {}.u32);", ea(), r(insn.operands[0])); + println("\t{}.u32 = {};", r(insn.operands[1]), ea()); + break; + + case PPC_INST_STWX: + print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32("); + if (insn.operands[1] != 0) + print("{}.u32 + ", r(insn.operands[1])); + println("{}.u32, {}.u32);", r(insn.operands[2]), r(insn.operands[0])); + break; + + case PPC_INST_SUBF: + println("\t{}.s64 = {}.s64 - {}.s64;", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_SUBFC: + println("\t{}.ca = {}.u32 >= {}.u32;", xer(), r(insn.operands[2]), r(insn.operands[1])); + println("\t{}.s64 = {}.s64 - {}.s64;", r(insn.operands[0]), r(insn.operands[2]), r(insn.operands[1])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_SUBFE: + println("\t{}.u8 = (~{}.u32 + {}.u32 < ~{}.u32) | (~{}.u32 + {}.u32 + {}.ca < {}.ca);", temp(), r(insn.operands[1]), r(insn.operands[2]), r(insn.operands[1]), r(insn.operands[1]), r(insn.operands[2]), xer(), xer()); + println("\t{}.u64 = ~{}.u64 + {}.u64 + {}.ca;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]), xer()); + println("\t{}.ca = {}.u8;", xer(), temp()); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_SUBFIC: + println("\t{}.ca = {}.u32 <= {};", xer(), r(insn.operands[1]), insn.operands[2]); + println("\t{}.s64 = {} - {}.s64;", r(insn.operands[0]), int32_t(insn.operands[2]), r(insn.operands[1])); + break; + + case PPC_INST_SYNC: + // no op + break; + + case PPC_INST_TDLGEI: + // no op + break; + + case PPC_INST_TDLLEI: + // no op + break; + + case PPC_INST_TWI: + // no op + break; + + case PPC_INST_TWLGEI: + // no op + break; + + case PPC_INST_TWLLEI: + // no op + break; + + case PPC_INST_VADDFP: + case PPC_INST_VADDFP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_add_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VADDSHS: + println("\t_mm_store_si128((__m128i*){}.s16, _mm_adds_epi16(_mm_load_si128((__m128i*){}.s16), _mm_load_si128((__m128i*){}.s16)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VADDUBM: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_add_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VADDUBS: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_adds_epu8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VADDUHM: + println("\t_mm_store_si128((__m128i*){}.u16, _mm_add_epi16(_mm_load_si128((__m128i*){}.u16), _mm_load_si128((__m128i*){}.u16)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VADDUWM: + println("\t_mm_store_si128((__m128i*){}.u32, _mm_add_epi32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VADDUWS: + println("\t_mm_store_si128((__m128i*){}.u32, _mm_adds_epu32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VAND: + case PPC_INST_VAND128: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_and_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VANDC: + printSetFlushMode(true); + // Computes VRA & ~VRB using _mm_andnot_si128(VRB, VRA) + println("\t_mm_store_si128((__m128i*){}.u8, _mm_andnot_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); // VRT, VRB, VRA + break; + + case PPC_INST_VANDC128: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_andnot_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); + break; + + case PPC_INST_VAVGSB: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_avg_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VAVGSH: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_avg_epi16(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VAVGUB: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_avg_epu8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VCTSXS: + case PPC_INST_VCFPSXWS128: + printSetFlushMode(true); + print("\t_mm_store_si128((__m128i*){}.s32, _mm_vctsxs(", v(insn.operands[0])); + if (insn.operands[2] != 0) + println("_mm_mul_ps(_mm_load_ps({}.f32), _mm_set1_ps({}))));", v(insn.operands[1]), 1u << insn.operands[2]); + else + println("_mm_load_ps({}.f32)));", v(insn.operands[1])); + break; + + case PPC_INST_VCFPUXWS128: // Or PPC_INST_VCTUXS if that's the ID used + printSetFlushMode(true); + println("\t{{"); + println("\t\t__m128 vrbf = _mm_load_ps({}.f32);", v(insn.operands[1])); // Load VRB floats + if (insn.operands[2] != 0) { // Check UIMM (operand 2) + // Scale VRB by 2^UIMM before converting + println("\t\tfloat scale = ldexpf(1.0f, {});", (int32_t)insn.operands[2]); // Calculate 2^UIMM + println("\t\t__m128 scale_ps = _mm_set1_ps(scale);"); + println("\t\tvrbf = _mm_mul_ps(vrbf, scale_ps);"); + } + // Use the helper function from ppc_context.h which handles conversion and saturation + println("\t\t__m128i result = _mm_vctuxs(vrbf);"); + println("\t\t_mm_store_si128((__m128i*){}.u32, result);", v(insn.operands[0])); // Store VRT + println("\t}}"); + break; + + case PPC_INST_VCFSX: + case PPC_INST_VCSXWFP128: + { + printSetFlushMode(true); + print("\t_mm_store_ps({}.f32, ", v(insn.operands[0])); + if (insn.operands[2] != 0) + { + const float value = ldexp(1.0f, -int32_t(insn.operands[2])); + println("_mm_mul_ps(_mm_cvtepi32_ps(_mm_load_si128((__m128i*){}.u32)), _mm_castsi128_ps(_mm_set1_epi32(int(0x{:X})))));", v(insn.operands[1]), *reinterpret_cast(&value)); + } + else + { + println("_mm_cvtepi32_ps(_mm_load_si128((__m128i*){}.u32)));", v(insn.operands[1])); + } + break; + } + + case PPC_INST_VCFUX: + case PPC_INST_VCUXWFP128: + { + printSetFlushMode(true); + print("\t_mm_store_ps({}.f32, ", v(insn.operands[0])); + if (insn.operands[2] != 0) + { + const float value = ldexp(1.0f, -int32_t(insn.operands[2])); + println("_mm_mul_ps(_mm_cvtepu32_ps_(_mm_load_si128((__m128i*){}.u32)), _mm_castsi128_ps(_mm_set1_epi32(int(0x{:X})))));", v(insn.operands[1]), *reinterpret_cast(&value)); + } + else + { + println("_mm_cvtepu32_ps_(_mm_load_si128((__m128i*){}.u32)));", v(insn.operands[1])); + } + break; + } + + case PPC_INST_VCMPBFP: + case PPC_INST_VCMPBFP128: + println("\t__builtin_debugtrap();"); + break; + + case PPC_INST_VCMPEQFP: + case PPC_INST_VCMPEQFP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_cmpeq_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.setFromMask(_mm_load_ps({}.f32), 0xF);", cr(6), v(insn.operands[0])); + break; + + case PPC_INST_VCMPEQUB: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_cmpeq_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.setFromMask(_mm_load_si128((__m128i*){}.u8), 0xFFFF);", cr(6), v(insn.operands[0])); + break; + + case PPC_INST_VCMPEQUW: + case PPC_INST_VCMPEQUW128: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_cmpeq_epi32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.setFromMask(_mm_load_ps({}.f32), 0xF);", cr(6), v(insn.operands[0])); + break; + + case PPC_INST_VCMPGEFP: + case PPC_INST_VCMPGEFP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_cmpge_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.setFromMask(_mm_load_ps({}.f32), 0xF);", cr(6), v(insn.operands[0])); + break; + + case PPC_INST_VCMPGTFP: + case PPC_INST_VCMPGTFP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_cmpgt_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.setFromMask(_mm_load_ps({}.f32), 0xF);", cr(6), v(insn.operands[0])); + break; + + case PPC_INST_VCMPGTUB: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_cmpgt_epu8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VCMPGTUH: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_cmpgt_epu16(_mm_load_si128((__m128i*){}.u16), _mm_load_si128((__m128i*){}.u16)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VEXPTEFP: + case PPC_INST_VEXPTEFP128: + // TODO: vectorize + printSetFlushMode(true); + for (size_t i = 0; i < 4; i++) + println("\t{}.f32[{}] = exp2f({}.f32[{}]);", v(insn.operands[0]), i, v(insn.operands[1]), i); + break; + + case PPC_INST_VLOGEFP: + case PPC_INST_VLOGEFP128: + // TODO: vectorize + printSetFlushMode(true); + for (size_t i = 0; i < 4; i++) + println("\t{}.f32[{}] = log2f({}.f32[{}]);", v(insn.operands[0]), i, v(insn.operands[1]), i); + break; + + case PPC_INST_VMADDCFP128: + case PPC_INST_VMADDFP: + case PPC_INST_VMADDFP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_add_ps(_mm_mul_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), v(insn.operands[3])); + break; + + case PPC_INST_VMAXFP: + case PPC_INST_VMAXFP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_max_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VMAXSW: + println("\t_mm_store_si128((__m128i*){}.u32, _mm_max_epi32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VMINFP: + case PPC_INST_VMINFP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_min_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VPKSWSS: + case PPC_INST_VPKSWSS128: // Or PPC_INST_VPKSWSS + printSetFlushMode(true); + println("\t_mm_store_si128((__m128i*){}.s16, _mm_packs_epi32(_mm_load_si128((__m128i*){}.s32), _mm_load_si128((__m128i*){}.s32)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); // VRT, VRA, VRB + break; + + case PPC_INST_VPKUWUS128: + printSetFlushMode(true); + println("\t{{"); + println("\t\t__m128i max_val = _mm_set1_epi32(0xFFFF);"); // Max value for unsigned 16-bit + println("\t\t__m128i vra = _mm_load_si128((__m128i*){}.u32);", v(insn.operands[2])); // Load VRA (operand 2) + println("\t\t__m128i vrb = _mm_load_si128((__m128i*){}.u32);", v(insn.operands[1])); // Load VRB (operand 1) + // Saturate VRA words (unsigned) [0, 65535] + println("\t\tvra = _mm_min_epu32(vra, max_val);"); + // Saturate VRB words (unsigned) [0, 65535] + println("\t\tvrb = _mm_min_epu32(vrb, max_val);"); + // Pack clamped words. _mm_packs_epi32 works correctly here because inputs are pre-clamped. + println("\t\t__m128i result = _mm_packs_epi32(vra, vrb);"); + println("\t\t_mm_store_si128((__m128i*){}.u16, result);", v(insn.operands[0])); // Store VRT (operand 0) + println("\t}}"); + break; + + case PPC_INST_VMRGHB: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_unpackhi_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); + break; + + case PPC_INST_VMRGHH: + println("\t_mm_store_si128((__m128i*){}.u16, _mm_unpackhi_epi16(_mm_load_si128((__m128i*){}.u16), _mm_load_si128((__m128i*){}.u16)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); + break; + + case PPC_INST_VMRGHW: + case PPC_INST_VMRGHW128: + println("\t_mm_store_si128((__m128i*){}.u32, _mm_unpackhi_epi32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); + break; + + case PPC_INST_VMRGLB: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_unpacklo_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); + break; + + case PPC_INST_VMRGLH: + println("\t_mm_store_si128((__m128i*){}.u16, _mm_unpacklo_epi16(_mm_load_si128((__m128i*){}.u16), _mm_load_si128((__m128i*){}.u16)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); + break; + + case PPC_INST_VMRGLW: + case PPC_INST_VMRGLW128: + println("\t_mm_store_si128((__m128i*){}.u32, _mm_unpacklo_epi32(_mm_load_si128((__m128i*){}.u32), _mm_load_si128((__m128i*){}.u32)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); + break; + + case PPC_INST_VMSUM3FP128: + // NOTE: accounting for full vector reversal here. should dot product yzw instead of xyz + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_dp_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32), 0xEF));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VMSUM4FP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_dp_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32), 0xFF));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VMULFP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_mul_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VNMSUBFP: + case PPC_INST_VNMSUBFP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_xor_ps(_mm_sub_ps(_mm_mul_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)), _mm_load_ps({}.f32)), _mm_castsi128_ps(_mm_set1_epi32(int(0x80000000)))));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), v(insn.operands[3])); + break; + + case PPC_INST_VOR: + case PPC_INST_VOR128: + print("\t_mm_store_si128((__m128i*){}.u8, ", v(insn.operands[0])); + + if (insn.operands[1] != insn.operands[2]) + println("_mm_or_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[1]), v(insn.operands[2])); + else + println("_mm_load_si128((__m128i*){}.u8));", v(insn.operands[1])); + + break; + + case PPC_INST_VPERM: + case PPC_INST_VPERM128: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_perm_epi8_(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), v(insn.operands[3])); + break; + + case PPC_INST_VPERMWI128: + { + // NOTE: accounting for full vector reversal here + uint32_t x = 3 - (insn.operands[2] & 0x3); + uint32_t y = 3 - ((insn.operands[2] >> 2) & 0x3); + uint32_t z = 3 - ((insn.operands[2] >> 4) & 0x3); + uint32_t w = 3 - ((insn.operands[2] >> 6) & 0x3); + uint32_t perm = x | (y << 2) | (z << 4) | (w << 6); + println("\t_mm_store_si128((__m128i*){}.u32, _mm_shuffle_epi32(_mm_load_si128((__m128i*){}.u32), 0x{:X}));", v(insn.operands[0]), v(insn.operands[1]), perm); + break; + } + + case PPC_INST_VPKD3D128: + // TODO: vectorize somehow? + // NOTE: handling vector reversal here too + printSetFlushMode(true); + switch (insn.operands[2]) + { + case 0: // D3D color + if (insn.operands[3] != 1 || insn.operands[4] != 3) + fmt::println("Unexpected D3D color pack instruction at {:X}", base); + + for (size_t i = 0; i < 4; i++) + { + constexpr size_t indices[] = { 3, 0, 1, 2 }; + println("\t{}.u32[{}] = 0x404000FF;", vTemp(), i); + println("\t{}.f32[{}] = {}.f32[{}] < 3.0f ? 3.0f : ({}.f32[{}] > {}.f32[{}] ? {}.f32[{}] : {}.f32[{}]);", vTemp(), i, v(insn.operands[1]), i, v(insn.operands[1]), i, vTemp(), i, vTemp(), i, v(insn.operands[1]), i); + println("\t{}.u32 {}= uint32_t({}.u8[{}]) << {};", temp(), i == 0 ? "" : "|", vTemp(), i * 4, indices[i] * 8); + } + println("\t{}.u32[3] = {}.u32;", v(insn.operands[0]), temp()); + break; + + default: + println("\t__builtin_debugtrap();"); + break; + } + break; + + case PPC_INST_VPKSHUS: + case PPC_INST_VPKSHUS128: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_packus_epi16(_mm_load_si128((__m128i*){}.s16), _mm_load_si128((__m128i*){}.s16)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); + break; + + case PPC_INST_VPKUHUS: + case PPC_INST_VPKUHUS128: // Or PPC_INST_VPKUHUS + printSetFlushMode(true); + // _mm_packus_epi16 performs unsigned saturation from signed 16-bit to unsigned 8-bit. + // This matches VPKUHUS behavior. + println("\t_mm_store_si128((__m128i*){}.u8, _mm_packus_epi16(_mm_load_si128((__m128i*){}.s16), _mm_load_si128((__m128i*){}.s16)));", v(insn.operands[0]), v(insn.operands[2]), v(insn.operands[1])); // VRT, VRA, VRB + break; + + case PPC_INST_VREFP: + case PPC_INST_VREFP128: + // TODO: see if we can use rcp safely + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_div_ps(_mm_set1_ps(1), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1])); + break; + + case PPC_INST_VRFIM: + case PPC_INST_VRFIM128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_round_ps(_mm_load_ps({}.f32), _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC));", v(insn.operands[0]), v(insn.operands[1])); + break; + + case PPC_INST_VRFIN: + case PPC_INST_VRFIN128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_round_ps(_mm_load_ps({}.f32), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC));", v(insn.operands[0]), v(insn.operands[1])); + break; + + case PPC_INST_VRFIZ: + case PPC_INST_VRFIZ128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_round_ps(_mm_load_ps({}.f32), _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC));", v(insn.operands[0]), v(insn.operands[1])); + break; + + case PPC_INST_VRLIMI128: + { + constexpr size_t shuffles[] = { _MM_SHUFFLE(3, 2, 1, 0), _MM_SHUFFLE(2, 1, 0, 3), _MM_SHUFFLE(1, 0, 3, 2), _MM_SHUFFLE(0, 3, 2, 1) }; + println("\t_mm_store_ps({}.f32, _mm_blend_ps(_mm_load_ps({}.f32), _mm_permute_ps(_mm_load_ps({}.f32), {}), {}));", v(insn.operands[0]), v(insn.operands[0]), v(insn.operands[1]), shuffles[insn.operands[3]], insn.operands[2]); + break; + } + + case PPC_INST_VRSQRTEFP: + case PPC_INST_VRSQRTEFP128: + // TODO: see if we can use rsqrt safely + // TODO: we can detect if the input is from a dot product and apply logic only on one value + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_div_ps(_mm_set1_ps(1), _mm_sqrt_ps(_mm_load_ps({}.f32))));", v(insn.operands[0]), v(insn.operands[1])); + break; + + case PPC_INST_VSEL: + case PPC_INST_VSEL128: // Or PPC_INST_VSEL + printSetFlushMode(true); + // VRT = (VRC sign bit set) ? VRB : VRA; + // _mm_blendv_epi8 uses the sign bit of the mask (VRC) to select bytes from VRB (if sign=1) or VRA (if sign=0) + println("\t_mm_store_si128((__m128i*){}.u8, _mm_blendv_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), v(insn.operands[3])); // VRT, VRA, VRB, VRC + break; + + case PPC_INST_VSLB: + // TODO: vectorize + for (size_t i = 0; i < 16; i++) + println("\t{}.u8[{}] = {}.u8[{}] << ({}.u8[{}] & 0x7);", v(insn.operands[0]), i, v(insn.operands[1]), i, v(insn.operands[2]), i); + break; + + case PPC_INST_VSLDOI: + case PPC_INST_VSLDOI128: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_alignr_epi8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8), {}));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]), 16 - insn.operands[3]); + break; + + case PPC_INST_VSLW: + case PPC_INST_VSLW128: + // TODO: vectorize, ensure endianness is correct + for (size_t i = 0; i < 4; i++) + println("\t{}.u32[{}] = {}.u32[{}] << ({}.u8[{}] & 0x1F);", v(insn.operands[0]), i, v(insn.operands[1]), i, v(insn.operands[2]), i * 4); + break; + + case PPC_INST_VSPLTB: + { + // NOTE: accounting for full vector reversal here + uint32_t perm = 15 - insn.operands[2]; + println("\t_mm_store_si128((__m128i*){}.u8, _mm_shuffle_epi8(_mm_load_si128((__m128i*){}.u8), _mm_set1_epi8(char(0x{:X}))));", v(insn.operands[0]), v(insn.operands[1]), perm); + break; + } + + case PPC_INST_VSPLTH: + { + // NOTE: accounting for full vector reversal here + uint32_t perm = 7 - insn.operands[2]; + perm = (perm * 2) | ((perm * 2 + 1) << 8); + println("\t_mm_store_si128((__m128i*){}.u16, _mm_shuffle_epi8(_mm_load_si128((__m128i*){}.u16), _mm_set1_epi16(short(0x{:X}))));", v(insn.operands[0]), v(insn.operands[1]), perm); + break; + } + + case PPC_INST_VSPLTISB: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_set1_epi8(char(0x{:X})));", v(insn.operands[0]), insn.operands[1]); + break; + + case PPC_INST_VSPLTISW: + case PPC_INST_VSPLTISW128: + println("\t_mm_store_si128((__m128i*){}.u32, _mm_set1_epi32(int(0x{:X})));", v(insn.operands[0]), insn.operands[1]); + break; + + case PPC_INST_VSPLTW: + case PPC_INST_VSPLTW128: + { + // NOTE: accounting for full vector reversal here + uint32_t perm = 3 - insn.operands[2]; + perm |= (perm << 2) | (perm << 4) | (perm << 6); + println("\t_mm_store_si128((__m128i*){}.u32, _mm_shuffle_epi32(_mm_load_si128((__m128i*){}.u32), 0x{:X}));", v(insn.operands[0]), v(insn.operands[1]), perm); + break; + } + + case PPC_INST_VSR: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_vsr(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VSRAW: + case PPC_INST_VSRAW128: + // TODO: vectorize, ensure endianness is correct + for (size_t i = 0; i < 4; i++) + println("\t{}.s32[{}] = {}.s32[{}] >> ({}.u8[{}] & 0x1F);", v(insn.operands[0]), i, v(insn.operands[1]), i, v(insn.operands[2]), i * 4); + break; + + case PPC_INST_VSRW: + case PPC_INST_VSRW128: + // TODO: vectorize, ensure endianness is correct + for (size_t i = 0; i < 4; i++) + println("\t{}.u32[{}] = {}.u32[{}] >> ({}.u8[{}] & 0x1F);", v(insn.operands[0]), i, v(insn.operands[1]), i, v(insn.operands[2]), i * 4); + break; + + case PPC_INST_VSUBFP: + case PPC_INST_VSUBFP128: + printSetFlushMode(true); + println("\t_mm_store_ps({}.f32, _mm_sub_ps(_mm_load_ps({}.f32), _mm_load_ps({}.f32)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VSUBSWS: + // TODO: vectorize + for (size_t i = 0; i < 4; i++) + { + println("\t{}.s64 = int64_t({}.s32[{}]) - int64_t({}.s32[{}]);", temp(), v(insn.operands[1]), i, v(insn.operands[2]), i); + println("\t{}.s32[{}] = {}.s64 > INT_MAX ? INT_MAX : {}.s64 < INT_MIN ? INT_MIN : {}.s64;", v(insn.operands[0]), i, temp(), temp(), temp()); + } + break; + + case PPC_INST_VSUBUBS: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_subs_epu8(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VSUBUHM: + println("\t_mm_store_si128((__m128i*){}.u8, _mm_sub_epi16(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2])); + break; + + case PPC_INST_VUPKD3D128: + // TODO: vectorize somehow? + // NOTE: handling vector reversal here too + switch (insn.operands[2] >> 2) + { + case 0: // D3D color + for (size_t i = 0; i < 4; i++) + { + constexpr size_t indices[] = { 3, 0, 1, 2 }; + println("\t{}.u32[{}] = {}.u8[{}] | 0x3F800000;", vTemp(), i, v(insn.operands[1]), indices[i]); + } + println("\t{} = {};", v(insn.operands[0]), vTemp()); + break; + + case 1: // 2 shorts + for (size_t i = 0; i < 2; i++) + { + println("\t{}.f32 = 3.0f;", temp()); + println("\t{}.s32 += {}.s16[{}];", temp(), v(insn.operands[1]), 1 - i); + println("\t{}.f32[{}] = {}.f32;", vTemp(), 3 - i, temp()); + } + println("\t{}.f32[1] = 0.0f;", vTemp()); + println("\t{}.f32[0] = 1.0f;", vTemp()); + println("\t{} = {};", v(insn.operands[0]), vTemp()); + break; + + default: + println("\t__builtin_debugtrap();"); + break; + } + break; + + case PPC_INST_VUPKHSB: + case PPC_INST_VUPKHSB128: + println("\t_mm_store_si128((__m128i*){}.s16, _mm_cvtepi8_epi16(_mm_unpackhi_epi64(_mm_load_si128((__m128i*){}.s8), _mm_load_si128((__m128i*){}.s8))));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[1])); + break; + + case PPC_INST_VUPKHSH: + case PPC_INST_VUPKHSH128: + println("\t_mm_store_si128((__m128i*){}.s32, _mm_cvtepi16_epi32(_mm_unpackhi_epi64(_mm_load_si128((__m128i*){}.s16), _mm_load_si128((__m128i*){}.s16))));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[1])); + break; + + case PPC_INST_VUPKLSB: + case PPC_INST_VUPKLSB128: + println("\t_mm_store_si128((__m128i*){}.s32, _mm_cvtepi8_epi16(_mm_load_si128((__m128i*){}.s16)));", v(insn.operands[0]), v(insn.operands[1])); + break; + + case PPC_INST_VUPKLSH: + case PPC_INST_VUPKLSH128: + println("\t_mm_store_si128((__m128i*){}.s32, _mm_cvtepi16_epi32(_mm_load_si128((__m128i*){}.s16)));", v(insn.operands[0]), v(insn.operands[1])); + break; + + case PPC_INST_VXOR: + case PPC_INST_VXOR128: + print("\t_mm_store_si128((__m128i*){}.u8, ", v(insn.operands[0])); + + if (insn.operands[1] != insn.operands[2]) + println("_mm_xor_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)));", v(insn.operands[1]), v(insn.operands[2])); + else + println("_mm_setzero_si128());"); + + break; + + case PPC_INST_XOR: + println("\t{}.u64 = {}.u64 ^ {}.u64;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2])); + if (strchr(insn.opcode->name, '.')) + println("\t{}.compare({}.s32, 0, {});", cr(0), r(insn.operands[0]), xer()); + break; + + case PPC_INST_XORI: + println("\t{}.u64 = {}.u64 ^ {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2]); + break; + + case PPC_INST_XORIS: + println("\t{}.u64 = {}.u64 ^ {};", r(insn.operands[0]), r(insn.operands[1]), insn.operands[2] << 16); + break; + + default: + return false; + } + +#if 1 + if (strchr(insn.opcode->name, '.')) + { + int lastLine = out.find_last_of('\n', out.size() - 2); + if (out.find("cr0", lastLine + 1) == std::string::npos && out.find("cr6", lastLine + 1) == std::string::npos) + fmt::println("{} at {:X} has RC bit enabled but no comparison was generated", insn.opcode->name, base); + } +#endif + + if (midAsmHook != config.midAsmHooks.end() && midAsmHook->second.afterInstruction) + printMidAsmHook(); + + return true; +} + +bool Recompiler::Recompile(const Function& fn) +{ + auto base = fn.base; + auto end = base + fn.size; + auto* data = (uint32_t*)image.Find(base); + + static std::unordered_set labels; + labels.clear(); + + for (size_t addr = base; addr < end; addr += 4) + { + const uint32_t instruction = ByteSwap(*(uint32_t*)((char*)data + addr - base)); + if (!PPC_BL(instruction)) + { + const size_t op = PPC_OP(instruction); + if (op == PPC_OP_B) + labels.emplace(addr + PPC_BI(instruction)); + else if (op == PPC_OP_BC) + labels.emplace(addr + PPC_BD(instruction)); + } + + auto switchTable = config.switchTables.find(addr); + if (switchTable != config.switchTables.end()) + { + for (auto label : switchTable->second.labels) + labels.emplace(label); + } + + auto midAsmHook = config.midAsmHooks.find(addr); + if (midAsmHook != config.midAsmHooks.end()) + { + if (midAsmHook->second.returnOnFalse || midAsmHook->second.returnOnTrue || + midAsmHook->second.jumpAddressOnFalse != NULL || midAsmHook->second.jumpAddressOnTrue != NULL) + { + print("extern bool "); + } + else + { + print("extern void "); + } + + print("{}(", midAsmHook->second.name); + for (auto& reg : midAsmHook->second.registers) + { + if (out.back() != '(') + out += ", "; + + switch (reg[0]) + { + case 'c': + if (reg == "ctr") + print("PPCRegister& ctr"); + else + print("PPCCRRegister& {}", reg); + break; + + case 'x': + print("PPCXERRegister& xer"); + break; + + case 'r': + print("PPCRegister& {}", reg); + break; + + case 'f': + if (reg == "fpscr") + print("PPCFPSCRRegister& fpscr"); + else + print("PPCRegister& {}", reg); + break; + + case 'v': + print("PPCVRegister& {}", reg); + break; + } + } + + println(");\n"); + + if (midAsmHook->second.jumpAddress != NULL) + labels.emplace(midAsmHook->second.jumpAddress); + if (midAsmHook->second.jumpAddressOnTrue != NULL) + labels.emplace(midAsmHook->second.jumpAddressOnTrue); + if (midAsmHook->second.jumpAddressOnFalse != NULL) + labels.emplace(midAsmHook->second.jumpAddressOnFalse); + } + } + + auto symbol = image.symbols.find(fn.base); + std::string name; + if (symbol != image.symbols.end()) + { + name = symbol->name; + } + else + { + name = fmt::format("sub_{}", fn.base); + } + +#ifdef XENON_RECOMP_USE_ALIAS + println("__attribute__((alias(\"__imp__{}\"))) PPC_WEAK_FUNC({});", name, name); +#endif + + println("PPC_FUNC_IMPL(__imp__{}) {{", name); + println("\tPPC_FUNC_PROLOGUE();"); + + auto switchTable = config.switchTables.end(); + bool allRecompiled = true; + CSRState csrState = CSRState::Unknown; + + // TODO: the printing scheme here is scuffed + RecompilerLocalVariables localVariables; + static std::string tempString; + tempString.clear(); + std::swap(out, tempString); + + ppc_insn insn; + while (base < end) + { + if (labels.find(base) != labels.end()) + { + println("loc_{:X}:", base); + + // Anyone could jump to this label so we wouldn't know what the CSR state would be. + csrState = CSRState::Unknown; + } + + if (switchTable == config.switchTables.end()) + switchTable = config.switchTables.find(base); + + ppc::Disassemble(data, 4, base, insn); + + if (insn.opcode == nullptr) + { + println("\t// {}", insn.op_str); +#if 1 + if (*data != 0) + fmt::println("Unable to decode instruction {:X} at {:X}", *data, base); +#endif + } + else + { + if (insn.opcode->id == PPC_INST_BCTR && (*(data - 1) == 0x07008038 || *(data - 1) == 0x00000060) && switchTable == config.switchTables.end()) + fmt::println("Found a switch jump table at {:X} with no switch table entry present", base); + + if (!Recompile(fn, base, insn, data, switchTable, localVariables, csrState)) + { + fmt::println("Unrecognized instruction at 0x{:X}: {}", base, insn.opcode->name); + allRecompiled = false; + } + } + + base += 4; + ++data; + } + +#if 0 + if (insn.opcode == nullptr || (insn.opcode->id != PPC_INST_B && insn.opcode->id != PPC_INST_BCTR && insn.opcode->id != PPC_INST_BLR)) + fmt::println("Function at {:X} ends prematurely with instruction {} at {:X}", fn.base, insn.opcode != nullptr ? insn.opcode->name : "INVALID", base - 4); +#endif + + println("}}\n"); + +#ifndef XENON_RECOMP_USE_ALIAS + println("PPC_WEAK_FUNC({}) {{", name); + println("\t__imp__{}(ctx, base);", name); + println("}}\n"); +#endif + + std::swap(out, tempString); + if (localVariables.ctr) + println("\tPPCRegister ctr{{}};"); + if (localVariables.xer) + println("\tPPCXERRegister xer{{}};"); + if (localVariables.reserved) + println("\tPPCRegister reserved{{}};"); + + for (size_t i = 0; i < 8; i++) + { + if (localVariables.cr[i]) + println("\tPPCCRRegister cr{}{{}};", i); + } + + for (size_t i = 0; i < 32; i++) + { + if (localVariables.r[i]) + println("\tPPCRegister r{}{{}};", i); + } + + for (size_t i = 0; i < 32; i++) + { + if (localVariables.f[i]) + println("\tPPCRegister f{}{{}};", i); + } + + for (size_t i = 0; i < 128; i++) + { + if (localVariables.v[i]) + println("\tPPCVRegister v{}{{}};", i); + } + + if (localVariables.env) + println("\tPPCContext env{{}};"); + + if (localVariables.temp) + println("\tPPCRegister temp{{}};"); + + if (localVariables.vTemp) + println("\tPPCVRegister vTemp{{}};"); + + if (localVariables.ea) + println("\tuint32_t ea{{}};"); + + out += tempString; + + return allRecompiled; +} + +void Recompiler::Recompile(const std::filesystem::path& headerFilePath) +{ + out.reserve(10 * 1024 * 1024); + + { + println("#pragma once"); + + println("#ifndef PPC_CONFIG_H_INCLUDED"); + println("#define PPC_CONFIG_H_INCLUDED\n"); + + if (config.skipLr) + println("#define PPC_CONFIG_SKIP_LR"); + if (config.ctrAsLocalVariable) + println("#define PPC_CONFIG_CTR_AS_LOCAL"); + if (config.xerAsLocalVariable) + println("#define PPC_CONFIG_XER_AS_LOCAL"); + if (config.reservedRegisterAsLocalVariable) + println("#define PPC_CONFIG_RESERVED_AS_LOCAL"); + if (config.skipMsr) + println("#define PPC_CONFIG_SKIP_MSR"); + if (config.crRegistersAsLocalVariables) + println("#define PPC_CONFIG_CR_AS_LOCAL"); + if (config.nonArgumentRegistersAsLocalVariables) + println("#define PPC_CONFIG_NON_ARGUMENT_AS_LOCAL"); + if (config.nonVolatileRegistersAsLocalVariables) + println("#define PPC_CONFIG_NON_VOLATILE_AS_LOCAL"); + + println(""); + + println("#define PPC_IMAGE_BASE 0x{:X}ull", image.base); + println("#define PPC_IMAGE_SIZE 0x{:X}ull", image.size); + + // Extract the address of the minimum code segment to store the function table at. + size_t codeMin = ~0; + size_t codeMax = 0; + + for (auto& section : image.sections) + { + if ((section.flags & SectionFlags_Code) != 0) + { + if (section.base < codeMin) + codeMin = section.base; + + if ((section.base + section.size) > codeMax) + codeMax = (section.base + section.size); + } + } + + println("#define PPC_CODE_BASE 0x{:X}ull", codeMin); + println("#define PPC_CODE_SIZE 0x{:X}ull", codeMax - codeMin); + + println(""); + + println("#ifdef PPC_INCLUDE_DETAIL"); + println("#include \"ppc_detail.h\""); + println("#endif"); + + println("\n#endif"); + + SaveCurrentOutData("ppc_config.h"); + } + + { + println("#pragma once"); + + println("#include \"ppc_config.h\"\n"); + + std::ifstream stream(headerFilePath); + if (stream.good()) + { + std::stringstream ss; + ss << stream.rdbuf(); + out += ss.str(); + } + + SaveCurrentOutData("ppc_context.h"); + } + + { + println("#pragma once\n"); + println("#include \"ppc_config.h\""); + println("#include \"ppc_context.h\"\n"); + + for (auto& symbol : image.symbols) + println("PPC_EXTERN_FUNC({});", symbol.name); + + SaveCurrentOutData("ppc_recomp_shared.h"); + } + + { + println("#include \"ppc_recomp_shared.h\"\n"); + + println("PPCFuncMapping PPCFuncMappings[] = {{"); + for (auto& symbol : image.symbols) + println("\t{{ 0x{:X}, {} }},", symbol.address, symbol.name); + + println("\t{{ 0, nullptr }}"); + println("}};"); + + SaveCurrentOutData("ppc_func_mapping.cpp"); + } + + for (size_t i = 0; i < functions.size(); i++) + { + if ((i % 256) == 0) + { + SaveCurrentOutData(); + println("#include \"ppc_recomp_shared.h\"\n"); + } + + if ((i % 2048) == 0 || (i == (functions.size() - 1))) + fmt::println("Recompiling functions... {}%", static_cast(i + 1) / functions.size() * 100.0f); + + Recompile(functions[i]); + } + + SaveCurrentOutData(); +} + +void Recompiler::SaveCurrentOutData(const std::string_view& name) +{ + if (!out.empty()) + { + std::string cppName; + + if (name.empty()) + { + cppName = fmt::format("ppc_recomp.{}.cpp", cppFileIndex); + ++cppFileIndex; + } + + bool shouldWrite = true; + + // Check if an identical file already exists first to not trigger recompilation + std::string directoryPath = config.directoryPath; + if (!directoryPath.empty()) + directoryPath += "/"; + + std::string filePath = fmt::format("{}{}/{}", directoryPath, config.outDirectoryPath, name.empty() ? cppName : name); + FILE* f = fopen(filePath.c_str(), "rb"); + if (f) + { + static std::vector temp; + + fseek(f, 0, SEEK_END); + long fileSize = ftell(f); + if (fileSize == out.size()) + { + fseek(f, 0, SEEK_SET); + temp.resize(fileSize); + fread(temp.data(), 1, fileSize, f); + + shouldWrite = !XXH128_isEqual(XXH3_128bits(temp.data(), temp.size()), XXH3_128bits(out.data(), out.size())); + } + fclose(f); + } + + if (shouldWrite) + { + f = fopen(filePath.c_str(), "wb"); + fwrite(out.data(), 1, out.size(), f); + fclose(f); + } + + out.clear(); + } +}